vue3+echarts+websocket分時圖與K線圖實時推送

一、父組件代碼:

<template>

? <div class="chart-box" v-loading="loading">

? ? <!-- tab導航欄 -->

? ? <div class="tab-box">

? ? ? <div class="tab-list">

? ? ? ? <div

? ? ? ? ? v-for="(item, index) in tabList"

? ? ? ? ? :key="index"

? ? ? ? ? class="item-tab"

? ? ? ? ? @click="handleClick(index)"

? ? ? ? >

? ? ? ? ? <div :class="tabActive === index ? 'color' : ''" class="tab">

? ? ? ? ? ? {{ item }}

? ? ? ? ? </div>

? ? ? ? ? <div v-if="tabActive === index" class="line-box" />

? ? ? ? </div>

? ? ? </div>

? ? </div>

? ? <!-- k線圖板塊 -->

? ? <div class="Kchart-box" v-if="tabActive === 0">

? ? ? <!-- 導航欄按鈕 -->

? ? ? <div class="btn-options">

? ? ? ? <div

? ? ? ? ? class="btn"

? ? ? ? ? v-for="(item, index) in groupList"

? ? ? ? ? :key="index"

? ? ? ? ? :class="activeIndex === index ? 'color' : ''"

? ? ? ? ? @click="onClickItem(item, index)"

? ? ? ? >

? ? ? ? ? {{ item.name }}

? ? ? ? </div>

? ? ? </div>

? ? ? <!-- k線板塊 -->

? ? ? <div class="kChart">

? ? ? ? <div :style="{ width: '100%' }" class="chart">

? ? ? ? ? <!-- 分時圖 -->

? ? ? ? ? <chartMin

? ? ? ? ? ? v-if="activeIndex === 0"

? ? ? ? ? ? :pre-price="prePrice"

? ? ? ? ? ? :data-list="list"

? ? ? ? ? ? :minDateList1="minDateList1"

? ? ? ? ? ? :digit="digit"

? ? ? ? ? ? :current-index="activeIndex"

? ? ? ? ? ? class="chartMin"

? ? ? ? ? >

? ? ? ? ? </chartMin>

? ? ? ? ? <!-- k線圖 -->

? ? ? ? ? <chartK

? ? ? ? ? ? v-if="activeIndex !== 0"

? ? ? ? ? ? :data-list="listK"

? ? ? ? ? ? :digit="digit"

? ? ? ? ? ? :current-tab="activeIndex"

? ? ? ? ? ? :current-index="currentIndex"

? ? ? ? ? ? class="chartMin"

? ? ? ? ? ? @getHoverData="getHoverData"

? ? ? ? ? >

? ? ? ? ? </chartK>

? ? ? ? ? <div v-if="activeIndex !== 0" class="indexBtn">

? ? ? ? ? ? <span

? ? ? ? ? ? ? :class="{ active: currentIndex === 1 }"

? ? ? ? ? ? ? @click="choseIndex(1)"

? ? ? ? ? ? >

? ? ? ? ? ? ? 成交量

? ? ? ? ? ? </span>

? ? ? ? ? ? <span

? ? ? ? ? ? ? :class="{ active: currentIndex === 2 }"

? ? ? ? ? ? ? @click="choseIndex(2)"

? ? ? ? ? ? >

? ? ? ? ? ? ? MACD

? ? ? ? ? ? </span>

? ? ? ? ? ? <span

? ? ? ? ? ? ? :class="{ active: currentIndex === 3 }"

? ? ? ? ? ? ? @click="choseIndex(3)"

? ? ? ? ? ? >

? ? ? ? ? ? ? KDJ

? ? ? ? ? ? </span>

? ? ? ? ? ? <span

? ? ? ? ? ? ? :class="{ active: currentIndex === 4 }"

? ? ? ? ? ? ? @click="choseIndex(4)"

? ? ? ? ? ? >

? ? ? ? ? ? ? RSI

? ? ? ? ? ? </span>

? ? ? ? ? </div>

? ? ? ? ? <div

? ? ? ? ? ? v-if="activeIndex !== 0 && currentIndex === 1"

? ? ? ? ? ? class="pos-box macd-box"

? ? ? ? ? >

? ? ? ? ? ? <p>

? ? ? ? ? ? ? 成交量(手):

? ? ? ? ? ? ? <span>{{

? ? ? ? ? ? ? ? KHoverData[5] == null ? '' : formatNumUnit(KHoverData[5])

? ? ? ? ? ? ? }}</span>

? ? ? ? ? ? </p>

? ? ? ? ? </div>

? ? ? ? ? <div

? ? ? ? ? ? v-if="activeIndex !== 0 && currentIndex === 2"

? ? ? ? ? ? class="pos-box macd-box"

? ? ? ? ? >

? ? ? ? ? ? <p>

? ? ? ? ? ? ? MACD:

? ? ? ? ? ? ? <span>{{ KHoverData[8] }}</span

? ? ? ? ? ? ? >&nbsp;&nbsp; <span class="color1"> DEA:</span>

? ? ? ? ? ? ? <span>{{ KHoverData[9] }}</span

? ? ? ? ? ? ? >&nbsp;&nbsp; <span class="color2"> DIF:</span>

? ? ? ? ? ? ? <span>{{ KHoverData[10] }}</span

? ? ? ? ? ? ? >&nbsp;&nbsp;

? ? ? ? ? ? </p>

? ? ? ? ? </div>

? ? ? ? ? <div

? ? ? ? ? ? v-if="activeIndex !== 0 && currentIndex === 3"

? ? ? ? ? ? class="pos-box macd-box"

? ? ? ? ? >

? ? ? ? ? ? <p>

? ? ? ? ? ? ? <span class="color1">K:</span>

? ? ? ? ? ? ? <span>{{ KHoverData[13] }}</span

? ? ? ? ? ? ? >&nbsp;&nbsp; <span class="color2">D:</span>

? ? ? ? ? ? ? <span>{{ KHoverData[11] }}</span

? ? ? ? ? ? ? >&nbsp;&nbsp; <span class="color3">J:</span>

? ? ? ? ? ? ? <span>{{ KHoverData[12] }}</span

? ? ? ? ? ? ? >&nbsp;&nbsp;

? ? ? ? ? ? </p>

? ? ? ? ? </div>

? ? ? ? ? <div

? ? ? ? ? ? v-if="activeIndex !== 0 && currentIndex === 4"

? ? ? ? ? ? class="pos-box macd-box"

? ? ? ? ? >

? ? ? ? ? ? <p>

? ? ? ? ? ? ? <span class="color1">RSI6:</span>

? ? ? ? ? ? ? <span>{{ KHoverData[14] }}</span

? ? ? ? ? ? ? >&nbsp;&nbsp; <span class="color2">RSI12:</span>

? ? ? ? ? ? ? <span>{{ KHoverData[15] }}</span

? ? ? ? ? ? ? >&nbsp;&nbsp; <span class="color3">RSI24:</span>

? ? ? ? ? ? ? <span>{{ KHoverData[16] }}</span

? ? ? ? ? ? ? >&nbsp;&nbsp;

? ? ? ? ? ? </p>

? ? ? ? ? </div>

? ? ? ? </div>

? ? ? </div>

? ? </div>

? </div>

</template>

<script setup lang="ts">

import { ElMessage } from 'element-plus'

import chartMin from './chartMin.vue'

import chartK from './chartk.vue'

import common from '@/utils/common'

import useWebSocket from '@/utils/useWebSocket'

import { WEBSOCKET_URL } from '@/service/config'

import { queryMinDate } from '@/service/stockIndex/index'

const props = defineProps({

? securityId: {

? ? // 證券id

? ? type: [String, Number],

? ? required: true

? },

? symbol: {

? ? // 證券代碼

? ? type: String,

? ? default: ''

? },

? market: {

? ? // 證券市場

? ? type: String,

? ? default: ''

? },

? tagIndex: {

? ? // tab索引

? ? type: Number,

? ? default: null

? }

})

const emit = defineEmits(['getKLineType'])

const minChartList = ref<any>([]) // 分時圖行情數據

const minDateList1 = ref<any>([]) // 分時圖行情數據

const kChartList = ref<any>([]) // k線圖行情數據

const prePrice = ref<any>() // 昨收價

const digit = ref(2) // 小數位

const list = ref<any>([]) // 分時圖數據

const minDateList = ref<any>([]) // 分時圖時間段

const kDateList = ref<any>([]) // K線圖時間段

const listK = ref<any>([]) // k線圖數據

const loading = ref(false) // 加載狀態

const activeIndex = ref(0) // 當前選擇的K線圖tab

const tabActive = ref(0) // 當前選擇的頂部tab

const currentIndex = ref(1) // 當前選擇的指標

const KHoverData = ref<any>([]) // k線hoverdata

const dateType = ref<any>(60) // 獲取時間段類型值

const KlineStock = ref() // K線圖websocket實例

const securityId1 = ref(props.securityId) // 證券id

const market1 = ref<any>(props.market) // 證券市場

const symbol1 = ref<any>(props.symbol) // 證券代碼

const tabList = [

? // 導航欄數據

? 'K線圖'

]

const groupList = [

? {

? ? id: 60,

? ? name: '分時圖'

? },

? {

? ? id: 1,

? ? name: '日K線'

? },

? {

? ? id: 4,

? ? name: '周K線'

? },

? {

? ? id: 7,

? ? name: '月K線'

? },

? {

? ? id: 300,

? ? name: '5分鐘'

? },

? {

? ? id: 1800,

? ? name: '30分鐘'

? },

? {

? ? id: 3600,

? ? name: '60分鐘'

? }

]

//監聽參數值重新渲染數據

watch(

? () => [props.securityId, props.market, props.symbol],

? (newVal, oldVal) => {

? ? if (newVal[0] !== oldVal[0]) {

? ? ? securityId1.value = newVal[0]

? ? }

? ? if (newVal[1] !== oldVal[1]) {

? ? ? market1.value = newVal[1]

? ? }

? ? if (newVal[2] !== oldVal[2]) {

? ? ? symbol1.value = newVal[2]

? ? }

? ? minChartList.value = []

? ? minDateList.value = []

? ? kChartList.value = []

? ? KHoverData.value = []

? ? list.value = []

? ? listK.value = []

? ? tabActive.value = 0

? ? activeIndex.value = 0

? ? currentIndex.value = 1

? ? dateType.value = 60

? ? getMinDate(securityId1.value, dateType.value)

? ? // 關閉連接

? ? closeAllSocket()

? ? // 重新建立連接

? ? webSocketInit()

? },

? { deep: true }

)

//初始化websocket

const webSocketInit = () => {

? KlineStock.value = useWebSocket({

? ? url: `${WEBSOCKET_URL}/api/web_socket/QuotationHub/Subscribe/${

? ? ? market1.value

? ? }/${securityId1.value}/${symbol1.value}/${dateType.value}`,

? ? heartBeatData: ''

? })

? KlineStock.value.connect()

}

//監聽分時圖與K線圖websocket數據推送變更

watch(

? () => KlineStock.value && KlineStock.value.message,

? (res: any) => {

? ? if (res && res.code === 200 && res.data) {

? ? ? if (activeIndex.value === 0) {

? ? ? ? // 判斷分時圖推送數據是否大于1,大于1為歷史數據,否則為最新推送數據

? ? ? ? if (JSON.parse(res.data).length > 1) {

? ? ? ? ? JSON.parse(res.data).forEach((el: any) => {

? ? ? ? ? ? // 判斷數據是否存在分時圖數據中

? ? ? ? ? ? const flag = minChartList.value.some(

? ? ? ? ? ? ? (el1: any) => el1.KData.UT === el.KData.UT

? ? ? ? ? ? )

? ? ? ? ? ? if (!flag) {

? ? ? ? ? ? ? // 不存在則push

? ? ? ? ? ? ? minChartList.value.push(el)

? ? ? ? ? ? }

? ? ? ? ? })

? ? ? ? } else {

? ? ? ? ? // 獲取時間x軸上推送過來的時間點的下標

? ? ? ? ? let i = minDateList1.value.indexOf(JSON.parse(res.data)[0].KData.UT)

? ? ? ? ? if (i > -1) {

? ? ? ? ? ? // 如果時間段小于或等于當前下標則直接push

? ? ? ? ? ? if (minChartList.value.length <= i) {

? ? ? ? ? ? ? minChartList.value.push(JSON.parse(res.data)[0])

? ? ? ? ? ? } else {

? ? ? ? ? ? ? // 如果大于則清空時間段直接賦值

? ? ? ? ? ? ? minChartList.value[i] = JSON.parse(res.data)[0]

? ? ? ? ? ? ? for (let j = i + 1; j < minChartList.value.length; j++) {

? ? ? ? ? ? ? ? minChartList.value[j] = []

? ? ? ? ? ? ? }

? ? ? ? ? ? }

? ? ? ? ? }

? ? ? ? }

? ? ? ? refreshMinChart(minChartList.value)

? ? ? } else {

? ? ? ? // 判斷K線圖推送數據是否大于1,大于1為歷史數據,否則為最新推送數據

? ? ? ? if (JSON.parse(res.data).length > 1) {

? ? ? ? ? JSON.parse(res.data).forEach((el: any) => {

? ? ? ? ? ? // 判斷數據是否存在K線圖數據中

? ? ? ? ? ? const flag1 = kChartList.value.some(

? ? ? ? ? ? ? (el1: any) => el1.KData.UT === el.KData.UT

? ? ? ? ? ? )

? ? ? ? ? ? if (!flag1) {

? ? ? ? ? ? ? // 不存在則push

? ? ? ? ? ? ? kChartList.value.push(el)

? ? ? ? ? ? }

? ? ? ? ? })

? ? ? ? } else {

? ? ? ? ? // 取最新數據的最后一條數據

? ? ? ? ? const arr = kChartList.value[kChartList.value.length - 1]

? ? ? ? ? // 判斷時間是否相等

? ? ? ? ? if (arr.KData && arr.KData.UT === JSON.parse(res.data)[0].KData.UT) {

? ? ? ? ? ? // 相等則刪除最后一條,更新新的一條進去

? ? ? ? ? ? kChartList.value.pop()

? ? ? ? ? ? kChartList.value.push(...JSON.parse(res.data))

? ? ? ? ? } else {

? ? ? ? ? ? // 不相等則直接push

? ? ? ? ? ? kChartList.value.push(JSON.parse(res.data)[0])

? ? ? ? ? }

? ? ? ? }

? ? ? ? refreshKChart()

? ? ? }

? ? }

? }

)

