Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
C
corporateCulture-qd
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
王立鹏
corporateCulture-qd
Commits
dedb7b4a
Commit
dedb7b4a
authored
Dec 03, 2025
by
lijiabin
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
【需求 17679】 feat: 完善问吧、 scroll等
parent
82eec017
Expand all
Show whitespace changes
Inline
Side-by-side
Showing
18 changed files
with
451 additions
and
78 deletions
+451
-78
App.vue
src/App.vue
+0
-1
types.ts
src/api/article/types.ts
+1
-0
index.ts
src/api/shop/index.ts
+0
-1
index.vue
src/components/common/Comment/index.vue
+7
-2
index.vue
src/components/common/PublishBox/index.vue
+2
-2
useScrollTop.tsx
src/hooks/useScrollTop.tsx
+23
-3
onlineTime.vue
src/layoutCulture/components/onlineTime.vue
+2
-2
index.vue
src/layoutCulture/index.vue
+19
-4
guards.ts
src/router/guards.ts
+7
-3
scrollStorage.ts
src/router/scrollStorage.ts
+11
-1
reset.css
src/style/reset.css
+3
-0
axios.ts
src/utils/request/axios.ts
+0
-1
index.vue
src/views/homePage/askTab/index.vue
+68
-33
index.vue
src/views/homePage/index.vue
+28
-14
exchangeContent.tsx
src/views/pointsStore/components/exchangeContent.tsx
+3
-3
index.vue
src/views/pointsStore/index.vue
+0
-0
rewardDialog.vue
src/views/videoDetail/components/rewardDialog.vue
+6
-3
index.vue
src/views/videoDetail/index.vue
+271
-5
No files found.
src/App.vue
View file @
dedb7b4a
...
...
@@ -13,7 +13,6 @@ const locale = ref(zhCn)
// userStore.fetchUserInfo().then((res) => {
// console.log(res)
// })
console
.
log
(
'App.vue mounted'
)
onMounted
(()
=>
{
setTimeout
(()
=>
{
initWxConfig
()
...
...
src/api/article/types.ts
View file @
dedb7b4a
...
...
@@ -147,6 +147,7 @@ export interface ArticleItemDto {
showComment
?:
boolean
relateColumn
?:
string
rewardNum
:
number
isExpand
:
boolean
}
/**
...
...
src/api/shop/index.ts
View file @
dedb7b4a
...
...
@@ -3,7 +3,6 @@ import type { BackendServicePageResult, PageSearchParams } from '@/utils/request
import
type
{
ExchangeGoodsParams
,
ExchangeGoodsRecordItemDto
,
ShopItemDto
,
ShopSearchParams
,
YaBiData
,
ExchangeYabiRecordItemDto
,
...
...
src/components/common/Comment/index.vue
View file @
dedb7b4a
...
...
@@ -254,7 +254,7 @@
</div>
</div>
<div
class=
"px-4"
>
<el-divider
class=
"my-
1
"
/>
<el-divider
class=
"my-
2!
"
/>
</div>
</div>
<!-- 底部分页 -->
...
...
@@ -289,6 +289,10 @@ const { id, defaultSize = 10 } = defineProps<{
defaultSize
?:
number
}
>
()
const
emit
=
defineEmits
<
{
(
e
:
'commentSuccess'
):
void
}
>
()
const
total
=
defineModel
<
number
>
(
'total'
,
{
required
:
true
,
default
:
0
})
const
userStore
=
useUserStore
()
...
...
@@ -393,6 +397,7 @@ const handleMyComment = async () => {
refresh
()
myComment
.
value
=
''
total
.
value
++
emit
(
'commentSuccess'
)
}
const
handleComment
=
async
(
index
:
number
)
=>
{
...
...
@@ -405,9 +410,9 @@ const handleComment = async (index: number) => {
comment
.
value
=
''
total
.
value
++
handleBackTopChildren
(
index
)
// 只需要刷新当前的评论
search
()
emit
(
'commentSuccess'
)
}
// 展开回复 获取子评论列表
...
...
src/components/common/PublishBox/index.vue
View file @
dedb7b4a
...
...
@@ -227,11 +227,11 @@ const handleDeleteImg = (img: string) => {
const
validateForm
=
()
=>
{
if
(
!
form
.
value
.
title
)
{
ElMessage
.
error
(
'请输入
实践
标题'
)
ElMessage
.
error
(
'请输入标题'
)
return
false
}
if
(
!
form
.
value
.
content
)
{
ElMessage
.
error
(
'请输入
实践
内容'
)
ElMessage
.
error
(
'请输入内容'
)
return
false
}
if
(
!
form
.
value
.
mainTagId
)
{
...
...
src/hooks/useScrollTop.tsx
View file @
dedb7b4a
...
...
@@ -26,9 +26,13 @@ export const useScrollTop = (
)
=>
{
const
{
compatFixedHeader
=
true
}
=
options
const
handleBackTop
=
(
currentIndex
:
number
=
0
)
=>
{
const
handleBackTop
=
(
currentIndex
:
number
=
0
):
Promise
<
void
>
=>
{
return
new
Promise
((
resolve
)
=>
{
const
initDoms
=
unref
(
el
)
if
(
!
initDoms
)
return
if
(
!
initDoms
)
{
resolve
()
return
}
let
doms
=
[]
...
...
@@ -38,6 +42,20 @@ export const useScrollTop = (
doms
=
initDoms
}
const
finish
=
()
=>
{
console
.
log
(
'scrollend'
)
resolve
()
}
// 手动添加一次scrollend事件
window
.
addEventListener
(
'scrollend'
,
finish
,
{
once
:
true
})
// 有时候在滚轮就在原位置 不会触发 scrollend事件 所以手动触发一次
setTimeout
(()
=>
{
window
.
removeEventListener
(
'scrollend'
,
finish
)
resolve
()
},
1000
)
// 下面会触发scrollend事件一次
const
dom
=
doms
[
currentIndex
]
as
HTMLElement
|
Window
if
(
dom
instanceof
Window
)
{
window
.
scrollTo
({
...
...
@@ -50,15 +68,17 @@ export const useScrollTop = (
if
(
compatFixedHeader
)
{
const
top
=
dom
?.
getBoundingClientRect
?.().
top
+
window
.
scrollY
-
52
window
.
scrollTo
({
top
,
top
,
// 可以设置滚动的距离
behavior
:
'smooth'
,
})
}
else
{
dom
?.
scrollIntoView
?.({
// 只能滚动到dom的顶部 不能设置滚动的距离
behavior
:
'smooth'
,
block
:
'start'
,
})
}
})
}
return
{
...
...
src/layoutCulture/components/onlineTime.vue
View file @
dedb7b4a
...
...
@@ -106,13 +106,13 @@ const formatSeconds = computed(() => {
onMounted
(
async
()
=>
{
const
{
data
}
=
await
getTodayOnlineSeconds
()
currentSeconds
.
value
=
parseInt
(
data
)
currentSeconds
.
value
=
data
})
setInterval
(()
=>
{
currentSeconds
.
value
++
},
1000
)
set
Timeout
(
async
()
=>
{
set
Interval
(
async
()
=>
{
heartbeat
()
},
1000
*
30
)
</
script
>
src/layoutCulture/index.vue
View file @
dedb7b4a
...
...
@@ -74,8 +74,18 @@
<el-dropdown-menu>
<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-item
:command=
"ArticleTypeEnum.VIDEO"
>
视频
</el-dropdown-item>
<el-dropdown-item
:command=
"ArticleTypeEnum.QUESTION"
>
问吧
</el-dropdown-item>
<el-dropdown-item
v-if=
"userInfo.isOfficialAccount"
:command=
"ArticleTypeEnum.COLUMN"
>
专栏
</el-dropdown-item
>
<el-dropdown-item
v-if=
"userInfo.isOfficialAccount"
:command=
"ArticleTypeEnum.INTERVIEW"
>
专访
</el-dropdown-item
>
</el-dropdown-menu>
</
template
>
</el-dropdown>
...
...
@@ -133,9 +143,14 @@ const getSecondLevelKey = (route: RouteLocationNormalizedLoadedGeneric) => {
return
key
}
const
handlePost
=
async
(
type
:
string
)
=>
{
// router.push(command)
const
handlePost
=
async
(
type
:
ArticleTypeEnum
)
=>
{
if
(
type
===
ArticleTypeEnum
.
VIDEO
)
{
router
.
push
(
'/publishVideo'
)
}
else
if
(
type
===
ArticleTypeEnum
.
QUESTION
)
{
router
.
push
(
`/homePage/askTab#tabsRef?t=
${
Date
.
now
()}
`
)
}
else
{
PublishDialogRef
.
value
?.
open
(
type
)
}
}
const
isDropdownHover
=
ref
(
false
)
</
script
>
...
...
src/router/guards.ts
View file @
dedb7b4a
...
...
@@ -22,9 +22,9 @@ export function registerRouterGuards(router: Router) {
const
code
=
parseCode
(
to
.
fullPath
)
// code是否来自企业微信 1 不是 0 是 2 开发人员登录方式
const
isCodeLogin
=
parseIsCodeLogin
()
const
cutEmail
=
parseIsCutEmail
()
console
.
log
(
code
,
isCodeLogin
,
cutEmail
)
//
const isCodeLogin = parseIsCodeLogin()
//
const cutEmail = parseIsCutEmail()
//
console.log(code, isCodeLogin, cutEmail)
const
userStore
=
useUserStore
()
if
(
code
)
{
console
.
log
(
'code'
,
code
)
...
...
@@ -41,4 +41,8 @@ export function registerRouterGuards(router: Router) {
return
true
}
})
// router.afterEach((to, from) => {
// console.log('afterEach to', to)
// console.log('afterEach from', from)
// })
}
src/router/scrollStorage.ts
View file @
dedb7b4a
...
...
@@ -37,6 +37,10 @@ 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
)
...
...
@@ -45,20 +49,26 @@ export const scrollBehavior: RouterScrollBehavior = (to, from, savedPosition) =>
// 2. 如果有锚点,滚动到锚点
if
(
to
.
hash
)
{
console
.
log
(
to
.
hash
)
setTimeout
(()
=>
{
resolve
({
el
:
to
.
hash
,
el
:
to
.
hash
.
split
(
'?'
)[
0
],
//去除?后面的查询字符串
behavior
:
'smooth'
,
// 平滑滚动
top
:
52
,
})
},
800
)
return
}
// 3. 检查是否有保存的滚动位置
const
savedScrollY
=
scrollPositionMap
.
get
(
to
.
fullPath
)
if
(
savedScrollY
!==
undefined
)
{
setTimeout
(()
=>
{
resolve
({
top
:
savedScrollY
,
behavior
:
'smooth'
,
})
},
300
)
return
}
...
...
src/style/reset.css
View file @
dedb7b4a
...
...
@@ -260,3 +260,6 @@ a {
*/
/* End of reset.css */
body
{
font-family
:
'Segoe UI Emoji'
;
}
src/utils/request/axios.ts
View file @
dedb7b4a
...
...
@@ -22,7 +22,6 @@ export default class DhRequest {
this
.
instance
.
interceptors
.
request
.
use
(
async
(
config
)
=>
{
const
userStore
=
useUserStore
()
console
.
log
(
userStore
.
token
)
const
token
=
userStore
.
token
if
(
token
)
{
config
.
headers
.
Authorization
=
token
...
...
src/views/homePage/askTab/index.vue
View file @
dedb7b4a
...
...
@@ -13,7 +13,7 @@
<!-- 主要内容区域 -->
<div
class=
"mx-auto py-6"
>
<PublishBox
:type=
"ArticleTypeEnum.QUESTION"
/>
<PublishBox
:type=
"ArticleTypeEnum.QUESTION"
ref=
"publishBoxRef"
/>
<div
v-loading=
"loading"
v-if=
"list.length"
>
<!-- 问题列表 -->
<div
class=
"space-y-4"
>
...
...
@@ -38,22 +38,29 @@
/>
<!-- 问题内容 -->
<div>
<p
class=
"text-gray-600 text-base leading-relaxed"
>
<p
:ref=
"(e) => (contentRefList[index] = e as HTMLElement)"
class=
"text-gray-600 text-base leading-relaxed transition-all duration-300"
:class=
"
{ 'line-clamp-3': !item.isExpand }"
>
{{
item
.
content
}}
</p>
<!--
line-clamp-2
-->
<
!-- 展开/收起按钮 --
>
<!--
<el-button
v-if=
"item.content.length > 100 && !item.isExpand
"
@
click=
"handleReadMore
(item)"
<!--
展开/收起按钮 靠右边布局
-->
<
div
class=
"flex justify-end"
>
<el-button
v-if=
"isOverThreeLine(index)
"
@
click=
"handleExpand
(item)"
type=
"primary"
text
size=
"small"
class=
"mt-2 p-0
text-blue-500"
class=
"
text-blue-500"
>
阅读全文
<el-icon
class=
"ml-1"
><ArrowDown
/></el-icon>
</el-button>
-->
{{
item
.
isExpand
?
'收起'
:
'阅读全文'
}}
<el-icon
class=
"ml-1"
:class=
"
{ 'rotate-180': item.isExpand }">
<ArrowDown
/>
</el-icon>
</el-button>
</div>
</div>
</div>
<!-- 底部信息栏 -->
...
...
@@ -73,13 +80,13 @@
}}
</span>
<!-- 操作按钮组 -->
<
!--
<
div
class=
"flex items-center"
>
<div
class=
"flex items-center"
>
<el-button
size=
"small"
plain
>
<el-icon><Plus
/></el-icon>
添加
</el-button>
<el-button
size=
"small"
plain
@
click=
"handle
Answer
(index)"
>
<el-button
size=
"small"
plain
@
click=
"handle
Comment
(index)"
>
<el-icon><Edit
/></el-icon>
回答
</el-button>
...
...
@@ -93,7 +100,7 @@
<el-icon><Warning
/></el-icon>
举报
</el-button>
</div>
-->
</div>
</div>
<!-- 右侧:统计信息 -->
...
...
@@ -129,10 +136,11 @@
</div>
<Transition
name=
"fade"
>
<Comment
v-
if
=
"item.showComment"
v-
show
=
"item.showComment"
:id=
"item.id"
:total=
"item.replyCount"
:defaultSize=
"5"
@
commentSuccess=
"() => handleCommentSuccess(index)"
/>
</Transition>
</el-card>
...
...
@@ -171,21 +179,19 @@
</div>
</
template
>
</div>
<el-tour
v-model=
"open"
>
<el-tour-step
:target=
"publishBoxRef?.$el"
placement=
"right"
>
<div>
在这里发布你的问题
</div>
</el-tour-step>
<
template
#
indicators
></
template
>
</el-tour>
</div>
</template>
<
script
setup
lang=
"ts"
name=
"CultureAsk"
>
import
Tabs
from
'@/components/common/Tabs'
import
{
Plus
,
Edit
,
Star
,
Warning
,
View
,
ChatDotRound
,
ArrowDown
,
Refresh
,
}
from
'@element-plus/icons-vue'
import
{
Star
,
View
,
ChatDotRound
,
Refresh
}
from
'@element-plus/icons-vue'
import
Comment
from
'@/components/common/Comment/index.vue'
import
{
useScrollTop
,
usePageSearch
}
from
'@/hooks'
import
{
TABS_REF_KEY
}
from
'@/constants'
...
...
@@ -193,7 +199,12 @@ import PublishBox from '@/components/common/PublishBox/index.vue'
import
{
ArticleTypeEnum
}
from
'@/constants'
import
dayjs
from
'dayjs'
import
{
getArticleList
,
addOrCanceArticlelCollect
}
from
'@/api'
import
{
ArticleItemDto
}
from
'@/api/article/types'
import
type
{
ArticleItemDto
}
from
'@/api/article/types'
const
route
=
useRoute
()
const
open
=
ref
(
false
)
const
publishBoxRef
=
useTemplateRef
(
'publishBoxRef'
)
const
activeTab
=
ref
(
'最新'
)
const
tabs
=
[
{
label
:
'最新'
,
value
:
'最新'
},
...
...
@@ -209,6 +220,7 @@ const { list, total, searchParams, loading, refresh } = usePageSearch(getArticle
list
.
map
((
item
)
=>
({
...
item
,
showComment
:
false
,
isExpand
:
false
,
})),
})
...
...
@@ -227,13 +239,8 @@ const handleComment = (index: number) => {
list
.
value
[
index
]
!
.
showComment
=
!
list
.
value
[
index
]
!
.
showComment
}
const
handleAnswer
=
(
index
:
number
)
=>
{
console
.
log
(
index
)
}
const
handleReadMore
=
(
item
:
any
)
=>
{
// 直接展开
item
.
isExpand
=
true
const
handleCommentSuccess
=
(
index
:
number
)
=>
{
list
.
value
[
index
]
!
.
replyCount
++
}
const
handleRefresh
=
()
=>
{
...
...
@@ -243,6 +250,34 @@ const handleRefresh = () => {
const
handleTabChange
=
()
=>
{
handleRefresh
()
}
const
contentRefList
=
ref
<
HTMLElement
[]
>
([])
// 检测当前是否超过三行 要用到具体的dom
const
isOverThreeLine
=
(
index
:
number
)
=>
{
if
(
!
contentRefList
.
value
[
index
])
return
false
const
lineHeight
=
parseFloat
(
getComputedStyle
(
contentRefList
.
value
[
index
]).
lineHeight
)
const
height
=
contentRefList
.
value
[
index
]
!
.
scrollHeight
const
maxHeight
=
lineHeight
*
3
return
height
>
maxHeight
}
const
handleExpand
=
(
item
:
ArticleItemDto
)
=>
{
item
.
isExpand
=
!
item
.
isExpand
}
// 监听路由变化 如果包含#tabsRef 则打开漫游
watch
(
()
=>
route
.
fullPath
,
(
newVal
)
=>
{
if
(
newVal
.
includes
(
'#tabsRef'
))
{
setTimeout
(()
=>
{
open
.
value
=
true
},
1500
)
}
},
)
</
script
>
<
style
lang=
"scss"
scoped
>
.fade-enter-from
,
...
...
src/views/homePage/index.vue
View file @
dedb7b4a
...
...
@@ -10,7 +10,11 @@
<div
class=
"flex gap-3"
>
<div
class=
"left flex-1 basis-full xl:basis-3/4 transition-all duration-500"
>
<div
ref=
"tabsRef"
class=
"tabs-container h-75px flex relative rounded-lg mb-3 shadow-md"
>
<div
id=
"tabsRef"
ref=
"tabsRef"
class=
"tabs-container h-75px flex relative rounded-lg mb-3 shadow-md"
>
<div
v-for=
"tab in tabs"
:key=
"tab.path"
...
...
@@ -202,7 +206,7 @@
>
<div
class=
"flex items-center min-w-0 flex-1"
>
<div
class=
"h-70px flex items-center justify-center"
>
<svg-icon
:name=
"item.svgName"
size=
"
50
"
/>
<svg-icon
:name=
"item.svgName"
size=
"
46
"
/>
</div>
<div
class=
"flex flex-col items-start justify-center ml-2 sm:ml-3 min-w-0 flex-1"
...
...
@@ -228,7 +232,7 @@
class
=
"w-72px h-32px shadow-[0_1px_8px_0_rgba(255,141,54,0.25)] border-none text-xs sm:text-sm rounded-full"
:
class
=
"[
item.currentCount === item.limitCount
? 'bg-#FFC5A1'
? 'bg-#FFC5A1
cursor-not-allowed
'
: 'bg-[linear-gradient(to_right,#FFC5A1_0%,#FFB77F_100%)] hover:-translate-y-1 transition-all duration-200 cursor-pointer',
]"
@
click
=
"handleTask(item)"
...
...
@@ -255,6 +259,13 @@
<
/div
>
<
/div
>
<
/div
>
<
el
-
tour
v
-
model
=
"open"
>
<
el
-
tour
-
step
:
target
=
"dailySignBtnRef"
>
<
div
>
签到成功后,可以获得亚币奖励
<
/div
>
<
/el-tour-step
>
<
template
#
indicators
><
/template
>
<
/el-tour
>
<
/div
>
<
/template
>
<
script
setup
lang
=
"ts"
>
...
...
@@ -267,21 +278,20 @@ import { getTaskList, dailySign, getCarouselList, getUserAccountData, getRecordD
import
{
TaskTypeEnum
,
TaskDateLimitTypeText
}
from
'@/constants'
import
type
{
CarouselItemDto
,
TaskItemDto
,
UserAccountDataDto
,
UserRecordDataDto
}
from
'@/api'
import
{
TABS_REF_KEY
,
levelListOptions
}
from
'@/constants'
import
{
useScrollTop
,
useHintAnimation
}
from
'@/hooks'
import
{
useScrollTop
}
from
'@/hooks'
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
{
triggerAnimation
}
=
useHintAnimation
(
dailySignBtnRef
,
{
classes
:
[
'scale-bounce'
,
'highlight'
,
'shake-y'
],
}
)
const
getThirdLevelKey
=
(
route
:
RouteLocationNormalizedLoadedGeneric
)
=>
{
// console.log(route, '三级路由')
return
route
.
fullPath
console
.
log
(
route
.
fullPath
,
'三级路由'
)
// console.log(route.path, 11111111111111)
// return route.fullPath // fullpath带有query参数
return
route
.
path
}
const
carouselList
=
ref
<
CarouselItemDto
[]
>
([])
...
...
@@ -364,21 +374,23 @@ const onDailySign = async () => {
const
{
data
}
=
await
getUserAccountData
()
userAccountData
.
value
=
data
ElMessage
.
success
(
'签到成功'
)
open
.
value
=
false
}
const
handleTask
=
(
item
:
TaskItemDto
)
=>
{
const
handleTask
=
async
(
item
:
TaskItemDto
)
=>
{
console
.
log
(
item
)
if
(
item
.
currentCount
===
item
.
limitCount
)
return
// 先暂时写死
if
(
item
.
svgName
===
'daily_sign'
)
{
//每日签到
handleBackTop
()
triggerAnimation
()
await
handleBackTop
()
open
.
value
=
true
// triggerAnimation()
}
else
if
(
item
.
svgName
===
'valid_comments'
)
{
// 发布评论
ElMessage
.
info
(
'快去文章评论区去发表评论吧~'
)
}
else
if
(
item
.
svgName
===
'topic_publish'
)
{
router
.
push
(
'/homePage/askTab'
)
router
.
push
(
`/homePage/askTab#tabsRef?t=${Date.now()
}
`
)
// 加一个时间戳 是因为对于不同的时间戳 用的keepalive是 path 都是同一个组件 在组件里面监听watch(()=>to.fullPath) 每次都能监听得到
}
else
if
(
item
.
svgName
===
'answer_ask'
)
{
// 回答问题
router
.
push
(
'/homePage/askTab'
)
...
...
@@ -417,9 +429,11 @@ const refreshTaskData = async (refreshRecordData = false) => {
specialTaskList
.
value
=
data
.
filter
((
item
)
=>
item
.
taskType
===
TaskTypeEnum
.
SPECIAL_TASK
)
}
// 刷新任务进度
onActivated
(()
=>
{
refreshTaskData
(
false
)
}
)
onMounted
(()
=>
{
initPage
()
}
)
...
...
src/views/pointsStore/components/exchangeContent.tsx
View file @
dedb7b4a
...
...
@@ -2,11 +2,11 @@
* 确认兑换商品的弹窗内容
*/
import
{
ShopGoodsTypeEnum
,
regionListOptions
}
from
'@/constants'
import
type
{
ExchangeGoodsParams
,
ShopItemDto
}
from
'@/api'
import
type
{
BackendShopItemDto
,
ExchangeGoodsParams
}
from
'@/api'
import
type
{
SetupContext
}
from
'vue'
type
ExchangeContentProps
=
{
item
:
ShopItemDto
item
:
Backend
ShopItemDto
modelValue
:
ExchangeGoodsParams
}
type
ExchangeContentEvents
=
{
...
...
@@ -90,7 +90,7 @@ export default function ExchangeContent(
ExchangeContent
.
props
=
{
item
:
{
type
:
Object
as
PropType
<
ShopItemDto
>
,
type
:
Object
as
PropType
<
Backend
ShopItemDto
>
,
required
:
true
,
},
modelValue
:
{
...
...
src/views/pointsStore/index.vue
View file @
dedb7b4a
This diff is collapsed.
Click to expand it.
src/views/videoDetail/components/rewardDialog.vue
View file @
dedb7b4a
...
...
@@ -29,13 +29,15 @@
<div
class=
"reward-character"
>
<img
v-if=
"option.amount === 1"
src=
"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'%3E%3C
circle cx='50' cy='50' r='45' fill='%23e0e0e0'/%3E%3Ctext x='50' y='65' font-size='40' text-anchor='middle' fill='%23666'%3E🪙%3C/text
%3E%3C/svg%3E"
src=
"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'%3E%3C
defs%3E%3ClinearGradient id='coin1' x1='0%25' y1='0%25' x2='100%25' y2='100%25'%3E%3Cstop offset='0%25' style='stop-color:%23fef3c7'/%3E%3Cstop offset='30%25' style='stop-color:%23fde68a'/%3E%3Cstop offset='70%25' style='stop-color:%23fcd34d'/%3E%3Cstop offset='100%25' style='stop-color:%23f59e0b'/%3E%3C/linearGradient%3E%3CradialGradient id='highlight1' cx='35%25' cy='35%25'%3E%3Cstop offset='0%25' style='stop-color:%23ffffff;stop-opacity:0.9'/%3E%3Cstop offset='50%25' style='stop-color:%23ffffff;stop-opacity:0.3'/%3E%3Cstop offset='100%25' style='stop-color:%23ffffff;stop-opacity:0'/%3E%3C/radialGradient%3E%3C/defs%3E%3Ccircle cx='50' cy='50' r='44' fill='none' stroke='%23f59e0b' stroke-width='3' opacity='0.4'/%3E%3Ccircle cx='50' cy='50' r='40' fill='url(%23coin1)' stroke='%23d97706' stroke-width='6'/%3E%3Ccircle cx='50' cy='50' r='33' fill='none' stroke='%23fbbf24' stroke-width='2' opacity='0.6'/%3E%3Ccircle cx='50' cy='50' r='27' fill='none' stroke='%23f59e0b' stroke-width='1.5' opacity='0.5'/%3E%3Cpath d='M50 30 L54 42 L67 42 L56 50 L60 62 L50 54 L40 62 L44 50 L33 42 L46 42 Z' fill='%23d97706' opacity='0.8'/%3E%3Ccircle cx='50' cy='50' r='40' fill='url(%23highlight1)' opacity='0.5'/
%3E%3C/svg%3E"
alt=
"1YA币"
class=
"w-full h-full"
/>
<!-- 2YA币 - 双枚金币(星形居中版) -->
<img
v-else
src=
"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'%3E%3C
circle cx='35' cy='50' r='30' fill='%23ffd700'/%3E%3Ccircle cx='65' cy='50' r='30' fill='%23ffd700'/%3E%3Ctext x='50' y='65' font-size='30' text-anchor='middle' fill='%23fff'%3E🪙%3C/text
%3E%3C/svg%3E"
src=
"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'%3E%3C
defs%3E%3ClinearGradient id='coin2' x1='0%25' y1='0%25' x2='100%25' y2='100%25'%3E%3Cstop offset='0%25' style='stop-color:%23fef3c7'/%3E%3Cstop offset='30%25' style='stop-color:%23fde68a'/%3E%3Cstop offset='70%25' style='stop-color:%23fcd34d'/%3E%3Cstop offset='100%25' style='stop-color:%23f59e0b'/%3E%3C/linearGradient%3E%3CradialGradient id='highlight2' cx='35%25' cy='35%25'%3E%3Cstop offset='0%25' style='stop-color:%23ffffff;stop-opacity:0.9'/%3E%3Cstop offset='50%25' style='stop-color:%23ffffff;stop-opacity:0.3'/%3E%3Cstop offset='100%25' style='stop-color:%23ffffff;stop-opacity:0'/%3E%3C/radialGradient%3E%3C/defs%3E%3Cg opacity='0.85'%3E%3Ccircle cx='35' cy='50' r='32' fill='none' stroke='%23f59e0b' stroke-width='2' opacity='0.4'/%3E%3Ccircle cx='35' cy='50' r='29' fill='url(%23coin2)' stroke='%23d97706' stroke-width='4'/%3E%3Ccircle cx='35' cy='50' r='24' fill='none' stroke='%23fbbf24' stroke-width='1.5' opacity='0.6'/%3E%3Ccircle cx='35' cy='50' r='20' fill='none' stroke='%23f59e0b' stroke-width='1' opacity='0.5'/%3E%3Cpath d='M35 33 L37.5 42 L47 42 L39 48 L41.5 57 L35 51 L28.5 57 L31 48 L23 42 L32.5 42 Z' fill='%23d97706' opacity='0.8'/%3E%3Ccircle cx='35' cy='50' r='29' fill='url(%23highlight2)' opacity='0.5'/%3E%3C/g%3E%3Cg%3E%3Ccircle cx='65' cy='50' r='32' fill='none' stroke='%23f59e0b' stroke-width='2' opacity='0.4'/%3E%3Ccircle cx='65' cy='50' r='29' fill='url(%23coin2)' stroke='%23d97706' stroke-width='4'/%3E%3Ccircle cx='65' cy='50' r='24' fill='none' stroke='%23fbbf24' stroke-width='1.5' opacity='0.6'/%3E%3Ccircle cx='65' cy='50' r='20' fill='none' stroke='%23f59e0b' stroke-width='1' opacity='0.5'/%3E%3Cpath d='M65 33 L67.5 42 L77 42 L69 48 L71.5 57 L65 51 L58.5 57 L61 48 L53 42 L62.5 42 Z' fill='%23d97706' opacity='0.8'/%3E%3Ccircle cx='65' cy='50' r='29' fill='url(%23highlight2)' opacity='0.5'/%3E%3C/g
%3E%3C/svg%3E"
alt=
"2YA币"
class=
"w-full h-full"
/>
...
...
@@ -58,7 +60,7 @@
</template>
<
script
setup
lang=
"ts"
>
import
{
addOrCancelArticleReward
,
getYaBiData
}
from
'@/api'
const
rewardNum
=
defineModel
<
number
>
(
'rewardNum'
,
{
required
:
true
})
interface
RewardOption
{
amount
:
number
icon
:
string
...
...
@@ -109,6 +111,7 @@ const handleConfirm = async () => {
})
ElMessage
.
success
(
'打赏成功!'
)
dialogVisible
.
value
=
false
rewardNum
.
value
+=
selectedAmount
.
value
}
defineExpose
({
...
...
src/views/videoDetail/index.vue
View file @
dedb7b4a
...
...
@@ -92,11 +92,99 @@
<span
class=
"text-base"
>
{{
videoDetail
?.
replyCount
||
0
}}
</span>
</el-button>
<!-- 打赏 -->
<el-button
text
class=
"flex items-center gap-2 transition-colors"
@
click=
"handleReward"
>
<!--
<el-icon><Star
/></el-icon>
-->
{{
videoDetail
?.
rewardNum
}}
<span
class=
"text-base"
>
打赏
</span>
<el-button
text
class=
"reward-button flex items-center gap-2 px-4 py-2 rounded-lg bg-white/40 hover:bg-white/70 backdrop-blur-sm border border-blue-100/30 hover:border-blue-200/50 transition-all duration-300 hover:scale-105 hover:shadow-lg hover:shadow-blue-100/50"
@
click=
"handleReward"
>
<!-- 金币容器 - 带多重动画 -->
<div
class=
"coin-wrapper relative"
>
<!-- 改进版金币图标 - 立体金币设计 -->
<svg
class=
"coin-icon"
viewBox=
"0 0 24 24"
width=
"18"
height=
"18"
>
<defs>
<!-- 金色渐变 -->
<linearGradient
id=
"coinGradient"
x1=
"0%"
y1=
"0%"
x2=
"100%"
y2=
"100%"
>
<stop
offset=
"0%"
style=
"stop-color: #fef3c7"
/>
<stop
offset=
"30%"
style=
"stop-color: #fde68a"
/>
<stop
offset=
"70%"
style=
"stop-color: #fcd34d"
/>
<stop
offset=
"100%"
style=
"stop-color: #f59e0b"
/>
</linearGradient>
<!-- 高光效果 -->
<radialGradient
id=
"highlight"
cx=
"35%"
cy=
"35%"
>
<stop
offset=
"0%"
style=
"stop-color: #ffffff; stop-opacity: 0.9"
/>
<stop
offset=
"50%"
style=
"stop-color: #ffffff; stop-opacity: 0.3"
/>
<stop
offset=
"100%"
style=
"stop-color: #ffffff; stop-opacity: 0"
/>
</radialGradient>
</defs>
<!-- 外圈装饰 -->
<circle
cx=
"12"
cy=
"12"
r=
"10.5"
fill=
"none"
stroke=
"#f59e0b"
stroke-width=
"0.8"
opacity=
"0.4"
/>
<!-- 金币主体 -->
<circle
cx=
"12"
cy=
"12"
r=
"9.5"
fill=
"url(#coinGradient)"
stroke=
"#d97706"
stroke-width=
"1.5"
/>
<!-- 内圈装饰线 -->
<circle
cx=
"12"
cy=
"12"
r=
"8"
fill=
"none"
stroke=
"#fbbf24"
stroke-width=
"0.5"
opacity=
"0.6"
/>
<circle
cx=
"12"
cy=
"12"
r=
"6.5"
fill=
"none"
stroke=
"#f59e0b"
stroke-width=
"0.3"
opacity=
"0.5"
/>
<!-- 星形图案(代替¥符号) -->
<path
d=
"M12 5 L13 9 L17 9 L14 11.5 L15 15 L12 13 L9 15 L10 11.5 L7 9 L11 9 Z"
fill=
"#d97706"
opacity=
"0.8"
/>
<!-- 高光 -->
<circle
cx=
"12"
cy=
"12"
r=
"9.5"
fill=
"url(#highlight)"
opacity=
"0.5"
/>
</svg>
<!-- 悬停时的光晕效果 -->
<div
class=
"coin-glow absolute inset-0 rounded-full"
></div>
<!-- 闪光粒子效果 -->
<div
class=
"sparkle sparkle-1"
></div>
<div
class=
"sparkle sparkle-2"
></div>
<div
class=
"sparkle sparkle-3"
></div>
</div>
<span
class=
"ml-2 reward-number font-medium text-gray-700"
>
{{
videoDetail
?.
rewardNum
}}
</span>
<span
class=
"ml-1 reward-text text-sm text-gray-600"
>
打赏
</span>
</el-button>
<!-- 更多 -->
<button
class=
"p-2 text-gray-400 hover:text-gray-600 hover:bg-gray-50 rounded-lg transition-all"
...
...
@@ -133,7 +221,7 @@
</div>
<Comment
ref=
"commentRef"
:id=
"videoId"
v-model:total=
"videoDetail.replyCount"
/>
<RewardDialog
ref=
"rewardDialogRef"
/>
<RewardDialog
ref=
"rewardDialogRef"
v-model:rewardNum=
"videoDetail.rewardNum"
/>
</div>
</
template
>
...
...
@@ -207,3 +295,181 @@ onMounted(async () => {
videoDetail
.
value
=
data
})
</
script
>
<
style
scoped
>
/* 按钮整体动画 */
/* 保持之前的所有动画样式不变 */
.reward-button
{
position
:
relative
;
overflow
:
visible
;
}
.coin-wrapper
{
width
:
18px
;
height
:
18px
;
display
:
inline-flex
;
align-items
:
center
;
justify-content
:
center
;
}
.coin-icon
{
font-size
:
88px
;
animation
:
coin-float
2.5s
ease-in-out
infinite
;
transition
:
all
0.3s
ease
;
}
.reward-button
:hover
.coin-icon
{
animation
:
coin-spin
0.6s
ease-in-out
,
coin-float
2.5s
ease-in-out
infinite
;
}
@keyframes
coin-float
{
0
%,
100
%
{
transform
:
translateY
(
0px
)
rotate
(
0deg
);
}
50
%
{
transform
:
translateY
(
-3px
)
rotate
(
5deg
);
}
}
@keyframes
coin-spin
{
0
%
{
transform
:
rotateY
(
0deg
);
}
100
%
{
transform
:
rotateY
(
360deg
);
}
}
.coin-glow
{
background
:
radial-gradient
(
circle
,
rgba
(
251
,
191
,
36
,
0.4
)
0%
,
transparent
70%
);
opacity
:
0
;
transform
:
scale
(
0.8
);
transition
:
all
0.3s
ease
;
}
.reward-button
:hover
.coin-glow
{
opacity
:
1
;
transform
:
scale
(
1.5
);
animation
:
glow-pulse
1.5s
ease-in-out
infinite
;
}
@keyframes
glow-pulse
{
0
%,
100
%
{
opacity
:
0.6
;
transform
:
scale
(
1.5
);
}
50
%
{
opacity
:
1
;
transform
:
scale
(
1.8
);
}
}
.sparkle
{
position
:
absolute
;
width
:
4px
;
height
:
4px
;
background
:
#fbbf24
;
border-radius
:
50%
;
opacity
:
0
;
pointer-events
:
none
;
}
.reward-button
:hover
.sparkle
{
animation
:
sparkle-burst
0.8s
ease-out
;
}
.sparkle-1
{
top
:
-2px
;
left
:
50%
;
animation-delay
:
0s
;
--tx
:
0
;
--ty
:
-10px
;
}
.sparkle-2
{
top
:
50%
;
right
:
-2px
;
animation-delay
:
0.15s
;
--tx
:
10px
;
--ty
:
0
;
}
.sparkle-3
{
bottom
:
-2px
;
left
:
50%
;
animation-delay
:
0.3s
;
--tx
:
0
;
--ty
:
10px
;
}
@keyframes
sparkle-burst
{
0
%
{
opacity
:
1
;
transform
:
translate
(
0
,
0
)
scale
(
0
);
}
50
%
{
opacity
:
1
;
}
100
%
{
opacity
:
0
;
transform
:
translate
(
var
(
--tx
),
var
(
--ty
))
scale
(
1
);
}
}
.reward-number
{
transition
:
all
0.2s
ease
;
}
.reward-button
:hover
.reward-number
{
animation
:
number-bounce
0.5s
ease
;
color
:
#3b82f6
;
}
@keyframes
number-bounce
{
0
%,
100
%
{
transform
:
translateY
(
0
);
}
25
%
{
transform
:
translateY
(
-3px
);
}
50
%
{
transform
:
translateY
(
0
);
}
75
%
{
transform
:
translateY
(
-2px
);
}
}
.reward-text
{
transition
:
all
0.3s
ease
;
}
.reward-button
:hover
.reward-text
{
color
:
#3b82f6
;
transform
:
translateX
(
2px
);
}
.reward-button
:active
{
transform
:
scale
(
0.95
);
}
.reward-button
:active
.coin-icon
{
animation
:
coin-click
0.3s
ease
;
}
@keyframes
coin-click
{
0
%
{
transform
:
scale
(
1
);
}
50
%
{
transform
:
scale
(
0.85
)
rotate
(
15deg
);
}
100
%
{
transform
:
scale
(
1
)
rotate
(
0deg
);
}
}
</
style
>
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment