添加和编辑菜品模块
文件上传
菜品的添加需要使用到富文本编辑器
添加菜品时需要上传图片,所以我们需要配置文件上传的操作,设定全局变量
UPLOAD = {
# 定义可上传的图片类型
'ext': ['jpg', 'gif', 'bmp', 'jpeg', 'png'],
# 普通文件保存路径
'prefix_path': 'web/static/upload/',
# 富文本图片路径
'prefix_url': 'static/upload/'
}
utils/views.py全局工具文件中
@utils_bp.route('/upload_pic', methods=['GET', 'POST'])
def upload_pic():
# 获取传递过来的值,这里的值需要前后端配合。固定为upfile,这个属性可以修改,但是前端请求时也需要使用对应的属性
file_target = request.files
upfile = file_target['upfile'] if 'upfile' in file_target else None
# 用于触发,set.js文件中的success方法,回显图片
callback_target = 'window.parent'
# 如果没有值,则弹窗显示上传失败。此处我们前后端未分离,后期可以修改为返回json格式的内容,由前端弹窗提示
if upfile is None:
return '<script>{0}.error("{1}")</script>'.format(callback_target, '上传失败')
# 调用upload_file方法,实现图片上传,此方法在utils/utils.py文件中
ret = upload_file(upfile)
# 判断图片上传的返回值,给出不同反馈
if ret['code'] != 200:
return '<script>{0}.error("{1}")</script>'.format(callback_target, '上传失败:' + ret['msg'])
return '<script>{0}.success("{1}")</script>'.format(callback_target, ret['data']['file_key'])
utils/utils.py中实现了upload_file方法,用于文件上传
# 文件上传本地
def upload_file(file_storage):
resp = {'code': 200, 'msg': '操作成功!', 'data': {}}
# 获取文件名称
filename = secure_filename(file_storage.filename)
# 按照文件名进行分割,并且取后缀
ext = filename.rsplit('.', 1)[1]
# 如果该后缀名不在配置文件里面,那么就要返回错误消息
if ext not in Config.UPLOAD['ext']:
resp['code'] = -1
resp['msg'] = '不允许的扩展类型文件'
return resp
file_dir = datetime.now().strftime('%Y%m%d')
save_dir = Config.UPLOAD['prefix_path'] + file_dir
if not os.path.exists(save_dir):
os.makedirs(save_dir)
os.chmod(save_dir, stat.S_IRWXU | stat.S_IRGRP | stat.S_IRWXO)
# 构建文件名称
file_name = str(uuid.uuid4().hex) + '.' + ext
# 保存文件
file_storage.save('{0}/{1}'.format(save_dir, file_name))
# 将上传记录存储到数据库中
add_file_obj = Files()
add_file_obj.file_key = file_dir + '/' + file_name
db.session.add(add_file_obj)
db.session.commit()
resp['data'] = {
'file_key': add_file_obj.file_key
}
return resp
# 上传图片
def upload_image():
resp = {
'state': 'SUCCESS',
'url': '',
'title': '',
'original': ''
}
file_target = request.files
upfile = file_target['upfile'] if 'upfile' in file_target else None
if upfile is None:
resp['state'] = '上传失败'
return jsonify(resp)
ret = upload_file(upfile)
if ret['code'] != 200:
resp['state'] = '上传失败:' + ret['msg']
# 调用图片回显方法,传入图片名称
resp['url'] = build_image_url(ret['data']['file_key'])
图片回显
# 返回图片地址
def build_image_url(path):
url = Config.DOMAIN + '/' + Config.UPLOAD['prefix_url'] + path
return url
添加和编辑菜品
富文本的添加图片逻辑类似。不过路由返回的是一个完整路径
http://127.0.0.1:8999/static/upload/20250709/84a0dc4388154fd18c224cc6d3123270.jpg
。方便后续在富文本中编译显示准备添加路由,开始添加和编辑菜品
@manage_bp.route('/food/edit', methods=['GET', 'POST'])
def food_edit():
resp = {'code': 200, 'msg': '操作成功', 'data': {}}
if request.method == 'GET':
resp_data = {}
req = request.args
f_id = int(req.get('id', 0))
food_obj = None
if f_id:
food_obj = Food.query.get(f_id)
cat_list = FoodCat.query.all()
resp_data['info'] = food_obj
resp_data['current'] = 'index'
resp_data['cat_list'] = cat_list
return ops_render('food/set.html', resp_data)
if request.method == 'POST':
req = request.values
f_id = req['food_id'] if 'food_id' in req else 0
c_id = int(req['cat_id']) if 'cat_id' in req else 0
name = req['name'] if 'name' in req else ''
price = req['price'] if 'price' in req else ''
main_image = req['main_image'] if 'main_image' in req else ''
summary = req['summary'] if 'summary' in req else ''
stock = int(req['stock']) if 'stock' in req else ''
tags = req['tags'] if 'tags' in req else ''
price = Decimal(price).quantize(Decimal('0.00'))
# if f_id < 1:
# resp['code'] = -1
# resp['msg'] = '商品不存在!'
# return jsonify(resp)
if c_id < 1:
resp['code'] = -1
resp['msg'] = '请选择分类'
return jsonify(resp)
if len(name) < 1 or name is None:
resp['code'] = -1
resp['msg'] = '名称不符合规范'
return jsonify(resp)
if price <= 0 or price is None:
resp['code'] = -1
resp['msg'] = '售卖价格不能为空且大于等于0'
return jsonify(resp)
if main_image is None or len(main_image) < 3:
resp['code'] = -1
resp['msg'] = '请上传封面图'
return jsonify(resp)
if summary is None or len(summary) < 10:
resp['code'] = -1
resp['msg'] = '详情不能少于十个字符!'
return jsonify(resp)
if stock < 1:
resp['code'] = -1
resp['msg'] = '库存不能小于1'
return jsonify(resp)
if tags is None or len(tags) < 1:
resp['code'] = -1
resp['msg'] = '请输入标签!'
return jsonify(resp)
food_obj = Food.query.get(f_id)
before_stock = 0
if not food_obj:
food_obj = Food()
else:
before_stock = food_obj.stock
food_obj.cat_id = c_id
food_obj.name = name
food_obj.price = price
food_obj.main_image = main_image
food_obj.summary = summary
food_obj.stock = stock
food_obj.tags = tags
db.session.add(food_obj)
db.session.commit()
stock_change = FoodStockChangeLog()
stock_change.food_id = food_obj.id
stock_change.unit = int(stock) - int(before_stock)
stock_change.total_stock = stock
stock_change.note = '后台调整'
db.session.add(stock_change)
db.session.commit()
return jsonify(resp)
显示所有菜品
@manage_bp.route('/food/list')
def food_list():
resp_data = {}
# 有page就取page
page = int(request.args.get('page', 1))
query = Food.query
# 有status就取status
status_name = int(request.args.get('status', '-1'))
if status_name > -1:
query = query.filter(Food.status == status_name)
# 有cat_id就取cat_id
cat_id = int(request.args.get('cat_id', 0))
if cat_id > 0:
query = query.filter_by(cat_id=cat_id)
# 有查询的信息就使用查询信息
mix_kw = request.args.get('mix_kw', '')
if mix_kw:
rule = or_(Food.name.contains('%s' % mix_kw), Food.tags.contains('%s' % mix_kw))
page_data = query.filter(rule).order_by(Food.id.desc()).paginate(page=page, per_page=Config.PER_PAGE)
else:
page_data = query.order_by(Food.id.desc()).paginate(page=page, per_page=Config.PER_PAGE)
resp_data = {
'list': page_data,
'status_mapping': constants.STATUS_MAPPING
}
# 这里获取分类数据,用于筛选.
food_list_obj = FoodCat.query.all()
resp_data['current'] = 'index'
resp_data['cat_list'] = food_list_obj
return ops_render('food/index.html', resp_data)
查看菜品详情
路由中接收传递过来的id值,根据id查询数据
详情中,我们还需要展示当前菜品的库存变更记录
后期还可以加上销售记录
@manage_bp.route('/food/info')
def food_info():
resp_data = {}
req = request.args
f_id = int(req.get('id', 0))
if f_id < 1:
return redirect('manage.food_list')
food_obj = Food.query.get(f_id)
if not food_obj:
return redirect('manage.food_list')
# 关联查询,库存记录变更表
stock_change_list = FoodStockChangeLog.query.filter(FoodStockChangeLog.food_id == f_id).order_by(
FoodStockChangeLog.id.desc()).all()
resp_data['info'] = food_obj
resp_data['current'] = 'index'
resp_data['stock_change_list'] = stock_change_list
return ops_render('food/info.html', resp_data)
删除菜品(修改状态)
同理可得,根据传递过来的id和act的值,修改状态
@manage_bp.route('/food/ops', methods=['PUT'])
def foot_ops():
resp = {'code': 200, 'msg': '操作成功!', 'data': {}}
req = request.values
f_id = req['id'] if 'id' in req else 0
act = req['act'] if 'act' in req else ''
if not f_id:
resp['code'] = -1
resp['msg'] = '操作失败!'
return jsonify(resp)
if act not in ['remove', 'recover']:
resp['code'] = -1
resp['msg'] = '操作失败!'
return jsonify(resp)
food_obj = Food.query.get(f_id)
if not food_obj:
resp['code'] = -1
resp['msg'] = '指定食物不存在!'
return jsonify(resp)
if act == 'remove':
food_obj.status = 0
elif act == 'recover':
food_obj.status = 1
db.session.commit()
return jsonify(resp)