Flask构建微信小程序①-架构

第7章 管理员后台账号模块开发

7-1 管理员模块功能介绍-管理员数据表

7-4 登录退出(2)

在这里插入图片描述

替换render_tamplate() 函数,无需每次将current_user的值添加

#  统一渲染
def ops_render(template, context={}):
    if 'current_user' in g:
        context['current_user'] = g.current_user
    return render_template(template, **context)

7-5 编辑和修改登录人信息

编辑

edit.js

;
var user_edit_ops = {
    init: function () {
        this.eventBind();
    },
    eventBind: function () {
        $(".user_edit_wrap .save").click(function () {
            var btn_target = $(this);
            if (btn_target.hasClass("disabled")) {
                common_ops.alert("正在处理,请不要重复提交!");
                return
            }
            var nickname_target = $(".user_edit_wrap input[name=nickname]");
            var nickname = nickname_target.val();

            var email_target = $(".user_edit_wrap input[name=email]");
            var email = email_target.val();

            if (!nickname || nickname.length < 2) {
                common_ops.tip("请输入符合规范的姓名!", nickname_target);
                return
            }
            if (!email || email.length < 2) {
                common_ops.tip("请输入符合规范的邮箱!", email_target);
                return
            }

            btn_target.addClass("disabled");

            var data = {
                nickname: nickname,
                email: email
            };
            $.ajax({
                url: common_ops.buildUrl("/user/edit"),
                type: 'POST',
                data: data,
                dataType: 'json',
                success: function (res) {
                    btn_target.removeClass("disabled");
                    var callback = null;
                    if (res.code == 200) {
                        callback = function () {
                            window.location.href = window.location.href;
                        }
                    }
                    common_ops.alert(res.msg, callback)
                }
            })

        })
    }
};

$(document).ready(function () {
    user_edit_ops.init();
});
@route_user.route("/edit", methods=["GET", "POST"])
def edit():
    if request.method == "GET":
        return ops_render("user/edit.html", {"current": "edit"})

    resp = {'code': 200, 'msg': "操作成功!", 'data': {}}

    req = request.values
    nickname = req['nickname'] if 'nickname' in req else ''
    email = req['email'] if 'email' in req else ''

    if nickname is None or len(nickname) < 1:
        resp['code'] = -1
        resp['msg'] = '请输入符合规范的姓名!'
        return jsonify(resp)

    if email is None or len(email) < 1:
        resp['code'] = -1
        resp['msg'] = '请输入符合规范的邮箱!'
        return jsonify(resp)

    user_info = g.current_user
    user_info.nickname = nickname
    user_info.email = email

    db.session.add(user_info)
    db.session.commit()

    return jsonify(resp)

将相同的地方分离,可点击切换页面, 根基后端传递的current 来选择 前端高亮显示

<div class="row  border-bottom">
    <div class="col-lg-12">
        <div class="tab_title">
            <ul class="nav nav-pills">
                <li {% if current == "edit" %}class="current"{% endif %}>
                    <a href="{{ build_url('/user/edit') }}">信息编辑</a>
                </li>
                <li {% if current == "reset-pwd" %}class="current"{% endif %}>
                    <a href="{{ build_url('/user/reset-pwd') }}">修改密码</a>
                </li>
            </ul>
        </div>
    </div>
</div>
{% extends "common/layout_main.html" %}
{% block content %}
{% include "common/tab_user.html" %}
<div class="row m-t  user_edit_wrap">
    <div class="col-lg-12">
        <h2 class="text-center">账号信息编辑</h2>
        <div class="form-horizontal m-t m-b">
            <div class="form-group">
                <label class="col-lg-2 control-label">手机:</label>
                <div class="col-lg-10">
                    <input type="text" name="mobile" class="form-control" placeholder="请输入手机~~" readonly=""
                           value="{{ current_user.mobile }}">
                </div>
            </div>
            <div class="hr-line-dashed"></div>

            <div class="form-group">
                <label class="col-lg-2 control-label">姓名:</label>
                <div class="col-lg-10">
                    <input type="text" name="nickname" class="form-control" placeholder="请输入姓名~~" value="{{ current_user.nickname }}">
                </div>
            </div>
            <div class="hr-line-dashed"></div>

            <div class="form-group">
                <label class="col-lg-2 control-label">邮箱:</label>
                <div class="col-lg-10">
                    <input type="text" name="email" class="form-control" placeholder="请输入邮箱~~" value="{{ current_user.email }}">
                </div>
            </div>
            <div class="hr-line-dashed"></div>
            <div class="form-group">
                <div class="col-lg-4 col-lg-offset-2">
                    <button class="btn btn-w-m btn-outline btn-primary save">保存</button>
                </div>
            </div>
        </div>
    </div>
