From c8759737246b6e6739933e9730fe2722d8d46ff8 Mon Sep 17 00:00:00 2001 From: tangmingyou <234767776@qq.com> Date: Sun, 29 Jan 2023 13:13:27 +0800 Subject: [PATCH] ws logic --- config/dev.js | 4 +- src/api/api.js | 4 +- src/api/http.js | 2 +- src/api/proto.js | 267 +++++++++++++++++++++++++++++- src/api/websocket.js | 33 +++- src/api/wsapi.js | 3 + src/assets/icon/plus-1.svg | 1 + src/assets/icon/robot-1.svg | 1 + src/assets/icon/sub-1.svg | 1 + src/pages/lobby/lobby.jsx | 15 +- src/pages/login/login.jsx | 66 ++++---- src/pages/new_table/new_table.jsx | 56 ++++++- src/pages/table/table.jsx | 54 ++++-- src/pages/table/table.scss | 130 ++++++++++++++- src/utils/application.js | 11 ++ 15 files changed, 556 insertions(+), 92 deletions(-) create mode 100644 src/api/wsapi.js create mode 100644 src/assets/icon/plus-1.svg create mode 100644 src/assets/icon/robot-1.svg create mode 100644 src/assets/icon/sub-1.svg diff --git a/config/dev.js b/config/dev.js index 12865cb..c9dc00a 100644 --- a/config/dev.js +++ b/config/dev.js @@ -9,8 +9,8 @@ module.exports = { mini: {}, h5: { devServer: { - host: '0.0.0.0', - // host: '192.168.3.102', + //host: '0.0.0.0', + host: '192.168.0.102', port: 10086, // 设置代理来解决 H5 请求的跨域问题 proxy: { diff --git a/src/api/api.js b/src/api/api.js index 2780a48..d8182c1 100644 --- a/src/api/api.js +++ b/src/api/api.js @@ -1,5 +1,7 @@ import { get, post } from '@/api/http' -export const login = params => post('/api/auth/authorize', params) +export const doLogin = params => post('/api/auth/authorize', params) export const fetchOpMap = () => get('/api/conn/opMap') + +export const fetchLobbyView = () => get('/api/game/lobby') diff --git a/src/api/http.js b/src/api/http.js index 9e00894..1541eeb 100644 --- a/src/api/http.js +++ b/src/api/http.js @@ -22,7 +22,7 @@ function objUrlEncode(params) { // Http GET 请求 const fetch = function (method, url, params) { - const token = getStorage('user_token'); + const token = getStorage('_t'); return new Promise(function (resolve, reject) { try { const data = objUrlEncode(params); diff --git a/src/api/proto.js b/src/api/proto.js index 3647181..8d287f4 100644 --- a/src/api/proto.js +++ b/src/api/proto.js @@ -671,6 +671,181 @@ export const api = $root.api = (() => { return Pong; })(); + api.ResSuccess = (function() { + + /** + * Properties of a ResSuccess. + * @memberof api + * @interface IResSuccess + */ + + /** + * Constructs a new ResSuccess. + * @memberof api + * @classdesc Represents a ResSuccess. + * @implements IResSuccess + * @constructor + * @param {api.IResSuccess=} [properties] Properties to set + */ + function ResSuccess(properties) { + if (properties) + for (let keys = Object.keys(properties), i = 0; i < keys.length; ++i) + if (properties[keys[i]] != null) + this[keys[i]] = properties[keys[i]]; + } + + /** + * Creates a new ResSuccess instance using the specified properties. + * @function create + * @memberof api.ResSuccess + * @static + * @param {api.IResSuccess=} [properties] Properties to set + * @returns {api.ResSuccess} ResSuccess instance + */ + ResSuccess.create = function create(properties) { + return new ResSuccess(properties); + }; + + /** + * Encodes the specified ResSuccess message. Does not implicitly {@link api.ResSuccess.verify|verify} messages. + * @function encode + * @memberof api.ResSuccess + * @static + * @param {api.IResSuccess} message ResSuccess message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + ResSuccess.encode = function encode(message, writer) { + if (!writer) + writer = $Writer.create(); + return writer; + }; + + /** + * Encodes the specified ResSuccess message, length delimited. Does not implicitly {@link api.ResSuccess.verify|verify} messages. + * @function encodeDelimited + * @memberof api.ResSuccess + * @static + * @param {api.IResSuccess} message ResSuccess message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + ResSuccess.encodeDelimited = function encodeDelimited(message, writer) { + return this.encode(message, writer).ldelim(); + }; + + /** + * Decodes a ResSuccess message from the specified reader or buffer. + * @function decode + * @memberof api.ResSuccess + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @param {number} [length] Message length if known beforehand + * @returns {api.ResSuccess} ResSuccess + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + ResSuccess.decode = function decode(reader, length) { + if (!(reader instanceof $Reader)) + reader = $Reader.create(reader); + let end = length === undefined ? reader.len : reader.pos + length, message = new $root.api.ResSuccess(); + while (reader.pos < end) { + let tag = reader.uint32(); + switch (tag >>> 3) { + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }; + + /** + * Decodes a ResSuccess message from the specified reader or buffer, length delimited. + * @function decodeDelimited + * @memberof api.ResSuccess + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @returns {api.ResSuccess} ResSuccess + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + ResSuccess.decodeDelimited = function decodeDelimited(reader) { + if (!(reader instanceof $Reader)) + reader = new $Reader(reader); + return this.decode(reader, reader.uint32()); + }; + + /** + * Verifies a ResSuccess message. + * @function verify + * @memberof api.ResSuccess + * @static + * @param {Object.} message Plain object to verify + * @returns {string|null} `null` if valid, otherwise the reason why it is not + */ + ResSuccess.verify = function verify(message) { + if (typeof message !== "object" || message === null) + return "object expected"; + return null; + }; + + /** + * Creates a ResSuccess message from a plain object. Also converts values to their respective internal types. + * @function fromObject + * @memberof api.ResSuccess + * @static + * @param {Object.} object Plain object + * @returns {api.ResSuccess} ResSuccess + */ + ResSuccess.fromObject = function fromObject(object) { + if (object instanceof $root.api.ResSuccess) + return object; + return new $root.api.ResSuccess(); + }; + + /** + * Creates a plain object from a ResSuccess message. Also converts values to other types if specified. + * @function toObject + * @memberof api.ResSuccess + * @static + * @param {api.ResSuccess} message ResSuccess + * @param {$protobuf.IConversionOptions} [options] Conversion options + * @returns {Object.} Plain object + */ + ResSuccess.toObject = function toObject() { + return {}; + }; + + /** + * Converts this ResSuccess to JSON. + * @function toJSON + * @memberof api.ResSuccess + * @instance + * @returns {Object.} JSON object + */ + ResSuccess.prototype.toJSON = function toJSON() { + return this.constructor.toObject(this, $protobuf.util.toJSONOptions); + }; + + /** + * Gets the default type url for ResSuccess + * @function getTypeUrl + * @memberof api.ResSuccess + * @static + * @param {string} [typeUrlPrefix] your custom typeUrlPrefix(default "type.googleapis.com") + * @returns {string} The default type url + */ + ResSuccess.getTypeUrl = function getTypeUrl(typeUrlPrefix) { + if (typeUrlPrefix === undefined) { + typeUrlPrefix = "type.googleapis.com"; + } + return typeUrlPrefix + "/api.ResSuccess"; + }; + + return ResSuccess; + })(); + api.ResFail = (function() { /** @@ -1660,6 +1835,8 @@ export const api = $root.api = (() => { * Properties of a ReqCreateTable. * @memberof api * @interface IReqCreateTable + * @property {number|null} [players] ReqCreateTable players + * @property {number|null} [robots] ReqCreateTable robots */ /** @@ -1677,6 +1854,22 @@ export const api = $root.api = (() => { this[keys[i]] = properties[keys[i]]; } + /** + * ReqCreateTable players. + * @member {number} players + * @memberof api.ReqCreateTable + * @instance + */ + ReqCreateTable.prototype.players = 0; + + /** + * ReqCreateTable robots. + * @member {number} robots + * @memberof api.ReqCreateTable + * @instance + */ + ReqCreateTable.prototype.robots = 0; + /** * Creates a new ReqCreateTable instance using the specified properties. * @function create @@ -1701,6 +1894,10 @@ export const api = $root.api = (() => { ReqCreateTable.encode = function encode(message, writer) { if (!writer) writer = $Writer.create(); + if (message.players != null && Object.hasOwnProperty.call(message, "players")) + writer.uint32(/* id 1, wireType 0 =*/8).int32(message.players); + if (message.robots != null && Object.hasOwnProperty.call(message, "robots")) + writer.uint32(/* id 2, wireType 0 =*/16).int32(message.robots); return writer; }; @@ -1735,6 +1932,14 @@ export const api = $root.api = (() => { while (reader.pos < end) { let tag = reader.uint32(); switch (tag >>> 3) { + case 1: { + message.players = reader.int32(); + break; + } + case 2: { + message.robots = reader.int32(); + break; + } default: reader.skipType(tag & 7); break; @@ -1770,6 +1975,12 @@ export const api = $root.api = (() => { ReqCreateTable.verify = function verify(message) { if (typeof message !== "object" || message === null) return "object expected"; + if (message.players != null && message.hasOwnProperty("players")) + if (!$util.isInteger(message.players)) + return "players: integer expected"; + if (message.robots != null && message.hasOwnProperty("robots")) + if (!$util.isInteger(message.robots)) + return "robots: integer expected"; return null; }; @@ -1784,7 +1995,12 @@ export const api = $root.api = (() => { ReqCreateTable.fromObject = function fromObject(object) { if (object instanceof $root.api.ReqCreateTable) return object; - return new $root.api.ReqCreateTable(); + let message = new $root.api.ReqCreateTable(); + if (object.players != null) + message.players = object.players | 0; + if (object.robots != null) + message.robots = object.robots | 0; + return message; }; /** @@ -1796,8 +2012,19 @@ export const api = $root.api = (() => { * @param {$protobuf.IConversionOptions} [options] Conversion options * @returns {Object.} Plain object */ - ReqCreateTable.toObject = function toObject() { - return {}; + ReqCreateTable.toObject = function toObject(message, options) { + if (!options) + options = {}; + let object = {}; + if (options.defaults) { + object.players = 0; + object.robots = 0; + } + if (message.players != null && message.hasOwnProperty("players")) + object.players = message.players; + if (message.robots != null && message.hasOwnProperty("robots")) + object.robots = message.robots; + return object; }; /** @@ -1835,6 +2062,7 @@ export const api = $root.api = (() => { * Properties of a ResCreateTable. * @memberof api * @interface IResCreateTable + * @property {number|null} [tableNo] ResCreateTable tableNo */ /** @@ -1852,6 +2080,14 @@ export const api = $root.api = (() => { this[keys[i]] = properties[keys[i]]; } + /** + * ResCreateTable tableNo. + * @member {number} tableNo + * @memberof api.ResCreateTable + * @instance + */ + ResCreateTable.prototype.tableNo = 0; + /** * Creates a new ResCreateTable instance using the specified properties. * @function create @@ -1876,6 +2112,8 @@ export const api = $root.api = (() => { ResCreateTable.encode = function encode(message, writer) { if (!writer) writer = $Writer.create(); + if (message.tableNo != null && Object.hasOwnProperty.call(message, "tableNo")) + writer.uint32(/* id 1, wireType 0 =*/8).int32(message.tableNo); return writer; }; @@ -1910,6 +2148,10 @@ export const api = $root.api = (() => { while (reader.pos < end) { let tag = reader.uint32(); switch (tag >>> 3) { + case 1: { + message.tableNo = reader.int32(); + break; + } default: reader.skipType(tag & 7); break; @@ -1945,6 +2187,9 @@ export const api = $root.api = (() => { ResCreateTable.verify = function verify(message) { if (typeof message !== "object" || message === null) return "object expected"; + if (message.tableNo != null && message.hasOwnProperty("tableNo")) + if (!$util.isInteger(message.tableNo)) + return "tableNo: integer expected"; return null; }; @@ -1959,7 +2204,10 @@ export const api = $root.api = (() => { ResCreateTable.fromObject = function fromObject(object) { if (object instanceof $root.api.ResCreateTable) return object; - return new $root.api.ResCreateTable(); + let message = new $root.api.ResCreateTable(); + if (object.tableNo != null) + message.tableNo = object.tableNo | 0; + return message; }; /** @@ -1971,8 +2219,15 @@ export const api = $root.api = (() => { * @param {$protobuf.IConversionOptions} [options] Conversion options * @returns {Object.} Plain object */ - ResCreateTable.toObject = function toObject() { - return {}; + ResCreateTable.toObject = function toObject(message, options) { + if (!options) + options = {}; + let object = {}; + if (options.defaults) + object.tableNo = 0; + if (message.tableNo != null && message.hasOwnProperty("tableNo")) + object.tableNo = message.tableNo; + return object; }; /** diff --git a/src/api/websocket.js b/src/api/websocket.js index 6872a3d..fe7add8 100644 --- a/src/api/websocket.js +++ b/src/api/websocket.js @@ -1,4 +1,5 @@ import proto from '@/api/proto'; +import { fetchOpMap } from '@/api/api' const { api } = proto @@ -7,12 +8,15 @@ const websocket = { state: 0, // 0未初始,1打开,2关闭 seq: 1, // 消息序号 offset: 0, - failOp: 0, + opFail: 0, + opSuccess: 0, opPathMap: {}, nameOpMap: {}, - init(token, { offset, failOp, opPathMap, nameOpMap }) { + init(token, { offset, opFail, opSuccess, opPathMap, nameOpMap }) { + // TODO 连接状态判定 this.offset = offset || 0; - this.failOp = failOp; + this.opFail = opFail; + this.opSuccess = opSuccess; this.opPathMap = opPathMap || {}; this.nameOpMap = nameOpMap || {}; // window.proto = proto; @@ -38,19 +42,19 @@ const websocket = { // 解码响应体 const opPath = this.opPathMap[wrap.op] if (!opPath) { - console.log('res op not exists!', wrap.op) + console.error('res op not exists!', wrap.op) return } const res = proto[opPath[0]][opPath[1]].decode(wrap.body) // const res = api.ResIdentity.decode(wrap.body) - console.log(wrap, res) + console.log('res msg:', res) if (wrap.seq !== 0) { // 对应请求callback const waitCall = this.waitCall[wrap.seq]; delete this.waitCall[wrap.seq]; - console.log(waitCall, this.waitCall); + // console.log(waitCall, this.waitCall); if (waitCall) { - if (wrap.op === failOp) { + if (wrap.op === opFail) { waitCall.onFail(res, wrap); } else { waitCall.onRes(res, wrap); @@ -85,7 +89,7 @@ const websocket = { const idx = typeUrl.lastIndexOf("/"); typeUrl = idx === -1 ? typeUrl : typeUrl.substring(idx + 1); const op = this.nameOpMap[typeUrl]; - console.log(typeUrl, op); + console.log('req', typeUrl, op); const msgBufer = msg.constructor.encode(msg).finish(); const wrap = api.ProtoWrap.create({ ver: 1, op: op, seq: this.seq++, body: msgBufer }); const wrapBuffer = api.ProtoWrap.encode(wrap).finish(); @@ -108,3 +112,16 @@ const websocket = { } export default websocket + +export const sendPromise = function(msg) { + return new Promise((resolve, reject) => { + websocket.send(msg, resolve, reject) + }) +} + +;(async function() { + const token = "15PohpTztq4YkMmErI4H71kJWX2pPqnvOZar9HKOfg1KEy0fd8fUNxnBIwuMDcKQEBPG7b9UC7ja_8FtwvYjnA==" + const opMap = await fetchOpMap() + websocket.init(token, opMap.data) +})() + diff --git a/src/api/wsapi.js b/src/api/wsapi.js new file mode 100644 index 0000000..21ec75d --- /dev/null +++ b/src/api/wsapi.js @@ -0,0 +1,3 @@ +import { sendPromise } from './websocket' + +export const aa = msg => sendPromise(msg) diff --git a/src/assets/icon/plus-1.svg b/src/assets/icon/plus-1.svg new file mode 100644 index 0000000..55e907b --- /dev/null +++ b/src/assets/icon/plus-1.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icon/robot-1.svg b/src/assets/icon/robot-1.svg new file mode 100644 index 0000000..a742b29 --- /dev/null +++ b/src/assets/icon/robot-1.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icon/sub-1.svg b/src/assets/icon/sub-1.svg new file mode 100644 index 0000000..d37b324 --- /dev/null +++ b/src/assets/icon/sub-1.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/pages/lobby/lobby.jsx b/src/pages/lobby/lobby.jsx index ad7a9df..92e14c6 100644 --- a/src/pages/lobby/lobby.jsx +++ b/src/pages/lobby/lobby.jsx @@ -5,8 +5,9 @@ import Taro from '@tarojs/taro' import { useSelector, useDispatch } from 'react-redux' import './lobby.scss' +import { navigateTo } from '@/utils/application' import { increment } from '@/store/index' -import { fetchOpMap } from '@/api/api' +import { fetchOpMap, fetchLobbyView } from '@/api/api' import Title from '@/components/title' import coin1 from '@/assets/coin-1.png' import tabQuestion from '@/assets/tab-question.svg' @@ -17,9 +18,13 @@ function Lobby() { const user = useSelector((state) => state.user) console.log(user) const dispatch = useDispatch() - fetchOpMap() - .then(res => { console.log(res) }) - .catch(err => console.log(err)) + fetchLobbyView() + .then(res => { + console.log('lobby', res) + }) + .catch(err => { + console.error(err) + }) const avatar = "https://img0.baidu.com/it/u=2477829979,2171947490&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=500"; @@ -97,7 +102,7 @@ function Lobby() { - + ) diff --git a/src/pages/login/login.jsx b/src/pages/login/login.jsx index 5f8a367..3e36e34 100644 --- a/src/pages/login/login.jsx +++ b/src/pages/login/login.jsx @@ -5,54 +5,48 @@ import Taro from '@tarojs/taro'; //import { Form, Input, TextArea, Cell, Button, Row, Col, Image } from '@nutui/nutui-react-taro'; import ws from '@/api/websocket' import './login.scss' -import { fetchOpMap } from '@/api/api' +import { doLogin, fetchOpMap } from '@/api/api' +import { redirectTo, showToast } from '@/utils/application' import iconUser from '@/assets/icon/user-3-fill.svg' import iconLock from '@/assets/icon/lock-2-fill.svg' import abIcon from '@/assets/icon/a-b.svg' +import { setStorage } from '@/utils/storage'; /* 登录 -> 大厅 -> 新桌面 -> 进桌面 -> profile(弹窗) */ -function Login(props, ref) { - // protobuf - // let msg = message.Proto.create({ver:1, seq:1, op: 1}); - // let buffer = message.Proto.encode(msg).finish(); - // console.log(buffer) - // let decoded = message.Proto.decode(buffer); - // console.log(decoded) - +function Login() { const [state, setState] = useState({ username: '', password: '', captcha: '', }); const [captcha, setCaptcha] = useState({ - origin: "/api/auth/captcha", // http://localhost:9999 + origin: "/api/auth/captcha", src: "/api/auth/captcha", - }) - const usernameChange = (username, e) => { - if (!e) return; - state.username = username; - console.log(state.username) - } - const passwordChange = (password, e) => { - if (!e) return; - state.password = password; - console.log(state.password) - } - const handleSubmit = () => { - console.log('submit', state) - const token = "AgOjcdf3goeYDX3lwWWwkXtVpcrL-l2rX8csrRKgs3_-BC3JOx0l6nZU0MV25eIn" - fetchOpMap() - .then(res => { - console.log('opMap', res) - ws.init(token, res.data) - }) - .catch(err => { - console.log('err', err) - }) + }); + const [submitLoding, setSubmitLoding] = useState(false) + + const handleSubmit = async () => { + if (submitLoding) { return; } + setSubmitLoding(true); + //const token = "AgOjcdf3goeYDX3lwWWwkXtVpcrL-l2rX8csrRKgs3_-BC3JOx0l6nZU0MV25eIn" + try { + const [tokenRes, opMapRes] = await Promise.all([doLogin(state), fetchOpMap()]); + setStorage('_t', tokenRes.data.token) + // 初始化 websocket 连接 + ws.init(tokenRes.data.token, opMapRes.data); + showToast({title: '登录成功', duration: 800}); + // 重定向到大厅 + setTimeout(() => redirectTo({url: '/pages/lobby/lobby'}), 800); + }catch(err) { + showToast({icon: 'error', title: err.msg}) + } finally { + setSubmitLoding(false); + } } + console.log(submitLoding) return ( @@ -63,22 +57,22 @@ function Login(props, ref) { + onChange={e => setState({...state, username: e.target.value})}/> + onChange={e => setState({...state, password: e.target.value})}/> {}}/> + onChange={e => setState({...state, captcha: e.target.value})}/> setCaptcha({...captcha, src: captcha.origin + '?t=' + Math.random()})} /> - + diff --git a/src/pages/new_table/new_table.jsx b/src/pages/new_table/new_table.jsx index eba20af..7c2070b 100644 --- a/src/pages/new_table/new_table.jsx +++ b/src/pages/new_table/new_table.jsx @@ -1,26 +1,52 @@ import React, { Component, useState } from 'react' import { View, Text, Image, Button } from '@tarojs/components' +import { Popup, Picker } from '@nutui/nutui-react-taro'; import { useSelector } from 'react-redux' import './new_table.scss' import Title from '@/components/title' +import { sendPromise } from '@/api/websocket' +import proto from '@/api/proto'; +import { showToast } from '@/utils/application' import userIcon from '@/assets/icon/user-2.svg' +import robotIcon from '@/assets/icon/robot-1.svg' import closeIcon from '@/assets/icon/close-1.svg' import addIcon from '@/assets/icon/add-2.svg' +const { ReqCreateTable } = proto.api + function NewTable() { const {username, nickname, avatar} = useSelector(state => state.user); + const [submitLoading, setSubmitLoading] = useState(false); + const [choosingPlace, setChoosingPlace] = useState(-1); - const players = [1,2,3,4,5,6]; + const [players, setPlayers] = useState([0,0,0,0,0,0]); // 0空位,1玩家,2机器人 const playerRows = players.reduce((arr, item, idx) => { const row = parseInt(idx / 2); let rowArr = arr[row] || []; rowArr[idx % 2] = {index: idx, item}; arr[row] = rowArr; - parseInt() return arr; }, []); + const createTable = async () => { + if (submitLoading) { return } + setSubmitLoading(true) + + const req = ReqCreateTable.create({ + players: players.filter(type => type === 1).length, + robots: players.filter(type => type === 2).length + }) + try { + const res = await sendPromise(req) + console.log('finish', res) + }catch(err) { + showToast({title: err}) + } finally { + setSubmitLoading(false) + } + } + return ( @@ -30,13 +56,16 @@ function NewTable() { <View key={i} className="playerRow"> { row.map((player, j) => ( - player.index === 5 - ? <View key={j} className='player empty-player'><Image className="empty-icon" src={addIcon} /></View> + player.item === 0 + ? <View key={j} onClick={() => setChoosingPlace(player.index)} className='player empty-player'><Image className="empty-icon" src={addIcon} /></View> : <View key={j} className="player-wrap"> - <View className="player-icon"><Image className="player-icon-img" src={userIcon} /></View> - <View className="close-icon"><Image className="close-icon-img" src={closeIcon} /></View> + <View className="player-icon"><Image className="player-icon-img" src={player.item === 1 ? userIcon : robotIcon} /></View> + <View className="close-icon" onClick={() => { + players[player.index] = 0; + setPlayers([...players]); + }}><Image className="close-icon-img" src={closeIcon} /></View> <View className="player"> - <Text className="player-name">Player{player.item}</Text> + <Text className="player-name">{player.item === 1 ? 'Player' : 'Robot'}#{player.index + 1}</Text> </View> </View> )) @@ -52,9 +81,20 @@ function NewTable() { </View> </View> <View className='submit-btn-wrap'> - <Button className='submit-btn'>CREATE</Button> + <Button className='submit-btn' loading={submitLoading} onClick={createTable}>CREATE</Button> </View> </View> + + <Picker + title='Player OR Robot' + isVisible={choosingPlace > -1} + listData={[{value: 1, text: 'Player'}, {value: 2, text: 'Robot'}]} + onConfirm={(values) => { + players[choosingPlace] = values[0]; + setPlayers([...players]); + }} + onClose={() => setChoosingPlace(-1)} + /> </View> ) } diff --git a/src/pages/table/table.jsx b/src/pages/table/table.jsx index 9006ba4..6090a0c 100644 --- a/src/pages/table/table.jsx +++ b/src/pages/table/table.jsx @@ -8,10 +8,14 @@ import Title from '@/components/title' import Card from '@/components/card' import coinIcon from '@/assets/icon/coin-1.svg' import coin2Icon from '@/assets/icon/coin-2.svg' +import plus1Icon from '@/assets/icon/plus-1.svg' +import sub1Icon from '@/assets/icon/sub-1.svg' +import Taro from '@tarojs/taro' + function Table() { - const {username, nickname, avatar} = useSelector(state => state.user); + const { username, nickname, avatar } = useSelector(state => state.user); const players = [1, 2, 3, 4, 5, 6]; const playerRows = players.reduce((arr, item, idx) => { @@ -32,7 +36,7 @@ function Table() { { row.map((player, j) => ( <View key={j} className="player-wrap"> - <View className={cnames("player-no",{"player-no-running": player.index===1})}><Text>#{player.index}</Text></View> + <View className={cnames("player-no", { "player-no-running": player.index === 1 })}><Text>#{player.index}</Text></View> <View className="card1"><Card w={42} h={48} dot={12} suit={2} /></View> <View className="card2"><Card w={42} h={48} dot={12} suit={3} /></View> <View className="player"> @@ -56,7 +60,7 @@ function Table() { <View className="desktop-wrap"> <View className="desktop-cards"> { - [1,2,3,4,5].map((item, i) => ( + [1, 2, 3, 4, 5].map((item, i) => ( <Card key={i} w={42} h={48} dot={12} suit={2} /> )) } @@ -69,7 +73,7 @@ function Table() { <View className="d-wallet-amount">22,088,000</View> </View> </View> - <View className="chatbox" style={{display: 'none'}}> + <View className="chatbox" style={{ display: 'none' }}> <View className='chat-window'> <View className="line">Kelly(#1)进入了游戏</View> <View className="line">Apollo(#2)进入了游戏</View> @@ -83,26 +87,42 @@ function Table() { <View className="self-box"> <View className="self-wrap"> - <View className="hand-card1"><Card w={42} h={48} dot={12} suit={3} /></View> - <View className="hand-card2"><Card w={42} h={48} dot={12} suit={3} /></View> - <View className="hand-five"> + <View className="hand-card1"><Card w={42} h={48} dot={12} suit={3} /></View> + <View className="hand-card2"><Card w={42} h={48} dot={12} suit={3} /></View> + <View className="hand-five"> { - [1,2,3,4,5].map((_, i) => ( + [1, 2, 3, 4, 5].map((_, i) => ( <View key={i} className="hand-five-item"><Card w={30} h={36} dot={12} suit={2} /></View> )) } + </View> + <View className='u-opt' style={{ display: 'flex' }}> + <View> + <View className="s-info-wrap"> + <View className="s-avatar"><Image className="s-avatar-img" src={avatar} /></View> + <View className="s-username"><Text>Prometheus</Text></View> + </View> + <View className="s-wallet-wrap"> + <View className="s-w-amount-wrap"> + <View className="s-w-amount-icon"><Image className="s-w-amount-icon-img" src={coin2Icon} /></View> + <View className="s-w-amount"><Text>3,000,000</Text></View> + </View> + <View className="s-w-pct"><Text>0%</Text></View> + </View> </View> - <View className="s-info-wrap"> - <View className="s-avatar"><Image className="s-avatar-img" src={avatar} /></View> - <View className="s-username"><Text>Prometheus</Text></View> - </View> - <View className="s-wallet-wrap"> - <View className="s-w-amount-wrap"> - <View className="s-w-amount-icon"><Image className="s-w-amount-icon-img" src={coin2Icon}/></View> - <View className="s-w-amount"><Text>3,000,000</Text></View> + <View className="control"> + <View className="ctl-opt-wrap"> + <View className="amount-input"><Input placeholder='stake' defaultValue={0} /></View> + <View className="amount-sub"><Image className="amount-sub-icon" src={sub1Icon} /></View> + <View className="amount-plus"><Image className="amount-plus-icon" src={plus1Icon} /></View> + </View> + <View className="ctl-opt-wrap" style={{ marginTop: Taro.pxTransform(10) }}> + <View className="ctl-opt"><Button className="ctl-opt-btn-1">让牌</Button></View> + <View className="ctl-opt"><Button className="ctl-opt-btn-2">弃牌</Button></View> + <View className="ctl-opt"><Button className="ctl-opt-btn-3">跟注</Button></View> </View> - <View className="s-w-pct"><Text>0%</Text></View> </View> + </View> </View> </View> </View> diff --git a/src/pages/table/table.scss b/src/pages/table/table.scss index 3232dfb..0961ecb 100644 --- a/src/pages/table/table.scss +++ b/src/pages/table/table.scss @@ -7,10 +7,12 @@ // width: 405px; margin: 46px 26px 0 26px; } + .player-wrap { // flex: 0 0 50%; position: relative; } + .player-no { position: absolute; top: -25px; @@ -23,23 +25,28 @@ border-radius: 50%; text-align: center; } + .player-no-running { font-weight: bold; color: #fff; background-color: #EE914E; } - .card1, .card2 { + .card1, + .card2 { position: absolute; z-index: 999; top: -27px; } + .card1 { left: 60px; } + .card2 { left: 104px; } + .player { width: 153px; height: 69px; @@ -51,14 +58,17 @@ border: 1px solid #000; border-radius: 5px; } + .avatar-wrap { margin: 10px 0 10px 4px; } + .avatar { border-radius: 5px; height: 50px; width: 50px; } + .player-name { font-size: 12px; line-height: 20px; @@ -75,6 +85,7 @@ display: flex; margin-top: 3px; } + .wallet { display: flex; background-color: #EE914E; @@ -85,19 +96,23 @@ box-sizing: border-box; padding: 0 4px; } + .wallet-icon { //margin: 2px 0 2px 4px; line-height: 22px; } + .wallet-icon-img { width: 14px; height: 14px; } + .wallet-amount { font-size: 12px; color: #fff; line-height: 18px; } + .pct { background-color: #807878; font-size: 12px; @@ -115,9 +130,9 @@ flex-wrap: nowrap; margin: 33px 26px 0 26px; } - .desktop-wrap { - } + .desktop-wrap {} + .desktop-cards { width: 250px; height: 95px; @@ -128,6 +143,7 @@ align-items: center; flex-wrap: nowrap; } + .desktop-wallet { margin-top: 4px; width: 250px; @@ -139,6 +155,7 @@ box-sizing: border-box; padding: 0 5px; } + .d-wallet-icon { font-size: 18px; color: #fff; @@ -147,19 +164,23 @@ display: flex; align-items: center; } + .d-wallet-icon-img { height: 28px; width: 28px; } + .d-wallet-icon-text { margin-left: 7px; } + .d-wallet-amount { font-size: 18px; color: #fff; font-weight: bold; line-height: 36px; } + .chatbox { width: 159px; height: 134px; @@ -167,6 +188,7 @@ border-radius: 5px; padding: 5px; } + .chat-window { .line { @@ -174,32 +196,37 @@ line-height: 12px; } } - .chat-panel { - } + .chat-panel {} .self-box { // width: 100%; height: 109px; background-color: #EE914E; margin: 30px 26px 0 26px; - border-radius: 5px;; + border-radius: 5px; + ; } + .self-wrap { position: relative; } + .s-info-wrap { display: flex; height: 89px; } + .s-avatar { align-self: center; margin-left: 6px; } + .s-avatar-img { width: 65px; height: 65px; } + .s-username { font-size: 12px; font-weight: bold; @@ -210,10 +237,12 @@ margin-bottom: 16px; margin-left: 10px; } + .s-wallet-wrap { display: flex; width: 156px; } + .s-w-amount-wrap { width: 97px; height: 18px; @@ -226,20 +255,24 @@ box-sizing: border-box; padding: 0 5px 0 3px; } + .s-w-amount-icon { width: 14px; height: 14px; margin-top: -2px; } + .s-w-amount-icon-img { width: 15px; height: 15px; } + .s-w-amount { color: #3D3D3D; font-size: 12px; line-height: 18px; } + .s-w-pct { width: 50px; height: 18px; @@ -251,14 +284,18 @@ background-color: #fff; margin-left: 8px; } - .hand-card1,.hand-card2 { + + .hand-card1, + .hand-card2 { position: absolute; top: -20px; left: 80px; } + .hand-card2 { left: 126px; } + .hand-five { position: absolute; left: 188px; @@ -271,7 +308,84 @@ justify-content: space-evenly; // background-color: #807878; } - .hand-five-item { + .hand-five-item {} + + .u-opt { + display: flex; + justify-content: flex-start; + } + + .control { + align-self: flex-end; + margin-left: 30px; + } + + .ctl-opt-wrap { + display: flex; + width: 170px; + justify-content: space-between; + } + + .amount-input { + width: 80px; + height: 24px; + box-sizing: border-box; + text-align: center; + background-color: #F1D09F; + border-radius: 8px; + font-size: 12px; + padding: 4px 0; + color: 3D3D3D; + } + + .amount-sub, + .amount-plus { + width: 36px; + height: 24px; + background-color: #fff; + border-radius: 8px; + text-align: center; + } + + .amount-sub-icon, + .amount-plus-icon { + width: 16px; + height: 13px; + } + + .amount-plus-icon { + width: 16px; + height: 13px; + } + + .ctl-opt {} + + .ctl-opt-btn-1, + .ctl-opt-btn-2, + .ctl-opt-btn-3 { + width: 53.25px; + height: 24px; + border-radius: 8px; + font-size: 12px; + font-weight: bold; + padding: 0; + line-height: 24px; + border: 0; + } + + .ctl-opt-btn-1 { + background-color: #F1D09F; + color: #EA8C28; + } + + .ctl-opt-btn-2 { + background-color: #F1D09F; + color: #D05959; + } + + .ctl-opt-btn-3 { + background-color: #FF4400; + color: #F1D09F; } } diff --git a/src/utils/application.js b/src/utils/application.js index 8db4f76..ec64351 100644 --- a/src/utils/application.js +++ b/src/utils/application.js @@ -53,3 +53,14 @@ export const navigateBack = function(option = {delta: 1}) { } return Taro.navigateBack(option); } + +export const showToast = function(option = {icon: 'success'}) { + let {title} = option + if (!title) { return } + title = typeof(title) === 'string' ? title : title.msg || title.message; + if (!title) { + console.log('no title toast:', option.title) + return + } + Taro.showToast(option) +}