// 頂部tab欄切換點擊

const handleClick = (index: number) => {

? tabActive.value = index

? if (tabActive.value === 0) {

? ? dateType.value = 60

? ? emit('getKLineType', dateType.value)

? ? getMinDate(props.securityId, dateType.value)

? ? minChartList.value = []

? ? kChartList.value = []

? ? KHoverData.value = []

? ? // 關閉連接

? ? closeAllSocket()

? ? // 重新建立連接

? ? webSocketInit()

? }

}

// K線圖tab欄切換

const onClickItem = (item: any, index: number) => {

? dateType.value = item.id

? activeIndex.value = index

? emit('getKLineType', dateType.value)

? getMinDate(props.securityId, dateType.value)

? minChartList.value = []

? kChartList.value = []

? KHoverData.value = []

? // 關閉連接

? closeAllSocket()

? // 重新建立連接

? webSocketInit()

}

// 獲取分時圖時間段

const getMinDate = (securityId: any, type: number) => {

? loading.value = true

? securityId = securityId1.value

? type = dateType.value

? minDateList.value = []

? kDateList.value = []

? queryMinDate(securityId, type).then((res: any) => {

? ? if (res.code === 200) {

? ? ? minDateList1.value = res.data

? ? ? // 數據處理(把每一項字符串轉成數組字符串,便于后面行情數據處理—)

? ? ? res.data.map((r: any) => {

? ? ? ? const item = r.split()

? ? ? ? if (activeIndex.value === 0) {

? ? ? ? ? minDateList.value.push(toRaw(item))

? ? ? ? } else {

? ? ? ? ? kDateList.value.push(toRaw(item))

? ? ? ? }

? ? ? })

? ? } else {

? ? ? ElMessage({

? ? ? ? message: res.message,

? ? ? ? type: 'error'

? ? ? })

? ? }

? ? loading.value = false

? })

}

// 刷新分時圖

const refreshMinChart = (data: any) => {

? // 獲取L1Min分時行情

? let lstData: any[] = []

? // 折線數據[utc,cp,cr,pp,avg,ta,tv]

? data.forEach((element: any) => {

? ? const item = [

? ? ? element.KData.UT, // 時間

? ? ? element.KData.CP, // 最新價

? ? ? element.KData.Avg, // 均價

? ? ? element.KData.TV, // 總量

? ? ? element.KData.TA, // 總額

? ? ? element.KData.CR, // 漲跌幅

? ? ? element.KData.PP // 昨收

? ? ]

? ? lstData.push(item)

? })

? list.value = lstData

? prePrice.value = list.value[0][6] // 獲取昨收價確定均線位置

}

// 刷新K線圖

const refreshKChart = () => {

? let lstKData: any[] = []

? // 折線數據

? kChartList.value.forEach((element: any) => {

? ? const item = [

? ? ? element.KData.UT,

? ? ? element.KData.OP, // 開盤值

? ? ? element.KData.CP, // 收盤值

? ? ? element.KData.LP, // 最低值

? ? ? element.KData.HP, // 最高值

? ? ? element.KData.TV, // 總量

? ? ? element.KData.TA, // 總額

? ? ? element.KData.CR, // 漲跌幅

? ? ? element.KIndex.MACD, // mace

? ? ? element.KIndex.DEA, // dea

? ? ? element.KIndex.DIF, // dif

? ? ? element.KIndex.D, // d

? ? ? element.KIndex.J, // j

? ? ? element.KIndex.K, // k

? ? ? element.KIndex.RSI6, // RSI6

? ? ? element.KIndex.RSI12, // RSI12

? ? ? element.KIndex.RSI24, // RSI24

? ? ? element.KData.CG //漲跌

? ? ]

? ? lstKData.push(item)

? })

? listK.value = lstKData

}

// 獲取k線數據

const getHoverData = (data: any) => {

? KHoverData.value = data

}

// 切換指標

const choseIndex = (index: number) => {

? currentIndex.value = index

? KHoverData.value = []

}

// 大數字單位處理(小于10萬不處理)

const formatNumUnit = (value: any) => {

? return common.formatNumUnit(value)

}

const closeAllSocket = () => {

? //斷開全部websocket連接

? KlineStock.value && KlineStock.value.disconnect()

}

onMounted(() => {

? getMinDate(securityId1.value, dateType.value)

? //當前頁面刷新清空

? closeAllSocket()

? webSocketInit()

})

onUnmounted(() => {

? closeAllSocket()

})

</script>

<style lang="less" scoped>

.chart-box {

? .tab-box {

? ? width: 100%;

? ? display: flex;

? ? background-color: #ffffff;

? ? margin-top: 12px;

? ? margin-bottom: 4px;

? ? .tab-list {

? ? ? height: 100%;

? ? ? display: flex;

? ? ? .item-tab {

? ? ? ? height: 100%;

? ? ? ? padding: 0 20px;

? ? ? ? display: flex;

? ? ? ? justify-content: center;

? ? ? ? align-items: center;

? ? ? ? flex-direction: column;

? ? ? ? cursor: pointer;

? ? ? ? position: relative;

? ? ? ? &:first-child {

? ? ? ? ? padding-left: 0;

? ? ? ? }

? ? ? ? .tab {

? ? ? ? ? font-weight: normal;

? ? ? ? ? font-size: 14px;

? ? ? ? ? color: #666666;

? ? ? ? ? position: relative;

? ? ? ? }

? ? ? ? .color {

? ? ? ? ? color: #3a5bb7;

? ? ? ? ? font-weight: 600;

? ? ? ? }

? ? ? ? .line-box {

? ? ? ? ? width: 40px;

? ? ? ? ? height: 3px;

? ? ? ? ? background: #3a5bb7;

? ? ? ? ? position: absolute;

? ? ? ? ? bottom: -9px;

? ? ? ? ? border-radius: 2px 2px 0px 0px;

? ? ? ? }

? ? ? }

? ? }

? }

? .btn-options {

? ? display: flex;

? ? margin: 25px 0 5px;

? ? .btn {

? ? ? padding: 0 15px;

? ? ? height: 24px;

? ? ? background: #f4f7fc;

? ? ? border-radius: 6px;

? ? ? font-weight: 400;

? ? ? font-size: 13px;

? ? ? color: #999999;

? ? ? display: flex;

? ? ? align-items: center;

? ? ? justify-content: center;

? ? ? margin-right: 14px;

? ? ? border: 1px solid #f4f7fc;

? ? ? cursor: pointer;

? ? ? &:hover {

? ? ? ? color: #3a5bb7;

? ? ? }

? ? }

? ? .color {

? ? ? color: #3a5bb7;

? ? ? border: 1px solid #3a5bb7;

? ? ? font-weight: 500;

? ? ? background-color: #ffffff;

? ? }

? }

? .chart {

? ? width: 100%;

? ? height: 360px;

? ? margin-bottom: 16px;

? ? position: relative;

? ? .chartMin {

? ? ? width: 100%;

? ? ? height: 100%;

? ? }

? ? .indexBtn {

? ? ? width: 100%;

? ? ? position: absolute;

? ? ? left: 8%;

? ? ? top: 83.8%;

? ? ? height: 38px;

? ? ? span {

? ? ? ? width: 21%;

? ? ? ? text-align: center;

? ? ? ? display: inline-block;

? ? ? ? line-height: 25px;

? ? ? ? height: 25px;

? ? ? ? border: 1px solid #3a5bb7;

? ? ? ? color: #3a5bb7;

? ? ? ? border-right: none;

? ? ? }

? ? ? span:last-child {

? ? ? ? border-right: 1px solid #3a5bb7;

? ? ? }

? ? ? span:hover,

? ? ? .active {

? ? ? ? cursor: pointer;

? ? ? ? color: #fff;

? ? ? ? background: #3a5bb7;

? ? ? }

? ? }

? ? .pos-box {

? ? ? position: absolute;

? ? }

? ? .macd-box {

? ? ? top: 51.5%;

? ? ? left: 8%;

? ? ? color: #666666;

? ? ? font-size: 12px;

? ? }

? }

? .color1 {

? ? color: #7499e4;

? }

? .color2 {

? ? color: #ff7786;

? }

? .color3 {

? ? color: #339900;

? }

}

</style>

二、chartMin組件代碼:

<template>

? <div class="chart-area no-drag" style="position: relative">

? ? <div id="chartMinline" style="width: 100%; height: 100%" />

? ? <p

? ? ? v-if="tipData"

? ? ? :style="{ left: clientX + 'px', top: clientY + 'px' }"

? ? ? class="echart-tip"

? ? >

? ? ? <span>時間:{{ tipInfo.date }}</span

? ? ? ><br />

? ? ? <span>價格:{{ tipInfo.price }}</span

? ? ? ><br />

? ? ? <span>均價:{{ tipInfo.mittelkurs }}</span

? ? ? ><br />

? ? ? <span>漲跌幅:{{ tipInfo.change }}%</span><br />

? ? ? <span>成交量(手):{{ tipInfo.hand }}</span

? ? ? ><br />

? ? ? <span>成交額:{{ tipInfo.turnover }}</span>

? ? </p>

? </div>

</template>

<script setup lang="ts">

import * as echarts from 'echarts'

import _ from 'lodash'

import common from '@/utils/common'

import { toDecimal } from '@/utils/numberFormat'

const props = defineProps({

? id: {

? ? type: String,

? ? default: 'chartMin'

? },

? // 折線數據

? dataList: {

? ? type: Array,

? ? default: () => []

? },

? // 折線數據

? minDateList1: {

? ? type: Array,

? ? default: () => []

? },

? // 小數位數

? digit: {

? ? type: Number,

? ? default: () => 2

? },

? // 昨收價

? prePrice: {

? ? type: Number,

? ? default: 0

? }

})

var upColor = '#ec0000'

var downColor = '#00da3c'

// 定義圖表

const myChart: any = ref(null)

const minDateList = ref<any>(props.minDateList1) // 分時圖行情數據

const tipData: any = ref() // 浮框信息

const clientX = ref<any>(0) // 距離左右距離

const clientY = ref<any>(0) // 距離上下距離

const leftMax = ref<any>(0) // 左邊Y軸最大值

const leftMin = ref<any>(0) // 左邊Y軸最小值

const rightMax = ref<any>(0) // 右邊Y軸最大值

const rightMin = ref<any>(0) // 右邊Y軸最小值

const leftInterval = ref<any>(0) // 左邊分割數

const rightInterval = ref<any>(0) // 右邊分割數

const chartData = ref<any>(props.dataList) // 折線數據

const prePrice1 = ref<any>(props.prePrice) // 折線數據

// 圖表數據處理

const splitData = (rawData: any) => {

? let categoryData = []

? let allData = []

? let avgValue = []

? let totalVolumeTraded = []

? let totalValueTraded = []

? let changeRatio = []

? for (var i = 0; i < rawData.length; i++) {

? ? categoryData.push(rawData[i][0])

? ? allData.push(rawData[i])

? ? avgValue.push(rawData[i][2])

? ? totalVolumeTraded.push([i, rawData[i][3], rawData[i][5] > 0 ? 1 : -1])

? ? totalValueTraded.push(rawData[i][4])

? ? changeRatio.push(rawData[i][5])

? }

? return {

? ? categoryData,

? ? allData,

? ? avgValue,

? ? totalVolumeTraded,

? ? totalValueTraded,

? ? changeRatio

? }

}

