Python Flask 微信小程序點餐系統(五):管理員後臺菜品模塊

菜品模塊架構

  • 菜品相關數據表設計
  • 菜品分類列表
  • 添加 / 編輯 / 刪除 / 恢復菜品分類
  • 菜品列表
  • 添加 / 編輯 / 刪除 / 恢復菜品
  • 菜品庫存變更歷史

在這裏插入圖片描述

表結構設計

CREATE TABLE `food_cat` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `name` varchar(50) NOT NULL DEFAULT '' COMMENT '類別名稱',
  `weight` tinyint(4) NOT NULL DEFAULT '1' COMMENT '權重',
  `status` tinyint(1) NOT NULL DEFAULT '1' COMMENT '狀態 1:有效 0:無效',
  `updated_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '最後一次更新時間',
  `created_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '插入時間',
  PRIMARY KEY (`id`),
  UNIQUE KEY `idx_name` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='食品分類';

以及同時定義的另外food、foodsalechangelog和foodstockchangelog三張表

代碼書寫

首先對於food.py文件中,我們可以看到相關文件如下:

route_food = Blueprint( 'food_page',__name__ )

@route_food.route( "/index" )
def index():
    return render_template( "food/index.html" )

@route_food.route( "/info" )
def info():
    return render_template( "food/info.html" )


@route_food.route( "/set" )
def set():
    return render_template( "food/set.html" )


@route_food.route( "/cat" )
def cat():
    return render_template( "food/cat.html" )

@route_food.route( "/cat-set" )
def catSet():
    return render_template( "food/cat_set.html" )

我們先對分類的編輯和創建接口進行書寫:

@route_food.route( "/cat" )     # 分類列表的展示功能
def cat():
    resp_data = {}
    req = request.values
    query = FoodCat.query

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

    list = query.order_by( FoodCat.weight.desc(),FoodCat.id.desc() ).all()
    resp_data['list'] = list
    resp_data['search_con'] = req
    resp_data['status_mapping'] = app.config['STATUS_MAPPING']
    resp_data['current'] = 'cat'
    return ops_render( "food/cat.html",resp_data )

@route_food.route( "/cat-set" )     # 美餐管理分類中的編輯和創建接口
def catSet():
    if request.method == "GET":     # get請求
        resp_data = {}
        req = request.args
        id = int(req.get("id",0))
        info = None
        if id:
            info = FoodCat.query.filter_by(id=id).first()
        resp_data["info"] = info
        resp_data["current"] = "cat"
        return ops_render( "food/cat_set.html" )

    resp = {"code":200,"msg":"操作成功~~","data":{}}    # 開始post請求
    req = request.values

    id = req["id"] if "id" in req else 0
    name = req["name"] if "name" in req else ""
    weight = int(req["weight"]) if ("weight" in req and int(req["weight"]) > 0) else 1

    if name is None or len(name) < 1:
        resp["code"] = -1
        resp["msg"] = "請輸入符合規範的分類名稱~~"

    food_cat_info = FoodCat.query.filter_by(id = id).first()
    if food_cat_info:
        model_food_cat = food_cat_info
    else:
        model_food_cat = FoodCat()
        model_food_cat.created_time = getCurrentDate()
    model_food_cat.name = name
    model_food_cat.weight = weight
    model_food_cat.updated_time = getCurrentDate()
    db.session.add(model_food_cat)
    db.session.commit()
    return jsonify( resp )

後端邏輯和之前類似,我也是好久沒敲了,但感覺換湯不換藥,都是差不多邏輯,這篇的難點基本都在前端,但我重點介紹後面的吧,這裏的前端邏輯就是需要根據後端更改的字段,將cat_set頁面更改爲相應形式,關於weight權重可以這樣設置:{% if info and info.weight > 0 %}{{ info.weight }}{% else %}1{% endif%},寫成一行,確實美觀。js和之前一樣也沒有多大改動。另外和Linux不同的是載入配置文件的命令,兩個區別如下:

#windows:
set ops-config=local && python manager.py runserver

#linux:
export ops-config=local && python manager.py runserver

然後我們還需要寫頁面展示和刪除功能,才能在頁面上顯示出具體的更新,其實現在已經可以測試了,只不過只是數據庫變化,頁面上沒有顯示:
在這裏插入圖片描述
然後我們可以從數據庫看到記錄,當時網速有點差,所以點了兩次,可以看到數據庫中出現同時間兩次。
在這裏插入圖片描述

再添加上分類展示,以及統一處理的兩個接口,那麼分類列表裏的增刪改查就做完了:

@route_food.route( "/cat" )
def cat():
    resp_data = {}
    req = request.values
    query = FoodCat.query

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

    list = query.order_by( FoodCat.weight.desc(),FoodCat.id.desc() ).all()
    resp_data['list'] = list
    resp_data['search_con'] = req
    resp_data['status_mapping'] = app.config['STATUS_MAPPING']
    resp_data['current'] = 'cat'
    return ops_render( "food/cat.html",resp_data )


@route_food.route("/cat-ops",methods = [ "POST" ])
def catOps():
    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)

    food_cat_info = FoodCat.query.filter_by( id= id ).first()
    if not food_cat_info:
        resp['code'] = -1
        resp['msg'] = "指定分類不存在~~"
        return jsonify(resp)

    if act == "remove":
        food_cat_info.status = 0
    elif act == "recover":
        food_cat_info.status = 1

        food_cat_info.update_time = getCurrentDate()
    db.session.add( food_cat_info )
    db.session.commit()
    return jsonify(resp)

前端中修改cat.html和相應的cat.js就能測試了,比較難改的地方在可選框的狀態,這條語句有點長爲

