订单后台管理系统-day07菜品模块
添加和编辑菜品模块
文件上传
菜品的添加需要使用到富文本编辑器
添加菜品时需要上传图片,所以我们需要配置文件上传的操作,设定全局变量
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.filesupfile = 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'] = -1resp['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_namedb.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.filesupfile = file_target['upfile'] if 'upfile' in file_target else Noneif 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'] + pathreturn 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.argsf_id = int(req.get('id', 0))
food_obj = Noneif f_id:food_obj = Food.query.get(f_id)
cat_list = FoodCat.query.all()resp_data['info'] = food_objresp_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 0c_id = int(req['cat_id']) if 'cat_id' in req else 0name = 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'] = -1resp['msg'] = '请选择分类'return jsonify(resp)
if len(name) < 1 or name is None:resp['code'] = -1resp['msg'] = '名称不符合规范'return jsonify(resp)
if price <= 0 or price is None:resp['code'] = -1resp['msg'] = '售卖价格不能为空且大于等于0'return jsonify(resp)
if main_image is None or len(main_image) < 3:resp['code'] = -1resp['msg'] = '请上传封面图'return jsonify(resp)
if summary is None or len(summary) < 10:resp['code'] = -1resp['msg'] = '详情不能少于十个字符!'return jsonify(resp)
if stock < 1:resp['code'] = -1resp['msg'] = '库存不能小于1'return jsonify(resp)
if tags is None or len(tags) < 1:resp['code'] = -1resp['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_idfood_obj.name = namefood_obj.price = pricefood_obj.main_image = main_imagefood_obj.summary = summaryfood_obj.stock = stockfood_obj.tags = tags
db.session.add(food_obj)db.session.commit()
stock_change = FoodStockChangeLog()stock_change.food_id = food_obj.idstock_change.unit = int(stock) - int(before_stock)stock_change.total_stock = stockstock_change.note = '后台调整'db.session.add(stock_change)db.session.commit()
return jsonify(resp)
显示所有菜品
@manage_bp.route('/food/list')
def food_list():resp_data = {}# 有page就取pagepage = int(request.args.get('page', 1))
query = Food.query# 有status就取statusstatus_name = int(request.args.get('status', '-1'))if status_name > -1:query = query.filter(Food.status == status_name)# 有cat_id就取cat_idcat_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_objreturn ops_render('food/index.html', resp_data)
查看菜品详情
路由中接收传递过来的id值,根据id查询数据
详情中,我们还需要展示当前菜品的库存变更记录
后期还可以加上销售记录
@manage_bp.route('/food/info')
def food_info():resp_data = {}
req = request.argsf_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_objresp_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 0act = req['act'] if 'act' in req else ''if not f_id:resp['code'] = -1resp['msg'] = '操作失败!'return jsonify(resp)if act not in ['remove', 'recover']:resp['code'] = -1resp['msg'] = '操作失败!'return jsonify(resp)food_obj = Food.query.get(f_id)if not food_obj:resp['code'] = -1resp['msg'] = '指定食物不存在!'return jsonify(resp)if act == 'remove':food_obj.status = 0elif act == 'recover':food_obj.status = 1db.session.commit()return jsonify(resp)