Update templates

This commit is contained in:
RemiZOffAlex
2020-02-08 05:05:43 +03:00
parent 5913836f53
commit db995990d4
14 changed files with 480 additions and 131 deletions

View File

@@ -1,9 +1,9 @@
{% extends "skeleton.html" %} {% extends "skeleton.html" %}
{% block body %} {% block content %}
<h3>Профиль</h3> <h3>Профиль</h3>
<hr /> <hr />
<p>Зарегистрирован: {{ user.created.strftime('%Y-%m-%d') }}</p> <p>Зарегистрирован: {{ user.created.strftime('%Y-%m-%d') }}</p>
{% endblock body %} {% endblock content %}

View File

@@ -1,5 +1,5 @@
{% extends "skeleton.html" %} {% extends "skeleton.html" %}
{% block body %} {% block content %}
{% raw %} {% raw %}
<h3> <h3>

View File

@@ -1,5 +1,5 @@
{% extends "skeleton.html" %} {% extends "skeleton.html" %}
{% block body %} {% block content %}
<h3> <h3>
<div class="btn btn-outline-success float-right" v-on:click="showFormNewTag"><i class="fa fa-plus"></i></div> <div class="btn btn-outline-success float-right" v-on:click="showFormNewTag"><i class="fa fa-plus"></i></div>

View File

@@ -1,5 +1,5 @@
{% extends "skeleton.html" %} {% extends "skeleton.html" %}
{% block body %} {% block content %}
<h3>Список пользователей</h3> <h3>Список пользователей</h3>
<hr /> <hr />

View File

@@ -0,0 +1,113 @@
/*
<pagination-component v-bind:pagination="pagination" v-bind:click-handler="getRecipes"></pagination-component>
<script type="text/javascript" src="/static/components/pagination.js"></script>
Vue
...
data: {
pagination: {
page: 1,
per_page: {{ per_page }},
size: {{ pagedata['count'] }}
},
}
...
Flask
pagedata['pagination'] = {
"page": page,
"per_page": app.config['ITEMS_ON_PAGE'],
"size": 0
}
*/
let paginationTemplate = `<div class="row">
<div class="col py-2 text-center">
<div class="btn btn-outline-secondary" v-if="pages()<=1">...</div>
<template v-else>
<button type="button" class="btn btn-outline-secondary pull-left" v-if="has_prev" v-on:click="handlePageSelected(pagination.page-1)">Предыдущая</button>
<template v-for="page in iter_pages">
<button type="button" class="btn btn-outline-secondary mr-1" v-if="page" v-on:click="handlePageSelected(page)">{{ page }}</button>
<div class="btn btn-outline-secondary mr-1" v-else>...</div>
</template>
<button type="button" class="btn btn-outline-secondary float-right" v-if="has_next" v-on:click="handlePageSelected(pagination.page+1)">Следующая</button>
</template>
</div>
</div>`;
Vue.component('pagination-component', {
template: paginationTemplate,
data: function() {
return {
}
},
props:{
pagination: {
type: Array,
required: true,
default: {
page: 1,
per_page: 25,
size: 0
}
},
clickHandler: {
type: Function,
default: function() { }
},
},
methods: {
handlePageSelected: function(selected) {
/* Установить номер текущей страницы и вызвать функция обновления
* страницы
*
* Аргументы:
* selected -- номер страницы
*/
let vm = this;
vm.pagination.page = selected;
vm.clickHandler();
},
pages: function() {
let vm = this;
return Math.ceil(vm.pagination.size/vm.pagination.per_page);
},
},
computed: {
has_prev: function() {
let vm = this;
return vm.pagination.page > 1;
},
has_next: function() {
let vm = this;
return vm.pagination.page < vm.pages();
},
iter_pages: function() {
/* */
let vm = this;
let last = 0;
let left_edge=2, left_current=2,
right_current=5, right_edge=2;
let result = [];
for (let num = 1; num < vm.pages()+1; num++) {
if (num <= left_edge ||
(num > vm.pagination.page - left_current - 1 &&
num < vm.pagination.page + right_current) ||
num > vm.pages() - right_edge) {
if (last + 1 != num) {
result.push(null);
} else {
result.push(num);
}
last = num
}
};
return result;
},
}
});

View File

