parent
7f08beda6d
commit
dfff087598
16 changed files with 545 additions and 130 deletions
Binary file not shown.
After Width: | Height: | Size: 1.1 KiB |
@ -1,22 +1,113 @@ |
|||||||
<template> |
<template> |
||||||
<div id="header"> |
<div id="header"> |
||||||
|
<div class="grid"> |
||||||
|
<div></div> |
||||||
<h1>My Drf-Vue Blog</h1> |
<h1>My Drf-Vue Blog</h1> |
||||||
|
<div class="search"> |
||||||
|
<form> |
||||||
|
<input v-model="searchText" type="text" placeholder="输入搜索内容..." /> |
||||||
|
<button v-on:click.prevent="searchArticles"> |
||||||
|
</button> |
||||||
|
</form> |
||||||
|
</div> |
||||||
<hr> |
<hr> |
||||||
</div> |
</div> |
||||||
|
</div> |
||||||
</template> |
</template> |
||||||
|
|
||||||
<script> |
<script> |
||||||
|
import { ref } from "vue" |
||||||
|
import router from '@/router/index'; |
||||||
|
|
||||||
export default { |
export default { |
||||||
name: "BlogHeader", |
name: "BlogHeader", |
||||||
} |
setup() { |
||||||
|
let searchText = ref(''); |
||||||
|
|
||||||
|
const searchArticles = () => { |
||||||
|
const text = searchText.value.trim(); |
||||||
|
if (text.charAt(0) !== '') { |
||||||
|
router.push({name:'home', query: {search: text }}) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return { |
||||||
|
searchText, |
||||||
|
searchArticles, |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
}; |
||||||
</script> |
</script> |
||||||
|
|
||||||
<style scoped> |
<style scoped> |
||||||
|
|
||||||
#header { |
#header { |
||||||
text-align: center; |
text-align: center; |
||||||
margin-top: 20px; |
margin-top: 20px; |
||||||
} |
} |
||||||
|
.grid { |
||||||
|
display: grid; |
||||||
|
grid-template-columns: 1fr 4fr 1fr; |
||||||
|
} |
||||||
|
|
||||||
|
.search { |
||||||
|
padding-top: 22px; |
||||||
|
} |
||||||
|
|
||||||
|
/* 搜索框样式 */ |
||||||
|
* { |
||||||
|
box-sizing: border-box; |
||||||
|
} |
||||||
|
|
||||||
|
form { |
||||||
|
position: relative; |
||||||
|
width: 20vw; |
||||||
|
margin: 0 auto; |
||||||
|
} |
||||||
|
|
||||||
|
input, |
||||||
|
button { |
||||||
|
outline: none; |
||||||
|
} |
||||||
|
|
||||||
|
input { |
||||||
|
border-color: gray; |
||||||
|
width: 100%; |
||||||
|
height: 30px; |
||||||
|
padding-left: 13px; |
||||||
|
padding-right: 46px; |
||||||
|
overflow:hidden; |
||||||
|
text-overflow:ellipsis; |
||||||
|
} |
||||||
|
|
||||||
|
button { |
||||||
|
border: none; |
||||||
|
height: 30px; |
||||||
|
width: 30px; |
||||||
|
cursor: pointer; |
||||||
|
position: absolute; |
||||||
|
} |
||||||
|
|
||||||
|
.search input { |
||||||
|
border: 2px solid gray; |
||||||
|
border-radius: 5px; |
||||||
|
background: transparent; |
||||||
|
top: 0; |
||||||
|
right: 0; |
||||||
|
} |
||||||
|
|
||||||
|
.search button { |
||||||
|
background: gray; |
||||||
|
border-radius: 0 5px 5px 0; |
||||||
|
width: 45px; |
||||||
|
top: 0; |
||||||
|
right: 0; |
||||||
|
} |
||||||
|
|
||||||
|
.search button:before { |
||||||
|
content: "搜索"; |
||||||
|
font-size: 13px; |
||||||
|
color: white; |
||||||
|
} |
||||||
</style> |
</style> |
||||||
|
@ -1,4 +1,13 @@ |
|||||||
import { createApp } from 'vue' |
import { createApp } from 'vue' |
||||||
import App from './App.vue' |
import App from './App.vue' |
||||||
|
import router from './router' |
||||||
|
import store from './store' |
||||||
|
|
||||||
createApp(App).mount('#app') |
URLSearchParams.prototype.appendIfExists = function (key, value) { |
||||||
|
if (value !== null && value !== undefined) { |
||||||
|
this.append(key, value) |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
|
||||||
|
createApp(App).use(store).use(router).mount('#app') |
||||||
|
@ -0,0 +1,23 @@ |
|||||||
|
import { createRouter, createWebHistory } from 'vue-router' |
||||||
|
import HomePage from '@/views/HomePageView.vue' |
||||||
|
import ArticleDetail from '@/views/ArticleDetailView.vue' |
||||||
|
|
||||||
|
const routes = [ |
||||||
|
{ |
||||||
|
path: '/', |
||||||
|
name: 'home', |
||||||
|
component: HomePage |
||||||
|
}, |
||||||
|
{ |
||||||
|
path: '/detail/:id', |
||||||
|
name: 'detail', |
||||||
|
component: ArticleDetail, |
||||||
|
}, |
||||||
|
] |
||||||
|
|
||||||
|
const router = createRouter({ |
||||||
|
history: createWebHistory(), // 路径中不再有恶心的#号
|
||||||
|
routes |
||||||
|
}) |
||||||
|
|
||||||
|
export default router |
@ -0,0 +1,14 @@ |
|||||||
|
import { createStore } from 'vuex' |
||||||
|
|
||||||
|
export default createStore({ |
||||||
|
state: { |
||||||
|
}, |
||||||
|
getters: { |
||||||
|
}, |
||||||
|
mutations: { |
||||||
|
}, |
||||||
|
actions: { |
||||||
|
}, |
||||||
|
modules: { |
||||||
|
} |
||||||
|
}) |
@ -0,0 +1,5 @@ |
|||||||
|
<template> |
||||||
|
<div class="about"> |
||||||
|
<h1>This is an about page</h1> |
||||||
|
</div> |
||||||
|
</template> |
@ -0,0 +1,95 @@ |
|||||||
|
<template> |
||||||
|
<BlogHeader /> |
||||||
|
<div v-if="article !== null" class="grid-container"> |
||||||
|
<div> |
||||||
|
<h1 id='title'>{{article.title}}</h1> |
||||||
|
<p id="subtitle"> |
||||||
|
本文由{{article.author.username}} 发布于 {{formatted_time(article.created)}} |
||||||
|
</p> |
||||||
|
<div v-html='article.body_html' class="article-body"> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
|
||||||
|
<div> |
||||||
|
<h3>目录</h3> |
||||||
|
<div v-html='article.toc_html' class="toc"></div> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
<BlogFooter /> |
||||||
|
</template> |
||||||
|
|
||||||
|
<script> |
||||||
|
import BlogHeader from "@/components/BlogHeader.vue"; |
||||||
|
import BlogFooter from "@/components/BlogFooter.vue"; |
||||||
|
import $ from 'jquery'; |
||||||
|
import { ref } from 'vue'; |
||||||
|
import { useRoute } from 'vue-router'; |
||||||
|
|
||||||
|
export default { |
||||||
|
name: "ArticleDetail", |
||||||
|
components: { |
||||||
|
BlogHeader, |
||||||
|
BlogFooter, |
||||||
|
}, |
||||||
|
setup() { |
||||||
|
let article = ref(null); |
||||||
|
const route = useRoute(); // 用来解析路由动态参数:id |
||||||
|
$.ajax({ |
||||||
|
url: "http://127.0.0.1:6789/api/article/" + route.params.id, |
||||||
|
type: 'GET', |
||||||
|
success(resp){ |
||||||
|
article.value=resp; |
||||||
|
}, |
||||||
|
error(resp) { |
||||||
|
console.log(resp) |
||||||
|
} |
||||||
|
}); |
||||||
|
|
||||||
|
const formatted_time = (iso_date_string) => { |
||||||
|
const date = new Date(iso_date_string); |
||||||
|
return date.toLocaleDateString(); |
||||||
|
} |
||||||
|
|
||||||
|
return { |
||||||
|
article, |
||||||
|
formatted_time, |
||||||
|
} |
||||||
|
}, |
||||||
|
}; |
||||||
|
</script> |
||||||
|
|
||||||
|
<style scoped> |
||||||
|
.grid-container { |
||||||
|
display: grid; |
||||||
|
grid-template-columns: 3fr 1fr; |
||||||
|
} |
||||||
|
|
||||||
|
#title { |
||||||
|
text-align: center; |
||||||
|
font-size: x-large; |
||||||
|
} |
||||||
|
|
||||||
|
#subtitle { |
||||||
|
text-align: center; |
||||||
|
color: gray; |
||||||
|
font-size: small; |
||||||
|
} |
||||||
|
</style> |
||||||
|
|
||||||
|
<style> |
||||||
|
.article-body p img { |
||||||
|
max-width: 100%; |
||||||
|
border-radius: 50px; |
||||||
|
box-shadow: gray 0 0 20px; |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
.toc ul { |
||||||
|
list-style-type: disc; |
||||||
|
padding-inline-start: 15px; |
||||||
|
} |
||||||
|
|
||||||
|
.toc a { |
||||||
|
color: gray; |
||||||
|
} |
||||||
|
</style> |
@ -0,0 +1,22 @@ |
|||||||
|
<template> |
||||||
|
<BlogHeader /> |
||||||
|
<ArticleList /> |
||||||
|
<BlogFooter /> |
||||||
|
</template> |
||||||
|
|
||||||
|
<script> |
||||||
|
import ArticleList from "@/components/ArticleList.vue"; |
||||||
|
import BlogHeader from "@/components/BlogHeader.vue"; |
||||||
|
import BlogFooter from "@/components/BlogFooter.vue"; |
||||||
|
|
||||||
|
export default { |
||||||
|
name: "HomePage", |
||||||
|
components: { |
||||||
|
ArticleList, |
||||||
|
BlogHeader, |
||||||
|
BlogFooter, |
||||||
|
}, |
||||||
|
}; |
||||||
|
</script> |
||||||
|
|
||||||
|
<style></style> |
Loading…
Reference in new issue