830 lines
18 KiB
Plaintext
830 lines
18 KiB
Plaintext
<!-- 专题页面 - 专题列表和专题详情 -->
|
||
<template>
|
||
<scroll-view direction="vertical" class="topics-page" :scroll-y="true" :enable-back-to-top="true">
|
||
<!-- 顶部导航栏 -->
|
||
<view class="header">
|
||
<view class="header-content">
|
||
<text class="title">{{ $t('mt.topic.hot') }}</text>
|
||
<view class="header-actions">
|
||
<view class="action-btn" @click="showTypeSelector">
|
||
<text class="action-text">{{ currentTypeText }}</text>
|
||
</view>
|
||
<view class="action-btn" @click="navigateToSearch">
|
||
<text class="action-icon">🔍</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 专题类型筛选 -->
|
||
<view class="type-section">
|
||
<scroll-view direction="horizontal" class="type-scroll" :scroll-x="true">
|
||
<view class="type-tabs">
|
||
<view
|
||
v-for="(type, index) in topicTypesList"
|
||
:key="type.value"
|
||
class="type-tab"
|
||
:class="{ active: selectedTypeValue === type.value }"
|
||
@click="selectTopicType(type)">
|
||
<text class="type-text">{{ type.text }}</text>
|
||
</view>
|
||
</view>
|
||
</scroll-view>
|
||
</view>
|
||
|
||
<!-- 精选专题 -->
|
||
<view class="featured-section" v-if="featuredTopicsList.length > 0">
|
||
<view class="section-header">
|
||
<text class="section-title">{{ $t('mt.topic.featured') }}</text>
|
||
</view>
|
||
<scroll-view direction="horizontal" class="featured-scroll" :scroll-x="true">
|
||
<view class="featured-topics">
|
||
<view
|
||
v-for="(topic, index) in featuredTopicsList"
|
||
:key="topic.id"
|
||
class="featured-topic"
|
||
@click="navigateToTopicDetail(topic)">
|
||
<view class="topic-cover" :style="{ backgroundImage: `url(${topic.cover_image})` }">
|
||
<view class="topic-overlay">
|
||
<view class="topic-badge" :style="{ backgroundColor: getTopicStatusColor(topic.status) }">
|
||
<text class="badge-text">{{ getTopicTypeDisplayName(topic.topic_type) }}</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
<view class="topic-info">
|
||
<text class="topic-title">{{ topic.title }}</text>
|
||
<text class="topic-desc">{{ topic.description }}</text>
|
||
<view class="topic-stats">
|
||
<text class="stat-text">{{ topic.content_count }}篇文章</text>
|
||
<text class="stat-text">{{ topic.view_count }}阅读</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</scroll-view>
|
||
</view>
|
||
|
||
<!-- 专题列表 -->
|
||
<view class="topics-section">
|
||
<view class="section-header">
|
||
<text class="section-title">{{ $t('mt.topic.all') }}</text>
|
||
<view class="section-actions">
|
||
<view class="sort-btn" @click="showSortOptions">
|
||
<text class="sort-text">{{ sortOptionText }}</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 加载状态 -->
|
||
<view class="loading-section" v-if="pageState.loading">
|
||
<text class="loading-text">{{ $t('mt.status.loading') }}</text>
|
||
</view>
|
||
|
||
<!-- 错误状态 -->
|
||
<view class="error-section" v-if="pageState.error !== null">
|
||
<text class="error-text">{{ pageState.error }}</text>
|
||
<view class="retry-btn" @click="retryLoad">
|
||
<text class="retry-text">{{ $t('mt.action.retry') }}</text>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 专题列表 -->
|
||
<view class="topics-list" v-if="topicsList.length > 0">
|
||
<view
|
||
v-for="(topic, index) in topicsList"
|
||
:key="topic.id"
|
||
class="topic-item"
|
||
@click="navigateToTopicDetail(topic)">
|
||
<view class="topic-header">
|
||
<view class="topic-type-badge" :style="{ backgroundColor: getTopicStatusColor(topic.status) }">
|
||
<text class="type-badge-text">{{ getTopicTypeDisplayName(topic.topic_type) }}</text>
|
||
</view>
|
||
<text class="topic-time">{{ formatRelativeTimeKey(topic.updated_at) }}</text>
|
||
</view>
|
||
<text class="topic-title">{{ topic.title }}</text>
|
||
<text class="topic-description">{{ topic.description }}</text>
|
||
<view class="topic-meta">
|
||
<view class="topic-stats">
|
||
<text class="stat-item">📄 {{ topic.content_count }}{{ $t('mt.topic.articleCount') }}</text>
|
||
<text class="stat-item">👁 {{ topic.view_count }}</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 空状态 -->
|
||
<view class="empty-section" v-if="topicsList.length === 0 && !pageState.loading && pageState.error === null">
|
||
<text class="empty-text">{{ $t('mt.topic.empty') }}</text>
|
||
<view class="refresh-btn" @click="refreshData">
|
||
<text class="refresh-text">{{ $t('mt.action.refresh') }}</text>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 加载更多 -->
|
||
<view class="load-more-section" v-if="topicsList.length > 0 && hasMore">
|
||
<view class="load-more-btn" @click="loadMore" v-if="!loadingMore">
|
||
<text class="load-more-text">{{ $t('mt.button.loadMore') }}</text>
|
||
</view>
|
||
<view class="loading-more" v-if="loadingMore">
|
||
<text class="loading-more-text">{{ $t('mt.loadingMore') }}</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 类型选择弹窗 -->
|
||
<view class="modal-overlay" v-if="showTypeModal" @click="hideTypeSelector">
|
||
<view class="type-modal" @click.stop="">
|
||
<view class="modal-header">
|
||
<text class="modal-title">{{ $t('mt.topic.typeTitle') }}</text>
|
||
<view class="modal-close" @click="hideTypeSelector">
|
||
<text class="close-text">×</text>
|
||
</view>
|
||
</view>
|
||
<view class="type-list">
|
||
<view
|
||
v-for="(type, index) in topicTypesList"
|
||
:key="type.value"
|
||
class="type-item"
|
||
:class="{ active: selectedTypeValue === type.value }"
|
||
@click="selectTopicType(type)">
|
||
<text class="type-name">{{ type.text }}</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 排序选择弹窗 -->
|
||
<view class="modal-overlay" v-if="showSortModal" @click="hideSortOptions">
|
||
<view class="sort-modal" @click.stop="">
|
||
<view class="modal-header">
|
||
<text class="modal-title">{{ $t('mt.modal.sort') }}</text>
|
||
<view class="modal-close" @click="hideSortOptions">
|
||
<text class="close-text">×</text>
|
||
</view>
|
||
</view>
|
||
<view class="sort-list">
|
||
<view
|
||
v-for="(option, index) in sortOptionsList"
|
||
:key="option.value"
|
||
class="sort-item"
|
||
:class="{ active: currentSortOption === option.value }"
|
||
@click="selectSortOption(option)">
|
||
<text class="sort-name">{{ option.text }}</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</scroll-view>
|
||
</template>
|
||
|
||
<script setup lang="uts">
|
||
import {
|
||
TopicData,
|
||
PageState,
|
||
ResponsiveState,
|
||
TOPIC_TYPES,
|
||
TOPIC_STATUS,
|
||
SORT_OPTIONS,
|
||
getTopicTypeDisplayName,
|
||
getTopicStatusColor,
|
||
formatRelativeTimeKey
|
||
} from './types.uts'
|
||
import supa from '@/components/supadb/aksupainstance.uts'
|
||
import { tt } from '@/utils/i18nfun.uts'
|
||
import i18n from '@/i18n/index.uts' // 保留用于语言切换
|
||
|
||
// 页面状态
|
||
const pageState = ref<PageState>({
|
||
loading: false,
|
||
error: null,
|
||
currentPage: 1,
|
||
pageSize: 20,
|
||
total: 0
|
||
})
|
||
|
||
// UI状态变量
|
||
const showTypeModal = ref<boolean>(false)
|
||
const showSortModal = ref<boolean>(false)
|
||
const loadingMore = ref<boolean>(false)
|
||
const hasMore = ref<boolean>(true)
|
||
|
||
// 当前选择状态
|
||
const selectedTypeValue = ref<string>('')
|
||
const currentTypeText = ref<string>(tt('mt.topic.allTypes'))
|
||
const currentSortOption = ref<string>('updated_at_desc')
|
||
const sortOptionText = ref<string>(tt('mt.topic.sort.recentUpdate'))
|
||
|
||
// 数据列表 - 直接使用强类型 TopicData 数组
|
||
const topicsList = ref<Array<TopicData>>([])
|
||
const featuredTopicsList = ref<Array<TopicData>>([])
|
||
|
||
|
||
// 选项列表
|
||
const topicTypesList = ref([
|
||
{ value: '', text: tt('mt.topic.allTypes') },
|
||
...TOPIC_TYPES.map(type => ({
|
||
value: type.value,
|
||
text: tt(`mt.topicType.${type.value}`)
|
||
}))
|
||
])
|
||
const sortOptionsList = ref([
|
||
{ value: 'updated_at_desc', text: tt('mt.topic.sort.recentUpdate') },
|
||
{ value: 'created_at_desc', text: tt('mt.topic.sort.newest') },
|
||
{ value: 'view_count_desc', text: tt('mt.topic.sort.popular') },
|
||
{ value: 'content_count_desc', text: tt('mt.topic.sort.contentCount') }
|
||
])
|
||
|
||
// 计算属性
|
||
const topicFilter = computed((): string => {
|
||
let filter = "status=in.(active,featured)"
|
||
if (selectedTypeValue.value !== '') {
|
||
filter += `&topic_type=eq.${selectedTypeValue.value}`
|
||
}
|
||
|
||
// 排序
|
||
const sortParts = currentSortOption.value.split('_')
|
||
const column = sortParts.slice(0, -1).join('_')
|
||
const direction = sortParts[sortParts.length - 1] === 'desc' ? 'desc' : 'asc'
|
||
filter += `&order=${column}.${direction}`
|
||
return filter
|
||
})
|
||
|
||
// 生命周期
|
||
onMounted(() => {
|
||
initializeData()
|
||
})
|
||
|
||
// 初始化数据
|
||
const initializeData = () => {
|
||
loadTopics()
|
||
loadFeaturedTopics()
|
||
}
|
||
|
||
// 加载专题数据
|
||
const loadTopics = async () => {
|
||
if (pageState.value.loading) return
|
||
pageState.value.loading = true
|
||
pageState.value.error = null
|
||
try {
|
||
let query = supa.from('ak_topics')
|
||
.select('*')
|
||
.in('status', ['active', 'featured'])
|
||
if (selectedTypeValue.value !== '') {
|
||
query = query.eq('topic_type', selectedTypeValue.value)
|
||
}
|
||
const sortParts = currentSortOption.value.split('_')
|
||
const column = sortParts.slice(0, -1).join('_')
|
||
const direction = sortParts[sortParts.length - 1] === 'desc'
|
||
if (direction) {
|
||
query = query.order(column, { ascending: false })
|
||
} else {
|
||
query = query.order(column, { ascending: true })
|
||
}
|
||
const start = (pageState.value.currentPage - 1) * pageState.value.pageSize
|
||
const end = start + pageState.value.pageSize - 1
|
||
query = query.range(start, end)
|
||
const result = await query.executeAs<Array<TopicData>>()
|
||
if (result.error !== null) {
|
||
throw new Error(result.error.message)
|
||
}
|
||
const data = result.data
|
||
if (data !== null) {
|
||
if (pageState.value.currentPage === 1) {
|
||
topicsList.value = data
|
||
} else {
|
||
topicsList.value = topicsList.value.concat(data)
|
||
}
|
||
hasMore.value = data.length === pageState.value.pageSize
|
||
}
|
||
} catch (e: any) {
|
||
pageState.value.error = tt('mt.error.loadTopicsFailed')
|
||
console.error('Topics loading error:', e)
|
||
} finally {
|
||
pageState.value.loading = false
|
||
}
|
||
}
|
||
|
||
// 加载精选专题
|
||
const loadFeaturedTopics = async () => {
|
||
try {
|
||
const result = await supa.from('ak_topics')
|
||
.select('*')
|
||
.eq('status', 'featured')
|
||
.order('updated_at', { ascending: false })
|
||
.limit(5)
|
||
.executeAs<Array<TopicData>>()
|
||
if (result.error !== null) {
|
||
console.error('Featured topics loading error:', result.error)
|
||
return
|
||
}
|
||
const data = result.data
|
||
if (data !== null) {
|
||
featuredTopicsList.value = data
|
||
}
|
||
} catch (e: any) {
|
||
console.error('Featured topics loading error:', e)
|
||
}
|
||
}
|
||
|
||
|
||
// 类型选择
|
||
const showTypeSelector = () => {
|
||
showTypeModal.value = true
|
||
}
|
||
|
||
const hideTypeSelector = () => {
|
||
showTypeModal.value = false
|
||
}
|
||
|
||
const selectTopicType = (type: any) => {
|
||
selectedTypeValue.value = type.value
|
||
currentTypeText.value = type.text
|
||
hideTypeSelector()
|
||
pageState.value.currentPage = 1
|
||
loadTopics()
|
||
}
|
||
|
||
// 排序选择
|
||
const showSortOptions = () => {
|
||
showSortModal.value = true
|
||
}
|
||
|
||
const hideSortOptions = () => {
|
||
showSortModal.value = false
|
||
}
|
||
|
||
const selectSortOption = (option: any) => {
|
||
currentSortOption.value = option.value
|
||
sortOptionText.value = option.text
|
||
hideSortOptions()
|
||
pageState.value.currentPage = 1
|
||
loadTopics()
|
||
}
|
||
|
||
// 加载更多
|
||
const loadMore = () => {
|
||
if (loadingMore.value || !hasMore.value) return
|
||
|
||
loadingMore.value = true
|
||
pageState.value.currentPage += 1
|
||
|
||
setTimeout(() => {
|
||
loadTopics()
|
||
loadingMore.value = false
|
||
}, 500)
|
||
}
|
||
|
||
// 刷新数据
|
||
const refreshData = () => {
|
||
pageState.value.currentPage = 1
|
||
loadTopics()
|
||
}
|
||
|
||
// 重试加载
|
||
const retryLoad = () => {
|
||
pageState.value.error = null
|
||
loadTopics()
|
||
}
|
||
|
||
// 导航函数
|
||
const navigateToTopicDetail = (topic: TopicData) => {
|
||
const topicId = topic.id
|
||
uni.navigateTo({
|
||
url: `/pages/info/topic-detail?id=${topicId}`
|
||
})
|
||
}
|
||
|
||
const navigateToSearch = () => {
|
||
uni.navigateTo({
|
||
url: '/pages/info/search?type=topic'
|
||
})
|
||
}
|
||
</script>
|
||
|
||
<style>
|
||
.topics-page {
|
||
flex: 1;
|
||
background-color: #f8fafc;
|
||
}
|
||
|
||
.header {
|
||
background-color: #ffffff;
|
||
border-bottom-width: 1px;
|
||
border-bottom-color: #e2e8f0;
|
||
padding-top: 10px;
|
||
padding-bottom: 10px;
|
||
}
|
||
|
||
.header-content {
|
||
flex-direction: row;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
padding-left: 16px;
|
||
padding-right: 16px;
|
||
}
|
||
|
||
.title {
|
||
font-size: 20px;
|
||
font-weight: bold;
|
||
color: #1f2937;
|
||
}
|
||
|
||
.header-actions {
|
||
flex-direction: row;
|
||
align-items: center;
|
||
}
|
||
|
||
.action-btn {
|
||
padding: 8px 12px;
|
||
margin-left: 8px;
|
||
background-color: #f1f5f9;
|
||
border-radius: 20px;
|
||
}
|
||
|
||
.action-text {
|
||
font-size: 14px;
|
||
color: #475569;
|
||
}
|
||
|
||
.action-icon {
|
||
font-size: 16px;
|
||
}
|
||
|
||
.type-section {
|
||
background-color: #ffffff;
|
||
padding-top: 12px;
|
||
padding-bottom: 12px;
|
||
border-bottom-width: 1px;
|
||
border-bottom-color: #e2e8f0;
|
||
}
|
||
|
||
.type-scroll {
|
||
height: 40px;
|
||
}
|
||
|
||
.type-tabs {
|
||
flex-direction: row;
|
||
padding-left: 16px;
|
||
padding-right: 16px;
|
||
}
|
||
|
||
.type-tab {
|
||
padding: 8px 16px;
|
||
margin-right: 12px;
|
||
background-color: #f8fafc;
|
||
border-radius: 20px;
|
||
border-width: 1px;
|
||
border-color: #e2e8f0;
|
||
}
|
||
|
||
.type-tab.active {
|
||
background-color: #8b5cf6;
|
||
border-color: #8b5cf6;
|
||
}
|
||
|
||
.type-text {
|
||
font-size: 14px;
|
||
color: #64748b;
|
||
white-space: nowrap;
|
||
}
|
||
|
||
.type-tab.active .type-text {
|
||
color: #ffffff;
|
||
}
|
||
|
||
.featured-section {
|
||
background-color: #ffffff;
|
||
margin-top: 8px;
|
||
padding: 16px 0;
|
||
}
|
||
|
||
.section-header {
|
||
flex-direction: row;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
padding-left: 16px;
|
||
padding-right: 16px;
|
||
margin-bottom: 12px;
|
||
}
|
||
|
||
.section-title {
|
||
font-size: 18px;
|
||
font-weight: bold;
|
||
color: #1f2937;
|
||
}
|
||
|
||
.section-actions {
|
||
flex-direction: row;
|
||
align-items: center;
|
||
}
|
||
|
||
.sort-btn {
|
||
padding: 6px 12px;
|
||
background-color: #f1f5f9;
|
||
border-radius: 16px;
|
||
}
|
||
|
||
.sort-text {
|
||
font-size: 12px;
|
||
color: #475569;
|
||
}
|
||
|
||
.featured-scroll {
|
||
height: 240px;
|
||
}
|
||
|
||
.featured-topics {
|
||
flex-direction: row;
|
||
padding-left: 16px;
|
||
padding-right: 16px;
|
||
}
|
||
|
||
.featured-topic {
|
||
width: 280px;
|
||
background-color: #ffffff;
|
||
border-radius: 12px;
|
||
margin-right: 12px;
|
||
border-width: 1px;
|
||
border-color: #e2e8f0;
|
||
}
|
||
|
||
.topic-cover {
|
||
height: 140px;
|
||
background-color: #f1f5f9;
|
||
border-top-left-radius: 12px;
|
||
border-top-right-radius: 12px;
|
||
background-size: cover;
|
||
background-position: center;
|
||
position: relative;
|
||
}
|
||
|
||
.topic-overlay {
|
||
position: absolute;
|
||
top: 0;
|
||
left: 0;
|
||
right: 0;
|
||
bottom: 0;
|
||
padding: 12px;
|
||
justify-content: flex-end;
|
||
align-items: flex-start;
|
||
}
|
||
|
||
.topic-badge {
|
||
background-color: #8b5cf6;
|
||
border-radius: 12px;
|
||
padding: 4px 8px;
|
||
}
|
||
|
||
.badge-text {
|
||
font-size: 12px;
|
||
color: #ffffff;
|
||
}
|
||
|
||
.topic-info {
|
||
padding: 12px;
|
||
}
|
||
|
||
.topic-title {
|
||
font-size: 16px;
|
||
font-weight: bold;
|
||
color: #1f2937;
|
||
line-height: 22px;
|
||
margin-bottom: 6px;
|
||
}
|
||
|
||
.topic-desc {
|
||
font-size: 14px;
|
||
color: #64748b;
|
||
line-height: 20px;
|
||
margin-bottom: 8px;
|
||
}
|
||
|
||
.topic-stats {
|
||
flex-direction: row;
|
||
align-items: center;
|
||
}
|
||
|
||
.stat-text {
|
||
font-size: 12px;
|
||
color: #94a3b8;
|
||
margin-right: 12px;
|
||
}
|
||
|
||
.topics-section {
|
||
background-color: #ffffff;
|
||
margin-top: 8px;
|
||
padding: 16px 0;
|
||
}
|
||
|
||
.topics-list {
|
||
padding-left: 16px;
|
||
padding-right: 16px;
|
||
}
|
||
|
||
.topic-item {
|
||
background-color: #f8fafc;
|
||
border-radius: 12px;
|
||
padding: 16px;
|
||
margin-bottom: 12px;
|
||
border-width: 1px;
|
||
border-color: #e2e8f0;
|
||
}
|
||
|
||
.topic-header {
|
||
flex-direction: row;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
margin-bottom: 8px;
|
||
}
|
||
|
||
.topic-type-badge {
|
||
background-color: #8b5cf6;
|
||
border-radius: 10px;
|
||
padding: 2px 8px;
|
||
}
|
||
|
||
.type-badge-text {
|
||
font-size: 12px;
|
||
color: #ffffff;
|
||
}
|
||
|
||
.topic-time {
|
||
font-size: 12px;
|
||
color: #94a3b8;
|
||
}
|
||
|
||
.topic-title {
|
||
font-size: 16px;
|
||
font-weight: bold;
|
||
color: #1f2937;
|
||
line-height: 22px;
|
||
margin-bottom: 6px;
|
||
}
|
||
|
||
.topic-description {
|
||
font-size: 14px;
|
||
color: #64748b;
|
||
line-height: 20px;
|
||
margin-bottom: 12px;
|
||
}
|
||
|
||
.topic-meta {
|
||
flex-direction: row;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
}
|
||
|
||
.topic-stats {
|
||
flex-direction: row;
|
||
align-items: center;
|
||
}
|
||
|
||
.stat-item {
|
||
font-size: 12px;
|
||
color: #94a3b8;
|
||
margin-right: 16px;
|
||
}
|
||
|
||
.loading-section, .error-section {
|
||
padding: 40px 16px;
|
||
align-items: center;
|
||
justify-content: center;
|
||
}
|
||
|
||
.loading-text {
|
||
font-size: 14px;
|
||
color: #94a3b8;
|
||
}
|
||
|
||
.error-text {
|
||
font-size: 14px;
|
||
color: #ef4444;
|
||
margin-bottom: 16px;
|
||
text-align: center;
|
||
}
|
||
|
||
.retry-btn, .refresh-btn {
|
||
background-color: #8b5cf6;
|
||
border-radius: 20px;
|
||
padding: 8px 24px;
|
||
}
|
||
|
||
.retry-text, .refresh-text {
|
||
font-size: 14px;
|
||
color: #ffffff;
|
||
}
|
||
|
||
.empty-section {
|
||
padding: 60px 16px;
|
||
align-items: center;
|
||
justify-content: center;
|
||
}
|
||
|
||
.empty-text {
|
||
font-size: 16px;
|
||
color: #94a3b8;
|
||
margin-bottom: 16px;
|
||
}
|
||
|
||
.load-more-section {
|
||
padding: 20px 16px;
|
||
align-items: center;
|
||
justify-content: center;
|
||
}
|
||
|
||
.load-more-btn {
|
||
background-color: #f1f5f9;
|
||
border-radius: 20px;
|
||
padding: 10px 24px;
|
||
border-width: 1px;
|
||
border-color: #e2e8f0;
|
||
}
|
||
|
||
.load-more-text {
|
||
font-size: 14px;
|
||
color: #475569;
|
||
}
|
||
|
||
.loading-more {
|
||
padding: 10px 24px;
|
||
}
|
||
|
||
.loading-more-text {
|
||
font-size: 14px;
|
||
color: #94a3b8;
|
||
}
|
||
|
||
.modal-overlay {
|
||
position: fixed;
|
||
top: 0;
|
||
left: 0;
|
||
right: 0;
|
||
bottom: 0;
|
||
background-color: rgba(0, 0, 0, 0.5);
|
||
align-items: center;
|
||
justify-content: center;
|
||
z-index: 1000;
|
||
}
|
||
|
||
.type-modal, .sort-modal {
|
||
background-color: #ffffff;
|
||
border-radius: 16px;
|
||
margin: 20px;
|
||
max-height: 500px;
|
||
min-width: 280px;
|
||
}
|
||
|
||
.modal-header {
|
||
flex-direction: row;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
padding: 16px 20px;
|
||
border-bottom-width: 1px;
|
||
border-bottom-color: #e2e8f0;
|
||
}
|
||
|
||
.modal-title {
|
||
font-size: 18px;
|
||
font-weight: bold;
|
||
color: #1f2937;
|
||
}
|
||
|
||
.modal-close {
|
||
width: 32px;
|
||
height: 32px;
|
||
align-items: center;
|
||
justify-content: center;
|
||
border-radius: 16px;
|
||
background-color: #f1f5f9;
|
||
}
|
||
|
||
.close-text {
|
||
font-size: 20px;
|
||
color: #64748b;
|
||
}
|
||
|
||
.type-list, .sort-list {
|
||
max-height: 400px;
|
||
}
|
||
|
||
.type-item, .sort-item {
|
||
padding: 16px 20px;
|
||
border-bottom-width: 1px;
|
||
border-bottom-color: #f1f5f9;
|
||
}
|
||
|
||
.type-item.active, .sort-item.active {
|
||
background-color: #f3f4f6;
|
||
}
|
||
|
||
.type-name, .sort-name {
|
||
font-size: 16px;
|
||
color: #1f2937;
|
||
}
|
||
|
||
.type-item.active .type-name,
|
||
.sort-item.active .sort-name {
|
||
color: #8b5cf6;
|
||
}
|
||
</style>
|