@@ -0,0 +1,268 @@
var tagsTemplate = `
<div>
<!-- Начало: Теги -->
<div class="row my-3">
<div class="col">
<div class="btn mb-1"><i class="fa fa-tags"></i></div>
<div class="btn btn-outline-success mb-1" v-on:click="showPanel(panels.standart)"><i class="fa fa-plus"></i></div>
<div class="btn-group mr-2 mb-1" v-for="(tag, index) in sortedTags">
<a class="btn btn-outline-secondary text-monospace" :href="'/tag/' + tag.id">{{ tag.name }}</a>
<div class="btn btn-outline-danger" v-on:click="removeTag(tag.id)"><i class="fa fa-remove"></i></div>
</div>
</div>
</div>
<div class="row mt-3" v-if="panels.standart.visible">
<div class="col">
<div class="input-group">
<input v-model="newtag" v-on:keyup.13="addTag" class="form-control" />
<div class="input-group-append">
<div class="btn btn-outline-success" v-on:click="addTag"><i class="fa fa-save"></i></div>
</div>
</div>
<div class="row mt-3">
<div class="col">
<template v-for="(tags, key, index) in groups">
<a class="btn btn-outline-secondary mt-2 mr-2" v-if="(index % 2)===0" :href="'#' + index">{{ key }}</a>
<a class="btn btn-outline-primary mt-2 mr-2" v-else :href="'#' + index">{{ key }}</a>
</template>
</div>
</div>
<!-- Начало: Форма добавления нового тега -->
<div class="row" v-if="panels.new.visible">
<div class="col py-2">
Добавить новый тег [<b id="newTag">{{ newtag }}</b>]?
<div class="row">
<div class="col py-2">
<div class="btn btn-outline-danger" v-on:click="cancelNewTag">Отмена</div>
<div class="btn btn-outline-success float-right" v-on:click="addNewTag">Добавить</div>
</div>
</div>
</div>
</div>
<!-- Конец: Форма добавления нового тега -->
<div class="row mt-3" v-for="(tags, key, index) in groups">
<div class="col pr-0">
<a :name="index" class="btn btn-outline-danger mr-2 mb-1">{{ key }}</a>
<template v-for="(tag, tagIdx) in tags">
<div class="btn btn-outline-secondary mr-2 mb-1" v-if="!tag_ids.includes(tag.id)" v-on:click="tag_add_to_node(tag)">{{ tag.name }}</div>
<div class="btn btn-primary mr-2 mb-1" v-else v-on:click="removeTag(tag.id)">{{ tag.name }}</div>
</template>
</div>
</div>
</div>
</div>
<!-- Конец: Теги -->
</div>`;
Vue.component('tags-component', {
data: function() {
return {
newtag: '',
groups: {},
forms: {
modal: false,
panel: false
},
panels: {
standart: {
visible: false
},
new: {
visible: false
}
}
}
},
props: ['tags', 'node', 'url'],
template: tagsTemplate,
methods: {
arrayRemove: function(arr, value) {
/* Удаление элемента из списка */
return arr.filter(function(ele){
return ele != value;
});
},
removeTag: function (id) {
/* Удаление тега из ресурса */
let vm = this;
var tag = null;
for (var i = 0; i < vm.tags.length; i++) {
if (id == vm.tags[i].id) {
tag = vm.tags[i]
}
}
if (!tag) {return;}
axios.post(
'/api',
{
"jsonrpc": "2.0",
"method": 'tag.deleteFrom' + vm.url,
"params": {
"id": vm.node,
"tag": tag.id
},
"id": 1
}
).then(
function(response) {
if ('result' in response.data) {
vm.tags = vm.arrayRemove(vm.tags, tag);
} else if ('error' in response.data) {
console.log(response.data);
}
}
);
},
addTag: function () {
/* Добавить тег к ресурсу */
let vm = this;
var newtag = vm.newtag.trim();
vm.newtag = newtag;
axios.post(
'/api',
{
"jsonrpc": "2.0",
"method": 'tag.exist',
"params": {
name: vm.newtag
},
"id": 1
}
).then(
function(response) {
if ('result' in response.data) {
vm.tag_add_to_node( response.data['result'] );
} else if ('error' in response.data) {
console.log(response.data);
vm.panels.new.visible = true;
}
}
).catch(
function (error) {
console.log(error);
}
);
},
addNewTag:function () {
/* Добавление нового тега */
let vm = this;
axios.post(
'/api',
{
"jsonrpc": "2.0",
"method": 'tag.add',
"params": {
name: vm.newtag
},
"id": 1
}
).then(
function(response) {
if ('result' in response.data) {
vm.getTags();
vm.tag_add_to_node( response.data['result'] );
}
}
);
},
cancelNewTag:function () {
let vm = this;
vm.newtag = '';
vm.panels.new.visible = false;
},
// Добавление тега к ресурсу
tag_add_to_node: function(tag) {
let vm = this;
axios.post(
'/api',
{
"jsonrpc": "2.0",
"method": 'tag.addTo' + this.url,
"params": {
"id": vm.node,
"tag": tag.id
},
"id": 1
}
).then(
function(response) {
if ('result' in response.data) {
vm.tags.push(response.data['result']);
vm.newtag = '';
vm.panels.new.visible = false;
}
}
);
},
// Получить список тегов
getTags: function() {
let vm = this;
axios.post(
'/api',
{
"jsonrpc": "2.0",
"method": 'tags.groups',
"params": {},
"id": 1
}
).then(
function(response) {
if ('result' in response.data) {
vm.groups = response.data['result'];
}
}
).catch(
function (error) {
console.log(error);
}
);
},
showPanel: function(panel) {
/* Показать/скрыть панель */
panel.visible = !panel.visible;
},
},
mounted: function() {
let vm = this;
vm.getTags();
},
computed: {
tag_ids: function() {
let vm = this;
var result = [];
for (var i = 0; i < vm.tags.length; i++) {
result.push(vm.tags[i].id);
}
return result;
},
sortedTags: function() {
let vm = this;
if (vm.tags === undefined) {return [];}
vm.tags.sort(
function(a, b) {
if (a.name > b.name) {
return 1;
}
if (a.name < b.name) {
return -1;
}
return 0;
}
);
return vm.tags;
}
}
});

