完成导航栏管理

dev
truthhun 12 months ago
parent 073f1495cb
commit 416973ee44

@ -12,7 +12,7 @@ type Navigation struct {
Title string `form:"title" json:"title,omitempty" gorm:"column:title;type:varchar(255);size:255;comment:链接名称;"` Title string `form:"title" json:"title,omitempty" gorm:"column:title;type:varchar(255);size:255;comment:链接名称;"`
Href string `form:"href" json:"href,omitempty" gorm:"column:href;type:varchar(255);size:255;comment:跳转链接;"` Href string `form:"href" json:"href,omitempty" gorm:"column:href;type:varchar(255);size:255;comment:跳转链接;"`
Target string `form:"target" json:"target,omitempty" gorm:"column:target;type:varchar(16);size:16;comment:打开方式;"` Target string `form:"target" json:"target,omitempty" gorm:"column:target;type:varchar(16);size:16;comment:打开方式;"`
Color string `form:"color" json:"color,omitempty" gorm:"column:color;type:varchar(16);size:16;comment:链接颜色;"` Color string `form:"color" json:"color,omitempty" gorm:"column:color;type:varchar(32);size:32;comment:链接颜色;"`
Sort int `form:"sort" json:"sort,omitempty" gorm:"column:sort;type:int(11);size:11;default:0;comment:排序,值越大越靠前;"` Sort int `form:"sort" json:"sort,omitempty" gorm:"column:sort;type:int(11);size:11;default:0;comment:排序,值越大越靠前;"`
Enable *bool `form:"enable" json:"enable,omitempty" gorm:"column:enable;type:int(11);size:11;default:0;comment:是否启用;"` Enable *bool `form:"enable" json:"enable,omitempty" gorm:"column:enable;type:int(11);size:11;default:0;comment:是否启用;"`
ParentId int64 `form:"parent_id" json:"parent_id,omitempty" gorm:"column:parent_id;type:int(11);size:11;default:0;comment:上级id;"` ParentId int64 `form:"parent_id" json:"parent_id,omitempty" gorm:"column:parent_id;type:int(11);size:11;default:0;comment:上级id;"`
@ -123,10 +123,26 @@ func (m *DBModel) GetNavigationList(opt *OptionGetNavigationList) (navigationLis
} }
// DeleteNavigation 删除数据 // DeleteNavigation 删除数据
// 连同子数据一起删除
func (m *DBModel) DeleteNavigation(ids []int64) (err error) { func (m *DBModel) DeleteNavigation(ids []int64) (err error) {
err = m.db.Where("id in (?)", ids).Delete(&Navigation{}).Error err = m.db.Where("id in (?)", ids).Delete(&Navigation{}).Error
if err != nil { if err != nil {
m.logger.Error("DeleteNavigation", zap.Error(err)) m.logger.Error("DeleteNavigation", zap.Error(err))
return
}
var children []Navigation
m.db.Select("id").Where("parent_id in (?)", ids).Find(&children)
if len(children) > 0 {
var childrenIds []int64
for _, child := range children {
childrenIds = append(childrenIds, child.Id)
}
err = m.DeleteNavigation(childrenIds)
if err != nil {
m.logger.Error("DeleteNavigation", zap.Error(err))
return
}
} }
return return
} }

@ -6,20 +6,21 @@
label-width="80px" label-width="80px"
:model="navigation" :model="navigation"
> >
<!-- -->
<el-form-item label="上级导航"> <el-form-item label="上级导航">
<el-cascader <el-select
v-model="navigation.parent_id" v-model="navigation.parent_id"
:options="trees"
:filterable="true" :filterable="true"
:props="{ :clearable="true"
checkStrictly: true, placeholder="请选择上级导航"
expandTrigger: 'hover', >
label: 'title', <el-option
value: 'id', v-for="item in trees"
}" :key="item.id"
clearable :label="item.title"
placeholder="请选择上级分类" :value="item.id"
></el-cascader> ></el-option>
</el-select>
</el-form-item> </el-form-item>
<el-form-item <el-form-item
label="名称" label="名称"
@ -35,13 +36,6 @@
<el-form-item <el-form-item
label="地址" label="地址"
prop="href" prop="href"
:rules="[
{
required: true,
trigger: 'blur',
message: ' https://mnt.ltd',
},
]"
> >
<el-input <el-input
v-model="navigation.href" v-model="navigation.href"
@ -97,7 +91,7 @@
<el-option <el-option
v-for="item in [ v-for="item in [
{ label: '', value: '_self' }, { label: '', value: '_self' },
{ label: '', value: '_blank' }, { label: '', value: '_blank' },
]" ]"
:key="item.value" :key="item.value"
:label="item.label" :label="item.label"

