import { createGlobalState } 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"; export const liveStore = createGlobalState(() => { const {auctionDetail} = goodStore(); const { token } = authStore() 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=ref({}) 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 getSocketData = async () => { const wsClient = new WebSocketClient( config.public.NUXT_PUBLIC_SOCKET_URL, token.value ) const ws = wsClient.connect('/api/v1/m/auction/live', { auctionUuid: auctionDetail.value.uuid, }) ws.onOpen(() => { console.log('WebSocket connected') }) ws.onMessage((data) => { auctionData.value = data.data if (data.data?.wsType === 'tip' ) { if (data.data?.tip?.tipType === 'falling'){ message.warning({ title: { text: '即将落槌', color: '#F09F1F', align: 'center', }, style: { width: '151px', bottom: '230px', }, }) }else if (data.data?.tip?.tipType === 'othersBid'){ message.error({ title: { text: '已有人出价', color: '#CF3050', align: 'center', }, icon:false, subTitle:{ text:'更新后再出价', color: '#939393', align: 'center', }, style: { width: '151px', bottom: '230px' }, }) }else if (data.data?.tip?.tipType === 'successBid'){ message.success({ title: { text: '恭喜您,竞拍成功', color: '#18A058', align: 'center', }, icon:false, subTitle:{ text:'请缴款', color: '#939393', align: 'center', }, style: { width: '151px', bottom: '230px' }, }) }else if (data.data?.tip?.tipType === 'artworkOver'){ message.success({ title: { text: '本拍品已结束', color: '#575757', align: 'center', }, icon:false, subTitle:{ text:'请期待下个拍品', color: '#939393', align: 'center', }, style: { width: '151px', bottom: '230px', backgroundColor: '#fff', borderColor:'#fff' }, }) }else if (data.data?.tip?.tipType === 'failBid'){ message.error({ title: { text: '很遗憾,竞拍失败', color: '#CF3050', align: 'center', }, icon:false, subTitle:{ text:'竞拍结束', color: '#939393', align: 'center', }, style: { width: '186px', bottom: '230px' }, }) } }else if (data.data?.wsType==='stopArtwor'){ quoteStatus.value=false }else if (data.data?.wsType==='over'){ message.success({ title: { text: '竞拍结束,谢谢参与', color: '#575757', align: 'center', }, icon:false, style: { width: '195px', bottom: '230px', backgroundColor: '#fff', borderColor:'#fff' }, }) } console.log('onmessage', data) }) ws.onClose(() => { console.log('WebSocket disconnected') }) ws.onError((error) => { console.error('WebSocket error:', error) }) } const changeStatus = () => { if (auctionData.value.artwork.isSelling&&!auctionData.value.artwork.isSoled){ quoteStatus.value = true }else { if (quoteStatus.value){ quoteStatus.value = false } } } return{ fullLive, isMinWindow, lastSnapshot, liveInfo, setMinWindow, setSnapshot, setLiveInfo, reset, pullLink, getLiveLink, auctionData, getSocketData, show1, playerId, show, quoteStatus, changeStatus } })