// 使用計算屬性創建tipInfo浮框信息

const tipInfo = computed(() => {

? if (!tipData.value) {

? ? return {

? ? ? date: '--',

? ? ? price: '0.00',

? ? ? change: '0.00',

? ? ? mittelkurs: '0.00',

? ? ? hand: 0,

? ? ? turnover: 0

? ? }

? }

? const info = {

? ? date: tipData.value[0],

? ? price:

? ? ? tipData.value[1] == null

? ? ? ? ? '--'

? ? ? ? : tipData.value[1] == 0

? ? ? ? ? '0.00'

? ? ? ? : toDecimal(tipData.value[1], props.digit, true),

? ? change:

? ? ? tipData.value[5] == null

? ? ? ? ? '--'

? ? ? ? : tipData.value[5] == 0

? ? ? ? ? '0.00'

? ? ? ? : tipData.value[5] > 0

? ? ? ? ? `+${toDecimal(tipData.value[5], 2, true)}`

? ? ? ? : toDecimal(tipData.value[5], 2, true),

? ? mittelkurs:

? ? ? tipData.value[2] == null

? ? ? ? ? '--'

? ? ? ? : tipData.value[2] == 0

? ? ? ? ? '0.00'

? ? ? ? : toDecimal(tipData.value[2], props.digit, true),

? ? hand:

? ? ? tipData.value[3] == null

? ? ? ? ? '--'

? ? ? ? : tipData.value[3] == 0

? ? ? ? ? 0

? ? ? ? : common.formatNumUnit(tipData.value[3]),

? ? turnover:

? ? ? tipData.value[4] == null

? ? ? ? ? '--'

? ? ? ? : tipData.value[4] == 0

? ? ? ? ? 0

? ? ? ? : common.formatNumUnit(tipData.value[4])

? }

? return info

})

//監聽dataList變化,給圖表賦值

watch(

? () => [props.dataList, props.minDateList1, props.prePrice],

? (newValue: any, oldValue: any) => {

? ? if (newValue[0] != oldValue[0]) {

? ? ? // 更新新的圖表數據

? ? ? chartData.value = newValue[0]

? ? }

? ? if (newValue[1] != oldValue[1]) {

? ? ? // 更新新的圖表數據

? ? ? minDateList.value = newValue[1]

? ? }

? ? if (newValue[2] != oldValue[2]) {

? ? ? // 更新新的圖表數據

? ? ? prePrice1.value = newValue[2]

? ? }

? ? tipData.value = null

? ? drawLine() // 重新畫圖

? },

? { deep: true }

)

// 畫圖

const drawLine = () => {

? // 獲取最大值最小值 間隔值

? getMaxMin()

? // 使用getZr添加圖表的整個canvas區域的事件

? myChart.value.getZr().on('mouseover', handleMouseEnterMove)

? myChart.value.getZr().on('mousemove', handleMouseEnterMove)

? const chartOption = getChartOption()

? // 繪制圖表

? myChart.value.setOption(chartOption)

? window.addEventListener('resize', handleResize, false)

}

// 獲取圖表option

const getChartOption = () => {

? // 處理datalist數據

? const data = splitData(toRaw(chartData.value))

? const option = {

? ? color: ['#7499E4', '#FF7786', '#339900'],

? ? legend: {

? ? ? show: true,

? ? ? type: 'plain',

? ? ? icon: 'roundRect',

? ? ? data: ['價格', '均價']

? ? },

? ? grid: [

? ? ? {

? ? ? ? left: 60,

? ? ? ? right: 70,

? ? ? ? top: '6.4%',

? ? ? ? height: '50%'

? ? ? },

? ? ? {

? ? ? ? left: 60,

? ? ? ? right: 70,

? ? ? ? top: '68%',

? ? ? ? height: '30%'

? ? ? }

? ? ],

? ? tooltip: {

? ? ? trigger: 'axis',

? ? ? // 設置浮框不超出容器

? ? ? overflowTooltip: 'none',

? ? ? axisPointer: {

? ? ? ? type: 'line',

? ? ? ? lineStyle: {

? ? ? ? ? type: 'dotted',

? ? ? ? ? color: '#EDE4FF',

? ? ? ? ? width: 2

? ? ? ? }

? ? ? },

? ? ? formatter: function (params: any) {

? ? ? ? const param = params.find((item: any) => item.seriesName == '價格')

? ? ? ? if (param !== undefined && param.data.length > 1) {

? ? ? ? ? tipData.value = param.data

? ? ? ? } else {

? ? ? ? ? tipData.value = null

? ? ? ? }

? ? ? ? return ''

? ? ? }

? ? },

? ? axisPointer: {

? ? ? link: { xAxisIndex: 'all' }

? ? },

? ? xAxis: [

? ? ? {

? ? ? ? type: 'category',

? ? ? ? // 標簽

? ? ? ? axisLabel: {

? ? ? ? ? show: true,

? ? ? ? ? interval: 29,

? ? ? ? ? color: '#333',

? ? ? ? ? showMaxLabel: true

? ? ? ? },

? ? ? ? // 軸線樣式

? ? ? ? axisLine: {

? ? ? ? ? show: false,

? ? ? ? ? lineStyle: {

? ? ? ? ? ? color: '#EDE4FF'

? ? ? ? ? }

? ? ? ? },

? ? ? ? // 坐標軸刻度

? ? ? ? axisTick: {

? ? ? ? ? show: true

? ? ? ? },

? ? ? ? data: minDateList.value

? ? ? },

? ? ? {

? ? ? ? type: 'category',

? ? ? ? gridIndex: 1,

? ? ? ? // 標簽

? ? ? ? axisLabel: {

? ? ? ? ? show: false

? ? ? ? },

? ? ? ? // 軸線樣式

? ? ? ? axisLine: {

? ? ? ? ? show: false,

? ? ? ? ? lineStyle: {

? ? ? ? ? ? color: '#EDE4FF'

? ? ? ? ? }

? ? ? ? },

? ? ? ? // 坐標軸刻度

? ? ? ? axisTick: {

? ? ? ? ? show: false

? ? ? ? },

? ? ? ? data: minDateList.value

? ? ? }

? ? ],

? ? yAxis: [

? ? ? {

? ? ? ? type: 'value',

? ? ? ? gridIndex: 0,

? ? ? ? // 坐標軸刻度

? ? ? ? axisTick: {

? ? ? ? ? show: false

? ? ? ? },

? ? ? ? // 標簽

? ? ? ? axisLabel: {

? ? ? ? ? interval: true,

? ? ? ? ? color: '#666',

? ? ? ? ? formatter: function (value: any) {

? ? ? ? ? ? return toDecimal(value, props.digit, true)

? ? ? ? ? }

? ? ? ? },

? ? ? ? // 軸線樣式

? ? ? ? axisLine: {

? ? ? ? ? show: false

? ? ? ? },

? ? ? ? // 坐標軸在 grid 區域中的分隔線

? ? ? ? splitLine: {

? ? ? ? ? show: false

? ? ? ? },

? ? ? ? min: leftMin.value,

? ? ? ? max: leftMax.value,

? ? ? ? interval: leftInterval.value

? ? ? },

? ? ? {

? ? ? ? type: 'value',

? ? ? ? gridIndex: 1,

? ? ? ? // 坐標軸刻度

? ? ? ? axisTick: {

? ? ? ? ? show: false

? ? ? ? },

? ? ? ? // 標簽

? ? ? ? axisLabel: {

? ? ? ? ? interval: true,

? ? ? ? ? color: '#666',

? ? ? ? ? formatter: function (value: any) {

? ? ? ? ? ? return common.formatNumUnit(value)

? ? ? ? ? }

? ? ? ? },

? ? ? ? // 軸線樣式

? ? ? ? axisLine: {

? ? ? ? ? show: false

? ? ? ? },

? ? ? ? // 坐標軸在 grid 區域中的分隔線

? ? ? ? splitLine: {

? ? ? ? ? show: false

? ? ? ? }

? ? ? },

? ? ? {

? ? ? ? type: 'value',

? ? ? ? gridIndex: 0,

? ? ? ? position: 'right',

? ? ? ? // 坐標軸刻度

? ? ? ? axisTick: {

? ? ? ? ? show: false

? ? ? ? },

? ? ? ? // 標簽

? ? ? ? axisLabel: {

? ? ? ? ? interval: true,

? ? ? ? ? color: '#666',

? ? ? ? ? formatter: function (value: any) {

? ? ? ? ? ? return toDecimal(value, 2, true) + '%'

? ? ? ? ? }

? ? ? ? },

? ? ? ? // 軸線樣式

? ? ? ? axisLine: {

? ? ? ? ? show: false

? ? ? ? },

? ? ? ? // 坐標軸在 grid 區域中的分隔線

? ? ? ? splitLine: {

? ? ? ? ? show: false

? ? ? ? },

? ? ? ? min: rightMin.value,

? ? ? ? max: rightMax.value,

? ? ? ? interval: rightInterval.value

? ? ? }

? ? ],

? ? series: [

? ? ? {

? ? ? ? name: '價格',

? ? ? ? type: 'line',

? ? ? ? xAxisIndex: 0,

? ? ? ? yAxisIndex: 0,

? ? ? ? showSymbol: false,

? ? ? ? symbolSize: 5,

? ? ? ? smooth: true,

? ? ? ? areaStyle: {

? ? ? ? ? color: {

? ? ? ? ? ? type: 'linear',

? ? ? ? ? ? x: 0,

? ? ? ? ? ? y: 0,

? ? ? ? ? ? x2: 0,

? ? ? ? ? ? y2: 1,

? ? ? ? ? ? colorStops: [

? ? ? ? ? ? ? {

? ? ? ? ? ? ? ? offset: 0,

? ? ? ? ? ? ? ? color: '#D8E0FF' // 0% 處的顏色

? ? ? ? ? ? ? },

? ? ? ? ? ? ? {

? ? ? ? ? ? ? ? offset: 1,

? ? ? ? ? ? ? ? color: '#F9FAFF' // 100% 處的顏色

? ? ? ? ? ? ? }

? ? ? ? ? ? ],

? ? ? ? ? ? global: false // 缺省為 false

? ? ? ? ? }

? ? ? ? },

? ? ? ? data: data.allData,

? ? ? ? lineStyle: {

? ? ? ? ? width: 1

? ? ? ? },

? ? ? ? // 標記線

? ? ? ? markLine: {

? ? ? ? ? silent: true,

? ? ? ? ? symbol: ['none', 'none'],

? ? ? ? ? label: {

? ? ? ? ? ? show: false

? ? ? ? ? },

? ? ? ? ? lineStyle: {

? ? ? ? ? ? color: '#7b7de5',

? ? ? ? ? ? opacity: 0.5,

? ? ? ? ? ? type: 'dot'

? ? ? ? ? },

? ? ? ? ? data: [

? ? ? ? ? ? {

? ? ? ? ? ? ? name: 'Y 軸值為 yAxis 的水平線',

? ? ? ? ? ? ? yAxis: toDecimal(prePrice1.value, props.digit, true)

? ? ? ? ? ? }

? ? ? ? ? ]

? ? ? ? }

? ? ? },

? ? ? {

? ? ? ? name: '均價',

? ? ? ? type: 'line',

? ? ? ? xAxisIndex: 0,

? ? ? ? yAxisIndex: 0,

? ? ? ? showSymbol: false,

? ? ? ? smooth: true,

? ? ? ? symbolSize: 5,

? ? ? ? lineStyle: {

? ? ? ? ? width: 1

? ? ? ? },

? ? ? ? data: data.avgValue

? ? ? },

? ? ? {

? ? ? ? name: '交易量',

? ? ? ? type: 'bar',

? ? ? ? xAxisIndex: 1,

? ? ? ? yAxisIndex: 1,

? ? ? ? data: data.totalVolumeTraded,

? ? ? ? itemStyle: {

? ? ? ? ? color: function (params: any) {

? ? ? ? ? ? let colorList = ''

? ? ? ? ? ? if (params.dataIndex == 0) {

? ? ? ? ? ? ? if (data.allData[0][1] >= prePrice1.value) {

? ? ? ? ? ? ? ? colorList = upColor

? ? ? ? ? ? ? } else {

? ? ? ? ? ? ? ? colorList = downColor

? ? ? ? ? ? ? }

? ? ? ? ? ? } else {

? ? ? ? ? ? ? if (

? ? ? ? ? ? ? ? data.allData[params.dataIndex][1] >=

? ? ? ? ? ? ? ? data.allData[params.dataIndex - 1][1]

? ? ? ? ? ? ? ) {

? ? ? ? ? ? ? ? colorList = upColor

? ? ? ? ? ? ? } else {

? ? ? ? ? ? ? ? colorList = downColor

? ? ? ? ? ? ? }

? ? ? ? ? ? }

? ? ? ? ? ? return colorList

? ? ? ? ? }

? ? ? ? }

? ? ? }

? ? ]

? }

? return option

}

