zoukankan      html  css  js  c++  java
  • day118:MoFang:根据激活/未激活的状态分别显示树桩&种植植物&解锁树桩&化肥/修剪/浇水/宠物粮小图标数字的显示

    登录

    1.根据激活状态和未激活状态分别显示树桩

    2.用户使用植物道具进行果树种植

    3.解锁树桩

    4.化肥/修剪/浇水/宠物粮小图标显示

    种植栏的功能实现

    1. 客户端需要的植物相关参数: 总树桩数量, 当前用户激活树桩数量, 当前种植的树桩数量, 树桩列表状态

    2. 客户端根据激活状态和未激活状态分别显示树桩

    3. 服务端在用户进入种植园时提供上面的数据

    4. 用户如果第一次进入种植园需要初始化参数

    5. 数据库中必须预设树桩的相关参数

    6. 用户可以使用道具对树桩进行响应的操作

    7. 用户可以在背包里面进行果树的种植

    1.根据激活状态和未激活状态分别显示树桩

    1.my_orchard.html-先确定前端需要哪些数据

    <!DOCTYPE html>
    <html>
    <head>
        <title>用户中心</title>
        <meta name="viewport" content="width=device-width,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no">
        <meta charset="utf-8">
        <link rel="stylesheet" href="../static/css/main.css">
        <script src="../static/js/vue.js"></script>
        <script src="../static/js/axios.js"></script>
        <script src="../static/js/main.js"></script>
        <script src="../static/js/uuid.js"></script>
        <script src="../static/js/settings.js"></script>
        <script src="../static/js/socket.io.js"></script>
    </head>
    <body>
        <div class="app orchard orchard-frame" id="app">
        <div class="background">
          <img class="grassland2" src="../static/images/grassland2.png" alt="">
          <img class="mushroom1" src="../static/images/mushroom1.png" alt="">
          <img class="stake1" src="../static/images/stake1.png" alt="">
          <img class="stake2" src="../static/images/stake2.png" alt="">
        </div>
        <div class="pet-box">
          <div class="pet">
                    <img v-if="pet_list.length > 0" class="pet-item" :src="settings.static_url+pet_list[0].image" alt="">
          </div>
                <div class="pet" v-if="pet_number > 1">
                    <img v-if="pet_list.length>1" class="pet-item" :src="settings.static_url+pet_list[1].image" alt="">
          </div>
          <div class="pet turned_off" v-if="pet_number==1">
            <img class="turned_image" src="../static/images/turned_off.png" alt="">
            <p>请购买宠物</p>
          </div>
        </div>
        <div class="tree-list">
          <div class="tree-box">
                    <!-- 已激活但是未种植的树桩列表 -->
                    <div class="tree" v-for="i in active_tree">
                        <img src="../static/images/tree1.png" alt="">
                    </div>
                    <!-- 未激活树桩列表 -->
                    <div class="tree" v-for="i in lock_tree">
                        <img src="../static/images/tree0.png" alt="">
                    </div>
          </div>
    
        </div>
        <div class="prop-list">
          <div class="prop">
            <img src="../static/images/prop1.png" alt="">
            <span>1</span>
            <p>化肥</p>
          </div>
          <div class="prop">
            <img src="../static/images/prop2.png" alt="">
            <span>0</span>
            <p>修剪</p>
          </div>
          <div class="prop">
            <img src="../static/images/prop3.png" alt="">
            <span>1</span>
            <p>浇水</p>
          </div>
          <div class="prop">
            <img src="../static/images/prop4.png" alt="">
            <span>1</span>
            <p>宠物粮</p>
          </div>
        </div>
        <div class="pet-hp-list">
          <div class="pet-hp" v-for="pet in pet_list">
            <p>宠物1 饱食度</p>
            <div class="hp">
              <div :style="{ pet.hp+'%'}" class="process">{{pet.hp}}%</div>
            </div>
          </div>
        </div>
        </div>
        <script>
        apiready = function(){
            init();
            new Vue({
                el:"#app",
                data(){
                    return {
              namespace: '/mofang',
              token:"",
              socket: null,
                        pet_list:[],
                        user_tree_data:{
                            "total_tree":9,        // 总树桩数量
                            "user_tree_number": 5, // 当前用户激活树桩数量
                            "user_tree_list":[     // 当前种植的树桩列表状态
    
                            ],
                        },
                        pet_number:[],
              timeout: 0,
                        prev:{name:"",url:"",params:{}},
                        current:{name:"orchard",url:"orchard.html",params:{}},
                    }
                },
                computed:{
                    // 已激活但是未使用的树桩
                    active_tree(){
                        return parseInt(this.user_tree_data.user_tree_number - this.user_tree_data.user_tree_list.length);
                    },
                    // 未激活的剩余树桩
                    lock_tree(){
                        return parseInt( this.user_tree_data.total_tree-this.user_tree_data.user_tree_number);
                    },
                },
          created(){
                    this.show_pet_list();
          },
                methods:{
                    show_pet_list(){
                        api.addEventListener({
                                name: 'pet_show_success'
                        }, (ret, err)=>{
                                if( ret ){
                                    // 用户购买道具
                                    this.pet_list = this.game.get("pet_list");
                                    this.pet_number = parseInt(this.game.get("pet_number"));
                                }
                        });
                    }
                }
            });
        }
        </script>
    </body>
    </html>
    my_orchard.html-先确定前端需要哪些数据

    2.my_orchard.html-确定当前种植的树桩状态列表的数据结构

    user_tree_data:{
        "total_tree":9,        // 总树桩数量
        "user_tree_number": 5, // 当前用户激活树桩数量
        "user_tree_list":[     // 当前种植的树桩列表状态
                    { // 树桩状态
                        "time":1609808084, // 种植时间
                        "status":4,        // 状态
                        "has_time": 300,   // 状态时间
                    },
                ],
    },
        pet_number:[],
            tree_status:{
                "tree0": "../static/images/tree0.png", // 树桩
                "tree1": "../static/images/tree1.png", // 空桩
                "tree2": "../static/images/tree2.png", // 幼苗
                "tree3": "../static/images/tree3.png", // 成长
                "tree4": "../static/images/tree4.png", // 成熟
            },

    客户端显示种植植物相关的参数,my_orchard.html代码

    <!DOCTYPE html>
    <html>
    <head>
        <title>用户中心</title>
        <meta name="viewport" content="width=device-width,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no">
        <meta charset="utf-8">
        <link rel="stylesheet" href="../static/css/main.css">
        <script src="../static/js/vue.js"></script>
        <script src="../static/js/axios.js"></script>
        <script src="../static/js/main.js"></script>
        <script src="../static/js/uuid.js"></script>
        <script src="../static/js/settings.js"></script>
        <script src="../static/js/socket.io.js"></script>
    </head>
    <body>
        <div class="app orchard orchard-frame" id="app">
        <div class="background">
          <img class="grassland2" src="../static/images/grassland2.png" alt="">
          <img class="mushroom1" src="../static/images/mushroom1.png" alt="">
          <img class="stake1" src="../static/images/stake1.png" alt="">
          <img class="stake2" src="../static/images/stake2.png" alt="">
        </div>
        <div class="pet-box">
          <div class="pet">
                    <img v-if="pet_list.length > 0" class="pet-item" :src="settings.static_url+pet_list[0].image" alt="">
          </div>
                <div class="pet" v-if="pet_number > 1">
                    <img v-if="pet_list.length>1" class="pet-item" :src="settings.static_url+pet_list[1].image" alt="">
          </div>
          <div class="pet turned_off" v-if="pet_number==1">
            <img class="turned_image" src="../static/images/turned_off.png" alt="">
            <p>请购买宠物</p>
          </div>
        </div>
        <div class="tree-list">
          <div class="tree-box">
                    <div class="tree" v-for="tree in user_tree_data.user_tree_list">
                        <img v-if="tree.status==2" src="../static/images/tree2.png" alt="">
                        <img v-if="tree.status==3" src="../static/images/tree3.png" alt="">
                        <img v-if="tree.status==4" src="../static/images/tree4.png" alt="">
                    </div>
                    <!-- 已激活但是未种植的树桩列表 -->
                    <div class="tree" v-for="i in active_tree">
                        <img src="../static/images/tree1.png" alt="">
                    </div>
                    <!-- 未激活树桩列表 -->
                    <div class="tree" v-for="i in lock_tree">
                        <img src="../static/images/tree0.png" alt="">
                    </div>
          </div>
    
        </div>
        <div class="prop-list">
          <div class="prop">
            <img src="../static/images/prop1.png" alt="">
            <span>1</span>
            <p>化肥</p>
          </div>
          <div class="prop">
            <img src="../static/images/prop2.png" alt="">
            <span>0</span>
            <p>修剪</p>
          </div>
          <div class="prop">
            <img src="../static/images/prop3.png" alt="">
            <span>1</span>
            <p>浇水</p>
          </div>
          <div class="prop">
            <img src="../static/images/prop4.png" alt="">
            <span>1</span>
            <p>宠物粮</p>
          </div>
        </div>
        <div class="pet-hp-list">
          <div class="pet-hp" v-for="pet in pet_list">
            <p>宠物1 饱食度</p>
            <div class="hp">
              <div :style="{ pet.hp+'%'}" class="process">{{pet.hp}}%</div>
            </div>
          </div>
        </div>
        </div>
        <script>
        apiready = function(){
            init();
            new Vue({
                el:"#app",
                data(){
                    return {
              namespace: '/mofang',
              token:"",
              socket: null,
                        pet_list:[],
                        user_tree_data:{
                            "total_tree":9,        // 总树桩数量
                            "user_tree_number": 5, // 当前用户激活树桩数量
                            "user_tree_list":[     // 当前种植的树桩列表状态
                                    { // 树桩状态
                                         "time":1609808084, // 种植时间
                                         "status":4,        // 状态
                                         "has_time": 300,   // 状态时间
                                    },
                            ],
                        },
                        pet_number:[],
                        tree_status:{
                            "tree0": "../static/images/tree0.png", // 树桩
                            "tree1": "../static/images/tree1.png", // 空桩
                            "tree2": "../static/images/tree2.png", // 幼苗
                            "tree3": "../static/images/tree3.png", // 成长
                            "tree4": "../static/images/tree4.png", // 成熟
                        },
              timeout: 0,
                        prev:{name:"",url:"",params:{}},
                        current:{name:"orchard",url:"orchard.html",params:{}},
                    }
                },
                computed:{
                    // 已激活但是未使用的树桩
                    active_tree(){
                        return parseInt(this.user_tree_data.user_tree_number - this.user_tree_data.user_tree_list.length);
                    },
                    // 未激活的剩余树桩
                    lock_tree(){
                        return parseInt( this.user_tree_data.total_tree-this.user_tree_data.user_tree_number);
                    },
                },
          created(){
                    this.show_pet_list();
          },
                methods:{
                    show_pet_list(){
                        api.addEventListener({
                                name: 'pet_show_success'
                        }, (ret, err)=>{
                                if( ret ){
                                    // 用户购买道具
                                    this.pet_list = this.game.get("pet_list");
                                    this.pet_number = parseInt(this.game.get("pet_number"));
                                }
                        });
                    }
                }
            });
        }
        </script>
    </body>
    </html>
    my_orchard.html-确定当前种植的树桩状态列表的数据结构

    3.服务端提供种植植物的相关数据

    1.在setting表中添加种植植物的参数数据

    添加参数数据,SQL代码:

    INSERT INTO mofang.mf_orchard_setting (id, name, is_deleted, orders, status, created_time, updated_time, title, value) VALUES (19, 'tree_status_0', 0, 1, 1, '2020-12-30 17:40:46', '2020-12-30 17:40:44', '树桩状态-未解锁', 'tree0.png');
    INSERT INTO mofang.mf_orchard_setting (id, name, is_deleted, orders, status, created_time, updated_time, title, value) VALUES (20, 'tree_status_1', 0, 1, 1, '2020-12-30 17:40:46', '2020-12-30 17:40:44', '树桩状态-未种植', 'tree1.png');
    INSERT INTO mofang.mf_orchard_setting (id, name, is_deleted, orders, status, created_time, updated_time, title, value) VALUES (21, 'tree_status_2', 0, 1, 1, '2020-12-30 17:40:46', '2020-12-30 17:40:44', '树桩状态-幼苗', 'tree2.png');
    INSERT INTO mofang.mf_orchard_setting (id, name, is_deleted, orders, status, created_time, updated_time, title, value) VALUES (22, 'tree_status_3', 0, 1, 1, '2020-12-30 17:40:46', '2020-12-30 17:40:44', '树桩状态-成长', 'tree3.png');
    INSERT INTO mofang.mf_orchard_setting (id, name, is_deleted, orders, status, created_time, updated_time, title, value) VALUES (23, 'tree_status_4', 0, 1, 1, '2020-12-30 17:40:46', '2020-12-30 17:40:44', '树桩状态-成熟', 'tree4.png');

    2.后端返回当前用户种植的植物信息

    服务端返回当前用户种植的植物信息,socket.py, 代码:

    from application import socketio
    from flask import request
    from application.apps.users.models import User
    from flask_socketio import join_room, leave_room
    from application import mongo
    from .models import Goods,Setting,db
    from status import APIStatus as status
    from message import ErrorMessage as errmsg
    
    
    # 断开socket通信
    @socketio.on("disconnect", namespace="/mofang")
    def user_disconnect():
        print("用户%s退出了种植园" % request.sid )
    
    @socketio.on("login", namespace="/mofang")
    def user_login(data):
        # 分配房间
        room = data["uid"]
        join_room(room)
        # 保存当前用户和sid的绑定关系
        # 判断当前用户是否在mongo中有记录
        query = {
            "_id": data["uid"]
        }
        ret = mongo.db.user_info_list.find_one(query)
        if ret:
            mongo.db.user_info_list.update_one(query,{"$set":{"sid": request.sid}})
        else:
            mongo.db.user_info_list.insert_one({
            "_id": data["uid"],
            "sid": request.sid,
        })
    
        # 返回种植园的相关配置参数
        orchard_settings = {}
        setting_list = Setting.query.filter(Setting.is_deleted==False, Setting.status==True).all()
        """
        现在的格式:
            [<Setting package_number_base>, <Setting package_number_max>, <Setting package_unlock_price_1>]
        需要返回的格式:
            {
                package_number_base:4,
                package_number_max: 32,
                ...
            }
        """
        for item in setting_list:
            orchard_settings[item.name] = item.value
    
        # 返回当前用户相关的配置参数
        user_settings = {}
        # 从mongo中查找用户信息,判断用户是否激活了背包格子
        user_dict = mongo.db.user_info_list.find_one({"sid":request.sid})
        # 背包格子
        if user_dict.get("package_number") is None:
            user_settings["package_number"]  = orchard_settings.get("package_number_base",4)
            mongo.db.user_info_list.update_one({"sid":request.sid},{"$set":{"package_number": user_settings["package_number"]}})
        else:
            user_settings["package_number"]  = user_dict.get("package_number")
    
        """种植园植物信息"""
         # 总树桩数量
        setting = Setting.query.filter(Setting.name == "user_total_tree").first()
        if setting is None:
            tree_total = 9
        else:
            tree_total = int(setting.value)
    
        # 用户已经激活的树桩
        setting = Setting.query.filter(Setting.name == "user_active_tree").first()
        if setting is None:
            user_tree_number = 3
        else:
            user_tree_number = int(setting.value)
        user_tree_number = user_dict.get("user_tree_number",user_tree_number)
    
        # 种植的植物列表
        user_tree_list = user_dict.get("user_tree_list", [])
    
        socketio.emit("login_response", {
            "errno":status.CODE_OK,
            "errmsg":errmsg.ok,
            "orchard_settings":orchard_settings,
            "user_settings":user_settings,
            "tree_total":tree_total,
            "user_tree_number":user_tree_number,
            "user_tree_list":user_tree_list,
        }, namespace="/mofang", room=room)
    
    @socketio.on("user_buy_prop", namespace="/mofang")
    def user_buy_prop(data):
        """用户购买道具"""
        room = request.sid
        # 从mongo中获取当前用户信息
        user_info = mongo.db.user_info_list.find_one({"sid":request.sid})
        user = User.query.get(user_info.get("_id"))
        if user is None:
            socketio.emit("user_buy_prop_response", {"errno":status.CODE_NO_USER,"errmsg":errmsg.user_not_exists}, namespace="/mofang", room=room)
            return
    
        # 判断背包物品存储是否达到上限
        use_package_number = int(user_info.get("use_package_number",0)) # 当前诗经使用的格子数量
        package_number = int(user_info.get("package_number",0))         # 当前用户已经解锁的格子数量
        # 本次购买道具需要使用的格子数量
        setting = Setting.query.filter(Setting.name == "td_prop_max").first()
        if setting is None:
            td_prop_max = 10
        else:
            td_prop_max = int(setting.value)
    
        # 计算购买道具以后需要额外占用的格子数量
        if ("prop_%s" % data["pid"]) in user_info.get("prop_list",{}):
            """曾经购买过当前道具"""
            prop_num = int( user_info.get("prop_list")["prop_%s" % data["pid"]]) # 购买前的道具数量
            new_prop_num = prop_num+int(data["num"]) # 如果成功购买道具以后的数量
            old_td_num = prop_num // td_prop_max
            if prop_num % td_prop_max > 0:
                old_td_num+=1
            new_td_num = new_prop_num // td_prop_max
            if new_prop_num % td_prop_max > 0:
                new_td_num+=1
            td_num = new_td_num - old_td_num
        else:
            """新增购买的道具"""
            # 计算本次购买道具需要占用的格子数量
    
            if int(data["num"]) > td_prop_max:
                """需要多个格子"""
                td_num = int(data["num"]) // td_prop_max
                if int(data["num"]) % td_prop_max > 0:
                    td_num+=1
            else:
                """需要一个格子"""
                td_num = 1
    
        if use_package_number+td_num > package_number:
            """超出存储上限"""
            socketio.emit("user_buy_prop_response", {"errno": status.CODE_NO_PACKAGE, "errmsg": errmsg.no_package},
                          namespace="/mofang", room=room)
            return
    
        # 从mysql中获取商品价格
        prop = Goods.query.get(data["pid"])
        if user.money > 0: # 当前商品需要通过RMB购买
            if float(user.money) < float(prop.price) * int(data["num"]):
                socketio.emit("user_buy_prop_response", {"errno":status.CODE_NO_MONEY,"errmsg":errmsg.money_no_enough}, namespace="/mofang", room=room)
                return
        else:
            """当前通过果子进行购买"""
            if int(user.credit) < int(prop.credit) * int(data["num"]):
                socketio.emit("user_buy_prop_response", {"errno": status.CODE_NO_CREDIT, "errmsg": errmsg.credit_no_enough},
                              namespace="/mofang", room=room)
                return
    
        # 从mongo中获取用户列表信息,提取购买的商品数量进行累加和余额
        query = {"sid": request.sid}
        if user_info.get("prop_list") is None:
            """此前没有购买任何道具"""
            message = {"$set":{"prop_list":{"prop_%s" % prop.id:int(data["num"])}}}
            mongo.db.user_info_list.update_one(query,message)
        else:
            """此前有购买了道具"""
            prop_list = user_info.get("prop_list") # 道具列表
            if ("prop_%s" % prop.id) in prop_list:
                """如果再次同一款道具"""
                prop_list[("prop_%s" % prop.id)] = prop_list[("prop_%s" % prop.id)] + int(data["num"])
            else:
                """此前没有购买过这种道具"""
                prop_list[("prop_%s" % prop.id)] = int(data["num"])
    
            mongo.db.user_info_list.update_one(query, {"$set":{"prop_list":prop_list}})
    
        # 扣除余额或果子
        if prop.price > 0:
            user.money = float(user.money) - float(prop.price) * int(data["num"])
        else:
            user.credit = int(user.credit) - int(prop.credit) * int(data["num"])
    
        db.session.commit()
    
        # 返回购买成功的信息
        socketio.emit("user_buy_prop_response", {"errno":status.CODE_OK,"errmsg":errmsg.ok}, namespace="/mofang", room=room)
        # 返回最新的用户道具列表
        user_prop()
    
    @socketio.on("user_prop", namespace="/mofang")
    def user_prop():
        """用户道具"""
        userinfo = mongo.db.user_info_list.find_one({"sid":request.sid})
        prop_list = userinfo.get("prop_list",{})
        prop_id_list = []
        for prop_str,num in prop_list.items():
            pid = int(prop_str[5:])
            prop_id_list.append(pid)
    
        data = []
        prop_list_data = Goods.query.filter(Goods.id.in_(prop_id_list)).all()
        setting = Setting.query.filter(Setting.name == "td_prop_max").first()
        if setting is None:
            td_prop_max = 10
        else:
            td_prop_max = int(setting.value)
    
        for prop_data in prop_list_data:
            num = int( prop_list[("prop_%s" % prop_data.id)])
            if td_prop_max > num:
                data.append({
                    "num": num,
                    "image": prop_data.image,
                    "pid": prop_data.id
                })
            else:
                padding_time = num // td_prop_max
                padding_last = num % td_prop_max
                arr = [{
                    "num": td_prop_max,
                    "image": prop_data.image,
                    "pid": prop_data.id
                }] * padding_time
                if padding_last != 0:
                    arr.append({
                        "num": padding_last,
                        "image": prop_data.image,
                        "pid": prop_data.id
                    })
                data = data + arr
        mongo.db.user_info_list.update_one({"sid":request.sid},{"$set":{"use_package_number":len(data)}})
        room = request.sid
        socketio.emit("user_prop_response", {
            "errno": status.CODE_OK,
            "errmsg": errmsg.ok,
            "data":data,
        }, namespace="/mofang",
                      room=room)
    
    @socketio.on("unlock_package", namespace="/mofang")
    def unlock_package():
        """解锁背包"""
        # 从mongo获取当前用户解锁的格子数量
        user_info = mongo.db.user_info_list.find_one({"sid":request.sid})
        user = User.query.get(user_info.get("_id"))
        if user is None:
            socketio.emit("unlock_package_response", {"errno":status.CODE_NO_USER,"errmsg":errmsg.user_not_exists}, namespace="/mofang", room=room)
            return
    
        package_number = int(user_info.get("package_number"))
        num = 7 - (32 - package_number) // 4  # 没有解锁的格子
    
        # 从数据库中获取解锁背包的价格
        setting = Setting.query.filter(Setting.name == "package_unlock_price_%s" % num).first()
        if setting is None:
            unlock_price = 0
        else:
            unlock_price = int(setting.value)
    
        # 判断是否有足够的积分或者价格
        room = request.sid
        if user.money < unlock_price:
            socketio.emit("unlock_package_response", {"errno": status.CODE_NO_MONEY, "errmsg": errmsg.money_no_enough},
                          namespace="/mofang", room=room)
            return
    
        # 解锁成功
        user.money = float(user.money) - float(unlock_price)
        db.session.commit()
    
        # mongo中调整数量
        mongo.db.user_info_list.update_one({"sid":request.sid},{"$set":{"package_number": package_number+1}})
        # 返回解锁的结果
        socketio.emit("unlock_package_response", {
            "errno": status.CODE_OK,
            "errmsg": errmsg.ok},
                      namespace="/mofang", room=room)
    import math
    from application import redis
    @socketio.on("pet_show", namespace="/mofang")
    def pet_show():
        """显示宠物"""
        room = request.sid
        user_info = mongo.db.user_info_list.find_one({"sid":request.sid})
        user = User.query.get(user_info.get("_id"))
        if user is None:
            socketio.emit("pet_show_response", {"errno":status.CODE_NO_USER,"errmsg":errmsg.user_not_exists}, namespace="/mofang", room=room)
            return
    
        # 获取宠物列表
        pet_list = user_info.get("pet_list", [])
    
        """
        pet_list: [
          { 
             "pid":11,
             "image":"pet.png",
             "hp":100%,
             "created_time":xxxx-xx-xx xx:xx:xx,
             "skill":"70%",
             "has_time":30天
          },
        ]
        """
        # 从redis中提取当前宠物的饱食度和有效期
        for key,pet in enumerate(pet_list):
            pet["hp"] = math.ceil( redis.ttl("pet_%s_%s_hp" % (user.id,key+1)) / 86400 * 100 )
            pet["has_time"] = redis.ttl("pet_%s_%s_expire" % (user.id,key+1))
    
        pet_number = user_info.get("pet_number", 1)
        socketio.emit(
            "pet_show_response",
            {
                "errno": status.CODE_OK,
                "errmsg": errmsg.ok,
                "pet_list": pet_list,
                "pet_number": pet_number,
            },
            namespace="/mofang",
            room=room
        )
    
    from datetime import datetime
    @socketio.on("use_prop", namespace="/mofang")
    def use_prop(pid):
        """使用宠物"""
        # 1. 判断当前的宠物数量
        room = request.sid
        user_info = mongo.db.user_info_list.find_one({"sid":request.sid})
        user = User.query.get(user_info.get("_id"))
        if user is None:
            socketio.emit("pet_use_response", {"errno":status.CODE_NO_USER,"errmsg":errmsg.user_not_exists}, namespace="/mofang", room=room)
            return
    
        # 获取宠物列表
        pet_list = user_info.get("pet_list", [])
        if len(pet_list) > 1:
            socketio.emit("pet_use_response", {"errno":status.CODE_NO_EMPTY,"errmsg":errmsg.pet_not_empty}, namespace="/mofang", room=room)
            return
    
        # 2. 是否有空余的宠物栏位
        pet_number = user_info.get("pet_number",1)
        if pet_number <= len(pet_list):
            socketio.emit("pet_use_response", {"errno":status.CODE_NO_EMPTY,"errmsg":errmsg.pet_not_empty}, namespace="/mofang", room=room)
            return
    
        # 3. 初始化当前宠物信息
        pet = Goods.query.get(pid)
        if pet is None:
            socketio.emit("pet_use_response", {"errno":status.CODE_NO_SUCH_PET,"errmsg":errmsg.not_such_pet}, namespace="/mofang", room=room)
            return
    
        # 获取有效期和防御值
        exp_data = Setting.query.filter(Setting.name=="pet_expire_%s" % pid).first()
        ski_data = Setting.query.filter(Setting.name=="pet_skill_%s" % pid).first()
    
        if exp_data is None:
            # 默认7天有效期
            expire = 7
        else:
            expire = exp_data.value
    
        if ski_data is None:
            skill  = 10
        else:
            skill  = ski_data.value
    
        # 在redis中设置当前宠物的饱食度
        pipe = redis.pipeline()
        pipe.multi()
        pipe.setex("pet_%s_%s_hp" % (user.id, len(pet_list)+1), 24*60*60, "_")
        pipe.setex("pet_%s_%s_expire" % (user.id, len(pet_list)+1), int(expire)*24*60*60, "_")
        pipe.execute()
    
        # 基本保存到mongo
        mongo.db.user_info_list.update_one({"sid":request.sid},{"$push":{"pet_list":{
             "pid": pid,
             "image": pet.image,
             "created_time": int( datetime.now().timestamp() ),
             "skill": skill,
          }}})
    
        """
        db.user_info_list.updateOne({"_id":"52"},{"$push":{"pet_list":{
                 "pid": 2,
                 "image": "pet1.png",
                 "created_time": 1609727155,
                 "skill": 30,
              }}})
        """
    
        # 扣除背包中的道具数量
        prop_list = user_info.get("prop_list",{})
        for key,value in prop_list.items():
            if key == ("prop_%s" % pid):
                if int(value) > 1:
                    prop_list[key] = int(value) - 1
                else:
                    prop_list.pop(key)
                break
    
        mongo.db.user_info_list.update_one({"sid":room},{"$set":{"prop_list":prop_list}})
        user_prop()
        pet_show()
    
        socketio.emit("pet_use_response", {"errno": status.CODE_OK, "errmsg": errmsg.ok},
                      namespace="/mofang", room=room)
    用户登录种植园成功后,后端返回当前用户种植的植物信息

    4.orchard.html-登陆种植园成功后将植物相关信息获取并保存在前端,并发送事件

    <!DOCTYPE html>
    <html>
    <head>
        <title>用户中心</title>
        <meta name="viewport" content="width=device-width,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no">
        <meta charset="utf-8">
        <link rel="stylesheet" href="../static/css/main.css">
        <script src="../static/js/vue.js"></script>
        <script src="../static/js/axios.js"></script>
        <script src="../static/js/main.js"></script>
        <script src="../static/js/uuid.js"></script>
        <script src="../static/js/settings.js"></script>
        <script src="../static/js/socket.io.js"></script>
    </head>
    <body>
        <div class="app orchard" id="app">
        <img class="music" :class="music_play?'music2':''" @click="music_play=!music_play" src="../static/images/player.png">
        <div class="orchard-bg">
                <img src="../static/images/bg2.png">
                <img class="board_bg2" src="../static/images/board_bg2.png">
            </div>
        <img class="back" @click="go_index" src="../static/images/user_back.png" alt="">
        <div class="header">
                <div class="info" @click="go_home">
                    <div class="avatar">
                        <img class="avatar_bf" src="../static/images/avatar_bf.png" alt="">
                        <img class="user_avatar" src="../static/images/avatar.png" alt="">
                        <img class="avatar_border" src="../static/images/avatar_border.png" alt="">
                    </div>
                    <p class="user_name">好听的昵称</p>
                </div>
                <div class="wallet">
                    <div class="balance" @click="user_recharge">
                        <p class="title"><img src="../static/images/money.png" alt="">钱包</p>
                        <p class="num">{{money}}</p>
                    </div>
                    <div class="balance">
                        <p class="title"><img src="../static/images/integral.png" alt="">果子</p>
                        <p class="num">99,999.00</p>
                    </div>
                </div>
          <div class="menu-list">
            <div class="menu">
              <img src="../static/images/menu1.png" alt="">
              排行榜
            </div>
            <div class="menu">
              <img src="../static/images/menu2.png" alt="">
              签到有礼
            </div>
            <div class="menu" @click="go_orchard_shop">
              <img src="../static/images/menu3.png" alt="">
              道具商城
            </div>
            <div class="menu">
              <img src="../static/images/menu4.png" alt="">
              邮件中心
            </div>
          </div>
            </div>
        <div class="footer" >
          <ul class="menu-list">
            <li class="menu">新手</li>
            <li class="menu" @click="go_my_package">背包</li>
            <li class="menu-center" @click="go_orchard_shop">商店</li>
            <li class="menu">消息</li>
            <li class="menu">好友</li>
          </ul>
        </div>
        </div>
        <script>
        apiready = function(){
            init();
            new Vue({
                el:"#app",
                data(){
                    return {
              music_play:true,
              namespace: '/mofang',
              token:"",
                        money:"",
                        settings_info:{
                            orchard: {},  // 种植园公共参数
                            user:{},      // 用户私有相关参数
                        },
              socket: null,
                        recharge_list: ['10','20','50','100','200','500','1000'],
              timeout: 0,
                        prev:{name:"",url:"",params:{}},
                        current:{name:"orchard",url:"orchard.html",params:{}},
                    }
                },
                beforeCreate(){
                    this.game.goFrame("orchard","my_orchard.html", this.current,{
                            x: 0,
                            y: 180,
                            w: 'auto',
                            h: 410,
                    },null);
                },
          created(){
            this.checkout();
                    this.money = this.game.fget("money");
          },
                methods:{
                    user_recharge(){
                        // 发起充值请求
                        api.actionSheet({
                            title: '余额充值',
                            cancelTitle: '取消',
                            buttons: this.recharge_list
                        }, (ret, err)=>{
                            if( ret ){
                                         if(ret.buttonIndex <= this.recharge_list.length){
                                                 // 充值金额
                                                 money = this.recharge_list[ret.buttonIndex-1];
                                                 // 调用支付宝充值
                                                 this.create_recharge(money);
                                         }
                            }else{
    
                            }
                        });
    
                    },
                    create_recharge(money){
                        // 获取历史信息记录
                        var token = this.game.get("access_token") || this.game.fget("access_token");
                        this.game.checkout(this, token, (new_access_token)=>{
                            this.axios.post("",{
                                "jsonrpc": "2.0",
                                "id": this.uuid(),
                                "method": "Recharge.create",
                                "params": {
                                    "money": money,
                                }
                            },{
                                headers:{
                                    Authorization: "jwt " + token,
                                }
                            }).then(response=>{
                                if(parseInt(response.data.result.errno)==1000){
                                    // 前往支付宝
                                    var aliPayPlus = api.require('aliPayPlus');
                                    aliPayPlus.payOrder({
                                         orderInfo: response.data.result.order_string,
                                         sandbox: response.data.result.sandbox, // 将来APP上线需要修改成false
                                     }, (ret, err)=>{
                                          pay_result = {
                                                9000:"支付成功",
                              8000:"正在处理中",
                              4000:"订单支付失败",
                              5000:"重复请求",
                              6001:"取消支付",
                              6002:"网络连接出错",
                                                6004:"支付结果未知",
                                            }
                                        api.alert({
                                            title: '支付结果',
                                            msg: pay_result[ret.code],
                                            buttons: ['确定']
                                        });
                                            // 通知服务端, 修改充值结果
                                            this.return_recharge(response.data.result.order_number,token);
                                    });
                                }else{
                                        this.game.print(response.data);
                                }
                            }).catch(error=>{
                                // 网络等异常
                                this.game.print(error);
                            });
                        })
                    },
                    return_recharge(out_trade_number,token){
                        this.axios.post("",{
                            "jsonrpc": "2.0",
                            "id": this.uuid(),
                            "method": "Recharge.return",
                            "params": {
                                "out_trade_number": out_trade_number,
                            }
                        },{
                            headers:{
                                Authorization: "jwt " + token,
                            }
                        }).then(response=>{
                            if(parseInt(response.data.result.errno)==1000){
                                this.money = response.data.result.money.toFixed(2);
                            }
                        })
                    },
            checkout(){
              var token = this.game.get("access_token") || this.game.fget("access_token");
              this.game.checkout(this,token,(new_access_token)=>{
                this.connect();
                            this.login();
                            this.user_package();
                            this.buy_prop();
                            this.unlock_package_number();
                            this.show_pet();
                            this.use_prop();
              });
            },
            connect(){
              // socket连接
              this.socket = io.connect(this.settings.socket_server + this.namespace, {transports: ['websocket']});
              this.socket.on('connect', ()=>{
                  this.game.print("开始连接服务端");
                                var id = this.game.fget("id");
                                this.socket.emit("login",{"uid":id});
                                this.socket.emit("user_prop");
              });
            },
                    login(){
                        this.socket.on("login_response",(message)=>{
                            this.settings_info.orchard = message.orchard_settings;
                            this.settings_info.user=message.user_settings;
                            this.game.fsave({
                                "orchard_settings":message.orchard_settings,
                                "user_settings":message.user_settings,
                            });
                            this.game.save({
                                "tree_total":message.tree_total,
                                "user_tree_number":message.user_tree_number,
                                "user_tree_list":message.user_tree_list,
                                "tree_status":message.tree_status,
                            });
                            setTimeout(()=>{
                                // 通知种植园页面获取到了当前用户的种植信息
                                api.sendEvent({
                                    name: 'user_tree_data',
                                    extra: {}
                                });
                            },500);
                        });
                    },
                    user_package(){
                        // 用户背包道具列表
                        this.socket.on("user_prop_response",(message)=>{
                            this.game.fsave({
                                "user_package":message.data,
                            })
                        })
                    },
            go_index(){
              this.game.goWin("root");
            },
            go_friends(){
              this.game.goFrame("friends","friends.html",this.current);
              this.game.goFrame("friend_list","friend_list.html",this.current,{
                  x: 0,
                  y: 190,
                  w: 'auto',
                  h: 'auto',
              },null,true);
            },
            go_home(){
              this.game.goWin("user","user.html", this.current);
            },
                    go_orchard_shop(){
                        // 种植园商店
                        this.game.goFrame("orchard_shop","shop.html", this.current,null,{
                  type:"push",
                  subType:"from_top",
                  duration:300
              });
                    },
                    go_my_package(){
                        // 我的背包
                        this.game.goFrame("package","package.html", this.current,null,{
                  type:"push",
                  subType:"from_top",
                  duration:300
              });
                    },
                    buy_prop(){
                        api.addEventListener({
                                name: 'buy_prop'
                        }, (ret, err)=>{
                                if( ret ){
                                    // 用户购买道具
                                    this.socket.emit("user_buy_prop",ret.value);
                                }
                        });
                        this.socket.on("user_buy_prop_response",(message)=>{
                            alert(message.errmsg);
                        })
                    },
                    use_prop(){
                        // 使用道具
                        var pid = 0;
                        api.addEventListener({
                                name: 'use_prop'
                        }, (ret, err)=>{
                                if( ret ){
                                    // 用户使用道具
                                    pid = ret.value.pid;
                                    this.socket.emit("use_prop",ret.value.pid);
                                }
                        });
    
                        this.socket.on("pet_use_response",(message)=>{
                            if(parseInt(message.errno) === 1000){
                                api.sendEvent({
                                        name: 'pet_use_success',
                                        extra: {
                                            pid: pid
                                        }
                                });
                            }else{
                                api.alert({
                                        title: '提示',
                                        msg: message.errmsg,
                                });
    
                            }
                        });
                    },
                    unlock_package_number(){
                        api.addEventListener({
                                name: 'unlock_package_number'
                        }, (ret, err)=>{
                                if( ret ){
                                    // 用户购买道具
                                    this.socket.emit("unlock_package");
                                }
                        });
    
                        this.socket.on("unlock_package_response",(message)=>{
                            if(parseInt(message.errno) === 1000){
                                api.sendEvent({
                                    name: 'unlock_package_success',
                                    extra: {
                                    }
                                });
                            }else{
                                api.alert({
                                        title: '提示',
                                        msg: message.errmsg,
                                });
    
                            }
    
                        })
                    },
                    show_pet(){
                        // 显示宠物
                        this.socket.emit("pet_show");
                        this.socket.on("pet_show_response",(message)=>{
                            if(parseInt(message.errno) === 1000){
                                // 把宠物信息保存到本地
                                this.game.save({"pet_list":message.pet_list})
                                this.game.save({"pet_number":message.pet_number})
                                setTimeout(()=>{
                                    api.sendEvent({
                                            name: 'pet_show_success',
                                            extra: {}
                                    });
                                },500);
                            }else{
                                api.alert({
                                        title: '提示',
                                        msg: message.errmsg,
                                });
                            }
    
                        })
                    }
                }
            });
        }
        </script>
    </body>
    </html>
    orchard.html-登陆种植园成功后将植物相关信息获取并保存在前端,并发送事件

    5.my_orchard.html-监听事件,并获取前端已经保存好的植物数据

    <!DOCTYPE html>
    <html>
    <head>
        <title>用户中心</title>
        <meta name="viewport" content="width=device-width,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no">
        <meta charset="utf-8">
        <link rel="stylesheet" href="../static/css/main.css">
        <script src="../static/js/vue.js"></script>
        <script src="../static/js/axios.js"></script>
        <script src="../static/js/main.js"></script>
        <script src="../static/js/uuid.js"></script>
        <script src="../static/js/settings.js"></script>
        <script src="../static/js/socket.io.js"></script>
    </head>
    <body>
        <div class="app orchard orchard-frame" id="app">
        <div class="background">
          <img class="grassland2" src="../static/images/grassland2.png" alt="">
          <img class="mushroom1" src="../static/images/mushroom1.png" alt="">
          <img class="stake1" src="../static/images/stake1.png" alt="">
          <img class="stake2" src="../static/images/stake2.png" alt="">
        </div>
        <div class="pet-box">
          <div class="pet">
                    <img v-if="pet_list.length > 0" class="pet-item" :src="settings.static_url+pet_list[0].image" alt="">
          </div>
                <div class="pet" v-if="pet_number > 1">
                    <img v-if="pet_list.length>1" class="pet-item" :src="settings.static_url+pet_list[1].image" alt="">
          </div>
          <div class="pet turned_off" v-if="pet_number==1">
            <img class="turned_image" src="../static/images/turned_off.png" alt="">
            <p>请购买宠物</p>
          </div>
        </div>
        <div class="tree-list">
          <div class="tree-box">
                    <div class="tree" v-for="tree in user_tree_data.user_tree_list">
                        <img :src="tree_img(tree.status)" alt="">
                    </div>
                    <!-- 已激活但是未种植的树桩列表 -->
                    <div class="tree" v-for="i in active_tree">
                        <img src="../static/images/tree1.png" alt="">
                    </div>
                    <!-- 未激活树桩列表 -->
                    <div class="tree" v-for="i in lock_tree">
                        <img src="../static/images/tree0.png" alt="">
                    </div>
          </div>
    
        </div>
        <div class="prop-list">
          <div class="prop">
            <img src="../static/images/prop1.png" alt="">
            <span>1</span>
            <p>化肥</p>
          </div>
          <div class="prop">
            <img src="../static/images/prop2.png" alt="">
            <span>0</span>
            <p>修剪</p>
          </div>
          <div class="prop">
            <img src="../static/images/prop3.png" alt="">
            <span>1</span>
            <p>浇水</p>
          </div>
          <div class="prop">
            <img src="../static/images/prop4.png" alt="">
            <span>1</span>
            <p>宠物粮</p>
          </div>
        </div>
        <div class="pet-hp-list">
          <div class="pet-hp" v-for="pet in pet_list">
            <p>宠物1 饱食度</p>
            <div class="hp">
              <div :style="{ pet.hp+'%'}" class="process">{{pet.hp}}%</div>
            </div>
          </div>
        </div>
        </div>
        <script>
        apiready = function(){
            init();
            new Vue({
                el:"#app",
                data(){
                    return {
              namespace: '/mofang',
              token:"",
              socket: null,
                        pet_list:[],
                        user_tree_data:{
                            "total_tree": 9,       // 总树桩数量
                            "user_tree_number": 0, // 当前用户激活树桩数量
                            "user_tree_list":[     // 当前种植的树桩列表状态
    
                            ],
                        },
                        tree_status:{
    
                        },
                        // user_tree_data:{
                        //     "total_tree":9,        // 总树桩数量
                        //     "user_tree_number": 5, // 当前用户激活树桩数量
                        //     "user_tree_list":[     // 当前种植的树桩列表状态
                        //             { // 树桩状态
                        //                  "time":1609808084, // 种植时间
                        //                  "status":4,        // 植物状态
                        //                  "has_time": 300,   // 状态时间
                        //             },
                        //     ],
                        // },
                        // tree_status:{
                        //     "tree_status_0": "tree0.png", // 树桩
                        //     "tree_status_1": "tree1.png", // 空桩
                        //     "tree_status_2": "tree2.png", // 幼苗
                        //     "tree_status_3": "tree3.png", // 成长
                        //     "tree_status_4": "tree4.png", // 成熟
                        // },
                        pet_number:[],
              timeout: 0,
                        prev:{name:"",url:"",params:{}},
                        current:{name:"orchard",url:"orchard.html",params:{}},
                    }
                },
                computed:{
                    // 已激活但是未使用的树桩
                    active_tree(){
                        return parseInt(this.user_tree_data.user_tree_number - this.user_tree_data.user_tree_list.length);
                    },
                    // 未激活的剩余树桩
                    lock_tree(){
                        return parseInt( this.user_tree_data.total_tree-this.user_tree_data.user_tree_number);
                    },
                },
          created(){
                    this.show_pet_list();
                    this.show_tree_list();
          },
                methods:{
                    tree_img(status){
                        return '../static/images/'+this.tree_status[status];
                    },
                    show_pet_list(){
                        api.addEventListener({
                                name: 'pet_show_success'
                        }, (ret, err)=>{
                                if( ret ){
                                    // 用户购买道具
                                    this.pet_list = this.game.get("pet_list");
                                    this.pet_number = parseInt(this.game.get("pet_number"));
                                }
                        });
                    },
                    show_tree_list(){
                        api.addEventListener({
                                name: 'user_tree_data'
                        }, (ret, err)=>{
                                if( ret ){
                                    // 用户种植植物信息
                                    this.user_tree_data.tree_total = parseInt(this.game.get("tree_total"));
                                    this.user_tree_data.user_tree_number = parseInt(this.game.get("user_tree_number"));
                                    this.user_tree_data.user_tree_list = this.game.get("user_tree_list");
                                    this.tree_status = this.game.get("tree_status");
                                }
                        });
                    }
                }
            });
        }
        </script>
    </body>
    </html>
    my_orchard.html-监听事件,并获取前端已经保存好的植物数据

    2.用户使用植物道具进行果树种植

    1.package.html-用户点击背包中的道具,前端发起使用道具的请求

    用户点击背包道具,客户端发起使用道具的请求,

    <!DOCTYPE html>
    <html>
    <head>
        <title>我的背包</title>
        <meta name="viewport" content="width=device-width,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no">
        <meta charset="utf-8">
        <link rel="stylesheet" href="../static/css/main.css">
        <script src="../static/js/vue.js"></script>
        <script src="../static/js/axios.js"></script>
        <script src="../static/js/main.js"></script>
        <script src="../static/js/uuid.js"></script>
        <script src="../static/js/settings.js"></script>
    </head>
    <body>
        <div class="app frame avatar add_friend package" id="app">
        <div class="box">
          <p class="title">我的背包</p>
          <img class="close" @click="close_frame" src="../static/images/close_btn1.png" alt="">
          <div class="prop_list">
            <div class="item" v-for="prop in user_package" @click="use_prop(prop.pid)">
                <img :src="settings.static_url+prop.image" alt="">
                        <span>{{prop.num}}</span>
            </div>
            <div class="item" v-for="number in unlock_td_number"></div>
            <div class="item lock" @click="unlock_package()" v-for="number in lock_td_number"></div>
          </div>
        </div>
        </div>
        <script>
        apiready = function(){
            init();
            new Vue({
                el:"#app",
                data(){
                    return {
                        td: 36,      // 背包格子总数量
                        user_id: "", // 当前登陆用户Id
                        orchard_settings:{}, // 种植园相关公共参数
                        user_settings:{}, // 用户相关私有参数
                        user_package:[],  // 用户背包信息
                        prev:{name:"",url:"",params:{}},
                        current:{name:"package",url:"package.html",params:{}},
                    }
                },
                computed:{// 计算属性
                    lock_td_number(){
                        // 未解锁的格子
                        return parseInt(this.orchard_settings.package_number_max-this.user_settings.package_number);
                    },
                    unlock_td_number(){
                        // 解锁的格子
                        return parseInt( this.user_settings.package_number - this.user_package.length);
                    }
                },
                created(){
                    this.user_id = this.game.get("id") || this.game.fget("id");
                    this.orchard_settings = JSON.parse(this.game.fget("orchard_settings"));
                    this.user_settings = JSON.parse(this.game.fget("user_settings"));
                    this.user_package = JSON.parse(this.game.fget("user_package"));
                },
                methods:{
                    use_prop(pid){
                        // 发起使用道具的通知
                        api.confirm({
                            title: '提示',
                            msg: '确认使用当前道具吗?',
                            buttons: ['确定', '取消']
                        }, (ret, err)=>{
                            if( ret.buttonIndex == 1 ){
                                    api.sendEvent({
                                         name: "use_prop",
                                         extra: {
                                                 pid: pid,
                                         }
                                 });
    
                            }
                        });
    
    
                        api.addEventListener({
                            name: 'prop_use_success'
                        }, (ret, err)=>{
                            if( ret ){
                                 // 扣除指定道具
                                             var pid = ret.value.pid;
                                             for(var i in this.user_package){
                                                     if(this.user_package[i].pid == pid){
                                                            this.user_package[i].num-=1;
                                                            if(this.user_package[i].num == 0){
                                                                this.user_package.splice(i,1);
                                                            }
                                                    }
                                            }
                                            this.game.fsave({"user_package":this.user_package});
                            }else{
                                 alert( JSON.stringify( err ) );
                            }
                        });
    
                    },
                    unlock_package(){
                        // 解锁格子上限
                        api.confirm({
                            title: '提示',
                            msg: '解锁背包上限',
                            buttons: ['确定', '取消']
                        }, (ret, err)=>{
                            if( ret.buttonIndex == 1 ){
    
                                    api.sendEvent({
                                            name: 'unlock_package_number',
                                            extra: {}
                                    });
    
                                    api.addEventListener({
                                        name: 'unlock_package_success'
                                    }, (ret, err)=>{
                                            this.user_settings.package_number=parseInt(this.user_settings.package_number)+1;
                                            this.game.fsave({"user_settings":this.user_settings});
                                    });
                            }
                        });
    
                    },
            close_frame(){
              this.game.outFrame("package");
            },
                }
            });
        }
        </script>
    </body>
    </html>
    package.html-用户点击背包中的道具,前端发起使用道具的请求

    2.orchard.html-监听使用道具事件

    <!DOCTYPE html>
    <html>
    <head>
        <title>用户中心</title>
        <meta name="viewport" content="width=device-width,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no">
        <meta charset="utf-8">
        <link rel="stylesheet" href="../static/css/main.css">
        <script src="../static/js/vue.js"></script>
        <script src="../static/js/axios.js"></script>
        <script src="../static/js/main.js"></script>
        <script src="../static/js/uuid.js"></script>
        <script src="../static/js/settings.js"></script>
        <script src="../static/js/socket.io.js"></script>
    </head>
    <body>
        <div class="app orchard" id="app">
        <img class="music" :class="music_play?'music2':''" @click="music_play=!music_play" src="../static/images/player.png">
        <div class="orchard-bg">
                <img src="../static/images/bg2.png">
                <img class="board_bg2" src="../static/images/board_bg2.png">
            </div>
        <img class="back" @click="go_index" src="../static/images/user_back.png" alt="">
        <div class="header">
                <div class="info" @click="go_home">
                    <div class="avatar">
                        <img class="avatar_bf" src="../static/images/avatar_bf.png" alt="">
                        <img class="user_avatar" src="../static/images/avatar.png" alt="">
                        <img class="avatar_border" src="../static/images/avatar_border.png" alt="">
                    </div>
                    <p class="user_name">好听的昵称</p>
                </div>
                <div class="wallet">
                    <div class="balance" @click="user_recharge">
                        <p class="title"><img src="../static/images/money.png" alt="">钱包</p>
                        <p class="num">{{money}}</p>
                    </div>
                    <div class="balance">
                        <p class="title"><img src="../static/images/integral.png" alt="">果子</p>
                        <p class="num">99,999.00</p>
                    </div>
                </div>
          <div class="menu-list">
            <div class="menu">
              <img src="../static/images/menu1.png" alt="">
              排行榜
            </div>
            <div class="menu">
              <img src="../static/images/menu2.png" alt="">
              签到有礼
            </div>
            <div class="menu" @click="go_orchard_shop">
              <img src="../static/images/menu3.png" alt="">
              道具商城
            </div>
            <div class="menu">
              <img src="../static/images/menu4.png" alt="">
              邮件中心
            </div>
          </div>
            </div>
        <div class="footer" >
          <ul class="menu-list">
            <li class="menu">新手</li>
            <li class="menu" @click="go_my_package">背包</li>
            <li class="menu-center" @click="go_orchard_shop">商店</li>
            <li class="menu">消息</li>
            <li class="menu">好友</li>
          </ul>
        </div>
        </div>
        <script>
        apiready = function(){
            init();
            new Vue({
                el:"#app",
                data(){
                    return {
              music_play:true,
              namespace: '/mofang',
              token:"",
                        money:"",
                        settings_info:{
                            orchard: {},  // 种植园公共参数
                            user:{},      // 用户私有相关参数
                        },
              socket: null,
                        recharge_list: ['10','20','50','100','200','500','1000'],
              timeout: 0,
                        prev:{name:"",url:"",params:{}},
                        current:{name:"orchard",url:"orchard.html",params:{}},
                    }
                },
                beforeCreate(){
                    this.game.goFrame("orchard","my_orchard.html", this.current,{
                            x: 0,
                            y: 180,
                            w: 'auto',
                            h: 410,
                    },null);
                },
          created(){
            this.checkout();
                    this.money = this.game.fget("money");
          },
                methods:{
                    user_recharge(){
                        // 发起充值请求
                        api.actionSheet({
                            title: '余额充值',
                            cancelTitle: '取消',
                            buttons: this.recharge_list
                        }, (ret, err)=>{
                            if( ret ){
                                         if(ret.buttonIndex <= this.recharge_list.length){
                                                 // 充值金额
                                                 money = this.recharge_list[ret.buttonIndex-1];
                                                 // 调用支付宝充值
                                                 this.create_recharge(money);
                                         }
                            }else{
    
                            }
                        });
    
                    },
                    create_recharge(money){
                        // 获取历史信息记录
                        var token = this.game.get("access_token") || this.game.fget("access_token");
                        this.game.checkout(this, token, (new_access_token)=>{
                            this.axios.post("",{
                                "jsonrpc": "2.0",
                                "id": this.uuid(),
                                "method": "Recharge.create",
                                "params": {
                                    "money": money,
                                }
                            },{
                                headers:{
                                    Authorization: "jwt " + token,
                                }
                            }).then(response=>{
                                if(parseInt(response.data.result.errno)==1000){
                                    // 前往支付宝
                                    var aliPayPlus = api.require('aliPayPlus');
                                    aliPayPlus.payOrder({
                                         orderInfo: response.data.result.order_string,
                                         sandbox: response.data.result.sandbox, // 将来APP上线需要修改成false
                                     }, (ret, err)=>{
                                          pay_result = {
                                                9000:"支付成功",
                              8000:"正在处理中",
                              4000:"订单支付失败",
                              5000:"重复请求",
                              6001:"取消支付",
                              6002:"网络连接出错",
                                                6004:"支付结果未知",
                                            }
                                        api.alert({
                                            title: '支付结果',
                                            msg: pay_result[ret.code],
                                            buttons: ['确定']
                                        });
                                            // 通知服务端, 修改充值结果
                                            this.return_recharge(response.data.result.order_number,token);
                                    });
                                }else{
                                        this.game.print(response.data);
                                }
                            }).catch(error=>{
                                // 网络等异常
                                this.game.print(error);
                            });
                        })
                    },
                    return_recharge(out_trade_number,token){
                        this.axios.post("",{
                            "jsonrpc": "2.0",
                            "id": this.uuid(),
                            "method": "Recharge.return",
                            "params": {
                                "out_trade_number": out_trade_number,
                            }
                        },{
                            headers:{
                                Authorization: "jwt " + token,
                            }
                        }).then(response=>{
                            if(parseInt(response.data.result.errno)==1000){
                                this.money = response.data.result.money.toFixed(2);
                            }
                        })
                    },
            checkout(){
              var token = this.game.get("access_token") || this.game.fget("access_token");
              this.game.checkout(this,token,(new_access_token)=>{
                this.connect();
                            this.login();
                            this.user_package();
                            this.buy_prop();
                            this.unlock_package_number();
                            this.show_pet();
                            this.use_prop();
              });
            },
            connect(){
              // socket连接
              this.socket = io.connect(this.settings.socket_server + this.namespace, {transports: ['websocket']});
              this.socket.on('connect', ()=>{
                  this.game.print("开始连接服务端");
                                var id = this.game.fget("id");
                                this.socket.emit("login",{"uid":id});
                                this.socket.emit("user_prop");
              });
            },
                    login(){
                        this.socket.on("login_response",(message)=>{
                            this.settings_info.orchard = message.orchard_settings;
                            this.settings_info.user=message.user_settings;
                            this.game.fsave({
                                "orchard_settings":message.orchard_settings,
                                "user_settings":message.user_settings,
                            });
                            this.game.save({
                                "tree_total":message.tree_total,
                                "user_tree_number":message.user_tree_number,
                                "user_tree_list":message.user_tree_list,
                                "tree_status":message.tree_status,
                            });
                            setTimeout(()=>{
                                // 通知种植园页面获取到了当前用户的种植信息
                                api.sendEvent({
                                    name: 'user_tree_data',
                                    extra: {}
                                });
                            },500);
                        });
                    },
                    user_package(){
                        // 用户背包道具列表
                        this.socket.on("user_prop_response",(message)=>{
                            this.game.fsave({
                                "user_package":message.data,
                            })
                        })
                    },
            go_index(){
              this.game.goWin("root");
            },
            go_friends(){
              this.game.goFrame("friends","friends.html",this.current);
              this.game.goFrame("friend_list","friend_list.html",this.current,{
                  x: 0,
                  y: 190,
                  w: 'auto',
                  h: 'auto',
              },null,true);
            },
            go_home(){
              this.game.goWin("user","user.html", this.current);
            },
                    go_orchard_shop(){
                        // 种植园商店
                        this.game.goFrame("orchard_shop","shop.html", this.current,null,{
                  type:"push",
                  subType:"from_top",
                  duration:300
              });
                    },
                    go_my_package(){
                        // 我的背包
                        this.game.goFrame("package","package.html", this.current,null,{
                  type:"push",
                  subType:"from_top",
                  duration:300
              });
                    },
                    buy_prop(){
                        api.addEventListener({
                                name: 'buy_prop'
                        }, (ret, err)=>{
                                if( ret ){
                                    // 用户购买道具
                                    this.socket.emit("user_buy_prop",ret.value);
                                }
                        });
                        this.socket.on("user_buy_prop_response",(message)=>{
                            alert(message.errmsg);
                        })
                    },
                    use_prop(){
                        // 使用道具
                        var pid = 0;
                        api.addEventListener({
                                name: 'use_prop'
                        }, (ret, err)=>{
                                if( ret ){
                                    // 用户使用道具
                                    pid = ret.value.pid;
                                    this.socket.emit("use_prop",ret.value.pid);
                                }
                        });
    
                        this.socket.on("prop_use_response",(message)=>{
                            if(parseInt(message.errno) === 1000){
                                api.sendEvent({
                                        name: 'prop_use_success',
                                        extra: {
                                            pid: pid
                                        }
                                });
                            }else{
                                api.alert({
                                        title: '提示',
                                        msg: message.errmsg,
                                });
    
                            }
                        });
                    },
                    unlock_package_number(){
                        api.addEventListener({
                                name: 'unlock_package_number'
                        }, (ret, err)=>{
                                if( ret ){
                                    // 用户购买道具
                                    this.socket.emit("unlock_package");
                                }
                        });
    
                        this.socket.on("unlock_package_response",(message)=>{
                            if(parseInt(message.errno) === 1000){
                                api.sendEvent({
                                    name: 'unlock_package_success',
                                    extra: {
                                    }
                                });
                            }else{
                                api.alert({
                                        title: '提示',
                                        msg: message.errmsg,
                                });
    
                            }
    
                        })
                    },
                    show_pet(){
                        // 显示宠物
                        this.socket.emit("pet_show");
                        this.socket.on("pet_show_response",(message)=>{
                            if(parseInt(message.errno) === 1000){
                                // 把宠物信息保存到本地
                                this.game.save({"pet_list":message.pet_list})
                                this.game.save({"pet_number":message.pet_number})
                                setTimeout(()=>{
                                    api.sendEvent({
                                            name: 'pet_show_success',
                                            extra: {}
                                    });
                                },500);
                            }else{
                                api.alert({
                                        title: '提示',
                                        msg: message.errmsg,
                                });
                            }
    
                        })
                    }
                }
            });
        }
        </script>
    </body>
    </html>
    orchard.html-监听使用道具事件

    3.my_orchard.html-增加tree_img方法,前端根据后端传过来的status显示植物的不同形态

    <!DOCTYPE html>
    <html>
    <head>
        <title>用户中心</title>
        <meta name="viewport" content="width=device-width,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no">
        <meta charset="utf-8">
        <link rel="stylesheet" href="../static/css/main.css">
        <script src="../static/js/vue.js"></script>
        <script src="../static/js/axios.js"></script>
        <script src="../static/js/main.js"></script>
        <script src="../static/js/uuid.js"></script>
        <script src="../static/js/settings.js"></script>
        <script src="../static/js/socket.io.js"></script>
    </head>
    <body>
        <div class="app orchard orchard-frame" id="app">
        <div class="background">
          <img class="grassland2" src="../static/images/grassland2.png" alt="">
          <img class="mushroom1" src="../static/images/mushroom1.png" alt="">
          <img class="stake1" src="../static/images/stake1.png" alt="">
          <img class="stake2" src="../static/images/stake2.png" alt="">
        </div>
        <div class="pet-box">
          <div class="pet">
                    <img v-if="pet_list.length > 0" class="pet-item" :src="settings.static_url+pet_list[0].image" alt="">
          </div>
                <div class="pet" v-if="pet_number > 1">
                    <img v-if="pet_list.length>1" class="pet-item" :src="settings.static_url+pet_list[1].image" alt="">
          </div>
          <div class="pet turned_off" v-if="pet_number==1">
            <img class="turned_image" src="../static/images/turned_off.png" alt="">
            <p>请购买宠物</p>
          </div>
        </div>
        <div class="tree-list">
          <div class="tree-box">
                    <div class="tree" v-for="tree in user_tree_data.user_tree_list">
                        <img :src="tree_img(tree.status)" alt="">
                    </div>
                    <!-- 已激活但是未种植的树桩列表 -->
                    <div class="tree" v-for="i in active_tree">
                        <img src="../static/images/tree1.png" alt="">
                    </div>
                    <!-- 未激活树桩列表 -->
                    <div class="tree" v-for="i in lock_tree">
                        <img src="../static/images/tree0.png" alt="">
                    </div>
          </div>
    
        </div>
        <div class="prop-list">
          <div class="prop">
            <img src="../static/images/prop1.png" alt="">
            <span>1</span>
            <p>化肥</p>
          </div>
          <div class="prop">
            <img src="../static/images/prop2.png" alt="">
            <span>0</span>
            <p>修剪</p>
          </div>
          <div class="prop">
            <img src="../static/images/prop3.png" alt="">
            <span>1</span>
            <p>浇水</p>
          </div>
          <div class="prop">
            <img src="../static/images/prop4.png" alt="">
            <span>1</span>
            <p>宠物粮</p>
          </div>
        </div>
        <div class="pet-hp-list">
          <div class="pet-hp" v-for="pet in pet_list">
            <p>宠物1 饱食度</p>
            <div class="hp">
              <div :style="{ pet.hp+'%'}" class="process">{{pet.hp}}%</div>
            </div>
          </div>
        </div>
        </div>
        <script>
        apiready = function(){
            init();
            new Vue({
                el:"#app",
                data(){
                    return {
              namespace: '/mofang',
              token:"",
              socket: null,
                        pet_list:[],
                        user_tree_data:{
                            "total_tree": 9,       // 总树桩数量
                            "user_tree_number": 0, // 当前用户激活树桩数量
                            "user_tree_list":[     // 当前种植的树桩列表状态
    
                            ],
                        },
                        tree_status:{
    
                        },
                        // user_tree_data:{
                        //     "total_tree":9,        // 总树桩数量
                        //     "user_tree_number": 5, // 当前用户激活树桩数量
                        //     "user_tree_list":[     // 当前种植的树桩列表状态
                        //             { // 树桩状态
                        //                  "time":1609808084, // 种植时间
                        //                  "status":4,        // 植物状态
                        //                  "has_time": 300,   // 状态时间
                        //             },
                        //     ],
                        // },
                        // tree_status:{
                        //     "tree_status_0": "tree0.png", // 树桩
                        //     "tree_status_1": "tree1.png", // 空桩
                        //     "tree_status_2": "tree2.png", // 幼苗
                        //     "tree_status_3": "tree3.png", // 成长
                        //     "tree_status_4": "tree4.png", // 成熟
                        // },
                        pet_number:[],
              timeout: 0,
                        prev:{name:"",url:"",params:{}},
                        current:{name:"orchard",url:"orchard.html",params:{}},
                    }
                },
                computed:{
                    // 已激活但是未使用的树桩
                    active_tree(){
                        return parseInt(this.user_tree_data.user_tree_number - this.user_tree_data.user_tree_list.length);
                    },
                    // 未激活的剩余树桩
                    lock_tree(){
                        return parseInt( this.user_tree_data.total_tree-this.user_tree_data.user_tree_number);
                    },
                },
          created(){
                    this.show_pet_list();
                    this.show_tree_list();
          },
                methods:{
                    tree_img(status){
                        return '../static/images/'+this.tree_status[status];
                    },
                    show_pet_list(){
                        api.addEventListener({
                                name: 'pet_show_success'
                        }, (ret, err)=>{
                                if( ret ){
                                    // 用户购买道具
                                    this.pet_list = this.game.get("pet_list");
                                    this.pet_number = parseInt(this.game.get("pet_number"));
                                }
                        });
                    },
                    show_tree_list(){
                        api.addEventListener({
                                name: 'user_tree_data'
                        }, (ret, err)=>{
                                if( ret ){
                                    // 用户种植植物信息
                                    this.user_tree_data.tree_total = parseInt(this.game.get("tree_total"));
                                    this.user_tree_data.user_tree_number = parseInt(this.game.get("user_tree_number"));
                                    this.user_tree_data.user_tree_list = this.game.get("user_tree_list");
                                    this.tree_status = this.game.get("tree_status");
                                }
                        });
                    }
                }
            });
        }
        </script>
    </body>
    </html>
    my_orchard.html-增加tree_img方法,前端根据后端传过来的status显示植物的不同形态

    4.服务端调整使用道具的后端接口和进入种植园的登录接口

    from application import socketio
    from flask import request
    from application.apps.users.models import User
    from flask_socketio import join_room, leave_room
    from application import mongo
    from .models import Goods,Setting,db
    from status import APIStatus as status
    from message import ErrorMessage as errmsg
    # 建立socket通信
    # @socketio.on("connect", namespace="/mofang")
    # def user_connect():
    #     """用户连接"""
    #     print("用户%s连接过来了!" % request.sid)
    #     # 主动响应数据给客户端
    #     socketio.emit("server_response","hello",namespace="/mofang")
    
    # 断开socket通信
    @socketio.on("disconnect", namespace="/mofang")
    def user_disconnect():
        print("用户%s退出了种植园" % request.sid )
    
    @socketio.on("login", namespace="/mofang")
    def user_login(data):
        # 分配房间
        room = data["uid"]
        join_room(room)
        # 保存当前用户和sid的绑定关系
        # 判断当前用户是否在mongo中有记录
        query = {
            "_id": data["uid"]
        }
        ret = mongo.db.user_info_list.find_one(query)
        if ret:
            mongo.db.user_info_list.update_one(query,{"$set":{"sid": request.sid}})
        else:
            mongo.db.user_info_list.insert_one({
            "_id": data["uid"],
            "sid": request.sid,
        })
    
        # 返回种植园的相关配置参数
        orchard_settings = {}
        setting_list = Setting.query.filter(Setting.is_deleted==False, Setting.status==True).all()
        """
        现在的格式:
            [<Setting package_number_base>, <Setting package_number_max>, <Setting package_unlock_price_1>]
        需要返回的格式:
            {
                package_number_base:4,
                package_number_max: 32,
                ...
            }
        """
        for item in setting_list:
            orchard_settings[item.name] = item.value
    
        # 返回当前用户相关的配置参数
        user_settings = {}
        # 从mongo中查找用户信息,判断用户是否激活了背包格子
        user_dict = mongo.db.user_info_list.find_one({"sid":request.sid})
        # 背包格子
        if user_dict.get("package_number") is None:
            user_settings["package_number"]  = orchard_settings.get("package_number_base",4)
            mongo.db.user_info_list.update_one({"sid":request.sid},{"$set":{"package_number": user_settings["package_number"]}})
        else:
            user_settings["package_number"]  = user_dict.get("package_number")
    
        """种植园植物信息"""
         # 总树桩数量
        setting = Setting.query.filter(Setting.name == "user_total_tree").first()
        if setting is None:
            tree_total = 9
        else:
            tree_total = int(setting.value)
    
        # 用户已经激活的树桩
        setting = Setting.query.filter(Setting.name == "user_active_tree").first()
        if setting is None:
            user_tree_number = 3
        else:
            user_tree_number = int(setting.value)
        user_tree_number = user_dict.get("user_tree_number",user_tree_number)
    
        # 种植的植物列表
        user_tree_list = user_dict.get("user_tree_list", [])
        key = 0
        for tree_item in user_tree_list:
            tree_item["status"] = "tree_status_%s" % tree_item["status"] # 植物状态
            tree_item["water_time"] = redis.ttl("user_tree_water_%s_%s" % (data["uid"],key))
            tree_item["growup_time"] = redis.ttl("user_tree_growup_%s_%s" % (data["uid"],key))
            key+=1
        # 植物状态信息
        status_list = [
            "tree_status_0",
            "tree_status_1",
            "tree_status_2",
            "tree_status_3",
            "tree_status_4",
        ]
        setting_list = Setting.query.filter(Setting.name.in_(status_list)).all()
        tree_status = {}
        for item in setting_list:
            tree_status[item.name] = item.value
        message = {
            "errno":status.CODE_OK,
            "errmsg":errmsg.ok,
            "orchard_settings":orchard_settings,
            "user_settings":user_settings,
            "tree_total":tree_total,
            "user_tree_number":user_tree_number,
            "user_tree_list":user_tree_list,
            "tree_status":tree_status,
        }
        socketio.emit("login_response", message, namespace="/mofang", room=room)
    
    @socketio.on("user_buy_prop", namespace="/mofang")
    def user_buy_prop(data):
        """用户购买道具"""
        room = request.sid
        # 从mongo中获取当前用户信息
        user_info = mongo.db.user_info_list.find_one({"sid":request.sid})
        user = User.query.get(user_info.get("_id"))
        if user is None:
            socketio.emit("user_buy_prop_response", {"errno":status.CODE_NO_USER,"errmsg":errmsg.user_not_exists}, namespace="/mofang", room=room)
            return
    
        # 判断背包物品存储是否达到上限
        use_package_number = int(user_info.get("use_package_number",0)) # 当前诗经使用的格子数量
        package_number = int(user_info.get("package_number",0))         # 当前用户已经解锁的格子数量
        # 本次购买道具需要使用的格子数量
        setting = Setting.query.filter(Setting.name == "td_prop_max").first()
        if setting is None:
            td_prop_max = 10
        else:
            td_prop_max = int(setting.value)
    
        # 计算购买道具以后需要额外占用的格子数量
        if ("prop_%s" % data["pid"]) in user_info.get("prop_list",{}):
            """曾经购买过当前道具"""
            prop_num = int( user_info.get("prop_list")["prop_%s" % data["pid"]]) # 购买前的道具数量
            new_prop_num = prop_num+int(data["num"]) # 如果成功购买道具以后的数量
            old_td_num = prop_num // td_prop_max
            if prop_num % td_prop_max > 0:
                old_td_num+=1
            new_td_num = new_prop_num // td_prop_max
            if new_prop_num % td_prop_max > 0:
                new_td_num+=1
            td_num = new_td_num - old_td_num
        else:
            """新增购买的道具"""
            # 计算本次购买道具需要占用的格子数量
    
            if int(data["num"]) > td_prop_max:
                """需要多个格子"""
                td_num = int(data["num"]) // td_prop_max
                if int(data["num"]) % td_prop_max > 0:
                    td_num+=1
            else:
                """需要一个格子"""
                td_num = 1
    
        if use_package_number+td_num > package_number:
            """超出存储上限"""
            socketio.emit("user_buy_prop_response", {"errno": status.CODE_NO_PACKAGE, "errmsg": errmsg.no_package},
                          namespace="/mofang", room=room)
            return
    
        # 从mysql中获取商品价格
        prop = Goods.query.get(data["pid"])
        if user.money > 0: # 当前商品需要通过RMB购买
            if float(user.money) < float(prop.price) * int(data["num"]):
                socketio.emit("user_buy_prop_response", {"errno":status.CODE_NO_MONEY,"errmsg":errmsg.money_no_enough}, namespace="/mofang", room=room)
                return
        else:
            """当前通过果子进行购买"""
            if int(user.credit) < int(prop.credit) * int(data["num"]):
                socketio.emit("user_buy_prop_response", {"errno": status.CODE_NO_CREDIT, "errmsg": errmsg.credit_no_enough},
                              namespace="/mofang", room=room)
                return
    
        # 从mongo中获取用户列表信息,提取购买的商品数量进行累加和余额
        query = {"sid": request.sid}
        if user_info.get("prop_list") is None:
            """此前没有购买任何道具"""
            message = {"$set":{"prop_list":{"prop_%s" % prop.id:int(data["num"])}}}
            mongo.db.user_info_list.update_one(query,message)
        else:
            """此前有购买了道具"""
            prop_list = user_info.get("prop_list") # 道具列表
            if ("prop_%s" % prop.id) in prop_list:
                """如果再次同一款道具"""
                prop_list[("prop_%s" % prop.id)] = prop_list[("prop_%s" % prop.id)] + int(data["num"])
            else:
                """此前没有购买过这种道具"""
                prop_list[("prop_%s" % prop.id)] = int(data["num"])
    
            mongo.db.user_info_list.update_one(query, {"$set":{"prop_list":prop_list}})
    
        # 扣除余额或果子
        if prop.price > 0:
            user.money = float(user.money) - float(prop.price) * int(data["num"])
        else:
            user.credit = int(user.credit) - int(prop.credit) * int(data["num"])
    
        db.session.commit()
    
        # 返回购买成功的信息
        socketio.emit("user_buy_prop_response", {"errno":status.CODE_OK,"errmsg":errmsg.ok}, namespace="/mofang", room=room)
        # 返回最新的用户道具列表
        user_prop()
    
    @socketio.on("user_prop", namespace="/mofang")
    def user_prop():
        """用户道具"""
        userinfo = mongo.db.user_info_list.find_one({"sid":request.sid})
        prop_list = userinfo.get("prop_list",{})
        prop_id_list = []
        for prop_str,num in prop_list.items():
            pid = int(prop_str[5:])
            prop_id_list.append(pid)
    
        data = []
        prop_list_data = Goods.query.filter(Goods.id.in_(prop_id_list)).all()
        setting = Setting.query.filter(Setting.name == "td_prop_max").first()
        if setting is None:
            td_prop_max = 10
        else:
            td_prop_max = int(setting.value)
    
        for prop_data in prop_list_data:
            num = int( prop_list[("prop_%s" % prop_data.id)])
            if td_prop_max > num:
                data.append({
                    "num": num,
                    "image": prop_data.image,
                    "pid": prop_data.id,
                    "pty": prop_data.prop_type,
                })
            else:
                padding_time = num // td_prop_max
                padding_last = num % td_prop_max
                arr = [{
                    "num": td_prop_max,
                    "image": prop_data.image,
                    "pid": prop_data.id,
                    "pty": prop_data.prop_type,
                }] * padding_time
                if padding_last != 0:
                    arr.append({
                        "num": padding_last,
                        "image": prop_data.image,
                        "pid": prop_data.id,
                        "pty": prop_data.prop_type,
                    })
                data = data + arr
        mongo.db.user_info_list.update_one({"sid":request.sid},{"$set":{"use_package_number":len(data)}})
        room = request.sid
        socketio.emit("user_prop_response", {
            "errno": status.CODE_OK,
            "errmsg": errmsg.ok,
            "data":data,
        }, namespace="/mofang",
                      room=room)
    
    @socketio.on("unlock_package", namespace="/mofang")
    def unlock_package():
        """解锁背包"""
        # 从mongo获取当前用户解锁的格子数量
        user_info = mongo.db.user_info_list.find_one({"sid":request.sid})
        user = User.query.get(user_info.get("_id"))
        if user is None:
            socketio.emit("unlock_package_response", {"errno":status.CODE_NO_USER,"errmsg":errmsg.user_not_exists}, namespace="/mofang", room=room)
            return
    
        package_number = int(user_info.get("package_number"))
        num = 7 - (32 - package_number) // 4  # 没有解锁的格子
    
        # 从数据库中获取解锁背包的价格
        setting = Setting.query.filter(Setting.name == "package_unlock_price_%s" % num).first()
        if setting is None:
            unlock_price = 0
        else:
            unlock_price = int(setting.value)
    
        # 判断是否有足够的积分或者价格
        room = request.sid
        if user.money < unlock_price:
            socketio.emit("unlock_package_response", {"errno": status.CODE_NO_MONEY, "errmsg": errmsg.money_no_enough},
                          namespace="/mofang", room=room)
            return
    
        # 解锁成功
        user.money = float(user.money) - float(unlock_price)
        db.session.commit()
    
        # mongo中调整数量
        mongo.db.user_info_list.update_one({"sid":request.sid},{"$set":{"package_number": package_number+1}})
        # 返回解锁的结果
        socketio.emit("unlock_package_response", {
            "errno": status.CODE_OK,
            "errmsg": errmsg.ok},
                      namespace="/mofang", room=room)
    import math
    from application import redis
    @socketio.on("pet_show", namespace="/mofang")
    def pet_show():
        """显示宠物"""
        room = request.sid
        user_info = mongo.db.user_info_list.find_one({"sid":request.sid})
        user = User.query.get(user_info.get("_id"))
        if user is None:
            socketio.emit("pet_show_response", {"errno":status.CODE_NO_USER,"errmsg":errmsg.user_not_exists}, namespace="/mofang", room=room)
            return
    
        # 获取宠物列表
        pet_list = user_info.get("pet_list", [])
    
        """
        pet_list: [
          { 
             "pid":11,
             "image":"pet.png",
             "hp":100%,
             "created_time":xxxx-xx-xx xx:xx:xx,
             "skill":"70%",
             "has_time":30天
          },
        ]
        """
        # 从redis中提取当前宠物的饱食度和有效期
        for key,pet in enumerate(pet_list):
            pet["hp"] = math.ceil( redis.ttl("pet_%s_%s_hp" % (user.id,key+1)) / 86400 * 100 )
            pet["has_time"] = redis.ttl("pet_%s_%s_expire" % (user.id,key+1))
    
        pet_number = user_info.get("pet_number", 1)
        socketio.emit(
            "pet_show_response",
            {
                "errno": status.CODE_OK,
                "errmsg": errmsg.ok,
                "pet_list": pet_list,
                "pet_number": pet_number,
            },
            namespace="/mofang",
            room=room
        )
    
    from datetime import datetime
    @socketio.on("use_prop", namespace="/mofang")
    def use_prop(pid):
        """使用道具"""
        room = request.sid
        # 获取mongo中的用户信息
        user_info = mongo.db.user_info_list.find_one({"sid": request.sid})
        # 获取mysql中的用户信息
        user = User.query.get(user_info.get("_id"))
        if user is None:
            socketio.emit("pet_use_response", {"errno": status.CODE_NO_USER, "errmsg": errmsg.user_not_exists},
                          namespace="/mofang", room=room)
            return
    
        # 获取道具
        prop_data = Goods.query.get(pid)
        if prop_data is None:
            socketio.emit("pet_use_response", {"errno":status.CODE_NO_SUCH_PROP,"errmsg":errmsg.not_such_prop}, namespace="/mofang", room=room)
            return
    
        if int(prop_data.prop_type) == 0:
            """使用植物道具"""
            # 1. 判断当前的植物数量是否有空余
            tree_list = user_info.get("user_tree_list", [])
    
            # 当前用户最多可种植的数量
            setting = Setting.query.filter(Setting.name == "user_active_tree").first()
            if setting is None:
                user_tree_number = 3
            else:
                user_tree_number = int(setting.value)
            user_tree_number = user_info.get("user_tree_number", user_tree_number)
    
            if len(tree_list) >= user_tree_number:
                socketio.emit("prop_use_response", {"errno": status.CODE_NO_EMPTY, "errmsg": errmsg.prop_not_empty},
                              namespace="/mofang", room=room)
                return
    
            # 使用道具
            mongo.db.user_info_list.update_one({"sid":room},{"$push":{"user_tree_list":
                { # 植物状态
                    "time": int(datetime.now().timestamp()), # 种植时间
                    "status": 2, # 植物状态,2表示幼苗状态
                    "water_time": 0, # 浇水次数
                }
            }})
    
            # 从种下去到浇水的时间
            pipe = redis.pipeline()
            pipe.multi()
            setting = Setting.query.filter(Setting.name == "tree_water_time").first()
            if setting is None:
                tree_water_time = 3600
            else:
                tree_water_time = int(setting.value)
            # 必须等时间到了才可以浇水
            pipe.setex("user_tree_water_%s_%s" % (user.id,len(tree_list)),int(tree_water_time),"_")
            # 必须等时间到了才可以到成长期
            setting = Setting.query.filter(Setting.name == "tree_growup_time").first()
            if setting is None:
                tree_growup_time = 3600
            else:
                tree_growup_time = int(setting.value)
            pipe.setex("user_tree_growup_%s_%s" % (user.id,len(tree_list)),tree_growup_time, "_")
            pipe.execute()
    
            user_login({"uid": user.id})
    
        if int(prop_data.prop_type) == 1:
            """使用宠物道具"""
            # 1. 判断当前的宠物数量
            # 获取宠物列表
            pet_list = user_info.get("pet_list", [])
            if len(pet_list) > 1:
                socketio.emit("pet_use_response", {"errno":status.CODE_NO_EMPTY,"errmsg":errmsg.pet_not_empty}, namespace="/mofang", room=room)
                return
    
            # 2. 是否有空余的宠物栏位
            pet_number = user_info.get("pet_number",1)
            if pet_number <= len(pet_list):
                socketio.emit("pet_use_response", {"errno":status.CODE_NO_EMPTY,"errmsg":errmsg.pet_not_empty}, namespace="/mofang", room=room)
                return
    
            # 3. 初始化当前宠物信息
            # 获取有效期和防御值
            exp_data = Setting.query.filter(Setting.name=="pet_expire_%s" % pid).first()
            ski_data = Setting.query.filter(Setting.name=="pet_skill_%s" % pid).first()
    
            if exp_data is None:
                # 默认7天有效期
                expire = 7
            else:
                expire = exp_data.value
    
            if ski_data is None:
                skill  = 10
            else:
                skill  = ski_data.value
    
            # 在redis中设置当前宠物的饱食度
            pipe = redis.pipeline()
            pipe.multi()
            pipe.setex("pet_%s_%s_hp" % (user.id, len(pet_list)+1), 24*60*60, "_")
            pipe.setex("pet_%s_%s_expire" % (user.id, len(pet_list)+1), int(expire)*24*60*60, "_")
            pipe.execute()
    
            # 基本保存到mongo
            mongo.db.user_info_list.update_one({"sid":request.sid},{"$push":{"pet_list":{
                 "pid": pid,
                 "image": prop_data.image,
                 "created_time": int( datetime.now().timestamp() ),
                 "skill": skill,
              }}})
    
            """
            db.user_info_list.updateOne({"_id":"52"},{"$push":{"pet_list":{
                     "pid": 2,
                     "image": "pet1.png",
                     "created_time": 1609727155,
                     "skill": 30,
                  }}})
            """
    
            pet_show()
    
        # 扣除背包中的道具数量
        prop_list = user_info.get("prop_list",{})
        for key,value in prop_list.items():
            if key == ("prop_%s" % pid):
                if int(value) > 1:
                    prop_list[key] = int(value) - 1
                else:
                    prop_list.pop(key)
                break
    
        mongo.db.user_info_list.update_one({"sid":room},{"$set":{"prop_list":prop_list}})
        user_prop()
    
        socketio.emit("prop_use_response", {"errno": status.CODE_OK, "errmsg": errmsg.ok},
                          namespace="/mofang", room=room)
    服务端调整使用道具后端接口&种植园登录接口

    3.解锁树桩

    1.在Setting表中添加解锁树桩的费用

    INSERT INTO mofang.mf_orchard_setting (id, name, is_deleted, orders, status, created_time, updated_time, title, value) VALUES (28, 'active_tree_price', 0, 1, 1, '2020-12-30 17:40:46', '2020-12-30 17:40:44', '激活树桩的递增果子费用', '1000');

    2.后端提供解锁树桩的接口

    @socketio.on("active_tree", namespace="/mofang")
    def active_tree():
        """激活树桩"""
        room = request.sid
        # 获取mongo中的用户信息
        user_info = mongo.db.user_info_list.find_one({"sid": request.sid})
        # 获取mysql中的用户信息
        user = User.query.get(user_info.get("_id"))
        if user is None:
            socketio.emit("active_tree_response", {"errno": status.CODE_NO_USER, "errmsg": errmsg.user_not_exists},
                          namespace="/mofang", room=room)
            return
    
        # 判断树桩是否达到上限
        tree_number_data = Setting.query.filter(Setting.name == "user_active_tree").first()
        total_tree_data = Setting.query.filter(Setting.name == "user_total_tree").first()
        if tree_number_data is None:
            tree_number = 1
        else:
            tree_number = tree_number_data.value
    
        if total_tree_data is None:
            total_tree = 9
        else:
            total_tree = int(total_tree_data.value)
    
        user_tree_number = int(user_info.get("user_tree_number",tree_number))
        if user_tree_number >= total_tree:
            socketio.emit("active_tree_response", {"errno": status.CODE_NO_EMPTY, "errmsg": errmsg.prop_not_empty},
                          namespace="/mofang", room=room)
            return
    
        # 扣除激活的果子数量
        ret = Setting.query.filter(Setting.name == "active_tree_price").first()
        if ret is None:
            active_tree_price = 100 * user_tree_number
        else:
            active_tree_price = int(ret.value) * user_tree_number
    
        if active_tree_price > int(user.credit):
            socketio.emit("active_tree_response", {"errno": status.CODE_NO_CREDIT, "errmsg": errmsg.credit_no_enough},
                          namespace="/mofang", room=room)
            return
    
        user.credit = int(user.credit) - active_tree_price
        db.session.commit()
    
        mongo.db.user_info_list.update_one({"sid":room},{"$set":{"user_tree_number":user_tree_number+1}})
    
        socketio.emit("active_tree_response", {"errno": status.CODE_OK, "errmsg": errmsg.ok},
                      namespace="/mofang", room=room)
        return

    3.前端发起请求解锁树桩

    1.my_orchard.html

    <!DOCTYPE html>
    <html>
    <head>
        <title>用户中心</title>
        <meta name="viewport" content="width=device-width,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no">
        <meta charset="utf-8">
        <link rel="stylesheet" href="../static/css/main.css">
        <script src="../static/js/vue.js"></script>
        <script src="../static/js/axios.js"></script>
        <script src="../static/js/main.js"></script>
        <script src="../static/js/uuid.js"></script>
        <script src="../static/js/settings.js"></script>
        <script src="../static/js/socket.io.js"></script>
    </head>
    <body>
        <div class="app orchard orchard-frame" id="app">
        <div class="background">
          <img class="grassland2" src="../static/images/grassland2.png" alt="">
          <img class="mushroom1" src="../static/images/mushroom1.png" alt="">
          <img class="stake1" src="../static/images/stake1.png" alt="">
          <img class="stake2" src="../static/images/stake2.png" alt="">
        </div>
        <div class="pet-box">
          <div class="pet">
                    <img v-if="pet_list.length > 0" class="pet-item" :src="settings.static_url+pet_list[0].image" alt="">
          </div>
                <div class="pet" v-if="pet_number > 1">
                    <img v-if="pet_list.length>1" class="pet-item" :src="settings.static_url+pet_list[1].image" alt="">
          </div>
          <div class="pet turned_off" v-if="pet_number==1">
            <img class="turned_image" src="../static/images/turned_off.png" alt="">
            <p>请购买宠物</p>
          </div>
        </div>
        <div class="tree-list">
          <div class="tree-box">
                    <div class="tree" v-for="tree in user_tree_data.user_tree_list">
                        <img :src="tree_img(tree.status)" alt="">
                    </div>
                    <!-- 已激活但是未种植的树桩列表 -->
                    <div class="tree" v-for="i in active_tree">
                        <img src="../static/images/tree1.png" alt="">
                    </div>
                    <!-- 未激活树桩列表 -->
                    <div @click="unlock_tree" class="tree" v-for="i in lock_tree">
                        <img src="../static/images/tree0.png" alt="">
                    </div>
          </div>
    
        </div>
        <div class="prop-list">
          <div class="prop">
            <img src="../static/images/prop1.png" alt="">
            <span>1</span>
            <p>化肥</p>
          </div>
          <div class="prop">
            <img src="../static/images/prop2.png" alt="">
            <span>0</span>
            <p>修剪</p>
          </div>
          <div class="prop">
            <img src="../static/images/prop3.png" alt="">
            <span>1</span>
            <p>浇水</p>
          </div>
          <div class="prop">
            <img src="../static/images/prop4.png" alt="">
            <span>1</span>
            <p>宠物粮</p>
          </div>
        </div>
        <div class="pet-hp-list">
          <div class="pet-hp" v-for="pet in pet_list">
            <p>宠物1 饱食度</p>
            <div class="hp">
              <div :style="{ pet.hp+'%'}" class="process">{{pet.hp}}%</div>
            </div>
          </div>
        </div>
        </div>
        <script>
        apiready = function(){
            init();
            new Vue({
                el:"#app",
                data(){
                    return {
              namespace: '/mofang',
              token:"",
              socket: null,
                        pet_list:[],
                        user_tree_data:{
                            "total_tree": 9,       // 总树桩数量
                            "user_tree_number": 0, // 当前用户激活树桩数量
                            "user_tree_list":[     // 当前种植的树桩列表状态
    
                            ],
                        },
                        tree_status:{
    
                        },
                        // user_tree_data:{
                        //     "total_tree":9,        // 总树桩数量
                        //     "user_tree_number": 5, // 当前用户激活树桩数量
                        //     "user_tree_list":[     // 当前种植的树桩列表状态
                        //             { // 树桩状态
                        //                  "time":1609808084, // 种植时间
                        //                  "status":4,        // 植物状态
                        //                  "has_time": 300,   // 状态时间
                        //             },
                        //     ],
                        // },
                        // tree_status:{
                        //     "tree_status_0": "tree0.png", // 树桩
                        //     "tree_status_1": "tree1.png", // 空桩
                        //     "tree_status_2": "tree2.png", // 幼苗
                        //     "tree_status_3": "tree3.png", // 成长
                        //     "tree_status_4": "tree4.png", // 成熟
                        // },
                        pet_number:[],
              timeout: 0,
                        prev:{name:"",url:"",params:{}},
                        current:{name:"orchard",url:"orchard.html",params:{}},
                    }
                },
                computed:{
                    // 已激活但是未使用的树桩
                    active_tree(){
                        return parseInt(this.user_tree_data.user_tree_number - this.user_tree_data.user_tree_list.length);
                    },
                    // 未激活的剩余树桩
                    lock_tree(){
                        return parseInt( this.user_tree_data.total_tree-this.user_tree_data.user_tree_number);
                    },
                },
          created(){
                    this.show_pet_list();
                    this.show_tree_list();
          },
                methods:{
                    tree_img(status){
                        return '../static/images/'+this.tree_status[status];
                    },
                    show_pet_list(){
                        api.addEventListener({
                                name: 'pet_show_success'
                        }, (ret, err)=>{
                                if( ret ){
                                    // 用户购买道具
                                    this.pet_list = this.game.get("pet_list");
                                    this.pet_number = parseInt(this.game.get("pet_number"));
                                }
                        });
                    },
                    show_tree_list(){
                        api.addEventListener({
                                name: 'user_tree_data'
                        }, (ret, err)=>{
                                if( ret ){
                                    // 用户种植植物信息
                                    this.user_tree_data.tree_total = parseInt(this.game.get("tree_total"));
                                    this.user_tree_data.user_tree_number = parseInt(this.game.get("user_tree_number"));
                                    this.user_tree_data.user_tree_list = this.game.get("user_tree_list");
                                    this.tree_status = this.game.get("tree_status");
                                }
                        });
                    },
                    unlock_tree(){
                        // 激活树桩通知
                        api.confirm({
                            title: '提示',
                            msg: '是否激活树桩?',
                            buttons: ['确定', '取消']
                        }, (ret, err)=>{
                            if( ret.buttonIndex == 1 ){
                                    api.sendEvent({
                                        name: 'active_tree',
                                        extra: {
    
                                            }
                                    });
                            }
                        });
    
                        // 激活成功!
                        api.addEventListener({
                            name: 'active_tree_success'
                        }, (ret, err)=>{
                            if( ret ){
                                 // 新增树桩的数量
                                         this.user_tree_data.user_tree_number+=1;
                                        this.game.save({"user_tree_number": this.user_tree_data.user_tree_number});
                            }
                        });
    
    
                    }
                }
            });
        }
        </script>
    </body>
    </html>
    解锁树桩:my_orchard.html

    2.orchard.html

    <!DOCTYPE html>
    <html>
    <head>
        <title>用户中心</title>
        <meta name="viewport" content="width=device-width,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no">
        <meta charset="utf-8">
        <link rel="stylesheet" href="../static/css/main.css">
        <script src="../static/js/vue.js"></script>
        <script src="../static/js/axios.js"></script>
        <script src="../static/js/main.js"></script>
        <script src="../static/js/uuid.js"></script>
        <script src="../static/js/settings.js"></script>
        <script src="../static/js/socket.io.js"></script>
    </head>
    <body>
        <div class="app orchard" id="app">
        <img class="music" :class="music_play?'music2':''" @click="music_play=!music_play" src="../static/images/player.png">
        <div class="orchard-bg">
                <img src="../static/images/bg2.png">
                <img class="board_bg2" src="../static/images/board_bg2.png">
            </div>
        <img class="back" @click="go_index" src="../static/images/user_back.png" alt="">
        <div class="header">
                <div class="info" @click="go_home">
                    <div class="avatar">
                        <img class="avatar_bf" src="../static/images/avatar_bf.png" alt="">
                        <img class="user_avatar" src="../static/images/avatar.png" alt="">
                        <img class="avatar_border" src="../static/images/avatar_border.png" alt="">
                    </div>
                    <p class="user_name">好听的昵称</p>
                </div>
                <div class="wallet">
                    <div class="balance" @click="user_recharge">
                        <p class="title"><img src="../static/images/money.png" alt="">钱包</p>
                        <p class="num">{{money}}</p>
                    </div>
                    <div class="balance">
                        <p class="title"><img src="../static/images/integral.png" alt="">果子</p>
                        <p class="num">99,999.00</p>
                    </div>
                </div>
          <div class="menu-list">
            <div class="menu">
              <img src="../static/images/menu1.png" alt="">
              排行榜
            </div>
            <div class="menu">
              <img src="../static/images/menu2.png" alt="">
              签到有礼
            </div>
            <div class="menu" @click="go_orchard_shop">
              <img src="../static/images/menu3.png" alt="">
              道具商城
            </div>
            <div class="menu">
              <img src="../static/images/menu4.png" alt="">
              邮件中心
            </div>
          </div>
            </div>
        <div class="footer" >
          <ul class="menu-list">
            <li class="menu">新手</li>
            <li class="menu" @click="go_my_package">背包</li>
            <li class="menu-center" @click="go_orchard_shop">商店</li>
            <li class="menu">消息</li>
            <li class="menu">好友</li>
          </ul>
        </div>
        </div>
        <script>
        apiready = function(){
            init();
            new Vue({
                el:"#app",
                data(){
                    return {
              music_play:true,
              namespace: '/mofang',
              token:"",
                        money:"",
                        settings_info:{
                            orchard: {},  // 种植园公共参数
                            user:{},      // 用户私有相关参数
                        },
              socket: null,
                        recharge_list: ['10','20','50','100','200','500','1000'],
              timeout: 0,
                        prev:{name:"",url:"",params:{}},
                        current:{name:"orchard",url:"orchard.html",params:{}},
                    }
                },
                beforeCreate(){
                    this.game.goFrame("orchard","my_orchard.html", this.current,{
                            x: 0,
                            y: 180,
                            w: 'auto',
                            h: 410,
                    },null);
                },
          created(){
            this.checkout();
                    this.money = this.game.fget("money");
          },
                methods:{
                    user_recharge(){
                        // 发起充值请求
                        api.actionSheet({
                            title: '余额充值',
                            cancelTitle: '取消',
                            buttons: this.recharge_list
                        }, (ret, err)=>{
                            if( ret ){
                                         if(ret.buttonIndex <= this.recharge_list.length){
                                                 // 充值金额
                                                 money = this.recharge_list[ret.buttonIndex-1];
                                                 // 调用支付宝充值
                                                 this.create_recharge(money);
                                         }
                            }else{
    
                            }
                        });
    
                    },
                    create_recharge(money){
                        // 获取历史信息记录
                        var token = this.game.get("access_token") || this.game.fget("access_token");
                        this.game.checkout(this, token, (new_access_token)=>{
                            this.axios.post("",{
                                "jsonrpc": "2.0",
                                "id": this.uuid(),
                                "method": "Recharge.create",
                                "params": {
                                    "money": money,
                                }
                            },{
                                headers:{
                                    Authorization: "jwt " + token,
                                }
                            }).then(response=>{
                                if(parseInt(response.data.result.errno)==1000){
                                    // 前往支付宝
                                    var aliPayPlus = api.require('aliPayPlus');
                                    aliPayPlus.payOrder({
                                         orderInfo: response.data.result.order_string,
                                         sandbox: response.data.result.sandbox, // 将来APP上线需要修改成false
                                     }, (ret, err)=>{
                                          pay_result = {
                                                9000:"支付成功",
                              8000:"正在处理中",
                              4000:"订单支付失败",
                              5000:"重复请求",
                              6001:"取消支付",
                              6002:"网络连接出错",
                                                6004:"支付结果未知",
                                            }
                                        api.alert({
                                            title: '支付结果',
                                            msg: pay_result[ret.code],
                                            buttons: ['确定']
                                        });
                                            // 通知服务端, 修改充值结果
                                            this.return_recharge(response.data.result.order_number,token);
                                    });
                                }else{
                                        this.game.print(response.data);
                                }
                            }).catch(error=>{
                                // 网络等异常
                                this.game.print(error);
                            });
                        })
                    },
                    return_recharge(out_trade_number,token){
                        this.axios.post("",{
                            "jsonrpc": "2.0",
                            "id": this.uuid(),
                            "method": "Recharge.return",
                            "params": {
                                "out_trade_number": out_trade_number,
                            }
                        },{
                            headers:{
                                Authorization: "jwt " + token,
                            }
                        }).then(response=>{
                            if(parseInt(response.data.result.errno)==1000){
                                this.money = response.data.result.money.toFixed(2);
                            }
                        })
                    },
            checkout(){
              var token = this.game.get("access_token") || this.game.fget("access_token");
              this.game.checkout(this,token,(new_access_token)=>{
                this.connect();
                            this.login();
                            this.user_package();
                            this.buy_prop();
                            this.unlock_package_number();
                            this.show_pet();
                            this.use_prop();
                            this.active_tree();
              });
            },
            connect(){
              // socket连接
              this.socket = io.connect(this.settings.socket_server + this.namespace, {transports: ['websocket']});
              this.socket.on('connect', ()=>{
                  this.game.print("开始连接服务端");
                                var id = this.game.fget("id");
                                this.socket.emit("login",{"uid":id});
                                this.socket.emit("user_prop");
              });
            },
                    login(){
                        this.socket.on("login_response",(message)=>{
                            this.settings_info.orchard = message.orchard_settings;
                            this.settings_info.user=message.user_settings;
                            this.game.fsave({
                                "orchard_settings":message.orchard_settings,
                                "user_settings":message.user_settings,
                            });
                            this.game.save({
                                "tree_total":message.tree_total,
                                "user_tree_number":message.user_tree_number,
                                "user_tree_list":message.user_tree_list,
                                "tree_status":message.tree_status,
                            });
                            setTimeout(()=>{
                                // 通知种植园页面获取到了当前用户的种植信息
                                api.sendEvent({
                                    name: 'user_tree_data',
                                    extra: {}
                                });
                            },500);
                        });
                    },
                    user_package(){
                        // 用户背包道具列表
                        this.socket.on("user_prop_response",(message)=>{
                            this.game.fsave({
                                "user_package":message.data,
                            })
                        })
                    },
            go_index(){
              this.game.goWin("root");
            },
            go_friends(){
              this.game.goFrame("friends","friends.html",this.current);
              this.game.goFrame("friend_list","friend_list.html",this.current,{
                  x: 0,
                  y: 190,
                  w: 'auto',
                  h: 'auto',
              },null,true);
            },
            go_home(){
              this.game.goWin("user","user.html", this.current);
            },
                    go_orchard_shop(){
                        // 种植园商店
                        this.game.goFrame("orchard_shop","shop.html", this.current,null,{
                  type:"push",
                  subType:"from_top",
                  duration:300
              });
                    },
                    go_my_package(){
                        // 我的背包
                        this.game.goFrame("package","package.html", this.current,null,{
                  type:"push",
                  subType:"from_top",
                  duration:300
              });
                    },
                    buy_prop(){
                        api.addEventListener({
                                name: 'buy_prop'
                        }, (ret, err)=>{
                                if( ret ){
                                    // 用户购买道具
                                    this.socket.emit("user_buy_prop",ret.value);
                                }
                        });
                        this.socket.on("user_buy_prop_response",(message)=>{
                            alert(message.errmsg);
                        })
                    },
                    use_prop(){
                        // 使用道具
                        var pid = 0;
                        api.addEventListener({
                                name: 'use_prop'
                        }, (ret, err)=>{
                                if( ret ){
                                    // 用户使用道具
                                    pid = ret.value.pid;
                                    this.socket.emit("use_prop",ret.value.pid);
                                }
                        });
    
                        this.socket.on("prop_use_response",(message)=>{
                            if(parseInt(message.errno) === 1000){
                                api.sendEvent({
                                        name: 'prop_use_success',
                                        extra: {
                                            pid: pid
                                        }
                                });
                            }else{
                                api.alert({
                                        title: '提示',
                                        msg: message.errmsg,
                                });
    
                            }
                        });
                    },
                    unlock_package_number(){
                        api.addEventListener({
                                name: 'unlock_package_number'
                        }, (ret, err)=>{
                                if( ret ){
                                    // 用户购买道具
                                    this.socket.emit("unlock_package");
                                }
                        });
    
                        this.socket.on("unlock_package_response",(message)=>{
                            if(parseInt(message.errno) === 1000){
                                api.sendEvent({
                                    name: 'unlock_package_success',
                                    extra: {
                                    }
                                });
                            }else{
                                api.alert({
                                        title: '提示',
                                        msg: message.errmsg,
                                });
    
                            }
    
                        })
                    },
                    show_pet(){
                        // 显示宠物
                        this.socket.emit("pet_show");
                        this.socket.on("pet_show_response",(message)=>{
                            if(parseInt(message.errno) === 1000){
                                // 把宠物信息保存到本地
                                this.game.save({"pet_list":message.pet_list})
                                this.game.save({"pet_number":message.pet_number})
                                setTimeout(()=>{
                                    api.sendEvent({
                                            name: 'pet_show_success',
                                            extra: {}
                                    });
                                },500);
                            }else{
                                api.alert({
                                        title: '提示',
                                        msg: message.errmsg,
                                });
                            }
    
                        })
                    },
                    active_tree(){
                        // 激活树桩
                        api.addEventListener({
                            name: 'active_tree'
                        }, (ret, err)=>{
                            if( ret ){
                                    this.socket.emit("active_tree");
                            }
                        });
    
                        this.socket.on("active_tree_response",(message)=>{
                            if(parseInt(message.errno) === 1000){
                                // 更新数据到本地
                                api.sendEvent({
                                        name: 'active_tree_success',
                                        extra: {}
                                });
                            }else{
                                api.alert({
                                        title: '提示',
                                        msg: message.errmsg,
                                });
                            }
    
                        })
                    }
                }
            });
        }
        </script>
    </body>
    </html>
    解锁树桩:orchard.html

    4.化肥/修剪/浇水/宠物粮小图标显示

    1.显示化肥和宠物粮的道具数量

    1.后端返回道具数量

    服务端提供化肥和宠物粮的道具数量

    from application import socketio
    from flask import request
    from application.apps.users.models import User
    from flask_socketio import join_room, leave_room
    from application import mongo
    from .models import Goods,Setting,db
    from status import APIStatus as status
    from message import ErrorMessage as errmsg
    # 建立socket通信
    # @socketio.on("connect", namespace="/mofang")
    # def user_connect():
    #     """用户连接"""
    #     print("用户%s连接过来了!" % request.sid)
    #     # 主动响应数据给客户端
    #     socketio.emit("server_response","hello",namespace="/mofang")
    
    # 断开socket通信
    @socketio.on("disconnect", namespace="/mofang")
    def user_disconnect():
        print("用户%s退出了种植园" % request.sid )
    
    @socketio.on("login", namespace="/mofang")
    def user_login(data):
        # 分配房间
        room = data["uid"]
        join_room(room)
        # 保存当前用户和sid的绑定关系
        # 判断当前用户是否在mongo中有记录
        query = {
            "_id": data["uid"]
        }
        ret = mongo.db.user_info_list.find_one(query)
        if ret:
            mongo.db.user_info_list.update_one(query,{"$set":{"sid": request.sid}})
        else:
            mongo.db.user_info_list.insert_one({
            "_id": data["uid"],
            "sid": request.sid,
        })
    
        # 返回种植园的相关配置参数
        orchard_settings = {}
        setting_list = Setting.query.filter(Setting.is_deleted==False, Setting.status==True).all()
        """
        现在的格式:
            [<Setting package_number_base>, <Setting package_number_max>, <Setting package_unlock_price_1>]
        需要返回的格式:
            {
                package_number_base:4,
                package_number_max: 32,
                ...
            }
        """
        for item in setting_list:
            orchard_settings[item.name] = item.value
    
        # 返回当前用户相关的配置参数
        user_settings = {}
        # 从mongo中查找用户信息,判断用户是否激活了背包格子
        user_dict = mongo.db.user_info_list.find_one({"sid":request.sid})
        # 背包格子
        if user_dict.get("package_number") is None:
            user_settings["package_number"]  = orchard_settings.get("package_number_base",4)
            mongo.db.user_info_list.update_one({"sid":request.sid},{"$set":{"package_number": user_settings["package_number"]}})
        else:
            user_settings["package_number"]  = user_dict.get("package_number")
    
        """种植园植物信息"""
         # 总树桩数量
        setting = Setting.query.filter(Setting.name == "user_total_tree").first()
        if setting is None:
            tree_total = 9
        else:
            tree_total = int(setting.value)
    
        # 用户已经激活的树桩
        setting = Setting.query.filter(Setting.name == "user_active_tree").first()
        if setting is None:
            user_tree_number = 3
        else:
            user_tree_number = int(setting.value)
        user_tree_number = user_dict.get("user_tree_number",user_tree_number)
    
        # 种植的植物列表
        user_tree_list = user_dict.get("user_tree_list", [])
        key = 0
        for tree_item in user_tree_list:
            tree_item["status"] = "tree_status_%s" % tree_item["status"] # 植物状态
            tree_item["water_time"] = redis.ttl("user_tree_water_%s_%s" % (data["uid"],key))
            tree_item["growup_time"] = redis.ttl("user_tree_growup_%s_%s" % (data["uid"],key))
            key+=1
        # 植物状态信息
        status_list = [
            "tree_status_0",
            "tree_status_1",
            "tree_status_2",
            "tree_status_3",
            "tree_status_4",
        ]
        setting_list = Setting.query.filter(Setting.name.in_(status_list)).all()
        tree_status = {}
        for item in setting_list:
            tree_status[item.name] = item.value
    
        """显示植物相关道具"""
        fertilizer_num,pet_food_num = get_orchard_prop_list(user_dict)
    
        message = {
            "errno":status.CODE_OK,
            "errmsg":errmsg.ok,
            "orchard_settings":orchard_settings,
            "user_settings":user_settings,
            "tree_total":tree_total,
            "user_tree_number":user_tree_number,
            "user_tree_list":user_tree_list,
            "fertilizer_num":fertilizer_num,
            "pet_food_num":pet_food_num,
            "tree_status":tree_status,
        }
        socketio.emit("login_response", message, namespace="/mofang", room=room)
    
    def get_orchard_prop_list(user_dict):
        fertilizer_num = 0
        pet_food_num = 0
        prop_list = user_dict.get("prop_list", {})
        for prop_item, num in prop_list.items():
            pid = prop_item.split("_")[-1]
            num = int(num)
            prop_obj = Goods.query.get(pid)
            if prop_obj.prop_type == 2:
                # 提取化肥
                fertilizer_num += num
            elif prop_obj.prop_type == 3:
                # 提取宠物粮
                pet_food_num += num
    
        return fertilizer_num,pet_food_num
    
    @socketio.on("user_buy_prop", namespace="/mofang")
    def user_buy_prop(data):
        """用户购买道具"""
        room = request.sid
        # 从mongo中获取当前用户信息
        user_info = mongo.db.user_info_list.find_one({"sid":request.sid})
        user = User.query.get(user_info.get("_id"))
        if user is None:
            socketio.emit("user_buy_prop_response", {"errno":status.CODE_NO_USER,"errmsg":errmsg.user_not_exists}, namespace="/mofang", room=room)
            return
    
        # 判断背包物品存储是否达到上限
        use_package_number = int(user_info.get("use_package_number",0)) # 当前诗经使用的格子数量
        package_number = int(user_info.get("package_number",0))         # 当前用户已经解锁的格子数量
        # 本次购买道具需要使用的格子数量
        setting = Setting.query.filter(Setting.name == "td_prop_max").first()
        if setting is None:
            td_prop_max = 10
        else:
            td_prop_max = int(setting.value)
    
        # 计算购买道具以后需要额外占用的格子数量
        if ("prop_%s" % data["pid"]) in user_info.get("prop_list",{}):
            """曾经购买过当前道具"""
            prop_num = int( user_info.get("prop_list")["prop_%s" % data["pid"]]) # 购买前的道具数量
            new_prop_num = prop_num+int(data["num"]) # 如果成功购买道具以后的数量
            old_td_num = prop_num // td_prop_max
            if prop_num % td_prop_max > 0:
                old_td_num+=1
            new_td_num = new_prop_num // td_prop_max
            if new_prop_num % td_prop_max > 0:
                new_td_num+=1
            td_num = new_td_num - old_td_num
        else:
            """新增购买的道具"""
            # 计算本次购买道具需要占用的格子数量
    
            if int(data["num"]) > td_prop_max:
                """需要多个格子"""
                td_num = int(data["num"]) // td_prop_max
                if int(data["num"]) % td_prop_max > 0:
                    td_num+=1
            else:
                """需要一个格子"""
                td_num = 1
    
        if use_package_number+td_num > package_number:
            """超出存储上限"""
            socketio.emit("user_buy_prop_response", {"errno": status.CODE_NO_PACKAGE, "errmsg": errmsg.no_package},
                          namespace="/mofang", room=room)
            return
    
        # 从mysql中获取商品价格
        prop = Goods.query.get(data["pid"])
        if user.money > 0: # 当前商品需要通过RMB购买
            if float(user.money) < float(prop.price) * int(data["num"]):
                socketio.emit("user_buy_prop_response", {"errno":status.CODE_NO_MONEY,"errmsg":errmsg.money_no_enough}, namespace="/mofang", room=room)
                return
        else:
            """当前通过果子进行购买"""
            if int(user.credit) < int(prop.credit) * int(data["num"]):
                socketio.emit("user_buy_prop_response", {"errno": status.CODE_NO_CREDIT, "errmsg": errmsg.credit_no_enough},
                              namespace="/mofang", room=room)
                return
    
        # 从mongo中获取用户列表信息,提取购买的商品数量进行累加和余额
        query = {"sid": request.sid}
        if user_info.get("prop_list") is None:
            """此前没有购买任何道具"""
            message = {"$set":{"prop_list":{"prop_%s" % prop.id:int(data["num"])}}}
            mongo.db.user_info_list.update_one(query,message)
        else:
            """此前有购买了道具"""
            prop_list = user_info.get("prop_list") # 道具列表
            if ("prop_%s" % prop.id) in prop_list:
                """如果再次同一款道具"""
                prop_list[("prop_%s" % prop.id)] = prop_list[("prop_%s" % prop.id)] + int(data["num"])
            else:
                """此前没有购买过这种道具"""
                prop_list[("prop_%s" % prop.id)] = int(data["num"])
    
            mongo.db.user_info_list.update_one(query, {"$set":{"prop_list":prop_list}})
    
        # 扣除余额或果子
        if prop.price > 0:
            user.money = float(user.money) - float(prop.price) * int(data["num"])
        else:
            user.credit = int(user.credit) - int(prop.credit) * int(data["num"])
    
        db.session.commit()
    
        # 返回购买成功的信息
        socketio.emit("user_buy_prop_response", {"errno":status.CODE_OK,"errmsg":errmsg.ok}, namespace="/mofang", room=room)
        # 返回最新的用户道具列表
        user_prop()
    
    @socketio.on("user_prop", namespace="/mofang")
    def user_prop():
        """用户道具"""
        userinfo = mongo.db.user_info_list.find_one({"sid":request.sid})
        prop_list = userinfo.get("prop_list",{})
        prop_id_list = []
        for prop_str,num in prop_list.items():
            pid = int(prop_str[5:])
            prop_id_list.append(pid)
    
        data = []
        prop_list_data = Goods.query.filter(Goods.id.in_(prop_id_list)).all()
        setting = Setting.query.filter(Setting.name == "td_prop_max").first()
        if setting is None:
            td_prop_max = 10
        else:
            td_prop_max = int(setting.value)
    
        for prop_data in prop_list_data:
            num = int( prop_list[("prop_%s" % prop_data.id)])
            if td_prop_max > num:
                data.append({
                    "num": num,
                    "image": prop_data.image,
                    "pid": prop_data.id,
                    "pty": prop_data.prop_type,
                })
            else:
                padding_time = num // td_prop_max
                padding_last = num % td_prop_max
                arr = [{
                    "num": td_prop_max,
                    "image": prop_data.image,
                    "pid": prop_data.id,
                    "pty": prop_data.prop_type,
                }] * padding_time
                if padding_last != 0:
                    arr.append({
                        "num": padding_last,
                        "image": prop_data.image,
                        "pid": prop_data.id,
                        "pty": prop_data.prop_type,
                    })
                data = data + arr
        mongo.db.user_info_list.update_one({"sid":request.sid},{"$set":{"use_package_number":len(data)}})
        room = request.sid
        fertilizer_num,pet_food_num = get_orchard_prop_list(userinfo)
        socketio.emit("user_prop_response", {
            "errno": status.CODE_OK,
            "errmsg": errmsg.ok,
            "data":data,
            "fertilizer_num":fertilizer_num,
            "pet_food_num":pet_food_num,
        }, namespace="/mofang",
                      room=room)
    
    @socketio.on("unlock_package", namespace="/mofang")
    def unlock_package():
        """解锁背包"""
        # 从mongo获取当前用户解锁的格子数量
        user_info = mongo.db.user_info_list.find_one({"sid":request.sid})
        user = User.query.get(user_info.get("_id"))
        if user is None:
            socketio.emit("unlock_package_response", {"errno":status.CODE_NO_USER,"errmsg":errmsg.user_not_exists}, namespace="/mofang", room=room)
            return
    
        package_number = int(user_info.get("package_number"))
        num = 7 - (32 - package_number) // 4  # 没有解锁的格子
    
        # 从数据库中获取解锁背包的价格
        setting = Setting.query.filter(Setting.name == "package_unlock_price_%s" % num).first()
        if setting is None:
            unlock_price = 0
        else:
            unlock_price = int(setting.value)
    
        # 判断是否有足够的积分或者价格
        room = request.sid
        if user.money < unlock_price:
            socketio.emit("unlock_package_response", {"errno": status.CODE_NO_MONEY, "errmsg": errmsg.money_no_enough},
                          namespace="/mofang", room=room)
            return
    
        # 解锁成功
        user.money = float(user.money) - float(unlock_price)
        db.session.commit()
    
        # mongo中调整数量
        mongo.db.user_info_list.update_one({"sid":request.sid},{"$set":{"package_number": package_number+1}})
        # 返回解锁的结果
        socketio.emit("unlock_package_response", {
            "errno": status.CODE_OK,
            "errmsg": errmsg.ok},
                      namespace="/mofang", room=room)
    import math
    from application import redis
    @socketio.on("pet_show", namespace="/mofang")
    def pet_show():
        """显示宠物"""
        room = request.sid
        user_info = mongo.db.user_info_list.find_one({"sid":request.sid})
        user = User.query.get(user_info.get("_id"))
        if user is None:
            socketio.emit("pet_show_response", {"errno":status.CODE_NO_USER,"errmsg":errmsg.user_not_exists}, namespace="/mofang", room=room)
            return
    
        # 获取宠物列表
        pet_list = user_info.get("pet_list", [])
    
        """
        pet_list: [
          { 
             "pid":11,
             "image":"pet.png",
             "hp":100%,
             "created_time":xxxx-xx-xx xx:xx:xx,
             "skill":"70%",
             "has_time":30天
          },
        ]
        """
        # 从redis中提取当前宠物的饱食度和有效期
        for key,pet in enumerate(pet_list):
            pet["hp"] = math.ceil( redis.ttl("pet_%s_%s_hp" % (user.id,key+1)) / 86400 * 100 )
            pet["has_time"] = redis.ttl("pet_%s_%s_expire" % (user.id,key+1))
    
        pet_number = user_info.get("pet_number", 1)
        socketio.emit(
            "pet_show_response",
            {
                "errno": status.CODE_OK,
                "errmsg": errmsg.ok,
                "pet_list": pet_list,
                "pet_number": pet_number,
            },
            namespace="/mofang",
            room=room
        )
    
    from datetime import datetime
    @socketio.on("use_prop", namespace="/mofang")
    def use_prop(pid):
        """使用道具"""
        room = request.sid
        # 获取mongo中的用户信息
        user_info = mongo.db.user_info_list.find_one({"sid": request.sid})
        # 获取mysql中的用户信息
        user = User.query.get(user_info.get("_id"))
        if user is None:
            socketio.emit("pet_use_response", {"errno": status.CODE_NO_USER, "errmsg": errmsg.user_not_exists},
                          namespace="/mofang", room=room)
            return
    
        # 获取道具
        prop_data = Goods.query.get(pid)
        if prop_data is None:
            socketio.emit("pet_use_response", {"errno":status.CODE_NO_SUCH_PROP,"errmsg":errmsg.not_such_prop}, namespace="/mofang", room=room)
            return
    
        if int(prop_data.prop_type) == 0:
            """使用植物道具"""
            # 1. 判断当前的植物数量是否有空余
            tree_list = user_info.get("user_tree_list", [])
    
            # 当前用户最多可种植的数量
            setting = Setting.query.filter(Setting.name == "user_active_tree").first()
            if setting is None:
                user_tree_number = 3
            else:
                user_tree_number = int(setting.value)
            user_tree_number = user_info.get("user_tree_number", user_tree_number)
    
            if len(tree_list) >= user_tree_number:
                socketio.emit("prop_use_response", {"errno": status.CODE_NO_EMPTY, "errmsg": errmsg.prop_not_empty},
                              namespace="/mofang", room=room)
                return
    
            # 使用道具
            mongo.db.user_info_list.update_one({"sid":room},{"$push":{"user_tree_list":
                { # 植物状态
                    "time": int(datetime.now().timestamp()), # 种植时间
                    "status": 2, # 植物状态,2表示幼苗状态
                    "water_time": 0, # 浇水次数
                }
            }})
    
            # 从种下去到浇水的时间
            pipe = redis.pipeline()
            pipe.multi()
            setting = Setting.query.filter(Setting.name == "tree_water_time").first()
            if setting is None:
                tree_water_time = 3600
            else:
                tree_water_time = int(setting.value)
            # 必须等时间到了才可以浇水
            pipe.setex("user_tree_water_%s_%s" % (user.id,len(tree_list)),int(tree_water_time),"_")
            # 必须等时间到了才可以到成长期
            setting = Setting.query.filter(Setting.name == "tree_growup_time").first()
            if setting is None:
                tree_growup_time = 3600
            else:
                tree_growup_time = int(setting.value)
            pipe.setex("user_tree_growup_%s_%s" % (user.id,len(tree_list)),tree_growup_time, "_")
            pipe.execute()
    
            user_login({"uid": user.id})
    
        if int(prop_data.prop_type) == 1:
            """使用宠物道具"""
            # 1. 判断当前的宠物数量
            # 获取宠物列表
            pet_list = user_info.get("pet_list", [])
            if len(pet_list) > 1:
                socketio.emit("pet_use_response", {"errno":status.CODE_NO_EMPTY,"errmsg":errmsg.pet_not_empty}, namespace="/mofang", room=room)
                return
    
            # 2. 是否有空余的宠物栏位
            pet_number = user_info.get("pet_number",1)
            if pet_number <= len(pet_list):
                socketio.emit("pet_use_response", {"errno":status.CODE_NO_EMPTY,"errmsg":errmsg.pet_not_empty}, namespace="/mofang", room=room)
                return
    
            # 3. 初始化当前宠物信息
            # 获取有效期和防御值
            exp_data = Setting.query.filter(Setting.name=="pet_expire_%s" % pid).first()
            ski_data = Setting.query.filter(Setting.name=="pet_skill_%s" % pid).first()
    
            if exp_data is None:
                # 默认7天有效期
                expire = 7
            else:
                expire = exp_data.value
    
            if ski_data is None:
                skill  = 10
            else:
                skill  = ski_data.value
    
            # 在redis中设置当前宠物的饱食度
            pipe = redis.pipeline()
            pipe.multi()
            pipe.setex("pet_%s_%s_hp" % (user.id, len(pet_list)+1), 24*60*60, "_")
            pipe.setex("pet_%s_%s_expire" % (user.id, len(pet_list)+1), int(expire)*24*60*60, "_")
            pipe.execute()
    
            # 基本保存到mongo
            mongo.db.user_info_list.update_one({"sid":request.sid},{"$push":{"pet_list":{
                 "pid": pid,
                 "image": prop_data.image,
                 "created_time": int( datetime.now().timestamp() ),
                 "skill": skill,
              }}})
    
            """
            db.user_info_list.updateOne({"_id":"52"},{"$push":{"pet_list":{
                     "pid": 2,
                     "image": "pet1.png",
                     "created_time": 1609727155,
                     "skill": 30,
                  }}})
            """
    
            pet_show()
    
        # 扣除背包中的道具数量
        prop_list = user_info.get("prop_list",{})
        for key,value in prop_list.items():
            if key == ("prop_%s" % pid):
                if int(value) > 1:
                    prop_list[key] = int(value) - 1
                else:
                    prop_list.pop(key)
                break
    
        mongo.db.user_info_list.update_one({"sid":room},{"$set":{"prop_list":prop_list}})
        user_prop()
    
        socketio.emit("prop_use_response", {"errno": status.CODE_OK, "errmsg": errmsg.ok},
                          namespace="/mofang", room=room)
    
    
    @socketio.on("active_tree", namespace="/mofang")
    def active_tree():
        """激活树桩"""
        room = request.sid
        # 获取mongo中的用户信息
        user_info = mongo.db.user_info_list.find_one({"sid": request.sid})
        # 获取mysql中的用户信息
        user = User.query.get(user_info.get("_id"))
        if user is None:
            socketio.emit("active_tree_response", {"errno": status.CODE_NO_USER, "errmsg": errmsg.user_not_exists},
                          namespace="/mofang", room=room)
            return
    
        # 判断树桩是否达到上限
        tree_number_data = Setting.query.filter(Setting.name == "user_active_tree").first()
        total_tree_data = Setting.query.filter(Setting.name == "user_total_tree").first()
        if tree_number_data is None:
            tree_number = 1
        else:
            tree_number = tree_number_data.value
    
        if total_tree_data is None:
            total_tree = 9
        else:
            total_tree = int(total_tree_data.value)
    
        user_tree_number = int(user_info.get("user_tree_number",tree_number))
        if user_tree_number >= total_tree:
            socketio.emit("active_tree_response", {"errno": status.CODE_NO_EMPTY, "errmsg": errmsg.prop_not_empty},
                          namespace="/mofang", room=room)
            return
    
        # 扣除激活的果子数量
        ret = Setting.query.filter(Setting.name == "active_tree_price").first()
        if ret is None:
            active_tree_price = 100 * user_tree_number
        else:
            active_tree_price = int(ret.value) * user_tree_number
    
        if active_tree_price > int(user.credit):
            socketio.emit("active_tree_response", {"errno": status.CODE_NO_CREDIT, "errmsg": errmsg.credit_no_enough},
                          namespace="/mofang", room=room)
            return
    
        user.credit = int(user.credit) - active_tree_price
        db.session.commit()
    
        mongo.db.user_info_list.update_one({"sid":room},{"$set":{"user_tree_number":user_tree_number+1}})
    
        socketio.emit("active_tree_response", {"errno": status.CODE_OK, "errmsg": errmsg.ok},
                      namespace="/mofang", room=room)
        return
    服务端提供化肥和宠物粮的道具数量

    2.前端显示道具数量

    <!DOCTYPE html>
    <html>
    <head>
        <title>用户中心</title>
        <meta name="viewport" content="width=device-width,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no">
        <meta charset="utf-8">
        <link rel="stylesheet" href="../static/css/main.css">
        <script src="../static/js/vue.js"></script>
        <script src="../static/js/axios.js"></script>
        <script src="../static/js/main.js"></script>
        <script src="../static/js/uuid.js"></script>
        <script src="../static/js/settings.js"></script>
        <script src="../static/js/socket.io.js"></script>
    </head>
    <body>
        <div class="app orchard" id="app">
        <img class="music" :class="music_play?'music2':''" @click="music_play=!music_play" src="../static/images/player.png">
        <div class="orchard-bg">
                <img src="../static/images/bg2.png">
                <img class="board_bg2" src="../static/images/board_bg2.png">
            </div>
        <img class="back" @click="go_index" src="../static/images/user_back.png" alt="">
        <div class="header">
                <div class="info" @click="go_home">
                    <div class="avatar">
                        <img class="avatar_bf" src="../static/images/avatar_bf.png" alt="">
                        <img class="user_avatar" src="../static/images/avatar.png" alt="">
                        <img class="avatar_border" src="../static/images/avatar_border.png" alt="">
                    </div>
                    <p class="user_name">好听的昵称</p>
                </div>
                <div class="wallet">
                    <div class="balance" @click="user_recharge">
                        <p class="title"><img src="../static/images/money.png" alt="">钱包</p>
                        <p class="num">{{money}}</p>
                    </div>
                    <div class="balance">
                        <p class="title"><img src="../static/images/integral.png" alt="">果子</p>
                        <p class="num">99,999.00</p>
                    </div>
                </div>
          <div class="menu-list">
            <div class="menu">
              <img src="../static/images/menu1.png" alt="">
              排行榜
            </div>
            <div class="menu">
              <img src="../static/images/menu2.png" alt="">
              签到有礼
            </div>
            <div class="menu" @click="go_orchard_shop">
              <img src="../static/images/menu3.png" alt="">
              道具商城
            </div>
            <div class="menu">
              <img src="../static/images/menu4.png" alt="">
              邮件中心
            </div>
          </div>
            </div>
        <div class="footer" >
          <ul class="menu-list">
            <li class="menu">新手</li>
            <li class="menu" @click="go_my_package">背包</li>
            <li class="menu-center" @click="go_orchard_shop">商店</li>
            <li class="menu">消息</li>
            <li class="menu">好友</li>
          </ul>
        </div>
        </div>
        <script>
        apiready = function(){
            init();
            new Vue({
                el:"#app",
                data(){
                    return {
              music_play:true,
              namespace: '/mofang',
              token:"",
                        money:"",
                        settings_info:{
                            orchard: {},  // 种植园公共参数
                            user:{},      // 用户私有相关参数
                        },
              socket: null,
                        recharge_list: ['10','20','50','100','200','500','1000'],
              timeout: 0,
                        prev:{name:"",url:"",params:{}},
                        current:{name:"orchard",url:"orchard.html",params:{}},
                    }
                },
                beforeCreate(){
                    this.game.goFrame("orchard","my_orchard.html", this.current,{
                            x: 0,
                            y: 180,
                            w: 'auto',
                            h: 410,
                    },null);
                },
          created(){
            this.checkout();
                    this.money = this.game.fget("money");
          },
                methods:{
                    user_recharge(){
                        // 发起充值请求
                        api.actionSheet({
                            title: '余额充值',
                            cancelTitle: '取消',
                            buttons: this.recharge_list
                        }, (ret, err)=>{
                            if( ret ){
                                         if(ret.buttonIndex <= this.recharge_list.length){
                                                 // 充值金额
                                                 money = this.recharge_list[ret.buttonIndex-1];
                                                 // 调用支付宝充值
                                                 this.create_recharge(money);
                                         }
                            }else{
    
                            }
                        });
    
                    },
                    create_recharge(money){
                        // 获取历史信息记录
                        var token = this.game.get("access_token") || this.game.fget("access_token");
                        this.game.checkout(this, token, (new_access_token)=>{
                            this.axios.post("",{
                                "jsonrpc": "2.0",
                                "id": this.uuid(),
                                "method": "Recharge.create",
                                "params": {
                                    "money": money,
                                }
                            },{
                                headers:{
                                    Authorization: "jwt " + token,
                                }
                            }).then(response=>{
                                if(parseInt(response.data.result.errno)==1000){
                                    // 前往支付宝
                                    var aliPayPlus = api.require('aliPayPlus');
                                    aliPayPlus.payOrder({
                                         orderInfo: response.data.result.order_string,
                                         sandbox: response.data.result.sandbox, // 将来APP上线需要修改成false
                                     }, (ret, err)=>{
                                          pay_result = {
                                                9000:"支付成功",
                              8000:"正在处理中",
                              4000:"订单支付失败",
                              5000:"重复请求",
                              6001:"取消支付",
                              6002:"网络连接出错",
                                                6004:"支付结果未知",
                                            }
                                        api.alert({
                                            title: '支付结果',
                                            msg: pay_result[ret.code],
                                            buttons: ['确定']
                                        });
                                            // 通知服务端, 修改充值结果
                                            this.return_recharge(response.data.result.order_number,token);
                                    });
                                }else{
                                        this.game.print(response.data);
                                }
                            }).catch(error=>{
                                // 网络等异常
                                this.game.print(error);
                            });
                        })
                    },
                    return_recharge(out_trade_number,token){
                        this.axios.post("",{
                            "jsonrpc": "2.0",
                            "id": this.uuid(),
                            "method": "Recharge.return",
                            "params": {
                                "out_trade_number": out_trade_number,
                            }
                        },{
                            headers:{
                                Authorization: "jwt " + token,
                            }
                        }).then(response=>{
                            if(parseInt(response.data.result.errno)==1000){
                                this.money = response.data.result.money.toFixed(2);
                            }
                        })
                    },
            checkout(){
              var token = this.game.get("access_token") || this.game.fget("access_token");
              this.game.checkout(this,token,(new_access_token)=>{
                this.connect();
                            this.login();
                            this.user_package();
                            this.buy_prop();
                            this.unlock_package_number();
                            this.show_pet();
                            this.use_prop();
                            this.active_tree();
              });
            },
            connect(){
              // socket连接
              this.socket = io.connect(this.settings.socket_server + this.namespace, {transports: ['websocket']});
              this.socket.on('connect', ()=>{
                  this.game.print("开始连接服务端");
                                var id = this.game.fget("id");
                                this.socket.emit("login",{"uid":id});
                                this.socket.emit("user_prop");
              });
            },
                    login(){
                        this.socket.on("login_response",(message)=>{
                            this.settings_info.orchard = message.orchard_settings;
                            this.settings_info.user=message.user_settings;
                            this.game.fsave({
                                "orchard_settings":message.orchard_settings,
                                "user_settings":message.user_settings,
                            });
                            this.game.save({
                                "tree_total":message.tree_total,
                                "user_tree_number":message.user_tree_number,
                                "user_tree_list":message.user_tree_list,
                                "tree_status":message.tree_status,
                                "pet_food_num":message.pet_food_num,
                                "fertilizer_num":message.fertilizer_num,
                            });
                            setTimeout(()=>{
                                // 通知种植园页面获取到了当前用户的种植信息
                                api.sendEvent({
                                    name: 'user_tree_data',
                                    extra: {}
                                });
                            },500);
                        });
                    },
                    user_package(){
                        // 用户背包道具列表
                        this.socket.on("user_prop_response",(message)=>{
                            this.game.fsave({
                                "user_package":message.data,
                            });
                            // 界面中的道具信息
                            this.game.save({
                                "pet_food_num":message.pet_food_num,
                                "fertilizer_num":message.fertilizer_num,
                            });
                            api.sendEvent({
                                name: 'update_prop_data',
                                extra: {
    
                                }
                            });
    
                        })
                    },
            go_index(){
              this.game.goWin("root");
            },
            go_friends(){
              this.game.goFrame("friends","friends.html",this.current);
              this.game.goFrame("friend_list","friend_list.html",this.current,{
                  x: 0,
                  y: 190,
                  w: 'auto',
                  h: 'auto',
              },null,true);
            },
            go_home(){
              this.game.goWin("user","user.html", this.current);
            },
                    go_orchard_shop(){
                        // 种植园商店
                        this.game.goFrame("orchard_shop","shop.html", this.current,null,{
                  type:"push",
                  subType:"from_top",
                  duration:300
              });
                    },
                    go_my_package(){
                        // 我的背包
                        this.game.goFrame("package","package.html", this.current,null,{
                  type:"push",
                  subType:"from_top",
                  duration:300
              });
                    },
                    buy_prop(){
                        api.addEventListener({
                                name: 'buy_prop'
                        }, (ret, err)=>{
                                if( ret ){
                                    // 用户购买道具
                                    this.socket.emit("user_buy_prop",ret.value);
                                }
                        });
                        this.socket.on("user_buy_prop_response",(message)=>{
                            alert(message.errmsg);
    
                        })
                    },
                    use_prop(){
                        // 使用道具
                        var pid = 0;
                        api.addEventListener({
                                name: 'use_prop'
                        }, (ret, err)=>{
                                if( ret ){
                                    // 用户使用道具
                                    pid = ret.value.pid;
                                    this.socket.emit("use_prop",ret.value.pid);
                                }
                        });
    
                        this.socket.on("prop_use_response",(message)=>{
                            if(parseInt(message.errno) === 1000){
                                api.sendEvent({
                                        name: 'prop_use_success',
                                        extra: {
                                            pid: pid
                                        }
                                });
                            }else{
                                api.alert({
                                        title: '提示',
                                        msg: message.errmsg,
                                });
    
                            }
                        });
                    },
                    unlock_package_number(){
                        api.addEventListener({
                                name: 'unlock_package_number'
                        }, (ret, err)=>{
                                if( ret ){
                                    // 用户购买道具
                                    this.socket.emit("unlock_package");
                                }
                        });
    
                        this.socket.on("unlock_package_response",(message)=>{
                            if(parseInt(message.errno) === 1000){
                                api.sendEvent({
                                    name: 'unlock_package_success',
                                    extra: {
                                    }
                                });
                            }else{
                                api.alert({
                                        title: '提示',
                                        msg: message.errmsg,
                                });
    
                            }
    
                        })
                    },
                    show_pet(){
                        // 显示宠物
                        this.socket.emit("pet_show");
                        this.socket.on("pet_show_response",(message)=>{
                            if(parseInt(message.errno) === 1000){
                                // 把宠物信息保存到本地
                                this.game.save({"pet_list":message.pet_list})
                                this.game.save({"pet_number":message.pet_number})
                                setTimeout(()=>{
                                    api.sendEvent({
                                            name: 'pet_show_success',
                                            extra: {}
                                    });
                                },500);
                            }else{
                                api.alert({
                                        title: '提示',
                                        msg: message.errmsg,
                                });
                            }
    
                        })
                    },
                    active_tree(){
                        // 激活树桩
                        api.addEventListener({
                            name: 'active_tree'
                        }, (ret, err)=>{
                            if( ret ){
                                    this.socket.emit("active_tree");
                            }
                        });
    
                        this.socket.on("active_tree_response",(message)=>{
                            if(parseInt(message.errno) === 1000){
                                // 更新数据到本地
                                api.sendEvent({
                                        name: 'active_tree_success',
                                        extra: {}
                                });
                            }else{
                                api.alert({
                                        title: '提示',
                                        msg: message.errmsg,
                                });
                            }
    
                        })
                    }
                }
            });
        }
        </script>
    </body>
    </html>
    前端显示道具数量:orchard.html
    <!DOCTYPE html>
    <html>
    <head>
        <title>用户中心</title>
        <meta name="viewport" content="width=device-width,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no">
        <meta charset="utf-8">
        <link rel="stylesheet" href="../static/css/main.css">
        <script src="../static/js/vue.js"></script>
        <script src="../static/js/axios.js"></script>
        <script src="../static/js/main.js"></script>
        <script src="../static/js/uuid.js"></script>
        <script src="../static/js/settings.js"></script>
        <script src="../static/js/socket.io.js"></script>
    </head>
    <body>
        <div class="app orchard orchard-frame" id="app">
        <div class="background">
          <img class="grassland2" src="../static/images/grassland2.png" alt="">
          <img class="mushroom1" src="../static/images/mushroom1.png" alt="">
          <img class="stake1" src="../static/images/stake1.png" alt="">
          <img class="stake2" src="../static/images/stake2.png" alt="">
        </div>
        <div class="pet-box">
          <div class="pet">
                    <img v-if="pet_list.length > 0" class="pet-item" :src="settings.static_url+pet_list[0].image" alt="">
          </div>
                <div class="pet" v-if="pet_number > 1">
                    <img v-if="pet_list.length>1" class="pet-item" :src="settings.static_url+pet_list[1].image" alt="">
          </div>
          <div class="pet turned_off" v-if="pet_number==1">
            <img class="turned_image" src="../static/images/turned_off.png" alt="">
            <p>请购买宠物</p>
          </div>
        </div>
        <div class="tree-list">
          <div class="tree-box">
                    <div class="tree" v-for="tree in user_tree_data.user_tree_list">
                        <img :src="tree_img(tree.status)" alt="">
                    </div>
                    <!-- 已激活但是未种植的树桩列表 -->
                    <div class="tree" v-for="i in active_tree">
                        <img src="../static/images/tree1.png" alt="">
                    </div>
                    <!-- 未激活树桩列表 -->
                    <div @click="unlock_tree" class="tree" v-for="i in lock_tree">
                        <img src="../static/images/tree0.png" alt="">
                    </div>
          </div>
    
        </div>
        <div class="prop-list">
          <div class="prop">
            <img src="../static/images/prop1.png" alt="">
            <span>{{fertilizer_num}}</span>
            <p>化肥</p>
          </div>
          <div class="prop">
            <img src="../static/images/prop2.png" alt="">
            <span>0</span>
            <p>修剪</p>
          </div>
          <div class="prop">
            <img src="../static/images/prop3.png" alt="">
            <span>1</span>
            <p>浇水</p>
          </div>
          <div class="prop">
            <img src="../static/images/prop4.png" alt="">
            <span>{{pet_food_num}}</span>
            <p>宠物粮</p>
          </div>
        </div>
        <div class="pet-hp-list">
          <div class="pet-hp" v-for="pet in pet_list">
            <p>宠物1 饱食度</p>
            <div class="hp">
              <div :style="{ pet.hp+'%'}" class="process">{{pet.hp}}%</div>
            </div>
          </div>
        </div>
        </div>
        <script>
        apiready = function(){
            init();
            new Vue({
                el:"#app",
                data(){
                    return {
              namespace: '/mofang',
              token:"",
              socket: null,
                        pet_food_num:0, // 宠物粮数量
                        fertilizer_num:0,  // 化肥数量
                        pet_list:[],
                        user_tree_data:{
                            "total_tree": 9,       // 总树桩数量
                            "user_tree_number": 0, // 当前用户激活树桩数量
                            "user_tree_list":[     // 当前种植的树桩列表状态
    
                            ],
                        },
                        tree_status:{
    
                        },
                        // user_tree_data:{
                        //     "total_tree":9,        // 总树桩数量
                        //     "user_tree_number": 5, // 当前用户激活树桩数量
                        //     "user_tree_list":[     // 当前种植的树桩列表状态
                        //             { // 树桩状态
                        //                  "time":1609808084, // 种植时间
                        //                  "status":4,        // 植物状态
                        //                  "has_time": 300,   // 状态时间
                        //             },
                        //     ],
                        // },
                        // tree_status:{
                        //     "tree_status_0": "tree0.png", // 树桩
                        //     "tree_status_1": "tree1.png", // 空桩
                        //     "tree_status_2": "tree2.png", // 幼苗
                        //     "tree_status_3": "tree3.png", // 成长
                        //     "tree_status_4": "tree4.png", // 成熟
                        // },
                        pet_number:[],
              timeout: 0,
                        prev:{name:"",url:"",params:{}},
                        current:{name:"orchard",url:"orchard.html",params:{}},
                    }
                },
                computed:{
                    // 已激活但是未使用的树桩
                    active_tree(){
                        return parseInt(this.user_tree_data.user_tree_number - this.user_tree_data.user_tree_list.length);
                    },
                    // 未激活的剩余树桩
                    lock_tree(){
                        return parseInt( this.user_tree_data.total_tree-this.user_tree_data.user_tree_number);
                    },
                },
          created(){
                    this.show_pet_list();
                    this.show_tree_list();
                    this.get_prop_list();
          },
                methods:{
                    get_prop_list(){
                        // 更新道具列表信息
                        api.addEventListener({
                            name: 'update_prop_data'
                        }, (ret, err)=>{
                            if( ret ){
                                    this.pet_food_num = this.game.get("pet_food_num");
                                    this.fertilizer_num = this.game.get("fertilizer_num");
                            }
                        });
    
                    },
                    tree_img(status){
                        return '../static/images/'+this.tree_status[status];
                    },
                    show_pet_list(){
                        api.addEventListener({
                                name: 'pet_show_success'
                        }, (ret, err)=>{
                                if( ret ){
                                    // 用户购买道具
                                    this.pet_list = this.game.get("pet_list");
                                    this.pet_number = parseInt(this.game.get("pet_number"));
                                }
                        });
                    },
                    show_tree_list(){
                        api.addEventListener({
                                name: 'user_tree_data'
                        }, (ret, err)=>{
                                if( ret ){
                                    // 用户种植植物信息
                                    this.user_tree_data.tree_total = parseInt(this.game.get("tree_total"));
                                    this.user_tree_data.user_tree_number = parseInt(this.game.get("user_tree_number"));
                                    this.user_tree_data.user_tree_list = this.game.get("user_tree_list");
                                    this.tree_status = this.game.get("tree_status");
                                    this.pet_food_num = this.game.get("pet_food_num");
                                    this.fertilizer_num = this.game.get("fertilizer_num");
                                }
                        });
                    },
                    unlock_tree(){
                        // 激活树桩通知
                        api.confirm({
                            title: '提示',
                            msg: '是否激活树桩?',
                            buttons: ['确定', '取消']
                        }, (ret, err)=>{
                            if( ret.buttonIndex == 1 ){
                                    api.sendEvent({
                                        name: 'active_tree',
                                        extra: {
    
                                            }
                                    });
                            }
                        });
    
                        // 激活成功!
                        api.addEventListener({
                            name: 'active_tree_success'
                        }, (ret, err)=>{
                            if( ret ){
                                 // 新增树桩的数量
                                         this.user_tree_data.user_tree_number+=1;
                                        this.game.save({"user_tree_number": this.user_tree_data.user_tree_number});
                            }
                        });
                    }
                }
            });
        }
        </script>
    </body>
    </html>
    前端显示道具数量:my_orchard.html

    2.显示剪刀和浇水数量

    1.后端:根据植物计算剪刀和浇水的数量

    from application import socketio
    from flask import request
    from application.apps.users.models import User
    from flask_socketio import join_room, leave_room
    from application import mongo
    from .models import Goods,Setting,db
    from status import APIStatus as status
    from message import ErrorMessage as errmsg
    # 建立socket通信
    # @socketio.on("connect", namespace="/mofang")
    # def user_connect():
    #     """用户连接"""
    #     print("用户%s连接过来了!" % request.sid)
    #     # 主动响应数据给客户端
    #     socketio.emit("server_response","hello",namespace="/mofang")
    
    # 断开socket通信
    @socketio.on("disconnect", namespace="/mofang")
    def user_disconnect():
        print("用户%s退出了种植园" % request.sid )
    
    @socketio.on("login", namespace="/mofang")
    def user_login(data):
        # 分配房间
        room = data["uid"]
        join_room(room)
        # 保存当前用户和sid的绑定关系
        # 判断当前用户是否在mongo中有记录
        query = {
            "_id": data["uid"]
        }
        ret = mongo.db.user_info_list.find_one(query)
        if ret:
            mongo.db.user_info_list.update_one(query,{"$set":{"sid": request.sid}})
        else:
            mongo.db.user_info_list.insert_one({
            "_id": data["uid"],
            "sid": request.sid,
        })
    
        # 返回种植园的相关配置参数
        orchard_settings = {}
        setting_list = Setting.query.filter(Setting.is_deleted==False, Setting.status==True).all()
        """
        现在的格式:
            [<Setting package_number_base>, <Setting package_number_max>, <Setting package_unlock_price_1>]
        需要返回的格式:
            {
                package_number_base:4,
                package_number_max: 32,
                ...
            }
        """
        for item in setting_list:
            orchard_settings[item.name] = item.value
    
        # 返回当前用户相关的配置参数
        user_settings = {}
        # 从mongo中查找用户信息,判断用户是否激活了背包格子
        user_dict = mongo.db.user_info_list.find_one({"sid":request.sid})
        # 背包格子
        if user_dict.get("package_number") is None:
            user_settings["package_number"]  = orchard_settings.get("package_number_base",4)
            mongo.db.user_info_list.update_one({"sid":request.sid},{"$set":{"package_number": user_settings["package_number"]}})
        else:
            user_settings["package_number"]  = user_dict.get("package_number")
    
        """种植园植物信息"""
         # 总树桩数量
        setting = Setting.query.filter(Setting.name == "user_total_tree").first()
        if setting is None:
            tree_total = 9
        else:
            tree_total = int(setting.value)
    
        # 用户已经激活的树桩
        setting = Setting.query.filter(Setting.name == "user_active_tree").first()
        if setting is None:
            user_tree_number = 3
        else:
            user_tree_number = int(setting.value)
        user_tree_number = user_dict.get("user_tree_number",user_tree_number)
    
        # 种植的植物列表
        user_tree_list = user_dict.get("user_tree_list", [])
        key = 0
        for tree_item in user_tree_list:
            tree_item["status"] = "tree_status_%s" % int(tree_item["status"]) # 植物状态
            tree_item["water_time"] = redis.ttl("user_tree_water_%s_%s" % (data["uid"],key))
            tree_item["growup_time"] = redis.ttl("user_tree_growup_%s_%s" % (data["uid"],key))
            key+=1
        # 植物状态信息
        status_list = [
            "tree_status_0",
            "tree_status_1",
            "tree_status_2",
            "tree_status_3",
            "tree_status_4",
        ]
        setting_list = Setting.query.filter(Setting.name.in_(status_list)).all()
        tree_status = {}
        for item in setting_list:
            tree_status[item.name] = item.value
    
        """显示植物相关道具"""
        # 获取背包中的化肥和宠物粮
        fertilizer_num,pet_food_num = get_package_prop_list(user_dict)
        # 获取剪刀和浇水
        # 只有植物处于成长期才会允许裁剪
        # 只有植物处于幼苗期才会允许浇水
        waters = 0
        shears = 0
        user_tree_list = user_dict.get("user_tree_list",[])
        if len(user_tree_list) > 0:
            key = 0
            for tree in user_tree_list:
                if (tree["status"] == "tree_status_%s" % 2) and int(tree.get("waters",0)) == 0:
                    """处于幼苗期"""
                    # 判断只有种植指定时间以后的幼苗才可以浇水
                    interval_time = redis.ttl("user_tree_water_%s_%s" % (user_dict.get("_id"), key) )
                    if interval_time==-2:
                        waters+=1
    
                elif (tree["status"] == "tree_status_%s" % 3) and int(tree.get("shears",0)) == 0:
                    """处于成长期"""
                    interval_time = redis.ttl("user_tree_shears_%s_%s" % (user_dict.get("_id"), key))
                    if interval_time==-2:
                        shears+=1
                key+=1
        message = {
            "errno":status.CODE_OK,
            "errmsg":errmsg.ok,
            "orchard_settings":orchard_settings,
            "user_settings":user_settings,
            "tree_total":tree_total,
            "user_tree_number":user_tree_number,
            "user_tree_list":user_tree_list,
            "fertilizer_num":fertilizer_num,
            "pet_food_num":pet_food_num,
            "tree_status":tree_status,
            "waters":waters,
            "shears":shears,
        }
        print(message)
        socketio.emit("login_response", message, namespace="/mofang", room=room)
    
    def get_package_prop_list(user_dict):
        fertilizer_num = 0
        pet_food_num = 0
        prop_list = user_dict.get("prop_list", {})
        for prop_item, num in prop_list.items():
            pid = prop_item.split("_")[-1]
            num = int(num)
            prop_obj = Goods.query.get(pid)
            if prop_obj.prop_type == 2:
                # 提取化肥
                fertilizer_num += num
            elif prop_obj.prop_type == 3:
                # 提取宠物粮
                pet_food_num += num
    
        return fertilizer_num,pet_food_num
    
    @socketio.on("user_buy_prop", namespace="/mofang")
    def user_buy_prop(data):
        """用户购买道具"""
        room = request.sid
        # 从mongo中获取当前用户信息
        user_info = mongo.db.user_info_list.find_one({"sid":request.sid})
        user = User.query.get(user_info.get("_id"))
        if user is None:
            socketio.emit("user_buy_prop_response", {"errno":status.CODE_NO_USER,"errmsg":errmsg.user_not_exists}, namespace="/mofang", room=room)
            return
    
        # 判断背包物品存储是否达到上限
        use_package_number = int(user_info.get("use_package_number",0)) # 当前诗经使用的格子数量
        package_number = int(user_info.get("package_number",0))         # 当前用户已经解锁的格子数量
        # 本次购买道具需要使用的格子数量
        setting = Setting.query.filter(Setting.name == "td_prop_max").first()
        if setting is None:
            td_prop_max = 10
        else:
            td_prop_max = int(setting.value)
    
        # 计算购买道具以后需要额外占用的格子数量
        if ("prop_%s" % data["pid"]) in user_info.get("prop_list",{}):
            """曾经购买过当前道具"""
            prop_num = int( user_info.get("prop_list")["prop_%s" % data["pid"]]) # 购买前的道具数量
            new_prop_num = prop_num+int(data["num"]) # 如果成功购买道具以后的数量
            old_td_num = prop_num // td_prop_max
            if prop_num % td_prop_max > 0:
                old_td_num+=1
            new_td_num = new_prop_num // td_prop_max
            if new_prop_num % td_prop_max > 0:
                new_td_num+=1
            td_num = new_td_num - old_td_num
        else:
            """新增购买的道具"""
            # 计算本次购买道具需要占用的格子数量
    
            if int(data["num"]) > td_prop_max:
                """需要多个格子"""
                td_num = int(data["num"]) // td_prop_max
                if int(data["num"]) % td_prop_max > 0:
                    td_num+=1
            else:
                """需要一个格子"""
                td_num = 1
    
        if use_package_number+td_num > package_number:
            """超出存储上限"""
            socketio.emit("user_buy_prop_response", {"errno": status.CODE_NO_PACKAGE, "errmsg": errmsg.no_package},
                          namespace="/mofang", room=room)
            return
    
        # 从mysql中获取商品价格
        prop = Goods.query.get(data["pid"])
        if user.money > 0: # 当前商品需要通过RMB购买
            if float(user.money) < float(prop.price) * int(data["num"]):
                socketio.emit("user_buy_prop_response", {"errno":status.CODE_NO_MONEY,"errmsg":errmsg.money_no_enough}, namespace="/mofang", room=room)
                return
        else:
            """当前通过果子进行购买"""
            if int(user.credit) < int(prop.credit) * int(data["num"]):
                socketio.emit("user_buy_prop_response", {"errno": status.CODE_NO_CREDIT, "errmsg": errmsg.credit_no_enough},
                              namespace="/mofang", room=room)
                return
    
        # 从mongo中获取用户列表信息,提取购买的商品数量进行累加和余额
        query = {"sid": request.sid}
        if user_info.get("prop_list") is None:
            """此前没有购买任何道具"""
            message = {"$set":{"prop_list":{"prop_%s" % prop.id:int(data["num"])}}}
            mongo.db.user_info_list.update_one(query,message)
        else:
            """此前有购买了道具"""
            prop_list = user_info.get("prop_list") # 道具列表
            if ("prop_%s" % prop.id) in prop_list:
                """如果再次同一款道具"""
                prop_list[("prop_%s" % prop.id)] = prop_list[("prop_%s" % prop.id)] + int(data["num"])
            else:
                """此前没有购买过这种道具"""
                prop_list[("prop_%s" % prop.id)] = int(data["num"])
    
            mongo.db.user_info_list.update_one(query, {"$set":{"prop_list":prop_list}})
    
        # 扣除余额或果子
        if prop.price > 0:
            user.money = float(user.money) - float(prop.price) * int(data["num"])
        else:
            user.credit = int(user.credit) - int(prop.credit) * int(data["num"])
    
        db.session.commit()
    
        # 返回购买成功的信息
        socketio.emit("user_buy_prop_response", {"errno":status.CODE_OK,"errmsg":errmsg.ok}, namespace="/mofang", room=room)
        # 返回最新的用户道具列表
        user_prop()
    
    @socketio.on("user_prop", namespace="/mofang")
    def user_prop():
        """用户道具"""
        userinfo = mongo.db.user_info_list.find_one({"sid":request.sid})
        prop_list = userinfo.get("prop_list",{})
        prop_id_list = []
        for prop_str,num in prop_list.items():
            pid = int(prop_str[5:])
            prop_id_list.append(pid)
    
        data = []
        prop_list_data = Goods.query.filter(Goods.id.in_(prop_id_list)).all()
        setting = Setting.query.filter(Setting.name == "td_prop_max").first()
        if setting is None:
            td_prop_max = 10
        else:
            td_prop_max = int(setting.value)
    
        for prop_data in prop_list_data:
            num = int( prop_list[("prop_%s" % prop_data.id)])
            if td_prop_max > num:
                data.append({
                    "num": num,
                    "image": prop_data.image,
                    "pid": prop_data.id,
                    "pty": prop_data.prop_type,
                })
            else:
                padding_time = num // td_prop_max
                padding_last = num % td_prop_max
                arr = [{
                    "num": td_prop_max,
                    "image": prop_data.image,
                    "pid": prop_data.id,
                    "pty": prop_data.prop_type,
                }] * padding_time
                if padding_last != 0:
                    arr.append({
                        "num": padding_last,
                        "image": prop_data.image,
                        "pid": prop_data.id,
                        "pty": prop_data.prop_type,
                    })
                data = data + arr
        mongo.db.user_info_list.update_one({"sid":request.sid},{"$set":{"use_package_number":len(data)}})
        room = request.sid
        fertilizer_num,pet_food_num = get_package_prop_list(userinfo)
        socketio.emit("user_prop_response", {
            "errno": status.CODE_OK,
            "errmsg": errmsg.ok,
            "data":data,
            "fertilizer_num":fertilizer_num,
            "pet_food_num":pet_food_num,
        }, namespace="/mofang",
                      room=room)
    
    @socketio.on("unlock_package", namespace="/mofang")
    def unlock_package():
        """解锁背包"""
        # 从mongo获取当前用户解锁的格子数量
        user_info = mongo.db.user_info_list.find_one({"sid":request.sid})
        user = User.query.get(user_info.get("_id"))
        if user is None:
            socketio.emit("unlock_package_response", {"errno":status.CODE_NO_USER,"errmsg":errmsg.user_not_exists}, namespace="/mofang", room=room)
            return
    
        package_number = int(user_info.get("package_number"))
        num = 7 - (32 - package_number) // 4  # 没有解锁的格子
    
        # 从数据库中获取解锁背包的价格
        setting = Setting.query.filter(Setting.name == "package_unlock_price_%s" % num).first()
        if setting is None:
            unlock_price = 0
        else:
            unlock_price = int(setting.value)
    
        # 判断是否有足够的积分或者价格
        room = request.sid
        if user.money < unlock_price:
            socketio.emit("unlock_package_response", {"errno": status.CODE_NO_MONEY, "errmsg": errmsg.money_no_enough},
                          namespace="/mofang", room=room)
            return
    
        # 解锁成功
        user.money = float(user.money) - float(unlock_price)
        db.session.commit()
    
        # mongo中调整数量
        mongo.db.user_info_list.update_one({"sid":request.sid},{"$set":{"package_number": package_number+1}})
        # 返回解锁的结果
        socketio.emit("unlock_package_response", {
            "errno": status.CODE_OK,
            "errmsg": errmsg.ok},
                      namespace="/mofang", room=room)
    import math
    from application import redis
    @socketio.on("pet_show", namespace="/mofang")
    def pet_show():
        """显示宠物"""
        room = request.sid
        user_info = mongo.db.user_info_list.find_one({"sid":request.sid})
        user = User.query.get(user_info.get("_id"))
        if user is None:
            socketio.emit("pet_show_response", {"errno":status.CODE_NO_USER,"errmsg":errmsg.user_not_exists}, namespace="/mofang", room=room)
            return
    
        # 获取宠物列表
        pet_list = user_info.get("pet_list", [])
    
        """
        pet_list: [
          { 
             "pid":11,
             "image":"pet.png",
             "hp":100%,
             "created_time":xxxx-xx-xx xx:xx:xx,
             "skill":"70%",
             "has_time":30天
          },
        ]
        """
        # 从redis中提取当前宠物的饱食度和有效期
        for key,pet in enumerate(pet_list):
            pet["hp"] = math.ceil( redis.ttl("pet_%s_%s_hp" % (user.id,key+1)) / 86400 * 100 )
            pet["has_time"] = redis.ttl("pet_%s_%s_expire" % (user.id,key+1))
    
        pet_number = user_info.get("pet_number", 1)
        socketio.emit(
            "pet_show_response",
            {
                "errno": status.CODE_OK,
                "errmsg": errmsg.ok,
                "pet_list": pet_list,
                "pet_number": pet_number,
            },
            namespace="/mofang",
            room=room
        )
    
    from datetime import datetime
    @socketio.on("use_prop", namespace="/mofang")
    def use_prop(pid):
        """使用道具"""
        room = request.sid
        # 获取mongo中的用户信息
        user_info = mongo.db.user_info_list.find_one({"sid": request.sid})
        # 获取mysql中的用户信息
        user = User.query.get(user_info.get("_id"))
        if user is None:
            socketio.emit("pet_use_response", {"errno": status.CODE_NO_USER, "errmsg": errmsg.user_not_exists},
                          namespace="/mofang", room=room)
            return
    
        # 获取道具
        prop_data = Goods.query.get(pid)
        if prop_data is None:
            socketio.emit("pet_use_response", {"errno":status.CODE_NO_SUCH_PROP,"errmsg":errmsg.not_such_prop}, namespace="/mofang", room=room)
            return
    
        if int(prop_data.prop_type) == 0:
            """使用植物道具"""
            # 1. 判断当前的植物数量是否有空余
            tree_list = user_info.get("user_tree_list", [])
    
            # 当前用户最多可种植的数量
            setting = Setting.query.filter(Setting.name == "user_active_tree").first()
            if setting is None:
                user_tree_number = 3
            else:
                user_tree_number = int(setting.value)
            user_tree_number = user_info.get("user_tree_number", user_tree_number)
    
            if len(tree_list) >= user_tree_number:
                socketio.emit("prop_use_response", {"errno": status.CODE_NO_EMPTY, "errmsg": errmsg.prop_not_empty},
                              namespace="/mofang", room=room)
                return
    
            # 使用道具
            mongo.db.user_info_list.update_one({"sid":room},{"$push":{"user_tree_list":
                { # 植物状态
                    "time": int(datetime.now().timestamp()), # 种植时间
                    "status": 2, # 植物状态,2表示幼苗状态
                    "waters": 0, # 浇水次数
                    "shears": 0, # 使用剪刀次数
                }
            }})
    
            # 从种下去到浇水的时间
            pipe = redis.pipeline()
            pipe.multi()
            setting = Setting.query.filter(Setting.name == "tree_water_time").first()
            if setting is None:
                tree_water_time = 3600
            else:
                tree_water_time = int(setting.value)
            # 必须等时间到了才可以浇水
            pipe.setex("user_tree_water_%s_%s" % (user.id,len(tree_list)),int(tree_water_time),"_")
            # 必须等时间到了才可以到成长期
            setting = Setting.query.filter(Setting.name == "tree_growup_time").first()
            if setting is None:
                tree_growup_time = 3600
            else:
                tree_growup_time = int(setting.value)
            pipe.setex("user_tree_growup_%s_%s" % (user.id,len(tree_list)),tree_growup_time, "_")
            pipe.execute()
    
            user_login({"uid": user.id})
    
        if int(prop_data.prop_type) == 1:
            """使用宠物道具"""
            # 1. 判断当前的宠物数量
            # 获取宠物列表
            pet_list = user_info.get("pet_list", [])
            if len(pet_list) > 1:
                socketio.emit("pet_use_response", {"errno":status.CODE_NO_EMPTY,"errmsg":errmsg.pet_not_empty}, namespace="/mofang", room=room)
                return
    
            # 2. 是否有空余的宠物栏位
            pet_number = user_info.get("pet_number",1)
            if pet_number <= len(pet_list):
                socketio.emit("pet_use_response", {"errno":status.CODE_NO_EMPTY,"errmsg":errmsg.pet_not_empty}, namespace="/mofang", room=room)
                return
    
            # 3. 初始化当前宠物信息
            # 获取有效期和防御值
            exp_data = Setting.query.filter(Setting.name=="pet_expire_%s" % pid).first()
            ski_data = Setting.query.filter(Setting.name=="pet_skill_%s" % pid).first()
    
            if exp_data is None:
                # 默认7天有效期
                expire = 7
            else:
                expire = exp_data.value
    
            if ski_data is None:
                skill  = 10
            else:
                skill  = ski_data.value
    
            # 在redis中设置当前宠物的饱食度
            pipe = redis.pipeline()
            pipe.multi()
            pipe.setex("pet_%s_%s_hp" % (user.id, len(pet_list)+1), 24*60*60, "_")
            pipe.setex("pet_%s_%s_expire" % (user.id, len(pet_list)+1), int(expire)*24*60*60, "_")
            pipe.execute()
    
            # 基本保存到mongo
            mongo.db.user_info_list.update_one({"sid":request.sid},{"$push":{"pet_list":{
                 "pid": pid,
                 "image": prop_data.image,
                 "created_time": int( datetime.now().timestamp() ),
                 "skill": skill,
              }}})
    
            """
            db.user_info_list.updateOne({"_id":"52"},{"$push":{"pet_list":{
                     "pid": 2,
                     "image": "pet1.png",
                     "created_time": 1609727155,
                     "skill": 30,
                  }}})
            """
    
            pet_show()
    
        # 扣除背包中的道具数量
        prop_list = user_info.get("prop_list",{})
        for key,value in prop_list.items():
            if key == ("prop_%s" % pid):
                if int(value) > 1:
                    prop_list[key] = int(value) - 1
                else:
                    prop_list.pop(key)
                break
    
        mongo.db.user_info_list.update_one({"sid":room},{"$set":{"prop_list":prop_list}})
        user_prop()
    
        socketio.emit("prop_use_response", {"errno": status.CODE_OK, "errmsg": errmsg.ok},
                          namespace="/mofang", room=room)
    
    
    @socketio.on("active_tree", namespace="/mofang")
    def active_tree():
        """激活树桩"""
        room = request.sid
        # 获取mongo中的用户信息
        user_info = mongo.db.user_info_list.find_one({"sid": request.sid})
        # 获取mysql中的用户信息
        user = User.query.get(user_info.get("_id"))
        if user is None:
            socketio.emit("active_tree_response", {"errno": status.CODE_NO_USER, "errmsg": errmsg.user_not_exists},
                          namespace="/mofang", room=room)
            return
    
        # 判断树桩是否达到上限
        tree_number_data = Setting.query.filter(Setting.name == "user_active_tree").first()
        total_tree_data = Setting.query.filter(Setting.name == "user_total_tree").first()
        if tree_number_data is None:
            tree_number = 1
        else:
            tree_number = tree_number_data.value
    
        if total_tree_data is None:
            total_tree = 9
        else:
            total_tree = int(total_tree_data.value)
    
        user_tree_number = int(user_info.get("user_tree_number",tree_number))
        if user_tree_number >= total_tree:
            socketio.emit("active_tree_response", {"errno": status.CODE_NO_EMPTY, "errmsg": errmsg.prop_not_empty},
                          namespace="/mofang", room=room)
            return
    
        # 扣除激活的果子数量
        ret = Setting.query.filter(Setting.name == "active_tree_price").first()
        if ret is None:
            active_tree_price = 100 * user_tree_number
        else:
            active_tree_price = int(ret.value) * user_tree_number
    
        if active_tree_price > int(user.credit):
            socketio.emit("active_tree_response", {"errno": status.CODE_NO_CREDIT, "errmsg": errmsg.credit_no_enough},
                          namespace="/mofang", room=room)
            return
    
        user.credit = int(user.credit) - active_tree_price
        db.session.commit()
    
        mongo.db.user_info_list.update_one({"sid":room},{"$set":{"user_tree_number":user_tree_number+1}})
    
        socketio.emit("active_tree_response", {"errno": status.CODE_OK, "errmsg": errmsg.ok},
                      namespace="/mofang", room=room)
        return
    服务端根据植物的状态计算剪刀和浇水的数量

    2.前端:进入种植园后显示剪刀和浇水的次数

    <!DOCTYPE html>
    <html>
    <head>
        <title>用户中心</title>
        <meta name="viewport" content="width=device-width,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no">
        <meta charset="utf-8">
        <link rel="stylesheet" href="../static/css/main.css">
        <script src="../static/js/vue.js"></script>
        <script src="../static/js/axios.js"></script>
        <script src="../static/js/main.js"></script>
        <script src="../static/js/uuid.js"></script>
        <script src="../static/js/settings.js"></script>
        <script src="../static/js/socket.io.js"></script>
    </head>
    <body>
        <div class="app orchard" id="app">
        <img class="music" :class="music_play?'music2':''" @click="music_play=!music_play" src="../static/images/player.png">
        <div class="orchard-bg">
                <img src="../static/images/bg2.png">
                <img class="board_bg2" src="../static/images/board_bg2.png">
            </div>
        <img class="back" @click="go_index" src="../static/images/user_back.png" alt="">
        <div class="header">
                <div class="info" @click="go_home">
                    <div class="avatar">
                        <img class="avatar_bf" src="../static/images/avatar_bf.png" alt="">
                        <img class="user_avatar" src="../static/images/avatar.png" alt="">
                        <img class="avatar_border" src="../static/images/avatar_border.png" alt="">
                    </div>
                    <p class="user_name">好听的昵称</p>
                </div>
                <div class="wallet">
                    <div class="balance" @click="user_recharge">
                        <p class="title"><img src="../static/images/money.png" alt="">钱包</p>
                        <p class="num">{{money}}</p>
                    </div>
                    <div class="balance">
                        <p class="title"><img src="../static/images/integral.png" alt="">果子</p>
                        <p class="num">99,999.00</p>
                    </div>
                </div>
          <div class="menu-list">
            <div class="menu">
              <img src="../static/images/menu1.png" alt="">
              排行榜
            </div>
            <div class="menu">
              <img src="../static/images/menu2.png" alt="">
              签到有礼
            </div>
            <div class="menu" @click="go_orchard_shop">
              <img src="../static/images/menu3.png" alt="">
              道具商城
            </div>
            <div class="menu">
              <img src="../static/images/menu4.png" alt="">
              邮件中心
            </div>
          </div>
            </div>
        <div class="footer" >
          <ul class="menu-list">
            <li class="menu">新手</li>
            <li class="menu" @click="go_my_package">背包</li>
            <li class="menu-center" @click="go_orchard_shop">商店</li>
            <li class="menu">消息</li>
            <li class="menu">好友</li>
          </ul>
        </div>
        </div>
        <script>
        apiready = function(){
            init();
            new Vue({
                el:"#app",
                data(){
                    return {
              music_play:true,
              namespace: '/mofang',
              token:"",
                        money:"",
                        settings_info:{
                            orchard: {},  // 种植园公共参数
                            user:{},      // 用户私有相关参数
                        },
              socket: null,
                        recharge_list: ['10','20','50','100','200','500','1000'],
              timeout: 0,
                        prev:{name:"",url:"",params:{}},
                        current:{name:"orchard",url:"orchard.html",params:{}},
                    }
                },
                beforeCreate(){
                    this.game.goFrame("orchard","my_orchard.html", this.current,{
                            x: 0,
                            y: 180,
                            w: 'auto',
                            h: 410,
                    },null);
                },
          created(){
            this.checkout();
                    this.money = this.game.fget("money");
          },
                methods:{
                    user_recharge(){
                        // 发起充值请求
                        api.actionSheet({
                            title: '余额充值',
                            cancelTitle: '取消',
                            buttons: this.recharge_list
                        }, (ret, err)=>{
                            if( ret ){
                                         if(ret.buttonIndex <= this.recharge_list.length){
                                                 // 充值金额
                                                 money = this.recharge_list[ret.buttonIndex-1];
                                                 // 调用支付宝充值
                                                 this.create_recharge(money);
                                         }
                            }else{
    
                            }
                        });
    
                    },
                    create_recharge(money){
                        // 获取历史信息记录
                        var token = this.game.get("access_token") || this.game.fget("access_token");
                        this.game.checkout(this, token, (new_access_token)=>{
                            this.axios.post("",{
                                "jsonrpc": "2.0",
                                "id": this.uuid(),
                                "method": "Recharge.create",
                                "params": {
                                    "money": money,
                                }
                            },{
                                headers:{
                                    Authorization: "jwt " + token,
                                }
                            }).then(response=>{
                                if(parseInt(response.data.result.errno)==1000){
                                    // 前往支付宝
                                    var aliPayPlus = api.require('aliPayPlus');
                                    aliPayPlus.payOrder({
                                         orderInfo: response.data.result.order_string,
                                         sandbox: response.data.result.sandbox, // 将来APP上线需要修改成false
                                     }, (ret, err)=>{
                                          pay_result = {
                                                9000:"支付成功",
                              8000:"正在处理中",
                              4000:"订单支付失败",
                              5000:"重复请求",
                              6001:"取消支付",
                              6002:"网络连接出错",
                                                6004:"支付结果未知",
                                            }
                                        api.alert({
                                            title: '支付结果',
                                            msg: pay_result[ret.code],
                                            buttons: ['确定']
                                        });
                                            // 通知服务端, 修改充值结果
                                            this.return_recharge(response.data.result.order_number,token);
                                    });
                                }else{
                                        this.game.print(response.data);
                                }
                            }).catch(error=>{
                                // 网络等异常
                                this.game.print(error);
                            });
                        })
                    },
                    return_recharge(out_trade_number,token){
                        this.axios.post("",{
                            "jsonrpc": "2.0",
                            "id": this.uuid(),
                            "method": "Recharge.return",
                            "params": {
                                "out_trade_number": out_trade_number,
                            }
                        },{
                            headers:{
                                Authorization: "jwt " + token,
                            }
                        }).then(response=>{
                            if(parseInt(response.data.result.errno)==1000){
                                this.money = response.data.result.money.toFixed(2);
                            }
                        })
                    },
            checkout(){
              var token = this.game.get("access_token") || this.game.fget("access_token");
              this.game.checkout(this,token,(new_access_token)=>{
                this.connect();
                            this.login();
                            this.user_package();
                            this.buy_prop();
                            this.unlock_package_number();
                            this.show_pet();
                            this.use_prop();
                            this.active_tree();
              });
            },
            connect(){
              // socket连接
              this.socket = io.connect(this.settings.socket_server + this.namespace, {transports: ['websocket']});
              this.socket.on('connect', ()=>{
                  this.game.print("开始连接服务端");
                                var id = this.game.fget("id");
                                this.socket.emit("login",{"uid":id});
                                this.socket.emit("user_prop");
              });
            },
                    login(){
                        this.socket.on("login_response",(message)=>{
                            this.settings_info.orchard = message.orchard_settings;
                            this.settings_info.user=message.user_settings;
                            this.game.fsave({
                                "orchard_settings":message.orchard_settings,
                                "user_settings":message.user_settings,
                            });
                            this.game.save({
                                "tree_total":message.tree_total,
                                "user_tree_number":message.user_tree_number,
                                "user_tree_list":message.user_tree_list,
                                "tree_status":message.tree_status,
                                "pet_food_num":message.pet_food_num,
                                "fertilizer_num":message.fertilizer_num,
                                "waters":message.waters,
                                "shears":message.shears,
                            });
                            setTimeout(()=>{
                                // 通知种植园页面获取到了当前用户的种植信息
                                api.sendEvent({
                                    name: 'user_tree_data',
                                    extra: {}
                                });
                            },500);
                        });
                    },
                    user_package(){
                        // 用户背包道具列表
                        this.socket.on("user_prop_response",(message)=>{
                            this.game.fsave({
                                "user_package":message.data,
                            });
                            // 界面中的道具信息
                            this.game.save({
                                "pet_food_num":message.pet_food_num,
                                "fertilizer_num":message.fertilizer_num,
                            });
                            api.sendEvent({
                                name: 'update_prop_data',
                                extra: {
    
                                }
                            });
    
                        })
                    },
            go_index(){
              this.game.goWin("root");
            },
            go_friends(){
              this.game.goFrame("friends","friends.html",this.current);
              this.game.goFrame("friend_list","friend_list.html",this.current,{
                  x: 0,
                  y: 190,
                  w: 'auto',
                  h: 'auto',
              },null,true);
            },
            go_home(){
              this.game.goWin("user","user.html", this.current);
            },
                    go_orchard_shop(){
                        // 种植园商店
                        this.game.goFrame("orchard_shop","shop.html", this.current,null,{
                  type:"push",
                  subType:"from_top",
                  duration:300
              });
                    },
                    go_my_package(){
                        // 我的背包
                        this.game.goFrame("package","package.html", this.current,null,{
                  type:"push",
                  subType:"from_top",
                  duration:300
              });
                    },
                    buy_prop(){
                        api.addEventListener({
                                name: 'buy_prop'
                        }, (ret, err)=>{
                                if( ret ){
                                    // 用户购买道具
                                    this.socket.emit("user_buy_prop",ret.value);
                                }
                        });
                        this.socket.on("user_buy_prop_response",(message)=>{
                            alert(message.errmsg);
    
                        })
                    },
                    use_prop(){
                        // 使用道具
                        var pid = 0;
                        api.addEventListener({
                                name: 'use_prop'
                        }, (ret, err)=>{
                                if( ret ){
                                    // 用户使用道具
                                    pid = ret.value.pid;
                                    this.socket.emit("use_prop",ret.value.pid);
                                }
                        });
    
                        this.socket.on("prop_use_response",(message)=>{
                            if(parseInt(message.errno) === 1000){
                                api.sendEvent({
                                        name: 'prop_use_success',
                                        extra: {
                                            pid: pid
                                        }
                                });
                            }else{
                                api.alert({
                                        title: '提示',
                                        msg: message.errmsg,
                                });
    
                            }
                        });
                    },
                    unlock_package_number(){
                        api.addEventListener({
                                name: 'unlock_package_number'
                        }, (ret, err)=>{
                                if( ret ){
                                    // 用户购买道具
                                    this.socket.emit("unlock_package");
                                }
                        });
    
                        this.socket.on("unlock_package_response",(message)=>{
                            if(parseInt(message.errno) === 1000){
                                api.sendEvent({
                                    name: 'unlock_package_success',
                                    extra: {
                                    }
                                });
                            }else{
                                api.alert({
                                        title: '提示',
                                        msg: message.errmsg,
                                });
    
                            }
    
                        })
                    },
                    show_pet(){
                        // 显示宠物
                        this.socket.emit("pet_show");
                        this.socket.on("pet_show_response",(message)=>{
                            if(parseInt(message.errno) === 1000){
                                // 把宠物信息保存到本地
                                this.game.save({"pet_list":message.pet_list})
                                this.game.save({"pet_number":message.pet_number})
                                setTimeout(()=>{
                                    api.sendEvent({
                                            name: 'pet_show_success',
                                            extra: {}
                                    });
                                },500);
                            }else{
                                api.alert({
                                        title: '提示',
                                        msg: message.errmsg,
                                });
                            }
    
                        })
                    },
                    active_tree(){
                        // 激活树桩
                        api.addEventListener({
                            name: 'active_tree'
                        }, (ret, err)=>{
                            if( ret ){
                                    this.socket.emit("active_tree");
                            }
                        });
    
                        this.socket.on("active_tree_response",(message)=>{
                            if(parseInt(message.errno) === 1000){
                                // 更新数据到本地
                                api.sendEvent({
                                        name: 'active_tree_success',
                                        extra: {}
                                });
                            }else{
                                api.alert({
                                        title: '提示',
                                        msg: message.errmsg,
                                });
                            }
    
                        })
                    }
                }
            });
        }
        </script>
    </body>
    </html>
    前端显示剪刀和浇水的次数:orchard.html
    <!DOCTYPE html>
    <html>
    <head>
        <title>用户中心</title>
        <meta name="viewport" content="width=device-width,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no">
        <meta charset="utf-8">
        <link rel="stylesheet" href="../static/css/main.css">
        <script src="../static/js/vue.js"></script>
        <script src="../static/js/axios.js"></script>
        <script src="../static/js/main.js"></script>
        <script src="../static/js/uuid.js"></script>
        <script src="../static/js/settings.js"></script>
        <script src="../static/js/socket.io.js"></script>
    </head>
    <body>
        <div class="app orchard orchard-frame" id="app">
        <div class="background">
          <img class="grassland2" src="../static/images/grassland2.png" alt="">
          <img class="mushroom1" src="../static/images/mushroom1.png" alt="">
          <img class="stake1" src="../static/images/stake1.png" alt="">
          <img class="stake2" src="../static/images/stake2.png" alt="">
        </div>
        <div class="pet-box">
          <div class="pet">
                    <img v-if="pet_list.length > 0" class="pet-item" :src="settings.static_url+pet_list[0].image" alt="">
          </div>
                <div class="pet" v-if="pet_number > 1">
                    <img v-if="pet_list.length>1" class="pet-item" :src="settings.static_url+pet_list[1].image" alt="">
          </div>
          <div class="pet turned_off" v-if="pet_number==1">
            <img class="turned_image" src="../static/images/turned_off.png" alt="">
            <p>请购买宠物</p>
          </div>
        </div>
        <div class="tree-list">
          <div class="tree-box">
                    <div class="tree" v-for="tree in user_tree_data.user_tree_list">
                        <img :src="tree_img(tree.status)" alt="">
                    </div>
                    <!-- 已激活但是未种植的树桩列表 -->
                    <div class="tree" v-for="i in active_tree">
                        <img src="../static/images/tree1.png" alt="">
                    </div>
                    <!-- 未激活树桩列表 -->
                    <div @click="unlock_tree" class="tree" v-for="i in lock_tree">
                        <img src="../static/images/tree0.png" alt="">
                    </div>
          </div>
    
        </div>
        <div class="prop-list">
          <div class="prop">
            <img src="../static/images/prop1.png" alt="">
            <span>{{fertilizer_num}}</span>
            <p>化肥</p>
          </div>
          <div class="prop">
            <img src="../static/images/prop2.png" alt="">
            <span>{{shears}}</span>
            <p>修剪</p>
          </div>
          <div class="prop">
            <img src="../static/images/prop3.png" alt="">
            <span>{{waters}}</span>
            <p>浇水</p>
          </div>
          <div class="prop">
            <img src="../static/images/prop4.png" alt="">
            <span>{{pet_food_num}}</span>
            <p>宠物粮</p>
          </div>
        </div>
        <div class="pet-hp-list">
          <div class="pet-hp" v-for="pet in pet_list">
            <p>宠物1 饱食度</p>
            <div class="hp">
              <div :style="{ pet.hp+'%'}" class="process">{{pet.hp}}%</div>
            </div>
          </div>
        </div>
        </div>
        <script>
        apiready = function(){
            init();
            new Vue({
                el:"#app",
                data(){
                    return {
              namespace: '/mofang',
              token:"",
              socket: null,
                        pet_food_num:0, // 宠物粮数量
                        fertilizer_num:0,  // 化肥数量
                        waters:0,  // 浇水次数
                        shears:0, // 剪刀次数
                        pet_list:[],
                        user_tree_data:{
                            "total_tree": 9,       // 总树桩数量
                            "user_tree_number": 0, // 当前用户激活树桩数量
                            "user_tree_list":[     // 当前种植的树桩列表状态
    
                            ],
                        },
                        tree_status:{
    
                        },
                        // user_tree_data:{
                        //     "total_tree":9,        // 总树桩数量
                        //     "user_tree_number": 5, // 当前用户激活树桩数量
                        //     "user_tree_list":[     // 当前种植的树桩列表状态
                        //             { // 树桩状态
                        //                  "time":1609808084, // 种植时间
                        //                  "status":4,        // 植物状态
                        //                  "has_time": 300,   // 状态时间
                        //             },
                        //     ],
                        // },
                        // tree_status:{
                        //     "tree_status_0": "tree0.png", // 树桩
                        //     "tree_status_1": "tree1.png", // 空桩
                        //     "tree_status_2": "tree2.png", // 幼苗
                        //     "tree_status_3": "tree3.png", // 成长
                        //     "tree_status_4": "tree4.png", // 成熟
                        // },
                        pet_number:[],
              timeout: 0,
                        prev:{name:"",url:"",params:{}},
                        current:{name:"orchard",url:"orchard.html",params:{}},
                    }
                },
                computed:{
                    // 已激活但是未使用的树桩
                    active_tree(){
                        return parseInt(this.user_tree_data.user_tree_number - this.user_tree_data.user_tree_list.length);
                    },
                    // 未激活的剩余树桩
                    lock_tree(){
                        return parseInt( this.user_tree_data.total_tree-this.user_tree_data.user_tree_number);
                    },
                },
          created(){
                    this.show_pet_list();
                    this.show_tree_list();
                    this.get_prop_list();
          },
                methods:{
                    get_prop_list(){
                        // 更新道具列表信息
                        api.addEventListener({
                            name: 'update_prop_data'
                        }, (ret, err)=>{
                            if( ret ){
                                    this.pet_food_num = this.game.get("pet_food_num");
                                    this.fertilizer_num = this.game.get("fertilizer_num");
                            }
                        });
    
                    },
                    tree_img(status){
                        return '../static/images/'+this.tree_status[status];
                    },
                    show_pet_list(){
                        api.addEventListener({
                                name: 'pet_show_success'
                        }, (ret, err)=>{
                                if( ret ){
                                    // 用户购买道具
                                    this.pet_list = this.game.get("pet_list");
                                    this.pet_number = parseInt(this.game.get("pet_number"));
                                }
                        });
                    },
                    show_tree_list(){
                        api.addEventListener({
                                name: 'user_tree_data'
                        }, (ret, err)=>{
                                if( ret ){
                                    // 用户种植植物信息
                                    this.user_tree_data.tree_total = parseInt(this.game.get("tree_total"));
                                    this.user_tree_data.user_tree_number = parseInt(this.game.get("user_tree_number"));
                                    this.user_tree_data.user_tree_list = this.game.get("user_tree_list");
                                    this.tree_status = this.game.get("tree_status");
                                    this.pet_food_num = this.game.get("pet_food_num");
                                    this.fertilizer_num = this.game.get("fertilizer_num");
                                    this.waters = this.game.get("waters");
                                    this.shears = this.game.get("shears");
                                }
                        });
                    },
                    unlock_tree(){
                        // 激活树桩通知
                        api.confirm({
                            title: '提示',
                            msg: '是否激活树桩?',
                            buttons: ['确定', '取消']
                        }, (ret, err)=>{
                            if( ret.buttonIndex == 1 ){
                                    api.sendEvent({
                                        name: 'active_tree',
                                        extra: {
    
                                            }
                                    });
                            }
                        });
    
                        // 激活成功!
                        api.addEventListener({
                            name: 'active_tree_success'
                        }, (ret, err)=>{
                            if( ret ){
                                 // 新增树桩的数量
                                         this.user_tree_data.user_tree_number+=1;
                                        this.game.save({"user_tree_number": this.user_tree_data.user_tree_number});
                            }
                        });
                    }
                }
            });
        }
        </script>
    </body>
    </html>
    前端显示剪刀和浇水的次数:my_orchard.html
  • 相关阅读:
    jQuery CVE-2019-11358原型污染漏洞分析和修复建议
    IIS中配置访问HTTPS
    如何把网址配置为http和https可以同时访问
    C#获取一周的工作日显示(星期几)
    Oracle查询数据库中所有表的记录数
    发布WebApi项目时,提示未包含binyourDocumentationFile.xml文档文件
    IntelliJ Idea 配置Tomcat提示Port is not specified
    C# 属性(Property)和字段(Field)的区别
    IDEA 出现错误:找不到或无法加载主类
    C# Enum 类型遍历
  • 原文地址:https://www.cnblogs.com/libolun/p/14260834.html
Copyright © 2011-2022 走看看