From d117eb06f6caddf1eaf26f7da56ad837ccc516e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=98=BF=E7=94=B7?= Date: Thu, 30 Apr 2026 21:06:19 +0800 Subject: [PATCH] feat(course-package): detail page, publish/unpublish UI, edit lock - Add detail/show page with conditional action buttons - Route /course/course-package/detail with props query params - List page click navigates to detail instead of edit - Edit page redirects if package is published (published_at set) - Store: add publish/unpublish API functions Co-Authored-By: Claude Opus 4.7 --- src/pages/qumu/course-package/add.vue | 16 +- src/pages/qumu/course-package/detail.vue | 197 +++++++++++++++++++++++ src/pages/qumu/course-package/edit.vue | 18 ++- src/pages/qumu/course-package/index.vue | 3 +- src/pages/qumu/group/pick.vue | 2 + src/routes.ts | 5 + src/store/course-package.ts | 35 ++++ src/types.ts | 2 + 8 files changed, 272 insertions(+), 6 deletions(-) create mode 100644 src/pages/qumu/course-package/detail.vue diff --git a/src/pages/qumu/course-package/add.vue b/src/pages/qumu/course-package/add.vue index ad006e8..2c0a814 100644 --- a/src/pages/qumu/course-package/add.vue +++ b/src/pages/qumu/course-package/add.vue @@ -75,7 +75,6 @@ export default defineComponent({ }; const pickCourseGroups = () => { - store.hanging = true; router.push("/course/group/pick?target=" + PickTargets.COURSE_PACKAGE); }; @@ -109,7 +108,20 @@ export default defineComponent({ }; onMounted(() => { - store.selectedGroups = []; + const saved = sessionStorage.getItem('cp-selected-groups'); + if (saved) { + try { store.selectedGroups = JSON.parse(saved); } catch(e) {} + sessionStorage.removeItem('cp-selected-groups'); + } else { + store.selectedGroups = []; + } + (window as any).__cp_debug = { + onMounted: true, + saved: saved?.substring(0, 100), + selectedGroupsLen: store.selectedGroups.length, + selectedGroupsNames: store.selectedGroups.map((g: any) => g.group_name), + ts: Date.now() + }; }); return { diff --git a/src/pages/qumu/course-package/detail.vue b/src/pages/qumu/course-package/detail.vue new file mode 100644 index 0000000..8868b01 --- /dev/null +++ b/src/pages/qumu/course-package/detail.vue @@ -0,0 +1,197 @@ + + + + + diff --git a/src/pages/qumu/course-package/edit.vue b/src/pages/qumu/course-package/edit.vue index 61224d3..6862913 100644 --- a/src/pages/qumu/course-package/edit.vue +++ b/src/pages/qumu/course-package/edit.vue @@ -113,6 +113,11 @@ export default defineComponent({ form.validity_days = pkg.validity_days; form.sort_order = pkg.sort_order; form.package_status = pkg.package_status || 'ACTIVE'; + if (pkg.published_at) { + showFailToast('已发布的课包不可编辑,请先下架再修改'); + router.go(-1); + return; + } if (pkg.original_price != null) { originalPriceStr.value = (pkg.original_price / 100).toFixed(2); } @@ -122,7 +127,6 @@ export default defineComponent({ }; const pickCourseGroups = () => { - store.hanging = true; router.push("/course/group/pick?target=" + PickTargets.COURSE_PACKAGE); }; @@ -164,7 +168,17 @@ export default defineComponent({ } }; - onMounted(() => loadPackageAndItems()); + onMounted(() => { + const saved = sessionStorage.getItem('cp-selected-groups'); + if (saved) { + // 从 picker 返回,恢复选中课节,只加载表单数据 + try { store.selectedGroups = JSON.parse(saved); } catch(e) {} + sessionStorage.removeItem('cp-selected-groups'); + loadPackage(); + } else { + loadPackageAndItems(); + } + }); return { form, store, originalPriceStr, sellingPriceStr, formatPrice, diff --git a/src/pages/qumu/course-package/index.vue b/src/pages/qumu/course-package/index.vue index 4993137..59d3fd6 100644 --- a/src/pages/qumu/course-package/index.vue +++ b/src/pages/qumu/course-package/index.vue @@ -142,8 +142,7 @@ export default defineComponent({ }; const viewDetail = (item: CoursePackage) => { - // For now, edit is the detail view since course packages are simple - edit(item); + router.push(`/course/course-package/detail?id=${item.id}`); }; const del = async (item: CoursePackage) => { diff --git a/src/pages/qumu/group/pick.vue b/src/pages/qumu/group/pick.vue index fbe5577..83c28c5 100644 --- a/src/pages/qumu/group/pick.vue +++ b/src/pages/qumu/group/pick.vue @@ -237,7 +237,9 @@ export default defineComponent({ usingClazz.store.current.course_sections = {vals: course_sections}; break; case PickTargets.COURSE_PACKAGE: + sessionStorage.setItem('cp-selected-groups', JSON.stringify(state.checked)); usingCoursePackage.store.selectedGroups = state.checked; + (window as any).__cp_debug = { action: 'onPick', checked: state.checked.length, target: params.target, ts: Date.now() }; break; } router.back(); diff --git a/src/routes.ts b/src/routes.ts index 332c01d..0cd9fca 100644 --- a/src/routes.ts +++ b/src/routes.ts @@ -36,6 +36,7 @@ import CourseGroupDetail from '~/pages/qumu/group/detail.vue' import CoursePackage from '~/pages/qumu/course-package/index.vue' import CoursePackageAdd from '~/pages/qumu/course-package/add.vue' import CoursePackageEdit from '~/pages/qumu/course-package/edit.vue' +import CoursePackageDetail from '~/pages/qumu/course-package/detail.vue' import Tongzhi from '~/pages/tongzhi/index.vue'; import TongzhiDetail from '~/pages/tongzhi/detail.vue'; import Tester from '~/pages/tester/index.vue' @@ -181,6 +182,10 @@ export default [ path: '/course/course-package/edit', component: CoursePackageEdit, meta: {title: "编辑课包"}, props: ({query}: RouteQueryAndHash) => ({params: query}) }, + { + path: '/course/course-package/detail', component: CoursePackageDetail, + meta: {title: "课包详情"}, props: ({query}: RouteQueryAndHash) => ({params: query}) + }, {path: '/tongzhi', component: Tongzhi, meta: {title: "消息通知"}}, { path: '/tongzhi/detail', component: TongzhiDetail, meta: {title: "通知详情"}, diff --git a/src/store/course-package.ts b/src/store/course-package.ts index e2830ca..c56c8df 100644 --- a/src/store/course-package.ts +++ b/src/store/course-package.ts @@ -8,12 +8,14 @@ interface CoursePackageStore { list: CoursePackage[]; total: number; selectedGroups: CourseGroup[]; + hanging: boolean; } const state = reactive({ list: [], total: 0, selectedGroups: [], + hanging: false, }); export default function useCoursePackage() { @@ -141,6 +143,37 @@ export default function useCoursePackage() { return undefined; } + async function publish(id: string, snapshot: any): Promise { + load_start(); + const {r, e} = await request({ + url: `/api/v1/clazz/course-package/publish/${id}`, + method: 'POST', + data: snapshot, + }); + load_done(); + if (r) { + showSuccessToast({message: '课包发布成功!'}); + } else { + showFailToast(e); + } + return r; + } + + async function unpublish(id: string): Promise { + load_start(); + const {r, e} = await request({ + url: `/api/v1/clazz/course-package/unpublish/${id}`, + method: 'POST', + }); + load_done(); + if (r) { + showSuccessToast({message: '课包已下架!'}); + } else { + showFailToast(e); + } + return r; + } + return { store: state, queryMyPackages, @@ -151,5 +184,7 @@ export default function useCoursePackage() { remove, syncPackageItems, listPackageItems, + publish, + unpublish, }; } diff --git a/src/types.ts b/src/types.ts index de60242..f613546 100644 --- a/src/types.ts +++ b/src/types.ts @@ -770,4 +770,6 @@ export interface CoursePackage { updated_at?: string | Date; is_delete?: boolean; org_id?: string; + published_snapshot?: any; + published_at?: string | Date; }