const getMaxMin = () => {

? if (chartData.value.length > 0) {

? ? const lstData = chartData.value.filter(

? ? ? (m: any) => m[1] != null && m[1] != undefined

? ? )

? ? const priceList = lstData.map(function (item: any) {

? ? ? return toDecimal(item[1], props.digit, true)

? ? })

? ? const averageList = lstData.map(function (item: any) {

? ? ? return toDecimal(item[2], props.digit, true)

? ? })

? ? const changeRatioList = lstData.map(function (item: any) {

? ? ? return toDecimal(item[5], 2, true)

? ? })

? ? // 左y軸數據

? ? var avgMax

? ? var avgMin

? ? var priceMax

? ? var priceMin = 0

? ? avgMax = getMax(averageList)

? ? avgMin = getMin(averageList)

? ? priceMax = getMax(priceList)

? ? priceMin = getMin(priceList)

? ? // 股票

? ? leftMax.value = Math.max(avgMax, priceMax)

? ? leftMin.value = avgMin == 0 ? priceMin : Math.min(avgMin, priceMin)

? ? const middleLineVal = prePrice1.value

? ? const max = common.numSub(leftMax.value, middleLineVal)

? ? const min = common.numSub(middleLineVal, leftMin.value)

? ? const absMax = Math.max(Math.abs(Number(max)), Math.abs(Number(min)))

? ? if (absMax == 0) {

? ? ? leftMax.value = common.numMul(middleLineVal, 1.05)

? ? ? leftMin.value = common.numMul(middleLineVal, 0.95)

? ? } else {

? ? ? leftMax.value = common.numAdd(middleLineVal, absMax)

? ? ? leftMin.value = common.numSub(middleLineVal, absMax)

? ? }

? ? leftInterval.value = Number(

? ? ? toDecimal(

? ? ? ? common.accDiv(common.numSub(leftMax.value, leftMin.value), 4),

? ? ? ? props.digit + 1,

? ? ? ? true

? ? ? )

? ? )

? ? // 右y軸數據

? ? rightMax.value = getMax(changeRatioList)

? ? rightMin.value = getMin(changeRatioList)

? ? const middleLineVal1 = 0

? ? const max1 = rightMax.value - middleLineVal1

? ? const min1 = middleLineVal1 - rightMin.value

? ? const absMax1 = Math.max(Math.abs(max1), Math.abs(min1))

? ? if (absMax1 == 0) {

? ? ? rightMax.value = middleLineVal1 * 1.05

? ? ? rightMin.value = middleLineVal1 * 0.95

? ? } else {

? ? ? rightMax.value = middleLineVal1 + absMax1

? ? ? rightMin.value = middleLineVal1 - absMax1

? ? }

? ? rightInterval.value = common.accDiv(

? ? ? common.numSub(rightMax.value, rightMin.value),

? ? ? 4

? ? )

? }

}

const getMax = (arr: any) => {

? const maxList = arr.filter((item: any) => item !== '-')

? let Max = 0

? if (maxList.length > 0) {

? ? const max0 = maxList[0]

? ? Max = max0

? ? maxList.forEach((item: any) => {

? ? ? if (Number(item) > Number(Max)) {

? ? ? ? Max = Number(item)

? ? ? }

? ? })

? }

? return Number(Max)

}

const getMin = (arr: any) => {

? const minList = arr.filter((item: any) => item !== '-')

? let Min = 0

? if (minList.length > 0) {

? ? const min0 = minList[0]

? ? Min = min0

? ? minList.forEach((item: any) => {

? ? ? if (Number(item) < Number(Min)) {

? ? ? ? Min = Number(item)

? ? ? }

? ? })

? }

? return Number(Min)

}

const handleResize = () => {

? myChart.value.resize()

}

const handleMouseEnterMove = (params: any) => {

? const { offsetX, offsetY, target, topTarget } = params

? clientX.value = offsetX - 40

? clientY.value = offsetY + 18

? // 移至坐標軸外時target和topTarget都為undefined

? if (!target && !topTarget) {

? ? tipData.value = null

? }

}

onMounted(() => {

? // 基于準備好的dom,初始化echarts實例

? myChart.value = markRaw(echarts.init(document.getElementById('chartMinline')))

? drawLine()

})

onUnmounted(() => {

? window.removeEventListener('resize', handleResize, false)

})

</script>

<style lang="less" scoped>

.echart-tip {

? position: absolute;

? background-color: rgba(38, 43, 81, 0.5);

? font-size: 12px;

? line-height: 16px;

? padding: 5px;

? border-radius: 4px;

? color: #fff;

? z-index: 9;

? min-width: 130px;

? > p {

? ? padding: 0;

? ? margin: 0;

? }

}

</style>

三、chartK組件代碼:

<template>

? <div

? ? class="chart-area no-drag"

? ? style="position: relative"

? ? v-loading="loading"

? >

? ? <div id="chartKline" style="width: 100%; height: 100%" />

? ? <p

? ? ? v-if="tipData"

? ? ? :style="{ left: clientX + 'px', top: clientY + 'px' }"

? ? ? class="echart-tip"

? ? >

? ? ? <span>{{ tipInfo.axisValue }}</span

? ? ? ><br />

? ? ? <span>開盤:{{ tipInfo.opening }}</span

? ? ? ><br />

? ? ? <span>收盤:{{ tipInfo.closing }}</span

? ? ? ><br />

? ? ? <span>最低:{{ tipInfo.bottommost }}</span

? ? ? ><br />

? ? ? <span>最高:{{ tipInfo.highest }}</span

? ? ? ><br />

? ? ? <span>漲跌幅:{{ tipInfo.change }}%</span><br />

? ? ? <span>成交量(手):{{ tipInfo.turnover }}</span

? ? ? ><br />

? ? ? <span>MA5:{{ tipInfo.MA5 }}</span

? ? ? ><br />

? ? ? <span>MA10:{{ tipInfo.MA10 }}</span

? ? ? ><br />

? ? ? <span>MA20:{{ tipInfo.MA20 }}</span

? ? ? ><br />

? ? ? <span>MA30:{{ tipInfo.MA30 }}</span>

? ? </p>

? </div>

</template>

<script setup lang="ts">

import * as echarts from 'echarts'

import common from '@/utils/common'

import { toDecimal } from '@/utils/numberFormat'

const props = defineProps({

? // 指標 1:成交量 2.MACD 3.KDJ

? currentIndex: {

? ? type: Number,

? ? default: 1

? },

? // 折線數據 時間 開盤價 收盤價 最低值 最高值 總量

? dataList: {

? ? type: Array,

? ? default: () => []

? },

? // 小數位數

? digit: {

? ? type: Number,

? ? default: () => 2

? },

? // 當前選擇的K線周期 1:日K 2:周K 3:月K 4:5min 5:30min 6:60min

? currentTab: {

? ? type: Number,

? ? default: () => 1

? }

})

const emit = defineEmits(['getHoverData'])

const upColor = '#ec0000'

const downColor = '#00da3c'

const ma5Color = '#39afe6'

const ma10Color = '#da6ee8'

const ma20Color = '#ffab42'

const ma30Color = '#00940b'

const color1 = '#7499E4'

const color2 = '#FF7786'

const color3 = '#339900'

const dataListTemp = ref<any>(props.dataList) // 備份dataList

const isDrawing = ref(false) // 是否展示圖表

const loading = ref(false) // 是否展示圖表

const clientX = ref<any>(0) // 距離左右距離

const clientY = ref<any>(0) // 距離上下距離

// 定義圖表

const myChart: any = ref(null)

const tipData: any = ref(null) // 浮框信息

const dataZoomY: any = ref(null) // 保存dataZoomY信息

// 圖表數據處理

const splitData = (rawData: any) => {

? const categoryData = []

? const values = []

? const volumes = []

? const MACD = []

? const DEA = []

? const DIF = []

? const D = []

? const J = []

? const K = []

? const RSI6 = []

? const RSI12 = []

? const RSI24 = []

? for (let i = 0; i < rawData.length; i++) {

? ? categoryData.push(rawData[i][0])

? ? values.push(rawData[i].slice(1))

? ? volumes.push([i, rawData[i][5], rawData[i][1] > rawData[i][2] ? 1 : -1])

? ? MACD.push([i, rawData[i][8], rawData[i][8] < 0 ? 1 : -1])

? ? DEA.push(rawData[i][9])

? ? DIF.push(rawData[i][10])

? ? D.push(rawData[i][11])

? ? J.push(rawData[i][12])

? ? K.push(rawData[i][13])

? ? RSI6.push(rawData[i][14])

? ? RSI12.push(rawData[i][15])

? ? RSI24.push(rawData[i][16])

? }

? if (rawData.length <= 70) {

? ? for (let index = 0; index < 70 - rawData.length; index++) {

? ? ? categoryData.push('')

? ? ? values.push([])

? ? ? volumes.push(['', '', ''])

? ? ? MACD.push(['', '', ''])

? ? ? DEA.push(0)

? ? ? DIF.push(0)

? ? ? D.push(0)

? ? ? J.push(0)

? ? ? K.push(0)

? ? ? RSI6.push(0)

? ? ? RSI12.push(0)

? ? ? RSI24.push(0)

? ? }

? }

? return {

? ? categoryData,

? ? values,

? ? volumes,

? ? MACD,

? ? DEA,

? ? DIF,

? ? D,

? ? J,

? ? K,

? ? RSI6,

? ? RSI12,

? ? RSI24

? }

}

// 使用計算屬性創建tipInfo浮框信息

const tipInfo = computed(() => {

? if (!tipData.value) {

? ? return {

? ? ? axisValue: '--',

? ? ? opening: '0.00',

? ? ? closing: '0.00',

? ? ? bottommost: '0.00',

? ? ? highest: '0.00',

? ? ? change: '0.00',

? ? ? turnover: 0,

? ? ? MA5: '--',

? ? ? MA10: '--',

? ? ? MA20: '--',

? ? ? MA30: '--'

? ? }

? }

? const data = tipData.value.data

? const info = {

? ? axisValue: tipData.value.axisValue,

? ? opening:

? ? ? data[1] == null

? ? ? ? ? '--'

? ? ? ? : data[1] == 0

? ? ? ? ? '0.00'

? ? ? ? : toDecimal(data[1], props.digit, true),

? ? closing:

? ? ? data[2] == null

? ? ? ? ? '--'

? ? ? ? : data[2] == 0

? ? ? ? ? '0.00'

? ? ? ? : toDecimal(data[2], props.digit, true),

? ? bottommost:

? ? ? data[3] == null

? ? ? ? ? '--'

? ? ? ? : data[3] == 0

? ? ? ? ? '0.00'

? ? ? ? : toDecimal(data[3], props.digit, true),

? ? highest:

? ? ? data[4] == null

? ? ? ? ? '--'

? ? ? ? : data[4] == 0

? ? ? ? ? '0.00'

? ? ? ? : toDecimal(data[4], props.digit, true),

? ? change:

? ? ? data[7] == null

? ? ? ? ? '--'

? ? ? ? : data[7] == 0

? ? ? ? ? '0.00'

? ? ? ? : data[7] > 0

? ? ? ? ? `+${toDecimal(data[7], props.digit, true)}`

? ? ? ? : toDecimal(data[7], props.digit, true),

? ? turnover:

? ? ? data[5] == null ? '--' : data[5] == 0 ? 0 : common.formatNumUnit(data[5]),

? ? MA5: isNaN(tipData.value.MA5) ? '--' : tipData.value.MA5,

? ? MA10: isNaN(tipData.value.MA10) ? '--' : tipData.value.MA10,

? ? MA20: isNaN(tipData.value.MA20) ? '--' : tipData.value.MA20,

? ? MA30: isNaN(tipData.value.MA30) ? '--' : tipData.value.MA30

? }

? return info

})

//監聽currentIndex與dataList變化,給圖表賦值

watch(

? () => [props.currentIndex, props.dataList, props.currentTab],

? (newValue: any, oldValue: any) => {

? ? if (newValue[0] != oldValue[0]) {

? ? ? initHoverData()

? ? ? drawLine()

? ? }

? ? if (newValue[1] != oldValue[1]) {

? ? ? dataListTemp.value = newValue[1]

? ? ? myChart.value && myChart.value.showLoading()

? ? ? initHoverData()

? ? ? drawLine()

? ? }

? ? if (newValue[2] != oldValue[2]) {

? ? ? resetChartDrawing()

? ? ? initHoverData()

? ? }

? },

? { deep: true }

)