View File

@@ -1,5 +1,5 @@
{% extends "skeleton.html" %} {% extends "skeleton.html" %}
{% block body %} {% block content %}
<h3 class="text-danger">{{ error_code }}: {{ error_message }}</h3> <h3 class="text-danger">{{ error_code }}: {{ error_message }}</h3>
<hr /> <hr />

View File

@@ -0,0 +1,37 @@
{% macro plugin(type="tinymce") -%}
{% if type=="tinymce" %}
<script type="text/javascript" src="/static/tinymce/tinymce.min.js"></script>
{% elif type=="ckeditor" %}
<script type="text/javascript" src="/static/ckeditor/ckeditor.js"></script>
{% endif %}
{%- endmacro %}
{% macro ckeditor(name) -%}
CKEDITOR.replace( '{{ name }}', {
customConfig: '/static/js/ckeditor-conf.js'
} );
{%- endmacro %}
{% macro tinymce(name) -%}
tinymce.init({
selector: 'textarea#{{ name }}',
height: 400,
language: 'ru',
plugins: "code link image table",
toolbar: "table tabledelete | tableprops tablerowprops tablecellprops | tableinsertrowbefore tableinsertrowafter tabledeleterow | tableinsertcolbefore tableinsertcolafter tabledeletecol"
});
{%- endmacro %}
{% macro getValue(name, param, type="tinymce") -%}
{% if type=="tinymce" %}
let value = tinymce.get("{{ name }}").getContent();
if (value != {{ param }}) {
{{ param }} = value;
}
{% elif type=="ckeditor" %}
var value = CKEDITOR.instances["{{ name }}"].getData();
if (value != {{ param }}) {
{{ param }} = value;
}
{% endif %}
{%- endmacro %}

View File

@@ -1,5 +1,5 @@
{% extends "skeleton.html" %} {% extends "skeleton.html" %}
{% block body %} {% block content %}
<div class="card"> <div class="card">
<div class="card-body"> <div class="card-body">

View File

@@ -1,19 +1,13 @@
<div class="row"> <div class="row">
<div class="col py-2"> <div class="col py-2">
{% if session.logged_in %}
<div class="btn-group float-right">
<a class="btn btn-outline-secondary border-0" href="/profile"><i class="fa fa-user"></i></a>
<a class="btn btn-outline-danger border-0" href="/logout"><i class="fa fa-sign-out"></i></a>
</div>
{% else %}
<a class="btn btn-outline-success border-0 float-right" href="/login"><i class="fa fa-sign-in"></i></a>
{% endif %}
<a class="btn btn-outline-secondary border-0" href="/"><i class="fa fa-home"></i></a> <a class="btn btn-outline-secondary border-0" href="/"><i class="fa fa-home"></i></a>
<a class="btn btn-outline-secondary border-0" href="/page">Статья</a> <a class="btn btn-outline-secondary border-0" href="/pages">Статьи</a>
<a class="btn btn-outline-secondary border-0" href="/tags">Метки</a> <a class="btn btn-outline-secondary border-0" href="/tags">Метки</a>
<a class="btn btn-outline-secondary border-0" href="/users">Пользователи</a> <a class="btn btn-outline-secondary border-0" href="/users">Пользователи</a>
<a class="btn btn-outline-secondary border-0" href="/api/browse">API JSON-RPC</a> <a class="btn btn-outline-secondary border-0" href="/api/browse">API JSON-RPC</a>
<a class="btn btn-outline-success border-0 float-right" href="/login"><i class="fa fa-sign-in"></i></a>
</div> </div>
</div> </div>