@ -0,0 +1,51 @@
<template>
<el-menu-item
:class="hiddenXS ? 'hidden-xs-only':''"
:key="'nav-' + navigation.id"
:index="`nav-${navigation.id}`"
>
<template v-if="navigation.href">
<!-- https:// 或者 http:// -->
<template
v-if="
navigation.href.indexOf('https://') === 0 ||
navigation.href.indexOf('http://') === 0
"
>
<a
:href="navigation.href"
:style="`color:${navigation.color}`"
:target="navigation.target"
class="el-link el-link--default"
>{{ navigation.title }}</a
>
</template>
<nuxt-link
v-else
:style="`color:${navigation.color}`"
:to="navigation.href"
:target="navigation.target"
class="el-link el-link--default"
>{{ navigation.title }}</nuxt-link
>
</template>
<template v-else>
<span :style="`color:${navigation.color}`">{{ navigation.title }}</span>
</template>
</el-menu-item>
</template>
<script>
export default {
name: 'NavigationLink',
props: {
navigation: {
type: Object,
default: () => {},
},
hiddenXS: {
type: Boolean,
default: false,
},
},
}
</script>

@ -17,19 +17,10 @@
<el-menu-item index="/" class="hidden-xs-only"> <el-menu-item index="/" class="hidden-xs-only">
<nuxt-link to="/"></nuxt-link> <nuxt-link to="/"></nuxt-link>
</el-menu-item> </el-menu-item>
<el-menu-item
v-for="(item, index) in categoryTrees"
:key="'c-' + item.id"
:index="`/category/${item.id}`"
v-show="$route.path === '/' && index < 6"
class="hidden-xs-only"
>
<nuxt-link :to="`/category/${item.id}`">{{ item.title }}</nuxt-link>
</el-menu-item>
<el-submenu <el-submenu
index="channel" index="channel"
class="hidden-xs-only" class="hidden-xs-only"
v-show="$route.path !== '/'" v-show="$route.path !== '/' || navigations.length > 0"
> >
<template slot="title"></template> <template slot="title"></template>
<el-menu-item <el-menu-item
@ -45,9 +36,47 @@
> >
</el-menu-item> </el-menu-item>
</el-submenu> </el-submenu>
<template v-if="navigations.length === 0">
<el-menu-item
v-for="(item, index) in categoryTrees"
:key="'c-' + item.id"
:index="`/category/${item.id}`"
v-show="$route.path === '/' && index < 6"
class="hidden-xs-only"
>
<nuxt-link :to="`/category/${item.id}`">{{
item.title
}}</nuxt-link>
</el-menu-item>
</template>
<template v-else>
<template v-for="item in navigations">
<el-submenu
:key="'nav-' + item.id"
:index="`nav-${item.id}`"
v-if="item.children && item.children.length > 0"
class="hidden-xs-only"
>
<template slot="title">{{ item.title }}</template>
<NavigationLink
:hiddenXS="true"
v-for="child in item.children || []"
:key="'child-' + child.id"
:navigation="child"
/>
</el-submenu>
<NavigationLink
v-else
:navigation="item"
:key="'nav-' + item.id"
:hiddenXS="true"
/>
</template>
</template>
<el-menu-item <el-menu-item
index="searchbox" index="searchbox"
class="nav-searchbox hidden-xs-only" class="nav-searchbox hidden-xs-only"
:class="navigations.length <= 2 ? 'nav-searchbox-large' : ''"
v-show="$route.path !== '/'" v-show="$route.path !== '/'"
> >
<el-input <el-input
@ -359,6 +388,23 @@
</ul> </ul>
</el-collapse-item> </el-collapse-item>
</el-collapse> </el-collapse>
<el-menu :default-active="$route.path" class="el-menu-mobile">
<template v-for="item in navigations">
<el-submenu
:key="'nav-' + item.id"
:index="`nav-${item.id}`"
v-if="item.children && item.children.length > 0"
>
<template slot="title">{{ item.title }}</template>
<NavigationLink
v-for="child in item.children || []"
:key="'child-' + child.id"
:navigation="child"
/>
</el-submenu>
<NavigationLink v-else :navigation="item" :key="'nav-' + item.id" />
</template>
</el-menu>
</el-drawer> </el-drawer>
</el-container> </el-container>
</template> </template>
@ -367,6 +413,7 @@ import { mapGetters, mapActions } from 'vuex'
import UserAvatar from '~/components/UserAvatar.vue' import UserAvatar from '~/components/UserAvatar.vue'
import FormUserinfo from '~/components/FormUserinfo.vue' import FormUserinfo from '~/components/FormUserinfo.vue'
import { listFriendlink } from '~/api/friendlink' import { listFriendlink } from '~/api/friendlink'
import { listNavigation } from '~/api/navigation'
import { categoryToTrees, requireLogin } from '~/utils/utils' import { categoryToTrees, requireLogin } from '~/utils/utils'
import { getSignedToday, signToday } from '~/api/user' import { getSignedToday, signToday } from '~/api/user'
@ -386,6 +433,7 @@ export default {
sign: { id: 0 }, sign: { id: 0 },
activeCollapse: 'categories', activeCollapse: 'categories',
loading: false, loading: false,
navigations: [],
} }
}, },
head() { head() {
@ -411,21 +459,17 @@ export default {
}, },
async created() { async created() {
this.loading = true this.loading = true
const [res] = await Promise.all([ await Promise.all([
listFriendlink({
enable: true,
field: ['id', 'title', 'link'],
}),
this.getCategories(), this.getCategories(),
this.getSettings(), this.getSettings(),
this.listNavigation(),
this.listFriendlink(),
]) ])
if (res.status === 200) {
this.friendlinks = res.data.friendlink
}
this.categoryTrees = categoryToTrees(this.categories).filter( this.categoryTrees = categoryToTrees(this.categories).filter(
(item) => item.enable (item) => item.enable
) )
this.loopUpdate() this.loopUpdate()
this.loading = false this.loading = false
if (requireLogin(this.settings, this.user, this.$route, this.permissions)) { if (requireLogin(this.settings, this.user, this.$route, this.permissions)) {
@ -451,6 +495,15 @@ export default {
this.sign = res.data || this.sign this.sign = res.data || this.sign
} }
}, },
async listFriendlink() {
const res = await listFriendlink({
enable: true,
field: ['id', 'title', 'link'],
})
if (res.status === 200) {
this.friendlinks = res.data.friendlink
}
},
async signToday() { async signToday() {
const res = await signToday() const res = await signToday()
if (res.status === 200) { if (res.status === 200) {
@ -466,6 +519,14 @@ export default {
this.$message.error(res.message || res.data.message) this.$message.error(res.message || res.data.message)
} }
}, },
async listNavigation() {
const res = await listNavigation({ page: 1, size: 10000 })
if (res.status === 200) {
let navigations = res.data.navigation || []
navigations = categoryToTrees(navigations).filter((item) => item.enable)
this.navigations = navigations
}
},
onSearch() { onSearch() {
if (!this.search.wd) return if (!this.search.wd) return
this.menuDrawerVisible = false this.menuDrawerVisible = false
@ -610,11 +671,16 @@ export default {
.nav-searchbox { .nav-searchbox {
padding: 0 25px !important; padding: 0 25px !important;
top: -2px; top: -2px;
&.nav-searchbox-large {
.el-input {
width: 300px;
}
}
&.is-active { &.is-active {
border-color: transparent; border-color: transparent;
} }
.el-input { .el-input {
width: 360px; width: 200px;
} }
} }
a { a {
@ -622,7 +688,8 @@ export default {
height: 60px; height: 60px;
line-height: 60px; line-height: 60px;
display: inline-block; display: inline-block;
padding: 0 20px; // padding: 0 20px;
padding: 0 15px;
} }
.el-menu-item { .el-menu-item {
padding: 0; padding: 0;
@ -732,6 +799,24 @@ export default {
padding: 1px 20px; padding: 1px 20px;
} }
.el-menu-mobile {
margin-top: 15px;
margin-left: -15px;
border-right: 0;
& > li {
padding-left: 0;
}
.el-submenu__title {
height: 32px;
line-height: 32px;
font-size: 15px;
}
.el-menu-item {
height: 32px;
line-height: 32px;
}
}
// ======================= // =======================
// 移动端样式 // 移动端样式
// ======================= // =======================

@ -127,10 +127,15 @@ export default {
let navigations = res.data.navigation || [] let navigations = res.data.navigation || []
navigations.map((item) => { navigations.map((item) => {
item.title_html = genLinkHTML(item.title, item.href) item.title_html = genLinkHTML(item.title, item.href)
if (item.color) {
// 增加链接颜色
item.title_html = item.title_html.replace(
'<a',
`<a style="color:${item.color}" `
)
}
}) })
console.log(navigations)
let trees = categoryToTrees(navigations, false) let trees = categoryToTrees(navigations, false)
this.navigations = trees this.navigations = trees
this.total = res.data.total this.total = res.data.total
@ -179,7 +184,7 @@ export default {
}, },
batchDelete() { batchDelete() {
this.$confirm( this.$confirm(
`您确定要删除选中的【${this.selectedRow.length}条】导航吗?删除之后不可恢复!`, `您确定要删除选中的【${this.selectedRow.length}条】导航吗?会连带着子导航一起删除,删除之后不可恢复!`,
'', '',
{ {
confirmButtonText: '', confirmButtonText: '',
@ -201,7 +206,7 @@ export default {
}, },
deleteRow(row) { deleteRow(row) {
this.$confirm( this.$confirm(
`您确定要删除导航【${row.title}】吗?删除之后不可恢复!`, `您确定要删除导航【${row.title}】吗?会连带着子导航一起删除,删除之后不可恢复!`,
'', '',
{ {
confirmButtonText: '', confirmButtonText: '',

Loading…
Cancel
Save