更新readme

master
barney 2 years ago
parent f2d3b6632b
commit d3886ae21d
  1. 10
      README.md
  2. 101
      frontend/src/components/ArticleList.vue
  3. 19
      frontend/src/components/BlogHeader.vue
  4. 131
      frontend/src/components/CommentView.vue
  5. 2
      frontend/src/router/index.js
  6. 4
      frontend/src/store/index.js
  7. 6
      frontend/src/utils/authorization.js
  8. 64
      frontend/src/views/ArticleCreate.vue
  9. 14
      frontend/src/views/ArticleDetailView.vue
  10. 25
      frontend/src/views/ArticleEdit.vue
  11. 36
      frontend/src/views/LoginView.vue
  12. 4
      frontend/src/views/RegisterView.vue
  13. 2
      frontend/src/views/UserCenterView.vue

@ -2,20 +2,20 @@
#### 1、博客首页展示
![image-20221003002818644](C:\Users\15270\AppData\Roaming\Typora\typora-user-images\image-20221003002818644.png)
![博客首页](https://cdn.jsdelivr.net/gh/JokerZhang66/images@master//img/image-20221003002818644.png)
#### 2.发布文章界面
![image-20221003002550725](C:\Users\15270\AppData\Roaming\Typora\typora-user-images\image-20221003002550725.png)
![](https://cdn.jsdelivr.net/gh/JokerZhang66/images@master//img/image-20221003002550725.png)
#### 3.登录、注册界面
![image-20221003002956157](C:\Users\15270\AppData\Roaming\Typora\typora-user-images\image-20221003002956157.png)
![](https://cdn.jsdelivr.net/gh/JokerZhang66/images@master//img/image-20221003002956157.png)
#### 4.用户管理界面
![image-20221003002845601](C:\Users\15270\AppData\Roaming\Typora\typora-user-images\image-20221003002845601.png)
![](https://cdn.jsdelivr.net/gh/JokerZhang66/images@master//img/image-20221003002845601.png)
#### 5.更新文章界面
![image-20221003003047933](C:\Users\15270\AppData\Roaming\Typora\typora-user-images\image-20221003003047933.png)
![](https://cdn.jsdelivr.net/gh/JokerZhang66/images@master//img/image-20221003003047933.png)

@ -1,54 +1,50 @@
<template>
<div v-for="article in articles" :key="article.url" id="articles">
<div class="grid" :style="gridStyle()">
<div class="image-container">
<img
:src="imageIfExists(article)"
alt=""
class="image"
v-if="imageIfExists(article) !== null"
/>
</div>
<div v-for="article in articles" :key="article.url" id="articles">
<div class="grid" :style="gridStyle()">
<div class="image-container">
<img
:src="imageIfExists(article)"
alt=""
class="image"
v-if="imageIfExists(article) !== null"
/>
</div>
<router-link
:to="{ name: 'detail', params: { id: article.id } }"
class="article-info"
>
<span v-if="article.category !== null" class="category">
{{ article.category.title }}
</span>
<span v-for="tag in article.tags" v-bind:key="tag" class="tag">
{{ tag }}
</span>
<!-- 路由链接:to的两个参数分别是: 路由名称和传入的id -->
<router-link
:to="{ name: 'detail', params: { id: article.id } }"
class="article-info"
>
<span v-if="article.category !== null" class="category">
{{ article.category.title }}
</span>
<span
v-for="tag in article.tags"
v-bind:key="tag"
class="tag"
>
{{ tag }}
</span>
<!-- 路由链接:to的两个参数分别是: 路由名称和传入的id -->
<router-link
:to="{ name: 'detail', params: { id: article.id } }"
>
<div class="article-title">{{ article.title }}</div>
</router-link>
<div>
{{ formatted_time(article.created) }}
</div>
<div class="article-title">{{ article.title }}</div>
</router-link>
</div>
</div>
<!-- 实现分页 -->
<div id="paginator">
<span v-if="is_page_exists('previous')">
<router-link :to="get_path('previous')"> Prev </router-link>
</span>
<span class="current-page">
{{ get_page_param("current") }}
</span>
<span v-if="is_page_exists('next')">
<router-link :to="get_path('next')"> Next </router-link>
</span>
<div>
{{ formatted_time(article.created) }}
</div>
</router-link>
</div>
</div>
<!-- 实现分页 -->
<div id="paginator">
<span v-if="is_page_exists('previous')">
<router-link :to="get_path('previous')"> Prev </router-link>
</span>
<span class="current-page">
{{ get_page_param("current") }}
</span>
<span v-if="is_page_exists('next')">
<router-link :to="get_path('next')"> Next </router-link>
</span>
</div>
</template>
<script>
@ -157,7 +153,7 @@ export default {
if (article.avatar) {
return article.avatar.content;
} else {
//
//
return "http://127.0.0.1:6789/media/avatar/20221002/Blog.jpg";
}
};
@ -200,7 +196,6 @@ export default {
</script>
<style scoped>
#articles {
padding: 0.4rem;
}
@ -210,25 +205,23 @@ export default {
border-radius: 10px;
box-shadow: darkslategrey 0 0 12px;
user-select: none;
}
.image-container {
width: 100%;
display: grid;
place-items: center;
display: grid;
place-items: center;
}
.article-info {
width:90%;
width: 90%;
border-radius: 10px;
background-color: lightgray;
display: grid;
place-items: start start;
padding-left: 10px;
user-select: none;
position: relative;
left: 20px;
position: relative;
left: 20px;
}
.article-info:hover {

@ -90,23 +90,23 @@ export default {
});
onMounted(() => {
authorization().then(
(data) => {[haslogin.value, username.value] = data}
);
authorization().then((data) => {
[haslogin.value, username.value] = data;
});
});
const refresh = () => {
const refresh = () => {
username.value = localStorage.getItem("username_blog");
}
};
return {
username,
haslogin,
refresh,
refresh,
homePage,
quit,
};
}
},
};
</script>
@ -152,10 +152,9 @@ export default {
/* 在鼠标移上去后显示下拉菜单 */
.dropdown:hover .dropdown-content {
display: block;
background-color: lightblue;
z-index: 10;
background-color: lightblue;
z-index: 10;
}
</style>
<style scoped>

@ -52,78 +52,79 @@
</template>
<script>
import $ from 'jquery';
import authorization from '@/utils/authorization'
import { ref, watchEffect } from 'vue';
import $ from "jquery";
import authorization from "@/utils/authorization";
import { ref, watchEffect } from "vue";
export default {
name: "CommentView",
props: {
article: {
required: true,
}
},
props: {
article: {
required: true,
},
},
setup(props) {
//
let comments = ref([]);
//
let message = ref('');
let placeholder = ref('说点啥吧...');
//
let parentId = ref(null);
//
const submit = () => {
authorization().then((response) => {
if (response[0]) {
$.ajax({
url: 'http://127.0.0.1:6789/api/comment/',
type: 'POST',
data: {
content: message.value,
article_id: props.article.id,
parent_id: parentId.value,
//
let comments = ref([]);
//
let message = ref("");
let placeholder = ref("说点啥吧...");
//
let parentId = ref(null);
//
const submit = () => {
authorization().then((response) => {
if (response[0]) {
$.ajax({
url: "http://127.0.0.1:6789/api/comment/",
type: "POST",
data: {
content: message.value,
article_id: props.article.id,
parent_id: parentId.value,
},
headers: {
authorization:
"Bearer " + localStorage.getItem("access_blog"),
},
success(resp) {
//
comments.value.unshift(resp);
message.value = "";
window.alert("留言成功");
},
});
} else {
window.alert("请登录后再评论!");
}
});
};
},
headers: {
authorization: "Bearer " + localStorage.getItem('access_blog')
},
success(resp) {
//
comments.value.unshift(resp);
message.value = '';
window.alert("留言成功");
}
})
}else {
window.alert("请登录后再评论!");
}
});
};
const replyTo = (comment) => {
parentId.value = comment.id;
placeholder.value = "对" + comment.author.username + "说";
};
const replyTo = (comment) => {
parentId.value = comment.id;
placeholder.value = '对' + comment.author.username + '说';
};
const formatted_time = (iso_date_string) => {
const date = new Date(iso_date_string);
return date.toLocaleDateString() + " " + date.toLocaleTimeString();
};
const formatted_time = (iso_date_string) => {
const date = new Date(iso_date_string);
return date.toLocaleDateString() + ' ' + date.toLocaleTimeString();
};
watchEffect(() => {
comments.value =
props.article !== null ? props.article.comments : [];
});
watchEffect(() => {
comments.value = props.article !== null ? props.article.comments : [];
});
return {
comments,
message,
placeholder,
parentId,
submit,
replyTo,
formatted_time,
}
},
return {
comments,
message,
placeholder,
parentId,
submit,
replyTo,
formatted_time,
};
},
};
</script>
@ -144,7 +145,7 @@ button {
height: 25px;
background: lightslategray;
width: 40px;
margin-bottom: 40px;
margin-bottom: 40px;
}
.comments {
padding-top: 10px;

@ -34,7 +34,7 @@ const routes = [
component: UserCenter,
},
{
path : "/article/create/",
path: "/article/create/",
name: "ArticleCreate",
component: ArticleCreate,
},

@ -9,9 +9,9 @@ export default createStore({
},
mutations: {
updateIsLogin(state,islogin) {
updateIsLogin(state, islogin) {
state.islogin = islogin;
},updateIsSuperUser(state, isSuperUser){
}, updateIsSuperUser(state, isSuperUser) {
state.isSuperUser = isSuperUser;
}
},

@ -28,7 +28,7 @@ const authorization = async () => {
refresh: refreshToken,
},
success(resp) {
const nextExpiredTime = new Date().getTime() + 24*3600*1000;
const nextExpiredTime = new Date().getTime() + 24 * 3600 * 1000;
storage.setItem("access_blog", resp.access);
storage.removeItem("refresh_blog"); // 移除刷新令牌
console.log(
@ -39,12 +39,12 @@ const authorization = async () => {
haslogin = true;
},
});
}catch(err){
} catch (err) {
storage.clear();
haslogin = false;
// console.log('authorization err');
}
} else {
storage.clear(); // 清除所有有效信息
haslogin = false;

@ -43,7 +43,7 @@
</div>
<div class="form-elem">
<div style="margin-bottom: 10px;">正文</div>
<div style="margin-bottom: 10px">正文</div>
<textarea
v-model="body"
placeholder="输入正文"
@ -66,26 +66,26 @@ import BlogFooter from "@/components/BlogFooter.vue";
import authorization from "@/utils/authorization";
import $ from "jquery";
import { onMounted } from "@vue/runtime-core";
import { useRouter } from 'vue-router';
import { useRouter } from "vue-router";
import { ref } from "vue";
export default {
name: "ArticleCreate",
components: { BlogHeader, BlogFooter },
setup() {
//
let title = ref("");
//
let title = ref("");
//
let body = ref("");
let body = ref("");
//
let categories = ref([]);
let categories = ref([]);
//
let selectedCategory = ref(null);
let selectedCategory = ref(null);
//
let tags = ref("");
// id
let avatarID = ref(null);
const router = useRouter();
const router = useRouter();
onMounted(() => {
//
@ -98,7 +98,7 @@ export default {
});
});
//
//
const categoryStyle = (category) => {
if (
selectedCategory.value !== null &&
@ -127,25 +127,25 @@ export default {
}
};
const onFileChange = (e) => {
const onFileChange = (e) => {
//
const file = e.target.files[0];
let formData = new FormData();
formData.append("content", file);
$.ajax({
url: "http://127.0.0.1:6789/api/avatar/",
type: "POST",
data: formData,
processData: false,
contentType: false,
processData: false,
contentType: false,
headers: {
"Authorization":
Authorization:
"Bearer " + localStorage.getItem("access_blog"),
},
success(resp) {
avatarID.value = resp.id;
}
},
});
};
@ -174,7 +174,7 @@ export default {
//
.filter((x) => x.charAt(0) !== "");
data.avatar_id = avatarID.value;
data.avatar_id = avatarID.value;
//
//
@ -183,8 +183,8 @@ export default {
url: "http://127.0.0.1:6789/api/article/",
type: "POST",
data: JSON.stringify(data),
contentType: 'application/json',
dataType: 'json',
contentType: "application/json",
dataType: "json",
headers: {
Authorization: "Bearer " + token,
},
@ -199,22 +199,20 @@ export default {
window.alert("令牌过期,请重新登录。");
}
});
}
return {
categories,
title,
body,
avatarID,
tags,
selectedCategory,
categoryStyle,
chooseCategory,
onFileChange,
submit,
}
};
return {
categories,
title,
body,
avatarID,
tags,
selectedCategory,
categoryStyle,
chooseCategory,
onFileChange,
submit,
};
},
};
</script>

@ -56,7 +56,7 @@ export default {
type: "GET",
success(resp) {
article.value = resp;
}
},
});
});
@ -64,16 +64,16 @@ export default {
const date = new Date(iso_date_string);
return date.toLocaleDateString();
};
// vue3使computed
//
let isSuperUser = computed(()=> {
return localStorage.getItem("is_superuser_blog") === "true";
})
// vue3使computed
//
let isSuperUser = computed(() => {
return localStorage.getItem("is_superuser_blog") === "true";
});
return {
article,
formatted_time,
isSuperUser,
isSuperUser,
};
},
// computed: {

