import { ref, watchEffect } from "vue"; import { autoSetModeCallback, createTemploraryID, unSetModelUpdate } from './sys' import { addStoreItem, deleteStoreItem, fetchStoreItems, recoverStoreItems, saveStoreItems, updateStoreItem, togetherCallback, diffStoreItemsChange } from '@/utils' import { fetchRecords, postAddRecord, postUpdateRecord, postDeleteRecord, RecordStatus, postMegerRecord, blobToFile, uploadFile, fetchRecordStatus } from '@/api' import { getRecordFragments, initRecordFragmentsByRecord, recordFragments, backupRecordFragments, recoverRecordFragments, saveRecordFragments } from './record-fragment' import type { Record as SRecord } from '@/api' import { Message } from "bill/index"; export type Record = LocalMode export type Records = Record[] export const records = ref([]) export const createRecord = (record: Partial = {}): Record => ({ id: createTemploraryID(), title: '讲解视频' + (records.value.length + 1), cover: '', url: '', status: RecordStatus.UN, sort: Math.min(...records.value.map(item => item.sort)) - 1, ...record }) let bcRecords: Records = [] export const getBackupRecords = () => bcRecords export const backupRecords = () => { bcRecords = records.value.map(record => ({...record })) } export const recoverRecords = recoverStoreItems(records, getBackupRecords) const refreshRecords: NodeJS.Timeout[] = [] const refreshRecordStatus = async (record: Record) => { const status = await fetchRecordStatus(record.id) if (status === RecordStatus.SUCCESS) { refreshRecords.forEach(clearTimeout) refreshRecords.length = 0 initialRecords() } else { refreshRecords.push( setTimeout(refreshRecordStatus.bind(null, record), 3000) ) } } const getRecordMergeFiles = (record: Record) => { const fragments = getRecordFragments(record) const files = fragments .filter(fragment => typeof fragment.url !== 'string') .map(fragment => blobToFile((fragment.url as Blob), '.mp4')) return files } export const initialRecords = async () => { const serviceRecords = await fetchRecords() unSetModelUpdate(() => records.value = serviceRecords) await Promise.all(records.value.map(initRecordFragmentsByRecord)) for (const record of records.value) { if (record.status === RecordStatus.RUN) { refreshRecordStatus(record) } } backupRecords() } export const addRecord = addStoreItem(records, async (record) => { const cover = record.cover ? await uploadFile(record.cover) : record.cover const serviceRecord = await postAddRecord({ ...record, cover }, getRecordMergeFiles(record)) record.id = serviceRecord.id await postUpdateRecord(record as SRecord) return record }) export const updateRecord = updateStoreItem(records, async (record) => { const cover = record.cover ? await uploadFile(record.cover) : record.cover return await postUpdateRecord({ ...record, cover }) }) export const deleteRecord = deleteStoreItem(records, async record => { recordFragments.value = recordFragments.value.filter(fragment => fragment.recordId !== record.id) await postDeleteRecord(record.id) }) export const saveRecords = saveStoreItems( records, getBackupRecords, { add: addRecord, delete: deleteRecord, update: updateRecord } ) export const autoSaveRecords = autoSetModeCallback( [records, recordFragments], { backup: togetherCallback([backupRecordFragments, backupRecords]), recovery: togetherCallback([recoverRecordFragments, recoverRecords]), save: async () => { if (!records.value.every(record => record.title)) { Message.warning('视频名称不可为空') throw '视频名称不可为空' } for (let i = 0; i < records.value.length; i++) { records.value[i].sort = i } const oldRecords = getBackupRecords() const { added } = diffStoreItemsChange(records.value, oldRecords) await saveRecords() await saveRecordFragments() const files = records.value .filter(record => !added.includes(record)) .map(record => ({ record, merge: getRecordMergeFiles(record) })) .filter(({merge}) => merge.length) await Promise.all(files.map(({record, merge}) => postMegerRecord(merge, record.id))) await initialRecords() } } ) export { RecordStatus }