const init = () => {

? // 基于準備好的dom,初始化echarts實例

? myChart.value = markRaw(echarts.init(document.getElementById('chartKline')))

? myChart.value.getZr().on('click', handleEchartsClick)

? // 使用getZr添加圖表的整個canvas區域的事件

? myChart.value.getZr().on('mouseover', handleMouseEnterMove)

? myChart.value.getZr().on('mousemove', handleMouseEnterMove)

? myChart.value.on('dataZoom', (event: any) => {

? ? if (event.batch) {

? ? ? event = event.batch[0]

? ? ? dataZoomY.value = event

? ? } else {

? ? ? const { dataZoomId } = event

? ? ? if (!dataZoomId) {

? ? ? ? return

? ? ? }

? ? ? dataZoomY.value = event

? ? }

? })

? initHoverData()

? drawLine()

? window.addEventListener('resize', handleResize, false)

}

const calculateMA = (dayCount: any, data: any) => {

? const result = []

? for (let i = 0, len = data.categoryData.length; i < len; i++) {

? ? if (i < dayCount - 1) {

? ? ? result.push('-')

? ? ? continue

? ? }

? ? let sum = 0

? ? for (let j = 0; j < dayCount; j++) {

? ? ? sum += Number(data.values[i - j][1])

? ? }

? ? result.push((sum / dayCount).toFixed(props.digit))

? }

? return result

}

const drawLine = () => {

? // 基于準備好的dom,初始化echarts實例

? if (isDrawing.value || !myChart.value) {

? ? setTimeout(() => {

? ? ? drawLine()

? ? })

? ? return

? }

? isDrawing.value = true

? const chartOption = getChartOption()

? // 繪制圖表

? isDrawing.value && myChart.value.setOption(chartOption, true)

? nextTick(() => {

? ? isDrawing.value = false

? ? myChart.value.hideLoading()

? })

}

// 獲取圖表option

const getChartOption = () => {

? loading.value = true

? // 處理datalist數據

? const data = splitData(dataListTemp.value)

? let dataZoomStart = getStart()

? let dataZoomEnd = 100

? if (isDrawing.value && dataZoomY.value) {

? ? const { start, end } = dataZoomY.value

? ? dataZoomStart = start

? ? dataZoomEnd = end

? }

? const option: any = {

? ? animation: false,

? ? legend: {

? ? ? // 圖例控件,點擊圖例控制哪些系列不顯示

? ? ? icon: 'rect',

? ? ? type: 'scroll',

? ? ? itemWidth: 14,

? ? ? itemHeight: 2,

? ? ? right: 30,

? ? ? top: -6,

? ? ? animation: true,

? ? ? fontSize: 12,

? ? ? color: '#999999',

? ? ? pageIconColor: '#999999',

? ? ? selectedMode: false,

? ? ? data: ['MA5', 'MA10', 'MA20', 'MA30']

? ? },

? ? color: [ma5Color, ma5Color, ma10Color, ma20Color, ma30Color],

? ? grid: [

? ? ? {

? ? ? ? left: 60,

? ? ? ? right: 30,

? ? ? ? top: '5.25%',

? ? ? ? height: '40%'

? ? ? },

? ? ? {

? ? ? ? left: 60,

? ? ? ? right: 30,

? ? ? ? top: '58%',

? ? ? ? height: '25%'

? ? ? }

? ? ],

? ? axisPointer: {

? ? ? link: { xAxisIndex: 'all' }, // 綁定兩個圖

? ? ? label: {

? ? ? ? backgroundColor: '#777'

? ? ? }

? ? },

? ? tooltip: {

? ? ? trigger: 'axis',

? ? ? axisPointer: {

? ? ? ? type: 'cross',

? ? ? ? lineStyle: {

? ? ? ? ? color: '#999',

? ? ? ? ? width: 2

? ? ? ? }

? ? ? },

? ? ? extraCssText: 'text-align: left;',

? ? ? formatter: function (params: any) {

? ? ? ? setHoverData(params)

? ? ? ? const param = params.find(

? ? ? ? ? (item: any) =>

? ? ? ? ? ? item.axisIndex === 0 && item.componentSubType === 'candlestick'

? ? ? ? )

? ? ? ? if (param && param.data && param.data.length > 1) {

? ? ? ? ? const MA5Item = params.find((item: any) => item.seriesName == 'MA5')

? ? ? ? ? const MA5 = MA5Item ? toDecimal(MA5Item.data, props.digit, true) : 0

? ? ? ? ? const MA10Item = params.find(

? ? ? ? ? ? (item: any) => item.seriesName === 'MA10'

? ? ? ? ? )

? ? ? ? ? const MA10 = MA10Item

? ? ? ? ? ? ? toDecimal(MA10Item.data, props.digit, true)

? ? ? ? ? ? : 0

? ? ? ? ? const MA20Item = params.find(

? ? ? ? ? ? (item: any) => item.seriesName === 'MA20'

? ? ? ? ? )

? ? ? ? ? const MA20 = MA20Item

? ? ? ? ? ? ? toDecimal(MA20Item.data, props.digit, true)

? ? ? ? ? ? : 0

? ? ? ? ? const MA30Item = params.find(

? ? ? ? ? ? (item: any) => item.seriesName === 'MA30'

? ? ? ? ? )

? ? ? ? ? const MA30 = MA30Item

? ? ? ? ? ? ? toDecimal(MA30Item.data, props.digit, true)

? ? ? ? ? ? : 0

? ? ? ? ? tipData.value = Object.assign({}, param, {

? ? ? ? ? ? MA5,

? ? ? ? ? ? MA10,

? ? ? ? ? ? MA20,

? ? ? ? ? ? MA30

? ? ? ? ? })

? ? ? ? } else {

? ? ? ? ? tipData.value = null

? ? ? ? }

? ? ? ? return ''

? ? ? }

? ? },

? ? xAxis: [

? ? ? {

? ? ? ? type: 'category',

? ? ? ? // 標簽

? ? ? ? axisLabel: {

? ? ? ? ? show: true,

? ? ? ? ? color: '#333'

? ? ? ? },

? ? ? ? // 軸線樣式

? ? ? ? axisLine: {

? ? ? ? ? show: false,

? ? ? ? ? lineStyle: {

? ? ? ? ? ? color: '#333'

? ? ? ? ? }

? ? ? ? },

? ? ? ? // 坐標軸刻度

? ? ? ? axisTick: {

? ? ? ? ? show: false

? ? ? ? },

? ? ? ? data: data.categoryData

? ? ? },

? ? ? {

? ? ? ? type: 'category',

? ? ? ? gridIndex: 1,

? ? ? ? // 標簽

? ? ? ? axisLabel: {

? ? ? ? ? show: false

? ? ? ? },

? ? ? ? // 軸線樣式

? ? ? ? axisLine: {

? ? ? ? ? show: false,

? ? ? ? ? lineStyle: {

? ? ? ? ? ? color: '#333'

? ? ? ? ? }

? ? ? ? },

? ? ? ? // 坐標軸刻度

? ? ? ? axisTick: {

? ? ? ? ? show: false

? ? ? ? },

? ? ? ? // 坐標軸指示器

? ? ? ? axisPointer: {

? ? ? ? ? label: {

? ? ? ? ? ? show: false

? ? ? ? ? }

? ? ? ? },

? ? ? ? data: data.categoryData

? ? ? }

? ? ],

? ? yAxis: [

? ? ? {

? ? ? ? type: 'value',

? ? ? ? gridIndex: 0,

? ? ? ? scale: true,

? ? ? ? splitNumber: 5,

? ? ? ? // 坐標軸刻度

? ? ? ? axisTick: {

? ? ? ? ? show: false

? ? ? ? },

? ? ? ? // 標簽

? ? ? ? axisLabel: {

? ? ? ? ? interval: true,

? ? ? ? ? color: '#666',

? ? ? ? ? formatter: function (value: any) {

? ? ? ? ? ? return toDecimal(value, props.digit, true)

? ? ? ? ? }

? ? ? ? },

? ? ? ? // 軸線樣式

? ? ? ? axisLine: {

? ? ? ? ? show: false

? ? ? ? }

? ? ? },

? ? ? // 交易量軸

? ? ? {

? ? ? ? type: 'value',

? ? ? ? gridIndex: 1,

? ? ? ? // y軸原點是否不從0開始

? ? ? ? scale: true,

? ? ? ? // 坐標軸刻度

? ? ? ? axisTick: {

? ? ? ? ? show: false

? ? ? ? },

? ? ? ? // 標簽

? ? ? ? axisLabel: {

? ? ? ? ? interval: true,

? ? ? ? ? color: '#666',

? ? ? ? ? formatter: function (value: any) {

? ? ? ? ? ? return common.formatNumUnit(value)

? ? ? ? ? }

? ? ? ? },

? ? ? ? // 軸線樣式

? ? ? ? axisLine: {

? ? ? ? ? show: false

? ? ? ? },

? ? ? ? // 坐標軸在 grid 區域中的分隔線

? ? ? ? splitLine: {

? ? ? ? ? show: false

? ? ? ? }

? ? ? }

? ? ],

? ? series: [

? ? ? {

? ? ? ? name: 'k線',

? ? ? ? type: 'candlestick',

? ? ? ? itemStyle: {

? ? ? ? ? color: upColor,

? ? ? ? ? color0: downColor,

? ? ? ? ? borderColor: upColor,

? ? ? ? ? borderColor0: downColor

? ? ? ? },

? ? ? ? xAxisIndex: 0,

? ? ? ? yAxisIndex: 0,

? ? ? ? data: data.values,

? ? ? ? lineStyle: {

? ? ? ? ? width: 1

? ? ? ? }

? ? ? },

? ? ? {

? ? ? ? name: '交易量',

? ? ? ? type: 'bar',

? ? ? ? xAxisIndex: 1,

? ? ? ? yAxisIndex: 1,

? ? ? ? data: data.volumes,

? ? ? ? itemStyle: {

? ? ? ? ? color: function (params: any) {

? ? ? ? ? ? let colorList = ''

? ? ? ? ? ? if (params.dataIndex == 0) {

? ? ? ? ? ? ? if (data.values[0][1] >= data.values[0][0]) {

? ? ? ? ? ? ? ? colorList = upColor

? ? ? ? ? ? ? } else {

? ? ? ? ? ? ? ? colorList = downColor

? ? ? ? ? ? ? }

? ? ? ? ? ? } else {

? ? ? ? ? ? ? if (

? ? ? ? ? ? ? ? data.values[params.dataIndex][1] >=

? ? ? ? ? ? ? ? data.values[params.dataIndex - 1][1]

? ? ? ? ? ? ? ) {

? ? ? ? ? ? ? ? colorList = upColor

? ? ? ? ? ? ? } else {

? ? ? ? ? ? ? ? colorList = downColor

? ? ? ? ? ? ? }

? ? ? ? ? ? }

? ? ? ? ? ? return colorList

? ? ? ? ? }

? ? ? ? }

? ? ? },

? ? ? {

? ? ? ? name: 'MA5',

? ? ? ? type: 'line',

? ? ? ? data: calculateMA(5, data),

? ? ? ? smooth: true,

? ? ? ? symbol: 'none', // 隱藏選中時有小圓點

? ? ? ? lineStyle: {

? ? ? ? ? opacity: 0.8,

? ? ? ? ? color: ma5Color,

? ? ? ? ? width: 1

? ? ? ? }

? ? ? },

? ? ? {

? ? ? ? name: 'MA10',

? ? ? ? type: 'line',

? ? ? ? data: calculateMA(10, data),

? ? ? ? smooth: true,

? ? ? ? symbol: 'none',

? ? ? ? lineStyle: {

? ? ? ? ? // 標線的樣式

? ? ? ? ? opacity: 0.8,

? ? ? ? ? color: ma10Color,

? ? ? ? ? width: 1

? ? ? ? }

? ? ? },

? ? ? {

? ? ? ? name: 'MA20',

? ? ? ? type: 'line',

? ? ? ? data: calculateMA(20, data),

? ? ? ? smooth: true,

? ? ? ? symbol: 'none',

? ? ? ? lineStyle: {

? ? ? ? ? opacity: 0.8,

? ? ? ? ? width: 1,

? ? ? ? ? color: ma20Color

? ? ? ? }

? ? ? },

? ? ? {

? ? ? ? name: 'MA30',

? ? ? ? type: 'line',

? ? ? ? data: calculateMA(30, data),

? ? ? ? smooth: true,

? ? ? ? symbol: 'none',

? ? ? ? lineStyle: {

? ? ? ? ? opacity: 0.8,

? ? ? ? ? width: 1,

? ? ? ? ? color: ma30Color

? ? ? ? }

? ? ? }

? ? ],

? ? dataZoom: [

? ? ? {

? ? ? ? id: 'dataZoomX',

? ? ? ? type: 'inside',

? ? ? ? xAxisIndex: [0, 1],

? ? ? ? start: dataZoomStart,

? ? ? ? end: dataZoomEnd

? ? ? },

? ? ? {

? ? ? ? id: 'dataZoomY',

? ? ? ? show: true,

? ? ? ? xAxisIndex: [0, 1],

? ? ? ? type: 'slider',

? ? ? ? height: 20, // 設置滑動條的高度

? ? ? ? realtime: true,

? ? ? ? bottom: 7,

? ? ? ? start: dataZoomStart,

? ? ? ? end: dataZoomEnd

? ? ? }

? ? ]

? }

? if (props.currentIndex == 2) {

? ? option.series[1] = {

? ? ? name: 'MACD',

? ? ? type: 'bar',

? ? ? xAxisIndex: 1,

? ? ? yAxisIndex: 1,

? ? ? data: data.MACD,

? ? ? showSymbol: false

? ? }

? ? option.visualMap = {

? ? ? show: false,

? ? ? seriesIndex: 1,

? ? ? dimension: 2,

? ? ? pieces: [

? ? ? ? {

? ? ? ? ? value: 1,

? ? ? ? ? color: downColor

? ? ? ? },

? ? ? ? {

? ? ? ? ? value: -1,

? ? ? ? ? color: upColor

? ? ? ? }

? ? ? ]

? ? }

? ? option.series.push({

? ? ? name: 'DEA',

? ? ? type: 'line',

? ? ? xAxisIndex: 1,

? ? ? yAxisIndex: 1,

? ? ? data: data.DEA,

? ? ? showSymbol: false,

? ? ? lineStyle: {

? ? ? ? color: color1

? ? ? }

? ? })

? ? option.series.push({

? ? ? name: 'DIF',

? ? ? type: 'line',

? ? ? xAxisIndex: 1,

? ? ? yAxisIndex: 1,

? ? ? data: data.DIF,

? ? ? showSymbol: false,

? ? ? lineStyle: {

? ? ? ? color: color2

? ? ? }

? ? })

? } else if (props.currentIndex == 3) {

? ? option.series.push({

? ? ? name: 'K',

? ? ? type: 'line',

? ? ? xAxisIndex: 1,

? ? ? yAxisIndex: 1,

? ? ? data: data.K,

? ? ? showSymbol: false,

? ? ? lineStyle: {

? ? ? ? color: color1

? ? ? }

? ? })

? ? option.series[1] = {

? ? ? name: 'D',

? ? ? type: 'line',

? ? ? xAxisIndex: 1,

? ? ? yAxisIndex: 1,

? ? ? data: data.D,

? ? ? showSymbol: false,

? ? ? lineStyle: {

? ? ? ? color: color2

? ? ? }

? ? }

? ? option.series.push({

? ? ? name: 'J',

? ? ? type: 'line',

? ? ? xAxisIndex: 1,

? ? ? yAxisIndex: 1,

? ? ? data: data.J,

? ? ? showSymbol: false,

? ? ? lineStyle: {

? ? ? ? color: color3

? ? ? }

? ? })

? } else if (props.currentIndex == 4) {

? ? option.series[1] = {

? ? ? name: 'RSI6',

? ? ? type: 'line',

? ? ? xAxisIndex: 1,

? ? ? yAxisIndex: 1,

? ? ? data: data.RSI6,

? ? ? showSymbol: false,

? ? ? lineStyle: {

? ? ? ? color: color1

? ? ? }

? ? }

? ? option.series.push({

? ? ? name: 'RSI12',

? ? ? type: 'line',

? ? ? xAxisIndex: 1,

? ? ? yAxisIndex: 1,

? ? ? data: data.RSI12,

? ? ? showSymbol: false,

? ? ? lineStyle: {

? ? ? ? color: color2

? ? ? }

? ? })

? ? option.series.push({

? ? ? name: 'RSI24',

? ? ? type: 'line',

? ? ? xAxisIndex: 1,

? ? ? yAxisIndex: 1,

? ? ? data: data.RSI24,

? ? ? showSymbol: false,

? ? ? lineStyle: {

? ? ? ? color: color3

? ? ? }

? ? })

? }

? loading.value = false

? return option

}

