管理信息系统 课程设计

一、系统概要说明

这次项目做到是一个简单的图片网站,就是将自己的图片发出来共享和保存使用的。并且也能浏览其他用户上传的图片,并能进行评论、点赞和收藏的操作。

除了一些基本的用户注册和登陆功能、投稿发布的功能、组合搜索等功能,还有分类显示功能、通过点赞和收藏数展示热门图片和推荐作品、用户头像和用户密码修改功能、图片上传时的图片预览功能、个人主页展示全部作品和收藏评论等功能。

本次系统开发使用的是python的flask框架,我使用了pycharm作为开发工具。作为一个近几年流行起来的语言,flask算是python语言中比较容易上手的一种框架。

二、网站结构设计

本网站一共设置了四大模块,展示模块、搜索模块、作品模块和用户模块。

1、作品展示

这里的作品展示模块,包括了首页展示、热门图片展示、推荐作品展示、分类图片展示四个页面。

首页展示包括了顶部导航和底部导航、页面头部的滚动栏以及下方的所有作品展示。

热门图片包括了顶部和底部导航、头部的广告展示栏和主要的热门图片展示区域。

推荐图片和热门图片相同,只是展示的为推荐的图片。

分类展示是根据点击了不同的下拉选项,从而显示选择的类型的图片。

2、搜索功能

这里的搜索功能是模糊查找,通过在导航栏中的搜索框输入进行搜索。

可以根据作品的标题来查找,也可以根据作者的名字查找,也可以根据分类名称进行查找。

3、作品模块

作品模块包括进行作品的投稿、对作品进行评论、对作品进行收藏以及对作品进行点赞等四个功能

投稿功能可以进行图片的上传,并能在投稿页面中预览上传的文件,并且填写作品名称和选择图片类型。

评论功能即用户可以对作品进行评论,并显示在作品详细页下面,会显示用户的头像、发布时间以及评论内容。

收藏和点赞功能较为雷同,每个用户都能对任意作品进行点赞或收藏,但是每个用户只能对同一个作品点赞或者收藏一次,不可以重复收藏和点赞,同时也就有取消收藏和点赞功能。并且会记录每个作品的收藏和点赞次数,收藏则可以在个人中心找到自己的收藏。

这里设置了需要用户登陆才能对作品进行以上操作。

4、用户模块

用户模块包括用户的登陆和注册、个人中心的展示、头像的修改和密码的修改。

登陆和注册是最基本的创建用户和查询用户的操作。

个人中心展示包括了全部投稿作品、全部收藏、全部评论和用户信息四个栏目。显示了该作者的作品、收藏和评论等信息。

修改头像功能,用户可以对自己头像进行修改,在修改页面可以预览自己上传的头像,并进行上传和跳转。

修改密码功能,用户只有在点进自己的个人中心才能选择修改密码,并且通过输入旧密码和对比两次新密码,从而对用户的密码进行修改。

 

三、模块详细设计

1、展示模块

a、首页展示

主py文件中通过对投稿表的遍历,将值传递给首页,而首页则用for循环将所有作品都展示出来。

这里的首页html中上半部分为首页的滚动栏,下半部为遍历的for循环。

主py文件:

@app.route('/')
def index():
    context={
        'touGao':Tougao.query.order_by('-time').all()
    }
    return render_template("index.html",**context)

首页html:

