Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
C
corporate-culture-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
王立鹏
corporate-culture-qd
Commits
0519d87a
Commit
0519d87a
authored
Apr 02, 2026
by
lijiabin
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
【需求 20520】 feat: 完成前台大转盘功能
parent
feb96717
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
193 additions
and
117 deletions
+193
-117
LuckyWheel.vue
src/components/common/LuckyWheel/components/LuckyWheel.vue
+104
-109
index.vue
src/components/common/LuckyWheel/index.vue
+0
-0
index.vue
src/components/common/MessageBox/index.vue
+4
-1
index.vue
src/views/homePage/index.vue
+22
-4
selfActivity.vue
src/views/userPage/components/selfActivity.vue
+63
-3
No files found.
src/components/common/LuckyWheel/
Comp
.vue
→
src/components/common/LuckyWheel/
components/LuckyWheel
.vue
View file @
0519d87a
<
template
>
<div
class=
"lucky-wheel-wrapper"
>
<div
class=
"lucky-wheel-wrapper"
:class=
"
{ 'scale-80': smallerThanXl }"
>
<LuckyWheel
ref=
"myLucky"
width=
"260px"
height=
"260px"
:blocks=
"blocks"
:buttons=
"buttons"
:prizes=
"
p
rizes"
:prizes=
"
computedP
rizes"
:default-config=
"defaultConfig"
:default-style=
"defaultStyle"
@
start=
"startCallback"
@
end=
"endCallback"
/>
<!-- 自定义指针 -->
...
...
@@ -21,7 +20,7 @@
class=
"go-btn"
:class=
"
{ 'is-spinning': isSpinning }"
:disabled="isSpinning"
@click="
startCallba
ck"
@click="
popCli
ck"
>
<span
class=
"go-text"
>
GO
</span>
</button>
...
...
@@ -29,11 +28,22 @@
</
template
>
<
script
lang=
"ts"
setup
>
import
{
ref
}
from
'vue'
import
{
LuckyWheel
}
from
'@lucky-canvas/vue'
import
ringTexture
from
'@/assets/img/lucky-wheel-outer-ring.svg'
import
type
{
WheelPrizeItemDto
,
LuckWheelResultDto
}
from
'@/api'
import
{
participateLuckyWheel
,
getWheelPrizeList
}
from
'@/api'
import
{
breakpointsTailwind
,
useBreakpoints
}
from
'@vueuse/core'
import
{
useMessageBox
}
from
'@/hooks'
const
{
confirm
}
=
useMessageBox
()
const
breakpoints
=
useBreakpoints
(
breakpointsTailwind
)
const
smallerThanXl
=
breakpoints
.
smaller
(
'xl'
)
const
wheelPrizeList
=
ref
<
WheelPrizeItemDto
[]
>
([])
const
emit
=
defineEmits
<
{
(
e
:
'prize'
,
prize
:
Record
<
string
,
unknown
>
):
void
handlePrizeResult
:
[
LuckWheelResultDto
]
}
>
()
const
myLucky
=
ref
<
InstanceType
<
typeof
LuckyWheel
>>
()
...
...
@@ -47,9 +57,9 @@ const defaultConfig = {
const
defaultStyle
=
{
fontColor
:
'#333'
,
fontSize
:
'12px'
,
fontWeight
:
'bold'
,
lineHeight
:
'14px'
,
lengthLimit
:
'60%'
,
}
const
blocks
=
[
...
...
@@ -69,102 +79,37 @@ const blocks = [
{
padding
:
'3px'
,
background
:
'#7b6fef'
},
]
const
prizes
=
[
{
background
:
'#ffffff'
,
fonts
:
[{
text
:
'谢谢参与'
,
top
:
'12%'
,
fontSize
:
'11px'
,
fontColor
:
'#6858ec'
}],
imgs
:
[
{
src
:
'https://cdn.jsdelivr.net/gh/twitter/twemoji@14.0.2/assets/72x72/1f60a.png'
,
width
:
'30%'
,
top
:
'38%'
,
},
],
},
{
background
:
'#f3f0ff'
,
fonts
:
[{
text
:
'10个京豆'
,
top
:
'12%'
,
fontSize
:
'11px'
,
fontColor
:
'#e5a012'
}],
imgs
:
[
{
src
:
'https://cdn.jsdelivr.net/gh/twitter/twemoji@14.0.2/assets/72x72/1f61d.png'
,
width
:
'30%'
,
top
:
'38%'
,
},
],
},
{
background
:
'#ffffff'
,
fonts
:
[{
text
:
'5个京豆'
,
top
:
'12%'
,
fontSize
:
'11px'
,
fontColor
:
'#e5a012'
}],
imgs
:
[
{
src
:
'https://cdn.jsdelivr.net/gh/twitter/twemoji@14.0.2/assets/72x72/1f929.png'
,
width
:
'30%'
,
top
:
'38%'
,
},
],
},
{
background
:
'#f3f0ff'
,
fonts
:
[{
text
:
'1个京豆'
,
top
:
'12%'
,
fontSize
:
'11px'
,
fontColor
:
'#e5a012'
}],
imgs
:
[
{
src
:
'https://cdn.jsdelivr.net/gh/twitter/twemoji@14.0.2/assets/72x72/1f60e.png'
,
width
:
'30%'
,
top
:
'38%'
,
},
],
},
{
background
:
'#ffffff'
,
fonts
:
[{
text
:
'谢谢参与'
,
top
:
'12%'
,
fontSize
:
'11px'
,
fontColor
:
'#6858ec'
}],
imgs
:
[
{
src
:
'https://cdn.jsdelivr.net/gh/twitter/twemoji@14.0.2/assets/72x72/1f385.png'
,
width
:
'30%'
,
top
:
'38%'
,
},
],
},
{
background
:
'#f3f0ff'
,
fonts
:
[{
text
:
'10个京豆'
,
top
:
'12%'
,
fontSize
:
'11px'
,
fontColor
:
'#e5a012'
}],
imgs
:
[
{
src
:
'https://cdn.jsdelivr.net/gh/twitter/twemoji@14.0.2/assets/72x72/1f917.png'
,
width
:
'30%'
,
top
:
'38%'
,
},
],
},
{
background
:
'#ffffff'
,
fonts
:
[{
text
:
'5个京豆'
,
top
:
'12%'
,
fontSize
:
'11px'
,
fontColor
:
'#e5a012'
}],
imgs
:
[
{
src
:
'https://cdn.jsdelivr.net/gh/twitter/twemoji@14.0.2/assets/72x72/1f618.png'
,
width
:
'30%'
,
top
:
'38%'
,
},
],
},
{
background
:
'#f3f0ff'
,
fonts
:
[{
text
:
'1个京豆'
,
top
:
'12%'
,
fontSize
:
'11px'
,
fontColor
:
'#e5a012'
}],
imgs
:
[
{
src
:
'https://cdn.jsdelivr.net/gh/twitter/twemoji@14.0.2/assets/72x72/1f970.png'
,
width
:
'30%'
,
top
:
'38%'
,
},
],
},
]
const
computedPrizes
=
computed
(()
=>
{
const
width
=
wheelPrizeList
.
value
.
length
>=
6
?
'37%'
:
'30%'
return
wheelPrizeList
.
value
.
map
((
item
:
WheelPrizeItemDto
,
index
:
number
)
=>
{
return
{
background
:
index
%
2
===
0
?
'#ffffff'
:
'#f3f0ff'
,
fonts
:
[
{
text
:
item
.
name
,
top
:
'8%'
,
fontSize
:
'10px'
,
fontColor
:
index
%
2
===
0
?
'#6858ec'
:
'#e5a012'
,
lineClamp
:
1
,
},
],
imgs
:
[
{
src
:
item
.
imageUrl
,
width
,
top
:
'35%'
,
},
],
id
:
item
.
id
,
}
})
})
const
buttons
=
[
{
radius
:
'
30
%'
,
background
:
'#fce4e4'
},
{
radius
:
'2
5
%'
,
background
:
'#f75a5a'
},
{
radius
:
'
25
%'
,
background
:
'#fce4e4'
},
{
radius
:
'2
0
%'
,
background
:
'#f75a5a'
},
{
radius
:
'
22
%'
,
radius
:
'
18
%'
,
background
:
'#e63939'
,
pointer
:
false
,
fonts
:
[{
text
:
''
,
top
:
'-14px'
}],
...
...
@@ -173,20 +118,70 @@ const buttons = [
const
isSpinning
=
ref
(
false
)
const
startCallback
=
()
=>
{
let
resultPrize
:
null
|
LuckWheelResultDto
=
null
const
startCallback
=
async
()
=>
{
if
(
isSpinning
.
value
)
return
isSpinning
.
value
=
true
myLucky
.
value
?.
play
()
setTimeout
(
async
()
=>
{
const
{
data
}
=
await
participateLuckyWheel
()
const
idx
=
computedPrizes
.
value
.
findIndex
((
item
)
=>
item
.
id
===
data
.
prizeId
)
myLucky
.
value
?.
stop
(
idx
)
resultPrize
=
data
},
1000
)
}
// 获取最新的奖品列表 对比 是否更新了
const
popClick
=
async
()
=>
{
if
(
isSpinning
.
value
)
return
const
{
data
}
=
await
getWheelPrizeList
()
const
newWheelPrizeList
=
data
const
oldWheelPrizeList
=
wheelPrizeList
.
value
// 暂时只需要对比 1长度不一致 需要更新 2如果长度一样 如果有一组的名字或者图片 不一样 需要更新
let
shouldUpdate
=
false
if
(
newWheelPrizeList
.
length
!==
oldWheelPrizeList
.
length
)
{
shouldUpdate
=
true
}
else
{
newWheelPrizeList
.
forEach
((
item
:
WheelPrizeItemDto
,
index
:
number
)
=>
{
if
(
item
.
name
!==
oldWheelPrizeList
[
index
]?.
name
||
item
.
imageUrl
!==
oldWheelPrizeList
[
index
]?.
imageUrl
)
{
shouldUpdate
=
true
}
})
}
if
(
shouldUpdate
)
{
// 给用户提示 奖池更新了 请重新点击
await
confirm
({
title
:
'检测到后台奖池有更新'
,
message
:
'请重新点击按钮开始抽奖'
,
type
:
'warning'
,
showCancelButton
:
false
,
})
wheelPrizeList
.
value
=
newWheelPrizeList
}
else
{
startCallback
()
}
}
const
endCallback
=
()
=>
{
setTimeout
(()
=>
{
const
index
=
Math
.
floor
(
Math
.
random
()
*
prizes
.
length
)
myLucky
.
value
?.
stop
(
index
)
},
30
00
)
isSpinning
.
value
=
false
emit
(
'handlePrizeResult'
,
resultPrize
as
LuckWheelResultDto
)
},
5
00
)
}
const
endCallback
=
(
prize
:
Record
<
string
,
unknown
>
)
=>
{
isSpinning
.
value
=
false
emit
(
'prize'
,
prize
)
const
initWheelPrizeList
=
async
(
)
=>
{
const
{
data
}
=
await
getWheelPrizeList
()
wheelPrizeList
.
value
=
data
}
onMounted
(()
=>
{
initWheelPrizeList
()
})
</
script
>
<
style
scoped
>
...
...
@@ -206,8 +201,8 @@ const endCallback = (prize: Record<string, unknown>) => {
top
:
50%
;
left
:
50%
;
transform
:
translate
(
-50%
,
-50%
);
width
:
6
0px
;
height
:
6
0px
;
width
:
5
0px
;
height
:
5
0px
;
border-radius
:
50%
;
border
:
3px
solid
#ff8a80
;
background
:
linear-gradient
(
145deg
,
#ff5252
,
#d32f2f
);
...
...
src/components/common/LuckyWheel/index.vue
View file @
0519d87a
This diff is collapsed.
Click to expand it.
src/components/common/MessageBox/index.vue
View file @
0519d87a
...
...
@@ -66,7 +66,10 @@
</p>
<!-- Actions -->
<div
class=
"mt-5 grid grid-cols-2 gap-2.5"
>
<div
class=
"mt-5 grid grid-cols-2 gap-2.5"
:class=
"
{ 'grid-cols-1!': !showCancelButton || !showConfirmButton }"
>
<button
v-if=
"showCancelButton"
type=
"button"
...
...
src/views/homePage/index.vue
View file @
0519d87a
...
...
@@ -71,7 +71,7 @@
</div>
</div>
</div>
<div
class=
"right flex-col gap-3
flex basis-1/4 xl:basis-1/4 min-w-0
"
>
<div
class=
"right flex-col gap-3
basis-1/4 xl:basis-1/4 min-w-0 hidden sm:flex
"
>
<!-- 等级等相关信息 -->
<div
ref=
"levelContainerRef"
...
...
@@ -350,14 +350,20 @@
</div>
<!-- 大转盘 -->
<div
class=
"lottery-container common-box rounded-lg bg-#F5F0FF"
>
<div
class=
"flex items-center gap-2 mb-4"
>
<div
v-if=
"wheelConfig?.isActivityActive"
class=
"lottery-container common-box rounded-lg bg-#F5F0FF"
>
<div
class=
"flex items-center gap-2 xl:mb-4"
>
<div
class=
"w-1 h-4 bg-gradient-to-b from-violet-500 to-purple-500 rounded-full"
></div>
<h1
class=
"text-sm sm:text-base font-bold"
>
大转盘
</h1>
</div>
<div
class=
"flex justify-center"
>
<LuckyWheel
/>
</div>
<div
class=
"flex items-center justify-center text-sm text-gray-500 xl:mt-4 px-1 truncate"
>
每次抽奖
{{
wheelConfig
?.
costYaCoin
}}
YA币
</div>
</div>
</div>
</div>
...
...
@@ -384,6 +390,7 @@ import {
getRecordData
,
getUserDailyLotteryInfo
,
userJoinLottery
,
getWheelConfig
,
}
from
'@/api'
import
{
TaskTypeEnum
,
TaskDateLimitTypeText
,
ArticleTypeEnum
}
from
'@/constants'
import
type
{
...
...
@@ -392,6 +399,7 @@ import type {
UserAccountDataDto
,
UserRecordDataDto
,
DailyLotteryInfo
,
WheelConfigDto
,
}
from
'@/api'
import
{
TABS_REF_KEY
,
levelListOptions
}
from
'@/constants'
import
{
useScrollTop
}
from
'@/hooks'
...
...
@@ -532,6 +540,11 @@ const handleLottery = async () => {
push.success('参与每日抽奖成功!')
}
/**
* 大转盘
*/
const wheelConfig = ref<WheelConfigDto>(null)
const handleTask = async (item: TaskItemDto) => {
if (item.currentCount === item.limitCount) return
// 先暂时写死
...
...
@@ -584,7 +597,8 @@ const initPage = () => {
getUserAccountData(),
getRecordData(),
getUserDailyLotteryInfo(),
]).then(([r1, r2, r3, r4]) => {
getWheelConfig(),
]).then(([r1, r2, r3, r4, r5]) => {
if (r1.status === 'fulfilled') {
carouselList.value = r1.value.data
}
...
...
@@ -598,6 +612,9 @@ const initPage = () => {
if (r4.status === 'fulfilled') {
lotteryPrizesDetail.value = r4.value.data
}
if (r5.status === 'fulfilled') {
wheelConfig.value = r5.value.data
}
})
}
...
...
@@ -623,6 +640,7 @@ onActivated(async () => {
refreshTaskData(false)
refreshUserAccountData()
getLotteryPrizesDetail()
if (route.fullPath.includes('#levelContainerRef')) {
await handleBackTop()
open.value = true
...
...
src/views/userPage/components/selfActivity.vue
View file @
0519d87a
...
...
@@ -66,10 +66,10 @@
</
template
>
<
script
lang=
"tsx"
setup
>
import
{
getSelfAuctionRecord
,
getUserLotteryRecordList
}
from
'@/api'
import
{
getSelfAuctionRecord
,
getUserLotteryRecordList
,
getUserWheelRecordList
}
from
'@/api'
import
{
usePageSearch
}
from
'@/hooks'
import
{
ActivityTypeEnum
}
from
'@/constants/enums'
import
type
{
UserLotteryRecordItemDto
}
from
'@/api/dailyLottery/types
'
import
type
{
UserLotteryRecordItemDto
,
UserWheelRecordItemDto
}
from
'@/api
'
const
EmptyComp
=
()
=>
(
<
div
class
=
"flex flex-col items-center justify-center h-64"
>
...
...
@@ -182,6 +182,56 @@ const activityTypeListOptions = [
),
refresh
:
()
=>
refresh2
(),
},
{
label
:
'大转盘'
,
value
:
ActivityTypeEnum
.
WHEEL
,
component
:
()
=>
(
<>
{
!
list3
.
value
.
length
?
(
<
EmptyComp
/>
)
:
(
<>
<
div
class
=
"space-y-4"
>
<
el
-
table
height
=
"500"
data
=
{
list3
.
value
}
stripe
border
loading
=
{
loading3
.
value
}
>
<
el
-
table
-
column
prop
=
"prizeName"
label
=
"名称"
>
{({
row
}:
{
row
:
UserWheelRecordItemDto
})
=>
(
<
div
>
{
row
.
prizeName
??
`谢谢参与(
${
row
.
blessingText
}
)`
}
<
/div
>
)}
<
/el-table-column
>
<
el
-
table
-
column
prop
=
"createdAtStr"
label
=
"参与时间"
/>
<
el
-
table
-
column
prop
=
"isLotteryDone"
label
=
"是否中奖"
>
{({
row
}:
{
row
:
UserWheelRecordItemDto
})
=>
(
<
div
>
{
row
.
isWin
?
(
<
span
class
=
"text-green-500"
>
是
<
/span
>
)
:
(
<
span
class
=
"text-red-500"
>
否
<
/span
>
)}
<
/div
>
)}
<
/el-table-column
>
<
/el-table
>
<
/div
>
<
div
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-2"
>
<
el
-
pagination
v
-
model
:
current
-
page
=
{
searchParams3
.
value
.
current
}
v
-
model
:
page
-
size
=
{
searchParams3
.
value
.
size
}
onSizeChange
=
{
changePageSize3
}
onCurrentChange
=
{
goToPage3
}
page
-
sizes
=
{[
10
,
20
,
30
,
40
]}
layout
=
"prev, pager, next, jumper, total"
total
=
{
total3
.
value
}
class
=
"custom-pagination"
/>
<
/div
>
<
/div
>
<
/
>
)}
<
/
>
),
refresh
:
()
=>
refresh3
(),
},
]
const
tab
=
ref
(
activityTypeListOptions
[
0
]
!
.
value
)
...
...
@@ -212,7 +262,17 @@ const {
}
=
usePageSearch
(
getUserLotteryRecordList
,
{
immediate
:
false
,
})
const
{
list
:
list3
,
loading
:
loading3
,
searchParams
:
searchParams3
,
total
:
total3
,
refresh
:
refresh3
,
goToPage
:
goToPage3
,
changePageSize
:
changePageSize3
,
}
=
usePageSearch
(
getUserWheelRecordList
,
{
immediate
:
false
,
})
const
refresh
=
()
=>
{
activityTypeListOptions
.
find
((
item
)
=>
item
.
value
===
tab
.
value
)?.
refresh
?.()
}
...
...
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