✅ 功能点
我们要做到:
- 🧠 ruleForm 仍然是唯一状态源
- 🔁
formDataToJson自动跟随 ruleForm - ⚡ 子组件 直接拿"已处理好的 params"
- 🚫 不在每个组件里重复写转换逻辑
一、升级 useFilterState(关键)
✅ 改造点:加入 params(computed)
useFilterState.ts
typescript
import { reactive, provide, watch, computed } from "vue";
import { ElMessage } from "element-plus";
export const FILTER_KEY = Symbol("FILTER_STATE");
export function useFilterState() {
const ruleForm = reactive({
time_menu: "day",
timeRange: [] as any[],
service_info: [] as any[],
});
/** ✅ 派生:接口参数 */
const params = computed(() => {
if (ruleForm.time_menu === "custom") {
if (ruleForm.timeRange.length === 2) {
const [start_time, end_time] = ruleForm.timeRange;
return {
start_time,
end_time,
service_info: ruleForm.service_info,
time_menu: ruleForm.time_menu,
};
}
return null;
}
return {
time_menu: ruleForm.time_menu,
service_info: ruleForm.service_info,
};
});
/** 可扩展的 change hook */
const onChangeCbs: Function[] = [];
watch(
ruleForm,
() => {
onChangeCbs.forEach((cb) => cb(ruleForm));
},
{ deep: true }
);
provide(FILTER_KEY, {
ruleForm,
params,
onFilterChange: (cb: Function) => onChangeCbs.push(cb),
});
return {
ruleForm,
params,
};
}
useFilterInject.ts
javascript
import { inject } from "vue";
import { FILTER_KEY } from "./useFilterState";
export function useFilterInject() {
const ctx = inject<any>(FILTER_KEY);
if (!ctx) {
throw new Error("useFilterInject 必须在 useFilterState 下使用");
}
return ctx;
}
二、页面中用法(更干净了)
xml
<script setup lang="ts">
import { useFilterState } from "@/composables/useFilterState";
const { ruleForm, params } = useFilterState();
</script>
三、子组件中如何用(重点)
任意子组件
xml
<script setup lang="ts">
import { watchEffect } from "vue";
import { useFilterInject } from "@/composables/useFilterInject";
import { getRiskInfo } from "@/api/visualizedOperations";
const { params } = useFilterInject();
async function fetchData() {
if (!params.value) return; // custom 未选时间
await getRiskInfo(params.value);
}
watchEffect(fetchData);
</script>
🎯 效果:
- ruleForm 任意字段变化
- params 自动重新计算
- watchEffect 自动触发
- 所有组件 同步刷新
四、如果你还想保留 ElMessage 提示
不要在 computed 里直接弹消息(副作用)
更优雅的方式是:提交或请求前校验
推荐写法
csharp
function validateParams(params: any) {
if (!params) {
ElMessage.warning("请选择时间范围");
return false;
}
return true;
}
使用时:
csharp
if (!validateParams(params.value)) return;
五、为什么这是「最佳实践」
| 点 | 原来 | 现在 |
|---|---|---|
| formDataToJson | scattered | 单一来源 |
| 子组件 | 自己拼参数 | 直接用 |
| 维护成本 | 高 | 低 |
| 复用 | ❌ | ✅ |
| 状态一致性 | 易乱 | ✅ |
六、结构总结(你现在的架构)
scss
页面
├─ useFilterState()
│ ├─ ruleForm ✅ 原始状态
│ ├─ params ✅ 派生参数
│
├─ AlarmView
│ └─ watchEffect(params) → 请求
│
└─ DataView
└─ watchEffect(params) → 请求
七、一句话记住这个模式
状态在 ruleForm,逻辑在 computed,请求在组件
这套模式特别适合:
- 可视化运营页面
- 多组件联动筛选
- 不想上 Pinia / URL