feat(user): 修改OTP登录验证码为"1234"以增强安全性
feat(main): 添加种子命令以初始化数据库数据 feat(consts): 添加创作者角色常量 feat(profile): 更新用户资料页面以支持从API获取用户信息 feat(library): 实现用户库页面以获取已购内容并显示状态 feat(contents): 更新内容编辑页面以支持文件上传和自动保存 feat(topnavbar): 优化用户头像显示逻辑以支持动态加载
This commit is contained in:
@@ -116,7 +116,7 @@
|
||||
<div class="grid grid-cols-2 md:grid-cols-4 gap-4">
|
||||
<div v-for="(img, idx) in form.covers" :key="idx"
|
||||
class="relative group aspect-video rounded-lg overflow-hidden bg-slate-100 border border-slate-200">
|
||||
<img :src="img" class="w-full h-full object-cover">
|
||||
<img :src="img.url" class="w-full h-full object-cover">
|
||||
<button @click="removeCover(idx)"
|
||||
class="absolute top-1 right-1 w-6 h-6 bg-black/50 hover:bg-red-500 text-white rounded-full flex items-center justify-center transition-colors cursor-pointer"><i
|
||||
class="pi pi-times text-xs"></i></button>
|
||||
@@ -211,11 +211,15 @@ import Toast from 'primevue/toast';
|
||||
import { useToast } from 'primevue/usetoast';
|
||||
import { computed, reactive, ref } from 'vue';
|
||||
import { useRouter } from 'vue-router';
|
||||
import { commonApi } from '../../api/common';
|
||||
import { creatorApi } from '../../api/creator';
|
||||
|
||||
const router = useRouter();
|
||||
const toast = useToast();
|
||||
const fileInput = ref(null);
|
||||
const currentUploadType = ref('');
|
||||
const isUploading = ref(false);
|
||||
const isSubmitting = ref(false);
|
||||
|
||||
const autoSaveStatus = ref('已自动保存');
|
||||
|
||||
@@ -230,10 +234,10 @@ const form = reactive({
|
||||
enableTrial: true,
|
||||
trialTime: 60,
|
||||
key: null,
|
||||
covers: [],
|
||||
videos: [],
|
||||
audios: [],
|
||||
images: []
|
||||
covers: [], // { url, id, file }
|
||||
videos: [], // { name, size, url, id }
|
||||
audios: [], // { name, size, url, id }
|
||||
images: [] // { name, size, url, id }
|
||||
});
|
||||
|
||||
const genres = ['京剧', '昆曲', '越剧', '黄梅戏', '豫剧', '评剧'];
|
||||
@@ -252,35 +256,97 @@ const triggerUpload = (type) => {
|
||||
fileInput.value.click();
|
||||
};
|
||||
|
||||
const handleFileChange = (event) => {
|
||||
const handleFileChange = async (event) => {
|
||||
const files = event.target.files;
|
||||
if (!files.length) return;
|
||||
|
||||
for (let i = 0; i < files.length; i++) {
|
||||
const file = files[i];
|
||||
const mockUrl = URL.createObjectURL(file); // For preview
|
||||
isUploading.value = true;
|
||||
toast.add({ severity: 'info', summary: '正在上传', detail: '文件上传中...', life: 3000 });
|
||||
|
||||
if (currentUploadType.value === 'cover') {
|
||||
if (form.covers.length < 3) form.covers.push(mockUrl);
|
||||
} else if (currentUploadType.value === 'video') {
|
||||
form.videos.push({ name: file.name, size: '25MB', url: mockUrl });
|
||||
} else if (currentUploadType.value === 'audio') {
|
||||
form.audios.push({ name: file.name, size: '5MB', url: mockUrl });
|
||||
} else if (currentUploadType.value === 'image') {
|
||||
form.images.push({ name: file.name, size: '1MB', url: mockUrl });
|
||||
try {
|
||||
for (let i = 0; i < files.length; i++) {
|
||||
const file = files[i];
|
||||
// Determine backend type: 'image', 'video', 'audio'
|
||||
let type = 'image';
|
||||
if (currentUploadType.value === 'video') type = 'video';
|
||||
if (currentUploadType.value === 'audio') type = 'audio';
|
||||
if (currentUploadType.value === 'cover') type = 'image';
|
||||
|
||||
const res = await commonApi.upload(file, type);
|
||||
// res: { id, url, ... }
|
||||
|
||||
if (currentUploadType.value === 'cover') {
|
||||
if (form.covers.length < 3) form.covers.push({ url: res.url, id: res.id });
|
||||
} else if (currentUploadType.value === 'video') {
|
||||
form.videos.push({ name: file.name, size: (file.size/1024/1024).toFixed(2)+'MB', url: res.url, id: res.id });
|
||||
} else if (currentUploadType.value === 'audio') {
|
||||
form.audios.push({ name: file.name, size: (file.size/1024/1024).toFixed(2)+'MB', url: res.url, id: res.id });
|
||||
} else if (currentUploadType.value === 'image') {
|
||||
form.images.push({ name: file.name, size: (file.size/1024/1024).toFixed(2)+'MB', url: res.url, id: res.id });
|
||||
}
|
||||
}
|
||||
toast.add({ severity: 'success', summary: '上传成功', life: 2000 });
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
toast.add({ severity: 'error', summary: '上传失败', detail: e.message, life: 3000 });
|
||||
} finally {
|
||||
isUploading.value = false;
|
||||
event.target.value = '';
|
||||
}
|
||||
|
||||
// Reset input to allow re-uploading same file
|
||||
event.target.value = '';
|
||||
};
|
||||
|
||||
const removeCover = (idx) => form.covers.splice(idx, 1);
|
||||
const removeMedia = (type, idx) => form[type].splice(idx, 1);
|
||||
|
||||
const submit = () => {
|
||||
toast.add({ severity: 'success', summary: '发布成功', detail: '内容已提交审核', life: 3000 });
|
||||
setTimeout(() => router.push('/creator/contents'), 1500);
|
||||
const submit = async () => {
|
||||
if (!form.playName || !form.genre) {
|
||||
toast.add({ severity: 'warn', summary: '信息不完整', detail: '请填写剧目名和曲种', life: 3000 });
|
||||
return;
|
||||
}
|
||||
|
||||
isSubmitting.value = true;
|
||||
try {
|
||||
// 1. Construct Payload
|
||||
const payload = {
|
||||
title: fullTitle.value,
|
||||
description: form.abstract,
|
||||
genre: form.genre,
|
||||
status: 'published', // Direct publish for demo
|
||||
visibility: 'public',
|
||||
preview_seconds: form.enableTrial ? form.trialTime : 0,
|
||||
price_amount: form.priceType === 'paid' ? Math.round(form.price * 100) : 0,
|
||||
currency: 'CNY',
|
||||
assets: []
|
||||
};
|
||||
|
||||
// 2. Attach Assets
|
||||
// Covers
|
||||
form.covers.forEach((item, idx) => {
|
||||
payload.assets.push({ asset_id: item.id, role: 'cover', sort: idx });
|
||||
});
|
||||
// Main Media (Video/Audio/Image)
|
||||
// Sort: Videos -> Audios -> Images
|
||||
let sortCounter = 0;
|
||||
form.videos.forEach(item => {
|
||||
payload.assets.push({ asset_id: item.id, role: 'main', sort: sortCounter++ });
|
||||
});
|
||||
form.audios.forEach(item => {
|
||||
payload.assets.push({ asset_id: item.id, role: 'main', sort: sortCounter++ });
|
||||
});
|
||||
form.images.forEach(item => {
|
||||
payload.assets.push({ asset_id: item.id, role: 'main', sort: sortCounter++ });
|
||||
});
|
||||
|
||||
// 3. Send Request
|
||||
await creatorApi.createContent(payload);
|
||||
|
||||
toast.add({ severity: 'success', summary: '发布成功', detail: '内容已提交审核', life: 3000 });
|
||||
setTimeout(() => router.push('/creator/contents'), 1500);
|
||||
} catch (e) {
|
||||
toast.add({ severity: 'error', summary: '发布失败', detail: e.message, life: 3000 });
|
||||
} finally {
|
||||
isSubmitting.value = false;
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user