feat: add community post category selection

This commit is contained in:
hajimi
2026-08-03 17:27:17 +08:00
parent d86b1b2cad
commit d5160e0ba3
3 changed files with 207 additions and 15 deletions
+170 -14
View File
@@ -48,9 +48,36 @@
</view>
</view>
<view class="publish-category">
<view class="publish-section__head">
<view class="publish-section__title-wrap">
<text class="publish-section__title">选择分类</text>
<text class="publish-section__required">必选</text>
</view>
<text class="publish-section__hint">{{ selectedCategory ? '已选择 1 个分类' : '请选择帖子所属分类' }}</text>
</view>
<view v-if="categoryList.length" class="publish-category__list">
<view v-for="category in categoryList" :key="category.id" class="publish-category__item"
:class="{ 'publish-category__item--active': selectedCategory === category.id }"
@tap="selectCategory(category.id)">
<image v-if="category.icon" :src="category.icon" class="publish-category__icon" mode="aspectFit" />
<view v-else class="publish-category__icon publish-category__icon--fallback">
<text>{{ category.name.slice(0, 1) }}</text>
</view>
<text class="publish-category__name">{{ category.name }}</text>
<u-icon v-if="selectedCategory === category.id" name="checkmark" size="22" color="#fff" />
</view>
</view>
<text v-else class="publish-section__empty">暂无可选分类</text>
</view>
<view class="publish-tags">
<view class="publish-tags__label">
<text>选择标签</text>
<view class="publish-section__head">
<view class="publish-section__title-wrap">
<text class="publish-section__title">选择标签</text>
<text class="publish-section__optional">可多选</text>
</view>
<text class="publish-section__hint">已选 {{ selectedTags.length }}/3</text>
</view>
<view class="publish-tags__list">
<view v-for="tag in tagList" :key="tag.id" class="publish-tags__item"
@@ -66,7 +93,7 @@
<script setup lang="ts">
import { reactive, ref, computed, onMounted } from 'vue'
import { publishPost, getTagLists } from '@/api/community'
import { publishPost, getTagLists, getCommunityCategoryLists } from '@/api/community'
import { uploadImage } from '@/api/app'
import { getToken } from '@/utils/auth'
@@ -75,6 +102,8 @@ const scrollHeight = ref(500)
const publishing = ref(false)
const tagList = ref<any[]>([])
const selectedTags = ref<number[]>([])
const categoryList = ref<any[]>([])
const selectedCategory = ref(0)
const isPaid = ref(false)
const pricePoints = ref('')
@@ -85,7 +114,7 @@ const form = reactive({
})
const canPublish = computed(() => {
return form.content.trim().length > 0 || form.images.length > 0
return selectedCategory.value > 0 && (form.content.trim().length > 0 || form.images.length > 0)
})
onMounted(() => {
@@ -97,13 +126,14 @@ onMounted(() => {
statusBarHeight.value = 20
// #endif
scrollHeight.value = sysInfo.windowHeight - statusBarHeight.value - 88
fetchTags()
fetchPublishOptions()
})
const fetchTags = async () => {
const fetchPublishOptions = async () => {
try {
const res = await getTagLists()
tagList.value = res || []
const [categories, tags] = await Promise.all([getCommunityCategoryLists(), getTagLists()])
categoryList.value = categories || []
tagList.value = tags || []
} catch (e) { }
}
@@ -121,6 +151,10 @@ const handlePublish = async () => {
uni.navigateTo({ url: '/pages/login/login' })
return
}
if (!selectedCategory.value) {
uni.showToast({ title: '请选择帖子分类', icon: 'none' })
return
}
if (!canPublish.value || publishing.value) return
publishing.value = true
try {
@@ -148,6 +182,7 @@ const handlePublish = async () => {
await publishPost({
content: form.content,
images: uploadedImages,
category_id: selectedCategory.value,
tag_ids: selectedTags.value,
post_type: 0,
is_paid: isPaid.value ? 1 : 0,
@@ -186,6 +221,10 @@ const removeImage = (idx: number) => {
form.images.splice(idx, 1)
}
const selectCategory = (id: number) => {
selectedCategory.value = id
}
const toggleTag = (id: number) => {
const idx = selectedTags.value.indexOf(id)
if (idx >= 0) {
@@ -377,17 +416,134 @@ const toggleTag = (id: number) => {
}
}
.publish-tags {
.publish-section__head {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 20rpx;
}
.publish-section__title-wrap {
display: flex;
align-items: center;
gap: 10rpx;
}
.publish-section__title {
font-size: 28rpx;
font-weight: 600;
color: #333;
}
.publish-section__required,
.publish-section__optional {
padding: 3rpx 10rpx;
border-radius: 10rpx;
font-size: 20rpx;
}
.publish-section__required {
color: #ef4444;
background: #fff1f2;
}
.publish-section__optional {
color: #185dff;
background: #edf4ff;
}
.publish-section__hint,
.publish-section__empty {
color: #9aa3b2;
font-size: 22rpx;
}
.publish-category {
padding: 24rpx 30rpx;
border-top: 16rpx solid #f5f5f5;
&__label {
font-size: 28rpx;
font-weight: 600;
color: #333;
margin-bottom: 20rpx;
&__list {
display: flex;
flex-wrap: wrap;
gap: 16rpx;
}
&__item {
position: relative;
display: flex;
align-items: center;
min-width: calc((100% - 16rpx) / 2);
max-width: 100%;
height: 76rpx;
box-sizing: border-box;
padding: 0 20rpx;
gap: 12rpx;
border: 2rpx solid #edf0f5;
border-radius: 14rpx;
background: #fafbfc;
&--active {
border-color: #185dff;
background: #eef4ff;
.publish-category__name {
color: #185dff;
font-weight: 600;
}
&::after {
position: absolute;
top: 0;
right: 0;
width: 34rpx;
height: 34rpx;
border-radius: 0 10rpx 0 12rpx;
background: #185dff;
content: '';
}
.u-icon {
position: absolute;
top: 2rpx;
right: 3rpx;
z-index: 1;
}
}
}
&__icon {
width: 40rpx;
height: 40rpx;
border-radius: 50%;
flex-shrink: 0;
&--fallback {
display: flex;
align-items: center;
justify-content: center;
background: #dbe8ff;
text {
color: #185dff;
font-size: 20rpx;
font-weight: 700;
}
}
}
&__name {
overflow: hidden;
color: #4b5563;
font-size: 25rpx;
text-overflow: ellipsis;
white-space: nowrap;
}
}
.publish-tags {
padding: 24rpx 30rpx;
border-top: 16rpx solid #f5f5f5;
&__list {
display: flex;
flex-wrap: wrap;