/** * 通知渠道 Composable * 封装列表/统计/详情/CRUD/批量操作,遵循 VNC §5 标准结构。 */ import { ref, computed } from 'vue' import { getNotificationChannelListApi, getNotificationChannelStatsApi, getNotificationChannelDetailApi, createNotificationChannelApi, updateNotificationChannelApi, deleteNotificationChannelApi } from '@/apis/external-systems/notification_channel_api' import { unwrap } from './utils' export function useNotificationChannel() { // ===== 响应式状态 ===== const list = ref([]) const stats = ref(null) const detail = ref(null) const total = ref(0) const loading = ref(false) const detailLoading = ref(false) const submitting = ref(false) const error = ref(null) // ===== 计算属性 ===== const isEmpty = computed(() => !loading.value && list.value.length === 0) // ===== 列表与统计 ===== async function fetchList(params) { loading.value = true error.value = null try { const data = unwrap(await getNotificationChannelListApi(params)) list.value = data?.items ?? [] total.value = data?.total ?? list.value.length } catch (e) { error.value = e list.value = [] total.value = 0 } finally { loading.value = false } } async function fetchStats() { error.value = null try { stats.value = unwrap(await getNotificationChannelStatsApi()) ?? null } catch (e) { error.value = e } } // ===== 详情 ===== async function fetchDetail(id) { detailLoading.value = true error.value = null try { detail.value = unwrap(await getNotificationChannelDetailApi(id)) ?? null return detail.value } catch (e) { error.value = e throw e } finally { detailLoading.value = false } } // ===== CRUD ===== async function create(data) { submitting.value = true error.value = null try { return unwrap(await createNotificationChannelApi(data)) } catch (e) { error.value = e throw e } finally { submitting.value = false } } async function update(id, data) { submitting.value = true error.value = null try { return unwrap(await updateNotificationChannelApi(id, data)) } catch (e) { error.value = e throw e } finally { submitting.value = false } } async function remove(id) { error.value = null try { await deleteNotificationChannelApi(id) } catch (e) { error.value = e throw e } } // ===== 批量操作(逐条调用,返回成功/失败明细) ===== async function batchUpdateEnabled(items, enabled) { const results = { success: 0, failed: [] } for (const item of items) { try { await updateNotificationChannelApi(item.id, { enabled }) results.success += 1 } catch (e) { results.failed.push({ id: item.id, name: item.name, reason: e.message || '更新失败' }) } } return results } async function batchDelete(items) { const results = { success: 0, failed: [] } for (const item of items) { try { await deleteNotificationChannelApi(item.id) results.success += 1 } catch (e) { results.failed.push({ id: item.id, name: item.name, reason: e.message || '删除失败' }) } } return results } function resetDetail() { detail.value = null } return { // 状态 list, stats, detail, total, loading, detailLoading, submitting, error, isEmpty, // 列表与统计 fetchList, fetchStats, // 详情 fetchDetail, resetDetail, // CRUD create, update, remove, // 批量操作 batchUpdateEnabled, batchDelete } }