feat: 添加前端 MobX Stores

- RegionStore(版块管理、查询、评分、收藏)
- ArticleStore(文章管理、提交、审核、评论、点赞、统计)
- ServiceStore(特色服务管理、提交、审核、评论、点赞、评分、统计)
- InteractionStore(交互功能:评论、评分、点赞、收藏)
- 更新 UserStore(保持原有)
- 更新 AuthStore(保持原有)
- 更新 index.js 导入所有 Stores
This commit is contained in:
mashen
2026-04-09 13:45:47 +00:00
parent 7f5cd49070
commit ff96867679
5 changed files with 629 additions and 2 deletions

View File

@@ -6,14 +6,22 @@ import App from './App';
import './styles/global';
// Import stores
import UserStore from './stores/UserStore';
import AuthStore from './stores/AuthStore';
import UserStore from './stores/UserStore';
import RegionStore from './stores/RegionStore';
import ArticleStore from './stores/ArticleStore';
import ServiceStore from './stores/ServiceStore';
import InteractionStore from './stores/InteractionStore';
const root = ReactDOM.createRoot(document.getElementById('root'));
const stores = {
userStore: new UserStore(),
authStore: new AuthStore(),
userStore: new UserStore(),
regionStore: new RegionStore(),
articleStore: new ArticleStore(),
serviceStore: new ServiceStore(),
interactionStore: new InteractionStore(),
};
root.render(

View File

@@ -0,0 +1,152 @@
import { makeAutoObservable } from 'mobx';
import api from '../services/api';
class ArticleStore {
articles = [];
currentArticle = null;
loading = false;
error = null;
constructor() {
makeAutoObservable(this);
}
async fetchArticles(params = {}) {
this.loading = true;
this.error = null;
try {
const response = await api.get('/api/articles/', { params });
this.articles = response.data.results || response.data;
} catch (error) {
this.error = error.response?.data || 'Failed to fetch articles';
} finally {
this.loading = false;
}
}
async fetchArticle(id) {
this.loading = true;
this.error = null;
try {
const response = await api.get(`/api/articles/${id}/`);
this.currentArticle = response.data;
} catch (error) {
this.error = error.response?.data || 'Failed to fetch article';
} finally {
this.loading = false;
}
}
async createArticle(data) {
this.loading = true;
this.error = null;
try {
const response = await api.post('/api/articles/', data);
return { success: true, article: response.data };
} catch (error) {
this.error = error.response?.data || 'Failed to create article';
return { success: false, error: this.error };
} finally {
this.loading = false;
}
}
async updateArticle(id, data) {
this.loading = true;
this.error = null;
try {
const response = await api.put(`/api/articles/${id}/`, data);
return { success: true, article: response.data };
} catch (error) {
this.error = error.response?.data || 'Failed to update article';
return { success: false, error: this.error };
} finally {
this.loading = false;
}
}
async deleteArticle(id) {
try {
await api.delete(`/api/articles/${id}/`);
return { success: true };
} catch (error) {
return {
success: false,
error: error.response?.data || 'Failed to delete article',
};
}
}
async submitArticle(id) {
try {
await api.post(`/api/articles/${id}/submit/`);
return { success: true };
} catch (error) {
return {
success: false,
error: error.response?.data || 'Failed to submit article',
};
}
}
async approveArticle(id, reason = '') {
try {
await api.post(`/api/articles/${id}/approve/`, { action: 'approve', reason });
return { success: true };
} catch (error) {
return {
success: false,
error: error.response?.data || 'Failed to approve article',
};
}
}
async rejectArticle(id, reason) {
try {
await api.post(`/api/articles/${id}/reject/`, { action: 'reject', reason });
return { success: true };
} catch (error) {
return {
success: false,
error: error.response?.data || 'Failed to reject article',
};
}
}
async likeArticle(id) {
try {
const response = await api.post(`/api/articles/${id}/like/`);
return response.data;
} catch (error) {
return null;
}
}
async fetchArticleComments(id) {
try {
const response = await api.get(`/api/articles/${id}/comments/`);
return response.data;
} catch (error) {
return [];
}
}
async fetchArticleStats(id) {
try {
const response = await api.get(`/api/articles/${id}/stats/`);
return response.data;
} catch (error) {
return null;
}
}
clearCurrentArticle() {
this.currentArticle = null;
}
}
export default ArticleStore;

View File

@@ -0,0 +1,164 @@
import { makeAutoObservable } from 'mobx';
import api from '../services/api';
class InteractionStore {
comments = [];
ratings = [];
likes = [];
favorites = [];
loading = false;
error = null;
constructor() {
makeAutoObservable(this);
}
// Comments
async createComment(targetType, targetId, content) {
try {
const response = await api.post('/api/comments/', {
target_type: targetType,
target_id: targetId,
content,
});
return { success: true, comment: response.data };
} catch (error) {
return {
success: false,
error: error.response?.data || 'Failed to create comment',
};
}
}
async fetchComments(targetType, targetId) {
try {
const response = await api.get('/api/comments/', {
params: { target_type: targetType, target_id: targetId },
});
this.comments = response.data.results || response.data;
} catch (error) {
this.error = error.response?.data || 'Failed to fetch comments';
}
}
async approveComment(commentId) {
try {
await api.post(`/api/comments/${commentId}/approve_ai/`);
return { success: true };
} catch (error) {
return {
success: false,
error: error.response?.data || 'Failed to approve comment',
};
}
}
async rejectComment(commentId, reason) {
try {
await api.post(`/api/comments/${commentId}/reject_ai/`, { reason });
return { success: true };
} catch (error) {
return {
success: false,
error: error.response?.data || 'Failed to reject comment',
};
}
}
// Ratings
async createRating(targetType, targetId, score) {
try {
const response = await api.post('/api/ratings/', {
target_type: targetType,
target_id: targetId,
score,
});
return { success: true, rating: response.data };
} catch (error) {
return {
success: false,
error: error.response?.data || 'Failed to create rating',
};
}
}
async fetchRatings(params = {}) {
try {
const response = await api.get('/api/ratings/', { params });
this.ratings = response.data.results || response.data;
} catch (error) {
this.error = error.response?.data || 'Failed to fetch ratings';
}
}
async fetchMyRatings() {
try {
const response = await api.get('/api/ratings/my_ratings/');
this.ratings = response.data;
} catch (error) {
this.error = error.response?.data || 'Failed to fetch my ratings';
}
}
// Likes
async toggleLike(targetType, targetId) {
try {
const response = await api.post('/api/likes/toggle/', {
target_type: targetType,
target_id: targetId,
});
return response.data;
} catch (error) {
return null;
}
}
async fetchMyLikes() {
try {
const response = await api.get('/api/likes/my_likes/');
this.likes = response.data;
} catch (error) {
this.error = error.response?.data || 'Failed to fetch my likes';
}
}
// Favorites
async toggleFavorite(targetType, targetId) {
try {
const response = await api.post('/api/favorites/toggle/', {
target_type: targetType,
target_id: targetId,
});
return response.data;
} catch (error) {
return null;
}
}
async fetchMyFavorites() {
try {
const response = await api.get('/api/favorites/my_favorites/');
this.favorites = response.data;
} catch (error) {
this.error = error.response?.data || 'Failed to fetch my favorites';
}
}
clearComments() {
this.comments = [];
}
clearRatings() {
this.ratings = [];
}
clearLikes() {
this.likes = [];
}
clearFavorites() {
this.favorites = [];
}
}
export default InteractionStore;

View File

@@ -0,0 +1,139 @@
import { makeAutoObservable } from 'mobx';
import api from '../services/api';
class RegionStore {
regions = [];
currentRegion = null;
loading = false;
error = null;
constructor() {
makeAutoObservable(this);
}
async fetchRegions() {
this.loading = true;
this.error = null;
try {
const response = await api.get('/api/regions/');
this.regions = response.data;
} catch (error) {
this.error = error.response?.data || 'Failed to fetch regions';
} finally {
this.loading = false;
}
}
async fetchRegion(id) {
this.loading = true;
this.error = null;
try {
const response = await api.get(`/api/regions/${id}/`);
this.currentRegion = response.data;
} catch (error) {
this.error = error.response?.data || 'Failed to fetch region';
} finally {
this.loading = false;
}
}
async fetchProvinces() {
this.loading = true;
this.error = null;
try {
const response = await api.get('/api/regions/provinces/');
this.regions = response.data;
} catch (error) {
this.error = error.response?.data || 'Failed to fetch provinces';
} finally {
this.loading = false;
}
}
async fetchChildren(parentId) {
this.loading = true;
this.error = null;
try {
const response = await api.get(`/api/regions/${parentId}/children/`);
return response.data;
} catch (error) {
this.error = error.response?.data || 'Failed to fetch children';
return [];
} finally {
this.loading = false;
}
}
async fetchRegionArticles(regionId) {
this.loading = true;
this.error = null;
try {
const response = await api.get(`/api/regions/${regionId}/articles/`);
return response.data;
} catch (error) {
this.error = error.response?.data || 'Failed to fetch region articles';
return [];
} finally {
this.loading = false;
}
}
async fetchRegionServices(regionId) {
this.loading = true;
this.error = null;
try {
const response = await api.get(`/api/regions/${regionId}/services/`);
return response.data;
} catch (error) {
this.error = error.response?.data || 'Failed to fetch region services';
return [];
} finally {
this.loading = false;
}
}
async rateRegion(regionId, score) {
try {
await api.post(`/api/regions/${regionId}/rate/`, { score });
return { success: true };
} catch (error) {
return {
success: false,
error: error.response?.data || 'Failed to rate region',
};
}
}
async getRegionRating(regionId) {
try {
const response = await api.get(`/api/regions/${regionId}/my_rating/`);
return response.data.score;
} catch (error) {
return null;
}
}
async favoriteRegion(regionId) {
try {
await api.post(`/api/regions/${regionId}/favorite/`);
return { success: true };
} catch (error) {
return {
success: false,
error: error.response?.data || 'Failed to favorite region',
};
}
}
clearCurrentRegion() {
this.currentRegion = null;
}
}
export default RegionStore;

View File

@@ -0,0 +1,164 @@
import { makeAutoObservable } from 'mobx';
import api from '../services/api';
class ServiceStore {
services = [];
currentService = null;
loading = false;
error = null;
constructor() {
makeAutoObservable(this);
}
async fetchServices(params = {}) {
this.loading = true;
this.error = null;
try {
const response = await api.get('/api/services/', { params });
this.services = response.data.results || response.data;
} catch (error) {
this.error = error.response?.data || 'Failed to fetch services';
} finally {
this.loading = false;
}
}
async fetchService(id) {
this.loading = true;
this.error = null;
try {
const response = await api.get(`/api/services/${id}/`);
this.currentService = response.data;
} catch (error) {
this.error = error.response?.data || 'Failed to fetch service';
} finally {
this.loading = false;
}
}
async createService(data) {
this.loading = true;
this.error = null;
try {
const response = await api.post('/api/services/', data);
return { success: true, service: response.data };
} catch (error) {
this.error = error.response?.data || 'Failed to create service';
return { success: false, error: this.error };
} finally {
this.loading = false;
}
}
async updateService(id, data) {
this.loading = true;
this.error = null;
try {
const response = await api.put(`/api/services/${id}/`, data);
return { success: true, service: response.data };
} catch (error) {
this.error = error.response?.data || 'Failed to update service';
return { success: false, error: this.error };
} finally {
this.loading = false;
}
}
async deleteService(id) {
try {
await api.delete(`/api/services/${id}/`);
return { success: true };
} catch (error) {
return {
success: false,
error: error.response?.data || 'Failed to delete service',
};
}
}
async submitService(id) {
try {
await api.post(`/api/services/${id}/submit/`);
return { success: true };
} catch (error) {
return {
success: false,
error: error.response?.data || 'Failed to submit service',
};
}
}
async approveService(id, reason = '') {
try {
await api.post(`/api/services/${id}/approve/`, { action: 'approve', reason });
return { success: true };
} catch (error) {
return {
success: false,
error: error.response?.data || 'Failed to approve service',
};
}
}
async rejectService(id, reason) {
try {
await api.post(`/api/services/${id}/reject/`, { action: 'reject', reason });
return { success: true };
} catch (error) {
return {
success: false,
error: error.response?.data || 'Failed to reject service',
};
}
}
async likeService(id) {
try {
const response = await api.post(`/api/services/${id}/like/`);
return response.data;
} catch (error) {
return null;
}
}
async rateService(id, score) {
try {
await api.post(`/api/services/${id}/rate/`, { score });
return { success: true };
} catch (error) {
return {
success: false,
error: error.response?.data || 'Failed to rate service',
};
}
}
async fetchServiceComments(id) {
try {
const response = await api.get(`/api/services/${id}/comments/`);
return response.data;
} catch (error) {
return [];
}
}
async fetchServiceStats(id) {
try {
const response = await api.get(`/api/services/${id}/stats/`);
return response.data;
} catch (error) {
return null;
}
}
clearCurrentService() {
this.currentService = null;
}
}
export default ServiceStore;