Commit daeb355b by lijiabin

【需求 17679】 feat: 继续完善优化

parent dedb7b4a
......@@ -239,3 +239,46 @@ export const getCommentChildren = (data: CommentChildrenSearchParams) => {
data,
})
}
/**
* 个人中心——问吧回答问题列表
*/
export const answerQuestionPage = (data: PageSearchParams) => {
return service.request<BackendServicePageResult<ArticleItemDto>>({
url: `/api/personalCenter/addQuestionPage`,
method: 'POST',
data,
})
}
/**
* 问吧列表 添加问题到回答问题列表
*/
export const addOrCancelToAnswerList = (data: { articleId: number }) => {
return service.request<boolean>({
url: `/api/cultureQuestionAdd/addOrCancel`,
method: 'POST',
data,
})
}
/**
* 问吧 用户添加问题的数量
*/
export const getUserQestionNum = () => {
return service.request<number>({
url: `/api/cultureQuestionAdd/addQuestionNum`,
method: 'POST',
})
}
/**
* 举报帖子
*/
export const addComplaint = (data: { articleId: number; reason: string }) => {
return service.request<boolean>({
url: `/cultureReport/addReport`,
method: 'POST',
data,
})
}
......@@ -148,6 +148,7 @@ export interface ArticleItemDto {
relateColumn?: string
rewardNum: number
isExpand: boolean
hasAddQuestion: boolean
}
/**
......
......@@ -17,6 +17,9 @@ import type {
SelfDraftSearchParams,
SelfTaskSearchParams,
SelfTaskItemDto,
ComplaintListItemDto,
AuditComplaintDto,
ComplaintListSearchParams,
} from './types'
import type { BackendServicePageResult, PageSearchParams } from '@/utils/request/types'
......@@ -140,3 +143,25 @@ export const auditArticle = (data: AuditArticleDto) => {
data,
})
}
/**
* 举报列表
*/
export const getComplaintList = (data: ComplaintListSearchParams) => {
return service.request<BackendServicePageResult<ComplaintListItemDto>>({
url: '/cultureReport/getReportList',
method: 'POST',
data,
})
}
/**
* 审核举报
*/
export const auditComplaint = (data: AuditComplaintDto) => {
return service.request({
url: '/cultureReport/auditReport',
method: 'POST',
data,
})
}
......@@ -222,6 +222,7 @@ export interface SelfCaseItemDto {
*/
export interface SelfCommentSearchParams extends PageSearchParams {
messageType: CommentTypeEnum
type: ArticleTypeEnum
}
/**
......@@ -278,3 +279,40 @@ export interface SelfTaskItemDto {
title: string
totalTasks: number
}
/**
* 举报列表item
*/
export interface ComplaintListItemDto {
articleId: number
auditId: number
auditName: string
auditTime: number
createId: number
createName: string
createTime: number
id: number
isDelete: number
reason: string
showAvatar: string
showName: string
status: number
title: string
type: string
}
/**
* 举报列表搜索参数
*/
export interface ComplaintListSearchParams extends PageSearchParams {
status: AuditStatusEnum
}
/**
* 审核举报
*/
export interface AuditComplaintDto {
id: number
status: AuditStatusEnum
remark?: string
}
<template>
<el-dropdown @command="handleMore" trigger="click">
<!-- <el-button class="p-2 rounded-md cursor-pointer border-none!"> -->
<el-icon class="cursor-pointer"><More /></el-icon>
<!-- </el-button> -->
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item command="举报">举报</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</template>
<script setup lang="ts">
import { addComplaint } from '@/api'
import type { ArticleItemDto } from '@/api/article/types'
// defineOptions({
// inheritAttrs: false,
// })
const { articleDetail } = defineProps<{
articleDetail: ArticleItemDto
}>()
const handleMore = async (command: string) => {
if (command === '举报') {
const { value } = await ElMessageBox.prompt('请输入举报原因', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
inputPattern: /^\S+$/,
inputPlaceholder: '请输入举报原因',
inputErrorMessage: '举报原因不能为空',
})
addComplaint({ articleId: articleDetail.id, reason: value })
ElMessage.success('举报成功')
}
}
</script>
......@@ -31,10 +31,24 @@
· {{ articleDetail?.viewCount || 0 }} 阅读
</p>
</div>
<el-button v-if="articleDetail.relateColumn" type="primary" link>{{
articleDetail.relateColumn
}}</el-button>
<el-button type="primary" link>{{ articleType }}</el-button>
<!-- 优化后的右侧内容 -->
<div class="flex items-center gap-3">
<span
class="px-3 py-1.5 text-sm font-medium bg-gray-100 text-gray-700 rounded-md"
v-if="articleDetail.relateColumn"
>
{{ articleDetail.relateColumn }}
</span>
<span
class="px-3 py-1.5 text-sm font-medium bg-blue-50 text-blue-600 rounded-md hover:bg-blue-100 transition-colors"
>
{{ articleType }}
</span>
<ActionMore :articleDetail="articleDetail" />
</div>
</div>
</div>
......@@ -86,8 +100,9 @@
<script setup lang="ts">
import dayjs from 'dayjs'
import { type ArticleItemDto } from '@/api'
import type { ArticleItemDto } from '@/api'
import { articleTypeListOptions, ArticleTypeEnum } from '@/constants'
import ActionMore from '@/components/common/ActionMore/index.vue'
const { articleDetail } = defineProps<{
articleDetail: ArticleItemDto
......
......@@ -17,8 +17,8 @@
<el-icon><Plus /></el-icon>
</el-upload>
<el-dialog v-model="dialogVisible">
<img w-full :src="dialogImageUrl" alt="Preview Image" />
<el-dialog :append-to-body="true" v-model="dialogVisible">
<img class="w-full" :src="dialogImageUrl" alt="Preview Image" />
</el-dialog>
</template>
......
......@@ -24,9 +24,9 @@ export const app_config: { [key: string]: IConfig } = {
// 开发环境
development: {
baseUrl: 'http://culture.yswg.com.cn:8089', // 线上测试机
// baseUrl: 'http://culture.yswg.com.cn:8089', // 线上测试机
// baseUrl: 'http://192.168.2.168:8089', // 立鹏本地/
// baseUrl: 'http://192.168.2.55:8089', // 首拥本地
baseUrl: 'http://192.168.2.55:8089', // 首拥本地
loginType: 1,
wxRedirect: '',
},
......
import type { InjectionKey, Ref } from 'vue'
export const TABS_REF_KEY = Symbol() as InjectionKey<Ref<HTMLElement | null>>
export const TABS_REF_KEY = Symbol('tabsRef') as InjectionKey<Ref<HTMLElement | null>>
export const COMMENT_REF_KEY = Symbol() as InjectionKey<Ref<HTMLElement | null>>
export const COMMENT_REF_KEY = Symbol('commentRef') as InjectionKey<Ref<HTMLElement | null>>
......@@ -77,12 +77,12 @@
<el-dropdown-item :command="ArticleTypeEnum.VIDEO">视频</el-dropdown-item>
<el-dropdown-item :command="ArticleTypeEnum.QUESTION">问吧</el-dropdown-item>
<el-dropdown-item
v-if="userInfo.isOfficialAccount"
v-if="userInfo.isOfficialAccount || userInfo.isAdmin"
:command="ArticleTypeEnum.COLUMN"
>专栏</el-dropdown-item
>
<el-dropdown-item
v-if="userInfo.isOfficialAccount"
v-if="userInfo.isOfficialAccount || userInfo.isAdmin"
:command="ArticleTypeEnum.INTERVIEW"
>专访</el-dropdown-item
>
......@@ -133,6 +133,7 @@ const showSearchInupt = computed(() => route.path !== '/searchPage')
// 获取二级路由的 key
const getSecondLevelKey = (route: RouteLocationNormalizedLoadedGeneric) => {
// 取第一级作为key homePage
const pathSegments = route.path.split('/').filter(Boolean)
// console.log(route)
// console.log(pathSegments)
......@@ -140,6 +141,7 @@ const getSecondLevelKey = (route: RouteLocationNormalizedLoadedGeneric) => {
const key = Object.keys(route.params).length
? pathSegments.slice(0, 2).join('/')
: pathSegments.slice(0, 1).join('/')
console.log(key, '*********************')
return key
}
......
......@@ -35,33 +35,98 @@ const routes = [
},
],
},
// 个人中心
{
path: 'userPage',
name: 'CultureUserPage',
redirect: '/userPage/selfPublish',
component: () => import('@/views/userPage/index.vue'),
children: [
// 我的帖子
{
path: 'selfPublish',
name: 'CultureSelfPublish',
component: () => import('@/views/userPage/components/selfPublish.vue'),
},
// 我的草稿
{
path: 'selfDraft',
name: 'CultureSelfDraft',
component: () => import('@/views/userPage/components/selfDraft.vue'),
},
// 我的收藏
{
path: 'selfCollect',
name: 'CultureSelfCollect',
component: () => import('@/views/userPage/components/selfCollect.vue'),
},
// 我的点赞
{
path: 'selfPraise',
name: 'CultureSelfPraise',
component: () => import('@/views/userPage/components/selfPraise.vue'),
},
// 我的案例库
{
path: 'selfCase',
name: 'CultureSelfCase',
component: () => import('@/views/userPage/components/selfCase.vue'),
},
// 我的任务
{
path: 'selfTask',
name: 'CultureSelfTask',
component: () => import('@/views/userPage/components/selfTask.vue'),
},
// 评论回复
{
path: 'selfComment',
name: 'CultureSelfComment',
component: () => import('@/views/userPage/components/selfComment.vue'),
},
// 回答问题
{
path: 'selfAnswer',
name: 'CultureSelfAnswer',
component: () => import('@/views/userPage/components/selfAnswer.vue'),
},
// 审核帖子列表
{
path: 'selfAudit',
name: 'CultureSelfAudit',
component: () => import('@/views/userPage/components/selfAudit.vue'),
},
// 审核举报列表
{
path: 'selfComplaint',
name: 'CultureSelfComplaint',
component: () => import('@/views/userPage/components/selfComplaint.vue'),
},
],
},
{
path: 'videoDetail/:id',
name: 'CultureVideoDetail',
component: () => import('@/views/videoDetail/index.vue'),
},
{
path: 'articleDetail/:id',
name: 'CultureArticleDetail',
component: () => import('@/views/articleDetail/index.vue'),
},
{
path: 'pointsStore',
name: 'CulturePointsStore',
component: () => import('@/views/pointsStore/index.vue'),
},
{
path: 'articleDetail/:articleId',
name: 'CultureArticleDetail',
component: () => import('@/views/articleDetail/index.vue'),
},
// 发布视频
{
path: 'publishVideo',
name: 'CulturePublishVideo',
component: () => import('@/views/publishVideo/index.vue'),
},
// 个人中心
{
path: 'userPage',
name: 'CultureUserPage',
component: () => import('@/views/userPage/index.vue'),
},
// 去投稿
{
path: 'publishCase',
......
......@@ -38,25 +38,20 @@ export function clearScrollPosition(path?: string): void {
export const scrollBehavior: RouterScrollBehavior = (to, from, savedPosition) => {
return new Promise((resolve) => {
console.log('触发路由滚动')
// setTimeout(() => {
// console.log(document.querySelector('#tabsRef'))
// }, 1000)
// 1. 如果有浏览器保存的位置(前进/后退),优先使用
if (savedPosition) {
resolve(savedPosition)
return
}
// 2. 如果有锚点,滚动到锚点
// 2. 如果有锚点, 约定 默认不滚动 然后在具体的组件里面onActivated里面 或者watch处理滚动逻辑 以及漫游逻辑等
if (to.hash) {
console.log(to.hash)
setTimeout(() => {
resolve({
el: to.hash.split('?')[0], //去除?后面的查询字符串
behavior: 'smooth', // 平滑滚动
top: 52,
})
}, 800)
// console.log(to.hash, window.scrollY)
// resolve({
// top: window.scrollY,
// })
return
}
......
......@@ -3,3 +3,4 @@ export * from './tags'
export * from './column'
export * from './interview'
export * from './video'
export * from './question'
import { defineStore } from 'pinia'
import { getUserQestionNum } from '@/api'
/**
* 问吧数据
*/
export const useQuestionStore = defineStore('question', () => {
const userQestionNum = ref<number>(0)
const fetchUserQestionNum = async () => {
try {
const { data } = await getUserQestionNum()
userQestionNum.value = data
} catch (error) {
console.error(error)
}
}
return { userQestionNum, fetchUserQestionNum }
})
......@@ -23,7 +23,7 @@ import ArticleContent from '@/components/common/ArticleContent/index.vue'
const commentRef = useTemplateRef<typeof Comment | null>('commentRef')
const route = useRoute()
const id = route.params.articleId as string
const id = route.params.id as string
const articleDetail = ref({} as ArticleItemDto)
......
......@@ -15,7 +15,7 @@
<el-table v-loading="loading" :data="list">
<el-table-column type="selection" width="55" />
<el-table-column prop="image" label="跳转路径" min-width="300">
<el-table-column prop="image" label="资源" min-width="300">
<template #default="{ row }">
<div class="image-cell">
<el-image
......@@ -23,11 +23,13 @@
fit="cover"
class="carousel-image"
:preview-src-list="[row.assetUrl]"
:preview-teleported="true"
/>
</div>
</template>
</el-table-column>
<el-table-column prop="url" label="跳转链接" width="100" align="center" />
<el-table-column prop="sort" label="排序" width="100" align="center" />
<el-table-column prop="type" label="类型" width="120" align="center">
......@@ -81,7 +83,7 @@
<el-form-item label="显示资源" prop="image">
<div class="upload-section">
<UploadFile v-model="form.assetUrl" :limit="1" />
<div class="upload-hint">上传图片,推荐比例为 W/H: 1280 / 480</div>
<div class="upload-hint">上传图片,推荐比例为 W/H: 4 / 1</div>
</div>
</el-form-item>
......
......@@ -149,13 +149,12 @@
</template>
<script setup lang="ts">
import { Search, Plus, Upload } from '@element-plus/icons-vue'
import { Search, Upload } from '@element-plus/icons-vue'
import { usePageSearch, useResetData } from '@/hooks'
import {
addOrUpdateColumn,
deleteColumn,
hideColumn,
getExchangeList,
getBackendExchangeList,
issueProduct,
} from '@/api/backend'
......@@ -164,7 +163,6 @@ import type { FormInstance, FormRules } from 'element-plus'
import type { BackendColumnListItemDto, AddOrUpdateColumnDto } from '@/api/backend'
import dayjs from 'dayjs'
import { ArticleTypeEnum } from '@/constants'
import { sortByOriginalOrder } from 'element-plus/es/components/cascader-panel/src/utils.mjs'
const { loading, list, total, reset, goToPage, changePageSize, refresh, searchParams, search } =
usePageSearch(getBackendExchangeList, {
......
......@@ -12,14 +12,14 @@
</div>
<!-- 主要内容区域 -->
<div class="mx-auto py-6">
<div class="mx-auto pt-6">
<PublishBox :type="ArticleTypeEnum.QUESTION" ref="publishBoxRef" />
<div v-loading="loading" v-if="list.length">
<!-- 问题列表 -->
<div class="space-y-4">
<el-card
v-for="(item, index) in list"
:key="index"
:key="item.id"
class="question-card !rounded-lg mb-4 transition-all duration-300"
shadow="hover"
>
......@@ -81,25 +81,37 @@
<!-- 操作按钮组 -->
<div class="flex items-center">
<el-button size="small" plain>
<el-icon><Plus /></el-icon>
添加
<el-button
size="small"
plain
:class="{ 'opacity-50': item.hasAddQuestion }"
@click="handleAddQuestion(item)"
>
<el-icon>
<component :is="item.hasAddQuestion ? 'CircleCheckFilled' : 'Plus'" />
</el-icon>
{{ item.hasAddQuestion ? '已添加' : '添加' }}
</el-button>
<el-button
size="small"
plain
:class="{ 'opacity-50': item.hasCollect }"
@click="handleCollect(item)"
>
<el-icon>
<component :is="item.hasCollect ? 'StarFilled' : 'Star'" />
</el-icon>
{{ item.hasCollect ? '已关注' : '关注' }}
</el-button>
<!-- 回答按钮保持不变 -->
<el-button size="small" plain @click="handleComment(index)">
<el-icon><Edit /></el-icon>
回答
</el-button>
<el-button size="small" plain>
<el-icon><Star /></el-icon>
关注
</el-button>
<el-button size="small" type="danger" plain>
<el-icon><Warning /></el-icon>
举报
</el-button>
<ActionMore class="ml-4" :articleDetail="item" />
</div>
</div>
......@@ -189,7 +201,7 @@
</div>
</template>
<script setup lang="ts" name="CultureAsk">
<script setup lang="ts">
import Tabs from '@/components/common/Tabs'
import { Star, View, ChatDotRound, Refresh } from '@element-plus/icons-vue'
import Comment from '@/components/common/Comment/index.vue'
......@@ -198,8 +210,12 @@ import { TABS_REF_KEY } from '@/constants'
import PublishBox from '@/components/common/PublishBox/index.vue'
import { ArticleTypeEnum } from '@/constants'
import dayjs from 'dayjs'
import { getArticleList, addOrCanceArticlelCollect } from '@/api'
import { getArticleList, addOrCanceArticlelCollect, addOrCancelToAnswerList } from '@/api'
import type { ArticleItemDto } from '@/api/article/types'
import { useQuestionStore } from '@/stores/question'
import ActionMore from '@/components/common/ActionMore/index.vue'
const { fetchUserQestionNum } = useQuestionStore()
const route = useRoute()
const open = ref(false)
......@@ -213,6 +229,7 @@ const tabs = [
]
const { list, total, searchParams, loading, refresh } = usePageSearch(getArticleList, {
immediate: false,
defaultParams: {
type: ArticleTypeEnum.QUESTION,
},
......@@ -226,7 +243,7 @@ const { list, total, searchParams, loading, refresh } = usePageSearch(getArticle
const tabsRef = inject(TABS_REF_KEY)
const { ScrollTopComp } = useScrollTop(tabsRef!)
const { ScrollTopComp, handleBackTop } = useScrollTop(tabsRef!)
const handleCollect = async (item: ArticleItemDto) => {
await addOrCanceArticlelCollect(item.id)
......@@ -243,6 +260,13 @@ const handleCommentSuccess = (index: number) => {
list.value[index]!.replyCount++
}
const handleAddQuestion = async (item: ArticleItemDto) => {
await addOrCancelToAnswerList({ articleId: item.id })
item.hasAddQuestion = !item.hasAddQuestion
fetchUserQestionNum()
ElMessage.success(item.hasAddQuestion ? '添加成功' : '取消添加')
}
const handleRefresh = () => {
refresh()
}
......@@ -267,17 +291,28 @@ const isOverThreeLine = (index: number) => {
const handleExpand = (item: ArticleItemDto) => {
item.isExpand = !item.isExpand
}
// 监听路由变化 如果包含#tabsRef 则打开漫游
setInterval(() => {
console.log(contentRefList.value)
}, 3000)
// 是否打开漫游
watch(
() => route.fullPath,
(newVal) => {
if (newVal.includes('#tabsRef')) {
setTimeout(() => {
open.value = true
}, 1500)
async (newVal) => {
// 处理在当前页面跳转到当前页面的情况 onActivated 不会触发
if (newVal.includes('#tabsRef') && newVal.includes('?t')) {
await handleBackTop()
open.value = true
}
},
)
onActivated(async () => {
if (route.fullPath.includes('#tabsRef')) {
await handleBackTop()
open.value = true
}
refresh()
})
</script>
<style lang="scss" scoped>
.fade-enter-from,
......
......@@ -120,7 +120,7 @@
</div>
</template>
<script setup lang="ts" name="RecommendList">
<script setup lang="ts">
import { usePageSearch } from '@/hooks'
import { getArticleList, type ArticleItemDto } from '@/api'
import { TABS_REF_KEY, ArticleTypeEnum } from '@/constants'
......
......@@ -418,7 +418,7 @@
</div>
</template>
<script setup lang="ts" name="RecommendList">
<script setup lang="ts" name="VideoList">
import { useRouter } from 'vue-router'
import { useScrollTop } from '@/hooks'
import { ArticleTypeEnum, TABS_REF_KEY } from '@/constants'
......
......@@ -24,7 +24,7 @@
</div>
</template>
<script setup lang="ts" name="CultureHome">
<script setup lang="ts">
import RecommendList from './components/recommendList.vue'
import VideoList from './components/videoList.vue'
import Tabs from '@/components/common/Tabs'
......
<template>
<div class="main-container">
<div class="banner mb-3 w-full aspect-[16/6] overflow-hidden">
<!-- aspect-[16/6] -->
<div class="banner mb-3 w-full aspect-[4/1] overflow-hidden">
<el-carousel class="h-full w-full shadow-lg rounded-lg">
<el-carousel-item v-for="(item, index) in carouselList" :key="index" class="h-full w-full">
<el-image :src="item.assetUrl" class="w-full h-full object-cover" />
......@@ -55,6 +56,7 @@
<!-- 等级等相关信息 -->
<div
ref="levelContainerRef"
id="levelContainerRef"
class="level-container common-box flex flex-col justify-center items-center gap-4 rounded-lg bg-#E4F5FE"
>
<div class="top flex items-center justify-center gap-3">
......@@ -138,18 +140,22 @@
<div class="grid grid-cols-3 gap-2 sm:gap-4 mb-4">
<div
class="flex flex-col items-center justify-center text-center cursor-pointer hover:-translate-y-1 transition-transform duration-200 p-2 rounded-lg hover:bg-white/10"
@click="router.push('/homePage/askTab')"
@click="publishTopic"
>
<svg-icon name="topic_release" size="80" />
<div class="text-xs sm:text-sm">话题发布</div>
</div>
<div
class="flex flex-col items-center justify-center text-center cursor-pointer hover:-translate-y-1 transition-transform duration-200 p-2 rounded-lg hover:bg-white/10"
@click="router.push('/homePage/askTab')"
@click="router.push('/userPage/selfAnswer')"
>
<svg-icon name="answer" size="80" />
<el-badge :value="userQestionNum" :offset="[-5, 20]" :hidden="!userQestionNum">
<svg-icon name="answer" size="80" />
</el-badge>
<div class="text-xs sm:text-sm">回答问题</div>
</div>
<div
@click="router.push('/publishVideo')"
class="flex flex-col items-center justify-center text-center cursor-pointer hover:-translate-y-1 transition-transform duration-200 p-2 rounded-lg hover:bg-white/10"
......@@ -160,7 +166,7 @@
</div>
<div class="flex justify-center items-center">
<el-button
@click="router.push('/homePage/askTab')"
@click="router.push(`/userPage/selfComment?type=${ArticleTypeEnum.QUESTION}`)"
class="bg-[linear-gradient(to_right,#D6C9FF_0%,#C5B1FF_100%)] shadow-[0_1px_4px_0_rgba(95,0,237,0.25)] border-none hover:-translate-y-1 transition-all duration-200 text-xs sm:text-sm w-116px"
type="primary"
>
......@@ -275,20 +281,26 @@ import ya from '@/assets/img/culture/ya_culture.png'
import ask from '@/assets/img/culture/ask.png'
import type { RouteLocationNormalizedLoadedGeneric } from 'vue-router'
import { getTaskList, dailySign, getCarouselList, getUserAccountData, getRecordData } from '@/api'
import { TaskTypeEnum, TaskDateLimitTypeText } from '@/constants'
import { TaskTypeEnum, TaskDateLimitTypeText, ArticleTypeEnum } from '@/constants'
import type { CarouselItemDto, TaskItemDto, UserAccountDataDto, UserRecordDataDto } from '@/api'
import { TABS_REF_KEY, levelListOptions } from '@/constants'
import { useScrollTop } from '@/hooks'
import { useQuestionStore } from '@/stores/question'
import { storeToRefs } from 'pinia'
const route = useRoute()
const router = useRouter()
const open = ref(false)
const levelContainerRef = useTemplateRef<HTMLElement>('levelContainerRef')
const dailySignBtnRef = useTemplateRef<HTMLElement>('dailySignBtnRef')
const { handleBackTop } = useScrollTop(levelContainerRef)
const questionStore = useQuestionStore()
const { userQestionNum } = storeToRefs(questionStore)
const getThirdLevelKey = (route: RouteLocationNormalizedLoadedGeneric) => {
console.log(route.fullPath, '三级路由')
// console.log(route.fullPath, '三级路由首页')
// console.log(route.path, 11111111111111)
// return route.fullPath // fullpath带有query参数
return route.path
......@@ -385,15 +397,19 @@ const handleTask = async (item: TaskItemDto) => {
//每日签到
await handleBackTop()
open.value = true
// triggerAnimation()
} else if (item.svgName === 'valid_comments') {
// 发布评论
ElMessage.info('快去文章评论区去发表评论吧~')
} else if (item.svgName === 'topic_publish') {
router.push(`/homePage/askTab#tabsRef?t=${Date.now()}`) // 加一个时间戳 是因为对于不同的时间戳 用的keepalive是 path 都是同一个组件 在组件里面监听watch(()=>to.fullPath) 每次都能监听得到
if (route.path.includes('/homePage/askTab')) {
// 同一个页面 需要加事件触发 watch
router.push(`/homePage/askTab#tabsRef?t=${Date.now()}`)
} else {
router.push(`/homePage/askTab#tabsRef`)
}
} else if (item.svgName === 'answer_ask') {
// 回答问题
router.push('/homePage/askTab')
router.push('/userPage/selfAnswer')
} else if (item.svgName === 'video_publish') {
// 视频发布
router.push('/publishVideo')
......@@ -403,6 +419,15 @@ const handleTask = async (item: TaskItemDto) => {
}
}
const publishTopic = () => {
if (route.path.includes('/homePage/askTab')) {
// 同一个页面 需要加事件触发 watch
router.push(`/homePage/askTab#tabsRef?t=${Date.now()}`)
} else {
router.push(`/homePage/askTab#tabsRef`)
}
}
const initPage = () => {
Promise.allSettled([getCarouselList(), getUserAccountData(), getRecordData()]).then(
([r1, r2, r3]) => {
......@@ -429,9 +454,14 @@ const refreshTaskData = async (refreshRecordData = false) => {
specialTaskList.value = data.filter((item) => item.taskType === TaskTypeEnum.SPECIAL_TASK)
}
// 刷新任务进度
onActivated(() => {
// 刷新任务进度 以及是否打开漫游
onActivated(async () => {
questionStore.fetchUserQestionNum()
refreshTaskData(false)
if (route.fullPath.includes('#levelContainerRef')) {
await handleBackTop()
open.value = true
}
})
onMounted(() => {
......
......@@ -22,7 +22,7 @@
</div>
</template>
<script setup lang="ts" name="CultureAsk">
<script setup lang="ts">
import Tabs from '@/components/common/Tabs'
import { Refresh } from '@element-plus/icons-vue'
import ColumnList from './components/columnList.vue'
......
<template>
<div class="flex-1 flex flex-col" v-loading="loading">
<!-- List Container -->
<div class="flex-1 p-4 pt-1">
<div class="relative">
<el-tabs v-model="searchParams.status" @tab-change="toggleTab">
<el-tab-pane
v-for="tab in auditTypeListOptions"
:key="tab.value"
:label="tab.label"
:name="tab.value"
/>
</el-tabs>
<div class="absolute right-0 top-2.5 z-1000">
<el-icon
size="15"
class="cursor-pointer hover:rotate-180 transition-all duration-300"
@click="refresh"
><Refresh
/></el-icon>
</div>
</div>
<div v-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>
<div v-else class="space-y-4">
<div
v-for="item in list"
:key="item.id"
class="flex items-center p-2 rounded-lg hover:bg-gray-100 transition-colors cursor-pointer"
>
<!-- Content -->
<div class="flex-1 min-w-0">
<!-- 头像 + 内容区 -->
<div class="flex gap-3">
<el-image :src="item.showAvatar" class="w-12 h-12 rounded-full flex-shrink-0" />
<div class="flex-1 min-w-0 flex flex-col justify-center">
<!-- 第一行:用户名 + 时间 + 标签 -->
<div class="flex items-center gap-2 mb-1.5">
<span class="text-gray-900 text-sm flex-shrink-0">{{ item.createName }}</span>
<span
class="text-gray-400 text-sm flex-shrink-0 whitespace-nowrap flex items-center"
>
{{ dayjs(item.createTime * 1000).format('YYYY-MM-DD HH:mm:ss') }}
<span class="flex items-center px-2"> 举报帖子 </span>
<el-link type="primary" :underline="false" @click="handleToDetail(item)">
{{ item.title }}
</el-link>
</span>
<!-- <div class="flex flex-wrap gap-1.5 flex-1 min-w-0">
<el-tag size="small">
{{ articleTypeListOptions.find((i) => i.value === item.type)?.label }}
</el-tag>
</div> -->
</div>
<div class="text-gray-700 font-medium">举报理由:{{ item.reason }}</div>
</div>
</div>
</div>
<div
v-if="searchParams.status === AuditStatusEnum.UNAUDITED"
class="flex items-center text-gray-400 text-sm ml-4"
>
<el-button
type="primary"
link
@click="handleAudit({ id: item.id, status: AuditStatusEnum.AGREED })"
>
同意
</el-button>
<el-button
type="danger"
link
@click="
handleAudit({
id: item.id,
status: AuditStatusEnum.REJECTED,
})
"
>
拒绝
</el-button>
</div>
</div>
</div>
</div>
<div
v-if="list.length"
class="flex items-center justify-end px-6 py-4 border-t border-gray-200"
>
<div class="pagination-wrapper bg-white rounded-lg shadow-sm border border-gray-100 p-3">
<el-pagination
v-model:current-page="searchParams.current"
v-model:page-size="searchParams.size"
@size-change="changePageSize"
@current-change="goToPage"
:page-sizes="[10, 20, 30, 40]"
layout="prev, pager, next, jumper, total"
:total="total"
class="custom-pagination"
/>
</div>
</div>
</div>
</template>
<script lang="ts" setup>
import { Document } from '@element-plus/icons-vue'
import { getComplaintList, auditComplaint } from '@/api'
import { usePageSearch } from '@/hooks'
import { auditTypeListOptions } from '@/constants/options'
import dayjs from 'dayjs'
import { AuditStatusEnum, ArticleTypeEnum } from '@/constants'
import type { AuditComplaintDto, ComplaintListItemDto } from '@/api'
import type { TabPaneName } from 'element-plus'
const router = useRouter()
const toggleTab = (key: TabPaneName) => {
searchParams.value.status = key as AuditStatusEnum
refresh()
}
// State
const { list, loading, searchParams, total, refresh, goToPage, changePageSize } = usePageSearch(
getComplaintList,
{
defaultParams: {
status: AuditStatusEnum.UNAUDITED,
},
},
)
const handleAudit = async (data: AuditComplaintDto) => {
if (data.status === AuditStatusEnum.REJECTED) {
const { value } = await ElMessageBox.prompt('请输入拒绝理由', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
inputPattern: /^\S+$/,
inputErrorMessage: '请输入拒绝理由',
})
data.remark = value
}
await auditComplaint(data)
ElMessage.success('审核成功')
refresh()
}
const handleToDetail = (item: ComplaintListItemDto) => {
if (item.type === ArticleTypeEnum.VIDEO) {
router.push(`/videoDetail/${item.articleId}`)
} else {
router.push(`/articleDetail/${item.articleId}`)
}
}
</script>
<template>
<div class="flex-1 flex flex-col" v-loading="loading">
<div class="flex-1 p-4 pt-1">
<div class="flex items-center justify-end">
<div>
<el-icon
size="15"
class="cursor-pointer hover:rotate-180 transition-all duration-300"
@click="refresh"
><Refresh
/></el-icon>
</div>
</div>
<div v-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>
<div v-else class="space-y-4">
<div
v-for="item in list"
:key="item.id"
class="flex items-center p-2 rounded-lg hover:bg-gray-100 transition-colors cursor-pointer"
>
<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">
<span class="mr-2">
{{ dayjs(item.createTime * 1000).format('YYYY-MM-DD HH:mm:ss') }}
</span>
<span class="mr-2">评论 {{ item.collectionCount }}</span>
</div>
</div>
<div class="flex items-center text-gray-400 text-sm ml-4">
<el-button type="primary" link @click="router.push(`/articleDetail/${item.id}`)">
去回复
</el-button>
</div>
</div>
</div>
</div>
<div
v-if="list.length"
class="flex items-center justify-end px-6 py-4 border-t border-gray-200"
>
<div class="pagination-wrapper bg-white rounded-lg shadow-sm border border-gray-100 p-3">
<el-pagination
v-model:current-page="searchParams.current"
v-model:page-size="searchParams.size"
@size-change="changePageSize"
@current-change="goToPage"
:page-sizes="[10, 20, 30, 40]"
layout="prev, pager, next, jumper, total"
:total="total"
class="custom-pagination"
/>
</div>
</div>
</div>
</template>
<script lang="ts" setup>
import { Document, Refresh } from '@element-plus/icons-vue'
import { answerQuestionPage } from '@/api'
import { usePageSearch } from '@/hooks'
import dayjs from 'dayjs'
const router = useRouter()
const { list, loading, searchParams, total, refresh, goToPage, changePageSize } = usePageSearch(
answerQuestionPage,
{
immediate: false,
},
)
onActivated(() => {
refresh()
})
</script>
......@@ -46,12 +46,9 @@
<span class="text-gray-400 text-sm flex-shrink-0 whitespace-nowrap">
{{ dayjs(item.createTime * 1000).format('YYYY-MM-DD HH:mm:ss') }}
</span>
<div
class="flex flex-wrap gap-1.5 flex-1 min-w-0"
v-if="item.tagNameList?.length"
>
<el-tag v-for="tag in item.tagNameList" :key="tag" size="small">
{{ tag }}
<div class="flex flex-wrap gap-1.5 flex-1 min-w-0">
<el-tag size="small">
{{ articleTypeListOptions.find((i) => i.value === item.type)?.label }}
</el-tag>
</div>
</div>
......@@ -92,6 +89,12 @@
拒绝
</el-button>
</div>
<div
v-if="searchParams.isAudit === AuditStatusEnum.AGREED"
class="flex items-center text-gray-400 text-sm ml-4"
>
<el-button type="info" link @click="handleView(item)">查看</el-button>
</div>
</div>
</div>
</div>
......@@ -120,11 +123,13 @@
import { Document } from '@element-plus/icons-vue'
import { getAuditList, auditArticle } from '@/api'
import { usePageSearch } from '@/hooks'
import { auditTypeListOptions } from '@/constants/options'
import { auditTypeListOptions, articleTypeListOptions } from '@/constants/options'
import dayjs from 'dayjs'
import { AuditStatusEnum } from '@/constants'
import type { AuditArticleDto } from '@/api'
import type { TabPaneName } from 'element-plus'
import { ArticleTypeEnum } from '@/constants'
import type { ArticleItemDto } from '@/api'
const router = useRouter()
......@@ -160,4 +165,12 @@ const handleAudit = async (data: AuditArticleDto) => {
refresh()
}
const handleView = (item: ArticleItemDto) => {
if (item.type === ArticleTypeEnum.VIDEO) {
router.push(`/videoDetail/${item.id}`)
} else {
router.push(`/articleDetail/${item.id}`)
}
}
</script>
......@@ -3,15 +3,29 @@
<!-- List Container -->
<div class="flex-1 p-4 pt-1">
<div class="relative">
<el-tabs v-model="searchParams.messageType" @tab-change="toggleTab">
<el-tabs v-model="searchParams.type" @tab-change="toggleTab">
<el-tab-pane
v-for="tab in commentTypeListOptions"
v-for="tab in articleTypeListOptions"
:key="tab.value"
:label="tab.label"
:name="tab.value"
/>
</el-tabs>
<div class="absolute right-0 top-2.5 z-1000">
<div class="absolute right-0 top-2.5 z-1000 flex items-center gap-2">
<el-select
v-model="searchParams.messageType"
@change="refresh"
class="w-25!"
size="small"
>
<el-option
v-for="tab in commentTypeListOptions"
:key="tab.value"
:label="tab.label"
:value="tab.value"
/>
</el-select>
<el-icon
size="15"
class="cursor-pointer hover:rotate-180 transition-all duration-300"
......@@ -35,22 +49,19 @@
class="flex items-center p-2 rounded-lg hover:bg-gray-100 transition-colors cursor-pointer"
>
<!-- 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">
<span class="mr-2">
{{ dayjs(item.createTime * 1000).format('YYYY-MM-DD HH:mm:ss') }}
</span>
<!-- <span class="mr-2">浏览 {{ item.viewCount }}</span>
<span class="mr-2">点赞 {{ item.replyCount }}</span>
<span class="mr-2">评论 {{ item.collectionCount }}</span>
<span class="mr-2">收藏 {{ item.praiseCount }}</span> -->
<div class="flex-1 min-w-0 space-y-1.5">
<div class="text-gray-900 font-medium text-base line-clamp-2">
{{ item.title }}
</div>
<div class="text-gray-600 text-sm line-clamp-2">评论内容:{{ item.content }}</div>
<div class="text-gray-400 text-xs">
{{ dayjs(item.createTime * 1000).format('YYYY-MM-DD HH:mm') }}
</div>
</div>
<!-- Meta Info -->
<div class="flex items-center text-gray-400 text-sm ml-4">
<el-button type="primary" link>编辑</el-button>
<el-button type="primary" link @click="handleView(item)">查看</el-button>
<el-button type="danger" link @click="handleDelete(item.id)">删除</el-button>
</div>
</div>
......@@ -81,21 +92,27 @@
import { Document, Refresh } from '@element-plus/icons-vue'
import { getSelfCommentList, deleteComment } from '@/api'
import { usePageSearch } from '@/hooks'
import { commentTypeListOptions } from '@/constants/options'
import { commentTypeListOptions, articleTypeListOptions } from '@/constants/options'
import dayjs from 'dayjs'
import { CommentTypeEnum } from '@/constants/enums'
import { CommentTypeEnum, ArticleTypeEnum } from '@/constants/enums'
import type { TabPaneName } from 'element-plus'
import type { SelfCommentItemDto } from '@/api/user/types'
const route = useRoute()
const router = useRouter()
const toggleTab = (key: TabPaneName) => {
searchParams.value.messageType = key as CommentTypeEnum
searchParams.value.type = key as ArticleTypeEnum
refresh()
}
const { list, loading, searchParams, total, refresh, goToPage, changePageSize } = usePageSearch(
getSelfCommentList,
{
immediate: false,
defaultParams: {
messageType: CommentTypeEnum.COMMNET_TO_OTHER,
type: ArticleTypeEnum.POST,
},
},
)
......@@ -110,4 +127,22 @@ const handleDelete = async (id: number) => {
ElMessage.success('删除成功')
refresh()
}
const handleView = (item: SelfCommentItemDto) => {
if (item.type === ArticleTypeEnum.VIDEO) {
router.push(`/videoDetail/${item.articleId}`)
} else {
router.push(`/articleDetail/${item.articleId}`)
}
}
onActivated(() => {
if (route.query.type) {
searchParams.value.type = route.query.type as ArticleTypeEnum
}
if (route.query.messageType) {
searchParams.value.messageType = Number(route.query.messageType)
}
refresh()
})
</script>
......@@ -51,10 +51,11 @@
</div>
<div class="flex items-center text-gray-400 text-sm ml-4">
<el-button v-if="item.currentCount === item.limitCount" type="info" link disabled
<!-- <el-button v-if="item.currentCount === item.limitCount" type="info" link disabled
>已完成</el-button
>
<el-button v-else type="primary" link>去完成</el-button>
> -->
<!-- <el-button v-else type="primary" link @click="handleTask(item)">去完成</el-button> -->
<el-button type="primary" link @click="handleTask(item)">去完成</el-button>
</div>
</div>
</div>
......@@ -89,7 +90,10 @@ import { taskTypeListOptions } from '@/constants/options'
import { TaskTypeEnum } from '@/constants/enums'
import type { TabPaneName } from 'element-plus'
import { TaskDateLimitTypeText } from '@/constants'
import type { TaskItemDto } from '@/api'
import { useRouter } from 'vue-router'
const router = useRouter()
const toggleTab = (key: TabPaneName) => {
searchParams.value.taskType = key as TaskTypeEnum
refresh()
......@@ -103,4 +107,25 @@ const { list, loading, searchParams, total, refresh, goToPage, changePageSize }
},
},
)
const handleTask = async (item: TaskItemDto) => {
console.log(item)
if (item.svgName === 'daily_sign') {
router.push(`/homePage/homeTab#levelContainerRef`)
} else if (item.svgName === 'valid_comments') {
// 发布评论
ElMessage.info('快去文章评论区去发表评论吧~')
} else if (item.svgName === 'topic_publish') {
router.push(`/homePage/askTab#tabsRef`)
} else if (item.svgName === 'answer_ask') {
// 回答问题
router.push('/userPage/selfAnswer')
} else if (item.svgName === 'video_publish') {
// 视频发布
router.push('/publishVideo')
} else if (item.svgName === 'practice_publish') {
// 个人实践/投稿
router.push('/publishCase')
}
}
</script>
......@@ -28,9 +28,8 @@
@click="handleBackUser"
>返回个人账号</el-button
>
<el-button v-if="userInfo.isAdmin" type="primary" plain size="small" @click="handleAdmin"
>后台管理</el-button
>
<!-- v-if="userInfo.isAdmin" 暂时不加权限 -->
<el-button type="primary" plain size="small" @click="handleAdmin">后台管理</el-button>
</div>
</div>
......@@ -62,11 +61,11 @@
<div class="bg-white rounded-lg shadow-sm mb-4">
<div
v-for="item in menuUserItems"
:key="item.key"
@click="activeMenu = item.key"
:key="item.path"
@click="changeMenu(item.path)"
:class="[
'flex items-center gap-3 px-4 py-3 cursor-pointer transition-colors border-b border-gray-100 last:border-b-0',
activeMenu === item.key
activeMenu === item.path
? 'bg-blue-50 text-blue-600 border-r-3 border-r-blue-600'
: 'text-gray-700 hover:bg-gray-50',
]"
......@@ -81,11 +80,11 @@
<div class="bg-white rounded-lg shadow-sm">
<div
v-for="item in menuOfficialItems"
:key="item.key"
@click="activeMenu = item.key"
:key="item.path"
@click="changeMenu(item.path)"
:class="[
'flex items-center gap-3 px-4 py-3 cursor-pointer transition-colors border-b border-gray-100 last:border-b-0',
activeMenu === item.key
activeMenu === item.path
? 'bg-blue-50 text-blue-600 border-r-3 border-r-blue-600'
: 'text-gray-700 hover:bg-gray-50',
]"
......@@ -101,11 +100,13 @@
<!-- 右侧内容区域 -->
<div class="flex-1">
<div class="bg-white rounded-lg shadow-sm min-h-500px">
<transition name="fade" mode="out-in">
<keep-alive>
<component :is="currentComponent" ref="currentComponentRef" />
</keep-alive>
</transition>
<router-view v-slot="{ Component, route }">
<transition name="fade" mode="out-in">
<keep-alive>
<component :is="Component" :key="getThirdLevelKey(route)" />
</keep-alive>
</transition>
</router-view>
</div>
</div>
</div>
......@@ -123,6 +124,8 @@ import {
Pointer,
Collection,
Finished,
ChatLineSquare,
Warning,
} from '@element-plus/icons-vue'
import { useUserStore } from '@/stores/user'
import { storeToRefs } from 'pinia'
......@@ -135,48 +138,73 @@ import SelfCase from './components/selfCase.vue'
import SelfTask from './components/selfTask.vue'
import SelfComment from './components/selfComment.vue'
import SelfAudit from './components/selfAudit.vue'
import SelfAnswer from './components/selfAnswer.vue'
import SelfComplaint from './components/selfComplaint.vue'
import { generateLoginKey, hasOfficialAccount } from '@/api'
import type { OfficialAccountItemDto } from '@/api/user/types'
import { wxLogin } from '@/utils/wxUtil'
import type { RouteLocationNormalizedLoadedGeneric } from 'vue-router'
import type { TabPaneName } from 'element-plus'
const router = useRouter()
const route = useRoute()
// 当前激活的菜单 用计算属性 好办法!
const activeMenu = computed(() => {
const path = route.path
if (path.includes('userPage')) {
return path.split('/').at(-1) || 'selfPublish'
}
return 'selfPublish'
})
const getThirdLevelKey = (route: RouteLocationNormalizedLoadedGeneric) => {
console.log(route.fullPath, '三级路由用户信息页面')
return route.path
}
const editUserInfoRef = useTemplateRef<InstanceType<typeof EditUserInfo>>('editUserInfoRef')
const userStore = useUserStore()
const { userInfo } = storeToRefs(userStore)
const route = useRoute()
const key = route.query.key as string
// 当前激活的菜单
const activeMenu = ref(key || 'posts')
// 左侧普通用户菜单
const menuUserItems = [
{ key: 'posts', label: '我的帖子', icon: User, component: SelfPublish, tab: '发布' },
{ key: 'activity', label: '我的草稿', icon: Document, component: SelfDraft, tab: '草稿' },
{ key: 'favorites', label: '我的收藏', icon: Star, component: SelfCollect, tab: '收藏' },
{ key: 'praise', label: '我的点赞', icon: Pointer, component: SelfPraise, tab: '点赞' },
{ key: 'case', label: '我的案例库', icon: Collection, component: SelfCase, tab: '案例库' },
{ key: 'task', label: '我的任务', icon: Finished, component: SelfTask, tab: '任务' },
{ path: 'selfPublish', label: '我的帖子', icon: User, component: SelfPublish, tab: '发布' },
{ path: 'selfDraft', label: '我的草稿', icon: Document, component: SelfDraft, tab: '草稿' },
{ path: 'selfCollect', label: '我的收藏', icon: Star, component: SelfCollect, tab: '收藏' },
{ path: 'selfPraise', label: '我的点赞', icon: Pointer, component: SelfPraise, tab: '点赞' },
{ path: 'selfCase', label: '我的案例库', icon: Collection, component: SelfCase, tab: '案例库' },
{ path: 'selfTask', label: '我的任务', icon: Finished, component: SelfTask, tab: '任务' },
{
key: 'comment',
path: 'selfComment',
label: '评论回复',
icon: ChatDotRound,
component: SelfComment,
tab: '评论回复',
},
{
path: 'selfAnswer',
label: '回答问题(问吧)',
icon: ChatLineSquare,
component: SelfAnswer,
tab: '回答问题',
},
]
// 左侧官方账号菜单
const menuOfficialItems = [
{ key: 'audit', label: '审核列表', icon: User, component: SelfAudit, tab: '审核列表' },
{ path: 'selfAudit', label: '审核列表', icon: User, component: SelfAudit, tab: '审核列表' },
{
path: 'selfComplaint',
label: '举报列表',
icon: Warning,
component: SelfComplaint,
tab: '举报列表',
},
]
const currentComponentRef = useTemplateRef<InstanceType<typeof SelfAudit>>('currentComponentRef')
const currentComponent = computed(
() =>
[...menuUserItems, ...menuOfficialItems].find((item) => item.key === activeMenu.value)
?.component,
)
const changeMenu = (key: TabPaneName) => {
router.push(`/userPage/${key}`)
}
const handleEdit = () => {
console.log('修改资料')
......
......@@ -60,7 +60,7 @@
</template>
<script setup lang="ts">
import { addOrCancelArticleReward, getYaBiData } from '@/api'
const rewardNum = defineModel<number>('rewardNum', { required: true })
const rewardNum = defineModel<number>('rewardNum', { required: true, default: 0 })
interface RewardOption {
amount: number
icon: string
......
<template>
<!-- 整体页面容器:浅灰背景 -->
<div>
<!-- 一定要保证过度里面的内容是 只有一个根节点!!!! 纯字符串也不行 -->
<!-- 整体页面容器:浅灰背景 -->
<!-- 卡片1: 视频播放器区域 -->
<div class="bg-white rounded-lg shadow-sm overflow-hidden">
<!-- 标题区 -->
<div class="p-4 pb-3">
<h1 class="text-xl md:text-2xl font-medium text-gray-900 mb-2 leading-snug">
<h1
class="text-xl md:text-2xl font-medium text-gray-900 mb-2 leading-snug flex items-center justify-between"
>
{{ videoDetail?.title }}
<ActionMore :articleDetail="videoDetail" />
</h1>
<div class="flex items-center text-14px text-gray-500 gap-4 flex-wrap">
<span class="flex items-center gap-1">
......@@ -20,7 +24,6 @@
</div>
</div>
<!-- 视频播放器 -->
<div class="w-full bg-black aspect-video">
<video
ref="videoRef"
......@@ -238,6 +241,7 @@ import type { ArticleItemDto } from '@/api/article/types'
import Comment from '@/components/common/Comment/index.vue'
import RewardDialog from './components/rewardDialog.vue'
import ActionMore from '@/components/common/ActionMore/index.vue'
const route = useRoute()
......
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