Compare commits
10 Commits
ee6cbd3614
...
ae45ae3cf5
Author | SHA1 | Date | |
---|---|---|---|
![]() |
ae45ae3cf5 | ||
![]() |
4c2b64879b | ||
![]() |
f3e4acba3a | ||
![]() |
0fd771cc00 | ||
![]() |
5822dcc59a | ||
![]() |
11562d1701 | ||
![]() |
3852e0ec03 | ||
![]() |
b0f2e5dbb2 | ||
![]() |
b5706dfb42 | ||
![]() |
def757134a |
@ -4,14 +4,8 @@ module.exports = {
|
||||
browser: true,
|
||||
node: true
|
||||
},
|
||||
parserOptions: {
|
||||
parser: 'babel-eslint',
|
||||
ecmaFeatures: {
|
||||
legacyDecorators: true
|
||||
}
|
||||
},
|
||||
extends: [
|
||||
'@nuxtjs',
|
||||
'@nuxtjs/eslint-config-typescript',
|
||||
'plugin:nuxt/recommended'
|
||||
],
|
||||
plugins: [
|
||||
|
@ -1,6 +1,3 @@
|
||||
// Import Bulma's core
|
||||
@import "~bulma/sass/utilities/_all";
|
||||
|
||||
$eHandyPrimaryColor: #0091ea;
|
||||
$eHandyPrimaryColor-invert: #fff;
|
||||
|
||||
@ -29,7 +26,3 @@ $addColors: (
|
||||
"primary": ($primary, $primary-invert)
|
||||
);
|
||||
$colors: map-merge($colors, $addColors);
|
||||
|
||||
// Import Bulma and Buefy styles
|
||||
@import "~bulma";
|
||||
@import "~buefy/src/scss/buefy";
|
5
assets/styles/base/_typography.scss
Normal file
5
assets/styles/base/_typography.scss
Normal file
@ -0,0 +1,5 @@
|
||||
*,
|
||||
html,
|
||||
body {
|
||||
font-family: 'Montserrat', sans-serif !important;
|
||||
}
|
0
assets/styles/components/.gitkeep
Normal file
0
assets/styles/components/.gitkeep
Normal file
0
assets/styles/layout/.gitkeep
Normal file
0
assets/styles/layout/.gitkeep
Normal file
11
assets/styles/main.scss
Normal file
11
assets/styles/main.scss
Normal file
@ -0,0 +1,11 @@
|
||||
// Bat buoc
|
||||
@import "~bulma/sass/utilities/_all";
|
||||
// Base
|
||||
@import './base/override';
|
||||
@import './base/typography';
|
||||
// Utils
|
||||
@import './utils/utils-dir';
|
||||
|
||||
// Bat buoc
|
||||
@import "~bulma";
|
||||
@import "~buefy/src/scss/buefy";
|
0
assets/styles/pages/.gitkeep
Normal file
0
assets/styles/pages/.gitkeep
Normal file
0
assets/styles/themes/.gitkeep
Normal file
0
assets/styles/themes/.gitkeep
Normal file
7
assets/styles/utils/_utils-dir.scss
Normal file
7
assets/styles/utils/_utils-dir.scss
Normal file
@ -0,0 +1,7 @@
|
||||
@import './mixins/prefix';
|
||||
@import './mixins/center';
|
||||
@import './mixins/position';
|
||||
@import './mixins/responsive';
|
||||
@import './mixins/size';
|
||||
@import './functions/get-variable-css';
|
||||
@import './mixins/three-dots';
|
43
assets/styles/utils/functions/_get-variable-css.scss
Normal file
43
assets/styles/utils/functions/_get-variable-css.scss
Normal file
@ -0,0 +1,43 @@
|
||||
/// Get color variable from css to scss
|
||||
/// @param $color-props: color variable : --color-text-rgb, --color-body-text
|
||||
///
|
||||
@function color($color-props) {
|
||||
@return var(--color-#{$color-props});
|
||||
}
|
||||
|
||||
/// Get font variable from css to scss
|
||||
/// @param $font-props: font variable : --font-size-header, --font-size-base
|
||||
///
|
||||
@function font($font-props) {
|
||||
@return var(--font-#{$font-props});
|
||||
}
|
||||
|
||||
/// Get all general variable from css to scss
|
||||
@function v($props) {
|
||||
@return var(--#{$props});
|
||||
}
|
||||
|
||||
/// Example :
|
||||
// :root {
|
||||
// --color-background: #FFFFFF;
|
||||
// }
|
||||
|
||||
// body {
|
||||
// color: color(primary);
|
||||
// }
|
||||
|
||||
// compiled sass code is:
|
||||
|
||||
// body {
|
||||
// color: var(--color-primary);
|
||||
// }
|
||||
|
||||
// .kmacoders {
|
||||
// color: v(--color-background);
|
||||
// }
|
||||
|
||||
// compiled sass code is:
|
||||
|
||||
// .kmacoders {
|
||||
// color: var(----color-background);
|
||||
// }
|
21
assets/styles/utils/mixins/_center.scss
Normal file
21
assets/styles/utils/mixins/_center.scss
Normal file
@ -0,0 +1,21 @@
|
||||
/* Self center vertical */
|
||||
@mixin vertical-align-center {
|
||||
position: relative;
|
||||
top: 50%;
|
||||
@include prefix(transform, translateY(-50%));
|
||||
}
|
||||
|
||||
/* Self center horizontal */
|
||||
@mixin horizontal-align-center {
|
||||
position: relative;
|
||||
left: 50%;
|
||||
@include prefix(transform, translateX(-50%));
|
||||
}
|
||||
|
||||
/* Self center both direction */
|
||||
@mixin both-align-center {
|
||||
position: relative;
|
||||
left: 50%;
|
||||
top: 50%;
|
||||
@include prefix(transform, translate(-50%, -50%));
|
||||
}
|
33
assets/styles/utils/mixins/_position.scss
Normal file
33
assets/styles/utils/mixins/_position.scss
Normal file
@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Add position property
|
||||
*
|
||||
* @param {String} $position - relative | absolute | fixed
|
||||
* @param {Length} $args - List direction property and value
|
||||
*
|
||||
* @example
|
||||
* - Usage:
|
||||
* .foo {
|
||||
* @include position(relative, top 0 left 1em);
|
||||
* }
|
||||
* - Output:
|
||||
* .foo {
|
||||
position: relative;
|
||||
top: 0;
|
||||
left: 1em;
|
||||
* }
|
||||
* ...or other way :
|
||||
* .foo {
|
||||
* @include position(relative, '');
|
||||
* }
|
||||
*/
|
||||
@mixin position($position, $args) {
|
||||
@each $o in top right bottom left {
|
||||
$i: index($args, $o);
|
||||
|
||||
@if $i and $i + 1<= length($args) and type-of(nth($args, $i + 1)) == number {
|
||||
#{$o}: nth($args, $i + 1);
|
||||
}
|
||||
}
|
||||
|
||||
position: $position;
|
||||
}
|
58
assets/styles/utils/mixins/_prefix.scss
Normal file
58
assets/styles/utils/mixins/_prefix.scss
Normal file
@ -0,0 +1,58 @@
|
||||
/* VERSION 1 ***********************************************/
|
||||
/*
|
||||
* Add prefix for property css
|
||||
*
|
||||
* @param $name : name of property
|
||||
* @param $value : value of property
|
||||
*
|
||||
* @example
|
||||
* - Usage:
|
||||
* .foo {
|
||||
* @inlucde prefix(transform, translateY(-50%));
|
||||
* }
|
||||
*/
|
||||
@mixin prefix($name, $value) {
|
||||
-webkit-#{$name}: $value;
|
||||
-moz-#{$name}: $value;
|
||||
-ms-#{$name}: $value;
|
||||
-o-#{$name}: $value;
|
||||
#{$name}: $value;
|
||||
}
|
||||
|
||||
/* VERSION 2 : Avanced version ****************************************/
|
||||
/*
|
||||
* Mixin to prefix several properties at once
|
||||
*
|
||||
* @param {List} $prefixes (()) - List of prefixes to print
|
||||
* @example
|
||||
* - Usage:
|
||||
* .foo {
|
||||
* @include prefix((
|
||||
* column-count: 3,
|
||||
* column-gap: 1.5em,
|
||||
* column-rule: 2px solid hotpink
|
||||
* ), webkit moz);
|
||||
* }
|
||||
* - Output:
|
||||
* .foo {
|
||||
* -webkit-column-count: 3;
|
||||
* -moz-column-count: 3;
|
||||
* column-count: 3;
|
||||
* -webkit-column-gap: 1.5em;
|
||||
* -moz-column-gap: 1.5em;
|
||||
* column-gap: 1.5em;
|
||||
* -webkit-column-rule: 2px solid hotpink;
|
||||
* -moz-column-rule: 2px solid hotpink;
|
||||
* column-rule: 2px solid hotpink;
|
||||
*}
|
||||
*/
|
||||
@mixin prefixServeral($declarations, $prefixes: ()) {
|
||||
@each $property, $value in $declarations {
|
||||
@each $prefix in $prefixes {
|
||||
#{'-' + $prefix + '-' + $property}: $value;
|
||||
}
|
||||
|
||||
// Output standard non-prefixed declaration
|
||||
#{$property}: $value;
|
||||
}
|
||||
}
|
40
assets/styles/utils/mixins/_responsive.scss
Normal file
40
assets/styles/utils/mixins/_responsive.scss
Normal file
@ -0,0 +1,40 @@
|
||||
/* Screen size ( Size này cùng với Theme Config global cho JS) */
|
||||
$SM: 576px;
|
||||
$MD: 768px;
|
||||
$LG: 992px;
|
||||
$XL: 1200px;
|
||||
|
||||
/*
|
||||
* Responsive element follow by screen size
|
||||
*
|
||||
* @param $screen-size: screen size
|
||||
*
|
||||
* @example
|
||||
* .foo {
|
||||
* font-size: 10px;
|
||||
* @include responsive(MD) {
|
||||
* font-size : 12px;
|
||||
* }
|
||||
* @include responsive(XL) {
|
||||
* font-size : 14px;
|
||||
* }
|
||||
*/
|
||||
@mixin responsive($screen-size) {
|
||||
@if $screen-size == SM {
|
||||
@media only screen and (min-width: $SM) {
|
||||
@content;
|
||||
}
|
||||
} @else if $screen-size == MD {
|
||||
@media only screen and (min-width: $MD) {
|
||||
@content;
|
||||
}
|
||||
} @else if $screen-size == LG {
|
||||
@media only screen and (min-width: $LG) {
|
||||
@content;
|
||||
}
|
||||
} @else if $screen-size == XL {
|
||||
@media only screen and (min-width: $XL) {
|
||||
@content;
|
||||
}
|
||||
}
|
||||
}
|
16
assets/styles/utils/mixins/_size.scss
Normal file
16
assets/styles/utils/mixins/_size.scss
Normal file
@ -0,0 +1,16 @@
|
||||
|
||||
/*
|
||||
* Set size for a element
|
||||
*
|
||||
* @param $width: width property
|
||||
* @param $height: height property
|
||||
*
|
||||
* @example
|
||||
* .foo {
|
||||
* @include box(10px, 5px);
|
||||
* }
|
||||
*/
|
||||
@mixin box($width, $height) {
|
||||
height: $height;
|
||||
width: $width;
|
||||
}
|
21
assets/styles/utils/mixins/_three-dots.scss
Normal file
21
assets/styles/utils/mixins/_three-dots.scss
Normal file
@ -0,0 +1,21 @@
|
||||
/*
|
||||
* Set size for a element
|
||||
*
|
||||
* @param $width: width property
|
||||
* @param $line: line property
|
||||
*
|
||||
* @example
|
||||
* .foo {
|
||||
* @include three-dots(200px, 3);
|
||||
* }
|
||||
*/
|
||||
|
||||
@mixin three-dots($width, $line) {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
display: -webkit-box;
|
||||
-webkit-box-orient: vertical;
|
||||
-webkit-line-clamp: $line;
|
||||
|
||||
width: $width;
|
||||
}
|
0
assets/styles/vendors/.gitkeep
vendored
Normal file
0
assets/styles/vendors/.gitkeep
vendored
Normal file
@ -1,3 +1,6 @@
|
||||
module.exports = {
|
||||
extends: ['@commitlint/config-conventional']
|
||||
extends: ['@commitlint/config-conventional'],
|
||||
ignoreFiles: [
|
||||
'./assets/styles/**/*.scss'
|
||||
]
|
||||
}
|
||||
|
18
components/global/BannerMinimal.vue
Normal file
18
components/global/BannerMinimal.vue
Normal file
@ -0,0 +1,18 @@
|
||||
<template>
|
||||
<div class="hero-body">
|
||||
<div class="container has-text-centered">
|
||||
<img src="https://cdn.emk.dev/templates/bulma-logo-light.png" width="150" height="40">
|
||||
<h2 class="subtitle">
|
||||
Ghost's Casper theme made with Bulma
|
||||
</h2>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { Vue, Component } from 'nuxt-property-decorator'
|
||||
|
||||
@Component
|
||||
export default class MinimalBanner extends Vue {
|
||||
|
||||
}
|
||||
</script>
|
17
components/global/MinimalBanner.vue
Normal file
17
components/global/MinimalBanner.vue
Normal file
@ -0,0 +1,17 @@
|
||||
<template>
|
||||
<div class="hero-body">
|
||||
<div class="container has-text-centered">
|
||||
<h1 class="title is-1 has-text-primary">
|
||||
eHandyBlog
|
||||
</h1>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { Vue, Component } from 'nuxt-property-decorator'
|
||||
|
||||
@Component
|
||||
export default class MinimalBanner extends Vue {
|
||||
|
||||
}
|
||||
</script>
|
@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div class="columns featured-post is-multiline">
|
||||
<div class="column is-12 post">
|
||||
<h2 class="title is-2">
|
||||
<h2 class="featured-post__title title is-3 has-text-primary">
|
||||
{{ featuredTitle }}
|
||||
</h2>
|
||||
<article class="columns featured">
|
||||
@ -39,7 +39,7 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
<script lang="ts">
|
||||
import { Vue, Component } from 'nuxt-property-decorator'
|
||||
|
||||
@Component({
|
||||
@ -51,6 +51,31 @@ import { Vue, Component } from 'nuxt-property-decorator'
|
||||
}
|
||||
})
|
||||
export default class FeaturedBlog extends Vue {
|
||||
featuredTitle = 'Bài viết mới nhất'
|
||||
featuredTitle: string = 'Latest'
|
||||
}
|
||||
</script>
|
||||
<style lang="scss">
|
||||
.featured-post {
|
||||
.featured-post__title {
|
||||
position: relative;
|
||||
width: fit-content;
|
||||
}
|
||||
|
||||
.featured-post__title::after {
|
||||
content: '';
|
||||
width: 60px;
|
||||
height: 4px;
|
||||
position: absolute;
|
||||
bottom: -5px;
|
||||
left: 0;
|
||||
background-color: #242424;
|
||||
transition: all 0.3s ease-in-out;
|
||||
}
|
||||
|
||||
.featured-post__title:hover {
|
||||
&::after {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@ -1,7 +1,9 @@
|
||||
<template>
|
||||
<b-navbar
|
||||
fixed-top
|
||||
transparent
|
||||
shadow
|
||||
spaced
|
||||
>
|
||||
<template #brand>
|
||||
<b-navbar-item tag="nuxt-link" :to="{ path: '/' }">
|
||||
@ -98,7 +100,16 @@ export default class TheHeader extends Vue {
|
||||
{
|
||||
title: 'Blog',
|
||||
link: '/blog',
|
||||
subMenu: []
|
||||
subMenu: [
|
||||
{
|
||||
title: 'Tất cả bài viết',
|
||||
link: '/blog'
|
||||
},
|
||||
{
|
||||
title: 'Bài viết theo chủ đề',
|
||||
link: '/tags'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
title: 'eHandy Teams',
|
||||
|
24
components/templates/tags/Hero.vue
Normal file
24
components/templates/tags/Hero.vue
Normal file
@ -0,0 +1,24 @@
|
||||
<template>
|
||||
<section class="hero is-info">
|
||||
<div class="hero-body">
|
||||
<h1 class="title is-3 has-text-centered">
|
||||
{{ title }}
|
||||
</h1>
|
||||
<p class="subtitle has-text-centered">
|
||||
{{ subTitle }}
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { Vue, Component, Prop } from 'nuxt-property-decorator'
|
||||
|
||||
@Component
|
||||
export default class Hero extends Vue {
|
||||
@Prop({ type: String })
|
||||
title!: string
|
||||
|
||||
@Prop({ type: String })
|
||||
subTitle!: string;
|
||||
}
|
||||
</script>
|
25
components/templates/tags/ListTags.vue
Normal file
25
components/templates/tags/ListTags.vue
Normal file
@ -0,0 +1,25 @@
|
||||
<template>
|
||||
<section class="section">
|
||||
<div class="container">
|
||||
<ul class="columns">
|
||||
<li v-for="tag in tags" :key="tag" class="column">
|
||||
<nuxt-link
|
||||
:to="{ name: 'tags-tag', params: { tag: tag.toLowerCase() } }"
|
||||
class="text-4xl hover:underline"
|
||||
>
|
||||
{{ tag }}
|
||||
</nuxt-link>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { Vue, Component, Prop } from 'nuxt-property-decorator'
|
||||
|
||||
@Component
|
||||
export default class ListTags extends Vue {
|
||||
@Prop({ type: Array })
|
||||
tags!: string[]
|
||||
}
|
||||
</script>
|
@ -1,8 +1,9 @@
|
||||
---
|
||||
title: How to code
|
||||
description: 'Empower your NuxtJS application with @nuxt/content module: write in a content/ directory and fetch your Markdown, JSON, YAML and CSV files through a MongoDB like API, acting as a Git-based Headless CMS.'
|
||||
author: 'Huwng'
|
||||
image: 'http://placekitten.com/g/1920/1080'
|
||||
tags: ['VueJS', 'Nuxt']
|
||||
tags: ['VueJS', 'Nuxt', 'Frontend']
|
||||
published: '2020-02-12'
|
||||
---
|
||||
|
||||
|
@ -1,8 +1,9 @@
|
||||
---
|
||||
title: Optimize your website
|
||||
description: 'Empower your NuxtJS application with @nuxt/content module: write in a content/ directory and fetch your Markdown, JSON, YAML and CSV files through a MongoDB like API, acting as a Git-based Headless CMS.'
|
||||
author: 'Huwng'
|
||||
image: 'http://placekitten.com/g/1920/1080'
|
||||
tags: ['VueJS', 'Nuxt']
|
||||
tags: ['NodeJs', 'Nuxt', 'Optimize']
|
||||
published: '2020-03-30'
|
||||
---
|
||||
|
||||
|
@ -1,8 +1,9 @@
|
||||
---
|
||||
title: Deep folder Vue
|
||||
description: 'Empower your NuxtJS application with @nuxt/content module: write in a content/ directory and fetch your Markdown, JSON, YAML and CSV files through a MongoDB like API, acting as a Git-based Headless CMS.'
|
||||
author: 'Huwng'
|
||||
image: 'http://placekitten.com/g/1920/1080'
|
||||
tags: ['VueJS']
|
||||
tags: ['vuejs']
|
||||
published: '2020-01-12'
|
||||
---
|
||||
|
||||
|
@ -1,8 +1,9 @@
|
||||
---
|
||||
title: Fisrt Blog Post eHandy
|
||||
description: 'Empower your NuxtJS application with @nuxt/content module: write in a content/ directory and fetch your Markdown, JSON, YAML and CSV files through a MongoDB like API, acting as a Git-based Headless CMS.'
|
||||
author: 'Huwng'
|
||||
image: 'http://placekitten.com/g/1920/1080'
|
||||
tags: ['VueJS', 'Nuxt']
|
||||
tags: ['Share', 'Nuxt']
|
||||
published: '2021-01-30'
|
||||
---
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
---
|
||||
title: Fisrt Blog Post eHandy
|
||||
description: 'Empower your NuxtJS application with @nuxt/content module: write in a content/ directory and fetch your Markdown, JSON, YAML and CSV files through a MongoDB like API, acting as a Git-based Headless CMS.'
|
||||
author: 'Huwng'
|
||||
image: 'https://cdn.emk.dev/templates/featured-image.png'
|
||||
tags: ['VueJS', 'Nuxt']
|
||||
published: '2021-02-30'
|
||||
|
@ -1,8 +1,9 @@
|
||||
---
|
||||
title: Fisrt Blog Post eHandy
|
||||
description: 'Empower your NuxtJS application with @nuxt/content module: write in a content/ directory and fetch your Markdown, JSON, YAML and CSV files through a MongoDB like API, acting as a Git-based Headless CMS.'
|
||||
author: 'Huwng'
|
||||
image: 'http://placekitten.com/g/1920/1080'
|
||||
tags: ['VueJS', 'Nuxt']
|
||||
tags: ['Node', 'Nuxt']
|
||||
published: '2021-03-30'
|
||||
---
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
---
|
||||
title: Learn Nuxt from scratch
|
||||
description: 'Empower your NuxtJS application with @nuxt/content module: write in a content/ directory and fetch your Markdown, JSON, YAML and CSV files through a MongoDB like API, acting as a Git-based Headless CMS.'
|
||||
author: 'Huwng'
|
||||
image: 'http://placekitten.com/g/1920/1080'
|
||||
tags: ['VueJS', 'Nuxt']
|
||||
published: '2021-04-30'
|
||||
|
@ -1,8 +1,9 @@
|
||||
---
|
||||
title: Placeholder image shopify
|
||||
description: 'Empower your NuxtJS application with @nuxt/content module: write in a content/ directory and fetch your Markdown, JSON, YAML and CSV files through a MongoDB like API, acting as a Git-based Headless CMS.'
|
||||
author: 'Huwng'
|
||||
image: 'http://placekitten.com/g/1920/1080'
|
||||
tags: ['VueJS', 'Nuxt']
|
||||
tags: ['Fun', 'Nuxt']
|
||||
published: '2021-05-30'
|
||||
---
|
||||
|
||||
|
@ -1,8 +1,9 @@
|
||||
---
|
||||
title: Shopify partner
|
||||
description: 'Empower your NuxtJS application with @nuxt/content module: write in a content/ directory and fetch your Markdown, JSON, YAML and CSV files through a MongoDB like API, acting as a Git-based Headless CMS.'
|
||||
author: 'Huwng'
|
||||
image: 'http://placekitten.com/g/1920/1080'
|
||||
tags: ['VueJS', 'Nuxt']
|
||||
tags: ['VueJS', 'Shopify']
|
||||
published: '2022-06-30'
|
||||
---
|
||||
|
||||
|
@ -1,12 +0,0 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"~/*": ["./*"],
|
||||
"@/*": ["./*"],
|
||||
"~~/*": ["./*"],
|
||||
"@@/*": ["./*"]
|
||||
}
|
||||
},
|
||||
"exclude": ["node_modules", ".nuxt", "dist"]
|
||||
}
|
@ -11,13 +11,17 @@ export default {
|
||||
{ hid: 'description', name: 'description', content: '' }
|
||||
],
|
||||
link: [
|
||||
{ rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' }
|
||||
{ rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' },
|
||||
{
|
||||
rel: 'stylesheet',
|
||||
href: 'https://fonts.googleapis.com/css2?family=Montserrat:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,100;1,200;1,300;1,400;1,500;1,600;1,700;1,800;1,900&display=swap'
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
// Global CSS: https://go.nuxtjs.dev/config-css
|
||||
css: [
|
||||
'~assets/style/main.scss'
|
||||
'~assets/styles/main.scss'
|
||||
],
|
||||
|
||||
// Plugins to run before rendering page: https://go.nuxtjs.dev/config-plugins
|
||||
@ -29,6 +33,7 @@ export default {
|
||||
|
||||
// Modules for dev and build (recommended): https://go.nuxtjs.dev/config-modules
|
||||
buildModules: [
|
||||
'@nuxt/typescript-build',
|
||||
// https://go.nuxtjs.dev/eslint
|
||||
'@nuxtjs/eslint-module',
|
||||
// https://go.nuxtjs.dev/stylelint
|
||||
@ -44,8 +49,12 @@ export default {
|
||||
// https://go.nuxtjs.dev/pwa
|
||||
'@nuxtjs/pwa',
|
||||
// https://go.nuxtjs.dev/content
|
||||
'@nuxt/content'
|
||||
'@nuxt/content',
|
||||
'@nuxtjs/style-resources'
|
||||
],
|
||||
styleResources: {
|
||||
scss: ['~assets/styles/utils/_utils-dir.scss']
|
||||
},
|
||||
|
||||
// Axios module configuration: https://go.nuxtjs.dev/config-axios
|
||||
axios: {},
|
||||
|
1264
package-lock.json
generated
1264
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -7,7 +7,7 @@
|
||||
"build": "nuxt build",
|
||||
"start": "nuxt start",
|
||||
"generate": "nuxt generate",
|
||||
"lint:js": "eslint --ext \".js,.vue\" --ignore-path .gitignore .",
|
||||
"lint:js": "eslint --ext \".js,.ts,.vue\" --ignore-path .gitignore .",
|
||||
"lint:style": "stylelint \"**/*.{vue,css}\" --ignore-path .gitignore",
|
||||
"lint": "npm run lint:js && npm run lint:style",
|
||||
"test": "jest"
|
||||
@ -29,8 +29,11 @@
|
||||
"devDependencies": {
|
||||
"@commitlint/cli": "^12.0.1",
|
||||
"@commitlint/config-conventional": "^12.0.1",
|
||||
"@nuxtjs/eslint-config": "^6.0.0",
|
||||
"@nuxt/types": "^2.15.7",
|
||||
"@nuxt/typescript-build": "^2.1.0",
|
||||
"@nuxtjs/eslint-config-typescript": "^6.0.1",
|
||||
"@nuxtjs/eslint-module": "^3.0.2",
|
||||
"@nuxtjs/style-resources": "^1.2.0",
|
||||
"@nuxtjs/stylelint-module": "^4.0.0",
|
||||
"@vue/test-utils": "^1.1.3",
|
||||
"babel-core": "7.0.0-bridge.0",
|
||||
@ -43,6 +46,7 @@
|
||||
"husky": "^4.3.8",
|
||||
"jest": "^26.6.3",
|
||||
"lint-staged": "^10.5.4",
|
||||
"node-sass": "^6.0.1",
|
||||
"sass": "~1.32.6",
|
||||
"sass-loader": "^10.2.0",
|
||||
"stylelint": "^13.12.0",
|
||||
|
@ -1,29 +1,51 @@
|
||||
<template>
|
||||
<article class="container content">
|
||||
<nuxt-content :document="blogDetail" />
|
||||
</article>
|
||||
<div class="blog-detail container is-max-desktop content">
|
||||
<section class="blog-detail__featured-img" style="margin-top: 2rem;">
|
||||
<img :src="blogDetail.image">
|
||||
</section>
|
||||
<section class="section blog-detail__info">
|
||||
<h1 class="title is-1 has-text-primary">
|
||||
{{ blogDetail.title }}
|
||||
</h1>
|
||||
<div>
|
||||
{{ blogDetail.description }}
|
||||
</div>
|
||||
<div>
|
||||
<span>{{ blogDetail.author }}</span>
|
||||
<span>{{ blogDetail.published }}</span>
|
||||
</div>
|
||||
<hr>
|
||||
</section>
|
||||
<section class="section">
|
||||
<div class="columns">
|
||||
<div class="column">
|
||||
<nuxt-content :document="blogDetail" />
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { Vue, Component } from 'nuxt-property-decorator'
|
||||
import global from '@/utils/global'
|
||||
import getSiteMeta from '@/utils/getSiteMeta'
|
||||
|
||||
@Component({
|
||||
name: 'ArticlePage',
|
||||
name: 'BlogPage',
|
||||
async asyncData ({ $content, params }) {
|
||||
const findedBlog = await $content('blog', { deep: true })
|
||||
.where({ slug: params.slug })
|
||||
.fetch()
|
||||
const [blogDetail] = findedBlog
|
||||
console.log(blogDetail)
|
||||
|
||||
const [prev, next] = await $content('blog')
|
||||
const [prev, next] = await $content('blog', { deep: true })
|
||||
.only(['title', 'slug', 'published'])
|
||||
.sortBy('published', 'desc')
|
||||
.surround(params.slug)
|
||||
.fetch()
|
||||
|
||||
const allBlogs = await $content('blog')
|
||||
const allBlogs = await $content('blog', { deep: true })
|
||||
.only(['title', 'description', 'image', 'slug', 'published', 'tags'])
|
||||
.sortBy('published', 'desc')
|
||||
.fetch()
|
||||
@ -89,3 +111,18 @@ import getSiteMeta from '@/utils/getSiteMeta'
|
||||
})
|
||||
export default class BlogDetail extends Vue {}
|
||||
</script>
|
||||
<style lang="scss">
|
||||
.blog-detail {
|
||||
section:not(:last-child) {
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
section:not(:first-child) {
|
||||
padding-top: 1rem;
|
||||
}
|
||||
|
||||
[aria-hidden="true"] {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@ -1,23 +1,26 @@
|
||||
<template>
|
||||
<section class="section">
|
||||
<div class="container">
|
||||
<div class="columns">
|
||||
<div class="column is-10 is-offset-1">
|
||||
<FeaturedBlog :first-blog="allArticles[0]" />
|
||||
<hr>
|
||||
<ListBlog :list-blogs="paginatedArticles" />
|
||||
<div class="container">
|
||||
<MinimalBanner />
|
||||
<section class="section">
|
||||
<div class="container">
|
||||
<div class="columns">
|
||||
<div class="column">
|
||||
<FeaturedBlog :first-blog="allArticles[0]" />
|
||||
<hr>
|
||||
<ListBlog :list-blogs="paginatedArticles" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="columns">
|
||||
<div class="column">
|
||||
<Pagination
|
||||
:total="allArticles.length"
|
||||
:per-page="perPage"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="columns">
|
||||
<div class="column">
|
||||
<Pagination
|
||||
:total="allArticles.length"
|
||||
:per-page="perPage"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</section>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
61
pages/tags/_tag.vue
Normal file
61
pages/tags/_tag.vue
Normal file
@ -0,0 +1,61 @@
|
||||
<template>
|
||||
<div class="container">
|
||||
<section class="section tags-page-wrapper">
|
||||
<div class="tags-page__header">
|
||||
<h2>
|
||||
Tag: {{ $route.params.tag }}
|
||||
</h2>
|
||||
</div>
|
||||
<div class="tags-page__content-wrapper">
|
||||
<ListBlog :list-blogs="blogsByTag" />
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { Vue, Component } from 'nuxt-property-decorator'
|
||||
import ListBlog from '@/components/organisms/ListBlog.vue'
|
||||
|
||||
@Component({
|
||||
components: {
|
||||
ListBlog
|
||||
},
|
||||
async asyncData ({ $content, params }) {
|
||||
/*
|
||||
* Folder inside /content
|
||||
*/
|
||||
const path = 'blog'
|
||||
const blog = await $content(path, { deep: true })
|
||||
.only(['title', 'description', 'image', 'slug', 'published', 'tags'])
|
||||
.sortBy('published', 'desc')
|
||||
.fetch()
|
||||
|
||||
const blogsByTag = blog.filter((article) => {
|
||||
const blogTags = article.tags.map(x => x.toLowerCase())
|
||||
return blogTags.includes(params.tag)
|
||||
})
|
||||
|
||||
return {
|
||||
blogsByTag
|
||||
}
|
||||
},
|
||||
head () {
|
||||
return {
|
||||
title: `Blog Tagged - ${this.captialise(this.$route.params.tag)}`,
|
||||
link: [
|
||||
{
|
||||
hid: 'canonical',
|
||||
rel: 'canonical',
|
||||
href: `${process.env.baseUrl}/tags/${this.$route.params.tag}`
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
})
|
||||
export default class TagPage extends Vue {
|
||||
captialise (text) {
|
||||
return text.charAt(0).toUpperCase() + text.slice(1)
|
||||
}
|
||||
}
|
||||
</script>
|
56
pages/tags/index.vue
Normal file
56
pages/tags/index.vue
Normal file
@ -0,0 +1,56 @@
|
||||
<template>
|
||||
<div class="container">
|
||||
<HeroSection
|
||||
title="Tags"
|
||||
sub-title="All tags in eHandy"
|
||||
/>
|
||||
<ListTags :tags="tags" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { IContentDocument } from '@nuxt/content/types/content'
|
||||
import { Vue, Component } from 'nuxt-property-decorator'
|
||||
import Hero from '@/components/templates/tags/Hero.vue'
|
||||
import ListTags from '@/components/templates/tags/ListTags.vue'
|
||||
|
||||
@Component({
|
||||
components: {
|
||||
HeroSection: Hero,
|
||||
ListTags
|
||||
},
|
||||
async asyncData ({ $content }) {
|
||||
function onlyUnique (value: string, index: number, self: string[]) {
|
||||
return self.indexOf(value) === index
|
||||
}
|
||||
|
||||
const blog: IContentDocument[] = await $content('blog', { deep: true })
|
||||
.only(['tags'])
|
||||
.fetch<IContentDocument[]>()
|
||||
|
||||
/**
|
||||
* Tags của tất cả pài viết
|
||||
*/
|
||||
const tags = blog
|
||||
.flatMap(blog => blog.tags)
|
||||
.filter(onlyUnique)
|
||||
|
||||
return {
|
||||
tags
|
||||
}
|
||||
},
|
||||
head () {
|
||||
return {
|
||||
title: 'Article Tags - Learning Laravel and VueJS',
|
||||
link: [
|
||||
{
|
||||
hid: 'canonical',
|
||||
rel: 'canonical',
|
||||
href: `${process.env.baseUrl}/tags`
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
})
|
||||
export default class TagListPage extends Vue {}
|
||||
</script>
|
38
tsconfig.json
Normal file
38
tsconfig.json
Normal file
@ -0,0 +1,38 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2018",
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "Node",
|
||||
"lib": [
|
||||
"ESNext",
|
||||
"ESNext.AsyncIterable",
|
||||
"DOM"
|
||||
],
|
||||
"esModuleInterop": true,
|
||||
"allowJs": true,
|
||||
"sourceMap": true,
|
||||
"strict": true,
|
||||
"noEmit": true,
|
||||
"experimentalDecorators": true,
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"~/*": [
|
||||
"./*"
|
||||
],
|
||||
"@/*": [
|
||||
"./*"
|
||||
]
|
||||
},
|
||||
"types": [
|
||||
"@nuxt/types",
|
||||
"@nuxtjs/axios",
|
||||
"@nuxt/content",
|
||||
"@types/node"
|
||||
]
|
||||
},
|
||||
"exclude": [
|
||||
"node_modules",
|
||||
".nuxt",
|
||||
"dist"
|
||||
]
|
||||
}
|
@ -15,4 +15,6 @@ export interface IContent extends IContentDocument {
|
||||
toc: IToc[];
|
||||
image: string;
|
||||
tags: string[];
|
||||
published: string;
|
||||
author: string;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user