Flask 会员列表展示

感谢编程浪子师傅的源码信息分享

web/controllers/member/Member.py

python 复制代码
# -*- coding: utf-8 -*-
from flask import Blueprint,request,redirect,jsonify
from common.libs.Helper import ops_render,iPagination,getCurrentDate,getDictFilterField,selectFilterObj
from common.libs.UrlManager import UrlManager
from common.models.member.Member import Member
from common.models.member.MemberComments import MemberComments
from common.models.food.Food import Food
from common.models.pay.PayOrder import PayOrder
from application import app,db
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 = iPagination( 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 )

@route_member.route( "/info" )
def info():
    resp_data = {}
    req = request.args
    id = int( req.get( "id",0 ) )
    reback_url = UrlManager.buildUrl( "/member/index" )
    if id < 1:
        return redirect( reback_url )

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

    pay_order_list = PayOrder.query.filter_by( member_id = id ).filter( PayOrder.status.in_( [-8,1] ) )\
        .order_by( PayOrder.id.desc() ).all()
    comment_list = MemberComments.query.filter_by( member_id = id ).order_by( MemberComments.id.desc() ).all()

    resp_data['info'] = info
    resp_data['pay_order_list'] = pay_order_list
    resp_data['comment_list'] = comment_list
    resp_data['current'] = 'index'
    return ops_render( "member/info.html",resp_data )

@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.buildUrl("/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 )


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

    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 = iPagination(page_params)
    offset = (page - 1) * app.config['PAGE_SIZE']

    comment_list = query.order_by(MemberComments.id.desc()).offset( offset ).limit( app.config['PAGE_SIZE'] ).all()
    data_list = []
    if comment_list:
        member_map = getDictFilterField( Member,Member.id,"id", selectFilterObj( comment_list ,"member_id" ) )
        food_ids = []
        for item in comment_list:
            tmp_food_ids = (item.food_ids[1:-1]).split("_")
            tmp_food_ids = {}.fromkeys( tmp_food_ids ).keys()
            food_ids = food_ids + list( tmp_food_ids )

        food_map = getDictFilterField( Food,Food.id,"id", food_ids )

        for item in comment_list:
            tmp_member_info = member_map[ item.member_id ]
            tmp_foods = []
            tmp_food_ids = (item.food_ids[1:-1]).split("_")
            for tmp_food_id in tmp_food_ids:
                tmp_food_info = food_map[ int( tmp_food_id ) ]
                tmp_foods.append({
                    'name': tmp_food_info.name,
                })

            tmp_data = {
                "content":item.content,
                "score":item.score,
                "member_info":tmp_member_info,
                "foods":tmp_foods
            }
            data_list.append( tmp_data )
    resp_data['list'] = data_list
    resp_data['pages'] = pages
    resp_data['current'] = 'comment'

    return ops_render( "member/comment.html",resp_data )


@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 )

@route_member.route("/index")

代码段是一个Flask路由函数index(),它接收一个GET请求并返回一个JSON响应。下面是对代码的详细解析:

  1. 首先,创建一个空字典resp_data来存储响应数据。
  2. 通过request.values获取请求参数,并将其赋值给变量req
  3. 使用条件语句判断是否存在参数p,如果存在且不为空,则将其转换为整数并赋值给变量page,否则将page设置为1。
  4. 创建一个Member查询对象query
  5. 如果请求参数中存在mix_kw,则使用filter()方法对query进行过滤,筛选出nickname字段中包含req['mix_kw']的记录。

请注意,代码中的Member是一个模型类,可能是通过SQLAlchemy定义的数据库表的映射类。

这段代码是一个条件判断语句,用于筛选查询结果。如果请求参数中包含名为'status'的键,并且该键对应的值大于-1,则会执行以下操作:

  1. 将查询对象query的筛选条件设置为Member.status等于请求参数中'status'对应的整数值。
  2. 创建一个名为page_params的字典,包含以下键值对:
    • 'total':查询结果的总数。
    • 'page_size':每页显示的数量。
    • 'page':当前页数。
    • 'display':每页显示的页码数量。
    • 'url':请求的完整路径,将其中的"&p={}".format(page)替换为空字符串。

根据传入的参数进行分页查询,并返回查询结果和分页信息

offset 偏移量

