Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
飞
飞鹤小程序
Project
Project
Details
Activity
Releases
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
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Commits
Issue Boards
Open sidebar
FH
飞鹤小程序
Commits
4d452811
Commit
4d452811
authored
Oct 20, 2025
by
lg
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
feat: 页面渲染引擎
parent
75cace33
Changes
9
Hide whitespace changes
Inline
Side-by-side
Showing
9 changed files
with
420 additions
and
0 deletions
+420
-0
pages.json
pages.json
+6
-0
PageRenderEngine.vue
pages/activity/engine/PageRenderEngine.vue
+50
-0
api.ts
pages/activity/engine/api.ts
+8
-0
ClickArea.vue
pages/activity/engine/components/ClickArea.vue
+73
-0
HotArea.vue
pages/activity/engine/components/HotArea.vue
+74
-0
ImageSwiper.vue
pages/activity/engine/components/ImageSwiper.vue
+64
-0
types.ts
pages/activity/engine/types.ts
+47
-0
use-md.ts
pages/activity/engine/use-md.ts
+45
-0
page.vue
pages/activity/page.vue
+53
-0
No files found.
pages.json
View file @
4d452811
...
@@ -41,6 +41,12 @@
...
@@ -41,6 +41,12 @@
"navigationStyle"
:
"custom"
"navigationStyle"
:
"custom"
}
}
},
},
{
"path"
:
"pages/activity/page"
,
"style"
:
{
"navigationStyle"
:
"custom"
}
},
{
{
"path"
:
"pages/search/search"
,
"path"
:
"pages/search/search"
,
"style"
:
{
"style"
:
{
...
...
pages/activity/engine/PageRenderEngine.vue
0 → 100644
View file @
4d452811
<
script
setup
lang=
"ts"
>
import
{
onMounted
,
ref
,
provide
,
computed
}
from
'vue'
import
{
fetchPageConfig
}
from
'./api'
import
{
PageConfig
}
from
"./types"
;
import
ImageSwiper
from
"./components/ImageSwiper.vue"
;
import
HotArea
from
"./components/HotArea.vue"
;
const
props
=
defineProps
<
{
id
:
string
debugMode
?:
boolean
}
>
()
const
refreshingPageConfig
=
ref
(
false
)
const
pageConfig
=
ref
({}
as
PageConfig
)
const
debugMode
=
computed
(()
=>
props
.
debugMode
)
async
function
refreshPageConfig
()
{
refreshingPageConfig
.
value
=
true
const
resp
=
await
fetchPageConfig
(
props
.
id
)
pageConfig
.
value
=
JSON
.
parse
(
resp
.
data
.
content
)
refreshingPageConfig
.
value
=
false
console
.
log
(
pageConfig
.
value
)
}
onMounted
(
async
()
=>
{
await
refreshPageConfig
()
})
provide
(
'debugMode'
,
debugMode
)
</
script
>
<
template
>
<view
v-if=
"!refreshingPageConfig"
class=
"page-render-engine"
>
<template
v-for=
"(component, index) in pageConfig.components"
:key=
"'comp-' + index"
>
<HotArea
v-if=
"component.name === 'HotArea'"
:properties=
"component.props"
/>
</
template
>
</view>
<view
v-else
>
加载中...
</view>
</template>
<
style
scoped
lang=
"less"
>
.page-render-engine {
background-repeat: no-repeat;
}
</
style
>
\ No newline at end of file
pages/activity/engine/api.ts
0 → 100644
View file @
4d452811
import
requestModule
from
'@/api/request.js'
import
{
CodeResponse
,
ThematicPageType
}
from
"./types"
;
const
{
api
}
=
requestModule
export
function
fetchPageConfig
(
id
:
string
)
{
return
api
.
get
(
'/c/thematicPage/data'
,
{
id
})
as
Promise
<
CodeResponse
<
ThematicPageType
>>
}
pages/activity/engine/components/ClickArea.vue
0 → 100644
View file @
4d452811
<
script
setup
lang=
"ts"
>
import
{
computed
,
inject
}
from
'vue'
import
{
jump
}
from
'@/utils'
import
{
MdConfig
}
from
"../types"
;
import
{
useMD
}
from
"../use-md"
;
const
ratio
=
2
const
props
=
defineProps
<
{
properties
?:
{
area
?:
{
x
:
number
,
y
:
number
,
width
:
number
,
height
:
number
}
link
?:
{
type
:
number
,
url
:
string
,
extra
:
any
}
mdConfig
?:
MdConfig
needLogin
?:
boolean
}
openType
?:
string
isLogin
?:
boolean
}
>
()
const
debugMode
=
inject
(
'debugMode'
,
false
)
const
style
=
computed
(()
=>
{
const
area
=
props
.
properties
.
area
return
{
left
:
area
.
x
*
ratio
+
'rpx'
,
top
:
area
.
y
*
ratio
+
'rpx'
,
width
:
area
.
width
*
ratio
+
'rpx'
,
height
:
area
.
height
*
ratio
+
'rpx'
,
}
})
const
{
logClick
}
=
useMD
(
props
.
properties
.
mdConfig
||
{})
const
emit
=
defineEmits
<
{
(
e
:
'click'
,
e
:
any
):
void
(
e
:
'getphonenumber'
,
e
:
any
):
void
}
>
()
function
onClick
(
e
)
{
emit
(
'click'
,
e
)
if
((
!
props
.
properties
.
needLogin
||
props
.
isLogin
)
&&
props
.
properties
.
link
)
{
jump
(
props
.
properties
.
link
)
}
logClick
()
}
</
script
>
<
template
>
<button
class=
"click-area"
:class=
"
{'debug-mode': debugMode}" :style="style"
:open-type="openType ?? (properties.needLogin
&&
!isLogin ? 'getPhoneNumber' : undefined)"
@click="onClick"
@getphonenumber="(e) => emit('getphonenumber', e)"
/>
</
template
>
<
style
scoped
lang=
"less"
>
.click-area {
position: absolute;
border: none;
border-radius: 0;
background-color: transparent;
&.debug-mode {
background-color: rgba(255, 0, 0, 0.3);
}
&::after {
border: none;
}
}
</
style
>
\ No newline at end of file
pages/activity/engine/components/HotArea.vue
0 → 100644
View file @
4d452811
<
script
setup
lang=
"ts"
>
import
{
inject
}
from
'vue'
import
ClickArea
from
"./ClickArea.vue"
;
import
{
JumpType
}
from
"../types"
;
export
interface
ClickAreaType
{
x
:
number
y
:
number
width
:
number
height
:
number
link
:
{
id
:
string
type
:
string
url
:
string
label
:
string
value
:
string
}
}
const
props
=
defineProps
<
{
properties
:
{
imageUrl
:
string
list
:
ClickAreaType
[]
}
}
>
()
const
debugMode
=
inject
(
'debugMode'
,
false
)
function
transformClickAreaProperties
(
item
:
ClickAreaType
)
{
let
link
;
if
(
item
.
link
){
switch
(
item
.
link
.
type
)
{
case
'MINI_APP_THIRD'
:
link
=
{
type
:
JumpType
.
MINI
,
url
:
item
.
link
.
url
,
extra
:
{
appId
:
item
.
link
.
value
,
}
}
break
}
}
return
{
area
:
{
x
:
item
.
x
,
y
:
item
.
y
,
width
:
item
.
width
,
height
:
item
.
height
,
},
link
,
}
}
</
script
>
<
template
>
<view
class=
"hot-area"
>
<image
:src=
"properties.imageUrl"
class=
"hot-area-bg"
mode=
"widthFix"
/>
<ClickArea
v-for=
"item in properties.list"
:key=
"item.id"
:properties=
"transformClickAreaProperties(item)"
/>
</view>
</
template
>
<
style
scoped
lang=
"less"
>
.hot-area {
position: relative;
&-bg {
width: 100%;
display: block;
}
}
</
style
>
\ No newline at end of file
pages/activity/engine/components/ImageSwiper.vue
0 → 100644
View file @
4d452811
<
script
setup
lang=
"ts"
>
import
{
onMounted
,
ref
}
from
'vue'
import
{
LinkType
,
MdConfig
}
from
"../types"
import
{
useMD
}
from
"../use-md"
import
{
jump
}
from
'@/utils'
export
interface
ImageSwiperItem
{
image
:
string
link
?:
LinkType
}
const
props
=
defineProps
<
{
properties
?:
{
autoplay
?:
boolean
interval
?:
number
duration
?:
number
circular
?:
boolean
indicatorDots
?:
boolean
items
:
ImageSwiperItem
[]
}
style
?:
Record
<
string
,
any
>
}
>
()
const
mds
=
ref
([])
onMounted
(()
=>
{
mds
.
value
=
props
.
properties
.
items
.
map
(
item
=>
{
const
md
=
useMD
(
item
.
mdConfig
||
{},
false
)
md
.
logExposure
()
return
md
})
})
function
onClickItem
(
item
:
ImageSwiperItem
,
index
:
number
)
{
if
(
item
.
link
)
{
jump
(
item
.
link
)
}
mds
.
value
[
index
].
logClick
()
}
</
script
>
<
template
>
<swiper
class=
"comp-swiper"
:style=
"style"
:indicator-dots=
"properties.indicatorDots"
:autoplay=
"properties.autoplay"
:circular=
"properties.circular"
:interval=
"properties.interval"
:duration=
"500"
>
<swiper-item
v-for=
"(item, index) in properties.items"
:key=
"index"
@
click=
"onClickItem(item, index)"
>
<image
class=
"comp-swiper-item"
:src=
"item.image"
mode=
"aspectFill"
></image>
</swiper-item>
</swiper>
</
template
>
<
style
scoped
lang=
"less"
>
.comp-swiper {
&-item {
width: 100%;
height: 100%;
}
}
</
style
>
\ No newline at end of file
pages/activity/engine/types.ts
0 → 100644
View file @
4d452811
export
enum
JumpType
{
INNER
=
1
,
// 内部小程序页面跳转
MINI
=
2
,
// 其他小程序页面跳转
H5
=
3
,
// https 网络连接跳转
}
export
interface
LinkType
{
type
:
JumpType
url
:
string
extra
?:
Record
<
string
,
any
>
}
export
interface
MdConfig
{
pageName
:
string
componentName
:
string
componentContent
:
string
logExposure
?:
boolean
logClick
?:
boolean
}
export
interface
CodeResponse
<
T
>
{
code
:
number
message
:
string
data
:
T
success
:
boolean
ok
:
boolean
}
export
interface
ComponentType
{
name
:
string
props
:
Record
<
string
,
any
>
styles
:
Record
<
string
,
any
>
}
export
interface
ThematicPageType
{
content
:
string
}
export
interface
PageConfig
{
title
:
string
shareConfig
:
{
title
:
string
imageUrl
:
string
}
components
:
ComponentType
[]
styles
:
Record
<
string
,
any
>
}
pages/activity/engine/use-md.ts
0 → 100644
View file @
4d452811
import
{
onMounted
}
from
'vue'
import
md
from
'@/md.js'
import
{
MdConfig
}
from
"./types"
;
export
function
useMD
(
mdConfig
:
MdConfig
,
autoLogExposure
=
true
)
{
function
logExposure
()
{
const
{
pageName
,
componentName
,
componentContent
}
=
mdConfig
||
{}
if
(
!
pageName
||
!
componentName
||
!
componentContent
)
{
return
}
md
.
sensorComponentLogTake
({
xcxComponentExposure
:
"true"
,
pageName
,
componentName
,
componentContent
,
})
}
function
logClick
()
{
const
{
pageName
,
componentName
,
componentContent
}
=
mdConfig
||
{}
if
(
!
pageName
||
!
componentName
||
!
componentContent
)
{
return
}
md
.
sensorComponentLogTake
({
xcxComponentClick
:
"true"
,
pageName
,
componentName
,
componentContent
,
})
}
if
(
autoLogExposure
)
{
onMounted
(()
=>
{
if
(
mdConfig
?.
logExposure
)
{
logExposure
()
}
})
}
return
{
logClick
,
logExposure
,
}
}
pages/activity/page.vue
0 → 100644
View file @
4d452811
<
script
setup
lang=
"ts"
>
import
{
ref
}
from
'vue'
import
PageRenderEngine
from
"./engine/PageRenderEngine.vue"
;
import
{
onLoad
}
from
"@dcloudio/uni-app"
;
const
pageOptions
=
ref
({})
const
pageId
=
ref
<
string
>
()
const
debugMode
=
ref
(
false
)
function
navBack
()
{
try
{
uni
.
navigateBack
({
delta
:
1
,
success
:
()
=>
{
console
.
log
(
'返回成功'
)
},
fail
:
()
=>
{
// 如果返回失败,跳转到首页
uni
.
redirectTo
({
url
:
'/pages/index/index'
})
}
})
}
catch
(
error
)
{
console
.
log
(
'返回失败:'
,
error
)
uni
.
redirectTo
({
url
:
'/pages/index/index'
})
}
}
onLoad
((
options
)
=>
{
pageOptions
.
value
=
options
pageId
.
value
=
options
.
id
debugMode
.
value
=
options
.
debugMode
})
</
script
>
<
template
>
<PageRenderEngine
:id=
"pageId"
:debug-mode=
"debugMode"
/>
<image
class=
"btn-back"
src=
"@/assets/images/back-btn.png"
mode=
"aspectFit"
@
click=
"navBack"
/>
</
template
>
<
style
scoped
lang=
"less"
>
.btn-back {
position: fixed;
left: 20rpx;
top: 70rpx;
width: 60rpx;
height: 60rpx;
}
</
style
>
\ No newline at end of file
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