Commit a311f76a by wusiyi

feat: 帮助页优化

parent bbb8f250
...@@ -10,14 +10,6 @@ ...@@ -10,14 +10,6 @@
</div> </div>
<div class="flex items-center justify-center pb-2"> <div class="flex items-center justify-center pb-2">
<div <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" class="mr-2 flex items-center justify-center gap-2"
v-if="!userInfo"> v-if="!userInfo">
<div <div
...@@ -62,10 +54,10 @@ ...@@ -62,10 +54,10 @@
</div> </div>
<div class="flex items-center justify-center gap-2"> <div class="flex items-center justify-center gap-2">
<div class="text-sm" v-if="!userInfo"> <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>
<span @click="register">注册</span> <span @click="register" class="ml-1">注册</span>
</div> </div>
<div class="text-sm flex items-center justify-center" v-else> <div class="text-sm flex items-center justify-center" v-else>
<span>{{ userInfo.companyName }}</span> <span>{{ userInfo.companyName }}</span>
...@@ -90,11 +82,33 @@ ...@@ -90,11 +82,33 @@
</div> </div>
</div> </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> </div>
</template> </template>
<script> <script>
import { Icon } from '@iconify/vue2' import { Icon } from '@iconify/vue2'
import { mapMutations } from 'vuex' import { mapMutations } from 'vuex'
import MenuItem from '../views/help/components/MenuItem.vue'
import { menuList } from '../views/help/menuConfig' import { menuList } from '../views/help/menuConfig'
export default { export default {
...@@ -104,6 +118,7 @@ export default { ...@@ -104,6 +118,7 @@ export default {
}, },
components: { components: {
Icon, Icon,
MenuItem,
}, },
props: { props: {
userInfo: { userInfo: {
...@@ -112,14 +127,50 @@ export default { ...@@ -112,14 +127,50 @@ export default {
}, },
data() { data() {
return { return {
navs: [ showing: false,
{ menuList,
name: '首页',
path: '/',
},
],
} }
}, },
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: { methods: {
...mapMutations(['setUserInfo', 'setShopKey']), ...mapMutations(['setUserInfo', 'setShopKey']),
...@@ -171,7 +222,7 @@ export default { ...@@ -171,7 +222,7 @@ export default {
window.location.reload() window.location.reload()
}, },
toggleShow() { toggleShow() {
console.log(menuList, '???') this.showing = !this.showing
}, },
}, },
} }
...@@ -219,4 +270,26 @@ export default { ...@@ -219,4 +270,26 @@ export default {
color: var(--secondary-color); 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> </style>
...@@ -100,6 +100,13 @@ const routes = [ ...@@ -100,6 +100,13 @@ const routes = [
props: true, props: true,
}, },
{ {
path: 'videoTutorial/:id',
name: 'VideoTutorial',
component: (resolve) =>
require(['../views/help/components/picTutorial.vue'], resolve),
props: true,
},
{
path: 'artical1', path: 'artical1',
name: 'Artical1', name: 'Artical1',
component: (resolve) => component: (resolve) =>
......
// 默认的帮助中心路径
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 // 从 localStorage 初始化 currentPathNames
const initCurrentPathNames = () => { const initCurrentPathNames = () => {
try { try {
const stored = localStorage.getItem('currentPathNames') const stored = localStorage.getItem('currentPathNames')
const parsed = stored ? JSON.parse(stored) : [] return stored ? JSON.parse(stored) : []
return ensureHelpCenterFirst(parsed)
} catch (e) { } catch (e) {
return [DEFAULT_HELP_CENTER] return []
} }
} }
...@@ -36,16 +15,14 @@ export default { ...@@ -36,16 +15,14 @@ export default {
}, },
mutations: { mutations: {
setCurrentPathNames(state, pathNames) { setCurrentPathNames(state, pathNames) {
// 确保第一个元素是帮助中心 state.currentPathNames = pathNames || []
state.currentPathNames = ensureHelpCenterFirst(pathNames || [])
localStorage.setItem( localStorage.setItem(
'currentPathNames', 'currentPathNames',
JSON.stringify(state.currentPathNames) JSON.stringify(state.currentPathNames)
) )
}, },
clearCurrentPathNames(state) { clearCurrentPathNames(state) {
// 清除时保留帮助中心 state.currentPathNames = []
state.currentPathNames = [DEFAULT_HELP_CENTER]
localStorage.setItem( localStorage.setItem(
'currentPathNames', 'currentPathNames',
JSON.stringify(state.currentPathNames) JSON.stringify(state.currentPathNames)
......
...@@ -9,7 +9,8 @@ ...@@ -9,7 +9,8 @@
<menu-item <menu-item
v-for="child in menuItem.children" v-for="child in menuItem.children"
:key="child.id" :key="child.id"
:menu-item="child" /> :menu-item="child"
@close-overlay="$emit('close-overlay')" />
</el-submenu> </el-submenu>
<el-menu-item <el-menu-item
v-else v-else
...@@ -18,7 +19,7 @@ ...@@ -18,7 +19,7 @@
? '/help/' + menuItem.path + '/' + menuItem.url ? '/help/' + menuItem.path + '/' + menuItem.url
: '/help/' + menuItem.path : '/help/' + menuItem.path
" "
@click="handleMenuItemClick"> @click.native.prevent="handleMenuItemClick">
<span>{{ menuItem.name }}</span> <span>{{ menuItem.name }}</span>
</el-menu-item> </el-menu-item>
</div> </div>
...@@ -37,11 +38,41 @@ export default { ...@@ -37,11 +38,41 @@ export default {
}, },
methods: { methods: {
handleMenuItemClick() { 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) { if (this.menuItem.link) {
this.$router.push({ this.$router
path: `/help/${this.menuItem.path}/${this.menuItem.url}`, .push({
query: { url: this.menuItem.link }, 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) const id = [...String(this.menuItem.id)].map(Number)
...@@ -60,6 +91,11 @@ export default { ...@@ -60,6 +91,11 @@ export default {
}) })
this.$store.dispatch('path/setCurrentPathNames', result) this.$store.dispatch('path/setCurrentPathNames', result)
// 如果是手机端,通知父组件关闭遮罩层
if (this.$isMobile) {
this.$emit('close-overlay')
}
}, },
}, },
} }
......
<template> <template>
<div> <el-breadcrumb separator-class="el-icon-arrow-right" class="mb-3">
<el-breadcrumb separator-class="el-icon-arrow-right"> <el-breadcrumb-item v-for="item in breadList" :key="item.id">
<el-breadcrumb-item v-for="(item, index) in breadList" :key="item.id"> <span class="breadcrumb-disabled">{{ item.name }}</span>
<a </el-breadcrumb-item>
v-if="index === 0" </el-breadcrumb>
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>
</template> </template>
<script> <script>
export default { export default {
...@@ -31,28 +22,17 @@ export default { ...@@ -31,28 +22,17 @@ export default {
const newPathNames = currentPathNames.slice(0, index + 1) const newPathNames = currentPathNames.slice(0, index + 1)
this.$store.dispatch('path/setCurrentPathNames', newPathNames) this.$store.dispatch('path/setCurrentPathNames', newPathNames)
// 如果是帮助中心,跳转到 /help/index const targetPath =
if (index === 0) { clickedItem.path === 'index'
this.$router.push('/help/index') ? '/help/index'
} else { : `/help/${clickedItem.path}`
this.$router.push(`/help/${clickedItem.path}`) this.$router.push(targetPath)
}
} }
}, },
}, },
} }
</script> </script>
<style scoped> <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 { .breadcrumb-disabled {
color: #606266; color: #606266;
cursor: default; cursor: default;
......
<template> <template>
<div> <div>
<div <div v-for="(tab, tabIndex) in tabs" :key="tabIndex">
v-for="(tab, tabIndex) in tabs"
:key="tabIndex"
class="rounded-md bg-white p-5 mb-10">
<div class="title flex place-content-between mb-5"> <div class="title flex place-content-between mb-5">
<div class="title-left flex items-center"> <div class="title-left flex items-center">
<img class="h-8" :src="Logo" alt="logo" /> <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>
<div <div
class="more flex justify-center items-center" class="more flex justify-center items-center"
@click="goToMore(tab)"> @click="goToMore(tab)">
<div class="text-sm">查看更多</div> <div class="lg:text-sm text-xs">查看更多</div>
<i class="el-icon-d-arrow-right"></i> <i class="el-icon-d-arrow-right"></i>
</div> </div>
</div> </div>
...@@ -84,7 +81,6 @@ export default { ...@@ -84,7 +81,6 @@ export default {
goToArticle(content) { goToArticle(content) {
const articlePath = content.url.replace('/help/', '') const articlePath = content.url.replace('/help/', '')
const breadcrumbPath = [ const breadcrumbPath = [
{ name: '帮助中心', path: 'index' },
{ name: '入门必看', path: 'beginner' }, { name: '入门必看', path: 'beginner' },
{ name: content.article, path: articlePath }, { name: content.article, path: articlePath },
] ]
......
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
</template> </template>
<script> <script>
import { FEISHU_BASE_URL } from '../menuConfig.js' import { FEISHU_PIC_URL, FEISHU_VIDEO_URL } from '../menuConfig.js'
export default { export default {
name: 'PicTutorial', name: 'PicTutorial',
...@@ -25,7 +25,10 @@ export default { ...@@ -25,7 +25,10 @@ export default {
const anchor = this.$route.query.url || '' const anchor = this.$route.query.url || ''
if (!anchor) return '' if (!anchor) return ''
if (this.$route.name === 'PicTutorial') { 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 '' return ''
}, },
......
...@@ -15,9 +15,8 @@ ...@@ -15,9 +15,8 @@
<div class="content p-5"> <div class="content p-5">
<sideNav v-if="!$isMobile" /> <sideNav v-if="!$isMobile" />
<div <div
class="right-content w-full lg:p-5 p-0" class="right-content w-full lg:p-5 p-2 rounded-md bg-white"
style="height: 90vh" style="height: 90vh">
:class="{ 'rounded-md bg-white': $route.path !== '/help/index' }">
<Bread v-if="$route.path !== '/help/index'" /> <Bread v-if="$route.path !== '/help/index'" />
<router-view></router-view> <router-view></router-view>
</div> </div>
......
// 飞书文档基础 URL // 飞书文档 图文 URL
const FEISHU_BASE_URL = const FEISHU_PIC_URL =
'https://rcntf4nac0ma.feishu.cn/wiki/OJcBwBJTLi4L0ekvFIPcrHQDnNh' 'https://rcntf4nac0ma.feishu.cn/wiki/OJcBwBJTLi4L0ekvFIPcrHQDnNh'
const FEISHU_VIDEO_URL =
'https://jcnq3gaihyh3.feishu.cn/wiki/PQQzwsNNOiHbg5kGjiVcqHGxnhg'
export const menuList = [ export const menuList = [
{ {
id: 1, id: 1,
...@@ -649,10 +652,128 @@ export const menuList = [ ...@@ -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: 4, path: 'problem', name: '常见问题' },
// { id: 5, path: 'contact', name: '联系我们' }, // { id: 5, path: 'contact', name: '联系我们' },
] ]
// 导出基础 URL,供组件使用 // 导出基础 URL,供组件使用
export { FEISHU_BASE_URL } export { FEISHU_PIC_URL, FEISHU_VIDEO_URL }
...@@ -3,13 +3,15 @@ ...@@ -3,13 +3,15 @@
<el-input <el-input
class="mt-3 ml-5 w-4/5 mb-5" class="mt-3 ml-5 w-4/5 mb-5"
v-model="searchKeyword" v-model="searchKeyword"
placeholder="在目录中搜索..."></el-input> placeholder="在目录中搜索..." />
<el-menu <el-menu
router
:unique-opened="true" :unique-opened="true"
:default-active="$route.path" :default-active="$route.path"
:default-openeds="openedMenus"> :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> </el-menu>
</div> </div>
</template> </template>
...@@ -29,6 +31,14 @@ export default { ...@@ -29,6 +31,14 @@ export default {
} }
}, },
computed: { computed: {
// 根据搜索关键词过滤菜单
filteredMenuList() {
if (!this.searchKeyword || this.searchKeyword.trim() === '') {
return this.menuList
}
const keyword = this.searchKeyword.trim().toLowerCase()
return this.filterMenuItems(this.menuList, keyword)
},
// 需要展开的父级菜单 // 需要展开的父级菜单
openedMenus() { openedMenus() {
const currentPath = this.$route.path const currentPath = this.$route.path
...@@ -68,6 +78,39 @@ export default { ...@@ -68,6 +78,39 @@ export default {
return result || [] 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> </script>
<style scoped lang="scss"> <style scoped lang="scss">
......
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