View File

@@ -1,112 +0,0 @@
{% extends "skeleton.html" %}
{% block body %}
{% raw %}
<h3>
<div class="btn btn-outline-secondary float-right" v-on:click="showPanel(panels.edit)"><i class="fa fa-edit"></i></div>
{{ page.title }}</h3>
<hr />
<div class="row" v-if="panels.edit.visible">
<div class="col">
<div class="form-group">
<label for="title">Заголовок</label>
<input id="title" name="title" type="text" value="Заголовок страницы" class="form-control" v-model="newPage.title">
</div>
<div class="form-group">
<label for="text">Текст</label>
<textarea class="form-control" id="text" name="text" v-model="newPage.text"></textarea>
</div>
<div class="row">
<div class="col">
<button class="btn btn-outline-success float-right" v-on:click="send"><i class="fa fa-save-o"></i> Сохранить</button>
</div>
</div>
<div class="row mt-3" v-if="error">
<div class="col bg-danger text-white" v-html="error">
</div>
</div>
</div>
</div>
<span v-html="page.text" v-else></span>
{% endraw %}
{% endblock %}
{% block breadcrumb %}
<ol class="breadcrumb mt-3">
<li class="breadcrumb-item"><a href="/"><i class="fa fa-home"></i></a></li>
<li class="breadcrumb-item active">Редактирование страницы</li>
</ol>
{% endblock %}
{% block script %}
<script type="text/javascript" src="/static/ckeditor/ckeditor.js"></script>
<script type="text/javascript">
var app = new Vue({
el: '#app',
data: {
page: {{ pagedata['page']|tojson|safe }},
newPage: {{ pagedata['page']|tojson|safe }},
panels: {
edit: {
visible: false
}
},
error: null
},
methods: {
showPanel: function(panel) {
/* Показать/скрыть панель */
panel.visible = !panel.visible;
},
send: function() {
let vm = this;
var value = CKEDITOR.instances["text"].getData();
if (value != vm.newPage.text) {
vm.newPage.text = value;
}
axios.post(
'/api',
{
"jsonrpc": "2.0",
"method": 'page.update',
"params": {
"title": vm.newPage.title,
"text": vm.newPage.text
},
"id": 1
}
).then(
function(response) {
if ('result' in response.data) {
vm.page = response.data['result'];
vm.newPage = vm.page;
vm.error = null;
} else if ('error' in response.data) {
vm.error = response.data['error'].message;
}
}
);
}
},
updated: function() {
let vm = this;
for(var instanceName in CKEDITOR.instances) {
CKEDITOR.instances[instanceName].destroy(true);
}
if (vm.panels.edit.visible) {
CKEDITOR.replace( "text", {
customConfig: '/static/js/ckeditor-conf.js'
} );
}
}
})
</script>
{% endblock %}

View File

@@ -7,8 +7,8 @@
<div class="container"> <div class="container">
{% include 'navbar.html' %} {% include 'navbar.html' %}
{% block body %} {% block content %}
{% endblock body %} {% endblock content %}
{% block breadcrumb %} {% block breadcrumb %}
{% endblock %} {% endblock %}

View File

@@ -0,0 +1,16 @@
<div class="row">
<div class="col py-2">
<a class="btn btn-outline-secondary border-0" href="/"><i class="fa fa-home"></i></a>
<a class="btn btn-outline-secondary border-0" href="/pages">Статьи</a>
<a class="btn btn-outline-secondary border-0" href="/tags">Метки</a>
<a class="btn btn-outline-secondary border-0" href="/users">Пользователи</a>
<a class="btn btn-outline-secondary border-0" href="/api/browse">API JSON-RPC</a>
<div class="btn-group float-right">
<a class="btn btn-outline-secondary border-0" href="/profile"><i class="fa fa-user"></i></a>
<a class="btn btn-outline-danger border-0" href="/logout"><i class="fa fa-sign-out"></i></a>
</div>
</div>
</div>

View File

@@ -0,0 +1,33 @@
<!DOCTYPE html>
<html lang="ru">
{% include 'header.html' %}
<body>
<section id="app">
<div class="container">
{% include 'user/navbar.html' %}
{% block content %}
{% endblock content %}
{% block breadcrumb %}
{% endblock %}
{% include 'footer.html' %}
</div>
</section>
{% block script %}
<script type="text/javascript">
var app = new Vue({
el: '#app',
data: {
},
methods: {
}
})
</script>
{% endblock script %}
</body>
</html>