Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
O
offical_web
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
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
zhangjie
offical_web
Commits
a311f76a
Commit
a311f76a
authored
Nov 19, 2025
by
wusiyi
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
feat: 帮助页优化
parent
bbb8f250
Hide whitespace changes
Inline
Side-by-side
Showing
10 changed files
with
334 additions
and
99 deletions
+334
-99
src/components/headHelper.vue
+90
-17
src/router/index.js
+7
-0
src/store/path.js
+4
-27
src/views/help/components/MenuItem.vue
+42
-6
src/views/help/components/bread.vue
+10
-30
src/views/help/components/homeGuides.vue
+3
-7
src/views/help/components/picTutorial.vue
+5
-2
src/views/help/helpPage.vue
+2
-3
src/views/help/menuConfig.js
+125
-4
src/views/help/sideNav.vue
+46
-3
No files found.
src/components/headHelper.vue
View file @
a311f76a
...
...
@@ -10,14 +10,6 @@
</div>
<div
class=
"flex items-center justify-center pb-2"
>
<div
v-for=
"nav in navs"
:key=
"nav.path"
class=
"flex flex-col items-center justify-center mr-8"
>
<a
class=
"nav-link relative"
:index=
"nav.path"
:href=
"nav.path"
>
{{
nav
.
name
}}
</a>
</div>
<div
class=
"mr-2 flex items-center justify-center gap-2"
v-if=
"!userInfo"
>
<div
...
...
@@ -62,10 +54,10 @@
</div>
<div
class=
"flex items-center justify-center gap-2"
>
<div
class=
"text-sm"
v-if=
"!userInfo"
>
<span
@
click=
"login"
class=
"border-r
-2
border-solid border-gray-200"
>
<span
@
click=
"login"
class=
"border-r border-solid border-gray-200"
>
登录
</span>
<span
@
click=
"register"
>
注册
</span>
<span
@
click=
"register"
class=
"ml-1"
>
注册
</span>
</div>
<div
class=
"text-sm flex items-center justify-center"
v-else
>
<span>
{{
userInfo
.
companyName
}}
</span>
...
...
@@ -90,11 +82,33 @@
</div>
</div>
</div>
<transition
name=
"head-helper-overlay"
>
<div
v-show=
"showing"
class=
"head-helper-overlay"
>
<div
class=
"flex items-center justify-between p-2.5"
>
<img
src=
"../assets/logo.png"
class=
"w-32"
/>
<i
class=
"el-icon-close text-xl font-bold cursor-pointer"
@
click=
"toggleShow"
/>
</div>
<el-menu
:unique-opened=
"true"
:default-active=
"$route.path"
:default-openeds=
"openedMenus"
>
<menu-item
v-for=
"item in menuList"
:key=
"item.id"
:menu-item=
"item"
@
close-overlay=
"toggleShow"
/>
</el-menu>
</div>
</transition>
</div>
</
template
>
<
script
>
import
{
Icon
}
from
'@iconify/vue2'
import
{
mapMutations
}
from
'vuex'
import
MenuItem
from
'../views/help/components/MenuItem.vue'
import
{
menuList
}
from
'../views/help/menuConfig'
export
default
{
...
...
@@ -104,6 +118,7 @@ export default {
},
components
:
{
Icon
,
MenuItem
,
},
props
:
{
userInfo
:
{
...
...
@@ -112,14 +127,50 @@ export default {
},
data
()
{
return
{
navs
:
[
{
name
:
'首页'
,
path
:
'/'
,
},
],
showing
:
false
,
menuList
,
}
},
computed
:
{
// 需要展开的父级菜单
openedMenus
()
{
const
currentPath
=
this
.
$route
.
path
// 递归查找匹配的菜单项及其父级菜单
const
findMenuItem
=
(
items
,
parentIds
=
[])
=>
{
for
(
const
item
of
items
)
{
// 构建菜单项的完整路径(与 MenuItem.vue 中的逻辑保持一致)
let
itemPath
=
''
if
(
item
.
url
)
{
// 如果有 url,路径格式为 /help/path/url
itemPath
=
item
.
path
?
`/help/
${
item
.
path
}
/
${
item
.
url
}
`
:
`/help/
${
item
.
url
}
`
}
else
if
(
item
.
path
)
{
// 如果只有 path,路径格式为 /help/path
itemPath
=
`/help/
${
item
.
path
}
`
}
// 如果当前路径完全匹配
if
(
itemPath
&&
itemPath
===
currentPath
)
{
return
parentIds
}
// 如果有子菜单,递归查找
if
(
item
.
children
&&
item
.
children
.
length
>
0
)
{
const
newParentIds
=
[...
parentIds
,
String
(
item
.
id
)]
const
result
=
findMenuItem
(
item
.
children
,
newParentIds
)
if
(
result
!==
null
)
{
return
result
}
}
}
return
null
}
const
result
=
findMenuItem
(
this
.
menuList
)
return
result
||
[]
},
},
methods
:
{
...
mapMutations
([
'setUserInfo'
,
'setShopKey'
]),
...
...
@@ -171,7 +222,7 @@ export default {
window
.
location
.
reload
()
},
toggleShow
()
{
console
.
log
(
menuList
,
'???'
)
this
.
showing
=
!
this
.
showing
},
},
}
...
...
@@ -219,4 +270,26 @@ export default {
color
:
var
(
--secondary-color
);
}
}
.head-helper-overlay-enter-active
,
.head-helper-overlay-leave-active
{
transition
:
0.3s
;
}
.head-helper-overlay-enter
,
.head-helper-overlay-leave-to
{
transform
:
translateX
(
100%
);
}
.head-helper-overlay
{
position
:
fixed
;
top
:
0
;
right
:
0
;
width
:
100vw
;
height
:
100vh
;
z-index
:
99999
;
background-color
:
#fff
;
display
:
flex
;
flex-direction
:
column
;
}
</
style
>
src/router/index.js
View file @
a311f76a
...
...
@@ -100,6 +100,13 @@ const routes = [
props
:
true
,
},
{
path
:
'videoTutorial/:id'
,
name
:
'VideoTutorial'
,
component
:
(
resolve
)
=>
require
([
'../views/help/components/picTutorial.vue'
],
resolve
),
props
:
true
,
},
{
path
:
'artical1'
,
name
:
'Artical1'
,
component
:
(
resolve
)
=>
...
...
src/store/path.js
View file @
a311f76a
// 默认的帮助中心路径
const
DEFAULT_HELP_CENTER
=
{
name
:
'帮助中心'
,
path
:
'index'
}
// 确保数组第一个元素是帮助中心
const
ensureHelpCenterFirst
=
(
pathNames
)
=>
{
if
(
!
pathNames
||
pathNames
.
length
===
0
)
{
return
[
DEFAULT_HELP_CENTER
]
}
// 如果第一个元素已经是帮助中心,直接返回
if
(
pathNames
[
0
].
name
===
'帮助中心'
)
{
return
pathNames
}
const
filtered
=
pathNames
.
filter
((
item
)
=>
item
.
name
!==
'帮助中心'
)
// 将帮助中心添加到第一位
return
[
DEFAULT_HELP_CENTER
,
...
filtered
]
}
// 从 localStorage 初始化 currentPathNames
const
initCurrentPathNames
=
()
=>
{
try
{
const
stored
=
localStorage
.
getItem
(
'currentPathNames'
)
const
parsed
=
stored
?
JSON
.
parse
(
stored
)
:
[]
return
ensureHelpCenterFirst
(
parsed
)
return
stored
?
JSON
.
parse
(
stored
)
:
[]
}
catch
(
e
)
{
return
[
DEFAULT_HELP_CENTER
]
return
[]
}
}
...
...
@@ -36,16 +15,14 @@ export default {
},
mutations
:
{
setCurrentPathNames
(
state
,
pathNames
)
{
// 确保第一个元素是帮助中心
state
.
currentPathNames
=
ensureHelpCenterFirst
(
pathNames
||
[])
state
.
currentPathNames
=
pathNames
||
[]
localStorage
.
setItem
(
'currentPathNames'
,
JSON
.
stringify
(
state
.
currentPathNames
)
)
},
clearCurrentPathNames
(
state
)
{
// 清除时保留帮助中心
state
.
currentPathNames
=
[
DEFAULT_HELP_CENTER
]
state
.
currentPathNames
=
[]
localStorage
.
setItem
(
'currentPathNames'
,
JSON
.
stringify
(
state
.
currentPathNames
)
...
...
src/views/help/components/MenuItem.vue
View file @
a311f76a
...
...
@@ -9,7 +9,8 @@
<menu-item
v-for=
"child in menuItem.children"
:key=
"child.id"
:menu-item=
"child"
/>
:menu-item=
"child"
@
close-overlay=
"$emit('close-overlay')"
/>
</el-submenu>
<el-menu-item
v-else
...
...
@@ -18,7 +19,7 @@
? '/help/' + menuItem.path + '/' + menuItem.url
: '/help/' + menuItem.path
"
@
click=
"handleMenuItemClick"
>
@
click
.
native
.
prevent
=
"handleMenuItemClick"
>
<span>
{{ menuItem.name }}
</span>
</el-menu-item>
</div>
...
...
@@ -37,11 +38,41 @@ export default {
},
methods
:
{
handleMenuItemClick
()
{
// 构建目标路径
const
targetPath
=
this
.
menuItem
.
url
?
`/help/
${
this
.
menuItem
.
path
}
/
${
this
.
menuItem
.
url
}
`
:
`/help/
${
this
.
menuItem
.
path
}
`
// 如果当前路径已经是目标路径,且没有 link 参数变化,则不跳转
if
(
this
.
$route
.
path
===
targetPath
&&
!
this
.
menuItem
.
link
)
{
return
}
// 如果有 link 属性,需要带上 query 参数
if
(
this
.
menuItem
.
link
)
{
this
.
$router
.
push
({
path
:
`/help/
${
this
.
menuItem
.
path
}
/
${
this
.
menuItem
.
url
}
`
,
query
:
{
url
:
this
.
menuItem
.
link
},
})
this
.
$router
.
push
({
path
:
targetPath
,
query
:
{
url
:
this
.
menuItem
.
link
},
})
.
catch
((
err
)
=>
{
// 忽略导航重复的错误
if
(
err
.
name
!==
'NavigationDuplicated'
)
{
console
.
error
(
err
)
}
})
}
else
{
// 没有 link 时,使用 replace 避免产生历史记录
this
.
$router
.
replace
({
path
:
targetPath
,
})
.
catch
((
err
)
=>
{
// 忽略导航重复的错误
if
(
err
.
name
!==
'NavigationDuplicated'
)
{
console
.
error
(
err
)
}
})
}
const
id
=
[...
String
(
this
.
menuItem
.
id
)].
map
(
Number
)
...
...
@@ -60,6 +91,11 @@ export default {
})
this
.
$store
.
dispatch
(
'path/setCurrentPathNames'
,
result
)
// 如果是手机端,通知父组件关闭遮罩层
if
(
this
.
$isMobile
)
{
this
.
$emit
(
'close-overlay'
)
}
},
},
}
...
...
src/views/help/components/bread.vue
View file @
a311f76a
<
template
>
<div>
<el-breadcrumb
separator-class=
"el-icon-arrow-right"
>
<el-breadcrumb-item
v-for=
"(item, index) in breadList"
:key=
"item.id"
>
<a
v-if=
"index === 0"
href=
"javascript:void(0)"
@
click=
"handleBreadClick(index)"
:class=
"
{ 'breadcrumb-clickable': true }">
{{
item
.
name
}}
</a>
<span
v-else
class=
"breadcrumb-disabled"
>
{{
item
.
name
}}
</span>
</el-breadcrumb-item>
</el-breadcrumb>
</div>
<el-breadcrumb
separator-class=
"el-icon-arrow-right"
class=
"mb-3"
>
<el-breadcrumb-item
v-for=
"item in breadList"
:key=
"item.id"
>
<span
class=
"breadcrumb-disabled"
>
{{
item
.
name
}}
</span>
</el-breadcrumb-item>
</el-breadcrumb>
</
template
>
<
script
>
export
default
{
...
...
@@ -31,28 +22,17 @@ export default {
const
newPathNames
=
currentPathNames
.
slice
(
0
,
index
+
1
)
this
.
$store
.
dispatch
(
'path/setCurrentPathNames'
,
newPathNames
)
// 如果是帮助中心,跳转到 /help/index
if
(
index
===
0
)
{
this
.
$router
.
push
(
'/help/index'
)
}
else
{
this
.
$router
.
push
(
`/help/
${
clickedItem
.
path
}
`
)
}
const
targetPath
=
clickedItem
.
path
===
'index'
?
'/help/index'
:
`/help/
${
clickedItem
.
path
}
`
this
.
$router
.
push
(
targetPath
)
}
},
},
}
</
script
>
<
style
scoped
>
.breadcrumb-clickable
{
color
:
var
(
--primary-color
);
cursor
:
pointer
;
text-decoration
:
none
;
}
.breadcrumb-clickable
:hover
{
color
:
var
(
--el-color-primary-light-4
);
}
.breadcrumb-disabled
{
color
:
#606266
;
cursor
:
default
;
...
...
src/views/help/components/homeGuides.vue
View file @
a311f76a
<
template
>
<div>
<div
v-for=
"(tab, tabIndex) in tabs"
:key=
"tabIndex"
class=
"rounded-md bg-white p-5 mb-10"
>
<div
v-for=
"(tab, tabIndex) in tabs"
:key=
"tabIndex"
>
<div
class=
"title flex place-content-between mb-5"
>
<div
class=
"title-left flex items-center"
>
<img
class=
"h-8"
:src=
"Logo"
alt=
"logo"
/>
<div
class=
"
text-lg
font-bold ml-3"
>
{{
tab
.
title
}}
</div>
<div
class=
"
lg:text-lg text-base
font-bold ml-3"
>
{{
tab
.
title
}}
</div>
</div>
<div
class=
"more flex justify-center items-center"
@
click=
"goToMore(tab)"
>
<div
class=
"
text-sm
"
>
查看更多
</div>
<div
class=
"
lg:text-sm text-xs
"
>
查看更多
</div>
<i
class=
"el-icon-d-arrow-right"
></i>
</div>
</div>
...
...
@@ -84,7 +81,6 @@ export default {
goToArticle
(
content
)
{
const
articlePath
=
content
.
url
.
replace
(
'/help/'
,
''
)
const
breadcrumbPath
=
[
{
name
:
'帮助中心'
,
path
:
'index'
},
{
name
:
'入门必看'
,
path
:
'beginner'
},
{
name
:
content
.
article
,
path
:
articlePath
},
]
...
...
src/views/help/components/picTutorial.vue
View file @
a311f76a
...
...
@@ -5,7 +5,7 @@
</
template
>
<
script
>
import
{
FEISHU_
BASE
_URL
}
from
'../menuConfig.js'
import
{
FEISHU_
PIC_URL
,
FEISHU_VIDEO
_URL
}
from
'../menuConfig.js'
export
default
{
name
:
'PicTutorial'
,
...
...
@@ -25,7 +25,10 @@ export default {
const
anchor
=
this
.
$route
.
query
.
url
||
''
if
(
!
anchor
)
return
''
if
(
this
.
$route
.
name
===
'PicTutorial'
)
{
return
FEISHU_BASE_URL
+
anchor
return
FEISHU_PIC_URL
+
anchor
}
if
(
this
.
$route
.
name
===
'VideoTutorial'
)
{
return
FEISHU_VIDEO_URL
+
anchor
}
return
''
},
...
...
src/views/help/helpPage.vue
View file @
a311f76a
...
...
@@ -15,9 +15,8 @@
<div
class=
"content p-5"
>
<sideNav
v-if=
"!$isMobile"
/>
<div
class=
"right-content w-full lg:p-5 p-0"
style=
"height: 90vh"
:class=
"
{ 'rounded-md bg-white': $route.path !== '/help/index' }">
class=
"right-content w-full lg:p-5 p-2 rounded-md bg-white"
style=
"height: 90vh"
>
<Bread
v-if=
"$route.path !== '/help/index'"
/>
<router-view></router-view>
</div>
...
...
src/views/help/menuConfig.js
View file @
a311f76a
// 飞书文档
基础
URL
const
FEISHU_
BASE
_URL
=
// 飞书文档
图文
URL
const
FEISHU_
PIC
_URL
=
'https://rcntf4nac0ma.feishu.cn/wiki/OJcBwBJTLi4L0ekvFIPcrHQDnNh'
const
FEISHU_VIDEO_URL
=
'https://jcnq3gaihyh3.feishu.cn/wiki/PQQzwsNNOiHbg5kGjiVcqHGxnhg'
export
const
menuList
=
[
{
id
:
1
,
...
...
@@ -649,10 +652,128 @@ export const menuList = [
},
],
},
// { id: 3, path: 'video', name: '视频教程' },
{
id
:
3
,
path
:
''
,
name
:
'视频教程'
,
children
:
[
{
id
:
31
,
path
:
'videoTutorial'
,
url
:
'31'
,
name
:
'基础数据维护流程教程'
,
link
:
'#MAIEdz4uzowUaUxI00XcLzKWnOd'
,
},
{
id
:
32
,
path
:
'videoTutorial'
,
url
:
'32'
,
name
:
'平台新增店铺流程教程'
,
link
:
'#EkgbdmZVeoTbYixbssYc0wden4c'
,
},
{
id
:
33
,
path
:
'videoTutorial'
,
url
:
'33'
,
name
:
'独立站新增店铺流程教程'
,
link
:
'#MZqrduLgxoODyPxug52cmzE2ntb'
,
},
{
id
:
34
,
path
:
'videoTutorial'
,
url
:
'34'
,
name
:
'shopify新增店铺流程教程'
,
link
:
'#WNk9dtpgTovhCLx8K1GcGAjCnGc'
,
},
{
id
:
35
,
path
:
'videoTutorial'
,
url
:
'35'
,
name
:
'仓库信息维护流程教程'
,
link
:
'#XqIYdav7OoAB32xsEsccqdiVnih'
,
},
{
id
:
36
,
path
:
'videoTutorial'
,
url
:
'36'
,
name
:
'供应商管理维护流程教程'
,
link
:
'#HiVUdsJLBok3UUxdZa7cEsvZnKc'
,
},
{
id
:
37
,
path
:
'videoTutorial'
,
url
:
'37'
,
name
:
'物流信息维护流程教程'
,
link
:
'#XQJ5ddEmRoR10SxM1LicgXj3nig'
,
},
{
id
:
38
,
path
:
'videoTutorial'
,
url
:
'38'
,
name
:
'新增普通商品、局部印商品'
,
link
:
'#WutLdLTFZooze1xjM7ScJPaSnOb'
,
},
{
id
:
39
,
path
:
'videoTutorial'
,
url
:
'39'
,
name
:
'待处理订单流程教程'
,
link
:
'#SuREd1RPKoQcRbxgzPYcVTWinMy'
,
},
{
id
:
40
,
path
:
'videoTutorial'
,
url
:
'40'
,
name
:
'BASE异常单流程教程'
,
link
:
'#NvKxdLL1sojlgZxyo1AcJnnKn4d'
,
},
{
id
:
41
,
path
:
'videoTutorial'
,
url
:
'41'
,
name
:
'variant异常单流程教程'
,
link
:
'#NWh7dIjdDojzsYxFgGVci5L0n0b'
,
},
{
id
:
42
,
path
:
'videoTutorial'
,
url
:
'42'
,
name
:
'采购流程教程'
,
link
:
'#GnjMdjHZmoguHjxyMYhcyjYSnsG'
,
},
{
id
:
43
,
path
:
'videoTutorial'
,
url
:
'43'
,
name
:
'生产流程教程'
,
link
:
'#F85qdbqPAoCfhIxjvHacCOM6nMf'
,
},
{
id
:
44
,
path
:
'videoTutorial'
,
url
:
'44'
,
name
:
'美国工厂流程教程'
,
link
:
'#YG7gdHuOXo2jJhxVQXEcAB17nYg'
,
},
{
id
:
45
,
path
:
'videoTutorial'
,
url
:
'45'
,
name
:
'美国仅胚衣流程教程'
,
link
:
'#SmHNd6CWFoTcwnxOObLcQglbnJc'
,
},
{
id
:
46
,
path
:
'videoTutorial'
,
url
:
'46'
,
name
:
'一件定制流程教程'
,
link
:
'#QZNtd6IzFoFNcUxMVcTcUCr6nPg'
,
},
],
},
// { id: 4, path: 'problem', name: '常见问题' },
// { id: 5, path: 'contact', name: '联系我们' },
]
// 导出基础 URL,供组件使用
export
{
FEISHU_
BASE
_URL
}
export
{
FEISHU_
PIC_URL
,
FEISHU_VIDEO
_URL
}
src/views/help/sideNav.vue
View file @
a311f76a
...
...
@@ -3,13 +3,15 @@
<el-input
class=
"mt-3 ml-5 w-4/5 mb-5"
v-model=
"searchKeyword"
placeholder=
"在目录中搜索..."
></el-input
>
placeholder=
"在目录中搜索..."
/
>
<el-menu
router
:unique-opened=
"true"
:default-active=
"$route.path"
:default-openeds=
"openedMenus"
>
<menu-item
v-for=
"item in menuList"
:key=
"item.id"
:menu-item=
"item"
/>
<menu-item
v-for=
"item in filteredMenuList"
:key=
"item.id"
:menu-item=
"item"
/>
</el-menu>
</div>
</
template
>
...
...
@@ -29,6 +31,14 @@ export default {
}
},
computed
:
{
// 根据搜索关键词过滤菜单
filteredMenuList
()
{
if
(
!
this
.
searchKeyword
||
this
.
searchKeyword
.
trim
()
===
''
)
{
return
this
.
menuList
}
const
keyword
=
this
.
searchKeyword
.
trim
().
toLowerCase
()
return
this
.
filterMenuItems
(
this
.
menuList
,
keyword
)
},
// 需要展开的父级菜单
openedMenus
()
{
const
currentPath
=
this
.
$route
.
path
...
...
@@ -68,6 +78,39 @@ export default {
return
result
||
[]
},
},
methods
:
{
// 递归过滤菜单项
filterMenuItems
(
items
,
keyword
)
{
if
(
!
items
||
items
.
length
===
0
)
{
return
[]
}
return
items
.
map
((
item
)
=>
{
// 检查当前项的名称是否匹配
const
nameMatch
=
item
.
name
&&
item
.
name
.
toLowerCase
().
includes
(
keyword
)
// 递归过滤子项
let
filteredChildren
=
[]
if
(
item
.
children
&&
item
.
children
.
length
>
0
)
{
filteredChildren
=
this
.
filterMenuItems
(
item
.
children
,
keyword
)
}
// 如果当前项匹配或有匹配的子项,则保留该项
if
(
nameMatch
||
filteredChildren
.
length
>
0
)
{
return
{
...
item
,
// 如果当前项匹配,保留所有子项;否则只保留匹配的子项
children
:
nameMatch
?
item
.
children
:
filteredChildren
,
}
}
return
null
})
.
filter
((
item
)
=>
item
!==
null
)
},
},
}
</
script
>
<
style
scoped
lang=
"scss"
>
...
...
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