diff --git a/frontend_nuxt/components/PostTypeSelect.vue b/frontend_nuxt/components/PostTypeSelect.vue new file mode 100644 index 000000000..0537e30bc --- /dev/null +++ b/frontend_nuxt/components/PostTypeSelect.vue @@ -0,0 +1,27 @@ + + + diff --git a/frontend_nuxt/pages/new-post.vue b/frontend_nuxt/pages/new-post.vue index 93f9b4211..6ff97c9df 100644 --- a/frontend_nuxt/pages/new-post.vue +++ b/frontend_nuxt/pages/new-post.vue @@ -8,6 +8,7 @@
+
@@ -32,6 +33,25 @@
发布中...
+
+
+ + + +
+
+ +
+ + + +
+
+
+ + +
+
@@ -40,6 +60,7 @@ import { ref, onMounted, computed } from 'vue' import PostEditor from '../components/PostEditor.vue' import CategorySelect from '../components/CategorySelect.vue' +import PostTypeSelect from '../components/PostTypeSelect.vue' import TagSelect from '../components/TagSelect.vue' import { API_BASE_URL, toast } from '../main' import { getToken, authState } from '../utils/auth' @@ -47,12 +68,16 @@ import LoginOverlay from '../components/LoginOverlay.vue' export default { name: 'NewPostPageView', - components: { PostEditor, CategorySelect, TagSelect, LoginOverlay }, + components: { PostEditor, CategorySelect, PostTypeSelect, TagSelect, LoginOverlay }, setup() { const title = ref('') const content = ref('') const selectedCategory = ref('') const selectedTags = ref([]) + const postType = ref('NORMAL') + const prizeIcon = ref('') + const prizeCount = ref(1) + const endTime = ref('') const isWaitingPosting = ref(false) const isAiLoading = ref(false) const isLogin = computed(() => authState.loggedIn) @@ -85,6 +110,10 @@ export default { content.value = '' selectedCategory.value = '' selectedTags.value = [] + postType.value = 'NORMAL' + prizeIcon.value = '' + prizeCount.value = 1 + endTime.value = '' // 删除草稿 const token = getToken() @@ -164,6 +193,39 @@ export default { } } + const uploadPrizeIcon = async (e) => { + const file = e.target.files[0] + if (!file) return + try { + const token = getToken() + const res = await fetch(`${API_BASE_URL}/api/upload/presign?filename=${encodeURIComponent(file.name)}`, { + headers: { Authorization: `Bearer ${token}` } + }) + if (!res.ok) { + toast.error('获取上传地址失败') + return + } + const info = await res.json() + const put = await fetch(info.uploadUrl, { method: 'PUT', body: file }) + if (!put.ok) { + toast.error('上传失败') + return + } + prizeIcon.value = info.fileUrl + toast.success('上传成功') + } catch (err) { + toast.error('上传失败') + } + } + + const increasePrizeCount = () => { + prizeCount.value++ + } + + const decreasePrizeCount = () => { + if (prizeCount.value > 1) prizeCount.value-- + } + const aiGenerate = async () => { if (!content.value.trim()) { toast.error('内容为空,无法优化') @@ -213,6 +275,20 @@ export default { toast.error('请选择标签') return } + if (postType.value === 'LOTTERY') { + if (!prizeIcon.value) { + toast.error('请上传奖品图片') + return + } + if (!prizeCount.value || prizeCount.value <= 0) { + toast.error('奖品数量必须大于0') + return + } + if (!endTime.value) { + toast.error('请选择抽奖结束时间') + return + } + } try { const token = getToken() await ensureTags(token) @@ -227,7 +303,13 @@ export default { title: title.value, content: content.value, categoryId: selectedCategory.value, - tagIds: selectedTags.value + tagIds: selectedTags.value, + type: postType.value, + prizeIcon: postType.value === 'LOTTERY' ? prizeIcon.value : null, + prizeCount: postType.value === 'LOTTERY' ? prizeCount.value : null, + startTime: postType.value === 'LOTTERY' ? new Date().toISOString() : null, + endTime: postType.value === 'LOTTERY' ? new Date(endTime.value).toISOString() : null, + prizeDescription: postType.value === 'LOTTERY' ? '' : null }) }) const data = await res.json() @@ -251,7 +333,26 @@ export default { isWaitingPosting.value = false } } - return { title, content, selectedCategory, selectedTags, submitPost, saveDraft, clearPost, isWaitingPosting, aiGenerate, isAiLoading, isLogin } + return { + title, + content, + selectedCategory, + selectedTags, + postType, + prizeIcon, + prizeCount, + endTime, + submitPost, + saveDraft, + clearPost, + isWaitingPosting, + aiGenerate, + isAiLoading, + isLogin, + uploadPrizeIcon, + increasePrizeCount, + decreasePrizeCount + } } } @@ -366,6 +467,40 @@ export default { padding-bottom: 50px; } +.lottery-options { + display: flex; + flex-direction: column; + gap: 10px; + margin-top: 20px; +} + +.lottery-field { + display: flex; + flex-direction: column; + gap: 5px; +} + +.prize-preview { + max-width: 200px; + margin-top: 5px; +} + +.prize-count-input { + display: flex; + align-items: center; + gap: 5px; +} + +.prize-count-input input { + width: 60px; + text-align: center; +} + +.prize-count-input button { + width: 30px; + height: 30px; +} + @media (max-width: 768px) { .new-post-page { width: calc(100vw - 20px);