发布文章

master
barney 2 years ago
parent 932ed5ed5a
commit f36f731994
  1. 2
      article/views.py
  2. BIN
      db.sqlite3
  3. 105
      frontend/src/components/ArticleList.vue
  4. 42
      frontend/src/components/BlogHeader.vue
  5. 7
      frontend/src/router/index.js
  6. 4
      frontend/src/store/index.js
  7. 208
      frontend/src/views/ArticleCreate.vue
  8. 9
      frontend/src/views/LoginView.vue
  9. 6
      frontend/src/views/RegisterView.vue
  10. 4
      user_info/serializers.py

@ -35,6 +35,7 @@ class CategoryViewSet(viewsets.ModelViewSet):
queryset = Category.objects.all()
serializer_class = CategorySerializer
permission_classes = [IsAdminUserOrReadOnly]
pagination_class = None # 不进行翻页
def get_serializer_class(self):
if self.action == 'list':
@ -48,6 +49,7 @@ class TagViewSet(viewsets.ModelViewSet):
queryset = Tag.objects.all()
serializer_class = TagSerializer
permission_classes = [IsAdminUserOrReadOnly]
pagination_class = None # 不进行翻页
class AvatarViewSet(viewsets.ModelViewSet):

Binary file not shown.

@ -1,5 +1,10 @@
<template>
<div v-for="article in articles" :key="article.url" id="articles">
<!-- 增加了这个 span -->
<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>
@ -15,21 +20,13 @@
<!-- 实现分页 -->
<div id="paginator">
<span v-if="is_page_exists('previous')">
<router-link
:to="get_path('previous')"
>
Prev
</router-link>
<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>
<router-link :to="get_path('next')"> Next </router-link>
</span>
</div>
</template>
@ -42,9 +39,9 @@ import { useRoute } from "vue-router";
export default {
name: "ArticleList",
setup() {
onMounted(()=>{
get_article_data();
})
onMounted(() => {
get_article_data();
});
let articles = ref([]); //
let previous = ref(null); // url
let next = ref(null); // url
@ -82,7 +79,7 @@ export default {
break;
default:
//
if (!('page' in router.query)) return 1;
if (!("page" in router.query)) return 1;
if (router.query.page === null) return 1;
return router.query.page;
}
@ -96,17 +93,17 @@ export default {
const get_article_data = () => {
let article_url = "http://127.0.0.1:6789/api/article"; // BaseUrl
let params = new URLSearchParams();
// appendIfExists
// append
// url
params.appendIfExists('page', router.query.page);
params.appendIfExists('search', router.query.search);
//
const paramsString = params.toString();
if (paramsString.charAt(0) !== '') {
article_url += '/?' + paramsString;
}
let params = new URLSearchParams();
// appendIfExists
// append
// url
params.appendIfExists("page", router.query.page);
params.appendIfExists("search", router.query.search);
//
const paramsString = params.toString();
if (paramsString.charAt(0) !== "") {
article_url += "/?" + paramsString;
}
$.ajax({
url: article_url,
type: "GET",
@ -117,27 +114,27 @@ export default {
},
});
};
// pagesearch
const get_path = (direction) => {
let url = "";
try {
switch(direction) {
case 'next':
if (next.value !== undefined) {
url += (new URL(next.value)).search
}
break;
case 'previous':
if (previous.value !== undefined) {
url += (new URL(previous.value)).search
}
break;
}
}
catch { return url}
return url;
}
// pagesearch
const get_path = (direction) => {
let url = "";
try {
switch (direction) {
case "next":
if (next.value !== undefined) {
url += new URL(next.value).search;
}
break;
case "previous":
if (previous.value !== undefined) {
url += new URL(previous.value).search;
}
break;
}
} catch {
return url;
}
return url;
};
return {
articles,
@ -145,7 +142,7 @@ export default {
is_page_exists,
get_page_param,
get_article_data,
get_path,
get_path,
};
},
watch: {
@ -166,7 +163,7 @@ export default {
font-size: 1.2rem;
font-weight: bolder;
text-decoration: none;
color: blue;
color: blue;
padding: 5px 0 5px 0;
}
@ -182,7 +179,7 @@ export default {
#paginator {
text-align: center;
padding-bottom: 4rem;
padding-bottom: 4rem;
}
#paginator a {
@ -195,4 +192,14 @@ export default {
padding-left: 10px;
padding-right: 10px;
}
.category {
padding: 5px 10px 5px 10px;
margin: 5px 5px 5px 0;
font-family: Georgia, Arial, sans-serif;
font-size: small;
background-color: darkred;
color: whitesmoke;
border-radius: 15px;
}
</style>

@ -29,8 +29,14 @@
用户中心
</router-link>
<router-link to="/" @click="quit"
<router-link
:to="{ name: 'ArticleCreate' }"
v-if="isSuperUser"
>
发表文章
</router-link>
<router-link to="/" @click="quit">
退出
</router-link>
</div>
@ -51,6 +57,9 @@ import { ref } from "vue";
import SearchButton from "@/components/SearchButton.vue";
import authorization from "@/utils/authorization";
const storage = localStorage;
export default {
name: "BlogHeader",
components: {
@ -60,28 +69,33 @@ export default {
authorization().then((data) => ([this.haslogin, this.username] = data));
},
setup() {
let username = ref("");
let haslogin = ref(false);
let isSuperUser = ref(false);
const quit = () => {
const storage = localStorage;
window.alert("已退出登录!");
window.alert("已退出登录!");
console.log("登出成功,过期时间: ", new Date().toLocaleString());
storage.clear(); //
haslogin.value = false;
};
isSuperUser.value = JSON.parse(localStorage.getItem('is_superuser_blog'));
return {
username,
haslogin,
isSuperUser,
quit,
};
},
methods: {
refresh() {
this.username = localStorage.getItem('username_blog');
}
}
},
methods: {
refresh() {
this.username = localStorage.getItem("username_blog");
},
},
};
</script>
@ -89,7 +103,7 @@ export default {
/* 样式来源: https://www.runoob.com/css/css-dropdowns.html* /
/* 下拉按钮样式 */
.dropbtn {
width: 10vw;
width: 10vw;
background-color: mediumslateblue;
color: white;
padding: 8px 8px 30px 8px;
@ -98,8 +112,8 @@ export default {
cursor: pointer;
height: 2vw;
border-radius: 5px;
text-overflow: ellipsis;
overflow: hidden;
text-overflow: ellipsis;
overflow: hidden;
}
/* 容器 <div> - 需要定位下拉内容 */
.dropdown {
@ -149,7 +163,7 @@ export default {
.grid-1 {
display: grid;
grid-template-columns: 8fr 2fr ;
grid-template-columns: 8fr 2fr;
}
.login-link {

@ -4,6 +4,7 @@ import ArticleDetail from '@/views/ArticleDetailView.vue'
import LoginView from '@/views/LoginView.vue'
import RegisterView from '@/views/RegisterView.vue'
import UserCenter from '@/views/UserCenterView.vue'
import ArticleCreate from '@/views/ArticleCreate.vue'
const routes = [
{
@ -31,6 +32,12 @@ const routes = [
name: "UserCenter",
component: UserCenter,
},
{
path : "/article/create/",
name: "ArticleCreate",
component: ArticleCreate,
}
]
const router = createRouter({

@ -2,10 +2,14 @@ import { createStore } from 'vuex'
export default createStore({
state: {
islogin: false // 用户是否登录,默认没有登录
},
getters: {
},
mutations: {
updateIsLogin(state,islogin) {
state.islogin = islogin;
},
},
actions: {
},

@ -0,0 +1,208 @@
<template>
<BlogHeader />
<div id="article-create">
<h3>发表文章</h3>
<form>
<div class="form-elem">
<span>标题</span>
<input v-model="title" type="text" placeholder="输入标题" />
</div>
<div class="form-elem">
<span>分类</span>
<span v-for="category in categories" :key="category.id">
<!--样式也可以通过 :style 绑定-->
<button
class="category-btn"
:style="categoryStyle(category)"
@click.prevent="chooseCategory(category)"
>
{{ category.title }}
</button>
</span>
</div>
<div class="form-elem">
<span>标签</span>
<input
v-model="tags"
type="text"
placeholder="输入标签,用逗号分隔"
/>
</div>
<div class="form-elem">
<span>正文</span>
<textarea
v-model="body"
placeholder="输入正文"
rows="20"
cols="80"
></textarea>
</div>
<div class="form-elem">
<button v-on:click.prevent="submit">提交</button>
</div>
</form>
</div>
<BlogFooter />
</template>
<script>
import BlogHeader from "@/components/BlogHeader.vue";
import BlogFooter from "@/components/BlogFooter.vue";
import authorization from "@/utils/authorization";
import $ from "jquery";
import { onMounted } from "@vue/runtime-core";
import { ref } from "vue";
export default {
name: "ArticleCreate",
components: { BlogHeader, BlogFooter },
data: function () {
return {
//
title: "",
//
body: "",
//
// categories: [],
//
selectedCategory: null,
//
tags: "",
};
},
setup() {
let categories = ref([]);
onMounted(() => {
//
$.ajax({
url: "http://127.0.0.1:6789/api/category",
type: "GET",
success(resp) {
categories.value = resp;
},
});
});
return {
categories,
};
},
methods: {
//
// css vue 便
categoryStyle(category) {
if (
this.selectedCategory !== null &&
category.id === this.selectedCategory.id
) {
return {
backgroundColor: "black",
};
}
return {
backgroundColor: "lightgrey",
color: "black",
};
},
//
chooseCategory(category) {
// selectedCategory
if (
this.selectedCategory !== null &&
this.selectedCategory.id === category.id
) {
this.selectedCategory = null;
}
//
else {
this.selectedCategory = category;
}
},
//
submit() {
const that = this;
//
authorization().then(function (response) {
if (response[0]) {
//
let data = {
title: that.title,
body: that.body,
};
//
if (that.selectedCategory) {
data.category_id = that.selectedCategory.id;
}
//
data.tags = that.tags
//
.split(/[,,]/)
//
.map((x) => x.trim())
//
.filter((x) => x.charAt(0) !== "");
//
//
const token = localStorage.getItem("access_blog");
$.ajax({
url: "http://127.0.0.1:6789/api/article/",
type: "POST",
data: data,
headers: {
Authorization: "Bearer " + token,
},
success(resp) {
that.$router.push({
name: "detail",
params: { id: resp.id },
});
},
});
} else {
window.alert("令牌过期,请重新登录。");
}
});
},
},
};
</script>
<style scoped>
.category-btn {
margin-right: 10px;
}
#article-create {
text-align: center;
font-size: large;
}
form {
text-align: left;
padding-left: 100px;
padding-right: 10px;
padding-bottom: 100px;
}
.form-elem {
padding: 10px;
}
input {
height: 25px;
padding-left: 10px;
width: 50%;
}
button {
height: 35px;
cursor: pointer;
border: none;
outline: none;
margin: 0 auto;
background-color: steelblue;
color: whitesmoke;
border-radius: 5px;
width: 60px;
}
</style>

@ -66,6 +66,15 @@ export default {
storage.setItem('refresh_blog', resp.refresh);
storage.setItem('expired_time', expiredTime);
storage.setItem('username_blog', username.value);
//
$.ajax({
url: 'http://127.0.0.1:6789/api/user/' + username.value + '/',
type: 'GET',
success(resp){
storage.setItem('is_superuser_blog',resp.is_superuser);
}
});
console.log("登录时间: ",new Date().toLocaleString());
router.push({ name: "home" });
},

@ -82,16 +82,12 @@ export default {
username: username.value,
password: password.value,
},
success(resp) {
console.log(resp);
success() {
window.alert("注册成功!即将跳转到登陆页面!");
setTimeout(() => {
router.push({ name: "login" }); //
}, 200);
},
error(resp) {
console.log(resp);
},
});
};

@ -26,9 +26,11 @@ class UserRegisterSerializer(serializers.ModelSerializer):
'id',
'username',
'password',
'is_superuser', # 是否是管理员
]
extra_kwargs = {
'password': {'write_only': True}
'password': {'write_only': True},
'is_superuser': {'read_only': True},
}
def create(self, validated_data):

Loading…
Cancel
Save