您好,欢迎来到二三娱乐。
搜索
您的当前位置:首页(十四)完成Web App

(十四)完成Web App

来源:二三娱乐

至此已经有一个简陋的Web App demo了,还有一些待开发的功能(*标注)以后一点一点完善。

后端API包括:
获取日志列表:GET /api/blogs
获取日志内容:GET /api/blogs/:id
创建日志:POST /api/blogs
*修改日志:POST /api/blogs/:id
*删除日志:POST /api/blogs/:id/delete
*获取评论:GET /api/comments
*创建评论:POST /api/blogs/:id/comments
*删除评论:POST /api/comments/:id/delete
*获取用户:GET /api/users
创建新用户:POST /api/users

用户浏览页面包括:
首页:GET /
注册页:GET /register
登录页:GET /signin
注销页:GET /signout
日志详情页:GET /blog/:id

管理页面包括:
*评论列表页:GET /manage/comments
日志列表页:GET /manage/blogs
创建日志页:GET /manage/blogs/create
*修改日志页:GET /manage/blogs/edit
*用户列表页:GET /manage/users

handlers.py中增加:

@post('/api/blogs/{id}')
def api_edit_blog(id, request, *, name, summary, content):
    # 需要传入request来检查是否为管理员
    check_admin(request)
    blog = await Blog.find(id)
    # 对于用户输入要记得检查
    if not name or not name.strip():
        raise APIValueError('name', 'name cannot be empty.')
    if not summary or not summary.strip():
        raise APIValueError('summary', 'summary cannot be empty.')
    if not content or not content.strip():
        raise APIValueError('content', 'content cannot be empty.')
    blog.name = name.strip()
    blog.summary = summary.strip()
    blog.content = content.strip()
    await blog.update()
    return blog

@post('/api/blogs/{blog_id}/delete')
def api_delete_blog(id, request):
    check_admin(request)
    blog = await Blog.find(id)
    await blog.remove()
    return dict(id=id)    # 谁来处理?

@get('/api/comments')
async def api_comments(*, page='1'):
    page_index = get_page_index(page)
    num = await Comment.findNumber('count(id)')
    p = Page(num, page_index)
    if num == 0:
        return dict(page=p, comments=())
    comments = await Comment.findAll(orderBy='created_at desc', limit=(p.offset, p.limit))
    return dict(page=p, comments=comments)

@post('/api/blogs/{id}/comments')
async def api_create_comment(id, request, *, content):
    user = request.__user__
    # 永远记得要对用户进行检查
    if user is None:
        raise APIPermissionError('Please signin first.')
    if not content or not content.strip():
        raise APIValueError('content', 'content cannot be empty.')
    # 评论id和created_at不需要指定, 可以自动生成
    blog = await Blog.find(id)
    if blog is None:
        raise APIResourceNotFoundError('Blog')
    comment = Comment(
        blog_id = blog.id,
        user_id = user.id,
        user_name = user.name,
        user_image = user.image,
        content = content.strip()
    )
    await comment.save()
    return comment

@post('/api/comments/:id/delete')
async def api_delete_comment(id, request):
    check_admin(request)
    comment = Comment.find(id)
    if comment is None:
        raise APIResourceNotFoundError('Comment')
    await comment.remove()
    return dict(id=id)

@get('/api/users')
async def api_users(*, page='1'):
    page_index = get_page_index(page)
    num = await User.findNumber('count(id)')
    p = Page(num, page_index)
    if num == 0:
        return dict(page=p, users=())
    users = await User.findAll(orderBy='created_at desc', limit=(p.offset, p.limit))
    return dict(page=p, users=users)

@get('/manage/')    # 首页下面的Manage点击跳转到博客列表
def manage():
    return 'redirect:/manage/blogs'

@get('/manage/comments')
def manage_comments(*, page='1'):
    return {
        '__template__': 'manage_comments.html',
        'page_index': get_page_index(page)
    }

@get('/manage/blogs/edit')
def manage_edit_blog(*, id):
    return {
        # 可以直接使用edit的模板
        '__template__': 'manage_blog_edit.html',
        'id': id,
        'action': '/api/blogs/%s' % id
    }

@get('/manage/users')
def manage_users(*, page='1'):
    return {
        '__template__': 'manage_users.html',
        'page_index': get_page_index(page)
    }

日志内容页面blog.html:

{% extends '__base__.html' %}
{% block title %}{{ blog.name }}{% endblock %}
{% block beforehead %}
<script>
    var comment_url = '/api/blogs/{{ blog.id }}/comments';    // 为id号博客创建一条评论
    $(function () {
        var $form = $('#form-comment');
        $form.submit(function (e) {    // 绑定button的submit事件
            // 在未输入内容时阻止对表单的提交, e是event
            e.preventDefault();
            $form.showFormError('');
            var content = $form.find('textarea').val().trim();
            if (content==='') {
                return $form.showFormError('请输入评论内容!');
            }
            $form.postJSON(comment_url, { content: content }, function (err, result) {
                if (err) {
                    return $form.showFormError(err);
                }
                refresh();
            });
        });
    });
</script>
{% endblock %}
{% block content %}
<div class="uk-width-medium-3-4">
    <article class="uk-article">
        <h2>{{ blog.name }}</h2>
        <p class="uk-article-meta">发表于{{ blog.created_at|datetime }}</p>
        <p>{{ blog.html_content|safe }}</p>
    </article>
    <hr class="uk-article-divider">
{% if __user__ %}
    <h3>发表评论</h3>
    <article class="uk-comment">
        <header class="uk-comment-header">
            <img class="uk-comment-avatar uk-border-circle" width="50" height="50" src="{{ __user__.image }}">
            <h4 class="uk-comment-title">{{ __user__.name }}</h4>
        </header>
        <div class="uk-comment-body">
            <form id="form-comment" class="uk-form">
                <div class="uk-alert uk-alert-danger uk-hidden"></div>
                <div class="uk-form-row">
                     <textarea rows="6" placeholder="说点什么吧" style="width:100%;resize:none;"></textarea>
                </div>
                <div class="uk-form-row">
                    <button type="submit" class="uk-button uk-button-primary"><i class="uk-icon-comment"></i> 发表评论</button>
                </div>
            </form>
        </div>
    </article>
    <hr class="uk-article-divider">
{% endif %}
    <h3>最新评论</h3>
    <ul class="uk-comment-list">
        {% for comment in comments %}
        <li>
            <article class="uk-comment">
                <header class="uk-comment-header">
                    <img class="uk-comment-avatar uk-border-circle" width="50" height="50" src="{{ __user__.image }}">
                    <h4 class="uk-comment-title">{{ comment.user_name }} {% if comment.user_id==blog.user_id %}(作者){% endif %}</h4>
                    <p class="uk-comment-meta">{{ comment.created_at|datetime }}</p>
                </header>
                <div class="uk-comment-body">
                    {{ comment.html_content|safe }}
                </div>
            </article>
        </li>
        {% else %}
        <p>还没有人评论...</p>
        {% endfor %}
    </ul>
</div>
<div class="uk-width-medium-1-4">
    <div class="uk-panel uk-panel-box">
        <div class="uk-text-center">
            <img class="uk-border-circle" width="120" height="120" src="{{ blog.user_image }}">
            <h3>{{ blog.user_name }}</h3>
        </div>
    </div>
    <div class="uk-panel uk-panel-header">
        <h3 class="uk-panel-title">友情链接</h3>
        <ul class="uk-list uk-list-line">
            <li><i class="uk-icon-link"></i> <a href="#">编程</a></li>
            <li><i class="uk-icon-link"></i> <a href="#">思考</a></li>
            <li><i class="uk-icon-link"></i> <a href="#">读书</a></li>
        </ul>
    </div>
</div>
{% endblock %}

效果:

评论列表页面manage_comments.html:

{% extends '__base__.html' %}
{% block title %}评论{% endblock %}
{% block beforehead %}
<script>
    function initVM(data) {
        $('#vm').show();    // 显示隐藏的元素
        var vm = new Vue({
            el: '#vm',
            data: {
                comments: 
                page: data.page
            },
            methods: {
                delete_comment: function (comment) {
                    var content = comment.content.length > 20? commment.content.substring(0, 20) + '...' : comment.content;
                    if (confirm('确认要删除平论“' + comment.content + '”?删除后不可恢复!')) {
                        postJSON('/api/comments/' + comment.id + '/delete', function (err, r) {
                            if (err) {
                                return error(err);    // awesome.js中定义
                            }
                            refresh();
                        });
                    }
                }
            }
        });
    }
    $(function() {
        // awesome.js中的getJSON(url,data,callback)把data中的参数转换为形如"a=1&b=2"的键值对
        // 然后传递给_httpJSON函数提交
        getJSON('/api/comments', {
            // page_index从handlers.py中传来
            page: {{ page_index }}
        }, function (err, results) {
            if (err) {
                return fatal(err);
            }
            $('#loading').hide();
            // results是handlers.py中api_comments函数返回的dict
            initVM(results);
        });
    });
