diff --git a/.idea/workspace.xml b/.idea/workspace.xml index 5e1f54f..8b63f33 100644 --- a/.idea/workspace.xml +++ b/.idea/workspace.xml @@ -2,20 +2,22 @@ - - - - - - - - - + + + + + + + + + - - + + + + \ No newline at end of file diff --git a/application.py b/application.py index 693396a..93717d4 100644 --- a/application.py +++ b/application.py @@ -23,4 +23,5 @@ manager = Manager( app ) from common.libs.UrlManager import UrlManager app.add_template_global(UrlManager.buildStaticUrl, 'buildStaticUrl') app.add_template_global(UrlManager.buildUrl, 'buildUrl') +app.add_template_global(UrlManager.buildImageUrl, 'buildImageUrl') diff --git a/common/libs/Helper.py b/common/libs/Helper.py index ef1a4ba..e70801d 100644 --- a/common/libs/Helper.py +++ b/common/libs/Helper.py @@ -65,3 +65,63 @@ def ops_render( template,context = {} ): def getCurrentDate( format = "%Y-%m-%d %H:%M:%S"): #return datetime.datetime.now().strftime( format ) return datetime.datetime.now() + +''' +获取格式化的时间 +''' +def getFormatDate( date = None ,format = "%Y-%m-%d %H:%M:%S" ): + if date is None: + date = datetime.datetime.now() + + return date.strftime( format ) + + +''' +根据某个字段获取一个dic出来 +''' +def getDictFilterField( db_model,select_filed,key_field,id_list ): + ret = {} + query = db_model.query + if id_list and len( id_list ) > 0: + query = query.filter( select_filed.in_( id_list ) ) + + list = query.all() + if not list: + return ret + for item in list: + if not hasattr( item,key_field ): + break + + ret[ getattr( item,key_field ) ] = item + return ret + + + +def selectFilterObj( obj,field ): + ret = [] + for item in obj: + if not hasattr(item, field ): + break + if getattr( item,field ) in ret: + continue + ret.append( getattr( item,field ) ) + return ret + + +def getDictFilterField( db_model,select_filed,key_field,id_list ): + ret = {} + query = db_model.query + if id_list and len( id_list ) > 0: + query = query.filter( select_filed.in_( id_list ) ) + + list = query.all() + if not list: + return ret + for item in list: + if not hasattr( item,key_field ): + break + if getattr( item,key_field ) not in ret: + ret[getattr(item, key_field)] = [] + + ret[ getattr( item,key_field ) ].append(item ) + return ret \ No newline at end of file diff --git a/common/libs/UploadService.py b/common/libs/UploadService.py new file mode 100644 index 0000000..1d13cb6 --- /dev/null +++ b/common/libs/UploadService.py @@ -0,0 +1,41 @@ +# -*- 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'] + resp = { 'code':200,'msg':'操作成功~~','data':{} } + filename = secure_filename( file.filename ) + ext = filename.rsplit(".",1)[1] + if ext not in config_upload['ext']: + resp['code'] = -1 + resp['msg'] = "不允许的扩展类型文件" + return resp + + + root_path = app.root_path + config_upload['prefix_path'] + #不使用getCurrentDate创建目录,为了保证其他写的可以用,这里改掉,服务器上好像对时间不兼容 + 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 \ No newline at end of file diff --git a/common/libs/UrlManager.py b/common/libs/UrlManager.py index b165778..dcebb41 100644 --- a/common/libs/UrlManager.py +++ b/common/libs/UrlManager.py @@ -14,4 +14,10 @@ class UrlManager(object): release_version = app.config.get('RELEASE_VERSION') ver = "%s" % (int(time.time())) if not release_version else release_version path = "/static" + path + "?ver=" + ver - return UrlManager.buildUrl( path ) \ No newline at end of file + return UrlManager.buildUrl( path ) + + @staticmethod + def buildImageUrl( path ): + app_config = app.config['APP'] + url = app_config['domain'] + app.config['UPLOAD']['prefix_url'] + path + return url \ No newline at end of file diff --git a/common/libs/food/FoodService.py b/common/libs/food/FoodService.py new file mode 100644 index 0000000..865ffa8 --- /dev/null +++ b/common/libs/food/FoodService.py @@ -0,0 +1,28 @@ +# -*- coding: utf-8 -*- +from application import app,db +from common.models.food.FoodStockChangeLog import FoodStockChangeLog +from common.models.food.Food import Food +from common.libs.Helper import getCurrentDate +class FoodService(): + + @staticmethod + def setStockChangeLog( food_id = 0,quantity = 0,note = '' ): + + if food_id < 1: + return False + + food_info = Food.query.filter_by( id = food_id ).first() + if not food_info: + return False + + model_stock_change = FoodStockChangeLog() + model_stock_change.food_id = food_id + model_stock_change.unit = quantity + model_stock_change.total_stock = food_info.stock + model_stock_change.note = note + model_stock_change.created_time = getCurrentDate() + db.session.add(model_stock_change) + db.session.commit() + return True + + diff --git a/common/models/Image.py b/common/models/Image.py new file mode 100644 index 0000000..0ba5cf9 --- /dev/null +++ b/common/models/Image.py @@ -0,0 +1,12 @@ +# coding: utf-8 +from sqlalchemy import Column, DateTime, Integer, String +from sqlalchemy.schema import FetchedValue + +from application import db + +class Image(db.Model): + __tablename__ = 'images' + + id = db.Column(db.Integer, primary_key=True) + file_key = db.Column(db.String(60), nullable=False, server_default=db.FetchedValue()) + created_time = db.Column(db.DateTime, nullable=False, server_default=db.FetchedValue()) diff --git a/config/base_setting.py b/config/base_setting.py index dcd9e76..57d711b 100644 --- a/config/base_setting.py +++ b/config/base_setting.py @@ -33,4 +33,14 @@ MINA_APP = { 'paykey':'xxxxxxxxxxxxxx换自己的', 'mch_id':'xxxxxxxxxxxx换自己的', 'callback_url':'/api/order/callback' +} + +UPLOAD = { + 'ext':[ 'jpg','gif','bmp','jpeg','png' ], + 'prefix_path':'/web/static/upload/', + 'prefix_url':'/static/upload/' +} + +APP = { + 'domain':'http://127.0.0.1:9000' } \ No newline at end of file diff --git a/web/controllers/food/Food.py b/web/controllers/food/Food.py index d00d64e..976f70d 100644 --- a/web/controllers/food/Food.py +++ b/web/controllers/food/Food.py @@ -1,24 +1,150 @@ # -*- coding: utf-8 -*- -from flask import Blueprint,request,jsonify +from flask import Blueprint,request,jsonify,redirect +from common.models.food.Food import Food from common.libs.Helper import ops_render from common.models.food.FoodCat import FoodCat from application import app,db -from common.libs.Helper import getCurrentDate - +from common.libs.Helper import getCurrentDate,iPagination,getDictFilterField +from common.libs.food.FoodService import FoodService +from common.libs.UrlManager import UrlManager +from decimal import Decimal +from sqlalchemy import or_ route_food = Blueprint( 'food_page',__name__ ) @route_food.route( "/index" ) def index(): - return ops_render( "food/index.html" ) + resp_data = {} + req = request.values + page = int(req['p']) if ('p' in req and req['p']) else 1 + query = Food.query + if 'mix_kw' in req: + rule = or_(Food.name.ilike("%{0}%".format(req['mix_kw'])), Food.tags.ilike("%{0}%".format(req['mix_kw']))) + query = query.filter( rule ) + + if 'status' in req and int( req['status'] ) > -1 : + query = query.filter( Food.status == int( req['status'] ) ) + + if 'cat_id' in req and int( req['cat_id'] ) > 0 : + query = query.filter( Food.cat_id == int( req['cat_id'] ) ) + + 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( Food.id.desc() ).offset( offset ).limit( app.config['PAGE_SIZE'] ).all() + + cat_mapping = getDictFilterField( FoodCat,FoodCat.id,"id",[] ) + resp_data['list'] = list + resp_data['pages'] = pages + resp_data['search_con'] = req + resp_data['status_mapping'] = app.config['STATUS_MAPPING'] + resp_data['cat_mapping'] = cat_mapping + resp_data['current'] = 'index' + return ops_render( "food/index.html",resp_data ) + @route_food.route( "/info" ) def info(): return ops_render( "food/info.html" ) -@route_food.route( "/set" ) +@route_food.route( "/set" ,methods = [ 'GET','POST'] ) def set(): - return ops_render( "food/set.html" ) + if request.method == "GET": + resp_data = {} + req = request.args + id = int(req.get('id', 0)) + info = Food.query.filter_by(id=id).first() + if info and info.status != 1: + return redirect(UrlManager.buildUrl("/food/index")) + + cat_list = FoodCat.query.all() + resp_data['info'] = info + resp_data['cat_list'] = cat_list + resp_data['current'] = 'index' + return ops_render("food/set.html", resp_data) + + resp = {'code': 200, 'msg': '操作成功~~', 'data': {}} + req = request.values + id = int(req['id']) if 'id' in req and req['id'] else 0 + cat_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 '' + + if cat_id < 1: + resp['code'] = -1 + resp['msg'] = "请选择分类~~" + return jsonify(resp) + + if name is None or len(name) < 1: + resp['code'] = -1 + resp['msg'] = "请输入符合规范的名称~~" + return jsonify(resp) + + if not price or len( price ) < 1: + resp['code'] = -1 + resp['msg'] = "请输入符合规范的售卖价格~~" + return jsonify(resp) + + price = Decimal(price).quantize(Decimal('0.00')) + if price <= 0: + resp['code'] = -1 + resp['msg'] = "请输入符合规范的售卖价格~~" + 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) < 3: + resp['code'] = -1 + resp['msg'] = "请输入图书描述,并不能少于10个字符~~" + return jsonify(resp) + + if stock < 1: + resp['code'] = -1 + resp['msg'] = "请输入符合规范的库存量~~" + return jsonify(resp) + + if tags is None or len(tags) < 1: + resp['code'] = -1 + resp['msg'] = "请输入标签,便于搜索~~" + return jsonify(resp) + food_info = Food.query.filter_by(id=id).first() + before_stock = 0 + if food_info: + model_food = food_info + before_stock = model_food.stock + else: + model_food = Food() + model_food.status = 1 + model_food.created_time = getCurrentDate() + + model_food.cat_id = cat_id + model_food.name = name + model_food.price = price + model_food.main_image = main_image + model_food.summary = summary + model_food.stock = stock + model_food.tags = tags + model_food.updated_time = getCurrentDate() + + db.session.add(model_food) + ret = db.session.commit() + + FoodService.setStockChangeLog(model_food.id, int(stock) - int(before_stock), "后台修改") + return jsonify(resp) @route_food.route( "/cat" ) diff --git a/web/controllers/upload/Upload.py b/web/controllers/upload/Upload.py new file mode 100644 index 0000000..c829766 --- /dev/null +++ b/web/controllers/upload/Upload.py @@ -0,0 +1,91 @@ +# -*- 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__) + +''' +参考文章:https://segmentfault.com/a/1190000002429055 +''' + +@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" + +@route_upload.route("/pic",methods = [ "GET","POST" ]) +def uploadPic(): + file_target = request.files + upfile = file_target['pic'] if 'pic' in file_target else None + callback_target = 'window.parent.upload' + if upfile is None: + return "".format( callback_target,"上传失败" ) + + ret = UploadService.uploadByFile(upfile) + if ret['code'] != 200: + return "".format(callback_target, "上传失败:" + ret['msg']) + + return "".format(callback_target,ret['data']['file_key'] ) + +def uploadImage(): + 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 = 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 ) + diff --git a/web/static/js/common.js b/web/static/js/common.js index 7030ffc..0e7fd9d 100644 --- a/web/static/js/common.js +++ b/web/static/js/common.js @@ -150,7 +150,11 @@ var common_ops = { } return url + _paramUrl; }, - + buildPicUrl:function( img_key ){ + var domain = $(".hidden_layout_wrap input[name=domain]").val(); + var prefix_url = $(".hidden_layout_wrap input[name=prefix_url]").val(); + return domain + prefix_url + img_key; + } }; $(document).ready( function() { diff --git a/web/static/upload/20190802/1ed32d7ec80f4fee91b56065ebc6687f.png b/web/static/upload/20190802/1ed32d7ec80f4fee91b56065ebc6687f.png new file mode 100644 index 0000000..9af4901 Binary files /dev/null and b/web/static/upload/20190802/1ed32d7ec80f4fee91b56065ebc6687f.png differ diff --git a/web/static/upload/20190802/23fa0d93ffea4a1fadd6c924bc54da39.png b/web/static/upload/20190802/23fa0d93ffea4a1fadd6c924bc54da39.png new file mode 100644 index 0000000..9af4901 Binary files /dev/null and b/web/static/upload/20190802/23fa0d93ffea4a1fadd6c924bc54da39.png differ diff --git a/web/static/upload/20190802/245bc835a2514d0e9c1aaa12a5c8672e.png b/web/static/upload/20190802/245bc835a2514d0e9c1aaa12a5c8672e.png new file mode 100644 index 0000000..237012d Binary files /dev/null and b/web/static/upload/20190802/245bc835a2514d0e9c1aaa12a5c8672e.png differ diff --git a/web/static/upload/20190802/57cf16593a3c4398a9503cb80c924da9.png b/web/static/upload/20190802/57cf16593a3c4398a9503cb80c924da9.png new file mode 100644 index 0000000..237012d Binary files /dev/null and b/web/static/upload/20190802/57cf16593a3c4398a9503cb80c924da9.png differ diff --git a/web/static/upload/20190802/6d6d7c6f3af7493f9af856d6c65dd3d9.png b/web/static/upload/20190802/6d6d7c6f3af7493f9af856d6c65dd3d9.png new file mode 100644 index 0000000..d54a78e Binary files /dev/null and b/web/static/upload/20190802/6d6d7c6f3af7493f9af856d6c65dd3d9.png differ diff --git a/web/static/upload/20190802/80c3611a2d5541998e97bc3d9e282b36.png b/web/static/upload/20190802/80c3611a2d5541998e97bc3d9e282b36.png new file mode 100644 index 0000000..d54a78e Binary files /dev/null and b/web/static/upload/20190802/80c3611a2d5541998e97bc3d9e282b36.png differ diff --git a/web/static/upload/20190802/8b6113a5bfd1486f9756d99d0f6612ab.png b/web/static/upload/20190802/8b6113a5bfd1486f9756d99d0f6612ab.png new file mode 100644 index 0000000..237012d Binary files /dev/null and b/web/static/upload/20190802/8b6113a5bfd1486f9756d99d0f6612ab.png differ diff --git a/web/static/upload/20190802/92081b8b866d47959666cc6306a3ab64.png b/web/static/upload/20190802/92081b8b866d47959666cc6306a3ab64.png new file mode 100644 index 0000000..9af4901 Binary files /dev/null and b/web/static/upload/20190802/92081b8b866d47959666cc6306a3ab64.png differ diff --git a/web/static/upload/20190802/9d86635022eb430da950f1069665186f.png b/web/static/upload/20190802/9d86635022eb430da950f1069665186f.png new file mode 100644 index 0000000..d54a78e Binary files /dev/null and b/web/static/upload/20190802/9d86635022eb430da950f1069665186f.png differ diff --git a/web/static/upload/20190802/a0704e7fdbf74dc3a98169a9602e22b8.png b/web/static/upload/20190802/a0704e7fdbf74dc3a98169a9602e22b8.png new file mode 100644 index 0000000..237012d Binary files /dev/null and b/web/static/upload/20190802/a0704e7fdbf74dc3a98169a9602e22b8.png differ diff --git a/web/static/upload/20190802/ced439416a7d49b6a694dd0c44cf04f4.png b/web/static/upload/20190802/ced439416a7d49b6a694dd0c44cf04f4.png new file mode 100644 index 0000000..d54a78e Binary files /dev/null and b/web/static/upload/20190802/ced439416a7d49b6a694dd0c44cf04f4.png differ diff --git a/web/static/upload/20190802/ea3743a978504fa0988fed9b2b3686a0.png b/web/static/upload/20190802/ea3743a978504fa0988fed9b2b3686a0.png new file mode 100644 index 0000000..d54a78e Binary files /dev/null and b/web/static/upload/20190802/ea3743a978504fa0988fed9b2b3686a0.png differ diff --git a/web/static/upload/20190802/f732e82251b141a798f6225718b65639.png b/web/static/upload/20190802/f732e82251b141a798f6225718b65639.png new file mode 100644 index 0000000..9af4901 Binary files /dev/null and b/web/static/upload/20190802/f732e82251b141a798f6225718b65639.png differ diff --git a/web/static/upload/readme.md b/web/static/upload/readme.md new file mode 100644 index 0000000..bbf248d --- /dev/null +++ b/web/static/upload/readme.md @@ -0,0 +1 @@ +上传目录upload \ No newline at end of file diff --git a/web/templates/common/layout_main.html b/web/templates/common/layout_main.html index bbcb1be..b5bb3a8 100644 --- a/web/templates/common/layout_main.html +++ b/web/templates/common/layout_main.html @@ -104,6 +104,10 @@ {% block content %}{% endblock %} + diff --git a/web/templates/food/index.html b/web/templates/food/index.html index b5d9b8d..c6b6399 100644 --- a/web/templates/food/index.html +++ b/web/templates/food/index.html @@ -1,19 +1,6 @@ {% extends "common/layout_main.html" %} {% block content %} -
-
-
- -
-
-
+{% include "common/tab_food.html" %}
+ {% if info and info.main_image %} + + + + + {% endif %}
@@ -58,32 +53,50 @@
- +
- +
- +
- +
+ {% endblock %} +{% block css %} + + +{% endblock %} +{% block js %} + + + + + + + + + + + +{% endblock %} \ No newline at end of file diff --git a/www.py b/www.py index 24270ec..60f50f2 100644 --- a/www.py +++ b/www.py @@ -20,6 +20,7 @@ from web.controllers.food.Food import route_food from web.controllers.member.Member import route_member from web.controllers.stat.Stat import route_stat from web.controllers.api import route_api +from web.controllers.upload.Upload import route_upload app.register_blueprint( route_index,url_prefix = "/" ) app.register_blueprint( route_user,url_prefix = "/user" ) app.register_blueprint( route_static,url_prefix = "/static" ) @@ -29,3 +30,4 @@ app.register_blueprint(route_member,url_prefix="/member") app.register_blueprint(route_food,url_prefix="/food") app.register_blueprint(route_stat,url_prefix="/stat") app.register_blueprint( route_api,url_prefix = "/api" ) +app.register_blueprint( route_upload,url_prefix = "/upload" )