感谢编程浪子师傅的源码信息分享
web/controllers/member/Member.py
# -*- 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响应。下面是对代码的详细解析:
- 首先,创建一个空字典
resp_data
来存储响应数据。 - 通过
request.values
获取请求参数,并将其赋值给变量req
。 - 使用条件语句判断是否存在参数
p
,如果存在且不为空,则将其转换为整数并赋值给变量page
,否则将page
设置为1。 - 创建一个
Member
查询对象query
。 - 如果请求参数中存在
mix_kw
,则使用filter()
方法对query
进行过滤,筛选出nickname
字段中包含req['mix_kw']
的记录。
请注意,代码中的Member
是一个模型类,可能是通过SQLAlchemy定义的数据库表的映射类。
这段代码是一个条件判断语句,用于筛选查询结果。如果请求参数中包含名为’status’的键,并且该键对应的值大于-1,则会执行以下操作:
- 将查询对象query的筛选条件设置为Member.status等于请求参数中’status’对应的整数值。
- 创建一个名为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是一个什么样的值?请详细解析
req
是request.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页面中使用。
字典对象是一种可变的数据类型,它可以存储键值对,并且可以通过键来访问对应的值。
以下是代码段的解释和示例:
@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函数
数据库
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='食品表';
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 数据库
# 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)方法。这些装饰器方法允许我们在访问类的属性时执行一些自定义的逻辑。
-
@property装饰器用于定义一个getter方法,它允许我们像访问普通属性一样访问该方法。在这段代码中,@property装饰器定义了两个getter方法:status_desc和sex_desc。
-
status_desc方法返回了一个名为app.config[‘STATUS_MAPPING’]的字典中与self.status对应的值。这个字典是一个配置文件中的映射,它将状态码映射到相应的描述。
-
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
{% 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的值的后续获取和条件判断
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属性用于指定单元格跨越的列数。
以下是一个示例:
<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是表格中的两个重要元素。
-
td元素:td代表表格中的单元格(table data)。每个td元素定义了一个表格中的单元格,并且可以包含文本、图像、链接或其他HTML元素。td元素必须位于tr元素内部。
-
tr元素:tr代表表格中的行(table row)。每个tr元素定义了表格中的一行,并且包含一个或多个td元素。tr元素必须位于table元素内部。
以下是一个简单的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
{% 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 %}
templates/member/set.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
{% 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
STATUS_MAPPING = {
"1":"正常",
"0":"已删除"
}
web/static/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();
} );