feat: 添加内容可见性字段和过滤选项,优化内容列表查询功能
This commit is contained in:
@@ -72,6 +72,7 @@ type CreatorContentItem struct {
|
|||||||
VideoCount int `json:"video_count"`
|
VideoCount int `json:"video_count"`
|
||||||
AudioCount int `json:"audio_count"`
|
AudioCount int `json:"audio_count"`
|
||||||
Status string `json:"status"`
|
Status string `json:"status"`
|
||||||
|
Visibility string `json:"visibility"`
|
||||||
CreatedAt string `json:"created_at"`
|
CreatedAt string `json:"created_at"`
|
||||||
IsPinned bool `json:"is_pinned"`
|
IsPinned bool `json:"is_pinned"`
|
||||||
IsPurchased bool `json:"is_purchased"`
|
IsPurchased bool `json:"is_purchased"`
|
||||||
@@ -90,6 +91,7 @@ type AssetDTO struct {
|
|||||||
type CreatorContentListFilter struct {
|
type CreatorContentListFilter struct {
|
||||||
requests.Pagination
|
requests.Pagination
|
||||||
Status *string `query:"status"`
|
Status *string `query:"status"`
|
||||||
|
Visibility *string `query:"visibility"`
|
||||||
Genre *string `query:"genre"`
|
Genre *string `query:"genre"`
|
||||||
Key *string `query:"key"`
|
Key *string `query:"key"`
|
||||||
Keyword *string `query:"keyword"`
|
Keyword *string `query:"keyword"`
|
||||||
|
|||||||
@@ -121,6 +121,12 @@ func (s *creator) ListContents(
|
|||||||
if filter.Status != nil && *filter.Status != "" {
|
if filter.Status != nil && *filter.Status != "" {
|
||||||
q = q.Where(tbl.Status.Eq(consts.ContentStatus(*filter.Status)))
|
q = q.Where(tbl.Status.Eq(consts.ContentStatus(*filter.Status)))
|
||||||
}
|
}
|
||||||
|
if filter.Visibility != nil && *filter.Visibility != "" {
|
||||||
|
q = q.Where(tbl.Visibility.Eq(consts.ContentVisibility(*filter.Visibility)))
|
||||||
|
}
|
||||||
|
if filter.Visibility != nil && *filter.Visibility != "" {
|
||||||
|
q = q.Where(tbl.Visibility.Eq(consts.ContentVisibility(*filter.Visibility)))
|
||||||
|
}
|
||||||
if filter.Genre != nil && *filter.Genre != "" {
|
if filter.Genre != nil && *filter.Genre != "" {
|
||||||
val := *filter.Genre
|
val := *filter.Genre
|
||||||
if cn, ok := genreMap[val]; ok {
|
if cn, ok := genreMap[val]; ok {
|
||||||
@@ -208,6 +214,7 @@ func (s *creator) ListContents(
|
|||||||
VideoCount: videoCount,
|
VideoCount: videoCount,
|
||||||
AudioCount: audioCount,
|
AudioCount: audioCount,
|
||||||
Status: string(item.Status),
|
Status: string(item.Status),
|
||||||
|
Visibility: string(item.Visibility),
|
||||||
CreatedAt: item.CreatedAt.Format("2006-01-02 15:04"),
|
CreatedAt: item.CreatedAt.Format("2006-01-02 15:04"),
|
||||||
IsPinned: item.IsPinned,
|
IsPinned: item.IsPinned,
|
||||||
IsPurchased: false,
|
IsPurchased: false,
|
||||||
|
|||||||
@@ -18,6 +18,14 @@
|
|||||||
<option v-for="opt in statusOptions" :key="opt.key" :value="opt.key">{{ opt.value }}</option>
|
<option v-for="opt in statusOptions" :key="opt.key" :value="opt.key">{{ opt.value }}</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="flex items-center gap-2">
|
||||||
|
<span class="text-sm font-bold text-slate-500">可见性:</span>
|
||||||
|
<select v-model="filterVisibility"
|
||||||
|
class="h-9 px-3 rounded border border-slate-200 text-sm focus:border-primary-500 outline-none bg-white cursor-pointer min-w-[100px]">
|
||||||
|
<option value="all">全部</option>
|
||||||
|
<option v-for="opt in visibilityOptions" :key="opt.key" :value="opt.key">{{ opt.value }}</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
<div class="flex items-center gap-2">
|
<div class="flex items-center gap-2">
|
||||||
<span class="text-sm font-bold text-slate-500">曲种:</span>
|
<span class="text-sm font-bold text-slate-500">曲种:</span>
|
||||||
<select v-model="filterGenre"
|
<select v-model="filterGenre"
|
||||||
@@ -77,6 +85,9 @@
|
|||||||
</div>
|
</div>
|
||||||
<!-- Status Badge -->
|
<!-- Status Badge -->
|
||||||
<div class="flex items-center gap-2 ml-4">
|
<div class="flex items-center gap-2 ml-4">
|
||||||
|
<span class="text-[10px] px-1.5 py-0.5 rounded border border-slate-200 text-slate-500 bg-slate-50" v-if="item.visibility">
|
||||||
|
{{ getVisibilityLabel(item.visibility) }}
|
||||||
|
</span>
|
||||||
<span v-if="item.status === 'blocked'" class="text-red-500 text-xs flex items-center gap-1 cursor-help"
|
<span v-if="item.status === 'blocked'" class="text-red-500 text-xs flex items-center gap-1 cursor-help"
|
||||||
title="已被封禁">
|
title="已被封禁">
|
||||||
<i class="pi pi-info-circle"></i> 封禁
|
<i class="pi pi-info-circle"></i> 封禁
|
||||||
@@ -173,10 +184,16 @@ const toast = useToast();
|
|||||||
const confirm = useConfirm();
|
const confirm = useConfirm();
|
||||||
const contents = ref([]);
|
const contents = ref([]);
|
||||||
const filterStatus = ref('all');
|
const filterStatus = ref('all');
|
||||||
|
const filterVisibility = ref('all');
|
||||||
const filterGenre = ref('all');
|
const filterGenre = ref('all');
|
||||||
const filterKey = ref('all');
|
const filterKey = ref('all');
|
||||||
const searchKeyword = ref('');
|
const searchKeyword = ref('');
|
||||||
const statusOptions = ref([]);
|
const statusOptions = ref([]);
|
||||||
|
const visibilityOptions = [
|
||||||
|
{ key: 'public', value: '公开' },
|
||||||
|
{ key: 'tenant_only', value: '仅会员' },
|
||||||
|
{ key: 'private', value: '私有' }
|
||||||
|
];
|
||||||
const genreOptions = ref([]);
|
const genreOptions = ref([]);
|
||||||
const keys = ['C大调', 'D大调', 'E大调', 'F大调', 'G大调', 'A大调', 'B大调', '降E大调'];
|
const keys = ['C大调', 'D大调', 'E大调', 'F大调', 'G大调', 'A大调', 'B大调', '降E大调'];
|
||||||
|
|
||||||
@@ -196,6 +213,7 @@ const fetchContents = async () => {
|
|||||||
try {
|
try {
|
||||||
const params = {};
|
const params = {};
|
||||||
if (filterStatus.value !== 'all') params.status = filterStatus.value;
|
if (filterStatus.value !== 'all') params.status = filterStatus.value;
|
||||||
|
if (filterVisibility.value !== 'all') params.visibility = filterVisibility.value;
|
||||||
if (filterGenre.value !== 'all') params.genre = filterGenre.value;
|
if (filterGenre.value !== 'all') params.genre = filterGenre.value;
|
||||||
if (filterKey.value !== 'all') params.key = filterKey.value;
|
if (filterKey.value !== 'all') params.key = filterKey.value;
|
||||||
if (searchKeyword.value) params.keyword = searchKeyword.value;
|
if (searchKeyword.value) params.keyword = searchKeyword.value;
|
||||||
@@ -212,7 +230,7 @@ onMounted(() => {
|
|||||||
fetchContents();
|
fetchContents();
|
||||||
});
|
});
|
||||||
|
|
||||||
watch([filterStatus, filterGenre, filterKey], () => {
|
watch([filterStatus, filterVisibility, filterGenre, filterKey], () => {
|
||||||
fetchContents();
|
fetchContents();
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -225,6 +243,15 @@ const getGenreLabel = (key) => {
|
|||||||
return opt ? opt.value : key;
|
return opt ? opt.value : key;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const getVisibilityLabel = (vis) => {
|
||||||
|
const map = {
|
||||||
|
'public': '公开',
|
||||||
|
'tenant_only': '仅会员',
|
||||||
|
'private': '私有'
|
||||||
|
};
|
||||||
|
return map[vis] || vis;
|
||||||
|
};
|
||||||
|
|
||||||
const statusStyle = (status) => {
|
const statusStyle = (status) => {
|
||||||
// Map backend status to UI style. Labels should ideally come from backend option value/label map if needed,
|
// Map backend status to UI style. Labels should ideally come from backend option value/label map if needed,
|
||||||
// but for style/color mapping we can keep it here or use a helper.
|
// but for style/color mapping we can keep it here or use a helper.
|
||||||
|
|||||||
Reference in New Issue
Block a user