add vuex
This commit is contained in:
parent
76e26d876a
commit
0eafaeb27f
@ -14,6 +14,10 @@
|
|||||||
<my-button @click="$router.push('/about')">
|
<my-button @click="$router.push('/about')">
|
||||||
О Нас
|
О Нас
|
||||||
</my-button>
|
</my-button>
|
||||||
|
|
||||||
|
<router-link to="/store">
|
||||||
|
<my-button>Посты Vuex</my-button>
|
||||||
|
</router-link>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@ -4,6 +4,7 @@ import App from './App';
|
|||||||
import components from '@/components/ui';
|
import components from '@/components/ui';
|
||||||
import dirictives from '@/dirictives';
|
import dirictives from '@/dirictives';
|
||||||
import router from '@/router/router';
|
import router from '@/router/router';
|
||||||
|
import store from '@/store';
|
||||||
|
|
||||||
const app = createApp(App);
|
const app = createApp(App);
|
||||||
|
|
||||||
@ -18,4 +19,5 @@ dirictives.forEach(dirictive => {
|
|||||||
|
|
||||||
app
|
app
|
||||||
.use(router)
|
.use(router)
|
||||||
|
.use(store)
|
||||||
.mount('#app')
|
.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 PostsPage from '@/pages/PostsPage.vue';
|
||||||
import AboutPage from '@/pages/AboutPage.vue';
|
import AboutPage from '@/pages/AboutPage.vue';
|
||||||
import PostPage from '@/pages/PostPage.vue';
|
import PostPage from '@/pages/PostPage.vue';
|
||||||
|
import PostsPageWithStore from '@/pages/PostsPageWithStore.vue';
|
||||||
|
|
||||||
const routes = [
|
const routes = [
|
||||||
{
|
{
|
||||||
@ -20,6 +21,10 @@ const routes = [
|
|||||||
{
|
{
|
||||||
path: '/about',
|
path: '/about',
|
||||||
component: AboutPage,
|
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