liveh5-nuxt/app/stores/live/index.js

270 lines
7.9 KiB
JavaScript
Raw Normal View History

import {createGlobalState, useLocalStorage} from '@vueuse/core'
import {ref} from "vue";
import {goodStore} from "@/stores/goods/index.js";
import {authStore} from "@/stores/auth/index.js";
import {message} from "~/components/x-message/useMessage.js";
import { WebSocketClient } from '@/utils/websocket'
import {logSendlog} from "~/api/goods/index.js";
import CryptoJS from "crypto-js";
import {useI18n} from 'vue-i18n'
export const liveStore = createGlobalState(() => {
const {auctionDetail} = goodStore();
const { token } = authStore()
const t=useI18n().t
const fullLive = ref(false)
const quoteStatus = ref(false)
const show = ref(false)
const cleanup = ref(null)
const show1=ref(false)
const playerId=ref('J_prismPlayer')
const auctionData=useLocalStorage('auctionData',{})
const socket=ref(null)
const config = useRuntimeConfig()
const pullLink=ref('')
const isMinWindow = ref(false)
const lastSnapshot = ref('')
const liveInfo = ref(null)
// 设置最小化状态
const setMinWindow = (status) => {
isMinWindow.value = status
}
// 设置截图
const setSnapshot = (snapshot) => {
lastSnapshot.value = snapshot
}
// 设置直播信息
const setLiveInfo = (info) => {
liveInfo.value = info
}
// 重置状态
const reset = () => {
isMinWindow.value = false
lastSnapshot.value = ''
liveInfo.value = null
}
// 解密工具函数
const decryptUtils = {
// 解密配置
cryptConfig: {
password: 'live-skkoql-1239-key',
salt: 'aldk100128ls',
iterations: 10000,
keySize: 32
},
// 生成密钥
generateKey(password, salt, iterations, keySize) {
return CryptoJS.PBKDF2(password, salt, {
keySize: keySize / 4,
iterations: iterations,
hasher: CryptoJS.algo.SHA1
}).toString(CryptoJS.enc.Hex)
},
// AES解密
decrypt(ciphertextBase64, key) {
const combined = CryptoJS.enc.Base64.parse(ciphertextBase64)
const iv = CryptoJS.lib.WordArray.create(combined.words.slice(0, 4))
const ciphertext = CryptoJS.lib.WordArray.create(combined.words.slice(4))
const decrypted = CryptoJS.AES.decrypt(
{ciphertext: ciphertext},
CryptoJS.enc.Hex.parse(key),
{
iv: iv,
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.Pkcs7
}
)
return decrypted.toString(CryptoJS.enc.Utf8)
},
// 解密数据的主函数
decryptData(encryptedData) {
const keyDerived = this.generateKey(
this.cryptConfig.password,
this.cryptConfig.salt,
this.cryptConfig.iterations,
this.cryptConfig.keySize
)
return this.decrypt(encryptedData, keyDerived)
}
}
const getLiveLink = () => {
return new Promise(async(resolve, reject) => {
const res = await logSendlog({
uuid: auctionDetail.value.uuid
})
if (res.status===0){
pullLink.value=decryptUtils.decryptData(res.data.code)
resolve(decryptUtils.decryptData(res.data.code))
}
})
}
// 定义常量
const WS_TYPES = {
TIP: 'tip',
STOP_ARTWORK: 'stopArtwork',
OVER: 'over',
CHANGEPRICEINFO : 'changePriceInfo'
}
const TIP_TYPES = {
FALLING: 'falling',
OTHERS_BID: 'othersBid',
SUCCESS_BID: 'successBid',
ARTWORK_OVER: 'artworkOver',
FAIL_BID: 'failBid'
}
// 基础消息配置
const BASE_MESSAGE_STYLE = {
width: '151px',
bottom: '265px'
}
const createMessageConfig = (text, color, subText = '', extraStyle = {}) => ({
title: {
text,
color,
align: 'center'
},
icon: false,
...(subText && {
subTitle: {
text: subText,
color: '#939393',
align: 'center'
}
}),
style: {
...BASE_MESSAGE_STYLE,
...extraStyle
}
})
const wsClient=ref(null)
const getSocketData = async () => {
wsClient.value = new WebSocketClient(
config.public.NUXT_PUBLIC_SOCKET_URL,
token.value
)
const ws = wsClient.value.connect('/api/v1/m/auction/live', {
auctionUuid: auctionDetail.value.uuid,
})
// 处理消息提示
const handleTipMessage = (tipType) => {
const tipConfigs = {
[TIP_TYPES.FALLING]: () =>
message.warning(createMessageConfig(t('live_room.text1'), '#F09F1F')),
[TIP_TYPES.OTHERS_BID]: () =>
message.error(createMessageConfig(t('live_room.text2'), '#CF3050', t('live_room.text3'))),
2025-02-26 03:11:29 +00:00
[TIP_TYPES.SUCCESS_BID]: ()=>{
quoteStatus.value=false
message.success(createMessageConfig(t('live_room.text4'), '#18A058', t('live_room.text5')))
},
[TIP_TYPES.ARTWORK_OVER]: () =>{
quoteStatus.value=false
message.success(createMessageConfig(
t('live_room.text6'),
'#575757',
t('live_room.text7'),
{ backgroundColor: '#fff', borderColor: '#fff' }
2025-02-26 03:11:29 +00:00
))
},
[TIP_TYPES.FAIL_BID]: () =>
message.error(createMessageConfig(
t('live_room.text8'),
'#CF3050',
t('live_room.text9'),
{ width: '186px' }
))
}
const handler = tipConfigs[tipType]
if (handler) handler()
}
// WebSocket 事件处理
ws.onOpen(() => {
2025-03-02 03:09:06 +00:00
})
ws.onMessage((data) => {
auctionData.value = data.data
const { wsType, tip } = data.data || {}
switch (wsType) {
case WS_TYPES.TIP:
handleTipMessage(tip?.tipType)
break
case WS_TYPES.STOP_ARTWORK:
//quoteStatus.value = false
break
case WS_TYPES.OVER:
2025-02-26 06:47:55 +00:00
quoteStatus.value = false
message.success(createMessageConfig(
t('live_room.text10'),
'#575757',
'',
{
width: '195px',
backgroundColor: '#fff',
borderColor: '#fff'
}
))
break
case WS_TYPES.CHANGEPRICEINFO:
message.warning(t('live_room.text11'))
break
}
2025-03-02 03:09:06 +00:00
})
ws.onClose(() => {
2025-03-02 03:09:06 +00:00
})
ws.onError((error) => {
2025-03-02 03:09:06 +00:00
})
}
const changeStatus = () => {
if (auctionData.value.artwork?.isSelling&&!auctionData.value.artwork.isSoled){
quoteStatus.value = !quoteStatus.value
}else {
if (quoteStatus.value){
quoteStatus.value = false
}
}
2025-03-02 03:09:06 +00:00
}
return{
wsClient,
fullLive,
isMinWindow,
lastSnapshot,
liveInfo,
setMinWindow,
setSnapshot,
setLiveInfo,
reset,
pullLink,
getLiveLink,
auctionData,
getSocketData,
show1,
playerId,
show,
quoteStatus,
changeStatus
}
})