434 lines
9.3 KiB
TypeScript
434 lines
9.3 KiB
TypeScript
|
|
/**
|
|||
|
|
* 状态管理示例代码(Pinia Store)
|
|||
|
|
* 展示项目中状态管理的编写规范和最佳实践
|
|||
|
|
*/
|
|||
|
|
|
|||
|
|
import { defineStore } from 'pinia';
|
|||
|
|
import { ref, computed } from 'vue';
|
|||
|
|
import { getUserList, createUser, updateUser, deleteUser, type User, type UserParams } from '@/api/user';
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 用户管理 Store
|
|||
|
|
* 使用 Composition API 风格定义 Store
|
|||
|
|
*/
|
|||
|
|
export const useUserStore = defineStore('user', () => {
|
|||
|
|
/**
|
|||
|
|
* 状态定义(使用 ref)
|
|||
|
|
*/
|
|||
|
|
|
|||
|
|
// 用户列表
|
|||
|
|
const userList = ref<User[]>([]);
|
|||
|
|
|
|||
|
|
// 当前用户
|
|||
|
|
const currentUser = ref<User | null>(null);
|
|||
|
|
|
|||
|
|
// 加载状态
|
|||
|
|
const loading = ref(false);
|
|||
|
|
|
|||
|
|
// 搜索关键词
|
|||
|
|
const searchKeyword = ref('');
|
|||
|
|
|
|||
|
|
// 分页信息
|
|||
|
|
const pagination = ref({
|
|||
|
|
current: 1,
|
|||
|
|
size: 10,
|
|||
|
|
total: 0,
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 选中的用户ID列表
|
|||
|
|
const selectedUserIds = ref<number[]>([]);
|
|||
|
|
|
|||
|
|
// 错误信息
|
|||
|
|
const error = ref<string | null>(null);
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 计算属性
|
|||
|
|
*/
|
|||
|
|
|
|||
|
|
// 是否全选
|
|||
|
|
const isAllSelected = computed(() => {
|
|||
|
|
return userList.value.length > 0 && selectedUserIds.value.length === userList.value.length;
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 是否部分选中
|
|||
|
|
const isIndeterminate = computed(() => {
|
|||
|
|
return selectedUserIds.value.length > 0 && selectedUserIds.value.length < userList.value.length;
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 选中的用户列表
|
|||
|
|
const selectedUsers = computed(() => {
|
|||
|
|
return userList.value.filter(user => selectedUserIds.value.includes(user.id));
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 正常状态用户数
|
|||
|
|
const activeUserCount = computed(() => {
|
|||
|
|
return userList.value.filter(user => user.status === 0).length;
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 禁用状态用户数
|
|||
|
|
const inactiveUserCount = computed(() => {
|
|||
|
|
return userList.value.filter(user => user.status === 1).length;
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 异步操作(Actions)
|
|||
|
|
*/
|
|||
|
|
|
|||
|
|
// ✅ 获取用户列表
|
|||
|
|
/**
|
|||
|
|
* 获取用户列表
|
|||
|
|
* @param params 搜索参数
|
|||
|
|
*/
|
|||
|
|
const fetchUserList = async (params?: UserParams) => {
|
|||
|
|
loading.value = true;
|
|||
|
|
error.value = null;
|
|||
|
|
|
|||
|
|
try {
|
|||
|
|
const result = await getUserList(params);
|
|||
|
|
userList.value = result.data;
|
|||
|
|
pagination.value = {
|
|||
|
|
current: result.page,
|
|||
|
|
size: result.size,
|
|||
|
|
total: result.total,
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// 清空选中状态
|
|||
|
|
selectedUserIds.value = [];
|
|||
|
|
|
|||
|
|
return result;
|
|||
|
|
} catch (err: any) {
|
|||
|
|
error.value = err.message || '获取用户列表失败';
|
|||
|
|
console.error('获取用户列表失败:', err);
|
|||
|
|
throw err;
|
|||
|
|
} finally {
|
|||
|
|
loading.value = false;
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// ✅ 创建用户
|
|||
|
|
/**
|
|||
|
|
* 创建用户
|
|||
|
|
* @param data 用户数据
|
|||
|
|
*/
|
|||
|
|
const createNewUser = async (data: any) => {
|
|||
|
|
loading.value = true;
|
|||
|
|
error.value = null;
|
|||
|
|
|
|||
|
|
try {
|
|||
|
|
const newUser = await createUser(data);
|
|||
|
|
// 添加到列表开头
|
|||
|
|
userList.value.unshift(newUser);
|
|||
|
|
return newUser;
|
|||
|
|
} catch (err: any) {
|
|||
|
|
error.value = err.message || '创建用户失败';
|
|||
|
|
console.error('创建用户失败:', err);
|
|||
|
|
throw err;
|
|||
|
|
} finally {
|
|||
|
|
loading.value = false;
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// ✅ 更新用户
|
|||
|
|
/**
|
|||
|
|
* 更新用户信息
|
|||
|
|
* @param data 用户数据
|
|||
|
|
*/
|
|||
|
|
const updateUserInfo = async (data: any) => {
|
|||
|
|
loading.value = true;
|
|||
|
|
error.value = null;
|
|||
|
|
|
|||
|
|
try {
|
|||
|
|
const updatedUser = await updateUser(data);
|
|||
|
|
// 更新列表中的用户
|
|||
|
|
const index = userList.value.findIndex(user => user.id === data.id);
|
|||
|
|
if (index !== -1) {
|
|||
|
|
userList.value[index] = updatedUser;
|
|||
|
|
}
|
|||
|
|
return updatedUser;
|
|||
|
|
} catch (err: any) {
|
|||
|
|
error.value = err.message || '更新用户失败';
|
|||
|
|
console.error('更新用户失败:', err);
|
|||
|
|
throw err;
|
|||
|
|
} finally {
|
|||
|
|
loading.value = false;
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// ✅ 删除用户
|
|||
|
|
/**
|
|||
|
|
* 删除用户
|
|||
|
|
* @param id 用户ID
|
|||
|
|
*/
|
|||
|
|
const removeUser = async (id: number) => {
|
|||
|
|
loading.value = true;
|
|||
|
|
error.value = null;
|
|||
|
|
|
|||
|
|
try {
|
|||
|
|
await deleteUser(id);
|
|||
|
|
// 从列表中移除
|
|||
|
|
userList.value = userList.value.filter(user => user.id !== id);
|
|||
|
|
// 移除选中状态
|
|||
|
|
selectedUserIds.value = selectedUserIds.value.filter(userId => userId !== id);
|
|||
|
|
} catch (err: any) {
|
|||
|
|
error.value = err.message || '删除用户失败';
|
|||
|
|
console.error('删除用户失败:', err);
|
|||
|
|
throw err;
|
|||
|
|
} finally {
|
|||
|
|
loading.value = false;
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// ✅ 批量删除用户
|
|||
|
|
/**
|
|||
|
|
* 批量删除用户
|
|||
|
|
* @param ids 用户ID列表
|
|||
|
|
*/
|
|||
|
|
const batchRemoveUsers = async (ids: number[]) => {
|
|||
|
|
loading.value = true;
|
|||
|
|
error.value = null;
|
|||
|
|
|
|||
|
|
try {
|
|||
|
|
// 这里应该调用批量删除 API
|
|||
|
|
// await batchDeleteUsers(ids);
|
|||
|
|
|
|||
|
|
// 模拟批量删除
|
|||
|
|
for (const id of ids) {
|
|||
|
|
await deleteUser(id);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 从列表中移除
|
|||
|
|
userList.value = userList.value.filter(user => !ids.includes(user.id));
|
|||
|
|
// 清空选中状态
|
|||
|
|
selectedUserIds.value = [];
|
|||
|
|
} catch (err: any) {
|
|||
|
|
error.value = err.message || '批量删除失败';
|
|||
|
|
console.error('批量删除失败:', err);
|
|||
|
|
throw err;
|
|||
|
|
} finally {
|
|||
|
|
loading.value = false;
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// ✅ 切换用户状态
|
|||
|
|
/**
|
|||
|
|
* 切换用户状态
|
|||
|
|
* @param id 用户ID
|
|||
|
|
*/
|
|||
|
|
const toggleUserStatus = async (id: number) => {
|
|||
|
|
const user = userList.value.find(u => u.id === id);
|
|||
|
|
if (!user) return;
|
|||
|
|
|
|||
|
|
const newStatus = user.status === 0 ? 1 : 0;
|
|||
|
|
|
|||
|
|
try {
|
|||
|
|
await updateUser({ id, status: newStatus });
|
|||
|
|
// 更新本地状态
|
|||
|
|
user.status = newStatus;
|
|||
|
|
} catch (err: any) {
|
|||
|
|
error.value = err.message || '状态切换失败';
|
|||
|
|
console.error('状态切换失败:', err);
|
|||
|
|
throw err;
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// ✅ 刷新用户列表
|
|||
|
|
/**
|
|||
|
|
* 刷新用户列表(使用当前搜索条件)
|
|||
|
|
*/
|
|||
|
|
const refreshUserList = () => {
|
|||
|
|
return fetchUserList({
|
|||
|
|
keyword: searchKeyword.value,
|
|||
|
|
page: pagination.value.current,
|
|||
|
|
size: pagination.value.size,
|
|||
|
|
});
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 同步操作(Actions)
|
|||
|
|
*/
|
|||
|
|
|
|||
|
|
// ✅ 设置当前用户
|
|||
|
|
/**
|
|||
|
|
* 设置当前用户
|
|||
|
|
* @param user 用户信息
|
|||
|
|
*/
|
|||
|
|
const setCurrentUser = (user: User | null) => {
|
|||
|
|
currentUser.value = user;
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// ✅ 设置搜索关键词
|
|||
|
|
/**
|
|||
|
|
* 设置搜索关键词
|
|||
|
|
* @param keyword 关键词
|
|||
|
|
*/
|
|||
|
|
const setSearchKeyword = (keyword: string) => {
|
|||
|
|
searchKeyword.value = keyword;
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// ✅ 设置分页
|
|||
|
|
/**
|
|||
|
|
* 设置分页信息
|
|||
|
|
* @param page 页码
|
|||
|
|
* @param size 每页数量
|
|||
|
|
*/
|
|||
|
|
const setPagination = (page: number, size?: number) => {
|
|||
|
|
pagination.value.current = page;
|
|||
|
|
if (size) {
|
|||
|
|
pagination.value.size = size;
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// ✅ 全选/取消全选
|
|||
|
|
/**
|
|||
|
|
* 切换全选状态
|
|||
|
|
*/
|
|||
|
|
const toggleSelectAll = () => {
|
|||
|
|
if (isAllSelected.value) {
|
|||
|
|
selectedUserIds.value = [];
|
|||
|
|
} else {
|
|||
|
|
selectedUserIds.value = userList.value.map(user => user.id);
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// ✅ 选择/取消选择用户
|
|||
|
|
/**
|
|||
|
|
* 切换用户选择状态
|
|||
|
|
* @param userId 用户ID
|
|||
|
|
*/
|
|||
|
|
const toggleUserSelection = (userId: number) => {
|
|||
|
|
const index = selectedUserIds.value.indexOf(userId);
|
|||
|
|
if (index === -1) {
|
|||
|
|
selectedUserIds.value.push(userId);
|
|||
|
|
} else {
|
|||
|
|
selectedUserIds.value.splice(index, 1);
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// ✅ 清空选中状态
|
|||
|
|
/**
|
|||
|
|
* 清空所有选中状态
|
|||
|
|
*/
|
|||
|
|
const clearSelection = () => {
|
|||
|
|
selectedUserIds.value = [];
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// ✅ 清空错误信息
|
|||
|
|
/**
|
|||
|
|
* 清空错误信息
|
|||
|
|
*/
|
|||
|
|
const clearError = () => {
|
|||
|
|
error.value = null;
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// ✅ 重置状态
|
|||
|
|
/**
|
|||
|
|
* 重置 Store 状态
|
|||
|
|
*/
|
|||
|
|
const resetState = () => {
|
|||
|
|
userList.value = [];
|
|||
|
|
currentUser.value = null;
|
|||
|
|
selectedUserIds.value = [];
|
|||
|
|
searchKeyword.value = '';
|
|||
|
|
pagination.value = {
|
|||
|
|
current: 1,
|
|||
|
|
size: 10,
|
|||
|
|
total: 0,
|
|||
|
|
};
|
|||
|
|
error.value = null;
|
|||
|
|
loading.value = false;
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 返回状态和方法
|
|||
|
|
*/
|
|||
|
|
return {
|
|||
|
|
// 状态
|
|||
|
|
userList,
|
|||
|
|
currentUser,
|
|||
|
|
loading,
|
|||
|
|
searchKeyword,
|
|||
|
|
pagination,
|
|||
|
|
selectedUserIds,
|
|||
|
|
error,
|
|||
|
|
|
|||
|
|
// 计算属性
|
|||
|
|
isAllSelected,
|
|||
|
|
isIndeterminate,
|
|||
|
|
selectedUsers,
|
|||
|
|
activeUserCount,
|
|||
|
|
inactiveUserCount,
|
|||
|
|
|
|||
|
|
// 异步操作
|
|||
|
|
fetchUserList,
|
|||
|
|
createNewUser,
|
|||
|
|
updateUserInfo,
|
|||
|
|
removeUser,
|
|||
|
|
batchRemoveUsers,
|
|||
|
|
toggleUserStatus,
|
|||
|
|
refreshUserList,
|
|||
|
|
|
|||
|
|
// 同步操作
|
|||
|
|
setCurrentUser,
|
|||
|
|
setSearchKeyword,
|
|||
|
|
setPagination,
|
|||
|
|
toggleSelectAll,
|
|||
|
|
toggleUserSelection,
|
|||
|
|
clearSelection,
|
|||
|
|
clearError,
|
|||
|
|
resetState,
|
|||
|
|
};
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 其他 Store 示例
|
|||
|
|
*/
|
|||
|
|
|
|||
|
|
// ✅ 主题管理 Store
|
|||
|
|
export const useThemeStore = defineStore('theme', () => {
|
|||
|
|
const theme = ref<'light' | 'dark'>('light');
|
|||
|
|
const primaryColor = ref('#4a90ff');
|
|||
|
|
|
|||
|
|
const setTheme = (newTheme: 'light' | 'dark') => {
|
|||
|
|
theme.value = newTheme;
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
const setPrimaryColor = (color: string) => {
|
|||
|
|
primaryColor.value = color;
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
return {
|
|||
|
|
theme,
|
|||
|
|
primaryColor,
|
|||
|
|
setTheme,
|
|||
|
|
setPrimaryColor,
|
|||
|
|
};
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// ✅ 购物车 Store(参考现有项目)
|
|||
|
|
export const useCartStore = defineStore('cart', () => {
|
|||
|
|
const items = ref<any[]>([]);
|
|||
|
|
const total = ref(0);
|
|||
|
|
|
|||
|
|
const addItem = (product: any) => {
|
|||
|
|
// 添加商品到购物车逻辑
|
|||
|
|
items.value.push(product);
|
|||
|
|
calculateTotal();
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
const removeItem = (productId: string) => {
|
|||
|
|
items.value = items.value.filter(item => item.id !== productId);
|
|||
|
|
calculateTotal();
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
const calculateTotal = () => {
|
|||
|
|
total.value = items.value.reduce((sum, item) => sum + item.price, 0);
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
return {
|
|||
|
|
items,
|
|||
|
|
total,
|
|||
|
|
addItem,
|
|||
|
|
removeItem,
|
|||
|
|
};
|
|||
|
|
});
|