这段代码的功能是根据传入的page_params参数进行分页查询,查询结果存储在list变量中。然后将查询结果、分页信息、请求参数、状态映射和当前页面信息存储在resp_data字典中。最后通过ops_render函数将查询结果和其他信息渲染到指定的HTML模板中。

问题1: order_by 是什么函数有什么作用?

在给定的代码中,order_by是SQLAlchemy中的一个函数,用于对查询结果进行排序。它的作用是按照指定的字段对查询结果进行排序,可以按照升序或降序排列。

具体来说,order_by函数接受一个或多个参数,每个参数表示一个字段,用于指定排序的依据。可以使用点操作符来指定字段,例如Member.id表示按照Member表中的id字段进行排序。

在给定的代码中,query.order_by(Member.id.desc())表示按照Member表中的id字段进行降序排序。

.desc()表示降序排序, abbr.降序排列

descend 美 /dɪˈsend/ v. 下来,下降;

如果要进行升序排序,可以使用.asc()。

ascending 美 /əˈsendɪŋ/ adj. 上升的,增长的;升(序)的

sequence 美 /ˈsiːkwəns/ n. 顺序,次序;连续事件(或动作、事物);

总结一下,order_by函数用于对查询结果进行排序,可以按照一个或多个字段进行排序,可以指定升序或降序。

问题2: .offset 有什么作用

在这段代码中,.offset用于指定查询结果的偏移量。偏移量表示从查询结果中跳过的行数。在这个例子中,.offset(offset)表示从查询结果中跳过offset行。这样可以用来实现分页功能,通过指定不同的偏移量来获取不同页的数据。

例如,如果offset为0,则表示从查询结果的第一行开始获取数据;如果offset为10,则表示从查询结果的第11行开始获取数据。

需要注意的是,.offset方法必须与.limit方法一起使用,以限制查询结果的数量。.limit用于指定要获取的行数。

总结起来,.offset方法用于指定查询结果的偏移量,以实现分页功能。

问题3: .limit有什么作用?

.limit()函数用于限制查询结果的数量。在给定的查询中,它指定了从结果集中返回的记录数的上限。在这段代码中,.limit(app.config['PAGE_SIZE'])将查询结果限制为app.config['PAGE_SIZE']指定的页面大小。这通常用于分页查询,以确保每个页面只返回指定数量的记录。

问题4: .all有什么作用?

根据提供的引用内容,代码段.all()的作用是将查询结果以列表的形式返回。在这个例子中,query.order_by( Member.id.desc() ).offset( offset ).limit( app.config['PAGE_SIZE'] )是一个查询语句,.all()将返回满足查询条件的所有结果,并将其存储在名为list的变量中。

问题5: resp_data是一个对象吗?

在这段代码中,resp_data是一个字典对象。字典是Python中的一种数据结构,它由键值对组成,可以用来存储和访问数据。在这段代码中,resp_data被初始化为空字典{},然后通过resp_data['search_con'] = req将键为'search_con'的值设置为req。这样,resp_data就成为了一个包含一个键值对的字典对象。

字典对象可以通过键来访问对应的值。例如,可以使用resp_data['search_con']来获取'search_con'键对应的值。在这段代码中,resp_data['search_con']的值将被传递给ops_render()函数,用于渲染模板文件"member/index.html"。

总结起来,resp_data是一个字典对象,用于存储和传递数据。

问题6: req是一个什么样的值?请详细解析

reqrequest.values的值。request.values是一个字典,包含了请求中的所有参数和值。在这段代码中,req将包含来自请求的所有参数和值。

这段代码的作用是在resp_data字典中添加一个键值对,键为search_con,值为req。然后将resp_data传递给ops_render函数,该函数将渲染member/index.html模板并返回结果。

请注意,request.values是一个字典,它包含了请求中的所有参数和值。因此,req的值将取决于请求中的参数和值。

@route_member.route("/info")

根据提供的引用内容,代码段中的resp_data是一个字典对象,而不是一个普通的对象。在代码中,resp_data被用来存储一些数据,包括info、pay_order_list和comment_list等。这些数据将在返回的HTML页面中使用。

字典对象是一种可变的数据类型,它可以存储键值对,并且可以通过键来访问对应的值。

以下是代码段的解释和示例:

python 复制代码
@route_member.route( "/info" )
def info():
    resp_data = {}  # 创建一个空的字典对象
    req = request.args
    id = int( req.get( "id",0 ) )
    reback_url = UrlManager.buildUrl( "/member/index" )
    if id < 1:
        return redirect( reback_url )
    info = Member.query.filter_by( id =id ).first()
    if not info:
        return redirect( reback_url )

    pay_order_list = PayOrder.query.filter_by( member_id = id ).filter( PayOrder.status.in_( [-8,1] ) )\
        .order_by( PayOrder.id.desc() ).all()
    comment_list = MemberComments.query.filter_by( member_id = id ).order_by( MemberComments.id.desc() ).all()

    resp_data['info'] = info  # 将info存储在resp_data字典中的'info'键下
    resp_data['pay_order_list'] = pay_order_list  # 将pay_order_list存储在resp_data字典中的'pay_order_list'键下
    resp_data['comment_list'] = comment_list  # 将comment_list存储在resp_data字典中的'comment_list'键下
    resp_data['current'] = 'index'  # 将'index'存储在resp_data字典中的'current'键下

    return ops_render( "member/info.html",resp_data )  # 返回HTML页面,并将resp_data作为参数传递给ops_render函数

代码段是一个Flask路由函数,用于处理/member/info的请求。函数名为info(),接受GET请求。

在函数内部,首先定义了一个空字典resp_data用于存储返回的数据。

然后通过request.args获取请求参数,并将id参数转换为整型。

接下来,根据id的值进行条件判断,

如果id小于1,则重定向到/reback_url指定的页面。

如果id大于等于1,则通过Member.query.filter_by(id=id).first()查询数据库中id等于给定id的会员信息,并将结果存储在info变量中。

如果查询结果为空,则同样重定向到/reback_url指定的页面。

接下来,通过PayOrder.query.filter_by(member_id=id).filter(PayOrder.status.in_([-8,1])).order_by(PayOrder.id.desc()).all()查询数据库中member_id等于给定id且status为-8或1的支付订单信息,并将结果存储在pay_order_list变量中。

再通过MemberComments.query.filter_by(member_id=id).order_by(MemberComments.id.desc()).all()查询数据库中member_id等于给定id的会员评论信息,并将结果存储在comment_list变量中。

最后,将info、pay_order_list、comment_list以及其他一些数据存储在resp_data字典中,并返回渲染后的HTML页面。


数据库

sql 复制代码
DROP TABLE IF EXISTS `pay_order`;

