python web开发实战(7)--前端页面编写

在完成web应用的基本框架后,本文将介绍如何使用uikit进行前端页面的编写。内容包括:uikit资源文件的引入,templates文件夹中各页面的创建,如__base__.html、blog.html等,以及在app.py中补充中间件,最终运行app.py完成博客网站的搭建。
部署运行你感兴趣的模型镜像

1、通过前面的课程我们把一个web application的框架结构都搭好了,后续就是前端页面的编写,让显示的页面更加丰度。

我们前端渲染用uikit,uikit首页下载打包的资源文件,解压到static文件夹下

其中awesome开头的文件以及js下有几个文件是我们自定义的,可以直接拷贝复制。

2、templates文件夹下页面添加

所有页面都加载的基础模板 __base__.html

<!DOCTYPE html>
<!--处理分页导航栏代码-->
{% macro pagination(page) %}
    <ul class="uk-pagination uk-flex-center uk-margin-medium-top uk-margin-large-bottom">
        {% if page.has_previous %}
            <li><a href="?page={{ page.page_index - 1 }}"><span uk-pagination-previous></span></a></li>
        {% else %}
            <li class="uk-disabled"><a href="#"><span uk-pagination-previous></span></a></li>
        {% endif %}
            <li class="uk-active"><span>{{ page.page_index }}</span></li>
        {% if page.has_next %}
            <li><a href="?page={{ page.page_index + 1 }}"><span uk-pagination-next></span></a></li>
        {% else %}
            <li class="uk-disabled"><a href="#"><span uk-pagination-next></span></a></li>
        {% endif %}
    </ul>
{% endmacro %}

<!--导航页代码-->
<html>
<head>
    <meta charset="utf-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="HandheldFriendly" content="True">
    <meta name="viewport" content="width=device-width,initial-scale=0.9,minimum-scale=0.9,maximum-scale=0.9,user-scalable=no">
    <meta name="wap-font-scale" content="no">
    <!--jinja2 meta块-->
    {% block meta %}<!-- block meta  -->{% endblock %}
    <!--jinja2 title块-->
    <title>{% block title %} ? {% endblock %}| AwesomeLeeYYBlog</title>
    <link rel="stylesheet" href="/static/css/uikit.min.css">
    <link rel="stylesheet" href="/static/css/awesome.css" />
    <script src="/static/js/jquery.min.js"></script>
    <script src="/static/js/sha1.min.js"></script>
    <script src="/static/js/uikit.min.js"></script>
    <script src="/static/js/icons.min.js"></script>
    <script src="/static/js/sticky.min.js"></script>
    <script src="/static/js/vue.min.js"></script>
    <script src="/static/js/awesome.js"></script>
    <!--jinja2 beforehead块-->
    {% block beforehead %}<!-- before head  -->{% endblock %}
</head>