</div>
{% endblock %}

{% block js %}
    <script src="{{ static_url('/js/user/edit.js') }}"></script>
{% endblock %}

修改密码

@route_user.route("/reset-pwd", methods=["GET", "POST"])
def reset_pwd():
    if request.method == "GET":
        return ops_render("user/reset_pwd.html", {"current": "reset-pwd"})

    resp = {'code': 200, 'msg': "操作成功!", 'data': {}}

    req = request.values
    old_password = req['old_password'] if 'old_password' in req else ''
    new_password = req['new_password'] if 'new_password' in req else ''

    if old_password is None or len(old_password) < 6:
        resp['code'] = -1
        resp['msg'] = '请输入符合规范的原密码!'
        return jsonify(resp)

    if new_password is None or len(new_password) < 6:
        resp['code'] = -1
        resp['msg'] = '请输入符合规范的新密码!'
        return jsonify(resp)

    if old_password == new_password:
        resp['code'] = -1
        resp['msg'] = '请重新输入一个吧,新密码和原密码不能相同!'
        return jsonify(resp)

    user_info = g.current_user
    user_info.login_pwd = UserService.gene_pwd(new_password, user_info.login_salt)  #密码加密
    db.session.add(user_info)
    db.session.commit()

    # 修改密码之后刷新cookie保持登陆状态
    # response = make_response(json.dumps(resp))
    # response.set_cookie(app.config['AUTH_COOKIE_NAME'],
    #                     "%s#%s" % (UserService.gene_auth_code(user_info), user_info.uid), 60 * 60 * 24 * 120) # 保存120天
    #
    # return response

    return jsonify(resp)

7-6 账号管理_列表和详情展示

后端逻辑

@route_account.route("/index")
def index():
    resp_data = {}
    req = request.values
    page = int(req['p']) if ('p' in req and req['p']) else 1
    query = User.query

    # 混合查询关键字‘mix_kw
    if 'mix_kw' in req:
        rule = or_(User.nickname.ilike("%{0}%".format(req['mix_kw'])), User.mobile.ilike("%{0}%".format(req['mix_kw'])))
        query = query.filter(rule)

    if 'status' in req and int(req['status']) > -1:
        query = query.filter(User.status == int(req['status']))

    page_params = {
        'total': query.count(),  #  总数量
        'page_size': app.config['PAGE_SIZE'],
        'page': page,  # 前端传递的页数,默认为1
        'display': app.config['PAGE_DISPLAY'],  # 展示的页数
        'url': request.full_path.replace('&p={}'.format(page), "")
    }
    pages = i_pagination(page_params)
    offset = (page - 1) * app.config['PAGE_SIZE']
    limit = page * app.config['PAGE_SIZE']

    list_ = query.order_by(User.uid.desc()).all()[offset:limit]

    resp_data['list'] = list_
    resp_data['pages'] = pages
    resp_data['search_con'] = req
    resp_data['status_mapping'] = app.config['STATUS_MAPPING']
    return ops_render("account/index.html", resp_data)

自定义分页类

# 自定义分页类
def i_pagination(params):
    import math

    ret = {
        "is_prev": 1,
        "is_next": 1,
        "from": 0,
        "end": 0,
        "current": 0,
        "total_pages": 0,
        "page_size": 0,
        "total": 0,
        "url": params['url']
    }

    total = int(params['total'])
    page_size = int(params['page_size'])
    page = int(params['page'])
    display = int(params['display'])
    total_pages = int(math.ceil(total / page_size))
    total_pages = total_pages if total_pages > 0 else 1
    if page <= 1:
        ret['is_prev'] = 0

    if page >= total_pages:
        ret['is_next'] = 0

    semi = int(math.ceil(display / 2))

    if page - semi > 0:
        ret['from'] = page - semi
    else:
        ret['from'] = 1

    if page + semi <= total_pages:
        ret['end'] = page + semi
    else:
        ret['end'] = total_pages

    ret['current'] = page
    ret['total_pages'] = total_pages
    ret['page_size'] = page_size
    ret['total'] = total
    ret['range'] = range(ret['from'], ret['end'] + 1)
    return ret

分页pagenation.html

<div class="row">
    <div class="col-lg-12">
        <span class="pagination_count" style="line-height: 40px;">共{{ pages.total }}条记录 | 每页{{pages.page_size}}条</span>
        <ul class="pagination pagination-lg   pull-right" style="margin: 0 0 ;">
            {% if pages.is_prev == 1 %}
             <li>
                 <a href="{{ pages.url }}&p=1" ><span>首页</span></a>
             </li>
            {%  endif %}

            {% for idx in pages.range %}
                {% if idx == pages.current %}
                    <li class="active"><a href="javascript:void(0);">{{ idx }}</a></li>
                {% else %}
                    <li><a href="{{ pages.url }}&p={{idx}}">{{ idx }}</a></li>
                {% endif %}
            {% endfor %}
            {% if pages.is_next == 1 %}
             <li>
                    <a href="{{ pages.url }}&p={{ pages.total_pages }}" ><span>尾页</span></a>
             </li>
            {%  endif %}
        </ul>
    </div>
</div>

7-7 账号管理:添加和编辑账号

后端逻辑

@route_account.route("/set", methods=['GET', 'POST'])
def set_():
    default_pwd = '******'
    if request.method == 'GET':
        resp_data = {}
        req = request.args
        uid = int(req.get('id', 0))
        user_info = None
        if uid:
            user_info = User.query.filter_by(uid=uid).first()
        resp_data['user_info'] = user_info
        return ops_render("account/set.html", resp_data)

    resp = {'code': 200, 'msg': '操作成功!', 'data': {}}
    req = request.values

    # 获取参数
    id = req['id'] if 'id' in req else 0
    nickname = req['nickname'] if 'nickname' in req else ''
    mobile = req['mobile'] if 'mobile' in req else ''
    email = req['email'] if 'email' in req else ''
    login_name = req['login_name'] if 'login_name' in req else ''
    login_pwd = req['login_pwd'] if 'login_pwd' in req else ''

    if nickname is None or len(nickname) < 1:
        resp['code'] = -1
        resp['msg'] = "请输入符合规范的姓名!"
        return jsonify(resp)

    if mobile is None or len(mobile) < 1:
        resp['code'] = -1
        resp['msg'] = "请输入符合规范的手机号码!"
        return jsonify(resp)

    if email is None or len(email) < 1:
        resp['code'] = -1
        resp['msg'] = "请输入符合规范的邮箱!"
        return jsonify(resp)

    if login_name is None or len(login_name) < 1:
        resp['code'] = -1
        resp['msg'] = "请输入符合规范的登陆用户名!"
        return jsonify(resp)

    if login_pwd is None or len(login_pwd) < 6:
        resp['code'] = -1
        resp['msg'] = "请输入符合规范的登陆密码!"
        return jsonify(resp)

    if nickname is None or len(nickname) < 1:
        resp['code'] = -1
        resp['msg'] = "请输入符合规范的姓名!"
        return jsonify(resp)

    has_in = User.query.filter(User.login_name == login_name, User.uid != id).first()
    if has_in:
        resp['code'] = -1
        resp['msg'] = "该登录名已经存在,请换一个试试!"
        return jsonify(resp)

    user_info = User.query.filter_by(uid=id).first()
    # 编辑
    if user_info:
        model_user = user_info
    # 新增
    else:
        model_user = User()
        model_user.created_time = getCurrentDate()
        model_user.login_salt = UserService.gene_salt()

    model_user.nickname = nickname
    model_user.mobile = mobile
    model_user.email = email
    model_user.login_name = login_name
    if login_pwd != default_pwd:
        model_user.login_pwd = UserService.gene_pwd(login_pwd, model_user.login_salt)
    model_user.updated_time = getCurrentDate()

    db.session.add(model_user)
    db.session.commit()
    return jsonify(resp)

生成16位的加密信息

@staticmethod
def gene_salt(length=16):
    keys = [random.choice((string.ascii_letters + string.digits)) for _ in range(length)]
    return ("".join(keys))

7-8 账号管理:搜索和分页

分页js

;
var account_index_ops = {
    init: function () {
        this.eventBind();
    },
    eventBind: function () {
        var that = this;
        $(".wrap_search .search").click(function () {
            $(".wrap_search").submit();
        });

        $(".remove").click(function () {
            that.ops("remove", $(this).attr("data"));
        });

        $(".recover").click(function () {
            that.ops("recover", $(this).attr("data"));
        });
    },
    ops: function (act, id) {
        var callback = {
            'ok': function () {
                $.ajax({
                    url: common_ops.buildUrl("/account/ops"),
                    type: 'POST',
                    data: {
                        act: act,
                        id: id
                    },
                    dataType: 'json',
                    success: function (res) {
                        var callback = null;
                        if (res.code == 200) {
                            callback = function () {
                                window.location.href = window.location.href;
                            }
                        }
                        common_ops.alert(res.msg, callback);
                    }
                })

            },
            'cancel': null
        };
        common_ops.confirm((act == "remove" ? "确定删除吗?" : "确定恢复吗?"), callback);
    }
};


$(document).ready(function () {
    account_index_ops.init()
});

混合搜索

# 混合查询关键字‘mix_kw
    if 'mix_kw' in req:
        rule = or_(User.nickname.ilike("%{0}%".format(req['mix_kw'])), User.mobile.ilike("%{0}%".format(req['mix_kw'])))
        query = query.filter(rule)

    if 'status' in req and int(req['status']) > -1:
        query = query.filter(User.status == int(req['status']))

json在html中遍历

{% for tmp_key in status_mapping %}
<option value="{{ tmp_key }}">{{ status_mapping[tmp_key] }}</option>
{% endfor %}

7-9 账号管理:删除和恢复

 <table class="table table-bordered m-t">
                <thead>
                <tr>
                    <th>序号</th>
                    <th>姓名</th>
                    <th>手机</th>
                    <th>邮箱</th>
                    <th>操作</th>
                </tr>
                </thead>
                <tbody>
                {% if list %}
                    {% for item in list %}
                        <tr>
                            <td>{{ item.uid }}</td>
                            <td>{{ item.nickname }}</td>
                            <td>{{ item.mobile }}</td>
                            <td>{{ item.email }}</td>
                            <td>
                                <a href="{{ build_url('/account/info') }}?id={{ item.uid }}">
                                    <i class="fa fa-eye fa-lg"></i>
                                </a>
                                {% if item.status == 1 %}
                                    <a class="m-l" href="{{ build_url('/account/set') }}?id={{ item.uid }}">
                                        <i class="fa fa-edit fa-lg"></i>
                                    </a>

                                    <a class="m-l remove" href="javascript:void(0);" data="{{ item.uid }}">
                                        <i class="fa fa-trash fa-lg"></i>
                                    </a>

                                {% else %}
                                    <a class="m-l recover" href="javascript:void(0);" data="{{ item.uid }}">
                                        <i class="fa fa-rotate-left fa-lg"></i>
                                    </a>
                                {% endif %}
                            </td>
                        </tr>
                    {% endfor %}
                {% else %}
                    <tr>
                        <td colspan="5">暂无数据</td>
                    </tr>
                {% endif %}
                </tbody>
            </table>

搜索、移除、恢复用户

var account_index_ops = {
    init: function () {
        this.eventBind();
    },
    eventBind: function () {
        var that = this;  // 作用域变化
        $(".wrap_search .search").click(function () {
            $(".wrap_search").submit();
        });

        $(".remove").click(function () {
            that.ops("remove", $(this).attr("data")); // JQuery获取属性用attr
        });

        $(".recover").click(function () {
            that.ops("recover", $(this).attr("data"));
        });
    },
    ops: function (act, id) {
        var callback = {
            'ok': function () {
                $.ajax({
                    url: common_ops.buildUrl("/account/ops"),
                    type: 'POST',
                    data: {
                        act: act,
                        id: id
                    },
                    dataType: 'json',
                    success: function (res) {
                        var callback = null;
                        if (res.code == 200) {
                            callback = function () {
                                window.location.href = window.location.href;
                            }
                        }
                        common_ops.alert(res.msg, callback);
                    }
                })

            },
            'cancel': null
        };
        common_ops.confirm((act == "remove" ? "确定删除吗?" : "确定恢复吗?"), callback);
    }
};

后端

@route_account.route("/ops", methods=["POST"])
def ops():
    resp = {'code': 200, 'msg': '操作成功!', 'data': {}}
    req = request.values

    id = req['id'] if 'id' in req else 0
    act = req['act'] if 'act' in req else ''

    if not id:
        resp['code'] = -1
        resp['msg'] = "请选择要操作的账号!"
        return jsonify(resp)

    if act not in ['remove', 'recover']:
        resp['code'] = -1
        resp['msg'] = "操作有误,请重试!"
        return jsonify(resp)

    user_info = User.query.filter_by(uid=id).first()
    if not user_info:
        resp['code'] = -1
        resp['msg'] = "指定账号不存在!"
        return jsonify(resp)

    if act == 'remove':
        user_info.status = 0
    elif act == 'recover':
        user_info.status = 1

    user_info.update_time = getCurrentDate()
    db.session.add(user_info)
    db.session.commit()

    return jsonify(resp)

7-10 登录和版本号优化、访问记录、错误记录和总结

登录判断账户状态

    if user_info.status != 1:
        resp['code'] = -1
        resp['msg'] = "账号已被禁用,请联系管理员!"
        return jsonify(resp)

在登陆拦截器中判断登录状态

def check_login():
    """
    判断用户是否已经登陆
    :return:
    """
    cookies = request.cookies
    auth_cookie = cookies[app.config['AUTH_COOKIE_NAME']] if app.config['AUTH_COOKIE_NAME'] in cookies else None
    if auth_cookie is None:
        return False

    auth_info = auth_cookie.split("#")  # 授权码与id
    if len(auth_info) != 2:
        return False
    try:
        user_info = User.query.filter_by(uid=auth_info[1]).first()  # 查询是否有此用户id
    except Exception:
        return False
    if user_info is None:
        return False

    if auth_info[0] != UserService.gene_auth_code(user_info):  # 通过user信息生成授权码
        return False

    if user_info.status != 1:
        return False

    return user_info

js加载缓存的问题-静态页面版本号

通过解决js加载缓存的问题

 @classmethod
    def static_url(cls, path):
        release_version = app.config.get('RELEASE_VERSION')
        ver = "%s" % int(time.time()) if not release_version else release_version
        path = '/static' + path + "?ver=" + ver
        return cls.build_url(path)

原生sql生成flask模型表

在这里插入图片描述

访问记录与访问错误日志

import json

from flask import request, g

from application import db
from common.libs.Helper import getCurrentDate
from common.models.log.AppAccessLog import AppAccessLog
from common.models.log.AppErrorLog import AppErrorLog


class LogService():
    @staticmethod
    def addAccessLog():
        target = AppAccessLog()
        target.target_url = request.url
        target.referer_url = request.referrer
        target.ip = request.remote_addr
        target.query_params = json.dumps(request.values.to_dict())
        if 'current_user' in g and g.current_user is not None:
            target.uid = g.current_user.uid
        target.ua = request.headers.get("User-Agent")
        target.created_time = getCurrentDate()
        db.session.add(target)
        db.session.commit()
        return True

    @staticmethod
    def addErrorLog(content):
        target = AppErrorLog()
        target.target_url = request.url
        target.referer_url = request.referrer
        target.query_params = json.dumps(request.values.to_dict())
        target.content = content
        target.created_time = getCurrentDate()
        db.session.add(target)
        db.session.commit()
        return True

8. 微信小程序登录

微信前端页面

当js加载的未注册时,button为授权登录,有注册信息为去订餐,此时可以直接使用goToIndex 去跳转到index页面,否则点击使用login的js,实现的用户的信息保存与跳转

 <view class="bd">
                <image class="smalltitle" src="/images/more/confirm-word1.png"></image>

                <view class="confirm-btn" bindtap='goToIndex' wx:if="{{ regFlag==true }}">
                    <text>走吧,订餐去</text>
                </view>

                <button class="confirm-btn" open-type="getUserInfo" bindgetuserinfo="login"
                        wx:if="{{ regFlag==false }}">授权登陆
                </button>

                <text class="copyright">@王大阳 Github:why19970628</text>
            </view>

前端js

//login.js
//获取应用实例
var app = getApp();
Page({
    data: {
        remind: '加载中',
        angle: 0,
        userInfo: {},
        regFlag: true
    },
    goToIndex: function () {
        wx.switchTab({
            url: '/pages/food/index',
        });
    },
    onLoad: function () {
        wx.setNavigationBarTitle({
            title: app.globalData.shopName
        });
        this.checkLogin();
    },
    onShow: function () {

    },
    onReady: function () {
        var that = this;
        setTimeout(function () {
            that.setData({
                remind: ''
            });
        }, 1000);
        wx.onAccelerometerChange(function (res) {
            var angle = -(res.x * 30).toFixed(1);
            if (angle > 14) {
                angle = 14;
            } else if (angle < -14) {
                angle = -14;
            }
            if (that.data.angle !== angle) {
                that.setData({
                    angle: angle
                });
            }
        });
    },
    checkLogin: function () {
        var that = this;
        wx.login({
            success: function (res) {
                if (!res.code) {
                    app.alert({'content': '登陆失败,请再次点击!'});
                    return;
                }
                wx.request({
                    url: app.buildUrl('/member/check-reg'),
                    header: app.getRequestHeader(),
                    method: 'POST',
                    data: {code: res.code},
                    success: function (res) {
                        if (res.data.code != 200) {
                            that.setData({
                                regFlag: false
                            });
                            return;
                        }
                        app.setCache("token", res.data.data.token);
                        // that.goToIndex()
                    }
                })
            }
        });
    },
    login: function (e) {
        var that = this;
        if (!e.detail.userInfo) {
            app.alert({'content': '登陆失败,请再次点击!'});
            return;
        }

        var data = e.detail.userInfo;
        wx.login({
            success: function (res) {
                if (!res.code) {
                    app.alert({'content': '登陆失败,请再次点击!'});
                    return;
                }
                data['code'] = res.code;
                wx.request({
                    url: app.buildUrl('/member/login'),
                    header: app.getRequestHeader(),
                    method: 'POST',
                    data: data,
                    success: function (res) {
                        if (res.data.code != 200) {
                            app.alert({'content': res.data.msg});
                            return
                        }
                        app.setCache("token", res.data.data.token);
                        that.goToIndex()
                    }
                });
            }
        });

    }
});

登录与检验是否注册

@route_api.route("member/login", methods=['GET', 'POST'])
def login():
    resp = {'code': 200, 'msg': '操作成功', 'data': {}}
    req = request.values
    code = req['code'] if 'code' in req else ''
    if not code or len(code) < 1:
        resp['code'] = -1
        resp['msg'] = "需要code"
        return jsonify(resp)

    openid = MemberService.getWeChatOpenId(code)
    if openid is None:
        resp['code'] = -1
        resp['msg'] = "调用微信出错"
        return jsonify(resp)

    nickname = req['nickName'] if 'nickName' in req else ''
    sex = req['gender'] if 'gender' in req else 0
    avatar = req['avatarUrl'] if 'avatarUrl' in req else ''
    print(req)
    # 判断是否已经测试过,注册了直接返回一些信息
    bind_info = OauthMemberBind.query.filter_by(openid=openid, type=1).first()
    if not bind_info:
        model_member = Member()
        model_member.nickname = nickname
        model_member.sex = sex
        model_member.avatar = avatar
        model_member.reg_ip = request.remote_addr
        model_member.salt = MemberService.gene_salt()
        model_member.updated_time = model_member.created_time = getCurrentDate()
        db.session.add(model_member)
        db.session.commit()

        model_bind = OauthMemberBind()
        model_bind.member_id = model_member.id
        model_bind.type = 1
        model_bind.openid = openid
        model_bind.extra = ''
        model_bind.updated_time = model_bind.created_time = getCurrentDate()
        db.session.add(model_bind)
        db.session.commit()

        bind_info = model_bind

    member_info = Member.query.filter_by(id=bind_info.member_id).first()

    token = "%s#%s" % (MemberService.gene_auth_code(member_info), member_info.id)
    resp['data'] = {'token': token}

    return jsonify(resp)
@route_api.route("/member/check-reg", methods=["GET", "POST"])
def checkReg():
    resp = {'code': 200, 'msg': '操作成功', 'data': {}}
    req = request.values
    code = req['code'] if 'code' in req else ''
    if not code or len(code) < 1:
        resp['code'] = -1
        resp['msg'] = "需要code"
        return jsonify(resp)

    openid = MemberService.getWeChatOpenId(code)
    if openid is None:
        resp['code'] = -1
        resp['msg'] = "调用微信出错"
        return jsonify(resp)

    bind_info = OauthMemberBind.query.filter_by(openid=openid, type=1).first()
    if not bind_info:
        resp['code'] = -1
        resp['msg'] = "未绑定"
        return jsonify(resp)

    member_info = Member.query.filter_by(id=bind_info.member_id).first()
    if not member_info:
        resp['code'] = -1
        resp['msg'] = "未查询到绑定信息"
        return jsonify(resp)
    token = "%s#%s" % (MemberService.gene_auth_code(member_info), member_info.id)
    resp['data'] = {'token': token}
    return jsonify(resp)

登录注册help

根据code拿openid

    @staticmethod
    def getWeChatOpenId(code):
        url = 'https://api.weixin.qq.com/sns/jscode2session?appid={0}&secret={1}&js_code={2}&grant_type=authorization_code'.format(
            app.config['MINA_APP']['appid'], app.config['MINA_APP']['appkey'], code)
        res = requests.get(url).json()
        openid = None
        if 'openid' in res:
            openid = res['openid']
        return openid

地址拼接 buildUrl

    buildUrl: function (path, params) {
        var url = "" + path;
        var _paramUrl = "";
        if (params) {
            // params = {
            //     a:"b",
            //     c:"d"
            // }
            // "a=b&c=d"
            _paramUrl = Object.keys(params).map(function (k) {
                return [encodeURIComponent(k), encodeURIComponent(params[k])].join("=");
            }).join("&");
            _paramUrl = "?" + _paramUrl;
        }
        return url + _paramUrl;
    },
   

微信小程序缓存

    getCache: function (key) {
        var value = undefined;
        try {
            value = wx.getStorageSync(key)
        } catch (e) {

        }
        return value
    },
    setCache: function (key, value) {
        wx.setStorage({
            key: key,
            data: value
        })

    }

第9章 后台会员管理模块开发

9-1 会员列表展示,分页,搜索和详情

会员列表后端逻辑

跟之前的账号管理方法类似

# -*- coding: utf-8 -*-
from flask import Blueprint, request, redirect, jsonify

from application import app, db
from common.libs.Helper import ops_render, i_pagination, getCurrentDate
from common.libs.UrlManager import UrlManager
from common.models.member.Member import Member

route_member = Blueprint('member_page', __name__)