<select name="status" class="form-control inline">
    <option value="-1">請選擇狀態</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>

這個是判斷status_mapping搜索框狀態,1表示正常,0表示刪除,search_con爲可選框的值,這裏做了兩次判斷兩次選值還是很能證明jinjia2的寫法。

然後我們就能看見最終結果:

在這裏插入圖片描述


ueditor可視化編輯器步驟

ueditor的文本編輯器可以看http://fex.baidu.com/ueditor/,我們只需要按照文檔中所寫的那樣一步步去配置。首先下載好安裝包放進項目文件中的plugin文件夾下,然後在前端的food/set.html中導入相關文件:

<script src="{{ buildStaticUrl( '/plugins/ueditor/ueditor.config.js' ) }}"></script>
<script src="{{ buildStaticUrl( '/plugins/ueditor/ueditor.all.min.js' ) }}"></script>
<script src="{{ buildStaticUrl( '/plugins/ueditor/lang/zh-cn/zh-cn.js' ) }}"></script>

<script src="{{ buildStaticUrl( '/js/food/set.js' ) }}"></script>

最後一個爲我們自定義的js文件,按照ueditor文檔中所寫的那樣,關鍵代碼爲var ue = UE.getEditor(‘container’);

js文件爲:

var food_set_ops = {
    init:function () {
        this.eventBind();
        this.initEditor();
    },
    eventBind:function () {

    },
    initEditor:function () {
        var that = this;
        that.ue = UE.getEditor("editor");
    }
};

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

在這裏插入圖片描述

上面雖然有可視化編輯器了,但我們需要完善後端邏輯,首先寫圖片上傳函數:

# -*- coding: utf-8 -*-
from werkzeug.utils import secure_filename
from application import app,db
from common.libs.Helper import getCurrentDate
import datetime
import  os,stat,uuid
from common.models.Image import Image
class UploadService():
	@staticmethod
	def uploadByFile( file ):


		config_upload = app.config['UPLOAD']       # """{'ext':[ 'jpg','gif','bmp','jpeg','png' ],'prefix_path':'/web/static/upload/','prefix_url':'/static/upload/'}"""

		resp = { 'code':200,'msg':'操作成功~~','data':{} }
		filename = secure_filename( file.filename )     # 得到一個安全驗證的文件
		ext = filename.rsplit(".",1)[1]     # 0是文件名,1是擴展後綴
		if ext not in config_upload['ext']: # 檢查文件後綴
			resp['code'] = -1
			resp['msg'] = "不允許的擴展類型文件"
			return resp


		root_path = app.root_path + config_upload['prefix_path']    # 創建服務器文件夾名字
		file_dir = datetime.datetime.now().strftime("%Y%m%d")
		save_dir = root_path + file_dir
		if not os.path.exists( save_dir ):
			os.mkdir( save_dir )
			os.chmod( save_dir,stat.S_IRWXU | stat.S_IRGRP |  stat.S_IRWXO )

		file_name = str( uuid.uuid4() ).replace("-","") + "." + ext  # 組成路徑與文件
		file.save( "{0}/{1}".format( save_dir,file_name ) )

		model_image = Image()   # 將圖片地址保存到數據庫中
		model_image.file_key = file_dir + "/" + file_name
		model_image.created_time = getCurrentDate()
		db.session.add( model_image)
		db.session.commit()

		resp['data'] = {
			'file_key': model_image.file_key
		}
		return resp

然後我們需要寫和前端對應的接口,再創建一個upload包,並申請一個藍圖,名字爲route_upload,前綴爲url_prefix = “/upload”,接口路徑爲"/ueditor",那麼代碼爲:

# -*- coding: utf-8 -*-
from flask import Blueprint,request,jsonify
from application import  app
import re,json
from common.libs.UploadService import UploadService
from common.libs.UrlManager import UrlManager
from common.models.Image import Image
route_upload = Blueprint('upload_page', __name__)


@route_upload.route("/ueditor",methods = [ "GET","POST" ])
def ueditor():

	req = request.values
	action = req['action'] if 'action' in req else ''

	if action == "config":
		root_path = app.root_path
		config_path = "{0}/web/static/plugins/ueditor/upload_config.json".format( root_path )
		with open( config_path,encoding="utf-8" ) as fp:
			try:
				config_data =  json.loads( re.sub( r'\/\*.*\*/' ,'',fp.read() ) )
			except:
				config_data = {}
		return  jsonify( config_data )

	if action == "uploadimage":
		return uploadImage()

	if action == "listimage":
		return listImage()

	return "upload"


def uploadImage():	# 上傳圖片邏輯
	resp = { 'state':'SUCCESS','url':'','title':'','original':'' }	# 返回json格式
	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 = UploadService.uploadByFile( upfile )
	if ret['code'] != 200:
		resp['state'] = "上傳失敗:" + ret['msg']
		return jsonify(resp)

	resp['url'] = UrlManager.buildImageUrl( ret['data']['file_key'] )
	return jsonify( resp )

def listImage():	# 圖片列表展示
	resp = { 'state':'SUCCESS','list':[],'start':0 ,'total':0 }

	req = request.values

	start = int( req['start']) if 'start' in req else 0
	page_size = int( req['size']) if 'size' in req else 20

	query = Image.query
	if start > 0:
		query = query.filter( Image.id < start )

	list = query.order_by( Image.id.desc() ).limit( page_size ).all()
	images = []

	if list:
		for item in list:
			images.append( { 'url': UrlManager.buildImageUrl( item.file_key ) } )
			start = item.id
	resp['list'] = images
	resp['start'] = start
	resp['total'] = len( images )
	return jsonify( resp )

在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章