<div class="container">
    <div class="row clearfix">
        <div class="header">
            <div class="banner">
                 <div class="banner_wrap">
                     <ul class="banner_img clear_fix">
                         <li class="ig"><img src="{{ url_for("static",filename="Img/banner1.jpg") }}" alt=""></li>
                         <li class="ig"><img src="{{ url_for("static",filename="Img/banner2.png") }}" alt=""></li>
                         <li class="ig"><img src="{{ url_for("static",filename="Img/banner3.png") }}" alt=""></li>
                         <li class="ig"><img src="{{ url_for("static",filename="Img/banner4.jpg") }}" alt=""></li>
                     </ul>
                     <div class="banner_left">
                         <img src="{{ url_for("static",filename="Img/向左.png") }}" alt="">
                     </div >
                     <div class="banner_right">
                         <img src="{{ url_for("static",filename="Img/向右.png") }}" alt="">
                     </div>
                </div>
             </div>
        </div>

        <div class="main">
            <ul>
                {% for item in touGao %}
                    <div class="imgbox">
                        <div class="img"><a href="{{ url_for('digital',tougao_id=item.id) }}"><img src="{{ url_for("static",filename="upload/"+item.picturename) }}"></a></div>
                        <div class="title"><p>{{ item.title }}</p></div>
                        <div class="msg">
                            <span><img src="{{ url_for("static",filename="Img/赞.png") }}">{{ item.zan}}</span>&nbsp;&nbsp;&nbsp;
                            <span><img src="{{ url_for("static",filename="Img/_收藏.png") }}">{{item.collection}}</span>
                        </div>
                    </div>
                {% endfor %}

            </ul>
        </div>

首页JS文件(滚动栏自动变换及上下页):

var i=0;
var timer;
$(window).ready(function () {
   $('.ig').eq(0).show().siblings('.ig').hide();
   showTimer();
  $('.banner_left').click(function () {
      clearInterval(timer);
      i--;
      if(i==-1){
          i=3;
      }
      Show();
      showTimer()
  })

    $('.banner_right').click(function () {
        clearInterval(timer);
        i++;
        if(i==4){
            i=0;
        }
        Show();
        showTimer()
    })
});

//会动的定时器
function showTimer() {
     clearInterval(timer)
    timer=setInterval(function () {
        Show();
        i++;
        if(i==4){
            i=0;
        }
    },2500)
}

//会动的方法
function Show() {
    $('.ig').eq(i).fadeIn(300).siblings('.ig').fadeOut(300);
}

b、热门图片展示

主py文件:

@app.route('/hotpic')
def hotpic():
    context={
        'hotTouGao':Tougao.query.order_by('-zan').all()
    }
    return render_template("hotpic.html",**context)

2、搜索模块

主py文件:

@app.route("/search")#搜索方法
def search():
    q=request.args.get("q")
    testuser=User.query.filter(User.username.contains(q)).all()#注意这里返回的是list类型
    tes1=User()#设置一个空User对象
    testuserid=[]#设置一个list用于存储包含关键字的用户的id
    for i in range(len(testuser)):#遍历包含关键字的User用户的列表
        tes1=testuser[i]          #将list列表里的每个用户转成User对象进行使用
        testuserid.append(tes1.id)#将User的id存起来
    # 同理获得类别
    testleibie=Fenlei.query.filter(Fenlei.fenleiname.contains(q)).all()
    test2=Fenlei()
    testfenleiid=[]
    for i in range(len(testleibie)):
        test2=testleibie[i]
        testfenleiid.append(test2.id)
    tg=Tougao.query.filter(
        or_(
            Tougao.title.contains(q),
            Tougao.userid.in_(testuserid),#判断userid是否在列表中
            Tougao.fenleiid.in_(testfenleiid)
        )
    ).order_by("-time")
    return render_template("index.html",touGao=tg)

c、分类图片展示

主py文件:

#分类显示页面
@app.route('/list/<fenlei_id>')
def list_fenlei(fenlei_id):
    tougao=Tougao.query.filter(Tougao.fenleiid==fenlei_id).all();
    context={
        'tougao':tougao
    }
    return render_template('fl_list.html', **context)

导航html:

            <li id="dropd"><a href="#">图片类型</a>
                <ul class="show_list">
                    <li><a href={{ url_for("list_fenlei",fenlei_id=1) }}>人物</a></li>
                    <li><a href={{ url_for("list_fenlei",fenlei_id=3) }}>自然风光</a></li>
                    <li><a href={{ url_for("list_fenlei",fenlei_id=6) }}>动物</a></li>
                    <li><a href={{ url_for("list_fenlei",fenlei_id=10) }}>设计元素</a></li>
                </ul>
            </li>

3、作品模块

a、投稿功能

主py文件:

@app.route('/tougao',methods=['GET','POST'])
@log
def tougao():
    if request.method=='GET':
         context = {
            'leiBie': Fenlei.query.all()
         }
         return render_template('tougao.html', **context)
    else:
        file_dir = os.path.join(basedir, app.config['UPLOAD_FOLDER'])
        if not os.path.exists(file_dir):
            os.makedirs(file_dir)
        f = request.files['pic1']  # 从表单的file字段获取文件,file为该表单的name值

        if f and allowed_file(f.filename):  # 判断是否是允许上传的文件类型
            fname = secure_filename(f.filename)
            ext = fname.rsplit('.', 1)[1]  # 获取文件后缀
            unix_time = int(time.time())
            new_filename = str(unix_time) + '.' + ext  # 修改了上传的文件名

            title = request.form.get('tougaotitle')#标题
            leibie = request.form.get('group')#分类名称
            id = User.query.filter(User.username == session.get('user')).first().id#用户id
            fenleiid=Fenlei.query.filter(Fenlei.fenleiname == leibie).first().id#分类id
            tougao = Tougao(title=title,userid=id,picturename=new_filename,fenleiid=fenleiid)
            db.session.add(tougao)
            db.session.commit()
            f.save(os.path.join(file_dir, new_filename))  # 保存文件到upload目录
            return redirect(url_for('index'))
        else:
            return jsonify({"errno": 1001, "错误信息": u"文件类型错误"})

        # return "From docker: {}".format(output.strip())
        return output

投稿html代码:

<div class="panel panel-default">
            <h2>投稿图片</h2>
            <div class="panel-body">

                <form class="form-horizontal" role="form" action="{{ url_for('tougao') }}" method="post" enctype=multipart/form-data>
                    <div class="form-group">

                        <label for="tougaotitle" class="col-sm-2 control-label">投稿标题</label>
                        <div class="col-sm-10">
                            <input type="text" class="form-control" id="tougaotitle" name="tougaotitle">
                        </div>
                    </div>
                    <div class="form-group">
                        <label for="inputfile" class="col-sm-2 control-label">上传图片</label>
                        <div class="col-sm-10">
                            <div id="container"></div>
                             <input type="file" name="pic1" id="pic1" onchange="preview(this)" multiple="multiple"
                                   accept="image/x-png, image/jpg, image/jpeg, image/gif">
                            <br>
                        </div>
                    </div>
                    <div class="form-group">
                        <label for="toutaotext" class="col-sm-2 control-label">投稿简介</label>
                        <div class="col-sm-10">
                            <select name="group" id="group">
                                <option>-请选择-</option>
                                {% for item in leiBie %}
                                    <option>{{ item.fenleiname }}</option>
                                {% endfor %}
                            </select>
                        </div>
                    </div>

                    <div class="form-group">
                        <div class="col-sm-offset-2 col-sm-10">
                            <button type="submit" class="btn btn-default">提交</button>
                        </div>
                    </div>
                </form>
            </div>
        </div>

首页JS:

<script>
    var msg = "您可以上传png, jpg, 或者gif格式的图片";
    var filter = {
        "jpeg": "/9j/4",
        "gif": "R0lGOD",
        "png": "iVBORw"   };
    function preview(file) {
        var container = document.getElementById("container");
        container.innerHTML = "";
        if (window.FileReader) {
            for (var index=0, f; f = file.files[index]; index++) {
                var filereader = new FileReader();
                filereader.onload = function (event) {
                    var srcpath = event.target.result;
                    if (!validateImg(srcpath)) {
                        console.log("H5"+msg);
                    } else {
                        showPreviewImage(srcpath);
                    }
                };
                filereader.readAsDataURL(f);
            }
        } else {
            if (!/.jpg$|.png$|.gif$/i.test(file.value)) {
                console.log("原生"+msg);
            } else {
                showPreviewImage(file.value);
            }
        }
    }
    function validateImg(data) {
        console.log(data);
        var pos = data.indexOf(",") + 1;
        for (var e in filter) {
            if (data.indexOf(filter[e]) === pos) {
                return e;
            }
        }
        return null;
    }

    function showPreviewImage(src) {
        console.log(src);
        var img = document.createElement('img');
        img.src = src;
        img.style = "400px;height:auto;"
        container.appendChild(img);
    }