@route_member.route("/index")
def index():
    resp_data = {}
    req = request.values
    page = int(req['p']) if ('p' in req and req['p']) else 1
    query = Member.query

    if 'mix_kw' in req:
        query = query.filter(Member.nickname.ilike("%{0}%".format(req['mix_kw'])))

    if 'status' in req and int(req['status']) > -1:
        query = query.filter(Member.status == int(req['status']))

    page_params = {
        'total': query.count(),
        'page_size': app.config['PAGE_SIZE'],
        'page': page,
        'display': app.config['PAGE_DISPLAY'],
        'url': request.full_path.replace('&p={}'.format(page), '')
    }
    pages = i_pagination(page_params)
    offset = (page - 1) * app.config['PAGE_SIZE']
    list = query.order_by(Member.id.desc()).offset(offset).limit(app.config['PAGE_SIZE']).all()
    resp_data['list'] = list
    resp_data['pages'] = pages  # 分页参数
    resp_data['search_con'] = req
    resp_data['status_mapping'] = app.config['STATUS_MAPPING']
    resp_data['current'] = 'index'
    return ops_render("member/index.html", resp_data)

自定义分页类

# 自定义分页类
def i_pagination(params):
    import math

    ret = {
        "is_prev": 1,
        "is_next": 1,
        "from": 0,
        "end": 0,
        "current": 0,
        "total_pages": 0,
        "page_size": 0,
        "total": 0,
        "url": params['url']
    }

    total = int(params['total'])
    page_size = int(params['page_size'])  # 每页数量
    page = int(params['page'])  # 当前第几页
    display = int(params['display'])
    total_pages = int(math.ceil(total / page_size))
    total_pages = total_pages if total_pages > 0 else 1
    if page <= 1:
        ret['is_prev'] = 0

    if page >= total_pages:
        ret['is_next'] = 0

    semi = int(math.ceil(display / 2))

    if page - semi > 0:
        ret['from'] = page - semi
    else:
        ret['from'] = 1

    if page + semi <= total_pages:
        ret['end'] = page + semi
    else:
        ret['end'] = total_pages

    ret['current'] = page
    ret['total_pages'] = total_pages
    ret['page_size'] = page_size
    ret['total'] = total
    ret['range'] = range(ret['from'], ret['end'] + 1)
    return ret

分页html

<div class="row">
    <div class="col-lg-12">
        <span class="pagination_count" style="line-height: 40px;">共{{ pages.total }}条记录 | 每页{{pages.page_size}}条</span>
        <ul class="pagination pagination-lg   pull-right" style="margin: 0 0 ;">
            {% if pages.is_prev == 1 %}
             <li>
                 <a href="{{ pages.url }}&p=1" ><span>首页</span></a>
             </li>
            {%  endif %}

            {% for idx in pages.range %}
                {% if idx == pages.current %}
                    <li class="active"><a href="javascript:void(0);">{{ idx }}</a></li>
                {% else %}
                    <li><a href="{{ pages.url }}&p={{idx}}">{{ idx }}</a></li>
                {% endif %}
            {% endfor %}
            {% if pages.is_next == 1 %}
             <li>
                    <a href="{{ pages.url }}&p={{ pages.total_pages }}" ><span>尾页</span></a>
             </li>
            {%  endif %}
        </ul>
    </div>
</div>

前端js
member/index.js

;
var member_index_ops = {
    init: function () {
        this.eventBind();
    },
    eventBind: function () {
        var that = this;
        $(".wrap_search .search").click(function () {
            $(".wrap_search").submit();
        });

        $(".remove").click(function () {
            that.ops("remove", $(this).attr("data"));
        });

        $(".recover").click(function () {
            that.ops("recover", $(this).attr("data"));
        });
    },
    ops: function (act, id) {
        var callback = {
            'ok': function () {
                $.ajax({
                    url: common_ops.buildUrl("/member/ops"),
                    type: 'POST',
                    data: {
                        act: act,
                        id: id
                    },
                    dataType: 'json',
                    success: function (res) {
                        var callback = null;
                        if (res.code == 200) {
                            callback = function () {
                                window.location.href = window.location.href;
                            }
                        }
                        common_ops.alert(res.msg, callback);
                    }
                })

            },
            'cancel': null
        };
        common_ops.confirm((act == "remove" ? "确定删除吗?" : "确定恢复吗?"), callback);
    }
};


$(document).ready(function () {
    member_index_ops.init()
});

会员详情

@route_member.route("/info")
def info():
    resp_data = {}
    req = request.args
    id = int(req.get('id', 0))
    reback_url = UrlManager.build_url("/member/index")

    if id < 1:
        return redirect(reback_url)
    info = Member.query.filter_by(id=id).first()
    if not info:
        return redirect(reback_url)
    resp_data['info'] = info
    resp_data['current'] = 'index'
    return ops_render("member/info.html", resp_data)

9-2 会员编辑