<!--导航页正文内容-->
<body>
    <!--"uk-"开头的都是UIkit里的组件,具体请参考UIkit官网的Documents详解-->
    <!--uk-visible@m是大于中等尺寸屏幕时显示的UI-->
    <div class="uk-margin uk-visible@m" style="background-color:rgba(100,150,185,0);">
    <div class="uk-container uk-container-medium">
    <!--导航栏UI-->
    <nav class="uk-navbar-container" uk-navbar style="background-color:rgba(255,255,255,0);">
        <div class="uk-navbar-left uk-margin-medium-top uk-margin-medium-bottom">
            <a class="uk-navbar-item uk-logo uk-margin-left" href="/">
                <!--此处uk-icon为图标,读者可以选UIkit自带icon,也可以添加自定义icon重新打包uk-icon.js,详见官网Documentation-->
                <span class="uk-icon uk-margin-small-right" uk-icon="pagekit" ratio="2"></span>
                 LEEYY
            </a>
            <ul class="uk-navbar-nav">
                <li><a href="/"> Article | 日志</a></li>
                <li><a href="https://blog.youkuaiyun.com/BurstLinking/article/details/107023806"> Tutorial | 教程</a></li>
                <li><a href="https://github.com/burstlink/awesome-python3-webapp2/"> SourceCode | 源码</a></li>
            </ul>
        </div>
        <div class="uk-navbar-right uk-margin-medium-top uk-margin-medium-bottom">
            <ul class="uk-navbar-nav">
            {% if __user__ %}
                <li>
                    <a href="#0"> {{ __user__.name }}</a>
                    <div class="uk-navbar-dropdown">
                        <ul class="uk-nav uk-navbar-dropdown-nav">
                            <li><a href="/manage/"> Manage</a></li>
                            <li><a href="/signout"> Logout</a></li>
                        </ul>
                    </div>
                </li>
            {% else %}
                <li><a href="/signin"> Login | 登陆</a></li>
                <li><a href="/register"> Register | 注册</a></li>
            {% endif %}
            </ul>
        </div>
    </nav>
    </div>
    </div>

    <!--uk-hidden@m是小于中等尺寸屏幕时显示的UI-->
    <nav class="uk-navbar-container uk-margin-medium uk-hidden@m" uk-navbar style="background-color:rgba(255,255,255,0);">
        <div class="uk-navbar-left">
            <a class="uk-navbar-item uk-logo" href="/">
                <span class="uk-icon uk-margin-small-right" uk-icon="pagekit" ratio="2"></span>
                 LEEYY
            </a>
        </div>
        <div class="uk-navbar-right">
            <ul class="uk-navbar-nav">
            <li>
            <a class="uk-navbar-toggle" uk-toggle="target: #offcanvas-nav" uk-navbar-toggle-icon></a>

            <div id="offcanvas-nav" uk-offcanvas="overlay: true; flip: true">
            <div class="uk-offcanvas-bar uk-flex uk-flex-column">
            <ul class="uk-nav uk-nav-default uk-margin-auto-vertical">
                <li><a href="/"> Article | 日志</a></li>
                <li><a href="https://blog.youkuaiyun.com/BurstLinking/article/details/107023806"> Tutorial | 教程</a></li>
                <li><a href="https://github.com/burstlink/awesome-python3-webapp2/"> SourceCode | 源码</a></li>
                {% if __user__ %}
                <li><a href="/manage/">Manage | 管理</a></li>
                <li><a href="/signout"> Logout | 注销</a></li>
                {% else %}
                <li><a href="/signin"> Login | 登陆</a></li>
                <li><a href="/register"> Register | 注册</a></li>
                {% endif %}
            </ul>
            </div>
            </div>
            </li>
            </ul>
        </div>
    </nav>


    <div class="uk-container uk-container-medium">
            <!-- jinja2 content块 -->
            {% block content %}
            {% endblock %}
    </div>

    <!-- 页面底部图标栏和网站信息 -->
    <div class="uk-margin-medium">
    <div class="uk-container uk-container-center uk-text-center">
        <p>
            <a target="_blank" href="https://github.com/burstlink" class="uk-icon-button uk-margin-small-right" ratio="1.1" uk-icon="github"></a>
            <a target="_blank" href="#" class="uk-icon-button uk-margin-small-right" ratio="1.1" uk-icon="instagram"></a>
            <a target="_blank" href="#" class="uk-icon-button uk-margin-small-right" ratio="1.2" uk-icon="twitter"></a>
            <a target="_blank" href="#" class="uk-icon-button uk-margin-small-right" ratio="1.2" uk-icon="google-plus"></a>
            <a target="_blank" href="#" class="uk-icon-button uk-margin-small-right" ratio="1.1" uk-icon="linkedin"></a>
        </p>
        <p class="uk-text-meta" style="line-height: 10px; padding: 10px 0; margin: 8px 0;">Powered by <a href="/">AwesomePythonWebBlog!</a> Copyright &copy; 2020.</p>
        <p class="uk-text-meta" style="line-height: 0px; padding: 0px 0; margin: 0px 0;"><a href="/" target="_blank">Leeyy</a>. All rights reserved.</p>
    </div>
    </div>

</body>
</html>

blog.html

<!-- 继承父模板 '__base__.html' -->
{% extends '__base__.html' %}
<!--jinja2 title 块内容替换-->
{% block title %}{{ blog.name }}{% endblock %}
<!--jinja2 beforehead 块内容替换-->
{% block beforehead %}
<!--script中构建vue,向后端API提交日志评论相关数据-->
<script>