</script>

b、评论功能

主py文件:

@app.route('/pinglun',methods=['POST'])
@log
def pinglun():
    pl=request.form.get('pingluntext')
    tougao_id=request.form.get('tougaoid')
    user_id=User.query.filter(User.username==session.get('user')).first().id
    pinglun=Pinglun(userid=user_id,tougaoid=tougao_id,content=pl)
    db.session.add(pinglun)
    db.session.commit()
    return redirect(url_for('digital',tougao_id=tougao_id))

c、收藏及点赞功能

主py文件(这里以点赞作为示例,收藏代码和点赞大致相同):

#点赞方法
@app.route('/_dianzan')
def dianzan():
    a = request.args.get('a', 0, type=int)
    b = request.args.get('b', 0, type=int)#投稿ID
    _tougao = Tougao.query.filter(Tougao.id == b).first()
    c = User.query.filter_by(username=session.get('user')).first().id#用户ID
    #判断用户是否已经点赞
    user_zan=Dianzan.query.filter(Dianzan.userid==c,Dianzan.tougaoid==b).first()
    if user_zan:#已经点赞,要取消点赞
        result = a - 1#点赞数减一
        db.session.delete(user_zan)#删除点赞表中的该用户点赞记录
        _tougao.zan=result
        db.session.add(_tougao)
        db.session.commit()
        sta = '-1'
    else:#没有点过赞,要点赞
        result = a + 1
        user_zan=Dianzan(userid=c,tougaoid=b)#新增用户点赞记录
        db.session.add(user_zan)
        _tougao.zan=result
        db.session.add(_tougao)
        db.session.commit()
        sta = '+1'
return jsonify(result=result,sta=sta)

#详情页页面刷新时通过ajax判断显示样式
@app.route('/_fresh')
def shuxin():
    b = request.args.get('b', 0, type=int)  # 投稿ID
    c = User.query.filter_by(username=session.get('user')).first().id  # 用户ID
    # 判断用户是否已经点赞
    user_zan = Dianzan.query.filter(Dianzan.userid == c, Dianzan.tougaoid == b).first()
    # 判断用户是否已经收藏
    user_shou = Shoucang.query.filter(Shoucang.userid == c, Shoucang.tougaoid == b).first()
    if user_zan:  # 已经点赞,要取消点赞
        sta = '-1'
    else:  # 没有点过赞,要点赞
        print('点赞')
        sta = '+1'
    if user_shou:  # 已经点赞,要取消点赞
        sta2 = '-1'
    else:  # 没有点过赞,要点赞
        print('点赞')
        sta2 = '+1'
    return jsonify(sta=sta,sta2=sta2)

点赞html代码:

<div class="_praisebox">
                <div class="_praise" id="praise">
                    <span class="imgbox zan oncur" id="zan_one"></span>
                    <span id="praise-txt" class="_praise-txt">{{ quest.zan }}</span>
                </div>
</div>

点赞JS代码:

$(function() {
        $('#praise').bind('click', function() {
          $.getJSON($SCRIPT_ROOT + '/_dianzan', {
            a: $('span#praise-txt').text(),
            b:$('#digital_id').text(),
            c:$('#digital_author_id').text(),
          }, function(data) {
            $("#praise-txt").text(data.result);
              if (data.sta=='-1'){
                  $("#zan_one").removeClass("imgbox yizan oncur");
                  $("#zan_one").addClass("imgbox zan oncur");
              }else if (data.sta=='+1') {
                  $("#zan_one").removeClass("imgbox zan oncur");
                  $("#zan_one").addClass("imgbox yizan oncur");
                }
          });
          return false;
        });
      });
#页面初始化(判断是否已经点赞,从而给出不一样的图标)
$(function () {
     $.getJSON($SCRIPT_ROOT + '/_fresh',{
        b:$('#digital_id').text(),
        c:$('#digital_author_id').text(),
     },function(data){
      if (data.sta=='+1'){
          $("#zan_one").removeClass("imgbox yizan oncur");
          $("#zan_one").addClass("imgbox zan oncur");
      }else if (data.sta=='-1') {
          $("#zan_one").removeClass("imgbox zan oncur");
          $("#zan_one").addClass("imgbox yizan oncur");
        }
     });
});

