hookehuyr

refactor(plan): 提取计划书提交回调逻辑为 composable

- 创建 usePlanSubmit composable 统一处理弹窗关闭、状态清空、导航跳转
- 重构 index、search、product-detail、product-center 页面使用 composable
- 移除重复的 handlePlanSubmit 函数实现(~100 行)

🤖 Generated with assistance from Claude Code
1 +/**
2 + * 计划书提交后处理 Composable
3 + *
4 + * @description 统一处理计划书提交后的弹窗关闭、状态清空、导航跳转逻辑
5 + * @author Claude Code
6 + * @example
7 + * const { handlePlanSubmit } = usePlanSubmit({
8 + * getPopupState: () => showPlanPopup.value,
9 + * setPopupState: (state) => { showPlanPopup.value = state }
10 + * })
11 + *
12 + * // 使用
13 + * await handlePlanSubmit(result, {
14 + * beforeNav: async () => { console.log('导航前') },
15 + * afterNav: async () => { console.log('导航后') }
16 + * })
17 + */
18 +import { nextTick } from 'vue'
19 +import Taro from '@tarojs/taro'
20 +import { useGo } from '@/hooks/useGo'
21 +
22 +/**
23 + * 计划书提交后处理 Hook
24 + *
25 + * @param {Object} options - 配置选项
26 + * @param {Function} options.getPopupState - 获取弹窗状态(必需)
27 + * @param {Function} options.setPopupState - 设置弹窗状态(必需)
28 + * @param {Function} options.clearSelectedProduct - 清空选中产品(可选)
29 + * @param {boolean} options.useGoUtil - 是否使用 go() 工具(默认 true)
30 + * @param {string} options.pageName - 页面名称(用于日志,默认 'Page')
31 + * @returns {Object} 包含 handlePlanSubmit 方法的对象
32 + */
33 +export function usePlanSubmit(options = {}) {
34 + const {
35 + getPopupState,
36 + setPopupState,
37 + clearSelectedProduct,
38 + useGoUtil = true,
39 + pageName = 'Page'
40 + } = options
41 +
42 + /**
43 + * 处理计划书提交结果
44 + *
45 + * @param {Object} result - 提交结果
46 + * @param {boolean} result.success - 是否成功
47 + * @param {string} result.message - 错误信息(失败时)
48 + * @param {number} result.order_id - 订单 ID(成功时)
49 + * @param {number} result.product_id - 产品 ID(成功时)
50 + * @param {string} result.form_sn - 表单标识(成功时)
51 + * @param {Object} callbacks - 回调函数
52 + * @param {Function} callbacks.beforeNav - 导航前回调
53 + * @param {Function} callbacks.afterNav - 导航后回调
54 + */
55 + const handlePlanSubmit = async (result, callbacks = {}) => {
56 + console.log(`[${pageName}] 计划书提交结果:`, result)
57 +
58 + // 1. 关闭弹窗
59 + setPopupState(false)
60 +
61 + // 2. 等待 DOM 更新
62 + await nextTick()
63 +
64 + // 3. 清空选中产品(如果提供)
65 + if (clearSelectedProduct) {
66 + clearSelectedProduct()
67 + }
68 +
69 + // 4. 构建结果页面参数
70 + const params = {
71 + success: result.success ? 'true' : 'false'
72 + }
73 +
74 + // 失败时传递错误信息
75 + if (!result.success && result.message) {
76 + params.message = result.message
77 + }
78 +
79 + console.log(`[${pageName}] 跳转到结果页面,参数:`, params)
80 +
81 + // 5. 执行导航前回调
82 + if (callbacks.beforeNav) {
83 + await callbacks.beforeNav(result)
84 + }
85 +
86 + // 6. 跳转到结果页面
87 + if (useGoUtil) {
88 + // 使用 go() 工具函数
89 + const go = useGo()
90 + go('/pages/plan-submit-result/index', params)
91 + } else {
92 + // 直接使用 Taro API
93 + Taro.navigateTo({
94 + url: `/pages/plan-submit-result/index?${new URLSearchParams(params).toString()}`
95 + })
96 + }
97 +
98 + // 7. 执行导航后回调
99 + if (callbacks.afterNav) {
100 + await callbacks.afterNav()
101 + }
102 + }
103 +
104 + return {
105 + handlePlanSubmit
106 + }
107 +}
...@@ -172,6 +172,7 @@ import MaterialCard from '@/components/cards/MaterialCard.vue'; ...@@ -172,6 +172,7 @@ import MaterialCard from '@/components/cards/MaterialCard.vue';
172 import { listAPI } from '@/api/get_product'; 172 import { listAPI } from '@/api/get_product';
173 import { weekHotAPI } from '@/api/file'; 173 import { weekHotAPI } from '@/api/file';
174 import { homeIconAPI } from '@/api/home'; 174 import { homeIconAPI } from '@/api/home';
175 +import { usePlanSubmit } from '@/composables/usePlanSubmit';
175 176
176 177
177 // User Store 178 // User Store
...@@ -220,40 +221,14 @@ const openPlanPopup = (productId) => { ...@@ -220,40 +221,14 @@ const openPlanPopup = (productId) => {
220 showPlanPopup.value = true; 221 showPlanPopup.value = true;
221 }; 222 };
222 223
223 -/** 224 +// 使用 composable 统一处理计划书提交后逻辑
224 - * 处理计划书提交 225 +const { handlePlanSubmit } = usePlanSubmit({
225 - * 226 + getPopupState: () => showPlanPopup.value,
226 - * @description 接收 PlanFormContainer 的提交结果事件 227 + setPopupState: (state) => { showPlanPopup.value = state },
227 - * PlanFormContainer 已经调用过 API 并处理了 toast 提示 228 + clearSelectedProduct: () => { selectedProduct.value = null },
228 - * 这里根据成功/失败状态跳转到结果页面 229 + useGoUtil: true,
229 - * @param {Object} result - 提交结果 230 + pageName: 'Home Page'
230 - * @param {boolean} result.success - 是否成功 231 +})
231 - * @param {number} result.order_id - 订单 ID
232 - * @param {number} result.product_id - 产品 ID
233 - * @param {string} result.form_sn - 表单标识
234 - */
235 -const handlePlanSubmit = async (result) => {
236 - console.log('[Home Page] 计划书提交结果:', result);
237 -
238 - // 关闭弹窗
239 - showPlanPopup.value = false;
240 -
241 - await nextTick();
242 -
243 - selectedProduct.value = null;
244 -
245 - // 构建结果页面参数
246 - const params = {
247 - success: result.success ? 'true' : 'false'
248 - };
249 -
250 - // 失败时传递后端返回的错误信息
251 - if (!result.success && result.message) {
252 - params.message = result.message;
253 - }
254 -
255 - go('/pages/plan-submit-result/index', params);
256 -};
257 232
258 const handlePlanClose = async () => { 233 const handlePlanClose = async () => {
259 showPlanPopup.value = false; 234 showPlanPopup.value = false;
......
...@@ -139,7 +139,7 @@ ...@@ -139,7 +139,7 @@
139 </template> 139 </template>
140 140
141 <script setup> 141 <script setup>
142 -import { ref, computed, nextTick } from 'vue' 142 +import { ref, computed } from 'vue'
143 import Taro, { useLoad } from '@tarojs/taro' 143 import Taro, { useLoad } from '@tarojs/taro'
144 import { useGo } from '@/hooks/useGo' 144 import { useGo } from '@/hooks/useGo'
145 import { useListItemClick, ListType } from '@/composables/useListItemClick' 145 import { useListItemClick, ListType } from '@/composables/useListItemClick'
...@@ -149,6 +149,7 @@ import SearchBar from '@/components/forms/SearchBar.vue' ...@@ -149,6 +149,7 @@ import SearchBar from '@/components/forms/SearchBar.vue'
149 import PlanFormContainer from '@/components/plan/PlanFormContainer.vue' 149 import PlanFormContainer from '@/components/plan/PlanFormContainer.vue'
150 import { listAPI } from '@/api/get_product' 150 import { listAPI } from '@/api/get_product'
151 import { mockProductListAPI } from '@/utils/mockData' 151 import { mockProductListAPI } from '@/utils/mockData'
152 +import { usePlanSubmit } from '@/composables/usePlanSubmit'
152 153
153 // ⚠️ MOCK 数据开关 - 开发环境使用 mock 数据,生产环境使用真实 API 154 // ⚠️ MOCK 数据开关 - 开发环境使用 mock 数据,生产环境使用真实 API
154 // const USE_MOCK_DATA = process.env.NODE_ENV === 'development' 155 // const USE_MOCK_DATA = process.env.NODE_ENV === 'development'
...@@ -498,42 +499,13 @@ const openPlanPopup = (product) => { ...@@ -498,42 +499,13 @@ const openPlanPopup = (product) => {
498 showPlanPopup.value = true 499 showPlanPopup.value = true
499 } 500 }
500 501
501 -/** 502 +// 使用 composable 统一处理计划书提交后逻辑
502 - * 处理计划书提交 503 +const { handlePlanSubmit } = usePlanSubmit({
503 - * 504 + getPopupState: () => showPlanPopup.value,
504 - * @description 接收 PlanFormContainer 的提交结果事件 505 + setPopupState: (state) => { showPlanPopup.value = state },
505 - * PlanFormContainer 已经调用过 API 并处理了 toast 提示 506 + useGoUtil: false,
506 - * 这里根据成功/失败状态跳转到结果页面 507 + pageName: 'Product Center'
507 - * @param {Object} result - 提交结果 508 +})
508 - * @param {boolean} result.success - 是否成功
509 - * @param {number} result.order_id - 订单 ID
510 - * @param {number} result.product_id - 产品 ID
511 - * @param {string} result.form_sn - 表单标识
512 - */
513 -const handlePlanSubmit = async (result) => {
514 - console.log('[Product Center] 计划书提交结果:', result)
515 -
516 - // 关闭弹窗
517 - showPlanPopup.value = false
518 -
519 - await nextTick()
520 -
521 - // 构建结果页面参数
522 - const params = {
523 - success: result.success ? 'true' : 'false'
524 - }
525 -
526 - // 失败时传递后端返回的错误信息
527 - if (!result.success && result.message) {
528 - params.message = result.message
529 - }
530 -
531 - console.log('[Product Center] 跳转到结果页面,参数:', params)
532 -
533 - Taro.navigateTo({
534 - url: `/pages/plan-submit-result/index?${new URLSearchParams(params).toString()}`
535 - })
536 -}
537 </script> 509 </script>
538 510
539 <style lang="less"> 511 <style lang="less">
......
...@@ -125,7 +125,7 @@ ...@@ -125,7 +125,7 @@
125 </template> 125 </template>
126 126
127 <script setup> 127 <script setup>
128 -import { ref, nextTick } from 'vue' 128 +import { ref } from 'vue'
129 import NavHeader from '@/components/navigation/NavHeader.vue' 129 import NavHeader from '@/components/navigation/NavHeader.vue'
130 import IconFont from '@/components/icons/IconFont.vue' 130 import IconFont from '@/components/icons/IconFont.vue'
131 import PlanFormContainer from '@/components/plan/PlanFormContainer.vue' 131 import PlanFormContainer from '@/components/plan/PlanFormContainer.vue'
...@@ -133,6 +133,7 @@ import { useFileOperation } from '@/composables/useFileOperation' ...@@ -133,6 +133,7 @@ import { useFileOperation } from '@/composables/useFileOperation'
133 import Taro, { useLoad } from '@tarojs/taro' 133 import Taro, { useLoad } from '@tarojs/taro'
134 import { getDocumentIcon, getDocumentLabel } from '@/utils/documentIcons' 134 import { getDocumentIcon, getDocumentLabel } from '@/utils/documentIcons'
135 import { detailAPI } from '@/api/get_product' 135 import { detailAPI } from '@/api/get_product'
136 +import { usePlanSubmit } from '@/composables/usePlanSubmit'
136 137
137 const { viewFile } = useFileOperation() 138 const { viewFile } = useFileOperation()
138 139
...@@ -216,42 +217,13 @@ const openPlanPopup = () => { ...@@ -216,42 +217,13 @@ const openPlanPopup = () => {
216 showPlanPopup.value = true 217 showPlanPopup.value = true
217 } 218 }
218 219
219 -/** 220 +// 使用 composable 统一处理计划书提交后逻辑
220 - * 处理计划书提交 221 +const { handlePlanSubmit } = usePlanSubmit({
221 - * 222 + getPopupState: () => showPlanPopup.value,
222 - * @description 接收 PlanFormContainer 的提交结果事件 223 + setPopupState: (state) => { showPlanPopup.value = state },
223 - * PlanFormContainer 已经调用过 API 并处理了 toast 提示 224 + useGoUtil: false,
224 - * 这里根据成功/失败状态跳转到结果页面 225 + pageName: 'Product Detail'
225 - * @param {Object} result - 提交结果 226 +})
226 - * @param {boolean} result.success - 是否成功
227 - * @param {number} result.order_id - 订单 ID(成功时)
228 - * @param {number} result.product_id - 产品 ID
229 - * @param {string} result.form_sn - 表单标识
230 - */
231 -const handlePlanSubmit = async (result) => {
232 - console.log('[Product Detail] 计划书提交结果:', result)
233 -
234 - // 关闭弹窗
235 - showPlanPopup.value = false
236 -
237 - await nextTick()
238 -
239 - // 构建结果页面参数
240 - const params = {
241 - success: result.success ? 'true' : 'false'
242 - }
243 -
244 - // 失败时传递后端返回的错误信息
245 - if (!result.success && result.message) {
246 - params.message = result.message
247 - }
248 -
249 - console.log('[Product Detail] 跳转到结果页面,参数:', params)
250 -
251 - Taro.navigateTo({
252 - url: `/pages/plan-submit-result/index?${new URLSearchParams(params).toString()}`
253 - })
254 -}
255 227
256 useLoad((options) => { 228 useLoad((options) => {
257 console.log('产品详情页参数:', options) 229 console.log('产品详情页参数:', options)
......
...@@ -125,7 +125,7 @@ ...@@ -125,7 +125,7 @@
125 </template> 125 </template>
126 126
127 <script setup> 127 <script setup>
128 -import { ref, computed, nextTick } from 'vue' 128 +import { ref, computed } from 'vue'
129 import Taro from '@tarojs/taro' 129 import Taro from '@tarojs/taro'
130 import { useGo } from '@/hooks/useGo' 130 import { useGo } from '@/hooks/useGo'
131 import LoadMoreList from '@/components/list/LoadMoreList' 131 import LoadMoreList from '@/components/list/LoadMoreList'
...@@ -137,6 +137,7 @@ import MaterialCard from '@/components/cards/MaterialCard.vue' ...@@ -137,6 +137,7 @@ import MaterialCard from '@/components/cards/MaterialCard.vue'
137 import PlanFormContainer from '@/components/plan/PlanFormContainer.vue' 137 import PlanFormContainer from '@/components/plan/PlanFormContainer.vue'
138 import { searchAPI } from '@/api/search' 138 import { searchAPI } from '@/api/search'
139 import { mockSearchAPI } from '@/utils/mockData' 139 import { mockSearchAPI } from '@/utils/mockData'
140 +import { usePlanSubmit } from '@/composables/usePlanSubmit'
140 141
141 // ⚠️ MOCK 数据开关 - 开发环境使用 mock 数据,生产环境使用真实 API 142 // ⚠️ MOCK 数据开关 - 开发环境使用 mock 数据,生产环境使用真实 API
142 const USE_MOCK_DATA = process.env.NODE_ENV === 'development' 143 const USE_MOCK_DATA = process.env.NODE_ENV === 'development'
...@@ -457,50 +458,14 @@ const openPlanPopup = (productId) => { ...@@ -457,50 +458,14 @@ const openPlanPopup = (productId) => {
457 showPlanPopup.value = true 458 showPlanPopup.value = true
458 } 459 }
459 460
460 -/** 461 +// 使用 composable 统一处理计划书提交后逻辑
461 - * 处理计划书提交 462 +const { handlePlanSubmit } = usePlanSubmit({
462 - * 463 + getPopupState: () => showPlanPopup.value,
463 - * @description 接收 PlanFormContainer 的提交结果事件 464 + setPopupState: (state) => { showPlanPopup.value = state },
464 - * PlanFormContainer 已经调用过 API 并处理了 toast 提示 465 + clearSelectedProduct: () => { selectedProduct.value = null },
465 - * 这里根据成功/失败状态跳转到结果页面 466 + useGoUtil: true,
466 - * @param {Object} result - 提交结果 467 + pageName: 'Search Page'
467 - * @param {boolean} result.success - 是否成功 468 +})
468 - * @param {number} result.order_id - 订单 ID
469 - * @param {number} result.product_id - 产品 ID
470 - * @param {string} result.form_sn - 表单标识
471 - */
472 -const handlePlanSubmit = async (result) => {
473 - console.log('[Search Page] 计划书提交结果:', result)
474 -
475 - // 关闭弹窗
476 - showPlanPopup.value = false
477 -
478 - await nextTick()
479 -
480 - selectedProduct.value = null
481 -
482 - // 构建结果页面参数
483 - const params = {
484 - success: result.success ? 'true' : 'false'
485 - }
486 -
487 - // 失败时传递后端返回的错误信息
488 - if (!result.success && result.message) {
489 - params.message = result.message
490 - }
491 -
492 - console.log('[Search Page] 跳转到结果页面,参数:', params)
493 -
494 - go('/pages/plan-submit-result/index', params)
495 -}
496 -
497 -const handlePlanClose = async () => {
498 - showPlanPopup.value = false
499 -
500 - await nextTick()
501 -
502 - selectedProduct.value = null
503 -}
504 469
505 /** 470 /**
506 * 处理收藏状态改变 471 * 处理收藏状态改变
......