@route_member.route("/set", methods=['GET', 'POST'])
def set():
    if request.method == 'GET':
        resp_data = {}
        req = request.args
        id = int(req.get('id', 0))
        reback_url = UrlManager.build_url('/member/index')
        if id < 1:
            return redirect(reback_url)

        info = Member.query.filter_by(id=id).first()
        if not info:
            return redirect(reback_url)

        if info.status != 1:
            return redirect(reback_url)

        resp_data['info'] = info
        resp_data['current'] = 'index'
        return ops_render("member/set.html", resp_data)
    resp = {'code': 200, 'msg': '操作成功', 'data': {}}
    req = request.values
    id = req['id'] if 'id' in req else 0
    nickname = req['nickname'] if 'nickname' in req else ''

    if nickname is None or len(nickname) < 1:
        resp['code'] = -1
        resp['msg'] = '请输入规范的姓名!'
        return jsonify(resp)

    member_info = Member.query.filter_by(id=id).first()
    if not member_info:
        resp['code'] = -1
        resp['msg'] = '指定的会员不存在!'
        return jsonify(resp)

    member_info.nickname = nickname
    member_info.updated_time = getCurrentDate()
    db.session.add(member_info)
    db.session.commit()
    return jsonify(resp)

会员编辑js

;
var member_set_ops = {
    init: function () {
        this.eventBind();
    },
    eventBind: function () {
        $('.wrap_member_set .save').click(function () {
            var btn_target = $(this);
            if (btn_target.hasClass('disabled')) {
                common_ops.alert('正在处理,请不要重复提交!');
                return;
            }

            var nickname_taget = $('.wrap_member_set input[name=nickname]');
            var nickname = nickname_taget.val();

            if (nickname.length < 1) {
                common_ops.tip("请输入符合规范的姓名!", nickname_taget);
                return;
            }

            btn_target.addClass('disabled');

            var data = {
                nickname: nickname,
                id: $(".wrap_member_set input[name=id]").val()
            };

            $.ajax({
                url: common_ops.buildUrl('/member/set'),
                type: 'POST',
                data: data,
                dataType: 'json',
                success: function (res) {
                    btn_target.removeClass('disabled');
                    var callback = null;
                    if (res.code == 200) {
                        callback = function () {
                            window.location.href = common_ops.buildUrl('/member/index')
                        }
                    }
                    common_ops.alert(res.msg, callback)
                }
            })
        })
    }
};

$(document).ready(function () {
    member_set_ops.init()
});

9-3 会员的删除和恢复

@route_member.route("/comment")
def comment():
    return ops_render("member/comment.html")


@route_member.route('/ops', methods=['POST'])
def ops():
    resp = {'code': 200, 'msg': '操作成功', 'data': {}}
    req = request.values

    id = req['id'] if 'id' in req else 0
    act = req['act'] if 'act' in req else ''

    if not id:
        resp['code'] = -1
        resp['msg'] = '请选择要操作的账号'
        return jsonify(resp)

    if act not in ['remove', 'recover']:
        resp['code'] = -1
        resp['msg'] = '操作有误,请重试'
        return jsonify(resp)

    member_info = Member.query.filter_by(id=id).first()
    if not member_info:
        resp['code'] = -1
        resp['msg'] = '指定会员不存在'
        return jsonify(resp)

    if act == 'remove':
        member_info.status = 0
    elif act == 'recover':
        member_info.status = 1

    member_info.updated_time = getCurrentDate()
    db.session.add(member_info)
    db.session.commit()

    return jsonify(resp)
;
var member_index_ops = {
    init: function () {
        this.eventBind();
    },
    eventBind: function () {
        var that = this;
        $(".wrap_search .search").click(function () {
            $(".wrap_search").submit();
        });

        $(".remove").click(function () {
            that.ops("remove", $(this).attr("data"));
        });

        $(".recover").click(function () {
            that.ops("recover", $(this).attr("data"));
        });
    },
    ops: function (act, id) {
        var callback = {
            'ok': function () {
                $.ajax({
                    url: common_ops.buildUrl("/member/ops"),
                    type: 'POST',
                    data: {
                        act: act,
                        id: id
                    },
                    dataType: 'json',
                    success: function (res) {
                        var callback = null;
                        if (res.code == 200) {
                            callback = function () {
                                window.location.href = window.location.href;
                            }
                        }
                        common_ops.alert(res.msg, callback);
                    }
                })

            },
            'cancel': null
        };
        common_ops.confirm((act == "remove" ? "确定删除吗?" : "确定恢复吗?"), callback);
    }
};


$(document).ready(function () {
    member_index_ops.init()
});

猜你喜欢

转载自blog.csdn.net/weixin_43746433/article/details/104904386
今日推荐