diff --git a/config/dev.js b/config/dev.js index aee4976..458f723 100644 --- a/config/dev.js +++ b/config/dev.js @@ -10,8 +10,8 @@ module.exports = { h5: { devServer: { //host: '0.0.0.0', - // host: '192.168.110.53', - host: '192.168.3.103', + host: '192.168.110.53', + // host: '192.168.3.103', port: 10086, // 设置代理来解决 H5 请求的跨域问题 proxy: { diff --git a/src/api/websocket.js b/src/api/websocket.js index bf81b03..d0fe3e9 100644 --- a/src/api/websocket.js +++ b/src/api/websocket.js @@ -3,7 +3,7 @@ import store from '@/store/index' import proto from '@/api/proto'; import { fetchOpMap, fetchRouteWs } from '@/api/api' import { getStorage, removeStorage } from '@/utils/storage'; -import { showToast } from '@/utils/application'; +import { redirectTo, showToast } from '@/utils/application'; import { setUserInfo } from '@/store/user' const { api } = proto @@ -83,6 +83,9 @@ const websocket = { // console.log(waitCall, this.waitCall); if (caller) { if (wrap.op === opFail) { + if (res.code === 401) { // 重新登录 + setTimeout(() => redirectTo({url: '/pages/login/login'}), 500); + } caller.onFail(res, wrap); } else { caller.onRes(res, wrap); @@ -180,16 +183,16 @@ export const sendPromise = function(msg) { }) } -;(async function() { - // const token = "15PohpTztq4YkMmErI4H71kJWX2pPqnvOZar9HKOfg1KEy0fd8fUNxnBIwuMDcKQEBPG7b9UC7ja_8FtwvYjnA==" - const token = getStorage('_t') - if (!token) { - return; - } - // console.log('token', token) - const [opMap, wsRes] = await Promise.all([fetchOpMap(), fetchRouteWs()]); - websocket.init(token, opMap.data, wsRes.data); - - window.websocket = websocket; -})() +// ;(async function() { +// // const token = "15PohpTztq4YkMmErI4H71kJWX2pPqnvOZar9HKOfg1KEy0fd8fUNxnBIwuMDcKQEBPG7b9UC7ja_8FtwvYjnA==" +// const token = getStorage('_t') +// if (!token) { +// return; +// } +// // console.log('token', token) +// const [opMap, wsRes] = await Promise.all([fetchOpMap(), fetchRouteWs()]); +// websocket.init(token, opMap.data, wsRes.data); + +// window.websocket = websocket; +// })() diff --git a/src/app.js b/src/app.js index 336ad4f..5ca4f43 100644 --- a/src/app.js +++ b/src/app.js @@ -5,6 +5,10 @@ import { Provider } from 'react-redux' import store from './store' import './app.scss' +import { getInstance, redirectTo } from '@/utils/application' +import { getStorage, removeStorage } from '@/utils/storage' +import websocket from '@/api/websocket' +import {fetchOpMap, fetchRouteWs} from '@/api/api' // IOS Promise finally 兼容 const promiseFinally = require('promise.prototype.finally'); @@ -65,9 +69,34 @@ window.srt = new SDK({ // window.srt.setUser({user_id: 'xxx'}) // 设置用户信息,用户信息将会被设置在props.wx_user对象中 class App extends Component { - componentDidMount() { } + componentDidMount() { + } - componentDidShow() { } + componentDidShow() { + console.log('instance', getInstance(), 'app mount') + + if (!getInstance().router.path.startsWith('/pages/login/login')) { + // 非登录页,检查token是否存在 + const token = getStorage('_t'); + if (!token) { + setTimeout(() => redirectTo({url: '/pages/login/login'}), 500) + } else { + // 尝试连接 websocket + // const [opMap, wsRes] = await ; + Promise.all([fetchOpMap(), fetchRouteWs()]) + .then(([opMap, wsRes]) => { + console.log('token', token, opMap, wsRes) + websocket.init(token, opMap.data, wsRes.data); + window.websocket = websocket; + }) + .catch(err => { + console.log('ws conn error:', err) + removeStorage('_t') + setTimeout(() => redirectTo({url: '/pages/login/login'}), 500) + }) + } + } + } componentDidHide() { } diff --git a/src/pages/table/table.jsx b/src/pages/table/table.jsx index dff3996..b7ea449 100644 --- a/src/pages/table/table.jsx +++ b/src/pages/table/table.jsx @@ -79,6 +79,11 @@ class Table extends Component { bettingChip: 0, betConfirmText: '下注', betSubmitLoading: false, + + // winsChip: {}, // {1:14, 2:-14} + playerNoticeLines: [], // {line1: "+6",line2: "跟注",playerId: 1} + playerNotice: null, //{line1: "+108",line2: "加注:12221126",line3: '跟注:6', playerId: 3}, + winsChip: {1: -20, 2: 40, 3: -20}, } } @@ -86,6 +91,27 @@ class Table extends Component { // 取消监听消息 removeMsgListener('api.ResGameFullStatus'); removeMsgListener('api.ResKickOutTable'); + removeMsgListener('api.ResDismissGameTable'); + removeMsgListener('api.ResNoticePlayerLine'); + removeMsgListener('api.ResCalcWinnerChip'); + } + + showPlayerNotice() { + if (this.state.playerNotice) { + return; + } + const playerNotice = this.state.playerNoticeLines.shift(); + if (!playerNotice) { + return; + } + this.setState({playerNotice}, () => { + // 隔1秒展示下一条 + setTimeout(() => { + this.setState({ + playerNotice: null + }, this.showPlayerNotice.bind(this)); + }, 1500); + }); } componentDidMount() { @@ -102,6 +128,21 @@ class Table extends Component { showToast({title: '牌桌已解散'}); setTimeout(() => redirectTo({url: '/pages/lobby/lobby'}), 800); }); + // 操作通知消息 {line1: "+6",line2: "跟注",playerId: 1} + addMsgListen('api.ResNoticePlayerLine', res => { + // 放置消息到通知队列 + this.state.playerNoticeLines.push(res); + this.showPlayerNotice.call(this); + }); + // 输赢筹码信息 + addMsgListen('api.ResCalcWinnerChip', res => { + console.log('winsChip...:', res); + window.winsChip = res; + this.setState({winsChip: res.winsChip}); + setTimeout(() => { // 输赢筹码展示一会后消失 + this.setState({winsChip: {}}); + }, 1300); + }); // // 扣除大盲注(显示3秒) // addMsgListen('api.ResBigBlindChip', res => { // showToast({title: '大盲注:' + res.chip}); @@ -194,7 +235,7 @@ class Table extends Component { // 准备开始 handleReadyStart(playerStatus) { - (playerStatus === 1 ? reqReadyStart() : reqCancelReady()) + (playerStatus === 2 ? reqCancelReady() : reqReadyStart()) .then(res => { console.log('ready res:', res); }) @@ -243,7 +284,7 @@ class Table extends Component { // 跟注,弃牌...操作按钮 handleBetting(betType) { - console.log('betType', betType) + // console.log('betType', betType) betOpts const {betRole: {betMin}} = this.state.selfPlayer; switch(betType) { case 1: // 1跟注,2加注(-[0]+),3All-In,4弃牌,5过牌 @@ -253,7 +294,6 @@ class Table extends Component { this.setState({betType: 4, betConfirmText: '弃牌'}); break; } - } handleBetConfirm() { @@ -287,7 +327,8 @@ class Table extends Component { stage, bigBlindPos, smallBlindPos, chip, otherPlayers, selfIndex, selfPlayer, - bettingChip, betConfirmText, betSubmitLoading + bettingChip, betConfirmText, betSubmitLoading, + playerNotice: pn, winsChip, } = this.state; const {betMin, betMax, betOpts} = selfPlayer.betRole || {}; // console.log('props', this.props); @@ -302,7 +343,9 @@ class Table extends Component { return ( - + <Title title={''} leftSolt={( + <View className="table-no"><Text>#{tableNo}</Text></View> + )} /> { playerRows.map((row, i) => ( <View key={i} className="playerRow"> @@ -343,6 +386,19 @@ class Table extends Component { } */} </View> } + { + !pn || pn.playerId !== player.id ? null : <View className={cnames({"notice-wrap-l": j%2===0,"notice-wrap-r": j%2===1})}> + {pn.line1 && <View className="line1"><Text>{pn.line1}</Text></View>} + {pn.line2 && <View className="line2"><Text>{pn.line2}</Text></View>} + {pn.line3 && <View className="line3"><Text>{pn.line3}</Text></View>} + </View> + } + { + typeof(winsChip[player.id]) !== 'number' ? null + : <View className="wins-chip-wrap"> + <View className={cnames("wins-chip", {'lose': winsChip[player.id] < 0, 'win': winsChip[player.id] >= 0})}><Text>{winsChip[player.id] >= 0 ? '+' : ''}{winsChip[player.id]}</Text></View> + </View> + } { // 踢出牌桌close图标 stage === 1 && selfPlayer.master && player.id @@ -351,7 +407,13 @@ class Table extends Component { } {/* 玩家手牌 */} {player.id && existsCard(selfPlayer.handCard[0]) ? <View className={cnames("card1", {'translate-2l':j%2===0,'translate-2r':j%2===1})}><Card w={38} h={48} dot={cardDot(player.handCard[0], player.status === 7 ? -2 : -1)} suit={cardSuit(player.handCard[0])} /></View>: null} - {player.id && existsCard(selfPlayer.handCard[0]) ? <View className={cnames("card2", {'translate-2l':j%2===0,'translate-2r':j%2===1})}><Card w={38} h={48} dot={cardDot(player.handCard[1], player.status === 7 ? -2 : -1)} suit={cardSuit(player.handCard[1])} /></View> : null} + {player.id && existsCard(selfPlayer.handCard[1]) ? <View className={cnames("card2", {'translate-2l':j%2===0,'translate-2r':j%2===1})}><Card w={38} h={48} dot={cardDot(player.handCard[1], player.status === 7 ? -2 : -1)} suit={cardSuit(player.handCard[1])} /></View> : null} + {/* 手牌牌型 */} + { + !(player.handType && player.handType.handZh) ? null : <View className="hand-type"> + <Text>{player.handType.handZh}</Text> + </View> + } {/* 头像名称 */} <View className={cnames("player", {'player-wait': !player.id || isIn(player.status, 3, 7) || (player.status === 1 && !player.master)})}> <View className="avatar-wrap">{!player.id ? null : <Image className="avatar" src={player.avatar || avatar} />}</View> @@ -439,6 +501,29 @@ class Table extends Component { } {existsCard(selfPlayer.handCard[0]) && <View className="hand-card1 translate-2b"><Card w={38} h={48} dot={cardDot(selfPlayer.handCard[0], -1)} suit={cardSuit(selfPlayer.handCard[0])} /></View>} {existsCard(selfPlayer.handCard[1]) && <View className="hand-card2 translate-2b"><Card w={38} h={48} dot={cardDot(selfPlayer.handCard[1], -1)} suit={cardSuit(selfPlayer.handCard[1])} /></View>} + {/* 手牌牌型 */} + { + !(selfPlayer.handType && selfPlayer.handType.handZh) ? null : <View className="self-hand-type"> + <Text>{selfPlayer.handType.handZh}</Text> + </View> + } + + { + !pn || pn.playerId !== selfPlayer.id ? null : <View className="notice-wrap-l notice-wrap-self"> + {pn.line1 && <View className="line1"><Text>{pn.line1}</Text></View>} + {pn.line2 && <View className="line2"><Text>{pn.line2}</Text></View>} + {pn.line3 && <View className="line3"><Text>{pn.line3}</Text></View>} + </View> + } + + { + typeof(winsChip[selfPlayer.id]) !== 'number' ? null + : <View className="wins-chip-wrap wins-chip-wrap-self"> + <View className={cnames("wins-chip", {'lose': winsChip[selfPlayer.id] < 0, 'win': winsChip[selfPlayer.id] >= 0})}> + <Text>{winsChip[selfPlayer.id] >= 0 ? '+' : ''}{winsChip[selfPlayer.id]}</Text> + </View> + </View> + } <View className="hand-five" style={{display:'none'}}> { existsCard(selfPlayer.handCard[0]) && [1, 2, 3, 4, 5].map((_, i) => ( @@ -473,7 +558,7 @@ class Table extends Component { {!isIn(2, betOpts) ? null : <View className="amount-plus" onClick={this.handleSubPlusBetting.bind(this, 1, 1)}><Image className="amount-plus-icon" src={plus1Icon} /></View>} </View> { - stage === 1 ? ( + isIn(stage, 1, 7) ? ( // 是房主: 关闭牌桌, 开始游戏 selfPlayer.master ? <View className="ctl-opt-wrap"> <View className="ctl-opt"><Button onClick={this.handleResDismissGameTable.bind(this)} className={cnames("ctl-opt-btn-4", {'ctl-opt-btn-disable': selfPlayer.status === 2})}>关闭牌桌</Button></View> diff --git a/src/pages/table/table.scss b/src/pages/table/table.scss index e84200c..717c023 100644 --- a/src/pages/table/table.scss +++ b/src/pages/table/table.scss @@ -22,6 +22,40 @@ transform: rotate(0deg) translateX(0%) translateY(0%); } } + +@keyframes move2Left { + from { + transform: translateX(-30%); + } + to { + transform: translateX(0%); + } +} +@keyframes move2Right { + from { + transform: translateX(30%); + } + to { + transform: translateX(0%); + } +} +@keyframes move2Bottom { + from { + transform: translateY(-50%); + } + to { + transform: translateY(0%); + } +} +@keyframes move2Top { + from { + transform: translateY(50%); + } + to { + transform: translateY(0%); + } +} + .translate-2l { animation: translate2Left 1000ms; } @@ -34,6 +68,16 @@ } .tab { + .table-no { + width: 86px; + height: 24px; + border-radius: 16px; + background-color: #EA8C28; + text-align: center; + line-height: 24px; + color: #fff; + font-size: 16px; + } .playerRow { display: flex; justify-content: space-between; @@ -51,8 +95,9 @@ .p-status-ready { position: absolute; + // z-index: 99; top: -25px; - left: 30px; + left: 25px; color: #1BCE7D; font-weight: bold; font-size: 12px; @@ -142,6 +187,107 @@ margin-left: -15px; line-height: 14px; } + + .notice-wrap-l,.notice-wrap-r { + position: absolute; + top: 0px; + left: 156px; + width: 68px; + height: 82px; + display: flex; + flex-direction: column; + justify-content: space-evenly; + z-index: 99; + // align-items: center; + // background-color: rgba($color: #fff, $alpha: 0.8); + animation: move2Left 1000ms; + } + .notice-wrap-r { + left: -49px; + animation: move2Right 1000ms; + // text-align: right; + } + .notice-wrap-self { + left: 180px; + top: -4px; + width: 120px; + + .line1 { + color: #FAFAFA; + } + } + + .line2,.line3 { // .line1, + white-space: nowrap; + word-break: keep-all; // 中文不换行, https://www.cnblogs.com/wqsbk/p/3493948.html + // overflow: hidden; + // text-overflow: ellipsis; + line-height: 14px; + height: 14px; + } + .line1 { + white-space: nowrap; + word-break: keep-all; + font-size: 18px; + font-weight: bold; + color: #EE914E; + } + .line2 { + font-size: 14px; + font-weight: bold; + color: #FF4400; + } + .line3 { + font-size: 14px; + font-weight: normal; + color: #3D3D3D; + } + .wins-chip-wrap { + position: absolute; + top: 93px; + // left: 56px; + left: 0; + width: 153px; + height: 16px; + line-height: 16px; + // border: 1px solid red; + display: flex; + justify-content: center; + animation: move2Bottom 1000ms; + } + .wins-chip { + font-size: 18px; + font-weight: bold; + color: #EE914E; + text-shadow: 0 4px 10px rgba($color: #f40, $alpha: 0.3); + } + .wins-chip-wrap-self { + left: -66px; + top: -50px; + animation: move2Top 1000ms; + } + .lose { + color: #A8A29D; + } + .hand-type,.self-hand-type { + position: absolute; + top: 4px; + left: 66px; + width: 70px; + height: 24px; + line-height: 24px; + border-radius: 5px; + background-color: rgba($color: #FDFDFD, $alpha: 0.75); + color: #359220; + font-size: 14px; + font-weight: bold; + text-align: center; + z-index: 99; + } + .self-hand-type { + left: 88px; + } + .filling-chip { font-size: 18px; font-weight: bold; @@ -169,7 +315,7 @@ .card1, .card2 { position: absolute; - z-index: 999; + z-index: 9; top: -20px; }