CREATE TABLE `pay_order` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `order_sn` varchar(40) NOT NULL DEFAULT '' COMMENT '随机订单号',
  `member_id` bigint(11) NOT NULL DEFAULT '0' COMMENT '会员id',
  `total_price` decimal(10,2) NOT NULL DEFAULT '0.00' COMMENT '订单应付金额',
  `yun_price` decimal(10,2) NOT NULL DEFAULT '0.00' COMMENT '运费金额',
  `pay_price` decimal(10,2) NOT NULL DEFAULT '0.00' COMMENT '订单实付金额',
  `pay_sn` varchar(128) NOT NULL DEFAULT '' COMMENT '第三方流水号',
  `prepay_id` varchar(128) NOT NULL DEFAULT '' COMMENT '第三方预付id',
  `note` text NOT NULL COMMENT '备注信息',
  `status` tinyint(4) NOT NULL DEFAULT '0' COMMENT '1:支付完成 0 无效 -1 申请退款 -2 退款中 -9 退款成功  -8 待支付  -7 完成支付待确认',
  `express_status` tinyint(4) NOT NULL DEFAULT '0' COMMENT '快递状态,-8 待支付 -7 已付款待发货 1:确认收货 0:失败',
  `express_address_id` int(11) NOT NULL DEFAULT '0' COMMENT '快递地址id',
  `express_info` varchar(1000) NOT NULL DEFAULT '' COMMENT '快递信息',
  `comment_status` tinyint(1) NOT NULL DEFAULT '0' COMMENT '评论状态',
  `pay_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '付款到账时间',
  `updated_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '最近一次更新时间',
  `created_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '插入时间',
  PRIMARY KEY (`id`),
  UNIQUE KEY `idx_order_sn` (`order_sn`),
  KEY `idx_member_id_status` (`member_id`,`status`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='在线购买订单表';



DROP TABLE IF EXISTS `member_comments`;

CREATE TABLE `member_comments` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `member_id` int(11) NOT NULL DEFAULT '0' COMMENT '会员id',
  `food_ids` varchar(200) NOT NULL DEFAULT '' COMMENT '商品ids',
  `pay_order_id` int(11) NOT NULL DEFAULT '0' COMMENT '订单id',
  `score` tinyint(4) NOT NULL DEFAULT '0' COMMENT '评分',
  `content` varchar(200) NOT NULL DEFAULT '' COMMENT '评论内容',
  `created_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '插入时间',
  PRIMARY KEY (`id`),
  KEY `idx_member_id` (`member_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='会员评论表';


DROP TABLE IF EXISTS `food`;

CREATE TABLE `food` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `cat_id` int(11) NOT NULL DEFAULT '0' COMMENT '分类id',
  `name` varchar(100) NOT NULL DEFAULT '' COMMENT '书籍名称',
  `price` decimal(10,2) NOT NULL DEFAULT '0.00' COMMENT '售卖金额',
  `main_image` varchar(100) NOT NULL DEFAULT '' COMMENT '主图',
  `summary` varchar(10000) NOT NULL DEFAULT '' COMMENT '描述',
  `stock` int(11) NOT NULL DEFAULT '0' COMMENT '库存量',
  `tags` varchar(200) NOT NULL DEFAULT '' COMMENT 'tag关键字,以","连接',
  `status` tinyint(1) NOT NULL DEFAULT '1' COMMENT '状态 1:有效 0:无效',
  `month_count` int(11) NOT NULL DEFAULT '0' COMMENT '月销售数量',
  `total_count` int(11) NOT NULL DEFAULT '0' COMMENT '总销售量',
  `view_count` int(11) NOT NULL DEFAULT '0' COMMENT '总浏览次数',
  `comment_count` int(11) NOT NULL DEFAULT '0' COMMENT '总评论量',
  `updated_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '最后更新时间',
  `created_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '最后插入时间',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='食品表';
python 复制代码
flask-sqlacodegen 'mysql://root:root@127.0.0.1/food_db' --tables pay_order --outfile "common/models/pay/PayOrder.py"  --flask


flask-sqlacodegen 'mysql://root:root@127.0.0.1/food_db' --tables member_comments --outfile "common/models/member/MemberComments.py"  --flask


flask-sqlacodegen 'mysql://root:root@127.0.0.1/food_db' --tables food --outfile "common/models/food/Food.py"  --flask

common/models/member/Member.py 数据库

python 复制代码
# coding: utf-8
from application import db, app


class Member(db.Model):
    __tablename__ = 'member'

    id = db.Column(db.Integer, primary_key=True)
    nickname = db.Column(db.String(100), nullable=False, server_default=db.FetchedValue(), info='会员名')
    mobile = db.Column(db.String(11), nullable=False, server_default=db.FetchedValue(), info='会员手机号码')
    sex = db.Column(db.Integer, nullable=False, server_default=db.FetchedValue(), info='性别 1:男 2:女')
    avatar = db.Column(db.String(200), nullable=False, server_default=db.FetchedValue(), info='会员头像')
    salt = db.Column(db.String(32), nullable=False, server_default=db.FetchedValue(), info='随机salt')
    reg_ip = db.Column(db.String(100), nullable=False, server_default=db.FetchedValue(), info='注册ip')
    status = db.Column(db.Integer, nullable=False, server_default=db.FetchedValue(), info='状态 1:有效 0:无效')
    updated_time = db.Column(db.DateTime, nullable=False, server_default=db.FetchedValue(), info='最后一次更新时间')
    created_time = db.Column(db.DateTime, nullable=False, server_default=db.FetchedValue(), info='插入时间')


    @property
    def status_desc(self):
        return app.config['STATUS_MAPPING'][str(self.status)]

    @property
    def sex_desc(self):
        sex_mapping = {
            "0":"未知",
            "1":"男",
            "2":"女"
        }
        return sex_mapping[str(self.sex)]

    #status 统一返回sex表格的字段,  虚拟的好处是  直接可以在index.html中直接使用。  虚拟字段不可以查询

该段代码是一个Python类中的两个属性装饰器(@property)方法。这些装饰器方法允许我们在访问类的属性时执行一些自定义的逻辑。

  1. @property装饰器用于定义一个getter方法,它允许我们像访问普通属性一样访问该方法。在这段代码中,@property装饰器定义了两个getter方法:status_desc和sex_desc。

  2. status_desc方法返回了一个名为app.config['STATUS_MAPPING']的字典中与self.status对应的值。这个字典是一个配置文件中的映射,它将状态码映射到相应的描述。

  3. sex_desc方法返回了一个名为sex_mapping的字典中与self.sex对应的值。这个字典将性别码映射到相应的描述。

这两个属性装饰器方法使得我们可以通过访问类的实例属性来获取相应的描述信息,而不需要直接访问底层的状态码或性别码。

如下在index.html中过渡使用

虚拟字段的好处: 可以直接使用的

虚拟字段是不能进入字段查询的, 它只能作为该数据类里的某一种属性


搜索功能是在member/index.js里通过对warp_search 这个form标签及里面的search这个button按钮来设定的click点击事件后并submit提交到py后端flask框架里的member.py后进行判断处理。

py后端收到mix_kw status相关信息,进行筛选后返回并爬着、时刻准备再次进行筛选行为。

templates/member/index.html

html 复制代码
{% extends "common/layout_main.html" %}
{% block content %}
{% include "common/tab_member.html" %}
<div class="row">
    <div class="col-lg-12">
        <form class="form-inline wrap_search">
            <div class="row  m-t p-w-m">
                <div class="form-group">
                    <select name="status" class="form-control inline">
                        <option value="-1">请选择状态</option>

{#                      <option value="1"> 正常 </option>#}
{#                      <option value="0">已删除</option>#}

                        {% for tmp_key in status_mapping %}
                            <option value="{{ tmp_key }}" {% if tmp_key == search_con['status'] %} selected {% endif %}>{{ status_mapping[ tmp_key ] }}</option>
                        {%  endfor %}
                    </select>
                </div>
                <div class="form-group">
                    <div class="input-group">
                        <input type="text" name="mix_kw" placeholder="请输入关键字" class="form-control" value="{{ search_con['mix_kw'] }}">
                        <input type="hidden" name="p" value="{{ search_con['p'] }}">
                        <span class="input-group-btn">
                            <button type="button" class="btn  btn-primary search">
                                <i class="fa fa-search"></i>搜索
                            </button>
                        </span>
                    </div>
                </div>
            </div>
            <hr>
        </form>
        <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><img alt="image" class="img-circle" src="{{ item.avatar }}" style="width: 40px;height: 40px;"></td>
                <td>{{ item.nickname }}</td>
                <td>{{ item.sex_desc }}</td>
                <td>{{ item.status_desc }}</td>
                <td>
                    <a href="{{ buildUrl('/member/info') }}?id={{  item.id }}">
                        <i class="fa fa-eye fa-lg"></i>
                    </a>

                    {%  if item.status == 1 %}
                    <a class="m-l" href="{{ buildUrl('/member/set') }}?id={{  item.id }}">
                        <i class="fa fa-edit fa-lg"></i>
                    </a>

                    <a class="m-l remove" href="javascript:void(0);" data="{{  item.id }}">
                        <i class="fa fa-trash fa-lg"></i>
                    </a>
                    {%  else %}
                    <a class="m-l recover" href="javascript:void(0);" data="{{  item.id }}">
                        <i class="fa fa-rotate-left fa-lg"></i>
                    </a>
                    {% endif %}
                </td>
            </tr>
                {% endfor %}
            {% else %}
                <tr><td colspan="5">暂无数据</td></tr>
            {% endif %}
            </tbody>
        </table>
        <!--分页代码已被封装到统一模板文件中-->
        {% include 'common/pagenation.html' %}
    </div>
</div>
{% endblock %}
{%  block js %}
<script src="{{ buildStaticUrl('/js/member/index.js') }}"></script>
{% endblock %}

在这个模板中,定义了一个表单,包含一个下拉选择框和一个搜索按钮。

下拉选择框中的选项是通过循环生成的,循环遍历了一个名为status_mapping的字典,并将字典中的键值对生成为下拉选项。在生成选项时,还判断了当前选项是否与搜索条件中的状态匹配,如果匹配则设置为选中状态。

search_con['status'] 是已经在Member.py中定义的req即request.values,被包装在resp_data里后,使用ops_render渲染并返回resp_data。

问题1: search_con['p'] 是怎样的一个值?

search_con[p]是一个在HTML中的隐藏输入字段,它的值是search_con字典中键为p的值。在这个例子中,search_con是一个字典,它包含了从请求中获取的数据。search_con[p]表示获取search_con字典中键为p的值。

问题2: 那p又是一个怎样的值呢?

在Member.py中可以发现对p的值的后续获取和条件判断

python 复制代码
page  = int( req['p'] ) if ( 'p' in req and req['p'] ) else 1

根据提供的引用内容,代码中的p是一个隐藏的输入字段,它的值来自于search_con['p']。在这段代码中,p可能是用来表示页码的参数。根据代码中的注释,它可能用于搜索功能中的分页功能。

<input type="hidden" name="p" value="{{ search_con['p'] }}"> 总结来说,该段代码用于传输分页的数据信息,p表示page


<a href="{{ buildUrl('/member/info') }}?id={{ item.id }}">

<i class="fa fa-eye fa-lg"></i>

内容中还包含了一个HTML标签,其中包含了一个链接和一个图标。根据链接的格式,这可能是一个用于查看成员信息的URL链接。图标可能是一个用于显示成员信息的眼睛图标。

点击该图标后进入info方法对用户信息进行展示查看

问题3: <tr><td colspan="5">暂无数据</td></tr> 是什么意思?

<tr><td colspan="5">暂无数据</td></tr>是HTML中的表格标签,用于在表格中创建一行并设置该行的单元格数目。其中,colspan属性用于指定单元格跨越的列数。

以下是一个示例:

html 复制代码
<table border="1">
  <tr>
    <td>姓名</td>
    <td>年龄</td>
    <td>性别</td>
  </tr>
  <tr>
    <td colspan="3">暂无数据</td>
  </tr>
</table>

在上述示例中,表格中的第二行使用了<tr><td colspan="3">暂无数据</td></tr>来创建一行,并且该行的单元格跨越了3列,显示为"暂无数据"。

问题4: html 中 td tr 分别是什么意思?

在HTML中,td和tr是表格中的两个重要元素。

  1. td元素:td代表表格中的单元格(table data)。每个td元素定义了一个表格中的单元格,并且可以包含文本、图像、链接或其他HTML元素。td元素必须位于tr元素内部。

  2. tr元素:tr代表表格中的行(table row)。每个tr元素定义了表格中的一行,并且包含一个或多个td元素。tr元素必须位于table元素内部。

以下是一个简单的HTML表格示例:

html 复制代码
<table>
  <tr>
    <td>单元格1</td>
    <td>单元格2</td>
  </tr>
  <tr>
    <td>单元格3</td>
    <td>单元格4</td>
  </tr>
</table>

在上面的示例中,有两个tr元素,每个tr元素包含两个td元素。这将创建一个包含两行两列的表格。


mix_kw 是关键字查询, value="{{ search_con['mix_kw'] }}" 是相关展示,没有则输入框点击搜索按钮后里面的内容立刻消失,但是有了value="{{search_con['mix_kw']}}", 则点完搜索按钮后 该值持续存在展示。

templates/member/info.html

html 复制代码
{% extends "common/layout_main.html" %}
{% block content %}
{% include "common/tab_member.html" %}
<div class="row m-t">
    <div class="col-lg-12">
        <div class="row">
            <div class="col-lg-12">
                <div class="m-b-md">
                    {% if info.status == 1 %}
                    <a class="btn btn-outline btn-primary pull-right" href="{{ buildUrl('/member/set') }}?id={{ info.id }}">
                        <i class="fa fa-pencil"></i>编辑
                    </a>
                    {% endif %}
                    <h2>会员信息</h2>
                </div>
            </div>
        </div>
        <div class="row">
            <div class="col-lg-2 text-center">
                <img class="img-circle circle-border" src="{{ info.avatar }}"
                     width="100px" height="100px">
            </div>
            <div class="col-lg-10">
                <p class="m-t">姓名:{{ info.nickname }}</p>
                <p>性别:{{ info.sex_desc }}</p>
            </div>
        </div>
        <div class="row m-t">
            <div class="col-lg-12">
                <div class="panel blank-panel">
                    <div class="panel-heading">
                        <div class="panel-options">
                            <ul class="nav nav-tabs">
                                <li class="active">
                                    <a href="#tab-1" data-toggle="tab" aria-expanded="false">会员订单</a>
                                </li>
                                <li>
                                    <a href="#tab-2" data-toggle="tab" aria-expanded="true">会员评论</a>
                                </li>
                            </ul>
                        </div>
                    </div>

                    <div class="panel-body">
                        <div class="tab-content">
                            <div class="tab-pane active" id="tab-1">
                                <table class="table table-striped">
                                    <thead>
                                    <tr>
                                        <th>订单编号</th>
                                        <th>支付时间</th>
                                        <th>支付金额</th>
                                        <th>订单状态</th>
                                    </tr>
                                    </thead>
                                    <tbody>
                                     {%  if pay_order_list %}
                                        {% for item in pay_order_list %}
                                    <tr>
                                       <td>{{ item.order_number }}</td>
                                       <td>{{ item.pay_time }}</td>
                                       <td>{{ item.total_price }}</td>
                                       <td>{{ item.status_desc }}</td>
                                    </tr>
                                        {% endfor %}
                                    {% else %}
                                         <td colspan="4">暂无订单</td>
                                    {% endif %}
                                    </tbody>
                                </table>
                            </div>
                            <div class="tab-pane" id="tab-2">
                                <table class="table table-striped">
                                    <thead>
                                    <tr>
                                        <th>评论时间</th>
                                        <th>评分</th>
                                        <th>评论内容</th>
                                    </tr>
                                    </thead>
                                    <tbody>
                                    <tr>
                                    </tr>
                                    {%  if comment_list %}
                                        {% for item in comment_list %}
                                    <tr>
                                        <td>{{ item.created_time }}</td>
                                        <td>{{ item.score }}</td>
                                        <td>{{ item.content }}</td>
                                    </tr>
                                         {% endfor %}
                                    {% else %}
                                         <td colspan="3">暂无评论</td>
                                    {% endif %}
                                    </tbody>
                                </table>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </div>
</div>
{% endblock %}

<img>

  • <img>标签用于在网页中插入图片。
  • class="img-circle circle-border"指定了图片的样式,img-circle将图片变为圆形,circle-border添加了一个圆形边框。
  • src="{{ info.avatar }}"指定了图片的来源,{{ info.avatar }}是一个占位符,表示图片的URL将从info对象的avatar属性中获取。
  • width="100px" height="100px"指定了图片的宽度和高度,将图片调整为100像素乘以100像素的大小。

这段代码的作用是在网页中显示一个圆形边框的头像图片,图片的来源和大小可以通过info对象的avatar属性和指定的宽度和高度进行设置。

templates/member/set.html

html 复制代码
{% extends "common/layout_main.html" %}
{% block content %}
{% include "common/tab_member.html" %}
<div class="row mg-t20 wrap_member_set">
	<div class="col-lg-12">
		<h2 class="text-center">会员设置</h2>
		<div class="form-horizontal m-t">
			<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" class="form-control" placeholder="请输入会员名称" name="nickname" value="{{ info.nickname }}">
				</div>
			</div>
			<div class="hr-line-dashed"></div>
			<div class="form-group">
				<div class="col-lg-4 col-lg-offset-2">
					<input type="hidden" name="id" value="{{ info.id }}">
					<button class="btn btn-w-m btn-outline btn-primary save">保存</button>
				</div>
			</div>
		</div>
	</div>
</div>
{% endblock %}
{%  block js %}
<script src="{{ buildStaticUrl('/js/member/set.js') }}"></script>
{% endblock %}

templates/member/comment.html

html 复制代码
{% extends "common/layout_main.html" %}
{% block content %}
{% include "common/tab_member.html" %}
<div class="row">
    <div class="col-lg-12">
        <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>
                    <img alt="image" class="img-circle" src="{{ item.member_info.avatar }}" style="width: 40px;height: 40px;">
                </td>
                <td>{{ item.member_info.nickname }}</td>
                <td>
                     {% for item_food in item.foods %}
                        {{ item_food.name }}、
                    {% endfor %}
                </td>
                <td>{{ item.content }}</td>
                <td>{{ item.score }}</td>
            </tr>
             {% endfor %}
            {% else %}
                <tr><td colspan="5">暂无数据</td></tr>
            {% endif %}
            </tbody>
        </table>
        <!--分页代码已被封装到统一模板文件中-->
        {% include 'common/pagenation.html' %}
    </div>
</div>
{% endblock %}

config/base_setting.py

python 复制代码
STATUS_MAPPING = {
    "1":"正常",
    "0":"已删除"
}

web/static/js/member/index.js

javascript 复制代码
;
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();
} );

web/static/js/member/set.js

javascript 复制代码
;
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_target = $(".wrap_member_set input[name=nickname]");
            var nickname = nickname_target.val();

            if( nickname.length < 1 ){
                common_ops.tip( "请输入符合规范的姓名",nickname_target );
                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();
});

这段代码是一个JavaScript的对象,其中包含了一个名为member_set_ops的变量。该变量具有三个方法:init、eventBind和save。其中,init方法用于初始化对象,eventBind方法用于绑定事件,save方法用于保存数据。

在eventBind方法中,当点击.wrap_member_set .save元素时,会执行一个回调函数。在回调函数中,首先判断按钮是否有disabled类,如果有,则弹出提示信息"正在处理,请不要重复提交",并返回。如果按钮没有disabled类,则继续执行后续代码。

接下来,通过$(".wrap_member_set input[name=nickname]")选择器选中一个名为nickname的输入框,并获取其值赋给变量nickname。然后,判断nickname的长度是否小于1,如果是,则通过common_ops.tip方法弹出提示信息"请输入符合规范的姓名",并返回。如果nickname的长度大于等于1,则继续执行后续代码。

接着,给按钮添加disabled类,防止重复提交。然后,创建一个名为data的对象,其中包含两个属性:nickname和id。nickname属性的值为上面获取的nickname变量的值,id属性的值为.wrap_member_set input[name=id]元素的值。

最后,可以根据具体需求来处理data对象,例如发送Ajax请求将数据保存到服务器。

该段代码是一个使用jQuery的Ajax请求的示例。下面是对代码的详细解析:

  1. $.ajax({}):这是一个jQuery的Ajax请求方法,用于发送异步HTTP请求。

  2. url:common_ops.buildUrl("/member/set"):指定请求的URL,这里使用了common_ops.buildUrl()方法来构建URL。

  3. type:'POST':指定请求的类型为POST。

  4. data:data:指定要发送到服务器的数据,这里的data是一个变量,表示要发送的数据。

  5. dataType:'json':指定服务器返回的数据类型为JSON。

  6. success:function(res){}:指定请求成功后的回调函数,res是服务器返回的数据。

  7. btn_target.removeClass("disabled"):移除按钮的disabled类,使按钮可用。

  8. if( res.code == 200 ):判断服务器返回的状态码是否为200。

  9. callback = function(){ window.location.href = common_ops.buildUrl("/member/index"); }:定义一个回调函数callback,如果服务器返回的状态码为200,则将页面重定向到/member/index。

  10. common_ops.alert( res.msg,callback):调用common_ops.alert()方法显示服务器返回的消息,并将回调函数callback作为参数传递。

  11. $(document).ready(function(){ member_set_ops.init(); });:在文档加载完成后,调用member_set_ops.init()方法进行初始化操作。

相关推荐
码事漫谈8 分钟前
后端开发如何将创新转化为专利?案例、流程与实操指南
后端
檀越剑指大厂1 小时前
【Python系列】fastapi和flask中的阻塞问题
python·flask·fastapi
小坏讲微服务1 小时前
SpringCloud零基础学全栈,实战企业级项目完整使用
后端·spring·spring cloud
humors2211 小时前
服务端开发案例(不定期更新)
java·数据库·后端·mysql·mybatis·excel
顾安r2 小时前
11.14 脚本网页 迷宫逃离
服务器·javascript·游戏·flask·html
YoungHong19922 小时前
【Python进阶】告别繁琐Debug!Loguru一键输出异常日志与变量值
python·debug·异常处理·日志·loguru·log·logger
顾安r2 小时前
11.14 脚本网页游戏 猜黑红
前端·javascript·游戏·flask·html
AiXed2 小时前
PC微信协议之nid算法
python·网络协议·算法·微信
小李哥哥3 小时前
基于数据的人工智能建模流程及源码示例
python
APIshop3 小时前
实战解析:苏宁易购 item_search 按关键字搜索商品API接口
开发语言·chrome·python