var comment_url = '/api/blogs/{{ blog.id }}/comments';

$(function () {
    var $form = $('#form-comment');
    $form.submit(function (e) {
        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 %}

<!--jinja2 content 块内容替换-->
{% block content %}
    <div class="uk-grid  uk-visible@m">
    <div class="uk-width-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>

    <!--日志评论区-->
    {% 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">
                    <fieldset class="uk-fieldset">
                        <div class="uk-alert uk-alert-danger uk-hidden"></div>
                        <div class="uk-margin">
                            <textarea class="uk-textarea" rows="6" placeholder="说点什么吧"></textarea>
                        </div>
                        <div class="uk-margin">
                            <button type="submit" class="uk-button uk-button-primary"> 发表评论</button>
                        </div>
                    </fieldset>
                </form>
            </div>
        </article>

        <hr>
    {% 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="{{ comment.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-1-4 uk-visible@m">
        <div class="uk-card uk-card-default">
            <div class="uk-card-body">
            <div class="uk-text-center">
                <img class="uk-border-circle" width="120" height="120" src="{{ blog.user_image }}">
                <h4>{{ blog.user_name }}</h4>
            </div>
            </div>
        </div>
    </div>
    </div>

    <div class="uk-hidden@m">
        <article class="uk-article">
            <h3>{{ blog.name }}</h3>
            <p class="uk-article-meta">{{ blog.user_name }} 发表于{{ blog.created_at|datetime }}</p>
            <p>{{ blog.html_content|safe }}</p>
        </article>

        <hr>

    {% if __user__ %}
        <h4>发表评论</h4>

        <article class="uk-comment">
            <header class="uk-comment-header">
                <img class="uk-comment-avatar uk-border-circle" width="50" height="50" src="{{ __user__.image }}">
                <h5 class="uk-comment-title">{{ __user__.name }}</h5>
            </header>
            <div class="uk-comment-body">
                <form id="form-comment" class="uk-form">
                    <fieldset class="uk-fieldset">
                        <div class="uk-alert uk-alert-danger uk-hidden"></div>
                        <div class="uk-margin">
                            <textarea class="uk-textarea" rows="6" placeholder="说点什么吧"></textarea>
                        </div>
                        <div class="uk-margin">
                            <button type="submit" class="uk-button uk-button-primary"> 发表评论</button>
                        </div>
                    </fieldset>
                </form>
            </div>
        </article>

        <hr>
    {% endif %}

        <h4>最新评论</h4>

        <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="{{ comment.user_image }}">
                        <h5 class="uk-comment-title">{{ comment.user_name }} {% if comment.user_id==blog.user_id %}(作者){% endif %}</h5>
                        <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>

{% endblock %}

blogs.html

<!-- 继承父模板 '__base__.html' -->
{% extends '__base__.html' %}
<!--jinja2 title 块内容替换-->
{% block title %}LEEYY{% endblock %}
<!--jinja2 beforehead 块内容替换-->
{% block beforehead %}

{% endblock %}

<!--jinja2 content 块内容替换-->
{% block content %}
    <!--uk-visible@m是大于中等尺寸屏幕时显示的UI-->
    <!--日志列表内容-->
    <div class="uk-grid  uk-visible@m">
    <div class="uk-width-3-4">
    {% for blog in blogs %}
        <article class="uk-article">
            <h3><a href="/blog/{{ blog.id }}">{{ blog.name }}</a></h3>
            <p class="uk-article-meta">发表于{{ blog.created_at|datetime }}</p>
            <p>{{ blog.summary }}</p>
            <p><a href="/blog/{{ blog.id }}">继续阅读 <i class="uk-icon-angle-double-right"></i></a></p>
        </article>
        <hr>
    {% endfor %}
    <!--分页导航栏,在父模板的开头定义过-->
    {{ pagination(page) }}
    </div>

    <!--uk-visible@m是大于中等尺寸屏幕时显示的UI-->
    <!--右边侧导航栏-->
    <div class="uk-width-1-4 uk-visible@m">
            <h4>站内搜索</h4>
            <FORM method=GET action="https://www.google.com/search">
                <div class="uk-inline">
                    <a class="uk-form-icon uk-form-icon-flip" href="#" uk-icon="search"></a>
                    <INPUT class="uk-input" TYPE=text name=q placeholder="站内搜索" size=31 maxlength=255 value="">
                </div>
                <font size=-1>
                    <INPUT TYPE=hidden name=domains value="/"><br>
                    <INPUT TYPE=hidden name=sitesearch value="/"></br>
                </font>
                <button TYPE=submit name=btnG class="uk-button uk-button-default"> SEARCH</button>

            </FORM>


            <h4>亲密链接</h4>
            <ul class="uk-list uk-list-divider">
                    <li><a target="_blank" href="https://www.liaoxuefeng.com/wiki/1016959663602400">廖大的python教程</a></li>
            </ul>
    </div>
    </div>

    <!--uk-hidden@m是小于中等尺寸屏幕时显示的UI-->
    <!--移动屏幕时日志列表排版-->
    <div class="uk-hidden@m">
    {% for blog in blogs %}
        <article class="uk-article">
            <h5><a href="/blog/{{ blog.id }}">{{ blog.name }}</a></h5>
            <p class="uk-article-meta">发表于{{ blog.created_at|datetime }}</p>
            <p>{{ blog.summary }}</p>
            <p><a href="/blog/{{ blog.id }}">继续阅读 <i class="uk-icon-angle-double-right"></i></a></p>
        </article>
        <hr>
    {% endfor %}
    <!--分页导航栏,在父模板的开头定义过-->
    {{ pagination(page) }}
    </div>


{% endblock %}

manage_blog_edit.html

<!-- 继承父模板 '__base__.html' -->
{% extends '__base__.html' %}
<!--jinja2 title 块内容替换-->
{% block title %}编辑日志{% endblock %}
<!--jinja2 beforehead 块内容替换-->
{% block beforehead %}
<!--script中构建vue,向后端API提交日志相关数据,包括创建新日志和修改旧日志-->
<script>

var
    ID = '{{ id }}',
    action = '{{ action }}';

function initVM(blog) {
    var vm = new Vue({
        el: '#vm',
        data: blog,
        methods: {
            submit: function (event) {
                event.preventDefault();
                var $form = $('#vm').find('form');
                $form.postJSON(action, this.$data, function (err, r) {
                    if (err) {
                        $form.showFormError(err);
                    }
                    else {
                        return location.assign('/manage/blogs');
                    }
                });
            }
        }
    });
    $('#vm').show();
}

$(function () {
    if (ID) {
        getJSON('/api/blogs/' + ID, function (err, blog) {
            if (err) {
                return fatal(err);
            }
            $('#loading').hide();
            initVM(blog);
        });
    }
    else {
        $('#loading').hide();
        initVM({
            name: '',
            summary: '',
            content: ''
        });
    }
});

</script>

{% endblock %}

<!--jinja2 content 块内容替换,构建日志编写页面UI主要内容-->
{% block content %}
    <div class="uk-grid">
    <div class="uk-width-1-1 uk-margin-bottom">
        <ul class="uk-breadcrumb">
            <li><a href="/manage/comments">评论</a></li>
            <li><a href="/manage/blogs">日志</a></li>
            <li><a href="/manage/users">用户</a></li>
        </ul>
    </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-2-3">
        <form v-on="submit: submit" class="uk-form-stacked">
            <div class="uk-alert uk-alert-danger uk-hidden"></div>
            <div class="uk-margin-top">
                <label class="uk-form-label">标题:</label>
                <div class="uk-form-controls">
                    <input v-model="name" name="name" type="text" placeholder="标题" class="uk-input uk-form-width-large">
                </div>
            </div>
            <div class="uk-margin-top">
                <label class="uk-form-label">摘要:</label>
                <div class="uk-form-controls">
                    <textarea v-model="summary" rows="4" name="summary" placeholder="摘要" class="uk-textarea" style="resize:none;"></textarea>
                </div>
            </div>
            <div class="uk-margin-top">
                <label class="uk-form-label">内容:</label>
                <div class="uk-form-controls">
                    <textarea v-model="content" rows="12" name="content" placeholder="内容" class="uk-textarea" style="resize:none;"></textarea>
                </div>
            </div>
            <div class="uk-margin-top">
                <button type="submit" class="uk-button uk-button-primary"><i class="uk-icon-save"></i> 保存</button>
                <a href="/manage/blogs" class="uk-button"><i class="uk-icon-times"></i> 取消</a>
            </div>
        </form>
    </div>
    </div>

{% endblock %}

manage_blogs.html

<!-- 继承父模板 '__base__.html' -->
{% extends '__base__.html' %}
<!--jinja2 title 块内容替换-->
{% block title %}日志{% endblock %}
<!--jinja2 beforehead 块内容替换-->
{% block beforehead %}
<!--script中构建vue,向后端API提交日志管理操作相关数据-->
<script>

function initVM(data) {
    var vm = new Vue({
        el: '#vm',
        data: {
            blogs: data.blogs,
            page: data.page
        },
        methods: {
            previous: function () {
                gotoPage(this.page.page_index - 1);
            },
            next: function () {
                gotoPage(this.page.page_index + 1);
            },
            edit_blog: function (blog) {
                location.assign('/manage/blogs/edit?id=' + blog.id);
            },
            delete_blog: function (blog) {
                if (confirm('确认要删除“' + blog.name + '”?删除后不可恢复!')) {
                    postJSON('/api/blogs/' + blog.id + '/delete', function (err, r) {
                        if (err) {
                            return alert(err.message || err.error || err);
                        }
                        refresh();
                    });
                }
            }
        }
    });
    $('#vm').show();
}

$(function() {
    getJSON('/api/blogs', {
        page: {{ page_index }}
    }, function (err, results) {
        if (err) {
            return fatal(err);
        }
        $('#loading').hide();
        initVM(results);
    });
});

</script>

{% endblock %}

<!--jinja2 content 块内容替换-->
{% block content %}
    <div class="uk-grid">
    <div class="uk-width-1-1 uk-margin-bottom">
        <ul class="uk-breadcrumb">
            <li><a href="/manage/comments">评论</a></li>
            <li class="uk-active"><span>日志</span></li>
            <li><a href="/manage/users">用户</a></li>
        </ul>
    </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">
        <a href="/manage/blogs/create" class="uk-button uk-button-primary"><i class="uk-icon-plus"></i> 新日志</a>

        <table class="uk-table uk-table-divider">
            <thead>
                <tr>
                    <th class="uk-table-expand uk-text-left"> 标题</th>
                    <th class="uk-text-left">作者</th>
                    <th class="uk-text-left">标签</th>
                    <th class="uk-text-left">创建时间</th>
                    <th class="uk-text-left">操作</th>
                </tr>
            </thead>
            <tbody>
                <tr v-repeat="blog: blogs" >
                    <td>
                        <a target="_blank" v-attr="href: '/blog/'+blog.id" v-text="blog.name"></a>
                    </td>
                    <td>
                        <a target="_blank" v-attr="href: '/user/'+blog.user_id" v-text="blog.user_name"></a>
                    </td>
                    <td>
                        <a target="_blank" v-attr="href: '/blogs/'+blog.tag" v-text="blog.tag"></a>
                    </td>
                    <td>
                        <span v-text="blog.created_at.toDateTime()"></span>
                    </td>
                    <td>
                        <a href="#0" v-on="click: edit_blog(blog)">编辑</a>
                        <a href="#0" v-on="click: delete_blog(blog)">删除</a>
                    </td>
                </tr>
            </tbody>
        </table>
        <div class="uk-width-1-1 uk-text-center">
        <ul class="uk-pagination">
            <li v-if="! page.has_previous" class="uk-disabled"><span><i uk-icon="chevron-left"></i></span></li>
            <li v-if="page.has_previous"><a v-on="click: previous()" href="#0"><i uk-icon="chevron-left"></i></a></li>
            <li class="uk-active"><span v-text="page.page_index"></span></li>
            <li v-if="! page.has_next" class="uk-disabled"><span><i uk-icon="chevron-right"></i></span></li>
            <li v-if="page.has_next"><a v-on="click: next()" href="#0"><i uk-icon="chevron-right"></i></a></li>
        </ul>
        </div>
    </div>
    </div>

{% endblock %}

manage_comments.html

<!-- 继承父模板 '__base__.html' -->
{% extends '__base__.html' %}
<!--jinja2 title 块内容替换-->
{% block title %}评论{% endblock %}
<!--jinja2 beforehead 块内容替换-->
{% block beforehead %}
<!--script中构建vue,向后端API提交评论管理操作相关数据-->
<script>

function initVM(data) {
    $('#vm').show();
    var vm = new Vue({
        el: '#vm',
        data: {
            comments: data.comments,
            page: data.page
        },
        methods: {
            previous: function () {
                gotoPage(this.page.page_index - 1);
            },
            next: function () {
                gotoPage(this.page.page_index + 1);
            },
            delete_comment: function (comment) {
                var content = comment.content.length > 20 ? comment.content.substring(0, 20) + '...' : comment.content;
                if (confirm('确认要删除评论“' + comment.content + '”?删除后不可恢复!')) {
                    postJSON('/api/comments/' + comment.id + '/delete', function (err, r) {
                        if (err) {
                            return error(err);
                        }
                        refresh();
                    });
                }
            }
        }
    });
}

$(function() {
    getJSON('/api/comments', {
        page: {{ page_index }}
    }, function (err, results) {
        if (err) {
            return fatal(err);
        }
        $('#loading').hide();
        initVM(results);
    });
});

</script>

{% endblock %}

<!--jinja2 content 块内容替换-->
{% block content %}

    <div class="uk-width-1-1 uk-margin-bottom">
        <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 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-justify uk-table-divider">
            <thead>
                <tr>
                    <th class="uk-text-left uk-width-small">作者</th>
                    <th class="uk-text-left">内容</th>
                    <th class="uk-text-left uk-table-expand">创建时间</th>
                    <th class="uk-text-left uk-width-small">操作</th>
                </tr>
            </thead>
            <tbody>
                <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)">删除</a>
                    </td>
                </tr>
            </tbody>
        </table>
        <div class="uk-width-1-1 uk-text-center">
        <ul class="uk-pagination">
            <li v-if="! page.has_previous" class="uk-disabled"><span><i uk-icon="chevron-left"></i></span></li>
            <li v-if="page.has_previous"><a v-on="click: previous()" href="#0"><i uk-icon="chevron-left"></i></a></li>
            <li class="uk-active"><span v-text="page.page_index"></span></li>
            <li v-if="! page.has_next" class="uk-disabled"><span><i uk-icon="chevron-right"></i></span></li>
            <li v-if="page.has_next"><a v-on="click: next()" href="#0"><i uk-icon="chevron-right"></i></a></li>
        </ul>
        </div>
    </div>
{% endblock %}

manage_users.html

<!-- 继承父模板 '__base__.html' -->
{% extends '__base__.html' %}
<!--jinja2 title 块内容替换-->
{% block title %}用户{% endblock %}
<!--jinja2 beforehead 块内容替换-->
{% block beforehead %}
<!--script中构建vue,向后端API提交用户管理操作相关数据-->
<script>

function initVM(data) {
    $('#vm').show();
    var vm = new Vue({
        el: '#vm',
        data: {
            users: data.users,
            page: data.page
        },
        methods: {
            previous: function () {
                gotoPage(this.page.page_index - 1);
            },
            next: function () {
                gotoPage(this.page.page_index + 1);
            },
            delete_user: function (user) {
                if (confirm('确认要删除用户“' + user.name + '”?删除后不可恢复!')) {
                    postJSON('/api/users/' + user.id + '/delete', function (err, r) {
                        if (err) {
                            return error(err);
                        }
                        refresh();
                    });
                }
            }
        }
    });
}

$(function() {
    getJSON('/api/users', {
        page: {{ page_index }}
    }, function (err, results) {
        if (err) {
            return fatal(err);
        }
        $('#loading').hide();
        initVM(results);
    });
});

</script>

{% endblock %}

<!--jinja2 content 块内容替换-->
{% block content %}
    <div class="uk-grid">
    <div class="uk-width-1-1 uk-margin-bottom">
        <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 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-divider">
            <thead>
                <tr>
                    <th class="uk-table-expand uk-text-left">名字</th>
                    <th class="uk-text-left">电子邮件</th>
                    <th class="uk-text-left">注册时间</th>
                    <th class="uk-text-left">操作</th>
                </tr>
            </thead>
            <tbody>
                <tr v-repeat="user: users">
                    <td>
                        <span v-text="user.name"></span>
                        <span v-if="user.admin" style="color:#d05">管理员</span>
                    </td>
                    <td>
                        <a v-attr="href: 'mailto:'+user.email" v-text="user.email"></a>
                    </td>
                    <td>
                        <span v-text="user.created_at.toDateTime()"></span>
                    </td>
                    <td>
                        <a href="#0" v-on="click: delete_user(user)">删除</a>
                    </td>
                </tr>
            </tbody>
        </table>
        <div class="uk-width-1-1 uk-text-center">
        <ul class="uk-pagination">
            <li v-if="! page.has_previous" class="uk-disabled"><span><i uk-icon="chevron-left"></i></span></li>
            <li v-if="page.has_previous"><a v-on="click: previous()" href="#0"><i uk-icon="chevron-left"></i></a></li>
            <li class="uk-active"><span v-text="page.page_index"></span></li>
            <li v-if="! page.has_next" class="uk-disabled"><span><i uk-icon="chevron-right"></i></span></li>
            <li v-if="page.has_next"><a v-on="click: next()" href="#0"><i uk-icon="chevron-right"></i></a></li>
        </ul>
        </div>
    </div>
    </div>

{% endblock %}

register.html

<!-- 继承父模板 '__base__.html' -->
{% extends '__base__.html' %}
<!--jinja2 title 块内容替换-->
{% block title %}Register/注册{% endblock %}
<!--jinja2 beforehead 块内容替换-->
{% block beforehead %}
<!--script中构建vue,向后端API提交合格的注册信息数据-->
<script>

function validateEmail(email) {
    var re = /^[a-z0-9\.\-\_]+\@[a-z0-9\-\_]+(\.[a-z0-9\-\_]+){1,4}$/;
    return re.test(email.toLowerCase());
}

$(function () {
    var vm = new Vue({
        el: '#vm',
        data: {
            name: '',
            email: '',
            password1: '',
            password2: ''
        },
        methods: {
            submit: function (event) {
                event.preventDefault();
                var $form = $('#vm');
                if (! this.name.trim()) {
                    return $form.showFormError('NAME/请输入名字');
                }
                if (! validateEmail(this.email.trim().toLowerCase())) {
                    return $form.showFormError('CORRECT EMAIL/请输入正确的Email地址');
                }
                if (this.password1.length < 6) {
                    return $form.showFormError('PASSWORD AT LEAST 6 CHAR/口令长度至少为6个字符');
                }
                if (this.password1 !== this.password2) {
                    return $form.showFormError('PASSWORD INCONSIST/两次输入的口令不一致');
                }
                var email = this.email.trim().toLowerCase();
                $form.postJSON('/api/users', {
                    name: this.name.trim(),
                    email: email,
                    passwd: CryptoJS.SHA1(email + ':' + this.password1).toString()
                }, function (err, r) {
                    if (err) {
                        return $form.showFormError(err);
                    }
                    return location.assign('/');
                });
            }
        }
    });
    $('#vm').show();
});

</script>

{% endblock %}

<!--jinja2 content 块内容替换,构建注册页面UI主要内容-->
{% block content %}
    <div class="uk-grid">
    <div class="uk-width-1-1">
        <h4>REGISTER/欢迎注册!</h4>
        <form id="vm" v-on="submit: submit" class="uk-form-stacked">
            <div class="uk-alert uk-alert-danger uk-hidden"></div>
            <div class="uk-margin-top">
                <label class="uk-form-label">NAME/名字:</label>
                <div class="uk-form-controls">
                    <input class="uk-input uk-form-width-medium" v-model="name" type="text" maxlength="50" placeholder="名字">
                </div>
            </div>
            <div class="uk-margin-top">
                <label class="uk-form-label">EMAIL/电子邮件:</label>
                <div class="uk-form-controls">
                    <input class="uk-input uk-form-width-medium" v-model="email" type="text" maxlength="50" placeholder="your-name@example.com">
                </div>
            </div>
            <div class="uk-margin-top">
                <label class="uk-form-label">PASSWORD/输入口令:</label>
                <div class="uk-form-controls">
                    <input class="uk-input uk-form-width-medium" v-model="password1" type="password" maxlength="50" placeholder="输入口令">
                </div>
            </div>
            <div class="uk-margin">
                <label class="uk-form-label">REPEAT PASSWORD/重复口令:</label>
                <div class="uk-form-controls">
                    <input class="uk-input uk-form-width-medium" v-model="password2" type="password" maxlength="50" placeholder="重复口令">
                </div>
            </div>
            <div class="uk-margin-large">
                <button type="submit" class="uk-button uk-button-primary"><i class="uk-icon-user"></i>REGISTER/注册</button>
            </div>
        </form>
    </div>
    </div>

{% endblock %}

signin.html

<!-- 继承父模板 '__base__.html' -->
{% extends '__base__.html' %}
<!--jinja2 title 块内容替换-->
{% block title %}Signin/登陆{% endblock %}
<!--jinja2 beforehead 块内容替换-->
{% block beforehead %}
<!--script中构建vue,向后端API提交登录验证信息数据-->
<script>

$(function() {
    var vmAuth = new Vue({
        el: '#vm',
        data: {
            email: '',
            passwd: ''
        },
        methods: {
            submit: function(event) {
                event.preventDefault();
                var $form = $('#vm');
                var email = this.email.trim().toLowerCase();
                var data = {
                        email: email,
                        passwd: this.passwd==='' ? '' : CryptoJS.SHA1(email + ':' + this.passwd).toString()
                    };
                $form.postJSON('/api/authenticate', data, function(err, result) {
                    if (! err) {
                        location.assign('/');
                    }
                });
            }
        }
    });
    $('#vm').show();
});

</script>

{% endblock %}

<!--jinja2 content 块内容替换,构建登录页面UI主要内容-->
{% block content %}
    <div class="uk-grid">
    <div class="uk-width-1-1">
        <h4>SIGNIN/欢迎登陆!</h4>
        <form id="vm" v-on="submit: submit" class="uk-form-stacked">
            <div class="uk-alert uk-alert-danger uk-hidden"></div>
            <div class="uk-margin-top">
                <label class="uk-form-label">EMAIL/电子邮箱:</label>
                <div class="uk-inline">
                    <span class="uk-form-icon" uk-icon="user"></span>
                    <input class="uk-input uk-form-width-medium" v-model="email" type="text" maxlength="50" placeholder="Email">
                </div>
            </div>
            <div class="uk-margin-top">
                <label class="uk-form-label">PASSWORD/输入口令:</label>
                <div class="uk-inline">
                    <span class="uk-form-icon uk-form-icon-flip" uk-icon="lock"></span>
                    <span class="uk-form-icon uk-form-icon-flip" uk-icon="lock"></span>
                    <input class="uk-input uk-form-width-medium" v-model="passwd" type="password" maxlength="50" placeholder="口令">
                </div>
            </div>
            <div class="uk-margin-top">
                <button type="submit" class="uk-button uk-button-primary">SIGNIN/登陆</button>
            </div>
        </form>
    </div>
    </div>

{% endblock %}

2、app.py的文件补充所有的中间件

async def init(loop):
    await orm.create_pool(loop=loop, **configs.db)
    app = web.Application(middlewares=[
        logger_factory, auth_factory, data_factory, response_factory
    ])

3、执行app.py文件,我们的blog网站就搭建好了

完整代码参考:https://github.com/burstlink/awesome-python3-webapp2/tree/day14

您可能感兴趣的与本文相关的镜像

Python3.9

Python3.9

Conda
Python

Python 是一种高级、解释型、通用的编程语言,以其简洁易读的语法而闻名,适用于广泛的应用,包括Web开发、数据分析、人工智能和自动化脚本

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值