@ -21,7 +21,12 @@
</button>
</span>
<div v-if="selectedCategory !== null" style="margin-top:5px; color:red;">:&nbsp;不选分类时可创建新分类!</div>
<div
v-if="selectedCategory !== null"
style="margin-top: 5px; color: red"
>
:&nbsp;不选分类时可创建新分类!
</div>
<div class="newCategory" v-if="selectedCategory === null">
<div>创建新分类:&nbsp;</div>
@ -172,7 +177,6 @@ export default {
//
.filter((x) => x.charAt(0) !== "");
//
if (
selectedCategory.value === null &&
@ -181,17 +185,18 @@ export default {
$.ajax({
url: "http://127.0.0.1:6789/api/category/",
type: "post",
async: false,
data: {
title: newCategory.value,
},
async: false,
data: {
title: newCategory.value,
},
headers: {
Authorization: "Bearer " + localStorage.getItem("access_blog"),
Authorization:
"Bearer " +
localStorage.getItem("access_blog"),
},
success(resp) {
selectedCategory.value = resp;
},
});
}
@ -199,7 +204,6 @@ export default {
? selectedCategory.value.id
: null;
$.ajax({
url:
"http://127.0.0.1:6789/api/article/" +
@ -210,7 +214,8 @@ export default {
contentType: "application/json",
data: JSON.stringify(data),
headers: {
Authorization: "Bearer " + localStorage.getItem("access_blog"),
Authorization:
"Bearer " + localStorage.getItem("access_blog"),
},
success(resp) {
router.push({

@ -27,9 +27,9 @@
<button v-on:click.prevent="login">登录</button>
</div>
<div v-if="!haslogin">
<div class="error">{{error_msg}}</div>
</div>
<div v-if="!haslogin">
<div class="error">{{ error_msg }}</div>
</div>
</form>
</div>
</div>
@ -53,8 +53,8 @@ export default {
setup() {
let username = ref("");
let password = ref("");
let error_msg = ref("");
let haslogin = ref(false); //
let error_msg = ref("");
let haslogin = ref(false); //
const store = useStore();
const login = () => {
$.ajax({
@ -80,10 +80,10 @@ export default {
"/",
type: "GET",
success(resp) {
//
error_msg.value = "";
//
haslogin.value = true;
//
error_msg.value = "";
//
haslogin.value = true;
//
storage.setItem(
"is_superuser_blog",
@ -98,18 +98,18 @@ export default {
router.push({ name: "home" });
},
error(resp) {
error_msg.value = resp.responseText;
haslogin.value = false;
}
error(resp) {
error_msg.value = resp.responseText;
haslogin.value = false;
},
});
};
return {
username,
password,
error_msg,
haslogin,
error_msg,
haslogin,
login,
};
},
@ -148,8 +148,8 @@ span {
}
.error {
color: red;
font-size: 20px;
font-weight: bold;
color: red;
font-size: 20px;
font-weight: bold;
}
</style>

@ -19,7 +19,7 @@
v-model="password"
type="password"
placeholder="请输入密码"
autocomplete="true"
autocomplete="true"
/>
</div>
@ -29,7 +29,7 @@
v-model="conform_password"
type="password"
placeholder="请再输入一次密码"
autocomplete="true"
autocomplete="true"
/>
</div>

@ -63,7 +63,7 @@ export default {
let username = ref("");
let password = ref("");
let token = ref("");
let showingDeleteAlert = ref(false);
let showingDeleteAlert = ref(false);
const router = useRouter();
const header = ref(null); // 使refdom

Loading…
Cancel
Save