4、用户模块

a、登陆及注册功能(以登陆为例)

@app.route('/login',methods=['GET','POST'])
def login():
    if request.method == 'GET':
        return render_template('login.html')
    else:
        username = request.form.get('id')  # 与html页面名字相同
        password = request.form.get('password')
        user = User.query.filter(User.username == username).first()
        if user:
            if user.check_password(password):
                 session['user']=username
                 session.permanent=True
                 return redirect(url_for('index'))
            else:
                return '密码错误'
        else:
            return '用户不存在'

b、个人中心展示功能

@app.route('/author/<user_id>/<tag>')
def author(user_id,tag):
    user=User.query.filter(User.id==user_id).first()
    soucang=Shoucang.query.filter(Shoucang.userid==user_id).all()
    context={
        'user':user,
        'tougao':user.tougao,
        'pinglun':user.pinglun,
        'soucang':soucang
    }
    if tag=='1':
        return render_template('author1.html', **context)
    elif tag=='2':
        return render_template('author2.html', **context)
    elif tag=='3':
        return render_template('author3.html', **context)
    elif tag=='4':
        return render_template('author4.html', **context)

html文件(分为四个板块,分别继承同一个模板,这里列出子页):

baseauthor:

<div class="container">
        <div class="row clearfix divcolor">
            <div class="col-md-8 column">
                <div id="content"><img src="{{ url_for("static",filename="upload/"+user.userpic) }}"></div>
                <div class="information">
                    <h2>{{ user.username }}</h2>
                    <p>投稿总数:{{ tougao|length }}&nbsp;&nbsp;&nbsp;评论数量:{{ pinglun|length }}</p>
                    {% if username == user.username%}
                    <div>
                        <a href="{{ url_for('upload') }}">修改头像</a>
                        <a href="{{ url_for('changepas') }}">修改密码</a>
                        <div class="clear"></div>
                    </div>
                    {% endif %}
                </div>
                <div class="clear"></div>
                <hr>
                <ul class="nav nav-pills" role="tablist">
                    <li class="active"><a href="{{ url_for('author',user_id=user.id,tag=1) }}">全部投稿</a></li>
                    <li><a href="{{ url_for('author',user_id=user.id,tag=4) }}">全部收藏</a></li>
                    <li><a href="{{ url_for('author',user_id=user.id,tag=2) }}">全部评论</a></li>
                    <li><a href="{{ url_for('author',user_id=user.id,tag=3) }}">用户信息</a></li>
                </ul>
            <br>
                {% block author %}{% endblock %}
            </div>
            <div class="col-md-4 column">
            </div>
        </div>
    </div>

第一个子页author1:

<ul>
        {% for item in tougao %}
            <div class="imgbox">
                <div class="img"><a href="{{ url_for('digital',tougao_id=item.id) }}"><img src="{{ url_for("static",filename="upload/"+item.picturename) }}"></a></div>
                <div class="title"><p>{{ item.title }}</p></div>
            </div>

        {% endfor %}
    </ul>

c、修改头像功能

主py文件:

# 跳转到上传页面
@app.route('/upload',methods=['GET'],strict_slashes=False)
def upload():
    return render_template("upload.html")

# 用于判断文件后缀
def allowed_file(filename):
    return '.' in filename and filename.rsplit('.',1)[1] in ALLOWED_EXTENSIONS

# 上传文件
@app.route('/upload',methods=['POST'],strict_slashes=False)
def api_upload():
    file_dir = os.path.join(basedir, app.config['UPLOAD_FOLDER'])
    if not os.path.exists(file_dir):
        os.makedirs(file_dir)
    f = request.files['pic1']  # 从表单的file字段获取文件,file为该表单的name值

    if f and allowed_file(f.filename):  # 判断是否是允许上传的文件类型
        fname = secure_filename(f.filename)
        ext = fname.rsplit('.',1)[1]  # 获取文件后缀
        unix_time = int(time.time())
        new_filename = str(unix_time)+'.'+ext  # 修改了上传的文件名
        # 修改数据库中的图片名称
        user1 = User.query.filter_by(username=session.get('user')).first()
        user1.userpic=new_filename
        db.session.commit()
        f.save(os.path.join(file_dir, new_filename))  #保存文件到upload目录
        return render_template('testresult.html')
    else:
        return jsonify({"errno": 1001, "errmsg": u"failed"})

    #return "From docker: {}".format(output.strip())
    return output

