---
description: 状态管理规范 - Store 模块拆分、State 设计、Getter/Action。
globs: ["**/store/**/*.ts", "**/stores/**/*.ts"]
alwaysApply: false
---

# 状态管理规范

该规则用于状态管理：

**当创建新 Store 时：**

- 按功能模块拆分（`user`、`app`、`cart`）
- 使用 Composition API 风格（`defineStore` + `ref`/`computed`）

**当设计 State 时：**

- 扁平化设计，避免深层嵌套
- 类型明确，合理初始值

**当使用 Store 时：**

- 解构 state/getters 用 `storeToRefs`
- 修改 state 必须通过 Action

---

## 📁 文件组织

**方案 A：集中式管理**（推荐）

```
{store目录}/
├── index.ts
├── modules/
│   ├── user.ts
│   └── app.ts
└── types.ts
```

**方案 B：页面级管理**

```
{pages目录}/order/
├── index.vue
└── store/index.ts  # 页面级 Store
```

**方案 C：混合式**

- 全局共享状态 → `store/modules/`
- 页面独立状态 → `pages/{page}/store/`

> 📋 具体存放路径详见 @foundation/project.mdc

---

## 🎨 设计策略

### 1. 模块拆分

| 类型       | 放置位置              | 特点                   | 示例          |
| ---------- | --------------------- | ---------------------- | ------------- |
| 全局 Store | `store/modules/`      | 多页面共享、持久化需求 | `user`、`app` |
| 页面 Store | `pages/{page}/store/` | 页面独立、离开即销毁   | `orderDetail` |

### 2. State 设计

**原则**：扁平化、类型明确、合理初始值

```typescript
export const useUserStore = defineStore("user", () => {
  // ✅ 扁平化设计
  const token = ref<string>("");
  const userId = ref<number>(0);
  const nickname = ref<string>("");

  // ❌ 避免：深层嵌套
  // const user = ref<{ info: { basic: { name: string } } }>({});

  return { token, userId, nickname };
});
```

**初始值设计**：

| 类型      | 初始值  | 说明                 |
| --------- | ------- | -------------------- |
| `string`  | `""`    | 空字符串             |
| `number`  | `0`     | 零值                 |
| `array`   | `[]`    | 空数组               |
| `object`  | `null`  | 用 `null` 表示未加载 |
| `boolean` | `false` | 默认否定             |

### 3. Getter vs Computed

| 场景               | 推荐       | 原因                     |
| ------------------ | ---------- | ------------------------ |
| Store 内派生数据   | `computed` | Composition API 风格一致 |
| 跨 Store 派生数据  | `computed` | 可组合多个 Store         |
| 需要传参的派生数据 | 返回函数   | getter 不支持传参        |

```typescript
const isLogin = computed(() => !!userInfo.value);
const hasPermission = (perm: string) =>
  userInfo.value?.permissions?.includes(perm);
```

### 4. Action 设计

**原则**：单一职责、错误处理、返回结果

```typescript
async function login(params: LoginParams): Promise<boolean> {
  loading.value = true;
  try {
    const res = await loginApi(params);
    userInfo.value = res.userInfo;
    return true;
  } catch (error) {
    console.error("登录失败:", error);
    return false;
  } finally {
    loading.value = false;
  }
}
```

### 5. 持久化策略

**插件持久化**（推荐）：

```typescript
// store/index.ts
import { createPinia } from "pinia";
import piniaPluginPersistedstate from "pinia-plugin-persistedstate";

const pinia = createPinia();
pinia.use(piniaPluginPersistedstate);

// store/modules/user.ts
export const useUserStore = defineStore(
  "user",
  () => {
    const token = ref<string>("");
    return { token };
  },
  {
    persist: {
      key: "user-store",
      storage: localStorage,
      pick: ["token"],
    },
  }
);
```

### 6. Store 间通信

```typescript
export const useOrderStore = defineStore("order", () => {
  async function fetchOrders() {
    // ✅ 在 Action 内部获取其他 Store
    const userStore = useUserStore();
    if (!userStore.isLogin) throw new Error("请先登录");
    orders.value = await getOrdersApi(userStore.userId);
  }
  return { fetchOrders };
});
```

---

## ✅ 检查清单

### 设计

- ✅ Store 必须按功能模块拆分
- ✅ Store 必须有明确的类型定义
- ✅ State 扁平化设计，避免深层嵌套
- ❌ 禁止所有状态放在一个 Store 中

### 使用

- ✅ 解构 state/getters 必须使用 `storeToRefs`
- ✅ 修改 state 必须通过 Action
- ❌ 禁止直接解构 Store（会失去响应性）
- ❌ 禁止在组件中直接修改 state

### 计算

- ❌ 禁止在 getter/computed 中执行异步操作

> 📋 TypeScript 规范详见 @foundation/typescript.mdc

---

## 📋 标准模板

### Store 定义

```typescript
// store/modules/user.ts
import { defineStore } from "pinia";
import { ref, computed } from "vue";
import { loginApi } from "@/api/user";
import type { UserInfo, LoginParams } from "@/api/user.types";

export const useUserStore = defineStore("user", () => {
  // State
  const userInfo = ref<UserInfo | null>(null);
  const token = ref<string>("");

  // Getters
  const isLogin = computed(() => !!token.value);

  // Actions
  async function login(params: LoginParams): Promise<boolean> {
    try {
      const res = await loginApi(params);
      token.value = res.token;
      userInfo.value = res.userInfo;
      return true;
    } catch {
      return false;
    }
  }

  function logout() {
    token.value = "";
    userInfo.value = null;
  }

  return { userInfo, token, isLogin, login, logout };
});
```

### 组件使用

```vue
<script setup lang="ts">
import { storeToRefs } from "pinia";
import { useUserStore } from "@/store";

const userStore = useUserStore();

// ✅ storeToRefs 解构 state/getters
const { userInfo, isLogin } = storeToRefs(userStore);

// ✅ 直接解构 actions
const { login, logout } = userStore;
</script>
```