const setHoverData = (params: any) => {

? const param = params.find(function (item: any) {

? ? return item.componentSubType == 'candlestick'

? })

? if (param !== undefined) {

? ? emit('getHoverData', param.data)

? }

}

const initHoverData = () => {

? const data: any = dataListTemp.value

? if (data.length > 0) {

? ? let arr = [

? ? ? '',

? ? ? '',

? ? ? '',

? ? ? '',

? ? ? '',

? ? ? data[data.length - 1][5],

? ? ? '',

? ? ? '',

? ? ? data[data.length - 1][8],

? ? ? data[data.length - 1][9],

? ? ? data[data.length - 1][10],

? ? ? data[data.length - 1][11],

? ? ? data[data.length - 1][12],

? ? ? data[data.length - 1][13],

? ? ? data[data.length - 1][14],

? ? ? data[data.length - 1][15],

? ? ? data[data.length - 1][16]

? ? ]

? ? emit('getHoverData', arr)

? }

}

// 獲取起始位置

const getStart = () => {

? if (dataListTemp.value && dataListTemp.value.length > 0) {

? ? const start =

? ? ? dataListTemp.value.length > 70

? ? ? ? ? 100 - (70 / dataListTemp.value.length) * 100

? ? ? ? : 0

? ? loading.value = false

? ? return start

? } else {

? ? let start = 0

? ? switch (props.currentTab) {

? ? ? case 1:

? ? ? ? start = 95

? ? ? ? break

? ? ? case 2:

? ? ? ? start = 95

? ? ? ? break

? ? ? case 3:

? ? ? ? start = 95

? ? ? ? break

? ? ? case 4:

? ? ? ? start = 95

? ? ? ? break

? ? ? case 5:

? ? ? ? start = 95

? ? ? ? break

? ? ? case 6:

? ? ? ? start = 95

? ? ? ? break

? ? ? default:

? ? ? ? start = 95

? ? }

? ? loading.value = false

? ? return start

? }

}

const resetChartDrawing = () => {

? dataZoomY.value = null

? isDrawing.value = false

? tipData.value = null

}

const handleResize = () => {

? myChart.value.resize()

}

const handleMouseEnterMove = (params: any) => {

? const { offsetX, offsetY, target, topTarget } = params

? clientX.value = offsetX - 40

? clientY.value = offsetY + 18

? // 移至坐標軸外時target和topTarget都為undefined

? if (!target && !topTarget) {

? ? tipData.value = null

? ? initHoverData()

? }

}

// 點擊事件

const handleEchartsClick = (params: any) => {

? const pointInPixel = [params.offsetX, params.offsetY]

? if (myChart.value.containPixel('grid', pointInPixel)) {

? ? const pointInGrid = myChart.value.convertFromPixel(

? ? ? {

? ? ? ? seriesIndex: 0

? ? ? },

? ? ? pointInPixel

? ? )

? ? const xIndex = pointInGrid[0] // 索引

? ? const handleIndex = Number(xIndex)

? ? const seriesObj = myChart.value.getOption() // 圖表object對象

? }

}

onMounted(() => {

? nextTick(() => {

? ? init()

? })

})

onUnmounted(() => {

? window.removeEventListener('resize', handleResize, false)

})

</script>

<style lang="less" scoped>

.echart-tip {

? position: absolute;

? background-color: rgba(38, 43, 81, 0.5);

? font-size: 12px;

? line-height: 16px;

? padding: 5px;

? border-radius: 4px;

? color: #fff;

? z-index: 9;

? min-width: 130px;

? > p {

? ? padding: 0;

? ? margin: 0;

? }

}

</style>

四、useWebSocket.ts文件代碼:

const DEFAULT_OPTIONS = {

? url: '', // websocket url

? heartBeatData: '', // 你的心跳數據

? heartBeatInterval: 60 * 1000, // 心跳間隔,單位ms

? reconnectInterval: 5000, // 斷線重連間隔,單位ms

? maxReconnectAttempts: 10 // 最大重連次數

}

export const SocketStatus = {

? Connecting: '正在連接...', //表示正在連接,這是初始狀態。

? Connected: '連接已建立', //表示連接已經建立。

? Disconnecting: '連接正在關閉', //表示連接正在關閉。

? Disconnected: '連接已斷開' //表示連接已經關閉

}

const SocketCloseCode = 1000

export default function useWebSocket(options = {}) {

? const state = {

? ? options: { ...DEFAULT_OPTIONS, ...options },

? ? socket: null,

? ? reconnectAttempts: 0,

? ? reconnectTimeout: null,

? ? heartBetaSendTimer: null, // 心跳發送定時器

? ? heartBetaTimeoutTimer: null // 心跳超時定時器

? }

? // 連接狀態

? const status = ref(SocketStatus.Disconnected)

? const message = ref(null)

? const error = ref(null)

? // 連接

? const connect = () => {

? ? disconnect()

? ? status.value = SocketStatus.Connecting

? ? if (!window.navigator.onLine) {

? ? ? setTimeout(() => {

? ? ? ? status.value = SocketStatus.Disconnected

? ? ? }, 500)

? ? ? return

? ? }

? ? //@ts-ignore

? ? state.socket = new WebSocket(state.options.url) as WebSocket

? ? //@ts-ignore

? ? state.socket.onopen = (openEvent:any) => {

? ? ? // console.log('socket連接:', openEvent)

? ? ? state.reconnectAttempts = 0

? ? ? status.value = SocketStatus.Connected

? ? ? error.value = null

? ? ? startHeartBeat()

? ? }

? ? //@ts-ignore

? ? state.socket.onmessage = (msgEvent: any) => {

? ? ? // console.log('socket消息:', msgEvent)

? ? ? // 收到任何數據,重新開始心跳

? ? ? startHeartBeat()

? ? ? const { data } = msgEvent

? ? ? const msg = JSON.parse(data)

? ? ? //心跳數據, 可自行修改

? ? ? if (+msg.msg_id === 0) {

? ? ? ? return

? ? ? }

? ? ? message.value = msg

? ? }

? ? //@ts-ignore

? ? state.socket.onclose = (closeEvent: any) => {

? ? ? // console.log('socket關閉:', closeEvent)

? ? ? status.value = SocketStatus.Disconnected

? ? ? // 非正常關閉,嘗試重連

? ? ? if (closeEvent.code !== SocketCloseCode) {

? ? ? ? reconnect()

? ? ? }

? ? }

? ? //@ts-ignore

? ? state.socket.onerror = (errEvent: any) => {

? ? ? // console.log('socket報錯:', errEvent)

? ? ? status.value = SocketStatus.Disconnected

? ? ? error.value = errEvent

? ? ? // 連接失敗,嘗試重連

? ? ? reconnect()

? ? }

? }

? const disconnect = () => {

? ? //@ts-ignore

? ? if (state.socket && (state.socket.OPEN || state.socket.CONNECTING)) {

? ? ? // console.log('socket斷開連接')

? ? ? status.value = SocketStatus.Disconnecting

? ? ? //@ts-ignore

? ? ? state.socket.onmessage = null

? ? ? //@ts-ignore

? ? ? state.socket.onerror = null

? ? ? //@ts-ignore

? ? ? state.socket.onclose = null

? ? ? // 發送關閉幀給服務端

? ? ? //@ts-ignore

? ? ? state.socket.close(SocketCloseCode, 'normal closure')

? ? ? status.value = SocketStatus.Disconnected

? ? ? state.socket = null

? ? }

? ? stopHeartBeat()

? ? stopReconnect()

? }

? const startHeartBeat = () => {

? ? stopHeartBeat()

? ? onHeartBeat(() => {

? ? ? if (status.value === SocketStatus.Connected) {

? ? ? ? //@ts-ignore

? ? ? ? state.socket.send(state.options.heartBeatData)

? ? ? ? // console.log('socket心跳發送:', state.options.heartBeatData)

? ? ? }

? ? })

? }

? const onHeartBeat = (callback: any) => {

? ? //@ts-ignore

? ? state.heartBetaSendTimer = setTimeout(() => {

? ? ? callback && callback()

? ? ? //@ts-ignore

? ? ? state.heartBetaTimeoutTimer = setTimeout(() => {

? ? ? ? // 心跳超時,直接關閉socket,拋出自定義code=4444, onclose里進行重連

? ? ? ? //@ts-ignore

? ? ? ? state.socket.close(4444, 'heart timeout')

? ? ? }, state.options.heartBeatInterval)

? ? }, state.options.heartBeatInterval)

? }

? const stopHeartBeat = () => {

? ? state.heartBetaSendTimer && clearTimeout(state.heartBetaSendTimer)

? ? state.heartBetaTimeoutTimer && clearTimeout(state.heartBetaTimeoutTimer)

? }

? // 重連

? const reconnect = () => {

? ? if (status.value === SocketStatus.Connected || status.value === SocketStatus.Connecting) {

? ? ? return

? ? }

? ? stopHeartBeat()

? ? if (state.reconnectAttempts < state.options.maxReconnectAttempts) {

? ? ? // console.log('socket重連:', state.reconnectAttempts)

? ? ? // 重連間隔,5秒起步,下次遞增1秒

? ? ? const interval = Math.max(state.options.reconnectInterval, state.reconnectAttempts * 1000)

? ? ? // console.log('間隔時間:', interval)

? ? ? //@ts-ignore

? ? ? state.reconnectTimeout = setTimeout(() => {

? ? ? ? if (status.value !== SocketStatus.Connected && status.value !== SocketStatus.Connecting) {

? ? ? ? ? connect()

? ? ? ? }

? ? ? }, interval)

? ? ? state.reconnectAttempts += 1

? ? } else {

? ? ? status.value = SocketStatus.Disconnected

? ? ? stopReconnect()

? ? }

? }

? // 停止重連

? const stopReconnect = () => {

? ? state.reconnectTimeout && clearTimeout(state.reconnectTimeout)

? }

? return {

? ? status,

? ? message,

? ? error,

? ? connect,

? ? disconnect

? }

}

