add vuex
This commit is contained in:
parent
76e26d876a
commit
0eafaeb27f
@ -14,6 +14,10 @@
|
||||
<my-button @click="$router.push('/about')">
|
||||
О Нас
|
||||
</my-button>
|
||||
|
||||
<router-link to="/store">
|
||||
<my-button>Посты Vuex</my-button>
|
||||
</router-link>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -4,6 +4,7 @@ import App from './App';
|
||||
import components from '@/components/ui';
|
||||
import dirictives from '@/dirictives';
|
||||
import router from '@/router/router';
|
||||
import store from '@/store';
|
||||
|
||||
const app = createApp(App);
|
||||
|
||||
@ -18,4 +19,5 @@ dirictives.forEach(dirictive => {
|
||||
|
||||
app
|
||||
.use(router)
|
||||
.use(store)
|
||||
.mount('#app')
|
||||
|
||||
134
src/pages/PostsPageWithStore.vue
Normal file
134
src/pages/PostsPageWithStore.vue
Normal file
@ -0,0 +1,134 @@
|
||||
<template>
|
||||
<div>
|
||||
<h1>Страница постов (Vuex)</h1>
|
||||
|
||||
<my-input
|
||||
v-focus
|
||||
:model-value="searchQuery"
|
||||
@update:model-value="setSearchQuery"
|
||||
placeholder="Поиск"
|
||||
class="search"
|
||||
/>
|
||||
|
||||
<div class="app__btns">
|
||||
<my-button @click="showCreateDialog" class="createBtn">
|
||||
Создать пост
|
||||
</my-button>
|
||||
|
||||
<my-select
|
||||
:model-value="selectedSort"
|
||||
@update:model-value="setSelectedSort"
|
||||
:options="sortOptions"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<my-dialog v-model:show="dialogVisible">
|
||||
<post-form @createPost="createPost" />
|
||||
</my-dialog>
|
||||
|
||||
<div v-if="isPostLoading" style="margin-top: 15px">Loading...</div>
|
||||
|
||||
<post-list v-else :posts="sortedAndSearchedPosts" @removePost="removePost" class="postList"/>
|
||||
|
||||
<div v-intersection="getIntersectionData()" ref="observer" class="observer"></div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import PostForm from '@/components/PostForm.vue';
|
||||
import PostList from '@/components/PostList.vue';
|
||||
import {mapActions, mapState, mapGetters, mapMutations} from 'vuex';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
PostForm,
|
||||
PostList
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
dialogVisible: false,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
...mapMutations({
|
||||
setPage: 'post/setPage',
|
||||
setSearchQuery: 'post/setSearchQuery',
|
||||
setSelectedSort: 'post/setSelectedSort',
|
||||
}),
|
||||
...mapActions({
|
||||
fetchPosts: 'post/fetchPosts',
|
||||
loadMorePost: 'post/loadMorePost'
|
||||
}),
|
||||
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;
|
||||
},
|
||||
getIntersectionData() {
|
||||
return {
|
||||
loadMorePost: this.loadMorePost,
|
||||
page: this.page,
|
||||
totalPages: this.totalPages,
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapState({
|
||||
posts: (state) => state.post.posts,
|
||||
isPostLoading: (state) => state.post.isPostLoading,
|
||||
selectedSort: (state) => state.post.selectedSort,
|
||||
sortOptions: (state) => state.post.sortOptions,
|
||||
searchQuery: (state) => state.post.searchQuery,
|
||||
page: (state) => state.post.page,
|
||||
limit: (state) => state.post.limit,
|
||||
totalPages: (state) => state.post.totalPagess
|
||||
}),
|
||||
...mapGetters({
|
||||
sortedPost: 'post/sortedPost',
|
||||
sortedAndSearchedPosts: 'post/sortedAndSearchedPosts'
|
||||
})
|
||||
},
|
||||
watch: {
|
||||
dialogVisible(newValue) {
|
||||
if (newValue) {
|
||||
document.body.style.overflow = 'hidden';
|
||||
} else {
|
||||
document.body.style.overflow = 'auto';
|
||||
}
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.fetchPosts();
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.createBtn {
|
||||
margin-top: 15px;
|
||||
}
|
||||
|
||||
.postList {
|
||||
margin-top: 15px;
|
||||
}
|
||||
|
||||
.app__btns {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.search {
|
||||
margin-top: 15px;
|
||||
}
|
||||
|
||||
.observer {
|
||||
height: 0px;
|
||||
background: teal;
|
||||
}
|
||||
</style>
|
||||
@ -3,6 +3,7 @@ import { createRouter, createWebHistory } from 'vue-router';
|
||||
import PostsPage from '@/pages/PostsPage.vue';
|
||||
import AboutPage from '@/pages/AboutPage.vue';
|
||||
import PostPage from '@/pages/PostPage.vue';
|
||||
import PostsPageWithStore from '@/pages/PostsPageWithStore.vue';
|
||||
|
||||
const routes = [
|
||||
{
|
||||
@ -20,6 +21,10 @@ const routes = [
|
||||
{
|
||||
path: '/about',
|
||||
component: AboutPage,
|
||||
},
|
||||
{
|
||||
path: '/store',
|
||||
component: PostsPageWithStore,
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
8
src/store/index.js
Normal file
8
src/store/index.js
Normal file
@ -0,0 +1,8 @@
|
||||
import { createStore } from 'vuex';
|
||||
import { postModule } from '@/store/postModule';
|
||||
|
||||
export default createStore({
|
||||
modules: {
|
||||
post: postModule,
|
||||
},
|
||||
})
|
||||
83
src/store/postModule.js
Normal file
83
src/store/postModule.js
Normal file
@ -0,0 +1,83 @@
|
||||
import axios from 'axios';
|
||||
|
||||
export const postModule = {
|
||||
state: () => ({
|
||||
posts: [],
|
||||
isPostLoading: false,
|
||||
selectedSort: '',
|
||||
sortOptions: [
|
||||
{value: 'title', name: 'По названию'},
|
||||
{value: 'body', name: 'По описанию'}
|
||||
],
|
||||
searchQuery: '',
|
||||
page: 1,
|
||||
limit: 10,
|
||||
totalPages: 0
|
||||
}),
|
||||
getters: {
|
||||
sortedPost(state) {
|
||||
return [...state.posts].sort((post1, post2) => {
|
||||
return post1?.[state.selectedSort]?.localeCompare(post2?.[state.selectedSort])
|
||||
})
|
||||
},
|
||||
sortedAndSearchedPosts(state, getters) {
|
||||
return getters.sortedPost.filter((post) => post.title.toLowerCase().includes(state.searchQuery.toLowerCase()))
|
||||
}
|
||||
},
|
||||
mutations: {
|
||||
setPosts: (state, posts) => {
|
||||
state.posts = posts
|
||||
},
|
||||
setIsPostLoading: (state, isPostLoading) => {
|
||||
state.isPostLoading = isPostLoading
|
||||
},
|
||||
setSelectedSort: (state, selectedSort) => {
|
||||
state.selectedSort = selectedSort
|
||||
},
|
||||
setSearchQuery: (state, searchQuery) => {
|
||||
state.searchQuery = searchQuery
|
||||
},
|
||||
setPage: (state, page) => {
|
||||
state.page = page
|
||||
},
|
||||
setTotalPages: (state, totalPages) => {
|
||||
state.totalPages = totalPages
|
||||
},
|
||||
},
|
||||
actions: {
|
||||
async fetchPosts({state, commit}) {
|
||||
try {
|
||||
commit('setIsPostLoading', true);
|
||||
const { data, headers } = await axios.get(`https://jsonplaceholder.typicode.com/posts`, {
|
||||
params: {
|
||||
_limit: state.limit,
|
||||
_page: state.page
|
||||
}
|
||||
});
|
||||
|
||||
commit('setPosts', data);
|
||||
commit('setTotalPages', Math.ceil(headers['x-total-count'] / state.limit))
|
||||
} catch (e) {
|
||||
alert(e.message);
|
||||
} finally {
|
||||
commit('setIsPostLoading', false);
|
||||
}
|
||||
},
|
||||
async loadMorePost({state, commit}) {
|
||||
commit('setPage', state.page + 1)
|
||||
try {
|
||||
const { data, headers } = await axios.get(`https://jsonplaceholder.typicode.com/posts`, {
|
||||
params: {
|
||||
_limit: state.limit,
|
||||
_page: state.page,
|
||||
}
|
||||
});
|
||||
commit('setPosts', [...state.posts, ...data]);
|
||||
commit('setTotalPages', Math.ceil(headers['x-total-count'] / state.limit))
|
||||
} catch (e) {
|
||||
alert(e.message);
|
||||
}
|
||||
},
|
||||
},
|
||||
namespaced: true,
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user