add post list
This commit is contained in:
parent
864efab01c
commit
eff8dbc671
@ -5,13 +5,9 @@
|
|||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
<meta name="viewport" content="width=device-width,initial-scale=1.0">
|
<meta name="viewport" content="width=device-width,initial-scale=1.0">
|
||||||
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
|
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
|
||||||
<title><%= htmlWebpackPlugin.options.title %></title>
|
<title>Vue 3</title>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<noscript>
|
|
||||||
<strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
|
|
||||||
</noscript>
|
|
||||||
<div id="app"></div>
|
<div id="app"></div>
|
||||||
<!-- built files will be auto injected -->
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
78
src/App.vue
78
src/App.vue
@ -1,30 +1,72 @@
|
|||||||
<template>
|
<template>
|
||||||
<nav>
|
<div class="app">
|
||||||
<router-link to="/">Home</router-link> |
|
<h1>Страница постов</h1>
|
||||||
<router-link to="/about">About</router-link>
|
<my-button @click="showCreateDialog" class="createBtn">
|
||||||
</nav>
|
Создать пост
|
||||||
<router-view/>
|
</my-button>
|
||||||
|
|
||||||
|
<my-dialog v-model:show="dialogVisible">
|
||||||
|
<post-form @createPost="createPost" />
|
||||||
|
</my-dialog>
|
||||||
|
|
||||||
|
<post-list :posts="posts" @removePost="removePost" class="postList"/>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import PostForm from '@/components/PostForm.vue';
|
||||||
|
import PostList from '@/components/PostList.vue';
|
||||||
|
import MyDialog from '@/components/ui/Dialog.vue';
|
||||||
|
import MyButton from '@/components/ui/Button.vue';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
MyButton,
|
||||||
|
MyDialog,
|
||||||
|
PostForm,
|
||||||
|
PostList
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
posts: [
|
||||||
|
{id: 1, title: 'Javascript', body: 'Описание поста' },
|
||||||
|
{id: 2, title: 'Typescript', body: 'Описание поста' },
|
||||||
|
{id: 3, title: 'GO lang', body: 'Описание поста' },
|
||||||
|
],
|
||||||
|
dialogVisible: false,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
createPost(newPost) {
|
||||||
|
this.posts.push(newPost);
|
||||||
|
this.dialogVisible = false;
|
||||||
|
},
|
||||||
|
removePost(post) {
|
||||||
|
this.posts = this.posts.filter(p => p.id !== post.id)
|
||||||
|
},
|
||||||
|
showCreateDialog() {
|
||||||
|
this.dialogVisible = true;
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
#app {
|
* {
|
||||||
font-family: Avenir, Helvetica, Arial, sans-serif;
|
margin: 0;
|
||||||
-webkit-font-smoothing: antialiased;
|
padding: 0;
|
||||||
-moz-osx-font-smoothing: grayscale;
|
box-sizing: border-box;
|
||||||
text-align: center;
|
|
||||||
color: #2c3e50;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
nav {
|
.app {
|
||||||
padding: 30px;
|
padding: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
nav a {
|
.createBtn {
|
||||||
font-weight: bold;
|
margin-top: 15px;
|
||||||
color: #2c3e50;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
nav a.router-link-exact-active {
|
.postList {
|
||||||
color: #42b983;
|
margin-top: 15px;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 6.7 KiB |
@ -1,59 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="hello">
|
|
||||||
<h1>{{ msg }}</h1>
|
|
||||||
<p>
|
|
||||||
For a guide and recipes on how to configure / customize this project,<br>
|
|
||||||
check out the
|
|
||||||
<a href="https://cli.vuejs.org" target="_blank" rel="noopener">vue-cli documentation</a>.
|
|
||||||
</p>
|
|
||||||
<h3>Installed CLI Plugins</h3>
|
|
||||||
<ul>
|
|
||||||
<li><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-babel" target="_blank" rel="noopener">babel</a></li>
|
|
||||||
<li><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-router" target="_blank" rel="noopener">router</a></li>
|
|
||||||
<li><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-vuex" target="_blank" rel="noopener">vuex</a></li>
|
|
||||||
</ul>
|
|
||||||
<h3>Essential Links</h3>
|
|
||||||
<ul>
|
|
||||||
<li><a href="https://vuejs.org" target="_blank" rel="noopener">Core Docs</a></li>
|
|
||||||
<li><a href="https://forum.vuejs.org" target="_blank" rel="noopener">Forum</a></li>
|
|
||||||
<li><a href="https://chat.vuejs.org" target="_blank" rel="noopener">Community Chat</a></li>
|
|
||||||
<li><a href="https://twitter.com/vuejs" target="_blank" rel="noopener">Twitter</a></li>
|
|
||||||
<li><a href="https://news.vuejs.org" target="_blank" rel="noopener">News</a></li>
|
|
||||||
</ul>
|
|
||||||
<h3>Ecosystem</h3>
|
|
||||||
<ul>
|
|
||||||
<li><a href="https://router.vuejs.org" target="_blank" rel="noopener">vue-router</a></li>
|
|
||||||
<li><a href="https://vuex.vuejs.org" target="_blank" rel="noopener">vuex</a></li>
|
|
||||||
<li><a href="https://github.com/vuejs/vue-devtools#vue-devtools" target="_blank" rel="noopener">vue-devtools</a></li>
|
|
||||||
<li><a href="https://vue-loader.vuejs.org" target="_blank" rel="noopener">vue-loader</a></li>
|
|
||||||
<li><a href="https://github.com/vuejs/awesome-vue" target="_blank" rel="noopener">awesome-vue</a></li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
export default {
|
|
||||||
name: 'HelloWorld',
|
|
||||||
props: {
|
|
||||||
msg: String
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<!-- Add "scoped" attribute to limit CSS to this component only -->
|
|
||||||
<style scoped>
|
|
||||||
h3 {
|
|
||||||
margin: 40px 0 0;
|
|
||||||
}
|
|
||||||
ul {
|
|
||||||
list-style-type: none;
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
li {
|
|
||||||
display: inline-block;
|
|
||||||
margin: 0 10px;
|
|
||||||
}
|
|
||||||
a {
|
|
||||||
color: #42b983;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
38
src/components/Post.vue
Normal file
38
src/components/Post.vue
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
<template>
|
||||||
|
<div class="post">
|
||||||
|
<div>
|
||||||
|
<div><strong>Название:</strong> {{post.title}}</div>
|
||||||
|
<div><strong>Описание:</strong> {{post.body}}</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="post__btns">
|
||||||
|
<my-button @click="$emit('removePost', post)">
|
||||||
|
Удалить
|
||||||
|
</my-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
|
||||||
|
export default {
|
||||||
|
props: {
|
||||||
|
post: {
|
||||||
|
type: Object,
|
||||||
|
required: true,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.post {
|
||||||
|
margin-top: 15px;
|
||||||
|
padding: 15px;
|
||||||
|
border: 2px solid teal;
|
||||||
|
border-radius: 5px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
69
src/components/PostForm.vue
Normal file
69
src/components/PostForm.vue
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
<template>
|
||||||
|
<form @submit.prevent="submit">
|
||||||
|
<h4>Создание поста</h4>
|
||||||
|
<my-input
|
||||||
|
v-model="formState.title"
|
||||||
|
class="input"
|
||||||
|
type="text"
|
||||||
|
name="title"
|
||||||
|
placeholder="Введите название поста"
|
||||||
|
/>
|
||||||
|
<my-input
|
||||||
|
v-model="formState.body"
|
||||||
|
class="input"
|
||||||
|
type="text"
|
||||||
|
placeholder="Введите описание поста"
|
||||||
|
name="body"
|
||||||
|
/>
|
||||||
|
<my-button type="submit" class="btn">Создать</my-button>
|
||||||
|
</form>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
formState: {
|
||||||
|
title: '',
|
||||||
|
body: ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
submit() {
|
||||||
|
const newPost = {
|
||||||
|
id: Date.now(),
|
||||||
|
title: this.formState.title,
|
||||||
|
body: this.formState.body,
|
||||||
|
}
|
||||||
|
|
||||||
|
this.$emit('createPost', newPost);
|
||||||
|
this.clearFormState();
|
||||||
|
},
|
||||||
|
|
||||||
|
clearFormState() {
|
||||||
|
this.formState = {
|
||||||
|
title: '',
|
||||||
|
body: ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
form {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn {
|
||||||
|
align-self: flex-end;
|
||||||
|
margin-top: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input {
|
||||||
|
margin-top: 15px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
36
src/components/PostList.vue
Normal file
36
src/components/PostList.vue
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
<template>
|
||||||
|
<div v-if="posts.length > 0">
|
||||||
|
<h3>Список постов</h3>
|
||||||
|
<post
|
||||||
|
v-for="post in posts"
|
||||||
|
:post="post"
|
||||||
|
:key="post.id"
|
||||||
|
@removePost="$emit('removePost', post)"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div v-else>
|
||||||
|
<h2>
|
||||||
|
Отсутствуют посты
|
||||||
|
</h2>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import Post from '@/components/Post.vue';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components:{
|
||||||
|
Post,
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
posts: {
|
||||||
|
type: Array,
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
||||||
21
src/components/ui/Button.vue
Normal file
21
src/components/ui/Button.vue
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
<template>
|
||||||
|
<button class="btn">
|
||||||
|
<slot/>
|
||||||
|
</button>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'my-button'
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.btn {
|
||||||
|
padding: 10px 15px;
|
||||||
|
background: none;
|
||||||
|
color: teal;
|
||||||
|
border: 1px solid teal;
|
||||||
|
border-radius: 5px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
45
src/components/ui/Dialog.vue
Normal file
45
src/components/ui/Dialog.vue
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
<template>
|
||||||
|
<div class="dialog" v-if="show" @click="hideDialog">
|
||||||
|
<div class="dialog__content" @click.stop>
|
||||||
|
<slot></slot>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'my-dialog',
|
||||||
|
props: {
|
||||||
|
show: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
hideDialog() {
|
||||||
|
this.$emit('update:show', false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.dialog {
|
||||||
|
top: 0;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
background: rgba(0, 0, 0, 0.5);
|
||||||
|
position: fixed;
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dialog__content {
|
||||||
|
margin: auto;
|
||||||
|
background: white;
|
||||||
|
border-radius: 12px;
|
||||||
|
min-height: 50px;
|
||||||
|
min-width: 300px;
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
31
src/components/ui/Input.vue
Normal file
31
src/components/ui/Input.vue
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
<template>
|
||||||
|
<input
|
||||||
|
class="input"
|
||||||
|
type="text"
|
||||||
|
:value="modelValue"
|
||||||
|
@input="updateModelValue"
|
||||||
|
>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'my-input',
|
||||||
|
props: {
|
||||||
|
modelValue: [String, Number],
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
updateModelValue(event) {
|
||||||
|
this.$emit('update:modelValue', event.target.value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.input {
|
||||||
|
width: 100%;
|
||||||
|
border: 1px solid teal;
|
||||||
|
padding: 10px 15px;
|
||||||
|
border-radius: 5px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
9
src/components/ui/index.js
Normal file
9
src/components/ui/index.js
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
import Button from '@/components/ui/Button.vue';
|
||||||
|
import Input from '@/components/ui/Input.vue';
|
||||||
|
import Dialog from '@/components/ui/Dialog.vue';
|
||||||
|
|
||||||
|
export default [
|
||||||
|
Button,
|
||||||
|
Input,
|
||||||
|
Dialog
|
||||||
|
]
|
||||||
14
src/main.js
14
src/main.js
@ -1,6 +1,12 @@
|
|||||||
import { createApp } from 'vue'
|
import { createApp } from 'vue'
|
||||||
import App from './App.vue'
|
import App from './App';
|
||||||
import router from './router'
|
|
||||||
import store from './store'
|
|
||||||
|
|
||||||
createApp(App).use(store).use(router).mount('#app')
|
import components from '@/components/ui';
|
||||||
|
|
||||||
|
const app = createApp(App);
|
||||||
|
|
||||||
|
components.forEach(component => {
|
||||||
|
app.component(component.name, component)
|
||||||
|
})
|
||||||
|
|
||||||
|
app.mount('#app')
|
||||||
|
|||||||
@ -1,25 +0,0 @@
|
|||||||
import { createRouter, createWebHistory } from 'vue-router'
|
|
||||||
import HomeView from '../views/HomeView.vue'
|
|
||||||
|
|
||||||
const routes = [
|
|
||||||
{
|
|
||||||
path: '/',
|
|
||||||
name: 'home',
|
|
||||||
component: HomeView
|
|
||||||
},
|
|
||||||
{
|
|
||||||
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/AboutView.vue')
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
||||||
const router = createRouter({
|
|
||||||
history: createWebHistory(process.env.BASE_URL),
|
|
||||||
routes
|
|
||||||
})
|
|
||||||
|
|
||||||
export default router
|
|
||||||
@ -1,14 +0,0 @@
|
|||||||
import { createStore } from 'vuex'
|
|
||||||
|
|
||||||
export default createStore({
|
|
||||||
state: {
|
|
||||||
},
|
|
||||||
getters: {
|
|
||||||
},
|
|
||||||
mutations: {
|
|
||||||
},
|
|
||||||
actions: {
|
|
||||||
},
|
|
||||||
modules: {
|
|
||||||
}
|
|
||||||
})
|
|
||||||
@ -1,5 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="about">
|
|
||||||
<h1>This is an about page</h1>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
@ -1,18 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="home">
|
|
||||||
<img alt="Vue logo" src="../assets/logo.png">
|
|
||||||
<HelloWorld msg="Welcome to Your Vue.js App"/>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
// @ is an alias to /src
|
|
||||||
import HelloWorld from '@/components/HelloWorld.vue'
|
|
||||||
|
|
||||||
export default {
|
|
||||||
name: 'HomeView',
|
|
||||||
components: {
|
|
||||||
HelloWorld
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
Loading…
x
Reference in New Issue
Block a user