</script>
{% endblock %}
{% block content %}
<div class="uk-width-1-1 uk-margin-bottom">
    <div class="uk-panel uk-panel-box">
        <ul class="uk-breadcrumb">
            <li class="uk-active"><span>评论</span></li>
            <li><a href="/manage/blogs">日志</a></li>
            <li><a href="/manage/users">用户</a></li>
        </ul>
    </div>
</div>
<div id="error" class="uk-width-1-1"></div>
<div id="loading" class="uk-width-1-1 uk-text-center">
    <span><i class="uk-icon-spinner uk-icon-medium uk-icon-spin"></i> 正在加载...</span>
</div>
<div id="vm" class="uk-width-1-1" style="display:none">
    <table class="uk-table uk-table-hover">
        <thead>
            <tr>
                <th class="uk-width-2-10">作者</th>
                <th class="uk-width-5-10">内容</th>
                <th class="uk-width-2-10">创建时间</th>
                <th class="uk-width-1-10">操作</th>
            </tr>
        </thead>
        <tbody>
            <!-- 可以把v-repeat="blog: blogs"看成循环代码, 所以可以在一个<tr>内部引用循环变量blog -->
            <tr v-repeat="comment: comments">
                <td>
                    <span v-text="comment.user_name"></span>
                </td>
                <td>
                    <span v-text="comment.content"></span>
                </td>
                <td>
                    <span v-text="comment.created_at.toDateTime()"></span>
                </td>
                <td>
                    <a href="#0" v-on="click: delete_comment(comment)"><i class="uk-icon-trash-o"></i></a>
                </td>
            </tr>
        </tbody>
    </table>
    <div v-component="pagination" v-with="page"></div>    <!-- awesome.js、__base__.html都定义了, 但是怎么用的? -->
</div>
{% endblock %}

效果:

用户列表页面manage_users.html:

{% extends '__base__.html' %}
{% block title %}用户{% endblock %}
{% block beforehead %}
<script>
    function initVM(data) {
        $('#vm').show();    // 显示隐藏的元素
        var vm = new Vue({
            el: '#vm',
            data: {
                users: data.users,
                page: data.page
            }
        });
    }
    $(function() {
        getJSON('/api/users', {
            page: {{ page_index }}
        }, function (err, results) {
            if (err) {
                return fatal(err);
            }
            $('#loading').hide();
            initVM(results);
        });
    });
</script>
{% endblock %}
{% block content %}
<div class="uk-width-1-1 uk-margin-bottom">
    <div class="uk-panel uk-panel-box">
        <ul class="uk-breadcrumb">
            <li><a href="/manage/comments">评论</a></li>
            <li><a href="/manage/blogs">日志</a></li>
            <li class="uk-active"><span>用户</span></li>
        </ul>
    </div>
</div>
<div id="error" class="uk-width-1-1"></div>
<div id="loading" class="uk-width-1-1 uk-text-center">
    <span><i class="uk-icon-spinner uk-icon-medium uk-icon-spin"></i> 正在加载...</span>
</div>
<div id="vm" class="uk-width-1-1">
    <table class="uk-table uk-table-hover">
        <thead>
            <tr>
                <th class="uk-width-4-10">名字</th>
                <th class="uk-width-4-10">电子邮件</th>
                <th class="uk-width-2-10">注册时间</th>
            </tr>
        </thead>
        <tbody>
            <tr v-repeat="user: users">
                <td>
                    <span v-text="user.name"></span>
                    <span v-if="user.admin" style="color:#d05"><i class="uk-icon-key"></i> 管理员</span>
                </td>
                <td>
                    <a v-attr="href: 'mailto:'+user.email" v-text="user.email"></a>
                </td>
                <td>
                    <span v-text="comment.created_at.toDateTime()"></span>
                </td>
            </tr>
        </tbody>
    </table>
    <div v-component="pagination" v-with="page"></div>    <!-- awesome.js、__base__.html都定义了, 但是怎么用的? -->
</div>
{% endblock %}

效果:

还有其他功能也都测试成功:发表评论、删除评论、

Copyright © 2019- yule263.com 版权所有 湘ICP备2023023988号-1

违法及侵权请联系:TEL:199 1889 7713 E-MAIL:2724546146@qq.com

本站由北京市万商天勤律师事务所王兴未律师提供法律服务