add blog
This commit is contained in:
1 parent
36338ed0d7
commit
7fe8f5d154
96 files changed
+21326
No files matched your search
@@ -0,0 +1,10 @@
|
||||
<template lang="pug">
|
||||
.container.m-md
|
||||
h4.title.is-4 Sorry, but no post can be found.
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { Vue } from 'nuxt-property-decorator'
|
||||
|
||||
export default Vue.extend()
|
||||
</script>
|
||||
@@ -0,0 +1,80 @@
|
||||
<template lang="pug">
|
||||
section
|
||||
.blog-post(v-if="header.path")
|
||||
.card.my-lg
|
||||
.card-content.content
|
||||
post-header(:header="header")
|
||||
h1.title {{header.title}}
|
||||
.image-full(v-if="header.image")
|
||||
img(:src="header.image")
|
||||
div(v-html="html")
|
||||
div(style="word-break: break-word")
|
||||
span.mr-sm Tags:
|
||||
span.mr-sm(v-for="t in header.tag || []" :key="t")
|
||||
a(:href="$router.resolve('/tag/' + t).href") {{t}}
|
||||
.card.my-lg
|
||||
.card-content
|
||||
vue-disqus(v-if="disqus" :shortname="disqus" :identifier="$route.path")
|
||||
empty(v-else)
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import PostHeader from './PostHeader.vue'
|
||||
import Empty from './Empty.vue'
|
||||
import { Vue, Component, Prop, Watch } from 'nuxt-property-decorator'
|
||||
import { makeHtml } from '../assets/make-html'
|
||||
import { globalStore } from '../store'
|
||||
|
||||
@Component({
|
||||
components: {
|
||||
PostHeader,
|
||||
Empty,
|
||||
},
|
||||
})
|
||||
export default class PostFull extends Vue {
|
||||
@Prop({ required: true }) header!: any
|
||||
|
||||
markdown = this.header.teaser || ''
|
||||
disqus = 'zhdiary'
|
||||
|
||||
mounted () {
|
||||
this.updatePost()
|
||||
}
|
||||
|
||||
@Watch('header.path')
|
||||
async updatePost () {
|
||||
if (!this.markdown) {
|
||||
this.markdown = await fetch(`/build/teaser/${this.header.path}?h=${globalStore.hash.posts[this.header.path]}`).then((r) => r.text())
|
||||
}
|
||||
this.markdown = await fetch(`/build/full/${this.header.path}?h=${globalStore.hash.posts[this.header.path]}`).then((r) => r.text())
|
||||
}
|
||||
|
||||
get html () {
|
||||
return makeHtml.parse(this.markdown || '')
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.image-full {
|
||||
text-align: center;
|
||||
margin: 1rem;
|
||||
|
||||
img {
|
||||
min-width: 500px;
|
||||
width: auto;
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 800px) {
|
||||
.image-full {
|
||||
margin-left: -1.5rem;
|
||||
margin-right: -1.5rem;
|
||||
|
||||
img {
|
||||
min-width: unset;
|
||||
width: auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,53 @@
|
||||
<template lang="pug">
|
||||
.post-meta.mb-sm
|
||||
.post-meta-author
|
||||
a.mr-md(:href="author.link" style="width: 24px; min-width: 24px"): img(:src="author.avatar" style="border-radius: 50%;")
|
||||
a.mr-md(:href="author.link") {{author.login}}
|
||||
div(style="flex-grow: 1")
|
||||
div {{dateString}}
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import moment, { Moment } from 'moment'
|
||||
import { Vue, Component, Prop } from 'nuxt-property-decorator'
|
||||
|
||||
@Component
|
||||
export default class PostHeader extends Vue {
|
||||
@Prop({ required: true }) header!: any
|
||||
|
||||
author = {
|
||||
login: 'polvcode',
|
||||
avatar: 'https://en.gravatar.com/userimage/35152960/7140205ee2a71cbe54d3f2ac43c0370d.jpg?size=24',
|
||||
link: 'https://polvcode.dev',
|
||||
}
|
||||
|
||||
get moment () {
|
||||
return this.header.date ? moment(this.header.date) : null
|
||||
}
|
||||
|
||||
get dateString () {
|
||||
return this.moment ? this.moment.format('ddd D MMMM YYYY') : ''
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.post-meta {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
white-space: nowrap;
|
||||
overflow: auto;
|
||||
|
||||
.post-meta-author {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
white-space: nowrap;
|
||||
justify-content: center;
|
||||
|
||||
a {
|
||||
border: none;
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,88 @@
|
||||
<template lang="pug">
|
||||
section
|
||||
section.m-lg(v-if="tag")
|
||||
h2.title.is-2
|
||||
span.mr-sm Tag:
|
||||
span(style="font-weight: 400") {{tag}}
|
||||
section(v-if="posts && posts.length > 0")
|
||||
post-teaser(v-for="p in posts" :header="p" :key="p.name")
|
||||
b-pagination.my-lg.mx-sm(:current.sync="page" :total="count" :per-page="perPage"
|
||||
icon-prev="caret-left" icon-next="caret-right" size="default" rounded)
|
||||
div(v-else-if="posts")
|
||||
empty
|
||||
div(v-else)
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import PostTeaser from './PostTeaser.vue'
|
||||
import Empty from './Empty.vue'
|
||||
import QParser from 'q2filter'
|
||||
import { normalizeArray, searchPosts } from '@/assets/util'
|
||||
import { Vue, Component, Prop, Watch } from 'nuxt-property-decorator'
|
||||
import { globalStore } from '../store'
|
||||
|
||||
@Component({
|
||||
components: {
|
||||
PostTeaser,
|
||||
Empty,
|
||||
},
|
||||
})
|
||||
export default class PostQuery extends Vue {
|
||||
@Prop({ default: '' }) q!: string
|
||||
@Prop({ default: '' }) tag!: string
|
||||
@Prop({ required: true }) defaults!: {
|
||||
count: number
|
||||
posts: any[]
|
||||
}
|
||||
|
||||
count = this.defaults.count
|
||||
posts: any[] = this.defaults.posts
|
||||
perPage = 5
|
||||
|
||||
get page () {
|
||||
return parseInt(normalizeArray(this.$route.query.page) || '1')
|
||||
}
|
||||
|
||||
set page (p) {
|
||||
const { page, ...query } = this.$route.query
|
||||
if (p === 1) {
|
||||
this.$router.push({ query })
|
||||
} else {
|
||||
this.$router.push({
|
||||
query: {
|
||||
...query,
|
||||
page: p.toString(),
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
mounted () {
|
||||
this.updatePosts()
|
||||
}
|
||||
|
||||
@Watch('page')
|
||||
@Watch('q')
|
||||
@Watch('tag')
|
||||
async updatePosts () {
|
||||
const ps = await Promise.all(searchPosts({
|
||||
q: this.tag ? {
|
||||
tag: this.tag,
|
||||
} : this.q,
|
||||
current: true,
|
||||
posts: await fetch(`/build/header/posts.json?h=${globalStore.hash.posts._header}`).then((r) => r.json()),
|
||||
}).map(async (h) => {
|
||||
if (!h.teaser) {
|
||||
h.teaser = await fetch(`/build/teaser/${h.path}?h=${globalStore.hash.posts[h.path]}`).then((r) => r.text())
|
||||
}
|
||||
return h
|
||||
}))
|
||||
|
||||
this.count = ps.length
|
||||
this.posts = ps.filter((p, i) => {
|
||||
const iPage = i / this.perPage
|
||||
return iPage >= this.page - 1 && iPage < this.page
|
||||
})
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,86 @@
|
||||
<template lang="pug">
|
||||
.card.my-lg.blog-post
|
||||
.card-content
|
||||
post-header(:header="header")
|
||||
.post-content
|
||||
.image-teaser(v-if="header.image")
|
||||
img(:src="header.image")
|
||||
h2.title {{header.title}}
|
||||
.content(v-html="html")
|
||||
div(style="display: flex; justify-content: flex-end")
|
||||
b-button(type="is-danger" outlined @click="openPostUrl") Read more
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import PostHeader from './PostHeader.vue'
|
||||
import { Vue, Component, Prop } from 'nuxt-property-decorator'
|
||||
import { makeHtml } from '../assets/make-html'
|
||||
import { globalStore } from '../store'
|
||||
|
||||
@Component({
|
||||
components: {
|
||||
PostHeader,
|
||||
},
|
||||
})
|
||||
export default class PostTeaser extends Vue {
|
||||
@Prop({ required: true }) header!: any
|
||||
|
||||
teaser: string = this.header.teaser || ''
|
||||
|
||||
async mounted () {
|
||||
if (!this.teaser) {
|
||||
this.teaser = await fetch(`/build/teaser/${this.header.path}.html?h=${globalStore.hash.posts[this.header.path]}`)
|
||||
.then((r) => r.text())
|
||||
}
|
||||
}
|
||||
|
||||
get html () {
|
||||
return makeHtml.parse(this.teaser)
|
||||
}
|
||||
|
||||
openPostUrl () {
|
||||
if (this.header.name) {
|
||||
if (this.header.y && this.header.mo) {
|
||||
this.$router.push(`/post/${this.header.y}/${this.header.mo}/${this.header.name}`)
|
||||
} else {
|
||||
this.$router.push(`/post/${this.header.name}`)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.content {
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
max-width: 80vw;
|
||||
}
|
||||
|
||||
.post-content {
|
||||
width: 100%;
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
.image-teaser {
|
||||
width: calc(100% + 3rem);
|
||||
margin-top: 1rem;
|
||||
margin-bottom: 1rem;
|
||||
margin-left: -1.5rem;
|
||||
margin-right: -1.5rem;
|
||||
}
|
||||
|
||||
@media only screen and (min-width: 800px) {
|
||||
.image-teaser {
|
||||
width: 100%;
|
||||
max-width: 300px;
|
||||
max-height: 300px;
|
||||
float: right;
|
||||
margin: 1rem;
|
||||
}
|
||||
|
||||
.post-content {
|
||||
overflow: auto;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,7 @@
|
||||
# COMPONENTS
|
||||
|
||||
**This directory is not required, you can delete it if you don't want to use it.**
|
||||
|
||||
The components directory contains your Vue.js Components.
|
||||
|
||||
_Nuxt.js doesn't supercharge these components._
|
||||
Reference in new issue
Block a user