Готовая лабораторная 3
This commit is contained in:
parent
166c1acb42
commit
0f688cd350
42
feed.html
42
feed.html
@ -14,6 +14,7 @@
|
||||
<link rel="stylesheet" href="./static/css/post.css">
|
||||
<link rel="stylesheet" href="./static/css/avatar.css">
|
||||
<link rel="shortcut icon" href="./static/img/favicon.svg" type="image/svg+xml">
|
||||
<script src="./static/js/feed.js" type="module"></script>
|
||||
<title>Новости</title>
|
||||
</head>
|
||||
|
||||
@ -72,8 +73,20 @@
|
||||
</div>
|
||||
<div class="wrapper d-flex justify-content-center">
|
||||
<div class="center d-flex flex-column">
|
||||
<div id="editorjs" class="border rounded-2 border-dark mb-2 p-2"></div>
|
||||
<button class="btn btn-primary mb-2">Опубликовать</button>
|
||||
<textarea placeholder="Что нового?" id="post-editor" class="border rounded-2 border-dark mb-2 p-2"></textarea>
|
||||
<div id="title-image-block" class="modal-critery">
|
||||
<input id="check-title-image" type="checkbox" style="display: none;">
|
||||
<input id="input-title-image" name="titleImage" accept="image/*" type="file" onchange="showTitleImagePreview(event);" />
|
||||
<label id="title-image-preview" for="input-title-image" title="Добавить изображение" class="rounded-2 mb-2">
|
||||
<i id="empty-title-image" class="bi bi-camera"></i>
|
||||
<img id="selected-title-image" class="rounded-2">
|
||||
</label>
|
||||
</div>
|
||||
<button class="btn btn-primary mb-2" id="post-publication-button">Опубликовать</button>
|
||||
<div class="mb-2 w-100" id="edit-block">
|
||||
<button class="btn btn-danger me-2 w-100" id="edit-post-button-cancel">Отмена</button>
|
||||
<button class="btn btn-success w-100" id="edit-post-button-accept">Применить</button>
|
||||
</div>
|
||||
<div class="posts-wrapper">
|
||||
<div class="post mb-2 mb-sm-4 w-100 d-flex flex-column border rounded-2 border-dark">
|
||||
<div
|
||||
@ -486,6 +499,29 @@
|
||||
</footer>
|
||||
</body>
|
||||
<script>document.getElementById('current-year').innerHTML = new Date().getFullYear();</script>
|
||||
<script src="./static/js/feed.js" type="module"></script>
|
||||
<script>
|
||||
function showTitleImagePreview(event) {
|
||||
var previewEmpty = document.getElementById("empty-title-image");
|
||||
var previewSelected = document.getElementById("selected-title-image");
|
||||
var check = document.getElementById("check-title-image");
|
||||
var preview = document.getElementById('title-image-preview');
|
||||
|
||||
if (check != null) check.checked = true;
|
||||
if (event.target.files.length > 0) {
|
||||
var src = URL.createObjectURL(event.target.files[0]);
|
||||
previewEmpty.style.display = "none";
|
||||
previewSelected.style.display = "block";
|
||||
previewSelected.style.opacity = "1.0";
|
||||
previewSelected.src = src;
|
||||
preview.title = "Редактировать изображение";
|
||||
} else {
|
||||
previewEmpty.style.display = "block";
|
||||
previewSelected.style.display = "none";
|
||||
previewSelected.style.opacity = "1.0";
|
||||
previewSelected.src = "";
|
||||
preview.title = "Добавить изображение";
|
||||
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</html>
|
||||
|
@ -77,3 +77,10 @@ header {
|
||||
--bs-btn-hover-border-color: #235D70;
|
||||
--bs-btn-active-bg: #235D70;
|
||||
}
|
||||
|
||||
.dropdown:hover .dropdown-menu {
|
||||
display: block;
|
||||
margin-top: 0;
|
||||
right: 0px;
|
||||
/* remove the gap so it doesn't close */
|
||||
}
|
@ -31,7 +31,40 @@
|
||||
}
|
||||
}
|
||||
|
||||
#editorjs {
|
||||
z-index: 9999;
|
||||
min-height: 100px;
|
||||
#post-editor {
|
||||
min-height: calc(1em + 26px);
|
||||
}
|
||||
|
||||
#title-image-preview {
|
||||
width: 100%;
|
||||
min-height: 100px;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
font-size: 50px;
|
||||
cursor: pointer;
|
||||
border: solid;
|
||||
border-width: 1px;
|
||||
}
|
||||
|
||||
#title-image-preview i {
|
||||
opacity: 0.3;
|
||||
transition: opacity .3s;
|
||||
}
|
||||
|
||||
#title-image-preview:hover i {
|
||||
opacity: 1.0;
|
||||
}
|
||||
|
||||
#input-title-image {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#selected-title-image {
|
||||
width: 100%;
|
||||
display: none;
|
||||
}
|
||||
|
||||
#title-image-block img {
|
||||
}
|
@ -28,3 +28,7 @@
|
||||
.post-body a:hover {
|
||||
background-size: 100% 2px;
|
||||
}
|
||||
|
||||
.post-body-text {
|
||||
white-space: pre-wrap;
|
||||
}
|
@ -1,49 +1,199 @@
|
||||
import { ApiEndpoint } from "./apiendpoint";
|
||||
import { ApiEndpoint, createPostObject } from "./apiendpoint";
|
||||
import { generatePostHtml } from "./posts";
|
||||
|
||||
import EditorJS from "@editorjs/editorjs";
|
||||
import Header from "@editorjs/header";
|
||||
import List from "@editorjs/list";
|
||||
import Link from "@editorjs/link";
|
||||
import Quote from "@editorjs/quote";
|
||||
import SimpleImage from "@editorjs/simple-image"
|
||||
import Embed from "@editorjs/embed";
|
||||
|
||||
|
||||
const editor = new EditorJS({
|
||||
/**
|
||||
* Id of Element that should contain the Editor
|
||||
*/
|
||||
holder: "editorjs",
|
||||
|
||||
/**
|
||||
* Available Tools list.
|
||||
* Pass Tool's class or Settings object for each Tool you want to use
|
||||
*/
|
||||
tools: {
|
||||
header: Header,
|
||||
list: List,
|
||||
quote: Quote,
|
||||
link: Link,
|
||||
image: SimpleImage,
|
||||
embed: Embed
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
const postApiEndpoint = new ApiEndpoint("posts");
|
||||
const userApiEndpoint = new ApiEndpoint("users");
|
||||
|
||||
document.addEventListener("DOMContentLoaded", loadPosts);
|
||||
|
||||
// функция для получения содержимого файла в виде base64 строки
|
||||
// https://ru.wikipedia.org/wiki/Base64
|
||||
async function readImageFileToBase64(file) {
|
||||
const reader = new FileReader();
|
||||
|
||||
// создание Promise-объекта для использования функции
|
||||
// с помощью await (асинхронно) без коллбэков (callback)
|
||||
// https://learn.javascript.ru/promise
|
||||
return new Promise((resolve, reject) => {
|
||||
// 2. "Возвращаем" содержимое когда файл прочитан
|
||||
// через вызов resolve
|
||||
// Если не использовать Promise, то всю работу по взаимодействию
|
||||
// с REST API пришлось бы делать в обработчике (callback) функции
|
||||
// onloadend
|
||||
reader.onloadend = () => {
|
||||
const fileContent = reader.result;
|
||||
// Здесь могла бы быть работа с REST API
|
||||
// Чтение заканчивает выполняться здесь
|
||||
resolve(fileContent);
|
||||
};
|
||||
// 3. Возвращаем ошибку
|
||||
reader.onerror = () => {
|
||||
// Или здесь в случае ошибки
|
||||
reject(new Error("oops, something went wrong with the file reader."));
|
||||
};
|
||||
// Шаг 1. Сначала читаем файл
|
||||
// Чтение начинает выполняться здесь
|
||||
reader.readAsDataURL(file);
|
||||
});
|
||||
}
|
||||
|
||||
let currentEditPostId = null;
|
||||
|
||||
async function buttonPostPublicationClicked() {
|
||||
let text = document.getElementById("post-editor").value.trim();
|
||||
const img = document.getElementById("input-title-image").files[0];
|
||||
|
||||
if (text == "" && img == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
let imgBase64 = null;
|
||||
if (img != null) {
|
||||
imgBase64 = await readImageFileToBase64(img);
|
||||
}
|
||||
|
||||
if (text == "")
|
||||
{
|
||||
text = null;
|
||||
}
|
||||
var post = createPostObject(1, new Date(), text, imgBase64);
|
||||
|
||||
await postApiEndpoint.createObject(post);
|
||||
location.reload();
|
||||
}
|
||||
|
||||
async function postObjectEdit(e) {
|
||||
let text = document.getElementById("post-editor").value.trim();
|
||||
const img = document.getElementById("input-title-image").files[0];
|
||||
var check = document.getElementById("check-title-image").checked;
|
||||
|
||||
if (text == "" && img == null && check) {
|
||||
return;
|
||||
}
|
||||
|
||||
let imgBase64 = null;
|
||||
if (img != null) {
|
||||
imgBase64 = await readImageFileToBase64(img);
|
||||
}
|
||||
|
||||
if (text == "") {
|
||||
text = null;
|
||||
}
|
||||
var post = await postApiEndpoint.getObject(currentEditPostId);
|
||||
post.text = text;
|
||||
|
||||
if (check)
|
||||
{
|
||||
post.img = imgBase64;
|
||||
}
|
||||
|
||||
await postApiEndpoint.updateObject(post);
|
||||
location.reload();
|
||||
}
|
||||
|
||||
async function editPost(e) {
|
||||
const btnId = e.target.id;
|
||||
const postId = parseInt(btnId.split("-")[2]);
|
||||
let postObj = await postApiEndpoint.getObject(postId);
|
||||
document.getElementById("post-editor").value = postObj.text == null ? "" : postObj.text;
|
||||
var textElement = document.getElementById("post-editor");
|
||||
textElement.style.height = "auto";
|
||||
textElement.style.height = textElement.scrollHeight + 2 + "px";
|
||||
showImage(postObj.img);
|
||||
scrollTo(0, 0);
|
||||
document.getElementById("edit-block").style.display = 'flex';
|
||||
document.getElementById("post-publication-button").style.display = 'none';
|
||||
currentEditPostId = postId;
|
||||
}
|
||||
|
||||
async function loadPosts() {
|
||||
document.getElementById("edit-block").style.display = "none";
|
||||
document.getElementById("edit-post-button-cancel").addEventListener('click', () => {location.reload();});
|
||||
document.getElementById("edit-post-button-accept").addEventListener('click', postObjectEdit);
|
||||
const posts = await postApiEndpoint.getObjects();
|
||||
const center = document.getElementsByClassName("posts-wrapper")[0];
|
||||
center.innerHTML = "";
|
||||
for (let i = 0; i < posts.length; i++) {
|
||||
const post = posts[i];
|
||||
const postOwner = await userApiEndpoint.getObject(post.userId);
|
||||
for (let j = 0; j < 3; j++)
|
||||
center.innerHTML += generatePostHtml(post, postOwner);
|
||||
center.innerHTML += generatePostHtml(post, postOwner);
|
||||
}
|
||||
|
||||
const postDeleteButtons =
|
||||
document.getElementsByClassName("post-delete-button");
|
||||
for (let i = 0; i < postDeleteButtons.length; i++) {
|
||||
const btn = postDeleteButtons[i];
|
||||
btn.addEventListener('click', (e) => {
|
||||
const btnId = e.target.id;
|
||||
const postId = parseInt(btnId.split("-")[2]);
|
||||
postApiEndpoint.deleteObject(postId);
|
||||
var postDiv = document.getElementById("post-" + postId);
|
||||
postDiv.parentNode.removeChild(postDiv);
|
||||
});
|
||||
}
|
||||
|
||||
const postEditButtons =
|
||||
document.getElementsByClassName("post-edit-button");
|
||||
for (let i = 0; i < postEditButtons.length; i++) {
|
||||
const btn = postEditButtons[i];
|
||||
btn.addEventListener("click", editPost);
|
||||
}
|
||||
|
||||
var text = document.getElementById("post-editor");
|
||||
function resize() {
|
||||
if (text.value == "") {
|
||||
text.style.height = "calc(1em + 26px)";
|
||||
return;
|
||||
}
|
||||
text.style.height = "auto";
|
||||
text.style.height = text.scrollHeight + 2 + "px";
|
||||
}
|
||||
/* 0-timeout to get the already changed text */
|
||||
function delayedResize() {
|
||||
window.setTimeout(resize, 0);
|
||||
}
|
||||
observe(text, "change", resize);
|
||||
observe(text, "cut", delayedResize);
|
||||
observe(text, "paste", delayedResize);
|
||||
observe(text, "drop", delayedResize);
|
||||
observe(text, "keydown", delayedResize);
|
||||
|
||||
text.focus();
|
||||
text.select();
|
||||
resize();
|
||||
|
||||
document.getElementById("post-publication-button").addEventListener('click', buttonPostPublicationClicked);
|
||||
}
|
||||
var observe;
|
||||
if (window.attachEvent) {
|
||||
observe = function (element, event, handler) {
|
||||
element.attachEvent("on" + event, handler);
|
||||
};
|
||||
} else {
|
||||
observe = function (element, event, handler) {
|
||||
element.addEventListener(event, handler, false);
|
||||
};
|
||||
}
|
||||
|
||||
function showImage(img) {
|
||||
var previewEmpty = document.getElementById("empty-title-image");
|
||||
var previewSelected = document.getElementById("selected-title-image");
|
||||
var check = document.getElementById("check-title-image");
|
||||
var preview = document.getElementById("title-image-preview");
|
||||
|
||||
// if (check != null) check.checked = true;
|
||||
if (img != null) {
|
||||
var src = img;
|
||||
previewEmpty.style.display = "none";
|
||||
previewSelected.style.display = "block";
|
||||
previewSelected.style.opacity = "1.0";
|
||||
previewSelected.src = src;
|
||||
preview.title = "Редактировать изображение";
|
||||
} else {
|
||||
previewEmpty.style.display = "block";
|
||||
previewSelected.style.display = "none";
|
||||
previewSelected.style.opacity = "1.0";
|
||||
previewSelected.src = "";
|
||||
preview.title = "Добавить изображение";
|
||||
}
|
||||
}
|
@ -10,10 +10,14 @@ export function generatePostHtml(postObject, userObject) {
|
||||
};
|
||||
const postCreatedDateTime = new Date(postObject.createdDateTime);
|
||||
|
||||
const html = `<div class="post mb-2 mb-sm-4 w-100 d-flex flex-column border rounded-2 border-dark">
|
||||
const html = `<div class="post mb-2 mb-sm-4 w-100 d-flex flex-column border rounded-2 border-dark" id="post-${
|
||||
postObject.id
|
||||
}">
|
||||
<div class="post-header py-1 px-2 d-flex border-bottom border-dark justify-content-between align-items-center">
|
||||
<a class="d-flex justify-content-start align-items-center">
|
||||
<img src="./static/img/${userObject.avatarImg}" class="post-author-avatar avatar-small rounded-circle">
|
||||
<img src="./static/img/${
|
||||
userObject.avatarImg
|
||||
}" class="post-author-avatar avatar-small rounded-circle">
|
||||
<div class="post-meta ms-2 d-flex flex-column justify-content-center">
|
||||
<div class="post-author-name">
|
||||
${userObject.firstName} ${userObject.lastName}
|
||||
@ -24,7 +28,17 @@ export function generatePostHtml(postObject, userObject) {
|
||||
</div>
|
||||
</a>
|
||||
<a>
|
||||
<i class="bi bi-three-dots fs-4"></i>
|
||||
<div class="dropdown">
|
||||
<i class="bi bi-three-dots fs-4" data-bs-toggle="dropdown" aria-expanded="false"></i>
|
||||
<ul class="dropdown-menu dropdown-menu-end">
|
||||
<li><a class="dropdown-item post-delete-button" id="delete-post-${
|
||||
postObject.id
|
||||
}"><i class="bi bi-trash3"></i> Удалить</a></li>
|
||||
<li><a class="dropdown-item post-edit-button" id="edit-post-${
|
||||
postObject.id
|
||||
}"><i class="bi bi-pencil"></i> Редактировать</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
<div class="post-body">
|
||||
|
BIN
отчеты/ПИбд-21 Потапов отчет ИП лаб3.docx
Normal file
BIN
отчеты/ПИбд-21 Потапов отчет ИП лаб3.docx
Normal file
Binary file not shown.
Loading…
Reference in New Issue
Block a user