d、修改密码功能

主py文件:

#修改密码的方法
@app.route('/changepas',methods=['GET','POST'])
def changepas():
    if request.method == 'GET':
        return render_template("changepas.html")
    else:
        _password = request.form.get('password')  # 与html页面名字相同
        _newpassword = request.form.get('newpassword')
        user1 = User.query.filter_by(username=session.get('user')).first()
        if user1:#判断用户是否存在
            if user1.check_password(_password):#判断密码是否正确
                user1.password = _newpassword#修改密码
                db.session.commit()
                return render_template('testresult.html')#返回个人主页
            else:
                return '密码错误'
        else:
            return '用户不存在'

四、数据库设计

本次项目一共有六个表,分别是用户表、投稿表、评论表、点赞表、收藏表和分类表。

用户表(User)

字段名

表示内容

字段类型

备注

id

主键

整型

自动生成

username

用户名

字符型

password

密码

字符型

已加密的密码

userpic

用户头像图片名称

字符型

投稿表(Tougao)

字段名

表示内容

字段类型

备注

id

主键

整型

自动生成

userid

用户ID

整型

外键,用户表主键

title

投稿标题

字符型

time

发布时间

datatime型

zan

用户点赞数

整型

默认为0

collection

用户收藏数

整型

默认为0

picturename

作品图片名称

字符型

包括文件后缀

fenleiid

作品分类id

整型

外键,分类表主键

评论表(Pinglun)

字段名

表示内容

字段类型

备注

id

主键

整型

自动生成

userid

用户ID

整型

外键,用户表主键

tougaoid

投稿ID

整型

外键,投稿表主键

time

评论时间

datatime型

content

评论内容

字符型

分类表(Fenlei)

字段名

表示内容

字段类型

备注

id

主键

整型

自动生成

fenleiname

分类名称

字符型

点赞表(Dianzan)

字段名

表示内容

字段类型

备注

id

主键

整型

自动生成

userid

用户ID

整型

外键,用户表主键

tougaoid

投稿ID

整型

外键,用户表主键


收藏表(Soucang)

字段名

表示内容

字段类型

备注

id

主键

整型

自动生成

userid

用户ID

整型

外键,用户表主键

tougaoid

投稿ID

整型

外键,用户表主键

 

五、系统实现的关键算法与数据结构

1、排序算法

这里的系统用了许多排序算法,通过查询时间、点赞数或收藏数来对投稿作品的展示进行排序。

@app.route('/')
def index():
    context={
        'touGao':Tougao.query.order_by('-time').all()
    }
    return render_template("index.html",**context)

2、加密算法

设置用户密码的时候分成内部使用和外部使用两种,内部使用的_password是管理员都看不见的,只能使用系统加密后的password进行使用,从而提高了用户的账户安全性。

class User(db.Model):

    __tablename__='user'

    id = db.Column(db.Integer,primary_key=True,autoincrement=True)

    username=db.Column(db.String(20),nullable=False)

    _password=db.Column(db.String(200),nullable=False)

    userpic=db.Column(db.String(200),nullable=True,default="moren.jpg")

 

    @property

    def password(self):#外部使用

        return self._password

 

    @password.setter

    def password(self,row_password):

        self._password=generate_password_hash(row_password)

 

    def check_password(self,row_password):

        result=check_password_hash(self._password,row_password)

        return result

六、成品展示

1、首页展示:

2、作品详情页展示:

3、热门图片展示:

4、分类导航下拉选项:

5、投稿页面:

6、个人主页:

7、修改头像页:

8、修改密码功能:

原文地址:https://www.cnblogs.com/Naiky/p/9189693.html