五、common.ts文件代碼:

// import XLSX from 'xlsx';

import CST from './constant'

import { toDecimal } from './numberFormat'

const common = {

? addDate(date: any, days: any) {

? ? if (days == undefined || days == '') {

? ? ? days = 1

? ? }

? ? // var date = new Date(date)

? ? date.setDate(date.getDate() + days)

? ? const month = date.getMonth() + 1

? ? const day = date.getDate()

? ? return (

? ? ? date.getFullYear() +

? ? ? '/' +

? ? ? this.getFormatDate(month) +

? ? ? '/' +

? ? ? this.getFormatDate(day)

? ? )

? },

? // 小數相減精確算法

? numSub(data1: any, data2: any) {

? ? let num = 0

? ? let num1 = 0

? ? let num2 = 0

? ? let precision = 0 // 精度

? ? try {

? ? ? num1 = data1.toString().split('.')[1].length

? ? } catch (e) {

? ? ? num1 = 0

? ? }

? ? try {

? ? ? num2 = data2.toString().split('.')[1].length

? ? } catch (e) {

? ? ? num2 = 0

? ? }

? ? num = Math.pow(10, Math.max(num1, num2))

? ? precision = num1 >= num2 ? num1 : num2

? ? return ((data1 * num - data2 * num) / num).toFixed(precision)

? },

? // 日期月份/天的顯示,如果是1位數,則在前面加上'0'

? getFormatDate(arg: any) {

? ? if (arg == undefined || arg == '') {

? ? ? return ''

? ? }

? ? let re = arg + ''

? ? if (re.length < 2) {

? ? ? re = '0' + re

? ? }

? ? return re

? },

? isArray: function (obj: any) {

? ? return Object.prototype.toString.call(obj) === '[object Array]'

? },

? isEmpty(obj: any) {

? ? obj = obj + ''

? ? if (

? ? ? typeof obj === 'undefined' ||

? ? ? obj == null ||

? ? ? obj.replace(/(^\s*)|(\s*$)/g, '') === ''

? ? ) {

? ? ? return true

? ? } else {

? ? ? return false

? ? }

? },

? // 小數相加精確算法

? numAdd(arg1: any, arg2: any) {

? ? let r1 = 0

? ? let r2 = 0

? ? let r3 = 0

? ? try {

? ? ? r1 = (arg1 + '').split('.')[1].length

? ? } catch (err) {

? ? ? r1 = 0

? ? }

? ? try {

? ? ? r2 = (arg2 + '').split('.')[1].length

? ? } catch (err) {

? ? ? r2 = 0

? ? }

? ? r3 = Math.pow(10, Math.max(r1, r2))

? ? return (this.numMul(arg1, r3) + this.numMul(arg2, r3)) / r3

? },

? // 判斷小數位數

? getDecLen(value: number) {

? ? if (!value) {

? ? ? return 0

? ? }

? ? const strVal = value.toString()

? ? if (!strVal.includes('.')) {

? ? ? return 0

? ? }

? ? return strVal.split('.')[1].length

? },

? // 兩數相除

? accDiv(num1: any, num2: any) {

? ? let t1, t2

? ? try {

? ? ? t1 = num1.toString().split('.')[1].length

? ? } catch (e) {

? ? ? t1 = 0

? ? }

? ? try {

? ? ? t2 = num2.toString().split('.')[1].length

? ? } catch (e) {

? ? ? t2 = 0

? ? }

? ? const r1 = Number(num1.toString().replace('.', ''))

? ? const r2 = Number(num2.toString().replace('.', ''))

? ? return (r1 / r2) * Math.pow(10, t2 - t1)

? },

? formatDate: function (date: any, format: any) {

? ? let v = ''

? ? if (typeof date === 'string' || typeof date !== 'object') {

? ? ? return

? ? }

? ? const year = date.getFullYear()

? ? const month = date.getMonth() + 1

? ? const day = date.getDate()

? ? const hour = date.getHours()

? ? const minute = date.getMinutes()

? ? const second = date.getSeconds()

? ? const weekDay = date.getDay()

? ? const ms = date.getMilliseconds()

? ? let weekDayString = ''

? ? if (weekDay === 1) {

? ? ? weekDayString = '星期一'

? ? } else if (weekDay === 2) {

? ? ? weekDayString = '星期二'

? ? } else if (weekDay === 3) {

? ? ? weekDayString = '星期三'

? ? } else if (weekDay === 4) {

? ? ? weekDayString = '星期四'

? ? } else if (weekDay === 5) {

? ? ? weekDayString = '星期五'

? ? } else if (weekDay === 6) {

? ? ? weekDayString = '星期六'

? ? } else if (weekDay === 0) {

? ? ? weekDayString = '星期日'

? ? }

? ? v = format

? ? // Year

? ? v = v.replace(/yyyy/g, year)

? ? v = v.replace(/YYYY/g, year)

? ? v = v.replace(/yy/g, (year + '').substring(2, 4))

? ? v = v.replace(/YY/g, (year + '').substring(2, 4))

? ? // Month

? ? const monthStr = '0' + month

? ? v = v.replace(/MM/g, monthStr.substring(monthStr.length - 2))

? ? // Day

? ? const dayStr = '0' + day

? ? v = v.replace(/dd/g, dayStr.substring(dayStr.length - 2))

? ? // hour

? ? const hourStr = '0' + hour

? ? v = v.replace(/HH/g, hourStr.substring(hourStr.length - 2))

? ? v = v.replace(/hh/g, hourStr.substring(hourStr.length - 2))

? ? // minute

? ? const minuteStr = '0' + minute

? ? v = v.replace(/mm/g, minuteStr.substring(minuteStr.length - 2))

? ? // Millisecond

? ? v = v.replace(/sss/g, ms)

? ? v = v.replace(/SSS/g, ms)

? ? // second

? ? const secondStr = '0' + second

? ? v = v.replace(/ss/g, secondStr.substring(secondStr.length - 2))

? ? v = v.replace(/SS/g, secondStr.substring(secondStr.length - 2))

? ? // weekDay

? ? v = v.replace(/E/g, weekDayString)

? ? return v

? },

? /**

? ?* 判斷是否同周,輸入時間date1小于date2

? ?* @param {*} date1

? ?* @param {*} date2

? ?*/

? isSameWeek: function (date1: any, date2: any) {

? ? const day1 = new Date(date1).getDay() == 0 ? 7 : new Date(date1).getDay()

? ? const day2 = new Date(date2).getDay() == 0 ? 7 : new Date(date2).getDay()

? ? const time1 = new Date(date1).getTime()

? ? const time2 = new Date(date2).getTime()

? ? if (day1 >= day2) {

? ? ? return false

? ? } else {

? ? ? return time2 - time1 < 7 * 24 * 3600 * 1000

? ? }

? },

? getUrlKey: function (name: any) {

? ? // eslint-disable-next-line no-sparse-arrays

? ? return (

? ? ? decodeURIComponent(

? ? ? ? //@ts-ignore

? ? ? ? (new RegExp('[?|&]' + name + '=' + '([^&;]+?)(&|#|;|$)').exec(

? ? ? ? ? location.href

? ? ? ? ? // eslint-disable-next-line no-sparse-arrays

? ? ? ? ) || [, ''])[1].replace(/\+/g, '%20')

? ? ? ) || null

? ? )

? },

? getUrlParam: function (name: any) {

? ? const reg = new RegExp('(^|&)' + name + '=([^&]*)(&|$)')

? ? const r = window.location.search.substr(1).match(reg)

? ? if (r != null) return unescape(r[2])

? ? return null

? },

? setPorpsReadonly(props: any) {

? ? for (const col in props) {

? ? ? if (props[col].Columns && common.isArray(props[col].Columns)) {

? ? ? ? props[col].require = 'false'

? ? ? ? props[col].isImport = 'false'

? ? ? ? props[col].ReadOnly = 'true'

? ? ? ? props[col].Columns.forEach((e: any) => {

? ? ? ? ? e.readonly = 'true'

? ? ? ? })

? ? ? } else {

? ? ? ? for (const co in props[col]) {

? ? ? ? ? props[col][co].readonly = 'true'

? ? ? ? }

? ? ? }

? ? }

? ? return props

? },

? // 根據表單里的 oldinstanceid 判斷是否是非首次報備的單

? isFirstFormByOldInstanceId(value: any, instanceId: any) {

? ? let isfirst = true

? ? instanceId = instanceId + ''

? ? for (const col in value) {

? ? ? if (!common.isArray(value[col])) {

? ? ? ? if (value[col].oldflowinstanceid) {

? ? ? ? ? if (

? ? ? ? ? ? value[col].oldflowinstanceid !== '' &&

? ? ? ? ? ? value[col].oldflowinstanceid !== instanceId

? ? ? ? ? ) {

? ? ? ? ? ? isfirst = false

? ? ? ? ? ? break

? ? ? ? ? }

? ? ? ? }

? ? ? }

? ? }

? ? return isfirst

? },

? setPropNotFrist(props: any) {

? ? for (const col in props) {

? ? ? // eslint-disable-next-line no-empty

? ? ? if (props[col].Columns && common.isArray(props[col].Columns)) {

? ? ? } else {

? ? ? ? for (const co in props[col]) {

? ? ? ? ? if (props[col][co].objectupdate !== 'true') {

? ? ? ? ? ? props[col][co].readonly = 'true'

? ? ? ? ? }

? ? ? ? }

? ? ? }

? ? }

? ? return props

? },

? /**

? ?* 精確乘

? ?* @param arg1

? ?* @param arg2

? ?* @returns {number}

? ?*/

? numMul(arg1: any, arg2: any) {

? ? const r1 = arg1 + ''

? ? const r2 = arg2 + ''

? ? let r3 = 0

? ? let r4 = 0

? ? try {

? ? ? r3 = r1.split('.')[1].length

? ? } catch (err) {

? ? ? r3 = 0

? ? }

? ? try {

? ? ? r4 = r2.split('.')[1].length

? ? } catch (err) {

? ? ? r4 = 0

? ? }

? ? return (

? ? ? (Number(r1.replace('.', '')) * Number(r2.replace('.', ''))) /

? ? ? Math.pow(10, r4 + r3)

? ? )

? },

? /**

? ?* 精確除

? ?* @param arg1

? ?* @param arg2

? ?* @returns {number}

? ?*/

? numDiv(arg1: any, arg2: any) {

? ? const r1 = arg1 + ''

? ? const r2 = arg2 + ''

? ? let r3 = 0

? ? let r4 = 0

? ? try {

? ? ? r3 = r1.split('.')[1].length

? ? } catch (err) {

? ? ? r3 = 0

? ? }

? ? try {

? ? ? r4 = r2.split('.')[1].length

? ? } catch (err) {

? ? ? r4 = 0

? ? }

? ? return this.numMul(

? ? ? Number(r1.replace('.', '')) / Number(r2.replace('.', '')),

? ? ? Math.pow(10, r4 - r3)

? ? )

? },

? /**

? ?* 精確取余

? ?* @param arg1

? ?* @param arg2

? ?* @returns {number}

? ?*/

? numRem(arg1: any, arg2: any) {

? ? let r1 = 0

? ? let r2 = 0

? ? let r3 = 0

? ? try {

? ? ? r1 = (arg1 + '').split('.')[1].length

? ? } catch (err) {

? ? ? r1 = 0

? ? }

? ? try {

? ? ? r2 = (arg2 + '').split('.')[1].length

? ? } catch (err) {

? ? ? r2 = 0

? ? }

? ? r3 = Math.pow(10, Math.max(r1, r2))

? ? return (this.numMul(arg1, r3) % this.numMul(arg2, r3)) / r3

? },

? formatNumUnit(value_: any) {

? ? const value = Math.abs(value_) // 1

? ? const newValue = ['', '', '']

? ? let fr = 1000

? ? let num = 3

? ? let fm = 1

? ? while (value / fr >= 1) {

? ? ? fr *= 10

? ? ? num += 1

? ? }

? ? if (num <= 4) {

? ? ? // 千

? ? ? newValue[0] = value + ''

? ? } else if (num <= 8) {

? ? ? // 萬

? ? ? fm = 10000

? ? ? if (value % fm === 0) {

? ? ? ? //@ts-ignore

? ? ? ? newValue[0] = parseInt(value / fm) + ''

? ? ? } else {

? ? ? ? //@ts-ignore

? ? ? ? newValue[0] = parseFloat(value / fm).toFixed(2) + ''

? ? ? }

? ? ? // newValue[1] = text1

? ? ? newValue[1] = '萬'

? ? } else if (num <= 16) {

? ? ? // 億

? ? ? fm = 100000000

? ? ? if (value % fm === 0) {

? ? ? ? //@ts-ignore

? ? ? ? newValue[0] = parseInt(value / fm) + ''

? ? ? } else {

? ? ? ? //@ts-ignore

? ? ? ? newValue[0] = parseFloat(value / fm).toFixed(2) + ''

? ? ? }

? ? ? newValue[1] = '億'

? ? }

? ? if (value < 1000) {

? ? ? newValue[0] = value + ''

? ? ? newValue[1] = ''

? ? }

? ? let text = newValue.join('')

? ? if (value_ < 0) {

? ? ? text = '-' + text

? ? }

? ? return text

? },

? // 獲取行情小數位數(最新價、漲跌、買價、賣價)

? getTickDecLen(securityType: any, market: any, plateID: any) {

? ? // 滬深A股 -> 2

? ? if (securityType == CST.SecurityType.Stock) {

? ? ? return 2

? ? }

? ? // 基金 -> 3

? ? if (securityType == CST.SecurityType.Fund) {

? ? ? return 3

? ? }

? ? // 債券 -> 上海市場除國債逆回購,小數點后保留2位小數,國債逆回購3位小數;深圳市場保留3位小數

? ? if (securityType == CST.SecurityType.Bond) {

? ? ? // 深圳市場

? ? ? if (market == CST.Market.SZSE) {

? ? ? ? return 3

? ? ? }

? ? ? // 上海市場

? ? ? if (market == CST.Market.SSE) {

? ? ? ? // 國債逆回購

? ? ? ? if (plateID == CST.PlateID.ZQHG_Bond) {

? ? ? ? ? return 3

? ? ? ? }

? ? ? ? return 3

? ? ? }

? ? }

? ? return 2

? },

? // 轉換成交量單位

? cvtVolumeUnit(volume: any, market: any, securityType: any) {

? ? // 深圳市場

? ? if (market == CST.Market.SZSE) {

? ? ? // 股票、基金、指數

? ? ? if (

? ? ? ? securityType == CST.SecurityType.Stock ||

? ? ? ? securityType == CST.SecurityType.Fund ||

? ? ? ? securityType == CST.SecurityType.Index

? ? ? ) {

? ? ? ? return volume / 100

? ? ? }

? ? ? // 債券

? ? ? if (securityType == CST.SecurityType.Bond) {

? ? ? ? return volume / 10

? ? ? }

? ? }

? ? // 上海市場

? ? if (market == CST.Market.SSE) {

? ? ? // 股票、基金、指數

? ? ? if (

? ? ? ? securityType == CST.SecurityType.Stock ||

? ? ? ? securityType == CST.SecurityType.Fund

? ? ? ) {

? ? ? ? return volume / 100

? ? ? }

? ? }

? ? // 北交所

? ? if (market == CST.Market.BSE) {

? ? ? // 北交所暫不做處理,后臺轉換

? ? ? return volume

? ? }

? ? return volume

? },

? // 千分位 保留digit位 isround四舍五入

? money(value: any, digit = 2, isRround = true) {

? ? let v = toDecimal(value, digit, isRround)

? ? if (v.indexOf(',') == -1) {

? ? ? v = v.replace(/(\d)(?=(\d{3})+(?!\d))/g, '$1,')

? ? }

? ? return v

? },

? //校驗輸入是否僅包含數字和字母

? isValidAlphanumeric(input: string) {

? ? const alphanumericPattern = /^[a-zA-Z0-9]+$/

? ? return alphanumericPattern.test(input)

? },

? //長度至少為6個字符,必須包含大寫字母、小寫字母、數字,不能包含特殊字符和漢字

? isValidPassword(password: string) {

? ? const passwordPattern = /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)[a-zA-Z\d]{6,}$/

? ? return passwordPattern.test(password)

? },

? //驗證手機號碼

? isValidPhoneNumber(phoneNumber: string) {

? ? const phonePattern = /^1[3-9]\d{9}$/

? ? return phonePattern.test(phoneNumber)

? },

? //中文姓名,不超過5個漢字,不包含任何特殊字符或數字

? isValidChineseName(name: string) {

? ? const namePattern = /^[\u4e00-\u9fff]{1,5}$/

? ? return namePattern.test(name)

? }

}

export default common

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/62972.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/62972.shtml
英文地址,請注明出處:http://en.pswp.cn/web/62972.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

用python的flask寫的一個MQTT中轉功能,http的方式發送數據和接收數據

需求背景 給一個客戶對接人臉識別的設備&#xff0c;最后需要通知服務端進行一些消息推送。 簡單例子 # 作者 陳老師 # https://v.iiar.cn import json import paho.mqtt.client as mqtt import requests from flask import Flask, requestapp Flask(__name__)# MQTT配置 mq…

ASP.NET |日常開發中讀寫XML詳解

ASP.NET &#xff5c;日常開發中讀寫XML詳解 前言一、XML 概述1.1 定義和結構1.2 應用場景 二、讀取 XML 文件2.1 使用XmlDocument類&#xff08;DOM 方式&#xff09;2.2 使用XmlReader類&#xff08;流方式&#xff09; 三、寫入 XML 文件3.1 使用XmlDocument類3.2 使用XmlWr…

分布式 Paxos算法 總結

前言 相關系列 《分布式 & 目錄》《分布式 & Paxos算法 & 總結》《分布式 & Paxos算法 & 問題》 參考文獻 《圖解超難理解的 Paxos 算法&#xff08;含偽代碼&#xff09;》《【超詳細】分布式一致性協議 - Paxos》 Basic-Paxos 基礎帕克索斯算法…

Git-基礎操作命令

目錄 Git基礎操作命令 case *查看提交日志 log 版本回退 get add . Git基礎操作命令 我們創建并且初始化這個倉庫以后&#xff0c;我們就要在里面進行操作。 Git 對于文件的增刪改查存在幾個狀態&#xff0c;這些修改狀態會隨著我們執行Git的命令而發生變化。 untracked、…

Spring Boot 實戰:構建一個社交平臺 API

在這篇博客中&#xff0c;我們將繼續深入 Spring Boot 的開發實踐&#xff0c;通過構建一個簡單的社交平臺 API&#xff0c;幫助大家理解如何使用 Spring Boot 高效地開發一個具有注冊、登錄、個人資料管理、帖子發布與評論、點贊等功能的社交平臺。在開發過程中&#xff0c;我…

配置mysqld(讀取選項內容,基本配置),數據目錄(配置的必要性,目錄下的內容,具體文件介紹,修改配置)

目錄 配置mysqld 讀取選項內容 介紹 啟動腳本 基本配置 內容 端口號 數據目錄的路徑 配置的必要性 配置路徑 mysql數據目錄 具體文件 修改配置時 權限問題 配置mysqld 讀取選項內容 介紹 會從[mysqld] / [server] 節點中讀取選項內容 優先讀取[server] 雖然服務…

智能家居WTR096-16S錄放音芯片方案,實現語音播報提示及錄音留言功能

前言&#xff1a; 在當今社會的高速運轉之下&#xff0c;夜幕低垂之時&#xff0c;許多辛勤工作的父母尚未歸家。對于肩負家庭責任的他們而言&#xff0c;確保孩童按時用餐與居家安全成為心頭大事。此時&#xff0c;家居留言錄音提示功能應運而生&#xff0c;恰似家中的一位無形…

Java 編程基礎:開啟編程世界的大門

一、Java 環境搭建 在開始編寫 Java 代碼之前&#xff0c;我們需要先搭建 Java 開發環境。 1. 安裝 JDK&#xff08;Java Development Kit&#xff09; JDK 是 Java 開發的核心工具包&#xff0c;它包含了編譯 Java 源文件所需的編譯器&#xff08;javac&#xff09;以及運行…

pytorch bilstm crf的教程,注意 這里不支持批處理,要支持批處理 用torchcrf這個。

### Bi-LSTM Conditional Random Field ### pytorch tutorials https://pytorch.org/tutorials/beginner/nlp/advanced_tutorial.html ### 模型主要結構&#xff1a; ![title](sources/bilstm.png) pytorch bilstm crf的教程&#xff0c;注意 這里不支持批處理 Python version…

【SickOs1.1靶場滲透】

文章目錄 一、基礎信息 二、信息收集 三、反彈shell 四、提權 一、基礎信息 Kali IP&#xff1a;192.168.20.146 靶機IP&#xff1a;192.168.20.150 二、信息收集 端口掃描 nmap -sS -sV -p- -A 192.168.20.150 開放了22、3128端口&#xff0c;8080端口顯示關閉 22端…

【HF設計模式】03-裝飾者模式

聲明&#xff1a;僅為個人學習總結&#xff0c;還請批判性查看&#xff0c;如有不同觀點&#xff0c;歡迎交流。 摘要 《Head First設計模式》第3章筆記&#xff1a;結合示例應用和代碼&#xff0c;介紹裝飾者模式&#xff0c;包括遇到的問題、遵循的 OO 原則、達到的效果。 …

Mysql數據庫中,什么情況下設置了索引但無法使用?

在MySQL數據庫中&#xff0c;即使已經正確設置了索引&#xff0c;但在某些情況下索引可能無法被使用。 以下是一些常見的情況&#xff1a; 1. 數據分布不均勻 當某個列的數據分布非常不均勻時&#xff0c;索引可能無法有效地過濾掉大部分的數據&#xff0c;導致索引失效。 …

秒殺業務中的庫存扣減為什么不加分布式鎖?

前言 說到秒殺業務的庫存扣減&#xff0c;就還是得先確認我們的扣減基本方案。 秒殺場景的庫存扣減方案 一般的做法是&#xff0c;先在Redis中做扣減&#xff0c;然后發送一個MQ消息&#xff0c;消費者在接到消息之后做數據庫中庫存的真正扣減及業務邏輯操作。 如何解決數據…

ChatGPT生成測試用例的最佳實踐(一)

前面介紹的案例主要展示了ChatGPT在功能、安全和性能測試用例生成方面的應用和成果。通過ChatGPT生成測試用例&#xff0c;測試團隊不僅可以提升工作效率&#xff0c;還可以加快測試工作的速度&#xff0c;盡早發現被測系統中的問題。問題及早發現有助于提高軟件的質量和用戶滿…

基于Redis實現令牌桶算法

基于Redis實現令牌桶算法 令牌桶算法算法流程圖優點缺點 實現其它限流算法 令牌桶算法 令牌桶是一種用于分組交換和電信網絡的算法。它可用于檢查數據包形式的數據傳輸是否符合定義的帶寬和突發性限制&#xff08;流量不均勻或變化的衡量標準&#xff09;。它還可以用作調度算…

操作系統(8)死鎖

一、概念 死鎖是指在一個進程集合中的每個進程都在等待只能由該集合中的其他進程才能引起的事件&#xff0c;而無限期地僵持下去的局面。在多任務環境中&#xff0c;由于資源分配不當&#xff0c;導致兩個或多個進程在等待對方釋放資源時陷入無限等待的狀態&#xff0c;這就是死…

Micropython 擴展C模塊<HelloWorld>

開發環境 MCU&#xff1a;Pico1&#xff08;無wifi版&#xff09;使用固件&#xff1a;自編譯版本開發環境&#xff1a;MacBook Pro Sonoma 14.5開發工具&#xff1a;Thonny 4.1.6開發語言&#xff1a;MicroPython 1.24 執行示例 在github上獲取micropython&#xff0c;我使…

并查集基礎

abstract 并查集&#xff08;Union-Find Set&#xff09;是一種數據結構&#xff0c;主要用于處理動態連通性問題&#xff08;Dynamic Connectivity Problem&#xff09;&#xff0c;例如在圖論中判斷兩點是否屬于同一個連通分量&#xff0c;以及動態地合并集合。 它廣泛應用…

CloudberryDB(一)安裝部署多節點分布式數據庫集群

CloudberryDB&#xff1a; 一個 Greenplum Database 分布式數據庫開源版本的衍生項目&#xff0c; 針對開源 Greenplum Database 優化的地方&#xff0c; CloudberryDB制定了路線圖&#xff08;https://github.com/orgs/cloudberrydb/discussions/369&#xff09;并在逐步改…

解決Logitech G hub 無法進入一直轉圈的方案(2024.12)

如果你不是最新版本無法加載嘗試以下方案&#xff1a;刪除AppData 文件夾下的logihub文件夾 具體路徑&#xff1a;用戶名根據實際你的請情況修改 C:\Users\Administrator\AppData\Local 如果你有通過lua編譯腳本&#xff0c;記得備份&#xff01;&#xff01; ↓如果你是最新…