Commit aefe108b by lijiabin

【需求 17679】wip: 继续加页面,首页、详情页等

parent 5ba0ac6a
......@@ -63,7 +63,7 @@
<div class="flex gap-3">
<div class="left flex-1 basis-full xl:basis-3/4 transition-all duration-500">
<div class="tabs-container h-70px flex relative md:h-60px rounded-lg mb-3">
<div ref="tabsRef" class="tabs-container h-70px flex relative md:h-60px rounded-lg mb-3">
<div
v-for="tab in tabs"
:key="tab.path"
......@@ -199,7 +199,16 @@
<div class="w-1 h-4 bg-gradient-to-b from-pink-500 to-rose-500 rounded-full"></div>
<h1 class="text-sm sm:text-base font-bold">任务中心</h1>
</div>
<h2 class="text-xs sm:text-sm mb-1 text-gray-600 ml-3">常规任务 | 特殊任务</h2>
<h2 class="text-xs sm:text-sm mb-1 text-gray-600 ml-3">
<span
v-for="item in taskTypeList"
:key="item.value"
class="text-#333 cursor-pointer after:content-['|'] after:mx-2 after:text-#999 last:after:content-none"
:class="{ 'text-#999': currentTask !== item.value }"
@click="currentTask = item.value"
>{{ item.label }}
</span>
</h2>
</div>
</div>
......@@ -239,7 +248,6 @@
</div>
</template>
<script setup lang="ts">
import { ref, onMounted } from 'vue'
import { useRouter, useRoute } from 'vue-router'
import front from '@/assets/img/culture/front_page.png'
import ya from '@/assets/img/culture/ya_culture.png'
......@@ -249,11 +257,13 @@ import { getTaskList, dailySign, getCarouselList } from '@/api'
import { usePageSearch } from '@/hooks'
import { TaskTypeEnum } from '@/constants'
import type { CarouselItemDto } from '@/api'
import { TABS_REF_KEY } from '@/constants'
const route = useRoute()
const router = useRouter()
const carouselList = ref<CarouselItemDto[]>([])
const tabsRef = useTemplateRef('tabsRef')
const tabs = [
{ name: '首页', path: 'deshboard', img: front, svg: 'culture-home' },
......@@ -265,6 +275,19 @@ const activeTab = ref(
tabs.find((item) => item.path === route.path.split('/').at(-1))?.name || '首页',
)
const taskTypeList = [
{
label: '常规任务',
value: TaskTypeEnum.REGULAR_TASK,
},
{
label: '特殊任务',
value: TaskTypeEnum.SPECIAL_TASK,
},
]
const currentTask = ref(TaskTypeEnum.REGULAR_TASK)
const toggleTab = (tab: { name: string; path: string }) => {
activeTab.value = tab.name
router.push(`/mainContainer/${tab.path}`)
......@@ -292,6 +315,8 @@ onMounted(async () => {
const { data } = await getCarouselList()
carouselList.value = data
})
provide(TABS_REF_KEY, tabsRef)
</script>
<style lang="scss" scoped>
.main-container {
......
......@@ -3,7 +3,7 @@ import UploadFile from '@/components/common/UploadFile/index.vue'
import { useResetData } from '@/hooks'
export default defineComponent((_, { expose }) => {
const [form] = useResetData({
const [form, resetForm] = useResetData({
title: '',
content: '',
faceUrl: '',
......@@ -28,16 +28,24 @@ export default defineComponent((_, { expose }) => {
return null
}
}
const resetFields = () => {
formRef.value?.resetFields()
resetForm()
}
expose({
validate,
resetFields,
})
return () => (
<div>
<el-form
ref={formRef}
model={form.value}
label-width="100px"
label-position="top"
label-width="auto"
label-position="right"
size="small"
rules={rules}
>
<el-form-item label="标题" prop="title">
......
import { ArticleTypeEnum, ReleaseStatusEnum } from '@/constants'
import { ArticleTypeEnum, ReleaseStatusEnum, SendTypeEnum } from '@/constants'
import UploadFile from '@/components/common/UploadFile/index.vue'
import SelectTags from '@/components/common/SelectTags/index.vue'
......@@ -6,7 +6,7 @@ import SelectTags from '@/components/common/SelectTags/index.vue'
import { useResetData } from '@/hooks'
export default defineComponent((_, { expose }) => {
const [form] = useResetData({
const [form, resetForm] = useResetData({
title: '',
content: '',
faceUrl: '',
......@@ -14,6 +14,8 @@ export default defineComponent((_, { expose }) => {
type: ArticleTypeEnum.PRACTICE,
mainTagId: '',
tagList: [],
sendType: SendTypeEnum.IMMEDIATE,
sendTime: '',
})
const formRef = ref<InstanceType<typeof ElForm>>()
const rules = {
......@@ -22,6 +24,8 @@ export default defineComponent((_, { expose }) => {
faceUrl: [{ required: true, message: '请上传贴图', trigger: 'change' }],
releaseStatus: [{ required: true, message: '请选择发布时间', trigger: 'blur' }],
mainTagId: [{ required: true, message: '请选择主标签', trigger: 'blur' }],
sendType: [{ required: true, message: '请选择发布类型', trigger: 'blur' }],
sendTime: [{ required: true, message: '请选择发布时间', trigger: 'blur' }],
}
const transformForm = () => {
......@@ -53,16 +57,23 @@ export default defineComponent((_, { expose }) => {
return allTags.filter((tag) => tag.value !== Number(form.value.mainTagId))
}
const resetFields = () => {
formRef.value?.resetFields()
resetForm()
}
expose({
validate,
resetFields,
})
return () => (
<div>
<el-form
size="small"
ref={formRef}
model={form.value}
label-width="100px"
label-position="top"
label-width="auto"
label-position="right"
rules={rules}
>
<el-form-item label="标题" prop="title">
......@@ -89,27 +100,63 @@ export default defineComponent((_, { expose }) => {
<UploadFile v-model={form.value.faceUrl} />
</el-form-item>
<el-form-item label="主标签" prop="mainTagId">
{/* @ts-ignore */}
<SelectTags v-model={form.value.mainTagId} />
{{
// @ts-ignore
default: () => <SelectTags v-model={form.value.mainTagId} />,
label: () => (
// <el-tooltip content="主标签最多选1个" placement="top">
<span class="cursor-pointer">
副标签
{/* <el-icon class="ml-1">
<InfoFilled />
</el-icon> */}
</span>
// </el-tooltip>
),
}}
</el-form-item>
<el-form-item label="副标签">
{/* @ts-ignore */}
{{
default: () => (
// @ts-ignore
<SelectTags
v-model={form.value.tagList}
maxSelectedTags={2}
filterTagsFn={filterTagsFn}
maxSelectedTags={3}
/>
),
label: () => (
// <el-tooltip content="副标签最多选3个" placement="top">
<span class="cursor-pointer">
副标签
{/* <el-icon class="ml-1">
<InfoFilled />
</el-icon> */}
</span>
// </el-tooltip>
),
}}
</el-form-item>
<el-form-item label="发布时间" prop="releaseStatus">
<el-radio-group v-model={form.value.releaseStatus} class="radio-group">
<el-radio value={ReleaseStatusEnum.PUBLISH} class="radio-item immediate">
<el-form-item label="发布类型" prop="sendType">
<el-radio-group v-model={form.value.sendType} class="radio-group">
<el-radio value={SendTypeEnum.IMMEDIATE} class="radio-item immediate">
立即发布
</el-radio>
<el-radio value={ReleaseStatusEnum.DRAFT} class="radio-item scheduled">
<el-radio value={SendTypeEnum.SCHEDULED} class="radio-item scheduled">
定时发布
</el-radio>
</el-radio-group>
</el-form-item>
{form.value.sendType === SendTypeEnum.SCHEDULED && (
<el-form-item label="发布时间" prop="sendTime">
<el-date-picker
class="ml-2"
v-model={form.value.sendTime}
type="datetime"
placeholder="请选择发布时间"
/>
</el-form-item>
)}
</el-form>
</div>
)
......
......@@ -3,37 +3,27 @@
v-model="dialogVisible"
:title="dialogTitle"
:close-on-click-modal="false"
width="600px"
height="600px"
:show-close="false"
width="500px"
class="post-dialog"
align-center
@closed="handleClosed"
>
<div class="bg-white/95 rounded-16px p-24px backdrop-blur-10px">
<keep-alive>
<!-- <keep-alive> -->
<component :is="currentFormComp" ref="formComponentRef" />
</keep-alive>
<!-- </keep-alive> -->
<!-- 底部按钮 -->
<div class="flex justify-end gap-12px mt-24px pt-20px border-t border-blue-100">
<button
@click.prevent="handleCancel"
class="bg-white/80 border-2 border-gray-200 text-gray-600 rounded-10px px-20px py-10px font-500 transition-all-300 hover:bg-white/90 hover:border-gray-300"
>
取消
</button>
<button
@click.prevent="handlePreview"
class="bg-white/80 border-2 border-blue-300 text-blue-500 rounded-10px px-20px py-10px font-500 transition-all-300 hover:bg-blue-50 hover:border-blue-500"
>
预览
</button>
<button
@click.prevent="handleSubmit"
class="cursor-pointer bg-gradient-to-r from-blue-400 to-cyan-400 border-none rounded-10px px-24px py-10px font-500 text-white transition-all-300 hover:transform hover:translate-y--2px hover:shadow-lg hover:shadow-blue-300"
<div class="flex justify-end gap-1">
<el-button @click="handleClosed">取消</el-button>
<el-button @click="handleSaveDraft" class="">存草稿</el-button>
<el-button
type="primary"
@click="handleSubmit"
class="px-6 py-2 bg-blue-500 hover:bg-blue-600 rounded-lg text-white text-sm font-medium shadow-sm hover:shadow-md transition-all duration-200"
>
发布
</button>
</el-button>
</div>
</div>
</el-dialog>
......@@ -61,11 +51,11 @@ const typeMap: Record<ArticleTypeEnum, { title: string; component: Component }>
},
[ArticleTypeEnum.POST]: {
title: '帖子',
component: PostForm,
component: defineAsyncComponent(() => import('./postForm.tsx')),
},
[ArticleTypeEnum.PRACTICE]: {
title: '实践',
component: PracticeForm,
component: defineAsyncComponent(() => import('./practiceForm.tsx')),
},
[ArticleTypeEnum.COLUMN]: {
title: '专栏',
......@@ -99,14 +89,15 @@ const open = (type: ArticleTypeEnum) => {
// 关闭弹窗
const close = () => {
dialogVisible.value = false
formComponentRef.value?.resetFields()
}
// 事件处理
const handleCancel = () => {
close()
const handleClosed = () => {
dialogVisible.value = false
formComponentRef.value?.resetFields()
}
const handlePreview = async () => {}
const handleSaveDraft = async () => {}
const handleSubmit = async () => {
const formData = await formComponentRef.value?.validate()
......
......@@ -30,8 +30,8 @@
@click="router.push('/userPage')"
>
<img
class="w-8 h-8 object-contain flex-shrink-0"
src="@/assets/img/culture/avatar_girl.png"
class="w-8 h-8 object-contain flex-shrink-0 rounded-full"
:src="userInfo?.avatar"
alt="个人中心"
/>
<span class="ml-2 text-sm text-gray-700 whitespace-nowrap hidden lg:inline"
......@@ -42,7 +42,7 @@
class="flex items-center cursor-pointer px-2 py-1 rounded sm:px-3 sm:py-2 hover:shadow-lg duration-200"
>
<img
class="w-8 h-8 object-contain flex-shrink-0"
class="w-7 h-7 object-contain flex-shrink-0"
src="@/assets/img/culture/feedback.png"
alt=""
/>
......@@ -50,12 +50,17 @@
>意见反馈</span
>
</div>
<el-dropdown placement="top-start" @command="handlePost">
<el-dropdown
placement="top-start"
@command="handlePost"
@visible-change="(val) => (isDropdownHover = val)"
>
<button
class="cursor-pointer w-24 h-9 text-black hover:text-black bg-[linear-gradient(to_right,#B3B8FD_0%,#7083FF_100%)] border-none hover:shadow-[0_1px_8px_0_rgba(95,0,237,0.25)] duration-200 flex-1 text-xs sm:text-sm rounded-xl group"
>
<img
class="h-7 w-7 transition-transform duration-300 ease-in-out group-hover:rotate-90"
class="h-7 w-7 transition-transform duration-300 ease-in-out"
:class="{ 'rotate-90': isDropdownHover }"
src="@/assets/img/culture/post.png"
alt=""
/>
......@@ -63,18 +68,10 @@
</button>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item :command="ArticleTypeEnum.POST" class="group"
>帖子</el-dropdown-item
>
<el-dropdown-item :command="ArticleTypeEnum.PRACTICE" class="group"
>实践</el-dropdown-item
>
<el-dropdown-item :command="ArticleTypeEnum.COLUMN" class="group"
>专栏</el-dropdown-item
>
<el-dropdown-item :command="ArticleTypeEnum.INTERVIEW" class="group"
>专访</el-dropdown-item
>
<el-dropdown-item :command="ArticleTypeEnum.POST">帖子</el-dropdown-item>
<el-dropdown-item :command="ArticleTypeEnum.PRACTICE">实践</el-dropdown-item>
<el-dropdown-item :command="ArticleTypeEnum.COLUMN">专栏</el-dropdown-item>
<el-dropdown-item :command="ArticleTypeEnum.INTERVIEW">专访</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
......@@ -107,6 +104,12 @@ import OnlineTime from './components/onlineTime.vue'
import type { RouteLocationNormalizedLoadedGeneric } from 'vue-router'
import PublishDialog from './components/publishDialog.vue'
import { ArticleTypeEnum } from '@/constants'
import { storeToRefs } from 'pinia'
import { useUserStore } from '@/stores/user'
const userStore = useUserStore()
const { userInfo } = storeToRefs(userStore)
const router = useRouter()
const search = ref('')
......@@ -129,6 +132,7 @@ const handlePost = async (type: string) => {
// router.push(command)
PublishDialogRef.value?.open(type)
}
const isDropdownHover = ref(false)
</script>
<style lang="scss" scoped>
.layout-culture {
......
......@@ -233,21 +233,7 @@
<div class="flex items-center justify-between">
<!-- 左侧:回到顶部按钮 -->
<div class="left">
<button
class="back-top-btn group flex items-center gap-3 px-4 py-2.5 bg-gradient-to-r from-blue-50 to-indigo-50 hover:from-blue-100 hover:to-indigo-100 border border-blue-200/50 rounded-full transition-all duration-300 hover:shadow-lg hover:-translate-y-1 active:scale-95"
title="回到顶部"
@click="handleBackTop"
>
<div
class="w-8 h-8 bg-gradient-to-r from-blue-500 to-indigo-500 rounded-full flex items-center justify-center group-hover:rotate-12 transition-transform duration-300 shadow-sm"
>
<SvgIcon color="#409eff" name="icon_top" size="16" />
</div>
<span
class="text-sm font-medium text-gray-700 group-hover:text-blue-600 transition-colors"
>回到顶部</span
>
</button>
<ScrollTopComp />
</div>
<!-- 右侧:分页器 -->
......@@ -272,7 +258,6 @@
</template>
<script setup lang="ts" name="CultureAsk">
import { ref } from 'vue'
import Tabs from '@/components/common/Tabs'
import {
User,
......@@ -284,6 +269,8 @@ import {
ChatDotRound,
ArrowDown,
} from '@element-plus/icons-vue'
import { useScrollTop } from '@/hooks'
import { TABS_REF_KEY } from '@/constants'
const currentPage = ref(1)
const pageSize = ref(10)
......@@ -297,12 +284,10 @@ const tabs = [
const questionContent = ref('')
const tagInput = ref('')
const handleBackTop = () => {
window.scrollTo({
top: 0,
behavior: 'smooth',
})
} // 示例数据
const tabsRef = inject(TABS_REF_KEY)
const { ScrollTopComp } = useScrollTop(tabsRef!)
const questions = ref([
{
title: '什么是顶级思维?',
......
<template>
<div>
<template v-if="list.length > 0">
<div ref="listRef">
<template v-if="list.length > 0 && !loading">
<div class="space-y-3 sm:space-y-4">
<div
v-for="item in list"
......@@ -55,7 +55,7 @@
<!-- 时间 -->
<span class="text-gray-400 font-medium ml-auto sm:ml-0">
<span class="hidden sm:inline">{{
dayjs(item.createTime).format('YYYY-MM-DD HH:mm')
dayjs(item.createTime * 1000).format('YYYY-MM-DD HH:mm')
}}</span>
</span>
</div>
......@@ -86,21 +86,7 @@
<div class="flex items-center justify-between">
<!-- 左侧:回到顶部按钮 -->
<div class="left">
<button
class="back-top-btn group flex items-center gap-3 px-4 py-2.5 bg-gradient-to-r from-blue-50 to-indigo-50 hover:from-blue-100 hover:to-indigo-100 border border-blue-200/50 rounded-full transition-all duration-300 hover:shadow-lg hover:-translate-y-1 active:scale-95"
title="回到顶部"
@click="handleBackTop"
>
<div
class="w-8 h-8 bg-gradient-to-r from-blue-500 to-indigo-500 rounded-full flex items-center justify-center group-hover:rotate-12 transition-transform duration-300 shadow-sm"
>
<SvgIcon color="#409eff" name="icon_top" size="16" />
</div>
<span
class="text-sm font-medium text-gray-700 group-hover:text-blue-600 transition-colors"
>回到顶部</span
>
</button>
<ScrollTopComp />
</div>
<!-- 右侧:分页器 -->
......@@ -115,7 +101,11 @@
layout="prev, pager, next, jumper, total"
:total="total"
class="custom-pagination"
@current-change="goToPage"
@current-change="
(e) => {
;(handleBackTop(), goToPage(e))
}
"
@size-change="changePageSize"
/>
</div>
......@@ -124,6 +114,12 @@
</div>
</div>
</template>
<template v-else-if="loading">
<div class="flex items-center justify-center h-full">
<el-icon class="is-loading mr-2 text-gray-500"><Loading /></el-icon>
<span class="text-gray-500">加载中...</span>
</div>
</template>
<template v-else>
<div class="flex items-center justify-center h-full">
<el-empty description="暂无数据" />
......@@ -135,23 +131,23 @@
<script setup lang="ts" name="RecommendList">
import { usePageSearch } from '@/hooks'
import { getArticleList } from '@/api'
import { ArticleTypeEnum } from '@/constants'
import { ArticleTypeEnum, TABS_REF_KEY } from '@/constants'
import { useScrollTop } from '@/hooks'
import dayjs from 'dayjs'
const router = useRouter()
const { list, total, searchParams, goToPage, changePageSize } = usePageSearch(getArticleList, {
const { list, total, searchParams, loading, goToPage, changePageSize } = usePageSearch(
getArticleList,
{
defaultParams: { type: ArticleTypeEnum.POST },
defaultCurrent: 1,
defaultSize: 5,
})
},
)
const tabsRef = inject(TABS_REF_KEY)
const handleBackTop = () => {
window.scrollTo({
top: 0,
behavior: 'smooth',
})
}
const { ScrollTopComp, handleBackTop } = useScrollTop(tabsRef!)
</script>
<!-- <style scoped>
/* 自定义分页器样式 */
......
......@@ -344,21 +344,7 @@
<div class="flex items-center justify-between">
<!-- 左侧:回到顶部按钮 -->
<div class="left">
<button
class="back-top-btn group flex items-center gap-3 px-4 py-2.5 bg-gradient-to-r from-blue-50 to-indigo-50 hover:from-blue-100 hover:to-indigo-100 border border-blue-200/50 rounded-full transition-all duration-300 hover:shadow-lg hover:-translate-y-1 active:scale-95"
title="回到顶部"
@click="handleBackTop"
>
<div
class="w-8 h-8 bg-gradient-to-r from-blue-500 to-indigo-500 rounded-full flex items-center justify-center group-hover:rotate-12 transition-transform duration-300 shadow-sm"
>
<SvgIcon color="#409eff" name="icon_top" size="16" />
</div>
<span
class="text-sm font-medium text-gray-700 group-hover:text-blue-600 transition-colors"
>回到顶部</span
>
</button>
<ScrollTopComp />
</div>
<!-- 右侧:分页器 -->
......@@ -384,6 +370,12 @@
<script setup lang="ts" name="RecommendList">
import { useRouter } from 'vue-router'
import { useScrollTop } from '@/hooks'
import { TABS_REF_KEY } from '@/constants'
const tabsRef = inject(TABS_REF_KEY)
const { ScrollTopComp, handleBackTop } = useScrollTop(tabsRef!)
const currentPage = ref(1)
const pageSize = ref(15)
......@@ -396,9 +388,6 @@ const tabs = [
]
const activeTab = ref('latest')
const handleBackTop = () => {
window.scrollTo({ top: 0, behavior: 'smooth' })
}
const goVideoDetail = (n: number) => {
router.push(`/videoDetail?id=${n}`)
......
......@@ -37,12 +37,14 @@ const activeTab = ref('推荐')
color: #000 !important;
box-shadow: none !important; /* 如果你还要禁掉 hover 阴影 */
}
.fade-enter-active,
.fade-leave-active {
transition: opacity 0.3s ease;
}
.fade-enter-from,
.fade-leave-to {
opacity: 0;
transform: translateX(30px);
filter: blur(4px);
}
.fade-enter-active,
.fade-leave-active {
transition: all 0.4s cubic-bezier(0.25, 0.46, 0.45, 0.94);
}
</style>
<template>
<div
class="fixed right-50 top-70% -translate-y-50% flex flex-col items-center content-center gap-5"
>
<div class="flex flex-col bg-white rounded-8px shadow-md overflow-hidden">
<div
v-for="item in stats"
:key="item.label"
:content="item.label"
placement="left"
:show-after="300"
>
<div
class="flex flex-col items-center justify-center w-56px h-56px transition-all duration-200 group"
@click="handleClick(item)"
>
<el-icon
class="group-hover:text-blue-500! cursor-pointer"
size="20"
:style="{ color: item.active ? '#409eff' : '#606266' }"
>
<component :is="item.icon" />
</el-icon>
<span
class="text-12px cursor-pointer group-hover:text-blue-500!"
:style="{ color: item.active ? '#409eff' : '#606266' }"
>
{{ item.count }}
</span>
</div>
</div>
</div>
<ScrollTopComp class="flex flex-col" />
</div>
</template>
<script setup lang="ts">
import { Star, ChatLineSquare, Pointer } from '@element-plus/icons-vue'
import type { ArticleItemDto } from '@/api'
import type { Component } from 'vue'
import { useScrollTop } from '@/hooks'
import { addOrCancelCollect } from '@/api'
import { COMMENT_REF_KEY } from '@/constants'
const { articleDetail } = defineProps<{
articleDetail: ArticleItemDto
}>()
const commentRef = inject(COMMENT_REF_KEY)
const { ScrollTopComp } = useScrollTop(window)
const { handleBackTop } = useScrollTop(commentRef!)
interface StatItem {
icon: Component
count: number
label: string
active?: boolean
actionFn?: () => Promise<boolean>
}
const stats = computed(() => {
return [
{
icon: Pointer,
count: articleDetail?.praiseCount,
label: '点赞',
active: articleDetail?.viewCount,
},
{
icon: Star,
count: articleDetail?.collectionCount,
label: '收藏',
active: articleDetail?.isRecommend,
api: addOrCancelCollect,
async actionFn(item: StatItem) {
const res = await addOrCancelCollect({
articleId: articleDetail.id,
type: articleDetail.type,
})
if (res) {
item.active = !item.active
}
},
},
{
icon: ChatLineSquare,
count: articleDetail?.replyCount,
label: '评论',
active: articleDetail?.isRecommend,
actionFn: handleBackTop,
},
]
})
const handleClick = async (item: StatItem) => {
if (item.actionFn) {
await item.actionFn()
}
}
</script>
<template>
<div class="min-h-screen bg-gradient-to-br from-blue-50 via-purple-50 to-cyan-50">
<!-- <div class="max-w-6xl mx-auto px-4 py-6 grid grid-cols-1 lg:grid-cols-4 gap-6"> -->
<div class="min-h-screen px-20">
<!-- 主内容区 -->
<ActionButtons :articleDetail="articleDetail"></ActionButtons>
<div class="lg:col-span-3">
<!-- 帖子主体 -->
<div
......@@ -32,8 +32,8 @@
</span>
</div>
<p class="text-sm text-gray-500 mt-1">
{{ dayjs(articleDetail?.createTime || 0 * 1000).format('YYYY-MM-DD HH:mm:ss') }} ·
{{ articleDetail?.viewCount || 0 }} 阅读
{{ dayjs((articleDetail?.createTime || 0) * 1000).format('YYYY-MM-DD HH:mm:ss') }}
· {{ articleDetail?.viewCount || 0 }} 阅读
</p>
</div>
<button
......@@ -95,38 +95,11 @@
</div>
</div>
</div>
<!-- 互动按钮 -->
<div class="px-6 py-4 border-t border-gray-100 bg-gray-50/50">
<div class="flex items-center justify-between">
<div class="flex items-center gap-6">
<button
class="flex items-center gap-2 px-4 py-2 rounded-full hover:bg-gray-100 text-gray-600 transition-all"
>
<i class="i-carbon-favorite"></i>
<span>128</span>
</button>
<button
class="flex items-center gap-2 px-4 py-2 rounded-full hover:bg-gray-100 text-gray-600 transition-all"
>
<i class="i-carbon-bookmark"></i>
<span>56</span>
</button>
<button
class="flex items-center gap-2 px-4 py-2 rounded-full hover:bg-gray-100 text-gray-600 transition-all"
>
<i class="i-carbon-share"></i>
<span>分享</span>
</button>
</div>
</div>
</div>
</div>
<!-- 评论区 -->
<div
ref="commentRef"
class="mt-6 bg-white/90 backdrop-blur-sm rounded-2xl shadow-sm border border-white/50 overflow-hidden"
>
<!-- 评论筛选 -->
......@@ -137,22 +110,22 @@
>
<div class="flex items-center gap-2">
<button
class="px-3 py-1.5 text-sm bg-gradient-to-r from-blue-500 to-purple-500 text-white rounded-full shadow-md"
class="cursor-pointer px-3 py-1.5 text-sm bg-gradient-to-r from-blue-500 to-purple-500 text-white rounded-full shadow-md"
>
最热
</button>
<button
class="px-3 py-1.5 text-sm hover:bg-gray-100 text-gray-600 rounded-full transition-all"
class="cursor-pointer px-3 py-1.5 text-sm hover:bg-gray-100 text-gray-600 rounded-full transition-all"
>
最新
</button>
<button
class="px-3 py-1.5 text-sm hover:bg-gray-100 text-gray-600 rounded-full transition-all"
class="cursor-pointer px-3 py-1.5 text-sm hover:bg-gray-100 text-gray-600 rounded-full transition-all"
>
置顶
</button>
<button
class="px-3 py-1.5 text-sm hover:bg-gray-100 text-gray-600 rounded-full transition-all"
class="cursor-pointer px-3 py-1.5 text-sm hover:bg-gray-100 text-gray-600 rounded-full transition-all"
>
精选
</button>
......@@ -163,17 +136,14 @@
<!-- 发表评论 -->
<div class="p-4 border-b border-gray-100">
<div class="flex gap-3">
<img
src="https://images.unsplash.com/photo-1472099645785-5658abf4ff4e?w=100&h=100&fit=crop&crop=face"
alt=""
class="w-10 h-10 rounded-full object-cover"
/>
<img :src="userInfo?.avatar" alt="" class="w-10 h-10 rounded-full object-cover" />
<div class="flex-1">
<textarea
<el-input
v-model="comment"
type="textarea"
placeholder="写下你的评论..."
class="w-full p-3 border border-gray-200 rounded-xl resize-none focus:outline-none focus:ring-2 focus:ring-blue-500/20 focus:border-blue-500"
rows="3"
></textarea>
:rows="3"
></el-input>
<div class="flex justify-between items-center mt-3">
<div class="flex items-center gap-2 text-sm text-gray-500">
<button class="hover:text-blue-500 transition-colors">
......@@ -184,7 +154,9 @@
</button>
</div>
<button
class="px-6 py-2 bg-gradient-to-r from-blue-500 to-purple-500 text-white rounded-full text-sm hover:shadow-lg transition-all"
class="cursor-pointer disabled:opacity-50 px-6 py-2 bg-gradient-to-r from-blue-500 to-purple-500 text-white rounded-full text-sm hover:shadow-lg transition-all"
:disabled="!comment.trim()"
@click="handleComment"
>
发表
</button>
......@@ -224,7 +196,12 @@
<i class="i-carbon-favorite"></i>
<span>24</span>
</button>
<button class="hover:text-blue-500 transition-colors">回复</button>
<button
class="cursor-pointer hover:text-blue-500 transition-colors"
@click="handleReply(1)"
>
回复
</button>
</div>
</div>
......@@ -244,12 +221,62 @@
<p class="text-sm text-gray-700">
谢谢认可!确实这个组合在开发效率上提升很大。
</p>
<div class="flex items-center justify-between">
<div class="flex items-center gap-4 text-sm text-gray-500">
<span>1小时前</span>
<button
class="flex items-center gap-1 hover:text-red-500 transition-colors"
>
<i class="i-carbon-favorite"></i>
<span>24</span>
</button>
<button
@click="handleReply(1)"
class="cursor-pointer hover:text-blue-500 transition-colors"
>
回复
</button>
</div>
</div>
</div>
</div>
<div v-show="currentId === 1" class="flex gap-3">
<img
:src="userInfo?.avatar"
alt=""
class="w-10 h-10 rounded-full object-cover"
/>
<div class="flex-1">
<el-input
v-model="comment"
type="textarea"
placeholder="写下你的评论..."
:rows="3"
></el-input>
<div class="flex justify-between items-center mt-3">
<div class="flex items-center gap-2 text-sm text-gray-500">
<button class="hover:text-blue-500 transition-colors">
<i class="i-carbon-face-satisfied"></i>
</button>
<button class="hover:text-blue-500 transition-colors">
<i class="i-carbon-image"></i>
</button>
</div>
<button
class="cursor-pointer disabled:opacity-50 px-6 py-2 bg-gradient-to-r from-blue-500 to-purple-500 text-white rounded-full text-sm hover:shadow-lg transition-all"
:disabled="!comment.trim()"
@click="handleComment"
>
发表
</button>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<el-divider />
<!-- 评论2 -->
<div class="p-4 hover:bg-gray-50/50 transition-colors">
......@@ -278,30 +305,327 @@
</button>
</div>
</div>
<!-- 回复列表 -->
<div class="mt-3 ml-4 space-y-3">
<div class="flex gap-2 p-3 bg-gray-50 rounded-lg">
<img
src="https://images.unsplash.com/photo-1507003211169-0a1dd7228f2d?w=100&h=100&fit=crop&crop=face"
alt=""
class="w-8 h-8 rounded-full object-cover"
/>
<div class="flex-1">
<div class="flex items-center gap-2 mb-1">
<span class="font-medium text-sm text-gray-800">前端小李</span>
<span class="text-xs text-gray-500">30分钟前</span>
</div>
<p class="text-sm text-gray-700">
谢谢认可!确实这个组合在开发效率上提升很大。
</p>
<div class="flex items-center justify-between">
<div class="flex items-center gap-4 text-sm text-gray-500">
<span>1小时前</span>
<button
class="flex items-center gap-1 hover:text-red-500 transition-colors"
>
<i class="i-carbon-favorite"></i>
<span>24</span>
</button>
<button
@click="handleReply(1)"
class="cursor-pointer hover:text-blue-500 transition-colors"
>
回复
</button>
</div>
</div>
</div>
</div>
<div v-show="currentId === 1" class="flex gap-3">
<img
:src="userInfo?.avatar"
alt=""
class="w-10 h-10 rounded-full object-cover"
/>
<div class="flex-1">
<el-input
v-model="comment"
type="textarea"
placeholder="写下你的评论..."
:rows="3"
></el-input>
<div class="flex justify-between items-center mt-3">
<div class="flex items-center gap-2 text-sm text-gray-500">
<button class="hover:text-blue-500 transition-colors">
<i class="i-carbon-face-satisfied"></i>
</button>
<button class="hover:text-blue-500 transition-colors">
<i class="i-carbon-image"></i>
</button>
</div>
<button
class="cursor-pointer disabled:opacity-50 px-6 py-2 bg-gradient-to-r from-blue-500 to-purple-500 text-white rounded-full text-sm hover:shadow-lg transition-all"
:disabled="!comment.trim()"
@click="handleComment"
>
发表
</button>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="p-4 hover:bg-gray-50/50 transition-colors">
<div class="flex gap-3">
<img
src="https://images.unsplash.com/photo-1494790108755-2616b612b786?w=100&h=100&fit=crop&crop=face"
alt=""
class="w-10 h-10 rounded-full object-cover"
/>
<div class="flex-1">
<div class="flex items-center gap-2 mb-2">
<span class="font-semibold text-gray-800">Vue开发者</span>
<span class="px-2 py-0.5 text-xs bg-orange-100 text-orange-600 rounded-full"
>热门</span
>
</div>
<p class="text-gray-700 mb-3">
能分享一下具体的项目配置吗?我在集成 UnoCSS 的时候遇到了一些问题。
</p>
<div class="flex items-center justify-between">
<div class="flex items-center gap-4 text-sm text-gray-500">
<span>2小时前</span>
<button class="flex items-center gap-1 hover:text-red-500 transition-colors">
<i class="i-carbon-favorite"></i>
<span>18</span>
</button>
<button
class="cursor-pointer hover:text-blue-500 transition-colors"
@click="handleReply(2)"
>
回复
</button>
</div>
</div>
</div>
</div>
</div>
<div class="p-4 hover:bg-gray-50/50 transition-colors">
<div class="flex gap-3">
<img
src="https://images.unsplash.com/photo-1494790108755-2616b612b786?w=100&h=100&fit=crop&crop=face"
alt=""
class="w-10 h-10 rounded-full object-cover"
/>
<div class="flex-1">
<div class="flex items-center gap-2 mb-2">
<span class="font-semibold text-gray-800">Vue开发者</span>
<span class="px-2 py-0.5 text-xs bg-orange-100 text-orange-600 rounded-full"
>热门</span
>
</div>
<p class="text-gray-700 mb-3">
能分享一下具体的项目配置吗?我在集成 UnoCSS 的时候遇到了一些问题。
</p>
<div class="flex items-center justify-between">
<div class="flex items-center gap-4 text-sm text-gray-500">
<span>2小时前</span>
<button class="flex items-center gap-1 hover:text-red-500 transition-colors">
<i class="i-carbon-favorite"></i>
<span>18</span>
</button>
</div>
</div>
</div>
</div>
</div>
<div class="p-4 hover:bg-gray-50/50 transition-colors">
<div class="flex gap-3">
<img
src="https://images.unsplash.com/photo-1494790108755-2616b612b786?w=100&h=100&fit=crop&crop=face"
alt=""
class="w-10 h-10 rounded-full object-cover"
/>
<div class="flex-1">
<div class="flex items-center gap-2 mb-2">
<span class="font-semibold text-gray-800">Vue开发者</span>
<span class="px-2 py-0.5 text-xs bg-orange-100 text-orange-600 rounded-full"
>热门</span
>
</div>
<p class="text-gray-700 mb-3">
能分享一下具体的项目配置吗?我在集成 UnoCSS 的时候遇到了一些问题。
</p>
<div class="flex items-center justify-between">
<div class="flex items-center gap-4 text-sm text-gray-500">
<span>2小时前</span>
<button class="flex items-center gap-1 hover:text-red-500 transition-colors">
<i class="i-carbon-favorite"></i>
<span>18</span>
</button>
</div>
</div>
</div>
</div>
</div>
<div class="p-4 hover:bg-gray-50/50 transition-colors">
<div class="flex gap-3">
<img
src="https://images.unsplash.com/photo-1494790108755-2616b612b786?w=100&h=100&fit=crop&crop=face"
alt=""
class="w-10 h-10 rounded-full object-cover"
/>
<div class="flex-1">
<div class="flex items-center gap-2 mb-2">
<span class="font-semibold text-gray-800">Vue开发者</span>
<span class="px-2 py-0.5 text-xs bg-orange-100 text-orange-600 rounded-full"
>热门</span
>
</div>
<p class="text-gray-700 mb-3">
能分享一下具体的项目配置吗?我在集成 UnoCSS 的时候遇到了一些问题。
</p>
<div class="flex items-center justify-between">
<div class="flex items-center gap-4 text-sm text-gray-500">
<span>2小时前</span>
<button class="flex items-center gap-1 hover:text-red-500 transition-colors">
<i class="i-carbon-favorite"></i>
<span>18</span>
</button>
</div>
</div>
</div>
</div>
</div>
<div class="p-4 hover:bg-gray-50/50 transition-colors">
<div class="flex gap-3">
<img
src="https://images.unsplash.com/photo-1494790108755-2616b612b786?w=100&h=100&fit=crop&crop=face"
alt=""
class="w-10 h-10 rounded-full object-cover"
/>
<div class="flex-1">
<div class="flex items-center gap-2 mb-2">
<span class="font-semibold text-gray-800">Vue开发者</span>
<span class="px-2 py-0.5 text-xs bg-orange-100 text-orange-600 rounded-full"
>热门</span
>
</div>
<p class="text-gray-700 mb-3">
能分享一下具体的项目配置吗?我在集成 UnoCSS 的时候遇到了一些问题。
</p>
<div class="flex items-center justify-between">
<div class="flex items-center gap-4 text-sm text-gray-500">
<span>2小时前</span>
<button class="flex items-center gap-1 hover:text-red-500 transition-colors">
<i class="i-carbon-favorite"></i>
<span>18</span>
</button>
</div>
</div>
</div>
</div>
</div>
<div class="p-4 hover:bg-gray-50/50 transition-colors">
<div class="flex gap-3">
<img
src="https://images.unsplash.com/photo-1494790108755-2616b612b786?w=100&h=100&fit=crop&crop=face"
alt=""
class="w-10 h-10 rounded-full object-cover"
/>
<div class="flex-1">
<div class="flex items-center gap-2 mb-2">
<span class="font-semibold text-gray-800">Vue开发者</span>
<span class="px-2 py-0.5 text-xs bg-orange-100 text-orange-600 rounded-full"
>热门</span
>
</div>
<p class="text-gray-700 mb-3">
能分享一下具体的项目配置吗?我在集成 UnoCSS 的时候遇到了一些问题。
</p>
<div class="flex items-center justify-between">
<div class="flex items-center gap-4 text-sm text-gray-500">
<span>2小时前</span>
<button class="flex items-center gap-1 hover:text-red-500 transition-colors">
<i class="i-carbon-favorite"></i>
<span>18</span>
</button>
</div>
</div>
</div>
</div>
</div>
<div class="p-4 hover:bg-gray-50/50 transition-colors">
<div class="flex gap-3">
<img
src="https://images.unsplash.com/photo-1494790108755-2616b612b786?w=100&h=100&fit=crop&crop=face"
alt=""
class="w-10 h-10 rounded-full object-cover"
/>
<div class="flex-1">
<div class="flex items-center gap-2 mb-2">
<span class="font-semibold text-gray-800">Vue开发者</span>
<span class="px-2 py-0.5 text-xs bg-orange-100 text-orange-600 rounded-full"
>热门</span
>
</div>
<p class="text-gray-700 mb-3">
能分享一下具体的项目配置吗?我在集成 UnoCSS 的时候遇到了一些问题。
</p>
<div class="flex items-center justify-between">
<div class="flex items-center gap-4 text-sm text-gray-500">
<span>2小时前</span>
<button class="flex items-center gap-1 hover:text-red-500 transition-colors">
<i class="i-carbon-favorite"></i>
<span>18</span>
</button>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- </div> -->
</div>
</template>
<script lang="ts" setup>
import dayjs from 'dayjs'
import { getArticleDetail, type ArticleItemDto } from '@/api'
import ActionButtons from './components/actionButtons.vue'
import { useUserStore } from '@/stores'
import { storeToRefs } from 'pinia'
import { COMMENT_REF_KEY } from '@/constants'
const userStore = useUserStore()
const commentRef = useTemplateRef<HTMLElement | null>('commentRef')
const { userInfo } = storeToRefs(userStore)
const route = useRoute()
const id = route.params.articleId as string
const articleDetail = ref<ArticleItemDto>()
const articleDetail = ref({} as ArticleItemDto)
const comment = ref('')
const currentId = ref(-1)
const fetchArticleDetail = async () => {
const { data } = await getArticleDetail(id)
articleDetail.value = data
}
const handleReply = (id: number) => {
comment.value = ''
currentId.value = id
}
const handleComment = async () => {
console.log(comment.value)
}
provide(COMMENT_REF_KEY, commentRef)
onMounted(async () => {
fetchArticleDetail()
})
......
......@@ -3,8 +3,9 @@
v-model="dialogVisible"
title="个人信息"
width="500px"
:before-close="handleClose"
:close-on-click-modal="false"
class="user-info-dialog"
:closed="close"
>
<el-form ref="formRef" :model="form" :rules="rules" label-width="60px" class="px-4">
<!-- 昵称 -->
......@@ -75,11 +76,6 @@ const close = () => {
resetForm()
}
// 关闭弹窗前的处理
const handleClose = (done: () => void) => {
done()
}
// 取消操作
const handleCancel = () => {
close()
......
<template>
<div class="w-full h-full bg-white rounded-lg overflow-hidden">
<!-- Custom Tabs (保持原样式) -->
<div class="flex border-b border-gray-200">
<div
v-for="tab in tabs"
:key="tab.key"
class="px-6 py-4 cursor-pointer text-gray-600 hover:text-blue-500 transition-colors relative"
:class="[
searchParams.type === tab.key
? 'text-blue-500! font-medium'
: 'text-gray-600 hover:text-blue-400',
]"
@click="toggleTab(tab.key)"
>
{{ tab.label }}
</div>
</div>
<!-- Content Area -->
<div class="flex-1 flex flex-col">
<!-- List Container -->
<div class="flex-1 p-6">
<!-- Loading State -->
<div v-if="loading" class="flex items-center justify-center h-64">
<el-icon class="is-loading mr-2 text-gray-500"><Loading /></el-icon>
<span class="text-gray-500">加载中...</span>
</div>
<!-- Empty State -->
<div v-else-if="!list.length" class="flex flex-col items-center justify-center h-64">
<div class="w-16 h-16 bg-gray-100 rounded-full flex items-center justify-center mb-4">
<el-icon class="text-2xl text-gray-300"><Document /></el-icon>
</div>
<div class="text-gray-500 text-lg mb-2">暂无内容</div>
<div class="text-gray-400 text-sm">{{ getEmptyText() }}</div>
</div>
<!-- List Items -->
<div v-else class="space-y-4">
<div
v-for="item in paginatedList"
:key="item.id"
class="flex items-center p-4 bg-gray-50 rounded-lg hover:bg-gray-100 transition-colors cursor-pointer"
>
<!-- Avatar -->
<el-avatar class="mr-4 flex-shrink-0" :size="48" style="background-color: #e5f3ff">
<el-icon class="text-blue-500"><User /></el-icon>
</el-avatar>
<!-- Content -->
<div class="flex-1 min-w-0">
<div class="text-gray-900 font-medium truncate">{{ item.title }}</div>
<div class="text-gray-500 text-sm mt-1 truncate">{{ item.description }}</div>
</div>
<!-- Meta Info -->
<div class="flex items-center text-gray-400 text-sm ml-4">
<el-icon class="mr-1"><Clock /></el-icon>
<span>{{ item.time }}</span>
</div>
</div>
</div>
</div>
<!-- Custom Pagination (保持原样式) -->
<div
v-if="list.length"
class="flex items-center justify-between px-6 py-4 border-t border-gray-200"
>
<div class="right">
<div class="pagination-wrapper bg-white rounded-xl shadow-sm border border-gray-100 p-3">
<el-pagination
v-model:current-page="searchParams.current"
v-model:page-size="searchParams.size"
:page-sizes="[10, 20, 30, 40]"
layout="prev, pager, next, jumper, total"
:total="total"
class="custom-pagination"
/>
</div>
</div>
</div>
</div>
</div>
</template>
<script lang="ts" setup>
import { Loading, User, Clock, Document } from '@element-plus/icons-vue'
import { getSelfCollectList } from '@/api'
import { usePageSearch } from '@/hooks'
import { ArticleTypeEnum } from '@/constants'
interface TabItem {
key: string
label: string
}
const toggleTab = (key: string) => {
searchParams.value.type = key
refresh()
}
const tabs: TabItem[] = [
{ key: 'posts', label: '帖子' },
{ key: 'videos', label: '视频' },
{ key: 'questions', label: '问题' },
{ key: 'articles', label: '专栏' },
{ key: 'practice', label: '实践' },
{ key: 'interviews', label: '专访' },
]
// State
const { list, loading, searchParams, total, refresh } = usePageSearch(getSelfCollectList, {
defaultParams: {
type: ArticleTypeEnum.POST,
},
})
// Computed
const paginatedList = computed(() => {
const start = (searchParams.value.current - 1) * searchParams.value.size
const end = start + searchParams.value.size
return list.value.slice(start, end)
})
const getEmptyText = () => {
const emptyTexts: Record<string, string> = {
posts: '还没有发布任何帖子',
videos: '还没有上传任何视频',
questions: '还没有提出任何问题',
articles: '还没有发表任何专栏文章',
practice: '还没有分享任何实践经验',
interviews: '还没有参与任何专访',
}
return emptyTexts[searchParams.type] || '暂无数据'
}
</script>
<template>
<div class="w-full h-full bg-white rounded-lg overflow-hidden">
<!-- Custom Tabs (保持原样式) -->
<div class="flex border-b border-gray-200">
<div
v-for="tab in tabs"
:key="tab.key"
class="px-6 py-4 cursor-pointer text-gray-600 hover:text-blue-500 transition-colors relative"
:class="[
searchParams.type === tab.key
? 'text-blue-500! font-medium'
: 'text-gray-600 hover:text-blue-400',
]"
@click="toggleTab(tab.key)"
>
{{ tab.label }}
</div>
</div>
<!-- Content Area -->
<div class="flex-1 flex flex-col">
<!-- List Container -->
<div class="flex-1 p-6">
<!-- Loading State -->
<div v-if="loading" class="flex items-center justify-center h-64">
<el-icon class="is-loading mr-2 text-gray-500"><Loading /></el-icon>
<span class="text-gray-500">加载中...</span>
</div>
<!-- Empty State -->
<div v-else-if="!list.length" class="flex flex-col items-center justify-center h-64">
<div class="w-16 h-16 bg-gray-100 rounded-full flex items-center justify-center mb-4">
<el-icon class="text-2xl text-gray-300"><Document /></el-icon>
</div>
<div class="text-gray-500 text-lg mb-2">暂无内容</div>
<div class="text-gray-400 text-sm">{{ getEmptyText() }}</div>
</div>
<!-- List Items -->
<div v-else class="space-y-4">
<div
v-for="item in paginatedList"
:key="item.id"
class="flex items-center p-4 bg-gray-50 rounded-lg hover:bg-gray-100 transition-colors cursor-pointer"
>
<!-- Avatar -->
<el-avatar class="mr-4 flex-shrink-0" :size="48" style="background-color: #e5f3ff">
<el-icon class="text-blue-500"><User /></el-icon>
</el-avatar>
<!-- Content -->
<div class="flex-1 min-w-0">
<div class="text-gray-900 font-medium truncate">{{ item.title }}</div>
<div class="text-gray-500 text-sm mt-1 truncate">{{ item.description }}</div>
</div>
<!-- Meta Info -->
<div class="flex items-center text-gray-400 text-sm ml-4">
<el-icon class="mr-1"><Clock /></el-icon>
<span>{{ item.time }}</span>
</div>
</div>
</div>
</div>
<!-- Custom Pagination (保持原样式) -->
<div
v-if="list.length"
class="flex items-center justify-between px-6 py-4 border-t border-gray-200"
>
<div class="right">
<div class="pagination-wrapper bg-white rounded-xl shadow-sm border border-gray-100 p-3">
<el-pagination
v-model:current-page="searchParams.current"
v-model:page-size="searchParams.size"
:page-sizes="[10, 20, 30, 40]"
layout="prev, pager, next, jumper, total"
:total="total"
class="custom-pagination"
/>
</div>
</div>
</div>
</div>
</div>
</template>
<script lang="ts" setup>
import { Loading, User, Clock, Document } from '@element-plus/icons-vue'
import { getSelfPraiseList } from '@/api'
import { usePageSearch } from '@/hooks'
import { ArticleTypeEnum } from '@/constants'
interface TabItem {
key: string
label: string
}
const toggleTab = (key: string) => {
searchParams.value.type = key
refresh()
}
const tabs: TabItem[] = [
{ key: 'posts', label: '帖子' },
{ key: 'videos', label: '视频' },
{ key: 'questions', label: '问题' },
{ key: 'articles', label: '专栏' },
{ key: 'practice', label: '实践' },
{ key: 'interviews', label: '专访' },
]
// State
const { list, loading, searchParams, total, refresh } = usePageSearch(getSelfPraiseList, {
defaultParams: {
type: ArticleTypeEnum.POST,
},
})
// Computed
const paginatedList = computed(() => {
const start = (searchParams.value.current - 1) * searchParams.value.size
const end = start + searchParams.value.size
return list.value.slice(start, end)
})
const getEmptyText = () => {
const emptyTexts: Record<string, string> = {
posts: '还没有发布任何帖子',
videos: '还没有上传任何视频',
questions: '还没有提出任何问题',
articles: '还没有发表任何专栏文章',
practice: '还没有分享任何实践经验',
interviews: '还没有参与任何专访',
}
return emptyTexts[searchParams.type] || '暂无数据'
}
</script>
......@@ -6,7 +6,6 @@
<div class="absolute top-4 right-4 flex gap-2">
<el-button type="info" plain size="small">清除缓存</el-button>
<el-button type="info" plain size="small">切换账号</el-button>
<el-button type="primary" size="small">编辑个人资料</el-button>
</div>
</div>
......@@ -58,8 +57,13 @@
<!-- 右侧内容区域 -->
<div class="flex-1">
<div class="bg-white rounded-lg shadow-sm min-h-500px">
<!-- 标签页 -->
<div class="border-b border-gray-200">
<transition name="fade" mode="out-in">
<keep-alive>
<component :is="currentComponent" />
</keep-alive>
</transition>
<!-- <div class="border-b border-gray-200">
<div class="flex">
<div
v-for="tab in tabs"
......@@ -79,9 +83,7 @@
</div>
</div>
<!-- 内容区域 -->
<div class="p-8">
<!-- 空状态 -->
<div class="flex flex-col items-center justify-center py-20">
<div class="w-120px h-120px mb-6 opacity-30">
<svg viewBox="0 0 1024 1024" class="w-full h-full fill-gray-400">
......@@ -97,7 +99,6 @@
<p class="text-gray-400 text-sm">暂无帖子</p>
</div>
<!-- 分页 -->
<div class="flex justify-center mt-8">
<el-pagination
v-model:current-page="currentPage"
......@@ -107,7 +108,7 @@
:hide-on-single-page="true"
/>
</div>
</div>
</div> -->
</div>
</div>
</div>
......@@ -132,6 +133,9 @@ import {
import { useUserStore } from '@/stores/user'
import { storeToRefs } from 'pinia'
import EditUserInfo from './components/editUserInfo.vue'
import SelfCollect from './components/selfCollect.vue'
import SelfPraise from './components/selfPraise.vue'
import { hasOfficialAccount } from '@/api'
const editUserInfoRef = useTemplateRef<InstanceType<typeof EditUserInfo>>('editUserInfoRef')
const userStore = useUserStore()
......@@ -147,19 +151,22 @@ const activeTab = ref('published')
const currentPage = ref(1)
// 左侧菜单项
const menuItems = ref([
const menuItems = [
{ key: 'posts', label: '我的帖子', icon: User },
{ key: 'activity', label: '我的活动', icon: Calendar },
{ key: 'votes', label: '我的投票', icon: Trophy },
{ key: 'articles', label: '我的文章', icon: Document },
{ key: 'favorites', label: '我的收藏', icon: Star },
{ key: 'favorites', label: '我的收藏', icon: Star, component: SelfCollect },
{ key: 'praise', label: '我的点赞', icon: Star, component: SelfPraise },
{ key: 'comments', label: '评论回复', icon: ChatDotRound },
{ key: 'follows', label: '获赞列表', icon: Trophy },
{ key: 'social', label: '关注和粉', icon: Link },
{ key: 'feedback', label: '反馈列表', icon: View },
{ key: 'screen', label: '屏蔽管理', icon: Setting },
])
]
const currentComponent = computed(() => {
return menuItems.find((item) => item.key === activeMenu.value)?.component
})
// 标签页
const tabs = ref([
{ key: 'published', label: '发布' },
......@@ -174,6 +181,17 @@ const handleEdit = () => {
signature: '',
})
}
const getIsOfficial = () => {
setTimeout(async () => {
const { data } = await hasOfficialAccount()
console.log(data)
}, 1000)
}
onMounted(() => {
getIsOfficial()
})
</script>
<style scoped>
......@@ -181,4 +199,12 @@ const handleEdit = () => {
background-image: url('http://soundasia.oss-cn-shenzhen.aliyuncs.com/OA/2022/10/13/1665643787806.jpg');
}
/* 如果需要额外的样式可以在这里添加 */
.fade-enter-active,
.fade-leave-active {
transition: opacity 0.5s ease;
}
.fade-enter-from,
.fade-leave-to {
opacity: 0;
}
</style>
......@@ -20,21 +20,7 @@
<div class="flex items-center justify-between">
<!-- 左侧:回到顶部按钮 -->
<div class="left">
<button
class="back-top-btn group flex items-center gap-3 px-4 py-2.5 bg-gradient-to-r from-blue-50 to-indigo-50 hover:from-blue-100 hover:to-indigo-100 border border-blue-200/50 rounded-full transition-all duration-300 hover:shadow-lg hover:-translate-y-1 active:scale-95"
title="回到顶部"
@click="handleBackTop"
>
<div
class="w-8 h-8 bg-gradient-to-r from-blue-500 to-indigo-500 rounded-full flex items-center justify-center group-hover:rotate-12 transition-transform duration-300 shadow-sm"
>
<SvgIcon color="#409eff" name="icon_top" size="16" />
</div>
<span
class="text-sm font-medium text-gray-700 group-hover:text-blue-600 transition-colors"
>回到顶部</span
>
</button>
<ScrollTopComp />
</div>
<!-- 右侧:分页器 -->
<div class="right">
......@@ -64,6 +50,12 @@ import { Refresh } from '@element-plus/icons-vue'
import ColumnList from './components/columnList.vue'
import InterviewList from './components/interviewList.vue'
import PracticeList from './components/practiceList.vue'
import { useScrollTop } from '@/hooks'
import { TABS_REF_KEY } from '@/constants'
const tabsRef = inject(TABS_REF_KEY)
const { ScrollTopComp } = useScrollTop(tabsRef!)
const currentPage = ref(1)
const pageSize = ref(10)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment