diff --git a/data/config.json b/data/config.json
new file mode 100644
index 0000000..19459f8
--- /dev/null
+++ b/data/config.json
@@ -0,0 +1,18 @@
+{
+ "banner": "ZhDiary",
+ "tabs": [
+ {
+ "name": "千字文",
+ "to": "/posts/千字文"
+ },
+ {
+ "name": "Resources",
+ "to": "/resouces"
+ },
+ {
+ "name": "About",
+ "url": "https://github.com/patarapolw/zhdiary"
+ }
+ ],
+ "fullUrl": "https://zhdiary.polvcode.dev"
+}
\ No newline at end of file
diff --git a/web/frontend/package.json b/web/frontend/package.json
index 65cc5c9..eb882bc 100644
--- a/web/frontend/package.json
+++ b/web/frontend/package.json
@@ -7,10 +7,12 @@
"build": "vue-cli-service build"
},
"dependencies": {
+ "axios": "^0.19.2",
"buefy": "^0.8.12",
"core-js": "^3.6.4",
"vue": "^2.6.11",
"vue-class-component": "^7.2.2",
+ "vue-extend-layout": "^2.0.6",
"vue-meta": "^2.3.3",
"vue-property-decorator": "^8.3.0",
"vue-router": "^3.1.5"
@@ -24,6 +26,7 @@
"sass": "^1.25.0",
"sass-loader": "^8.0.2",
"typescript": "~3.7.5",
+ "vue-cli-plugin-layouts": "~1.0.2",
"vue-cli-plugin-prerender-spa": "~1.1.6",
"vue-cli-plugin-pug": "^1.0.7",
"vue-template-compiler": "^2.6.11"
diff --git a/web/frontend/src/App.vue b/web/frontend/src/App.vue
index 3943904..9dc07c8 100644
--- a/web/frontend/src/App.vue
+++ b/web/frontend/src/App.vue
@@ -1,32 +1,13 @@
-
-
-
-
-
+
+
+
+
+
+
+
diff --git a/web/frontend/src/global.ts b/web/frontend/src/global.ts
new file mode 100644
index 0000000..5e263cc
--- /dev/null
+++ b/web/frontend/src/global.ts
@@ -0,0 +1 @@
+export const config = JSON.parse(process.env.VUE_APP_CONFIG!)
diff --git a/web/frontend/src/layouts/default.vue b/web/frontend/src/layouts/default.vue
new file mode 100644
index 0000000..0fabe8f
--- /dev/null
+++ b/web/frontend/src/layouts/default.vue
@@ -0,0 +1,87 @@
+
+section
+ b-navbar.navbar-shadow.noscroll.is-fixed-top(style="max-width: 100vw;")
+ template(slot="brand")
+ div(style="display: inline-flex; overflow: auto; max-width: calc(100vw - 3.25rem); flex-grow: 1")
+ b-navbar-item(tag="router-link" to="/")
+ h1.bold {{config.banner}}
+ template(slot="start")
+ b-navbar-item(v-for="t in config.tabs"
+ :tag="t.to ? 'router-link' : 'a'"
+ :href="t.to ? '' : t.url"
+ :to="t.to ? t.to : ''"
+ :target="t.to ? '' : '_blank'"
+ ) {{t.name}}
+ template(slot="end")
+ form.field.m-sm.px-md(@submit.prevent="onSearch")
+ .control.has-icons-right
+ input.input.is-rounded(type="search" placeholder="Search" v-model="q")
+ span.icon.is-right
+ fa(icon="search")
+ section(style="margin-top: 3.25rem")
+ .columns
+ .column.column.is-10-desktop.is-offset-1-desktop
+ router-view
+
+
+
+
+
diff --git a/web/frontend/src/main.ts b/web/frontend/src/main.ts
index 240fe21..8627fe1 100644
--- a/web/frontend/src/main.ts
+++ b/web/frontend/src/main.ts
@@ -1,7 +1,10 @@
import Vue from 'vue'
+
import App from './App.vue'
import router from './router'
+
import './plugins/buefy'
+import './plugins/meta'
Vue.config.productionTip = false
diff --git a/web/frontend/src/plugins/meta.ts b/web/frontend/src/plugins/meta.ts
new file mode 100644
index 0000000..b9ff774
--- /dev/null
+++ b/web/frontend/src/plugins/meta.ts
@@ -0,0 +1,7 @@
+import Vue from 'vue'
+import VueMeta from 'vue-meta'
+
+Vue.use(VueMeta, {
+ // optional pluginOptions
+ refreshOnceOnNavigation: true
+})
diff --git a/web/frontend/src/router/index.ts b/web/frontend/src/router/index.ts
index e44cdcd..51a9887 100644
--- a/web/frontend/src/router/index.ts
+++ b/web/frontend/src/router/index.ts
@@ -1,27 +1,17 @@
import Vue from 'vue'
import VueRouter from 'vue-router'
-import Home from '../views/Home.vue'
Vue.use(VueRouter)
-const routes = [
- {
- path: '/',
- name: 'Home',
- component: Home
- },
- {
- path: '/about',
- name: 'About',
- // route level code-splitting
- // this generates a separate chunk (about.[hash].js) for this route
- // which is lazy-loaded when the route is visited.
- component: () => import(/* webpackChunkName: "about" */ '../views/About.vue')
- }
-]
-
const router = new VueRouter({
- routes
+ mode: 'history',
+ routes: [
+ {
+ path: '/:page',
+ alias: '/',
+ component: () => import('../views/Home.vue')
+ }
+ ]
})
export default router
diff --git a/web/frontend/src/utils.ts b/web/frontend/src/utils.ts
new file mode 100644
index 0000000..7718848
--- /dev/null
+++ b/web/frontend/src/utils.ts
@@ -0,0 +1,3 @@
+export function normalizeArray (a: T | T[]): T | undefined {
+ return Array.isArray(a) ? a[0] : a
+}
diff --git a/web/frontend/vue.config.js b/web/frontend/vue.config.js
index e881059..a7318b2 100644
--- a/web/frontend/vue.config.js
+++ b/web/frontend/vue.config.js
@@ -1,3 +1,5 @@
+process.env.VUE_APP_CONFIG = JSON.stringify(require('../../data/config.json'))
+
module.exports = {
pluginOptions: {
prerenderSpa: {
diff --git a/web/frontend/yarn.lock b/web/frontend/yarn.lock
index 9c44cde..723f614 100644
--- a/web/frontend/yarn.lock
+++ b/web/frontend/yarn.lock
@@ -1610,6 +1610,13 @@ aws4@^1.8.0:
resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.9.1.tgz#7e33d8f7d449b3f673cd72deb9abdc552dbe528e"
integrity sha512-wMHVg2EOHaMRxbzgFJ9gtjOOCrI80OHLG14rxi28XwOW8ux6IiEbRCGGGqCtdAIg4FQCbW20k9RsT4y3gJlFug==
+axios@^0.19.2:
+ version "0.19.2"
+ resolved "https://registry.yarnpkg.com/axios/-/axios-0.19.2.tgz#3ea36c5d8818d0d5f8a8a97a6d36b86cdc00cb27"
+ integrity sha512-fjgm5MvRHLhx+osE2xoekY70AhARk3a6hkN+3Io1jc00jtquGvxYlKlsFUhmUET0V5te6CcZI7lcv2Ym61mjHA==
+ dependencies:
+ follow-redirects "1.5.10"
+
babel-code-frame@^6.22.0:
version "6.26.0"
resolved "https://registry.yarnpkg.com/babel-code-frame/-/babel-code-frame-6.26.0.tgz#63fd43f7dc1e3bb7ce35947db8fe369a3f58c74b"
@@ -2799,6 +2806,13 @@ debug@2.6.9, debug@^2.2.0, debug@^2.3.3:
dependencies:
ms "2.0.0"
+debug@=3.1.0:
+ version "3.1.0"
+ resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261"
+ integrity sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==
+ dependencies:
+ ms "2.0.0"
+
debug@^3.0.0, debug@^3.1.0, debug@^3.1.1, debug@^3.2.5:
version "3.2.6"
resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b"
@@ -3571,6 +3585,13 @@ flush-write-stream@^1.0.0:
inherits "^2.0.3"
readable-stream "^2.3.6"
+follow-redirects@1.5.10:
+ version "1.5.10"
+ resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.5.10.tgz#7b7a9f9aea2fdff36786a94ff643ed07f4ff5e2a"
+ integrity sha512-0V5l4Cizzvqt5D44aTXbFZz+FtyXV1vrDN6qrelxtfYQKW0KO0W2T/hkE8xvGa/540LkZlkaUjO4ailYTFtHVQ==
+ dependencies:
+ debug "=3.1.0"
+
follow-redirects@^1.0.0:
version "1.10.0"
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.10.0.tgz#01f5263aee921c6a54fb91667f08f4155ce169eb"
@@ -7970,6 +7991,11 @@ vue-class-component@^7.1.0, vue-class-component@^7.2.2:
resolved "https://registry.yarnpkg.com/vue-class-component/-/vue-class-component-7.2.3.tgz#a5b1abd53513a72ad51098752e2dedd499807cca"
integrity sha512-oEqYpXKaFN+TaXU+mRLEx8dX0ah85aAJEe61mpdoUrq0Bhe/6sWhyZX1JjMQLhVsHAkncyhedhmCdDVSasUtDw==
+vue-cli-plugin-layouts@~1.0.2:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/vue-cli-plugin-layouts/-/vue-cli-plugin-layouts-1.0.2.tgz#10c737db9ae63188fb21fc26740ac2af6a144c9c"
+ integrity sha512-wQkFiLCcwjyfYVeSU4XWatkVhBpIrQKNmh8JL6Oz865qD6MkMME7tzPsp7rvImnkCSCjMCfTbvimVWxrSw1tgg==
+
vue-cli-plugin-prerender-spa@~1.1.6:
version "1.1.6"
resolved "https://registry.yarnpkg.com/vue-cli-plugin-prerender-spa/-/vue-cli-plugin-prerender-spa-1.1.6.tgz#fd3ed890c949c29c1e0c74ee81c7e044539242e3"
@@ -7986,6 +8012,11 @@ vue-cli-plugin-pug@^1.0.7:
pug-plain-loader "^1.0.0"
raw-loader "^0.5.1"
+vue-extend-layout@^2.0.6:
+ version "2.0.6"
+ resolved "https://registry.yarnpkg.com/vue-extend-layout/-/vue-extend-layout-2.0.6.tgz#f76e368343c0afd80b9f0e07b0be5b3f5013a416"
+ integrity sha512-wzKOoeWPR0z4goWyHWy7mBYDqVoO1th4DexEv34cqhOqp41wpCwVfZMI0N85Nwxyis/FloGvX+PAhU03I4BUsA==
+
vue-hot-reload-api@^2.3.0:
version "2.3.4"
resolved "https://registry.yarnpkg.com/vue-hot-reload-api/-/vue-hot-reload-api-2.3.4.tgz#532955cc1eb208a3d990b3a9f9a70574657e08f2"