mirror of
https://gitee.com/niucloud-team/niucloud.git
synced 2025-12-12 01:47:08 +00:00
fix admin
This commit is contained in:
parent
4282130387
commit
231f476a6f
@ -147,7 +147,10 @@ div.edui-box {
|
|||||||
overflow: visible;
|
overflow: visible;
|
||||||
z-index: 1 !important;
|
z-index: 1 !important;
|
||||||
}
|
}
|
||||||
|
/* 全屏状态 */
|
||||||
|
.edui-default .edui-editor.edui-fullscreen {
|
||||||
|
z-index: 999 !important;
|
||||||
|
}
|
||||||
.edui-editor div {
|
.edui-editor div {
|
||||||
width: auto;
|
width: auto;
|
||||||
height: auto;
|
height: auto;
|
||||||
|
|||||||
@ -291,13 +291,13 @@ export function getSiteAddons() {
|
|||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
export function getShowApp() {
|
export function getShowApp() {
|
||||||
return request.get('site/showApp')
|
return request.get('site/showCustomer')
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取营销列表
|
* 获取站点应用特殊
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
export function getShowMarketing() {
|
export function getShowSpecialMenu() {
|
||||||
return request.get('site/showMarketing')
|
return request.get('site/special_menu')
|
||||||
}
|
}
|
||||||
|
|||||||
@ -299,7 +299,7 @@ const getUpgradeTaskFn = () => {
|
|||||||
if (!upgradeContent.value) {
|
if (!upgradeContent.value) {
|
||||||
upgradeContent.value = data.upgrade_content
|
upgradeContent.value = data.upgrade_content
|
||||||
|
|
||||||
if (upgradeContent.value || !data.upgrade_content || !Array.isArray(data.upgrade_content.content)) {
|
if (!data.upgrade_content || !Array.isArray(data.upgrade_content.content)) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -691,7 +691,7 @@ defineExpose({
|
|||||||
white-space: pre-wrap;
|
white-space: pre-wrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
::v-deep .number-of-steps {
|
:deep(.number-of-steps) {
|
||||||
.el-step__line {
|
.el-step__line {
|
||||||
margin: 0 25px;
|
margin: 0 25px;
|
||||||
background: #dddddd;
|
background: #dddddd;
|
||||||
|
|||||||
@ -50,6 +50,7 @@ import { img } from '@/utils/common'
|
|||||||
import useUserStore from '@/stores/modules/user'
|
import useUserStore from '@/stores/modules/user'
|
||||||
import { useRouter } from 'vue-router'
|
import { useRouter } from 'vue-router'
|
||||||
import { t } from '@/lang'
|
import { t } from '@/lang'
|
||||||
|
import storage from '@/utils/storage'
|
||||||
|
|
||||||
const addonIndexRoute = useUserStore().addonIndexRoute
|
const addonIndexRoute = useUserStore().addonIndexRoute
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
@ -73,6 +74,7 @@ const toLink = (item: any) => {
|
|||||||
} else {
|
} else {
|
||||||
addonIndexRoute[item.key] && router.push({ name: addonIndexRoute[item.key] })
|
addonIndexRoute[item.key] && router.push({ name: addonIndexRoute[item.key] })
|
||||||
}
|
}
|
||||||
|
storage.set({ key: 'activeAppKey', data: item.key })
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@ -45,7 +45,7 @@
|
|||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { ref } from 'vue'
|
import { ref } from 'vue'
|
||||||
import { getShowMarketing } from '@/app/api/site'
|
// import { getShowMarketing } from '@/app/api/site'
|
||||||
import { img } from '@/utils/common'
|
import { img } from '@/utils/common'
|
||||||
import useUserStore from '@/stores/modules/user'
|
import useUserStore from '@/stores/modules/user'
|
||||||
import { useRouter } from 'vue-router'
|
import { useRouter } from 'vue-router'
|
||||||
@ -57,8 +57,8 @@ const marketingList = ref<Record<string, any>[]>([])
|
|||||||
|
|
||||||
const loading = ref(true)
|
const loading = ref(true)
|
||||||
const getMarketingList = async () => {
|
const getMarketingList = async () => {
|
||||||
const res = await getShowMarketing()
|
// const res = await getShowMarketing()
|
||||||
marketingList.value = res.data
|
// marketingList.value = res.data
|
||||||
loading.value = false
|
loading.value = false
|
||||||
}
|
}
|
||||||
getMarketingList()
|
getMarketingList()
|
||||||
|
|||||||
@ -62,7 +62,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-show="formData.package_type == 'cloud'">
|
<div v-show="formData.package_type == 'cloud'">
|
||||||
<el-form-item :label="t('icon')">
|
<el-form-item :label="t('icon')" prop="build.icon">
|
||||||
<div class="input-width" >
|
<div class="input-width" >
|
||||||
<upload-file v-model="formData.build.icon" accept=".zip" api="sys/document/applet"></upload-file>
|
<upload-file v-model="formData.build.icon" accept=".zip" api="sys/document/applet"></upload-file>
|
||||||
</div>
|
</div>
|
||||||
@ -80,7 +80,7 @@
|
|||||||
<div class="form-tip flex items-center">证书可以自己生成也可通过niucloud提供的<span class="text-primary cursor-pointer" @click="generateSingCertRef.open()">证书生成工具生成</span></div>
|
<div class="form-tip flex items-center">证书可以自己生成也可通过niucloud提供的<span class="text-primary cursor-pointer" @click="generateSingCertRef.open()">证书生成工具生成</span></div>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<div v-show="formData.cert.type == 'private'">
|
<div v-show="formData.cert.type == 'private'">
|
||||||
<el-form-item :label="t('certFile')" prop="cert.cert_file">
|
<el-form-item :label="t('certFile')" prop="cert.file">
|
||||||
<div class="input-width" >
|
<div class="input-width" >
|
||||||
<upload-file v-model="formData.cert.file" accept="" api="sys/document/android_cert"></upload-file>
|
<upload-file v-model="formData.cert.file" accept="" api="sys/document/android_cert"></upload-file>
|
||||||
</div>
|
</div>
|
||||||
@ -166,6 +166,7 @@ const initialFormData = {
|
|||||||
},
|
},
|
||||||
cert: {
|
cert: {
|
||||||
type: 'public',
|
type: 'public',
|
||||||
|
file: '',
|
||||||
key_alias: '',
|
key_alias: '',
|
||||||
key_password: '',
|
key_password: '',
|
||||||
store_password: ''
|
store_password: ''
|
||||||
@ -211,7 +212,7 @@ const formRules = computed(() => {
|
|||||||
'build.icon': [
|
'build.icon': [
|
||||||
{ required: formData.package_type == 'cloud', message: '请上传图标文件', trigger: 'blur' },
|
{ required: formData.package_type == 'cloud', message: '请上传图标文件', trigger: 'blur' },
|
||||||
],
|
],
|
||||||
'cert.cert_file': [
|
'cert.file': [
|
||||||
{ required: formData.package_type == 'cloud' && formData.cert.type == 'private', message: '请上传证书文件', trigger: 'blur' }
|
{ required: formData.package_type == 'cloud' && formData.cert.type == 'private', message: '请上传证书文件', trigger: 'blur' }
|
||||||
],
|
],
|
||||||
'cert.key_alias': [
|
'cert.key_alias': [
|
||||||
|
|||||||
@ -65,7 +65,7 @@
|
|||||||
<el-button type="primary" link v-if="row.status == 'upload_success'" @click="releaseEvent(row)">{{ t('release') }}</el-button>
|
<el-button type="primary" link v-if="row.status == 'upload_success'" @click="releaseEvent(row)">{{ t('release') }}</el-button>
|
||||||
<el-button type="primary" link v-if="row.status == 'create_fail'" @click="handleFailReason(row)">{{ t('failReason') }}</el-button>
|
<el-button type="primary" link v-if="row.status == 'create_fail'" @click="handleFailReason(row)">{{ t('failReason') }}</el-button>
|
||||||
<el-button type="primary" link v-if="row.package_path && row.upgrade_type != 'market'" @click="downloadEvent(row)">{{ t('download') }}</el-button>
|
<el-button type="primary" link v-if="row.package_path && row.upgrade_type != 'market'" @click="downloadEvent(row)">{{ t('download') }}</el-button>
|
||||||
<el-button type="primary" link @click="deleteEvent(row)">{{ t('delete') }}</el-button>
|
<el-button type="primary" link @click="deleteEvent(row.id)">{{ t('delete') }}</el-button>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
|
|
||||||
|
|||||||
@ -35,7 +35,7 @@
|
|||||||
<div class="mt-[16px] flex justify-end">
|
<div class="mt-[16px] flex justify-end">
|
||||||
<el-pagination v-model:current-page="bottomNavTableData.page" v-model:page-size="bottomNavTableData.limit"
|
<el-pagination v-model:current-page="bottomNavTableData.page" v-model:page-size="bottomNavTableData.limit"
|
||||||
layout="total, sizes, prev, pager, next, jumper" :total="bottomNavTableData.total"
|
layout="total, sizes, prev, pager, next, jumper" :total="bottomNavTableData.total"
|
||||||
@size-change="loadbottomNavList()" @current-change="loadbottomNavList" />
|
@size-change="loadBottomNavList()" @current-change="loadBottomNavList" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</el-card>
|
</el-card>
|
||||||
|
|||||||
@ -91,7 +91,7 @@ const editEvent = (data)=> {
|
|||||||
<!-- 设置弹窗标题 -->
|
<!-- 设置弹窗标题 -->
|
||||||
<style scoped>
|
<style scoped>
|
||||||
/* 使用深度选择器 */
|
/* 使用深度选择器 */
|
||||||
::v-deep .custom-theme-dialog .el-dialog__title {
|
:deep(.custom-theme-dialog .el-dialog__title) {
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@ -1383,7 +1383,7 @@ html.dark .table-head-bg {
|
|||||||
-webkit-line-clamp: 2;
|
-webkit-line-clamp: 2;
|
||||||
-webkit-box-orient: vertical;
|
-webkit-box-orient: vertical;
|
||||||
}
|
}
|
||||||
::v-deep .number-of-steps {
|
:deep(.number-of-steps) {
|
||||||
.el-step__line {
|
.el-step__line {
|
||||||
margin: 0 25px;
|
margin: 0 25px;
|
||||||
background: #dddddd;
|
background: #dddddd;
|
||||||
|
|||||||
@ -529,17 +529,20 @@ const save = (callback: any) => {
|
|||||||
|
|
||||||
const api = posterStore.id ? editPoster : addPoster
|
const api = posterStore.id ? editPoster : addPoster
|
||||||
api(data).then((res: any) => {
|
api(data).then((res: any) => {
|
||||||
isRepeat.value = false
|
// isRepeat.value = false
|
||||||
if (res.code == 1) {
|
if (res.code == 1) {
|
||||||
if (posterStore.id) {
|
if (posterStore.id) {
|
||||||
isRepeat.value = false // 不刷新
|
isRepeat.value = false // 不刷新
|
||||||
} else {
|
} else {
|
||||||
|
// isRepeat.value = false
|
||||||
location.href = `${location.origin}${backPath}`
|
location.href = `${location.origin}${backPath}`
|
||||||
}
|
}
|
||||||
if (callback) callback(res.data.id)
|
if (callback) callback(res.data.id)
|
||||||
}
|
}
|
||||||
}).catch(() => {
|
}).catch(() => {
|
||||||
isRepeat.value = false
|
isRepeat.value = false
|
||||||
|
}).finally(() => {
|
||||||
|
// isRepeat.value = false
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -6,7 +6,7 @@
|
|||||||
<span class="text-page-title">{{ pageName }}</span>
|
<span class="text-page-title">{{ pageName }}</span>
|
||||||
<el-button type="primary" class="w-[100px]" @click="dialogVisible = true">{{ t('添加海报') }}</el-button>
|
<el-button type="primary" class="w-[100px]" @click="dialogVisible = true">{{ t('添加海报') }}</el-button>
|
||||||
</div>
|
</div>
|
||||||
<div class="mt-[20px]" v-if="!isImagick">
|
<div class="mt-[20px]" v-if="!isImagick && !posterTableData.loading">
|
||||||
<el-alert type="warning" show-icon :closable="false">
|
<el-alert type="warning" show-icon :closable="false">
|
||||||
<template #title>
|
<template #title>
|
||||||
<span class="!text-[14px]">检测到PHP未安装ImageMagick扩展,需安装后才能使用海报功能</span>
|
<span class="!text-[14px]">检测到PHP未安装ImageMagick扩展,需安装后才能使用海报功能</span>
|
||||||
@ -287,11 +287,10 @@ const resetForm = (formEl: FormInstance | undefined) => {
|
|||||||
formEl.resetFields()
|
formEl.resetFields()
|
||||||
loadPosterPageList()
|
loadPosterPageList()
|
||||||
}
|
}
|
||||||
const isImagick = ref(false)
|
const isImagick = ref(true)
|
||||||
// 判断是否安装imagemagick扩展
|
// 判断是否安装imagemagick扩展
|
||||||
const checkImagickFn = () => {
|
const checkImagickFn = () => {
|
||||||
checkImagick().then((res:any) => {
|
checkImagick().then((res:any) => {
|
||||||
console.log(res)
|
|
||||||
isImagick.value = res.data
|
isImagick.value = res.data
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|||||||
@ -22,19 +22,19 @@
|
|||||||
<h3 class="panel-title !text-[14px] bg-[#F4F5F7] p-3 border-[#E6E6E6] border-solid border-b-[1px]">{{ t('putOnRecordEdit') }}</h3>
|
<h3 class="panel-title !text-[14px] bg-[#F4F5F7] p-3 border-[#E6E6E6] border-solid border-b-[1px]">{{ t('putOnRecordEdit') }}</h3>
|
||||||
<el-form-item :label="t('icp')" prop="icp">
|
<el-form-item :label="t('icp')" prop="icp">
|
||||||
<el-input v-model.trim="formData.icp" :placeholder="t('icpPlaceholder')" class="input-width" clearable maxlength="20"/>
|
<el-input v-model.trim="formData.icp" :placeholder="t('icpPlaceholder')" class="input-width" clearable maxlength="20"/>
|
||||||
<div class="form-tip">{{ t('网站的ICP备案号,显示在H5和PC端底部') }}</div>
|
<div class="form-tip">{{ t('网站的ICP备案号,显示在PC端底部') }}</div>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item :label="t('govRecord')" >
|
<el-form-item :label="t('govRecord')" >
|
||||||
<el-input v-model.trim="formData.gov_record" :placeholder="t('govRecordPlaceholder')" class="input-width" clearable maxlength="50"/>
|
<el-input v-model.trim="formData.gov_record" :placeholder="t('govRecordPlaceholder')" class="input-width" clearable maxlength="50"/>
|
||||||
<div class="form-tip">{{ t('公安部门登记的备案信息,显示在pc底部') }}</div>
|
<div class="form-tip">{{ t('公安部门登记的备案信息,显示在PC底部') }}</div>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item :label="t('govUrl')" >
|
<el-form-item :label="t('govUrl')" >
|
||||||
<el-input v-model.trim="formData.gov_url" :placeholder="t('govUrlPlaceholder')" class="input-width" clearable />
|
<el-input v-model.trim="formData.gov_url" :placeholder="t('govUrlPlaceholder')" class="input-width" clearable />
|
||||||
<div class="form-tip">{{ t('H5和PC底部显示的网站公安点击跳转的链接') }}</div>
|
<div class="form-tip">{{ t('PC底部显示的网站公安点击跳转的链接') }}</div>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item :label="t('marketSupervisionUrl')" >
|
<el-form-item :label="t('marketSupervisionUrl')" >
|
||||||
<el-input v-model.trim="formData.market_supervision_url" rows="4" clearable :placeholder="t('marketSupervisionUrlPlaceholder')" class="input-width" />
|
<el-input v-model.trim="formData.market_supervision_url" rows="4" clearable :placeholder="t('marketSupervisionUrlPlaceholder')" class="input-width" />
|
||||||
<div class="form-tip">{{ t('H5和PC底部显示的市场监督管理局点击跳转的链接') }}</div>
|
<div class="form-tip">{{ t('PC底部显示的市场监督管理局点击跳转的链接') }}</div>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</div>
|
</div>
|
||||||
</el-card>
|
</el-card>
|
||||||
|
|||||||
@ -11,6 +11,10 @@
|
|||||||
<upload-image v-model="formData.head_img" />
|
<upload-image v-model="formData.head_img" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
|
||||||
|
<el-form-item :label="t('手机号')" prop="mobile">
|
||||||
|
<el-input v-model.trim="formData.mobile" :placeholder="t('请输入手机号')" clearable class="input-width" maxlength="11" show-word-limit />
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
<el-form-item :label="t('userRealName')" prop="real_name">
|
<el-form-item :label="t('userRealName')" prop="real_name">
|
||||||
<el-input v-model.trim="formData.real_name" :placeholder="t('userRealNamePlaceholder')" :readonly="realnameInput" @click="realnameInput = false" @blur="realnameInput = true" clearable class="input-width" maxlength="10" show-word-limit />
|
<el-input v-model.trim="formData.real_name" :placeholder="t('userRealNamePlaceholder')" :readonly="realnameInput" @click="realnameInput = false" @blur="realnameInput = true" clearable class="input-width" maxlength="10" show-word-limit />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
@ -97,6 +101,7 @@ const formData = ref({
|
|||||||
username: '',
|
username: '',
|
||||||
password: '',
|
password: '',
|
||||||
head_img: '',
|
head_img: '',
|
||||||
|
mobile: '',
|
||||||
real_name: '',
|
real_name: '',
|
||||||
confirm_password: '',
|
confirm_password: '',
|
||||||
create_site_limit: [],
|
create_site_limit: [],
|
||||||
@ -112,6 +117,19 @@ const formRules = computed(() => {
|
|||||||
password: [
|
password: [
|
||||||
{ required: userStore.userInfo && userStore.userInfo.is_super_admin == true, message: t('passwordPlaceholder'), trigger: 'blur' }
|
{ required: userStore.userInfo && userStore.userInfo.is_super_admin == true, message: t('passwordPlaceholder'), trigger: 'blur' }
|
||||||
],
|
],
|
||||||
|
mobile: [
|
||||||
|
{ required: true, message: t('请输入手机号'), trigger: 'blur' },
|
||||||
|
{
|
||||||
|
validator: (rule: any, value: string, callback: any) => {
|
||||||
|
if (!Test.mobile(value)) {
|
||||||
|
callback(new Error(t('手机号格式错误')))
|
||||||
|
} else {
|
||||||
|
callback()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
trigger: 'blur'
|
||||||
|
}
|
||||||
|
],
|
||||||
real_name: [
|
real_name: [
|
||||||
{ required: true, message: t('userRealNamePlaceholder'), trigger: 'blur' }
|
{ required: true, message: t('userRealNamePlaceholder'), trigger: 'blur' }
|
||||||
],
|
],
|
||||||
@ -173,6 +191,7 @@ const setFormData = (uid: number = 0) => {
|
|||||||
getUserInfo(uid).then(({ data }) => {
|
getUserInfo(uid).then(({ data }) => {
|
||||||
formData.value.uid = data.uid
|
formData.value.uid = data.uid
|
||||||
formData.value.username = data.username
|
formData.value.username = data.username
|
||||||
|
formData.value.mobile = data.mobile
|
||||||
formData.value.real_name = data.real_name
|
formData.value.real_name = data.real_name
|
||||||
formData.value.head_img = data.head_img
|
formData.value.head_img = data.head_img
|
||||||
loading.value = false
|
loading.value = false
|
||||||
@ -184,6 +203,7 @@ const setFormData = (uid: number = 0) => {
|
|||||||
username: '',
|
username: '',
|
||||||
password: '',
|
password: '',
|
||||||
head_img: '',
|
head_img: '',
|
||||||
|
mobile: '',
|
||||||
real_name: '',
|
real_name: '',
|
||||||
confirm_password: '',
|
confirm_password: '',
|
||||||
create_site_limit: [],
|
create_site_limit: [],
|
||||||
@ -254,7 +274,7 @@ defineExpose({
|
|||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.displayPass {
|
.displayPass {
|
||||||
::v-deep .el-input__inner{
|
:deep(.el-input__inner){
|
||||||
-webkit-text-security: disc !important;
|
-webkit-text-security: disc !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -46,6 +46,7 @@
|
|||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column prop="username" :label="t('accountNumber')" min-width="120" show-overflow-tooltip />
|
<el-table-column prop="username" :label="t('accountNumber')" min-width="120" show-overflow-tooltip />
|
||||||
|
<el-table-column prop="mobile" :label="t('手机号')" min-width="120" show-overflow-tooltip />
|
||||||
<el-table-column prop="real_name" :label="t('userRealName')" min-width="120" show-overflow-tooltip />
|
<el-table-column prop="real_name" :label="t('userRealName')" min-width="120" show-overflow-tooltip />
|
||||||
<el-table-column prop="site_num" :label="t('siteNum')" min-width="120" show-overflow-tooltip align="center" />
|
<el-table-column prop="site_num" :label="t('siteNum')" min-width="120" show-overflow-tooltip align="center" />
|
||||||
<!-- <el-table-column prop="create_time" :label="t('createTime')" min-width="180" align="center">
|
<!-- <el-table-column prop="create_time" :label="t('createTime')" min-width="180" align="center">
|
||||||
|
|||||||
@ -24,27 +24,35 @@
|
|||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
<el-row :gutter="20" class="mb-[20px]">
|
<el-row :gutter="20" class="mb-[20px]">
|
||||||
<el-col :span="6">
|
<el-col :span="6" >
|
||||||
<span class="text-[14px] w-[130px] text-right mr-[20px]">{{ t('realname') }}</span>
|
<span class="text-[14px] w-[130px] text-right mr-[20px]">{{ t('realname') }}</span>
|
||||||
<span class="text-[14px] text-[#666666]">
|
<span class="text-[14px] text-[#666666]">
|
||||||
{{ detail.real_name || '--' }}
|
{{ detail.real_name || '--' }}
|
||||||
</span>
|
</span>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="6" :offset="6">
|
<el-col :span="6" :offset="6">
|
||||||
<span class="text-[14px] w-[130px] text-right mr-[20px]">{{ t('addTime') }}</span>
|
<span class="text-[14px] w-[130px] text-right mr-[20px]">{{ t('手机号') }}</span>
|
||||||
<span class="text-[14px] text-[#666666]">
|
<span class="text-[14px] text-[#666666]">
|
||||||
{{ detail.create_time }}
|
{{ detail.mobile || '--' }}
|
||||||
</span>
|
</span>
|
||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
<el-row :gutter="20" class="mb-[20px]">
|
<el-row :gutter="20" class="mb-[20px]">
|
||||||
<el-col :span="6">
|
<el-col :span="6">
|
||||||
|
<span class="text-[14px] w-[130px] text-right mr-[20px]">{{ t('addTime') }}</span>
|
||||||
|
<span class="text-[14px] text-[#666666]">
|
||||||
|
{{ detail.create_time }}
|
||||||
|
</span>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="6" :offset="6">
|
||||||
<span class="text-[14px] w-[130px] text-right mr-[20px]">{{ t('lastLoginTime') }}</span>
|
<span class="text-[14px] w-[130px] text-right mr-[20px]">{{ t('lastLoginTime') }}</span>
|
||||||
<span class="text-[14px] text-[#666666]">
|
<span class="text-[14px] text-[#666666]">
|
||||||
{{ detail.last_time || '' }}
|
{{ detail.last_time || '' }}
|
||||||
</span>
|
</span>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="6" :offset="6">
|
</el-row>
|
||||||
|
<el-row :gutter="20" class="mb-[20px]">
|
||||||
|
<el-col :span="6">
|
||||||
<span class="text-[14px] w-[130px] text-right mr-[20px]">{{ t('lastLoginIP') }}</span>
|
<span class="text-[14px] w-[130px] text-right mr-[20px]">{{ t('lastLoginIP') }}</span>
|
||||||
<span class="text-[14px] text-[#666666]">
|
<span class="text-[14px] text-[#666666]">
|
||||||
{{ detail.last_ip || '' }}
|
{{ detail.last_ip || '' }}
|
||||||
|
|||||||
@ -858,7 +858,7 @@ const batchDelete = () => {
|
|||||||
background-color: var(--el-table-header-bg-color);
|
background-color: var(--el-table-header-bg-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
::v-deep .number-of-steps {
|
:deep(.number-of-steps) {
|
||||||
.el-step__line {
|
.el-step__line {
|
||||||
margin: 0 25px;
|
margin: 0 25px;
|
||||||
background: #dddddd;
|
background: #dddddd;
|
||||||
|
|||||||
@ -53,7 +53,7 @@
|
|||||||
<el-timeline-item :hollow="true">
|
<el-timeline-item :hollow="true">
|
||||||
<div class="text-[16px] text-[#1D1F3A]">编译uniapp代码</div>
|
<div class="text-[16px] text-[#1D1F3A]">编译uniapp代码</div>
|
||||||
<div class="p-[10px] bg-[#F9F9FB] mt-[10px] text-[#4F516D] text-[14px] w-[1085px] border-[#F1F1F8] border-solid border-[1px] h-[40px] flex items-center rounded-[4px]">
|
<div class="p-[10px] bg-[#F9F9FB] mt-[10px] text-[#4F516D] text-[14px] w-[1085px] border-[#F1F1F8] border-solid border-[1px] h-[40px] flex items-center rounded-[4px]">
|
||||||
<span>云编泽会将uniapp端的vue代码编译为对应的html文件,同时将生成的代码下载到系统 niucloud下的</span>
|
<span>云编译会将uniapp端的vue代码编译为对应的html文件,同时将生成的代码下载到系统 niucloud下的</span>
|
||||||
<span class="text-[#F09000] mx-[3px] font-bold">public/wap</span>
|
<span class="text-[#F09000] mx-[3px] font-bold">public/wap</span>
|
||||||
<span>目录中,这样手机端网页的访问路径将变为</span>
|
<span>目录中,这样手机端网页的访问路径将变为</span>
|
||||||
<span class="text-primary ml-[3px] font-500"> https://域名/wap</span>
|
<span class="text-primary ml-[3px] font-500"> https://域名/wap</span>
|
||||||
@ -62,7 +62,7 @@
|
|||||||
<el-timeline-item :hollow="true">
|
<el-timeline-item :hollow="true">
|
||||||
<div class="text-[16px] text-[#1D1F3A]">编译web代码</div>
|
<div class="text-[16px] text-[#1D1F3A]">编译web代码</div>
|
||||||
<div class="p-[10px] bg-[#F9F9FB] mt-[10px] text-[#4F516D] text-[14px] w-[1085px] border-[#F1F1F8] border-solid border-[1px] h-[40px] flex items-center rounded-[4px]">
|
<div class="p-[10px] bg-[#F9F9FB] mt-[10px] text-[#4F516D] text-[14px] w-[1085px] border-[#F1F1F8] border-solid border-[1px] h-[40px] flex items-center rounded-[4px]">
|
||||||
<span>云编泽会将web端的vue代码编译为对应的html文件,同时将生成的代码下载到系统 niucloud下的</span>
|
<span>云编译会将web端的vue代码编译为对应的html文件,同时将生成的代码下载到系统 niucloud下的</span>
|
||||||
<span class="text-[#F09000] mx-[3px] font-bold">public/web</span>
|
<span class="text-[#F09000] mx-[3px] font-bold">public/web</span>
|
||||||
<span>目录中,这样电脑端网页的访问路径将变为</span>
|
<span>目录中,这样电脑端网页的访问路径将变为</span>
|
||||||
<span class="text-primary ml-[3px] font-500"> https://域名/web</span>
|
<span class="text-primary ml-[3px] font-500"> https://域名/web</span>
|
||||||
|
|||||||
@ -91,6 +91,17 @@ const handleEditorReady = (editor) => {
|
|||||||
emit('handleBlur', editor.getContent()) // 把内容传出去
|
emit('handleBlur', editor.getContent()) // 把内容传出去
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// 全屏切换监听
|
||||||
|
editor.addListener('fullscreenchanged', (type, fullscreen) =>{
|
||||||
|
const editorDom = editor.ui.getDom()
|
||||||
|
if (fullscreen) {
|
||||||
|
editorDom.classList.add('edui-fullscreen')
|
||||||
|
} else {
|
||||||
|
editorDom.classList.remove('edui-fullscreen')
|
||||||
|
}
|
||||||
|
console.log('全屏切换', fullscreen)
|
||||||
|
})
|
||||||
|
|
||||||
// 方案二:原型链扩展(如果编辑器版本支持)
|
// 方案二:原型链扩展(如果编辑器版本支持)
|
||||||
const originalCount = editor.getContentLength; // 原生统计方法
|
const originalCount = editor.getContentLength; // 原生统计方法
|
||||||
|
|
||||||
|
|||||||
@ -103,7 +103,7 @@
|
|||||||
"405": "请求方法未允许",
|
"405": "请求方法未允许",
|
||||||
"408": "请求超时",
|
"408": "请求超时",
|
||||||
"409": "请求跨域",
|
"409": "请求跨域",
|
||||||
"500": "服务器端出错,错误原因:",
|
"500": "服务器内部错误",
|
||||||
"501": "网络未实现",
|
"501": "网络未实现",
|
||||||
"502": "网络错误",
|
"502": "网络错误",
|
||||||
"503": "服务不可用",
|
"503": "服务不可用",
|
||||||
|
|||||||
@ -42,4 +42,8 @@ const tabbarStore = useTabbarStore()
|
|||||||
.bg-page {
|
.bg-page {
|
||||||
background-color: #F7F7FA;
|
background-color: #F7F7FA;
|
||||||
}
|
}
|
||||||
|
:deep(.inter .el-breadcrumb__inner){
|
||||||
|
font-weight: inherit !important;
|
||||||
|
color: var(--el-text-color-regular) !important;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@ -38,4 +38,9 @@ const appStore = useAppStore()
|
|||||||
const tabbarStore = useTabbarStore()
|
const tabbarStore = useTabbarStore()
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped></style>
|
<style lang="scss" scoped>
|
||||||
|
:deep(.inter .el-breadcrumb__inner){
|
||||||
|
font-weight: inherit !important;
|
||||||
|
color: var(--el-text-color-regular) !important;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|||||||
@ -1,18 +1,18 @@
|
|||||||
<template>
|
<template>
|
||||||
<template v-if="meta.show">
|
<template v-if="meta.show">
|
||||||
<el-sub-menu v-if="routes.children" :index="String(routes.name)">
|
<el-sub-menu v-if="hasVisibleChild" :index="String(routes.name)">
|
||||||
<template #title>
|
<template #title>
|
||||||
<span :class="['ml-[10px]']">{{ meta.title }}</span>
|
<span :class="['ml-[10px]']">{{ meta.title }}</span>
|
||||||
</template>
|
</template>
|
||||||
<menu-item v-for="(route, index) in routes.children" :routes="route" :key="index" />
|
<menu-item v-for="(route, index) in routes.children" :routes="route" :key="index" />
|
||||||
</el-sub-menu>
|
</el-sub-menu>
|
||||||
<template v-else>
|
<template v-else>
|
||||||
<el-menu-item :index="String(routes.name)" @click="router.push({ name: routes.name })" v-if="meta.addon && meta.parent_route && meta.parent_route.addon == ''">
|
<el-menu-item :index="String(routes.name)" @click="handleJump(routes.name)" v-if="meta.addon && meta.parent_route && meta.parent_route.addon == ''">
|
||||||
<template #title>
|
<template #title>
|
||||||
<span :class="[{'text-[15px]': routes.meta.class == 1}, {'text-[14px]': routes.meta.class != 1}, {'ml-[10px]': routes.meta.class == 2, 'ml-[15px]': routes.meta.class == 3}]">{{ meta.title }}</span>
|
<span :class="[{'text-[15px]': routes.meta.class == 1}, {'text-[14px]': routes.meta.class != 1}, {'ml-[10px]': routes.meta.class == 2, 'ml-[15px]': routes.meta.class == 3}]">{{ meta.title }}</span>
|
||||||
</template>
|
</template>
|
||||||
</el-menu-item>
|
</el-menu-item>
|
||||||
<el-menu-item :index="String(routes.name)" @click="router.push({ name: routes.name })" v-else>
|
<el-menu-item :index="String(routes.name)" @click="handleJump(routes.name)" v-else>
|
||||||
<template #title>
|
<template #title>
|
||||||
<span :class="[{'text-[15px]': routes.meta.class == 1}, {'text-[14px]': routes.meta.class != 1}, {'ml-[10px]': routes.meta.class == 2, 'ml-[15px]': routes.meta.class == 3}]">{{ meta.title }}</span>
|
<span :class="[{'text-[15px]': routes.meta.class == 1}, {'text-[14px]': routes.meta.class != 1}, {'ml-[10px]': routes.meta.class == 2, 'ml-[15px]': routes.meta.class == 3}]">{{ meta.title }}</span>
|
||||||
</template>
|
</template>
|
||||||
@ -29,6 +29,7 @@ import { computed } from 'vue'
|
|||||||
import { img } from '@/utils/common'
|
import { img } from '@/utils/common'
|
||||||
import menuItem from './menu-item.vue'
|
import menuItem from './menu-item.vue'
|
||||||
import useUserStore from '@/stores/modules/user'
|
import useUserStore from '@/stores/modules/user'
|
||||||
|
import storage from '@/utils/storage'
|
||||||
|
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
@ -41,12 +42,37 @@ const userStore = useUserStore()
|
|||||||
const siteInfo = userStore.siteInfo
|
const siteInfo = userStore.siteInfo
|
||||||
const meta = computed(() => props.routes.meta)
|
const meta = computed(() => props.routes.meta)
|
||||||
|
|
||||||
|
const hasVisibleChild = computed(() => {
|
||||||
|
if (!props.routes.children || !Array.isArray(props.routes.children)) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return props.routes.children.some(child => child.meta?.show === 1)
|
||||||
|
})
|
||||||
|
|
||||||
const addons = computed(() => {
|
const addons = computed(() => {
|
||||||
const addons:Record<string, any> = {}
|
const addons:Record<string, any> = {}
|
||||||
siteInfo?.apps.forEach((item: any) => { addons[item.key] = item })
|
siteInfo?.apps.forEach((item: any) => { addons[item.key] = item })
|
||||||
siteInfo?.site_addons.forEach((item: any) => { addons[item.key] = item })
|
siteInfo?.site_addons.forEach((item: any) => { addons[item.key] = item })
|
||||||
return addons
|
return addons
|
||||||
})
|
})
|
||||||
|
// 统一处理跳转逻辑
|
||||||
|
const handleJump = (routeName: string) => {
|
||||||
|
// 检查目标路由是否在特殊菜单列表中
|
||||||
|
const specialMenuNames = storage.get('specialMenuNames')
|
||||||
|
const specialMenuNamesLevel1 = storage.get('specialMenuNamesLevel1')
|
||||||
|
const isInSpecialMenus = specialMenuNames.includes(routeName)
|
||||||
|
// 核心逻辑:如果不在特殊菜单中,就删除activeAppKey
|
||||||
|
if (!isInSpecialMenus) {
|
||||||
|
storage.remove('activeAppKey')
|
||||||
|
} else {
|
||||||
|
}
|
||||||
|
// 点击特殊菜单的一级,跳转应用列表
|
||||||
|
if (specialMenuNamesLevel1.includes(routeName)) {
|
||||||
|
routeName = 'addon_list'
|
||||||
|
}
|
||||||
|
// 执行跳转
|
||||||
|
router.push({ name: routeName })
|
||||||
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@ -17,7 +17,7 @@
|
|||||||
<el-scrollbar class="h-[calc( 100vh - 64px )]">
|
<el-scrollbar class="h-[calc( 100vh - 64px )]">
|
||||||
<el-menu :default-active="oneMenuActive" :router="true" class="aside-menu" :unique-opened="true">
|
<el-menu :default-active="oneMenuActive" :router="true" class="aside-menu" :unique-opened="true">
|
||||||
<template v-for="(item, index) in oneMenuData" :key="index">
|
<template v-for="(item, index) in oneMenuData" :key="index">
|
||||||
<el-menu-item :index="item.original_name" @click="router.push({ name: item.name })" v-if="item.meta.show">
|
<el-menu-item :index="item.original_name" @click="handleJump(item.name)" v-if="item.meta.show">
|
||||||
<div v-if="item.meta.icon" class="w-[16px] h-[16px] relative flex justify-center">
|
<div v-if="item.meta.icon" class="w-[16px] h-[16px] relative flex justify-center">
|
||||||
<icon :name="item.meta.icon" class="absolute top-[50%] -translate-y-[50%]" />
|
<icon :name="item.meta.icon" class="absolute top-[50%] -translate-y-[50%]" />
|
||||||
</div>
|
</div>
|
||||||
@ -49,9 +49,10 @@ import { useRoute, useRouter } from 'vue-router'
|
|||||||
import useSystemStore from '@/stores/modules/system'
|
import useSystemStore from '@/stores/modules/system'
|
||||||
import useUserStore from '@/stores/modules/user'
|
import useUserStore from '@/stores/modules/user'
|
||||||
import menuItem from './menu-item.vue'
|
import menuItem from './menu-item.vue'
|
||||||
import { getShowApp, getShowMarketing } from '@/app/api/site'
|
import { getShowApp,getShowSpecialMenu} from '@/app/api/site'
|
||||||
import { img } from '@/utils/common'
|
import { img } from '@/utils/common'
|
||||||
import { findFirstValidRoute } from '@/router/routers'
|
import { findFirstValidRoute,formatRouters } from '@/router/routers'
|
||||||
|
import storage from '@/utils/storage'
|
||||||
|
|
||||||
const systemStore = useSystemStore()
|
const systemStore = useSystemStore()
|
||||||
const userStore = useUserStore()
|
const userStore = useUserStore()
|
||||||
@ -136,87 +137,222 @@ const oneMenuActive = ref(route.matched[1].name)
|
|||||||
const appList = ref(null)
|
const appList = ref(null)
|
||||||
const marketingList = ref(null)
|
const marketingList = ref(null)
|
||||||
// const loading = ref(true);
|
// const loading = ref(true);
|
||||||
|
|
||||||
const getAppList = async () => {
|
const getAppList = async () => {
|
||||||
const res = await getShowApp()
|
const res = await getShowApp()
|
||||||
appList.value = res.data
|
appList.value = res.data
|
||||||
// loading.value = false;
|
// 应用列表的key
|
||||||
|
storage.set({ key: 'defaultAppList', data: appList.value })
|
||||||
}
|
}
|
||||||
const getMarketingList = async () => {
|
const specialList = ref<Record<string, any>[]>([])
|
||||||
const res = await getShowMarketing()
|
const getShowSpecialMenuList = async () => {
|
||||||
marketingList.value = res.data
|
const res = await getShowSpecialMenu()
|
||||||
|
// specialList.value = formatRouters(res.data.list)
|
||||||
|
specialList.value = res.data.list
|
||||||
|
// 应用列表特殊菜单
|
||||||
|
storage.set({ key: 'specialAppList', data: specialList.value })
|
||||||
}
|
}
|
||||||
onMounted(async () => {
|
|
||||||
await getAppList() // 确保数据先加载
|
const specialMenuNames = ref<string[]>([])
|
||||||
await getMarketingList()
|
const specialMenuNamesLevel1 = ref<string[]>([])
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
getAppList()
|
||||||
|
getShowSpecialMenuList()
|
||||||
|
const processedSpecialMenus = handleSpecialMenus();
|
||||||
|
specialMenuNamesLevel1.value = collectSpecialMenuNamesLevel1(processedSpecialMenus)
|
||||||
|
specialMenuNames.value = collectSpecialMenuNames(processedSpecialMenus)
|
||||||
|
storage.set({ key: 'specialMenuNames', data: specialMenuNames.value })
|
||||||
|
storage.set({ key: 'specialMenuNamesLevel1', data: specialMenuNamesLevel1.value })
|
||||||
})
|
})
|
||||||
|
|
||||||
watchEffect(() => {
|
|
||||||
// if (!appList.value || loading.value) return; // 确保数据加载完毕
|
|
||||||
const addonKeys = appList.value?.addon?.list?.map(item => item.key) ?? []
|
|
||||||
const toolKeys = appList.value?.tool?.list?.map(item => item.key) ?? []
|
|
||||||
const allKeys = [...addonKeys, ...toolKeys]
|
|
||||||
const marketingKeys = marketingList.value?.marketing?.list?.map(item => item.key) ?? []
|
|
||||||
const matchedName = route.matched[1]?.name
|
|
||||||
if (allKeys.includes(matchedName)) {
|
|
||||||
oneMenuActive.value = 'addon'
|
|
||||||
twoMenuData.value = route.matched[1]?.children ?? []
|
|
||||||
} else if (marketingKeys.includes(matchedName)) {
|
|
||||||
oneMenuActive.value = 'active'
|
|
||||||
twoMenuData.value = route.matched[1]?.children ?? []
|
|
||||||
} else if (route.meta.attr !== '') {
|
|
||||||
oneMenuActive.value = route.matched[2]?.name
|
|
||||||
twoMenuData.value = route.matched[1]?.children ?? []
|
|
||||||
} else {
|
|
||||||
// 多应用
|
|
||||||
if (siteInfo?.apps.length > 1) {
|
|
||||||
twoMenuData.value = route.matched[1]?.children
|
|
||||||
oneMenuActive.value = route.matched[1]?.name
|
|
||||||
} else {
|
|
||||||
// 单应用
|
|
||||||
const oneMenu = route.matched[1]
|
|
||||||
if (oneMenu.meta.addon === '') {
|
|
||||||
oneMenuActive.value = route.matched[1]?.name
|
|
||||||
twoMenuData.value = route.matched[1]?.children ?? []
|
|
||||||
} else {
|
|
||||||
if (oneMenu.meta.addon === siteInfo?.apps[0]?.key) {
|
|
||||||
oneMenuActive.value = route.matched[2]?.name
|
|
||||||
twoMenuData.value = route.matched[2]?.children ?? []
|
|
||||||
} else {
|
|
||||||
oneMenuActive.value = route.matched[1]?.name
|
|
||||||
twoMenuData.value = route.matched[1]?.children ?? []
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
// watch(route, () => {
|
// onMounted(async () => {
|
||||||
// if (route.meta.attr != '') {
|
// await getAppList() // 确保数据先加载
|
||||||
// oneMenuActive.value = route.matched[2].name
|
// })
|
||||||
// twoMenuData.value = route.matched[1].children ?? []
|
|
||||||
|
// watchEffect(() => {
|
||||||
|
// // if (!appList.value || loading.value) return; // 确保数据加载完毕
|
||||||
|
// const addonKeys = appList.value?.addon?.list?.map(item => item.key) ?? []
|
||||||
|
// const toolKeys = appList.value?.tool?.list?.map(item => item.key) ?? []
|
||||||
|
// const allKeys = [...addonKeys, ...toolKeys]
|
||||||
|
// const marketingKeys = marketingList.value?.marketing?.list?.map(item => item.key) ?? []
|
||||||
|
// const matchedName = route.matched[1]?.name
|
||||||
|
// if (allKeys.includes(matchedName)) {
|
||||||
|
// oneMenuActive.value = 'addon'
|
||||||
|
// twoMenuData.value = route.matched[1]?.children ?? []
|
||||||
|
// } else if (marketingKeys.includes(matchedName)) {
|
||||||
|
// oneMenuActive.value = 'active'
|
||||||
|
// twoMenuData.value = route.matched[1]?.children ?? []
|
||||||
|
// } else if (route.meta.attr !== '') {
|
||||||
|
// oneMenuActive.value = route.matched[2]?.name
|
||||||
|
// twoMenuData.value = route.matched[1]?.children ?? []
|
||||||
// } else {
|
// } else {
|
||||||
// // 多应用
|
// // 多应用
|
||||||
// if (siteInfo?.apps.length > 1) {
|
// if (siteInfo?.apps.length > 1) {
|
||||||
// twoMenuData.value = route.matched[1].children
|
// twoMenuData.value = route.matched[1]?.children
|
||||||
// oneMenuActive.value = route.matched[1].name
|
// oneMenuActive.value = route.matched[1]?.name
|
||||||
// } else {
|
// } else {
|
||||||
// // 单应用
|
// // 单应用
|
||||||
// const oneMenu = route.matched[1]
|
// const oneMenu = route.matched[1]
|
||||||
// if (oneMenu.meta.addon == '') {
|
// if (oneMenu.meta.addon === '') {
|
||||||
// oneMenuActive.value = route.matched[1].name
|
// oneMenuActive.value = route.matched[1]?.name
|
||||||
// twoMenuData.value = route.matched[1].children ?? []
|
// twoMenuData.value = route.matched[1]?.children ?? []
|
||||||
// } else {
|
// } else {
|
||||||
// if (oneMenu.meta.addon == siteInfo?.apps[0].key) {
|
// if (oneMenu.meta.addon === siteInfo?.apps[0]?.key) {
|
||||||
// oneMenuActive.value = route.matched[2].name
|
// oneMenuActive.value = route.matched[2]?.name
|
||||||
// twoMenuData.value = route.matched[2].children ?? []
|
// twoMenuData.value = route.matched[2]?.children ?? []
|
||||||
// } else {
|
// } else {
|
||||||
// oneMenuActive.value = route.matched[1].name
|
// oneMenuActive.value = route.matched[1]?.name
|
||||||
// twoMenuData.value = route.matched[1].children ?? []
|
// twoMenuData.value = route.matched[1]?.children ?? []
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
// }, { immediate: true })
|
// })
|
||||||
|
|
||||||
|
// 从 addonKeys 中提取所有需要匹配的 key
|
||||||
|
const getAddonAllKeys = (addonData) => {
|
||||||
|
if (!addonData || typeof addonData !== 'object') return [];
|
||||||
|
const allKeys = [];
|
||||||
|
Object.values(addonData).forEach(category => {
|
||||||
|
if (Array.isArray(category.list)) {
|
||||||
|
category.list.forEach(item => {
|
||||||
|
if (item.key) allKeys.push(item.key);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return allKeys;
|
||||||
|
};
|
||||||
|
// 处理 specialMenusKeys 子菜单 show 的方法
|
||||||
|
const handleSpecialMenus = () => {
|
||||||
|
const specialMenusKeys = storage.get('specialAppList')
|
||||||
|
if (Array.isArray(specialMenusKeys) && specialMenusKeys.length) {
|
||||||
|
const processedSpecialMenus = JSON.parse(JSON.stringify(specialMenusKeys));
|
||||||
|
const activeAppKey = storage.get('activeAppKey');
|
||||||
|
|
||||||
|
// 收集所有特殊菜单的name
|
||||||
|
processedSpecialMenus.forEach(menu => {
|
||||||
|
if (menu.children && Array.isArray(menu.children)) {
|
||||||
|
const traverseChildren = (children) => {
|
||||||
|
children.forEach(child => {
|
||||||
|
if (child && child.is_show !== undefined) {
|
||||||
|
child.is_show = (child.menu_key === activeAppKey) ? 1 : 0;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
traverseChildren(menu.children);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// 过滤掉 children 为空的特殊菜单
|
||||||
|
const filteredSpecialMenus = processedSpecialMenus.filter(menu => {
|
||||||
|
return menu.children && menu.children.length > 0;
|
||||||
|
});
|
||||||
|
return formatRouters(filteredSpecialMenus);
|
||||||
|
}
|
||||||
|
return [];
|
||||||
|
};
|
||||||
|
|
||||||
|
// 提取所有特殊菜单的name
|
||||||
|
const collectSpecialMenuNames = (menus: any[]) => {
|
||||||
|
const names: string[] = []
|
||||||
|
const traverse = (children: any[]) => {
|
||||||
|
children.forEach(child => {
|
||||||
|
if (child.name) {
|
||||||
|
names.push(child.name)
|
||||||
|
}
|
||||||
|
// 递归处理子菜单
|
||||||
|
if (child.children && Array.isArray(child.children)) {
|
||||||
|
traverse(child.children)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
menus.forEach(menu => {
|
||||||
|
if (menu.children && Array.isArray(menu.children)) {
|
||||||
|
traverse(menu.children)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return names
|
||||||
|
}
|
||||||
|
// 提取所有一级特殊菜单的name
|
||||||
|
const collectSpecialMenuNamesLevel1 = (menus: any[]) =>{
|
||||||
|
const names: string[] = []
|
||||||
|
menus.forEach(menu => {
|
||||||
|
if (menu.name) {
|
||||||
|
names.push(menu.name)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return names
|
||||||
|
}
|
||||||
|
|
||||||
|
// 统一处理跳转逻辑
|
||||||
|
const handleJump = (routeName: string) => {
|
||||||
|
// 检查目标路由是否在特殊菜单列表中
|
||||||
|
const isInSpecialMenus = specialMenuNames.value.includes(routeName)
|
||||||
|
// 核心逻辑:如果不在特殊菜单中,就删除activeAppKey
|
||||||
|
if (!isInSpecialMenus) {
|
||||||
|
storage.remove('activeAppKey')
|
||||||
|
} else {
|
||||||
|
}
|
||||||
|
|
||||||
|
// 执行跳转
|
||||||
|
router.push({ name: routeName })
|
||||||
|
}
|
||||||
|
|
||||||
|
watch(route, () => {
|
||||||
|
if (route.meta.attr != '') {
|
||||||
|
oneMenuActive.value = route.matched[1].name
|
||||||
|
twoMenuData.value = route.matched[1].children ?? []
|
||||||
|
} else {
|
||||||
|
// 多应用
|
||||||
|
if (siteInfo?.apps.length > 1) {
|
||||||
|
twoMenuData.value = route.matched[1].children
|
||||||
|
oneMenuActive.value = route.matched[1].name
|
||||||
|
} else {
|
||||||
|
// 单应用
|
||||||
|
const oneMenu = route.matched[1]
|
||||||
|
if (oneMenu.meta.addon == '') {
|
||||||
|
oneMenuActive.value = route.matched[1].name
|
||||||
|
twoMenuData.value = route.matched[1].children ?? []
|
||||||
|
} else {
|
||||||
|
if (oneMenu.meta.addon == siteInfo?.apps[0].key) {
|
||||||
|
oneMenuActive.value = route.matched[2].name
|
||||||
|
twoMenuData.value = route.matched[2].children ?? []
|
||||||
|
} else {
|
||||||
|
oneMenuActive.value = route.matched[1].name
|
||||||
|
twoMenuData.value = route.matched[1].children ?? []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const addonKeys = storage.get('defaultAppList')
|
||||||
|
const addonAllKeys = getAddonAllKeys(addonKeys);
|
||||||
|
twoMenuData.value = twoMenuData.value.filter((child) =>{
|
||||||
|
return !child.name || !addonAllKeys.includes(child.name);
|
||||||
|
});
|
||||||
|
if(oneMenuActive.value == 'addon'){
|
||||||
|
// 处理特殊菜单并插入到 twoMenuData 中(与 addon_list 同级)
|
||||||
|
const processedSpecialMenus = handleSpecialMenus();
|
||||||
|
if (processedSpecialMenus.length) {
|
||||||
|
// 先找到 addon_list 在 twoMenuData 中的索引
|
||||||
|
const addonListIndex = twoMenuData.value.findIndex(
|
||||||
|
(item) => item.name === 'addon_list'
|
||||||
|
);
|
||||||
|
if (addonListIndex !== -1) {
|
||||||
|
// 将特殊菜单插入到 addon_list 后面(同级)
|
||||||
|
twoMenuData.value.splice(
|
||||||
|
addonListIndex + 1,
|
||||||
|
0,
|
||||||
|
...processedSpecialMenus
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
// 如果没有 addon_list,直接将特殊菜单添加到 twoMenuData 中
|
||||||
|
twoMenuData.value.push(...processedSpecialMenus);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, { immediate: true })
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
@ -309,6 +445,27 @@ watchEffect(() => {
|
|||||||
.el-menu-item{
|
.el-menu-item{
|
||||||
padding-left: 20px !important;
|
padding-left: 20px !important;
|
||||||
}
|
}
|
||||||
|
.el-sub-menu{
|
||||||
|
.el-sub-menu__title{
|
||||||
|
margin: 0 8px 2px;
|
||||||
|
height: 40px;
|
||||||
|
padding-left: 18px;
|
||||||
|
border-radius: 2px;
|
||||||
|
span{
|
||||||
|
height: 40px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
&:hover{
|
||||||
|
background-color: transparent;
|
||||||
|
color: var(--el-color-primary);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.el-menu-item{
|
||||||
|
padding-left: 30px !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<template v-if="meta.show">
|
<template v-if="meta.show">
|
||||||
<el-sub-menu v-if="routes.children" :index="String(routes.name)">
|
<el-sub-menu v-if="hasVisibleChild" :index="String(routes.name)">
|
||||||
<template #title>
|
<template #title>
|
||||||
<div class="w-[16px] h-[16px] relative flex items-center" v-if="props.level == 1">
|
<div class="w-[16px] h-[16px] relative flex items-center" v-if="props.level == 1">
|
||||||
<icon v-if="meta.icon" :name="meta.icon" class="absolute !w-auto" />
|
<icon v-if="meta.icon" :name="meta.icon" class="absolute !w-auto" />
|
||||||
@ -15,7 +15,7 @@
|
|||||||
</template>
|
</template>
|
||||||
</el-sub-menu>
|
</el-sub-menu>
|
||||||
<template v-else>
|
<template v-else>
|
||||||
<el-menu-item :index="String(routes.name)" @click="router.push({ name: routes.name })" v-if="meta.addon && meta.parent_route && meta.parent_route.addon == ''">
|
<el-menu-item :index="String(routes.name)" @click="handleJump(routes.name)" v-if="meta.addon && meta.parent_route && meta.parent_route.addon == ''">
|
||||||
<template #title>
|
<template #title>
|
||||||
<div class="w-[16px] h-[16px] relative flex items-center" v-if="props.level == 1">
|
<div class="w-[16px] h-[16px] relative flex items-center" v-if="props.level == 1">
|
||||||
<icon v-if="meta.icon" :name="meta.icon" class="absolute !w-auto" />
|
<icon v-if="meta.icon" :name="meta.icon" class="absolute !w-auto" />
|
||||||
@ -23,7 +23,7 @@
|
|||||||
<span class="ml-[10px]">{{ meta.title }}</span>
|
<span class="ml-[10px]">{{ meta.title }}</span>
|
||||||
</template>
|
</template>
|
||||||
</el-menu-item>
|
</el-menu-item>
|
||||||
<el-menu-item :index="String(routes.name)" @click="router.push({ name: routes.name })" v-else>
|
<el-menu-item :index="String(routes.name)" @click="handleJump(routes.name)" v-else>
|
||||||
<template #title>
|
<template #title>
|
||||||
<div class="w-[16px] h-[16px] relative flex items-center" v-if="props.level == 1">
|
<div class="w-[16px] h-[16px] relative flex items-center" v-if="props.level == 1">
|
||||||
<icon v-if="meta.icon" :name="meta.icon" class="absolute !w-auto" />
|
<icon v-if="meta.icon" :name="meta.icon" class="absolute !w-auto" />
|
||||||
@ -39,11 +39,12 @@
|
|||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { useRouter, useRoute } from 'vue-router'
|
import { useRouter, useRoute } from 'vue-router'
|
||||||
import { ref, computed, watch, onMounted } from 'vue'
|
import { ref, computed, watch , onMounted, onUnmounted} from 'vue'
|
||||||
import menuItem from './menu-item.vue'
|
import menuItem from './menu-item.vue'
|
||||||
import useSystemStore from '@/stores/modules/system'
|
import useSystemStore from '@/stores/modules/system'
|
||||||
import useUserStore from '@/stores/modules/user'
|
import useUserStore from '@/stores/modules/user'
|
||||||
import storage from '@/utils/storage'
|
import storage from '@/utils/storage'
|
||||||
|
import { findFirstValidRoute ,formatRouters} from '@/router/routers'
|
||||||
|
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
@ -62,6 +63,9 @@ const props = defineProps({
|
|||||||
const systemStore = useSystemStore()
|
const systemStore = useSystemStore()
|
||||||
const meta = computed(() => props.routes.meta)
|
const meta = computed(() => props.routes.meta)
|
||||||
|
|
||||||
|
// 存储所有特殊菜单的name
|
||||||
|
const specialMenuNames = ref<string[]>([])
|
||||||
|
const specialMenuNamesLevel1 = ref<string[]>([])
|
||||||
const addons = computed(() => {
|
const addons = computed(() => {
|
||||||
const addons:Record<string, any> = {}
|
const addons:Record<string, any> = {}
|
||||||
userStore.siteInfo?.apps.forEach((item: any) => { addons[item.key] = item })
|
userStore.siteInfo?.apps.forEach((item: any) => { addons[item.key] = item })
|
||||||
@ -72,7 +76,12 @@ const addons = computed(() => {
|
|||||||
const systemAddonKeys = computed(() => {
|
const systemAddonKeys = computed(() => {
|
||||||
return userStore.siteInfo?.site_addons.map((item: any) => item.key)
|
return userStore.siteInfo?.site_addons.map((item: any) => item.key)
|
||||||
})
|
})
|
||||||
|
const hasVisibleChild = computed(() => {
|
||||||
|
if (!props.routes.children || !Array.isArray(props.routes.children)) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return props.routes.children.some(child => child.meta?.show === 1)
|
||||||
|
})
|
||||||
const addonRouters: Record<string, any> = {}
|
const addonRouters: Record<string, any> = {}
|
||||||
routers.forEach(item => {
|
routers.forEach(item => {
|
||||||
item.original_name = item.name
|
item.original_name = item.name
|
||||||
@ -86,8 +95,135 @@ routers.forEach(item => {
|
|||||||
|
|
||||||
const addonsMenus = ref(null)
|
const addonsMenus = ref(null)
|
||||||
|
|
||||||
|
// 提取所有特殊菜单的name
|
||||||
|
const collectSpecialMenuNames = (menus: any[]) => {
|
||||||
|
const names: string[] = []
|
||||||
|
const traverse = (children: any[]) => {
|
||||||
|
children.forEach(child => {
|
||||||
|
if (child.name) {
|
||||||
|
names.push(child.name)
|
||||||
|
}
|
||||||
|
// 递归处理子菜单
|
||||||
|
if (child.children && Array.isArray(child.children)) {
|
||||||
|
traverse(child.children)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
menus.forEach(menu => {
|
||||||
|
if (menu.children && Array.isArray(menu.children)) {
|
||||||
|
traverse(menu.children)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return names
|
||||||
|
}
|
||||||
|
// 提取所有一级特殊菜单的name
|
||||||
|
const collectSpecialMenuNamesLevel1 = (menus: any[]) =>{
|
||||||
|
const names: string[] = []
|
||||||
|
menus.forEach(menu => {
|
||||||
|
if (menu.name) {
|
||||||
|
names.push(menu.name)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return names
|
||||||
|
}
|
||||||
|
|
||||||
|
// 1. 从 addonKeys 中提取所有需要匹配的 key(核心:遍历所有分类的 list)
|
||||||
|
const getAddonAllKeys = (addonData) => {
|
||||||
|
// 先判断 addonKeys 是否有效,避免报错
|
||||||
|
if (!addonData || typeof addonData !== 'object') return [];
|
||||||
|
|
||||||
|
// 存储所有分类下的 key
|
||||||
|
const allKeys = [];
|
||||||
|
// 遍历 addonKeys.data 下的所有分类(如 marketing_active、marketing_tool)
|
||||||
|
Object.values(addonData).forEach(category => {
|
||||||
|
// 每个分类下的 list 可能为空,先判断
|
||||||
|
if (Array.isArray(category.list)) {
|
||||||
|
// 提取当前分类 list 里的所有 key,push 到 allKeys
|
||||||
|
category.list.forEach(item => {
|
||||||
|
if (item.key) allKeys.push(item.key);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return allKeys;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 处理 specialMenusKeys 子菜单 show 的方法
|
||||||
|
const handleSpecialMenus = () => {
|
||||||
|
const specialMenusKeys = storage.get('specialAppList')
|
||||||
|
if (Array.isArray(specialMenusKeys) && specialMenusKeys.length) {
|
||||||
|
const processedSpecialMenus = JSON.parse(JSON.stringify(specialMenusKeys));
|
||||||
|
const activeAppKey = storage.get('activeAppKey');
|
||||||
|
|
||||||
|
// 收集所有特殊菜单的name
|
||||||
|
processedSpecialMenus.forEach(menu => {
|
||||||
|
if (menu.children && Array.isArray(menu.children)) {
|
||||||
|
const traverseChildren = (children) => {
|
||||||
|
children.forEach(child => {
|
||||||
|
if (child && child.is_show !== undefined) {
|
||||||
|
child.is_show = (child.menu_key === activeAppKey) ? 1 : 0;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
traverseChildren(menu.children);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// 过滤掉 children 为空的特殊菜单
|
||||||
|
const filteredSpecialMenus = processedSpecialMenus.filter(menu => {
|
||||||
|
return menu.children && menu.children.length > 0;
|
||||||
|
});
|
||||||
|
return formatRouters(filteredSpecialMenus);
|
||||||
|
}
|
||||||
|
return [];
|
||||||
|
};
|
||||||
|
|
||||||
|
// 统一处理跳转逻辑
|
||||||
|
const handleJump = (routeName: string) => {
|
||||||
|
// 检查目标路由是否在特殊菜单列表中
|
||||||
|
const isInSpecialMenus = specialMenuNames.value.includes(routeName)
|
||||||
|
// 核心逻辑:如果不在特殊菜单中,就删除activeAppKey
|
||||||
|
if (!isInSpecialMenus) {
|
||||||
|
storage.remove('activeAppKey')
|
||||||
|
} else {
|
||||||
|
}
|
||||||
|
// 点击特殊菜单的一级,跳转应用列表
|
||||||
|
if (specialMenuNamesLevel1.value.includes(routeName)) {
|
||||||
|
routeName = 'addon_list'
|
||||||
|
}
|
||||||
|
|
||||||
|
// 执行跳转
|
||||||
|
router.push({ name: routeName })
|
||||||
|
}
|
||||||
|
|
||||||
watch(route, () => {
|
watch(route, () => {
|
||||||
|
const addonKeys = storage.get('defaultAppList')
|
||||||
|
// console.log('addonKeys', addonKeys)
|
||||||
if (props.routes.name == 'addon_list') {
|
if (props.routes.name == 'addon_list') {
|
||||||
|
const addonAllKeys = getAddonAllKeys(addonKeys);
|
||||||
|
// 步骤2:过滤 children,保留 name 不在 addonAllKeys 中的项
|
||||||
|
if (props.routes.children) {
|
||||||
|
props.routes.children = props.routes.children.filter(child => {
|
||||||
|
// 若 child 没有 name 或 name 不在 addonAllKeys 中,保留;否则删除
|
||||||
|
return !child.name || !addonAllKeys.includes(child.name);
|
||||||
|
});
|
||||||
|
// 处理 specialMenusKeys,根据 activeAppKey 设置子菜单 show
|
||||||
|
const processedSpecialMenus = handleSpecialMenus();
|
||||||
|
if (processedSpecialMenus.length) {
|
||||||
|
const newChildren = [...(props.routes.children || [])];
|
||||||
|
processedSpecialMenus.forEach(special => {
|
||||||
|
const index = newChildren.findIndex(child => child.name === special.name);
|
||||||
|
if (index !== -1) {
|
||||||
|
// 存在就替换(更新 show)
|
||||||
|
newChildren[index] = special;
|
||||||
|
} else {
|
||||||
|
newChildren.push(special);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
props.routes.children = newChildren;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
if (systemAddonKeys.value.includes(route.meta.addon) && addonRouters[route.meta.addon]) {
|
if (systemAddonKeys.value.includes(route.meta.addon) && addonRouters[route.meta.addon]) {
|
||||||
addonsMenus.value = addonRouters[route.meta.addon]
|
addonsMenus.value = addonRouters[route.meta.addon]
|
||||||
} else if (route.meta.attr && addonRouters[route.meta.attr]) {
|
} else if (route.meta.attr && addonRouters[route.meta.attr]) {
|
||||||
@ -97,7 +233,7 @@ watch(route, () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const marketingKeys = storage.get('darksideMarketingKeys')
|
const marketingKeys = storage.get('defaultMarketingKeys')
|
||||||
const matchedName = route.matched[1]?.name
|
const matchedName = route.matched[1]?.name
|
||||||
if (props.routes.name == 'marketing_list') {
|
if (props.routes.name == 'marketing_list') {
|
||||||
if (marketingKeys && marketingKeys.includes(matchedName)) {
|
if (marketingKeys && marketingKeys.includes(matchedName)) {
|
||||||
@ -107,7 +243,42 @@ watch(route, () => {
|
|||||||
addonsMenus.value = null
|
addonsMenus.value = null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}, { immediate: true })
|
}, { immediate: true })
|
||||||
|
|
||||||
|
// 监听 localStorage 中 activeAppKey 的变化
|
||||||
|
onMounted(() => {
|
||||||
|
const processedSpecialMenus = handleSpecialMenus();
|
||||||
|
specialMenuNames.value = collectSpecialMenuNames(processedSpecialMenus)
|
||||||
|
specialMenuNamesLevel1.value = collectSpecialMenuNamesLevel1(processedSpecialMenus)
|
||||||
|
const handleStorageChange = (event: StorageEvent) => {
|
||||||
|
if (event.key === 'activeAppKey') {
|
||||||
|
if (props.routes.name == 'addon_list') {
|
||||||
|
const processedSpecialMenus = handleSpecialMenus();
|
||||||
|
if (processedSpecialMenus.length && props.routes.children) {
|
||||||
|
const newChildren = [...(props.routes.children || [])];
|
||||||
|
processedSpecialMenus.forEach(special => {
|
||||||
|
const index = newChildren.findIndex(child => child.name === special.name);
|
||||||
|
if (index !== -1) {
|
||||||
|
// 存在就替换(更新 show)
|
||||||
|
newChildren[index] = special;
|
||||||
|
} else {
|
||||||
|
newChildren.push(special);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
props.routes.children = newChildren;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
window.addEventListener('storage', handleStorageChange);
|
||||||
|
|
||||||
|
// 组件卸载时移除事件监听
|
||||||
|
onUnmounted(() => {
|
||||||
|
window.removeEventListener('storage', handleStorageChange);
|
||||||
|
});
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
|
|||||||
@ -30,9 +30,9 @@ import useSystemStore from '@/stores/modules/system'
|
|||||||
import useUserStore from '@/stores/modules/user'
|
import useUserStore from '@/stores/modules/user'
|
||||||
import menuItem from './menu-item.vue'
|
import menuItem from './menu-item.vue'
|
||||||
import { img } from '@/utils/common'
|
import { img } from '@/utils/common'
|
||||||
import { findFirstValidRoute } from '@/router/routers'
|
import { findFirstValidRoute ,formatRouters} from '@/router/routers'
|
||||||
import { getShowMarketing } from '@/app/api/site'
|
|
||||||
import storage from '@/utils/storage'
|
import storage from '@/utils/storage'
|
||||||
|
import {getShowApp, getShowSpecialMenu} from '@/app/api/site'
|
||||||
|
|
||||||
const systemStore = useSystemStore()
|
const systemStore = useSystemStore()
|
||||||
const userStore = useUserStore()
|
const userStore = useUserStore()
|
||||||
@ -46,20 +46,26 @@ const logoUrl = computed(() => {
|
|||||||
return userStore.siteInfo.icon ? userStore.siteInfo.icon : systemStore.website.icon
|
return userStore.siteInfo.icon ? userStore.siteInfo.icon : systemStore.website.icon
|
||||||
})
|
})
|
||||||
|
|
||||||
const getMarketingList = async () => {
|
const appList = ref<Record<string, any>[]>([])
|
||||||
const res = await getShowMarketing()
|
|
||||||
const marketingList = res.data
|
const getAppList = async () => {
|
||||||
const marketingKeys = marketingList?.marketing?.list?.map(item => item.key) ?? []
|
const res = await getShowApp()
|
||||||
// menuData.value.forEach((item, index, arr) => {
|
appList.value = res.data
|
||||||
// if (marketingKeys.includes(item.name)) {
|
|
||||||
// arr.splice(index, 1)
|
storage.set({ key: 'defaultAppList', data: appList.value })
|
||||||
// }
|
}
|
||||||
// })
|
const specialList = ref<Record<string, any>[]>([])
|
||||||
storage.set({ key: 'darksideMarketingKeys', data: marketingKeys })
|
const getShowSpecialMenuList = async () => {
|
||||||
|
const res = await getShowSpecialMenu()
|
||||||
|
// specialList.value = formatRouters(res.data.list)
|
||||||
|
specialList.value = res.data.list
|
||||||
|
storage.set({ key: 'specialAppList', data: specialList.value })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
getMarketingList()
|
getAppList()
|
||||||
|
getShowSpecialMenuList()
|
||||||
})
|
})
|
||||||
|
|
||||||
routers.forEach((item, index) => {
|
routers.forEach((item, index) => {
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<template v-if="meta.show">
|
<template v-if="meta.show">
|
||||||
<el-sub-menu v-if="routes.children" :index="String(routes.name)">
|
<el-sub-menu v-if="hasVisibleChild" :index="String(routes.name)">
|
||||||
<template #title>
|
<template #title>
|
||||||
<div class="w-[16px] h-[16px] relative flex items-center" v-if="props.level == 1">
|
<div class="w-[16px] h-[16px] relative flex items-center" v-if="props.level == 1">
|
||||||
<icon v-if="meta.icon" :name="meta.icon" class="absolute !w-auto" />
|
<icon v-if="meta.icon" :name="meta.icon" class="absolute !w-auto" />
|
||||||
@ -15,7 +15,7 @@
|
|||||||
</template>
|
</template>
|
||||||
</el-sub-menu>
|
</el-sub-menu>
|
||||||
<template v-else>
|
<template v-else>
|
||||||
<el-menu-item :index="String(routes.name)" @click="router.push({ name: routes.name })" v-if="meta.addon && meta.parent_route && meta.parent_route.addon == ''">
|
<el-menu-item :index="String(routes.name)" @click="handleJump(routes.name)" v-if="meta.addon && meta.parent_route && meta.parent_route.addon == ''">
|
||||||
<template #title>
|
<template #title>
|
||||||
<div class="w-[16px] h-[16px] relative flex items-center" v-if="props.level == 1">
|
<div class="w-[16px] h-[16px] relative flex items-center" v-if="props.level == 1">
|
||||||
<icon v-if="meta.icon" :name="meta.icon" class="absolute !w-auto" />
|
<icon v-if="meta.icon" :name="meta.icon" class="absolute !w-auto" />
|
||||||
@ -23,7 +23,7 @@
|
|||||||
<span class="ml-[10px]">{{ meta.title }}</span>
|
<span class="ml-[10px]">{{ meta.title }}</span>
|
||||||
</template>
|
</template>
|
||||||
</el-menu-item>
|
</el-menu-item>
|
||||||
<el-menu-item :index="String(routes.name)" @click="router.push({ name: routes.name })" v-else>
|
<el-menu-item :index="String(routes.name)" @click="handleJump(routes.name)" v-else>
|
||||||
<template #title>
|
<template #title>
|
||||||
<div class="w-[16px] h-[16px] relative flex items-center" v-if="props.level == 1">
|
<div class="w-[16px] h-[16px] relative flex items-center" v-if="props.level == 1">
|
||||||
<icon v-if="meta.icon" :name="meta.icon" class="absolute !w-auto" />
|
<icon v-if="meta.icon" :name="meta.icon" class="absolute !w-auto" />
|
||||||
@ -34,16 +34,16 @@
|
|||||||
</template>
|
</template>
|
||||||
<div v-if="routes.is_border" class="!border-0 !border-t-[1px] border-solid mx-[25px] bg-[#f7f7f7] my-[5px]"></div>
|
<div v-if="routes.is_border" class="!border-0 !border-t-[1px] border-solid mx-[25px] bg-[#f7f7f7] my-[5px]"></div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { useRouter, useRoute } from 'vue-router'
|
import { useRouter, useRoute } from 'vue-router'
|
||||||
import { ref, computed, watch } from 'vue'
|
import { ref, computed, watch , onMounted, onUnmounted} from 'vue'
|
||||||
import menuItem from './menu-item.vue'
|
import menuItem from './menu-item.vue'
|
||||||
import useSystemStore from '@/stores/modules/system'
|
import useSystemStore from '@/stores/modules/system'
|
||||||
import useUserStore from '@/stores/modules/user'
|
import useUserStore from '@/stores/modules/user'
|
||||||
import storage from '@/utils/storage'
|
import storage from '@/utils/storage'
|
||||||
|
import { findFirstValidRoute ,formatRouters} from '@/router/routers'
|
||||||
|
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
@ -62,8 +62,11 @@ const props = defineProps({
|
|||||||
const systemStore = useSystemStore()
|
const systemStore = useSystemStore()
|
||||||
const meta = computed(() => props.routes.meta)
|
const meta = computed(() => props.routes.meta)
|
||||||
|
|
||||||
|
// 存储所有特殊菜单的name
|
||||||
|
const specialMenuNames = ref<string[]>([])
|
||||||
|
const specialMenuNamesLevel1 = ref<string[]>([])
|
||||||
const addons = computed(() => {
|
const addons = computed(() => {
|
||||||
const addons:Record<string, any> = {}
|
const addons: Record<string, any> = {}
|
||||||
userStore.siteInfo?.apps.forEach((item: any) => { addons[item.key] = item })
|
userStore.siteInfo?.apps.forEach((item: any) => { addons[item.key] = item })
|
||||||
userStore.siteInfo?.site_addons.forEach((item: any) => { addons[item.key] = item })
|
userStore.siteInfo?.site_addons.forEach((item: any) => { addons[item.key] = item })
|
||||||
return addons
|
return addons
|
||||||
@ -72,7 +75,12 @@ const addons = computed(() => {
|
|||||||
const systemAddonKeys = computed(() => {
|
const systemAddonKeys = computed(() => {
|
||||||
return userStore.siteInfo?.site_addons.map((item: any) => item.key)
|
return userStore.siteInfo?.site_addons.map((item: any) => item.key)
|
||||||
})
|
})
|
||||||
|
const hasVisibleChild = computed(() => {
|
||||||
|
if (!props.routes.children || !Array.isArray(props.routes.children)) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return props.routes.children.some(child => child.meta?.show === 1)
|
||||||
|
})
|
||||||
const addonRouters: Record<string, any> = {}
|
const addonRouters: Record<string, any> = {}
|
||||||
routers.forEach(item => {
|
routers.forEach(item => {
|
||||||
item.original_name = item.name
|
item.original_name = item.name
|
||||||
@ -86,8 +94,125 @@ routers.forEach(item => {
|
|||||||
|
|
||||||
const addonsMenus = ref(null)
|
const addonsMenus = ref(null)
|
||||||
|
|
||||||
|
// 提取所有特殊菜单的name
|
||||||
|
const collectSpecialMenuNames = (menus: any[]) => {
|
||||||
|
const names: string[] = []
|
||||||
|
const traverse = (children: any[]) => {
|
||||||
|
children.forEach(child => {
|
||||||
|
if (child.name) {
|
||||||
|
names.push(child.name)
|
||||||
|
}
|
||||||
|
// 递归处理子菜单
|
||||||
|
if (child.children && Array.isArray(child.children)) {
|
||||||
|
traverse(child.children)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
menus.forEach(menu => {
|
||||||
|
if (menu.children && Array.isArray(menu.children)) {
|
||||||
|
traverse(menu.children)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return names
|
||||||
|
}
|
||||||
|
// 提取所有一级特殊菜单的name
|
||||||
|
const collectSpecialMenuNamesLevel1 = (menus: any[]) =>{
|
||||||
|
const names: string[] = []
|
||||||
|
menus.forEach(menu => {
|
||||||
|
if (menu.name) {
|
||||||
|
names.push(menu.name)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return names
|
||||||
|
}
|
||||||
|
|
||||||
|
// 从 addonKeys 中提取所有需要匹配的 key
|
||||||
|
const getAddonAllKeys = (addonData) => {
|
||||||
|
if (!addonData || typeof addonData !== 'object') return [];
|
||||||
|
const allKeys = [];
|
||||||
|
Object.values(addonData).forEach(category => {
|
||||||
|
if (Array.isArray(category.list)) {
|
||||||
|
category.list.forEach(item => {
|
||||||
|
if (item.key) allKeys.push(item.key);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return allKeys;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 处理 specialMenusKeys 子菜单 show 的方法
|
||||||
|
const handleSpecialMenus = () => {
|
||||||
|
const specialMenusKeys = storage.get('specialAppList')
|
||||||
|
if (Array.isArray(specialMenusKeys) && specialMenusKeys.length) {
|
||||||
|
const processedSpecialMenus = JSON.parse(JSON.stringify(specialMenusKeys));
|
||||||
|
const activeAppKey = storage.get('activeAppKey');
|
||||||
|
|
||||||
|
// 收集所有特殊菜单的name
|
||||||
|
processedSpecialMenus.forEach(menu => {
|
||||||
|
if (menu.children && Array.isArray(menu.children)) {
|
||||||
|
const traverseChildren = (children) => {
|
||||||
|
children.forEach(child => {
|
||||||
|
if (child && child.is_show !== undefined) {
|
||||||
|
child.is_show = (child.menu_key === activeAppKey) ? 1 : 0;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
traverseChildren(menu.children);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// 过滤掉 children 为空的特殊菜单
|
||||||
|
const filteredSpecialMenus = processedSpecialMenus.filter(menu => {
|
||||||
|
return menu.children && menu.children.length > 0;
|
||||||
|
});
|
||||||
|
return formatRouters(filteredSpecialMenus);
|
||||||
|
}
|
||||||
|
return [];
|
||||||
|
};
|
||||||
|
|
||||||
|
// 统一处理跳转逻辑
|
||||||
|
const handleJump = (routeName: string) => {
|
||||||
|
// 检查目标路由是否在特殊菜单列表中
|
||||||
|
const isInSpecialMenus = specialMenuNames.value.includes(routeName)
|
||||||
|
// 核心逻辑:如果不在特殊菜单中,就删除activeAppKey
|
||||||
|
if (!isInSpecialMenus) {
|
||||||
|
storage.remove('activeAppKey')
|
||||||
|
} else {
|
||||||
|
}
|
||||||
|
// 点击特殊菜单的一级,跳转应用列表
|
||||||
|
if (specialMenuNamesLevel1.value.includes(routeName)) {
|
||||||
|
routeName = 'addon_list'
|
||||||
|
}
|
||||||
|
|
||||||
|
// 执行跳转
|
||||||
|
router.push({ name: routeName })
|
||||||
|
}
|
||||||
|
|
||||||
watch(route, () => {
|
watch(route, () => {
|
||||||
|
const addonKeys = storage.get('defaultAppList')
|
||||||
if (props.routes.name == 'addon_list') {
|
if (props.routes.name == 'addon_list') {
|
||||||
|
const addonAllKeys = getAddonAllKeys(addonKeys);
|
||||||
|
if (props.routes.children) {
|
||||||
|
// 过滤掉不需要显示的子菜单
|
||||||
|
props.routes.children = props.routes.children.filter(child => {
|
||||||
|
return !child.name || !addonAllKeys.includes(child.name);
|
||||||
|
});
|
||||||
|
// 处理特殊菜单
|
||||||
|
const processedSpecialMenus = handleSpecialMenus();
|
||||||
|
if (processedSpecialMenus.length) {
|
||||||
|
const newChildren = [...(props.routes.children || [])];
|
||||||
|
processedSpecialMenus.forEach(special => {
|
||||||
|
const index = newChildren.findIndex(child => child.name === special.name);
|
||||||
|
if (index !== -1) {
|
||||||
|
newChildren[index] = special;
|
||||||
|
} else {
|
||||||
|
newChildren.push(special);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
props.routes.children = newChildren;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (systemAddonKeys.value.includes(route.meta.addon) && addonRouters[route.meta.addon]) {
|
if (systemAddonKeys.value.includes(route.meta.addon) && addonRouters[route.meta.addon]) {
|
||||||
addonsMenus.value = addonRouters[route.meta.addon]
|
addonsMenus.value = addonRouters[route.meta.addon]
|
||||||
} else if (route.meta.attr && addonRouters[route.meta.attr]) {
|
} else if (route.meta.attr && addonRouters[route.meta.attr]) {
|
||||||
@ -107,12 +232,46 @@ watch(route, () => {
|
|||||||
addonsMenus.value = null
|
addonsMenus.value = null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// console.log('addonsMenus', props.routes)
|
||||||
}, { immediate: true })
|
}, { immediate: true })
|
||||||
|
|
||||||
|
// 监听 localStorage 中 activeAppKey 的变化
|
||||||
|
onMounted(() => {
|
||||||
|
const processedSpecialMenus = handleSpecialMenus();
|
||||||
|
specialMenuNames.value = collectSpecialMenuNames(processedSpecialMenus)
|
||||||
|
specialMenuNamesLevel1.value = collectSpecialMenuNamesLevel1(processedSpecialMenus)
|
||||||
|
const handleStorageChange = (event: StorageEvent) => {
|
||||||
|
if (event.key === 'activeAppKey') {
|
||||||
|
if (props.routes.name == 'addon_list') {
|
||||||
|
const processedSpecialMenus = handleSpecialMenus();
|
||||||
|
if (processedSpecialMenus.length && props.routes.children) {
|
||||||
|
const newChildren = [...(props.routes.children || [])];
|
||||||
|
processedSpecialMenus.forEach(special => {
|
||||||
|
const index = newChildren.findIndex(child => child.name === special.name);
|
||||||
|
if (index !== -1) {
|
||||||
|
newChildren[index] = special;
|
||||||
|
} else {
|
||||||
|
newChildren.push(special);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
props.routes.children = newChildren;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
window.addEventListener('storage', handleStorageChange);
|
||||||
|
|
||||||
|
// 组件卸载时移除事件监听
|
||||||
|
onUnmounted(() => {
|
||||||
|
window.removeEventListener('storage', handleStorageChange);
|
||||||
|
});
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
.el-sub-menu{
|
.el-sub-menu {
|
||||||
.el-icon{
|
.el-icon {
|
||||||
width: auto;
|
width: auto;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -30,8 +30,9 @@ import useSystemStore from '@/stores/modules/system'
|
|||||||
import useUserStore from '@/stores/modules/user'
|
import useUserStore from '@/stores/modules/user'
|
||||||
import menuItem from './menu-item.vue'
|
import menuItem from './menu-item.vue'
|
||||||
import { img } from '@/utils/common'
|
import { img } from '@/utils/common'
|
||||||
import { findFirstValidRoute } from '@/router/routers'
|
import { findFirstValidRoute ,formatRouters} from '@/router/routers'
|
||||||
import { getShowMarketing } from '@/app/api/site'
|
import { getShowApp,getShowSpecialMenu} from '@/app/api/site'
|
||||||
|
|
||||||
import storage from '@/utils/storage'
|
import storage from '@/utils/storage'
|
||||||
|
|
||||||
const systemStore = useSystemStore()
|
const systemStore = useSystemStore()
|
||||||
@ -46,20 +47,27 @@ const logoUrl = computed(() => {
|
|||||||
return userStore.siteInfo.icon ? userStore.siteInfo.icon : systemStore.website.icon
|
return userStore.siteInfo.icon ? userStore.siteInfo.icon : systemStore.website.icon
|
||||||
})
|
})
|
||||||
|
|
||||||
const getMarketingList = async () => {
|
const appList = ref<Record<string, any>[]>([])
|
||||||
const res = await getShowMarketing()
|
|
||||||
const marketingList = res.data
|
const getAppList = async () => {
|
||||||
const marketingKeys = marketingList?.marketing?.list?.map(item => item.key) ?? []
|
const res = await getShowApp()
|
||||||
// menuData.value.forEach((item, index, arr) => {
|
appList.value = res.data
|
||||||
// if (marketingKeys.includes(item.name)) {
|
|
||||||
// arr.splice(index, 1)
|
storage.set({ key: 'defaultAppList', data: appList.value })
|
||||||
// }
|
}
|
||||||
// })
|
const specialList = ref<Record<string, any>[]>([])
|
||||||
storage.set({ key: 'defaultMarketingKeys', data: marketingKeys })
|
const getShowSpecialMenuList = async () => {
|
||||||
|
const res = await getShowSpecialMenu()
|
||||||
|
// specialList.value = formatRouters(res.data.list)
|
||||||
|
specialList.value = res.data.list
|
||||||
|
|
||||||
|
storage.set({ key: 'specialAppList', data: specialList.value })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
getMarketingList()
|
getAppList()
|
||||||
|
getShowSpecialMenuList()
|
||||||
})
|
})
|
||||||
|
|
||||||
routers.forEach(item => {
|
routers.forEach(item => {
|
||||||
@ -87,6 +95,7 @@ routers.forEach(item => {
|
|||||||
addonRouters[item.meta.addon] = item
|
addonRouters[item.meta.addon] = item
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// console.log('menuData', menuData.value)
|
||||||
// 排序, 功能正确,改了排序后需要把菜单排序的默认值重新调整一下【多应用一级菜单,单应用二级菜单】
|
// 排序, 功能正确,改了排序后需要把菜单排序的默认值重新调整一下【多应用一级菜单,单应用二级菜单】
|
||||||
// menuData.value.sort((a, b) => {
|
// menuData.value.sort((a, b) => {
|
||||||
// if (a.meta.sort && b.meta.sort) {
|
// if (a.meta.sort && b.meta.sort) {
|
||||||
|
|||||||
@ -128,7 +128,7 @@ const dark = computed(() => {
|
|||||||
})
|
})
|
||||||
const isMenuSearch = ref(false)
|
const isMenuSearch = ref(false)
|
||||||
const routers = userStore.routers
|
const routers = userStore.routers
|
||||||
const getParentTitleChain=(meta:any) =>{
|
const getParentTitleChain = (meta: any) => {
|
||||||
let titles = []
|
let titles = []
|
||||||
let current = meta?.parent_route
|
let current = meta?.parent_route
|
||||||
|
|
||||||
@ -139,17 +139,27 @@ const getParentTitleChain=(meta:any) =>{
|
|||||||
current = current.parent_route
|
current = current.parent_route
|
||||||
}
|
}
|
||||||
|
|
||||||
return titles.join(' - ')
|
return titles.join(' - ');
|
||||||
}
|
};
|
||||||
const flattenRoutes = (routes:any, parent = null)=> {
|
|
||||||
|
// 2. 改造 flattenRoutes:增加 parentShow 参数,传递父级 show 状态
|
||||||
|
const flattenRoutes = (routes: any, parent = null, parentShow = 1) => {
|
||||||
let flat = [];
|
let flat = [];
|
||||||
|
|
||||||
routes.forEach(route => {
|
routes.forEach(route => {
|
||||||
const { path, name, meta = {}, short_title, children } = route
|
const { path, name, meta = {}, short_title, children } = route;
|
||||||
const isLeaf = meta.type ==1 && meta.show==1
|
// 关键:当前菜单的最终 show 状态 = 自身 show(默认1) && 父级 show(默认1)
|
||||||
if(isLeaf){
|
// 若父级 show 不是1,当前菜单直接隐藏,不加入列表
|
||||||
const title = meta.title || short_title || ''
|
const currentShow = meta.show === undefined ? 1 : meta.show;
|
||||||
const parentTitleChain = getParentTitleChain(meta)
|
const finalShow = currentShow && parentShow; // 父级隐藏则子级必隐藏
|
||||||
const fullTitle = parentTitleChain ? `${parentTitleChain} - ${title}` : title
|
|
||||||
|
// 叶子节点判断:type=1 + 最终 show=1(父级+自身都显示)
|
||||||
|
const isLeaf = meta.type === 1 && finalShow === 1;
|
||||||
|
|
||||||
|
if (isLeaf) {
|
||||||
|
const title = meta.title || short_title || '';
|
||||||
|
const parentTitleChain = getParentTitleChain(meta);
|
||||||
|
const fullTitle = parentTitleChain ? `${parentTitleChain} - ${title}` : title;
|
||||||
const item = {
|
const item = {
|
||||||
path,
|
path,
|
||||||
name,
|
name,
|
||||||
@ -157,17 +167,19 @@ const flattenRoutes = (routes:any, parent = null)=> {
|
|||||||
parent_title: parentTitleChain,
|
parent_title: parentTitleChain,
|
||||||
full_title: fullTitle
|
full_title: fullTitle
|
||||||
};
|
};
|
||||||
|
|
||||||
flat.push(item);
|
flat.push(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 递归处理子菜单:传递当前菜单的 finalShow 作为子级的 parentShow
|
||||||
if (children && children.length > 0) {
|
if (children && children.length > 0) {
|
||||||
flat = flat.concat(flattenRoutes(children, route))
|
flat = flat.concat(flattenRoutes(children, route, finalShow));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return flat;
|
return flat;
|
||||||
}
|
};
|
||||||
const flatRoutes = flattenRoutes(routers)
|
|
||||||
|
const flatRoutes = flattenRoutes(routers);
|
||||||
const selectedRoute = ref('')
|
const selectedRoute = ref('')
|
||||||
const handleRouteSelect = (name:any) => {
|
const handleRouteSelect = (name:any) => {
|
||||||
if (name) {
|
if (name) {
|
||||||
|
|||||||
@ -1,18 +1,18 @@
|
|||||||
<template>
|
<template>
|
||||||
<template v-if="meta.show">
|
<template v-if="meta.show">
|
||||||
<el-sub-menu v-if="routes.children" :index="String(routes.name)">
|
<el-sub-menu v-if="hasVisibleChild" :index="String(routes.name)">
|
||||||
<template #title>
|
<template #title>
|
||||||
<span :class="['ml-[10px]']">{{ meta.title }}</span>
|
<span :class="['ml-[10px]']">{{ meta.title }}</span>
|
||||||
</template>
|
</template>
|
||||||
<menu-item v-for="(route, index) in routes.children" :routes="route" :key="index" />
|
<menu-item v-for="(route, index) in routes.children" :routes="route" :key="index" />
|
||||||
</el-sub-menu>
|
</el-sub-menu>
|
||||||
<template v-else>
|
<template v-else>
|
||||||
<el-menu-item :index="String(routes.name)" @click="router.push({ name: routes.name })" v-if="meta.addon && meta.parent_route && meta.parent_route.addon == ''">
|
<el-menu-item :index="String(routes.name)" @click="handleJump(routes.name)" v-if="meta.addon && meta.parent_route && meta.parent_route.addon == ''">
|
||||||
<template #title>
|
<template #title>
|
||||||
<span :class="[{'text-[15px]': routes.meta.class == 1}, {'text-[14px]': routes.meta.class != 1}, {'ml-[10px]': routes.meta.class == 2, 'ml-[15px]': routes.meta.class == 3}]">{{ meta.title }}</span>
|
<span :class="[{'text-[15px]': routes.meta.class == 1}, {'text-[14px]': routes.meta.class != 1}, {'ml-[10px]': routes.meta.class == 2, 'ml-[15px]': routes.meta.class == 3}]">{{ meta.title }}</span>
|
||||||
</template>
|
</template>
|
||||||
</el-menu-item>
|
</el-menu-item>
|
||||||
<el-menu-item :index="String(routes.name)" @click="router.push({ name: routes.name })" v-else>
|
<el-menu-item :index="String(routes.name)" @click="handleJump(routes.name)" v-else>
|
||||||
<template #title>
|
<template #title>
|
||||||
<span :class="[{'text-[15px]': routes.meta.class == 1}, {'text-[14px]': routes.meta.class != 1}, {'ml-[10px]': routes.meta.class == 2, 'ml-[15px]': routes.meta.class == 3}]">{{ meta.title }}</span>
|
<span :class="[{'text-[15px]': routes.meta.class == 1}, {'text-[14px]': routes.meta.class != 1}, {'ml-[10px]': routes.meta.class == 2, 'ml-[15px]': routes.meta.class == 3}]">{{ meta.title }}</span>
|
||||||
</template>
|
</template>
|
||||||
@ -28,6 +28,7 @@ import { useRouter } from 'vue-router'
|
|||||||
import { computed } from 'vue'
|
import { computed } from 'vue'
|
||||||
import menuItem from './menu-item.vue'
|
import menuItem from './menu-item.vue'
|
||||||
import useUserStore from '@/stores/modules/user'
|
import useUserStore from '@/stores/modules/user'
|
||||||
|
import storage from '@/utils/storage'
|
||||||
|
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
@ -40,13 +41,37 @@ const userStore = useUserStore()
|
|||||||
const siteInfo = userStore.siteInfo
|
const siteInfo = userStore.siteInfo
|
||||||
const meta = computed(() => props.routes.meta)
|
const meta = computed(() => props.routes.meta)
|
||||||
|
|
||||||
|
const hasVisibleChild = computed(() => {
|
||||||
|
if (!props.routes.children || !Array.isArray(props.routes.children)) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return props.routes.children.some(child => child.meta?.show === 1)
|
||||||
|
})
|
||||||
|
|
||||||
const addons = computed(() => {
|
const addons = computed(() => {
|
||||||
const addons:Record<string, any> = {}
|
const addons:Record<string, any> = {}
|
||||||
siteInfo?.apps.forEach((item: any) => { addons[item.key] = item })
|
siteInfo?.apps.forEach((item: any) => { addons[item.key] = item })
|
||||||
siteInfo?.site_addons.forEach((item: any) => { addons[item.key] = item })
|
siteInfo?.site_addons.forEach((item: any) => { addons[item.key] = item })
|
||||||
return addons
|
return addons
|
||||||
})
|
})
|
||||||
|
// 统一处理跳转逻辑
|
||||||
|
const handleJump = (routeName: string) => {
|
||||||
|
// 检查目标路由是否在特殊菜单列表中
|
||||||
|
const specialMenuNames = storage.get('specialMenuNames')
|
||||||
|
const specialMenuNamesLevel1 = storage.get('specialMenuNamesLevel1')
|
||||||
|
const isInSpecialMenus = specialMenuNames.includes(routeName)
|
||||||
|
// 核心逻辑:如果不在特殊菜单中,就删除activeAppKey
|
||||||
|
if (!isInSpecialMenus) {
|
||||||
|
storage.remove('activeAppKey')
|
||||||
|
} else {
|
||||||
|
}
|
||||||
|
// 点击特殊菜单的一级,跳转应用列表
|
||||||
|
if (specialMenuNamesLevel1.includes(routeName)) {
|
||||||
|
routeName = 'addon_list'
|
||||||
|
}
|
||||||
|
// 执行跳转
|
||||||
|
router.push({ name: routeName })
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
|
|||||||
@ -20,7 +20,7 @@
|
|||||||
<el-scrollbar class="h-[calc( 100vh - 64px )]">
|
<el-scrollbar class="h-[calc( 100vh - 64px )]">
|
||||||
<el-menu :default-active="oneMenuActive" :router="true" class="aside-menu" :unique-opened="true" :collapse="systemStore.menuIsCollapse">
|
<el-menu :default-active="oneMenuActive" :router="true" class="aside-menu" :unique-opened="true" :collapse="systemStore.menuIsCollapse">
|
||||||
<template v-for="(item, index) in oneMenuData" :key="index">
|
<template v-for="(item, index) in oneMenuData" :key="index">
|
||||||
<el-menu-item :index="item.original_name" @click="router.push({ name: item.name })" v-if="item.meta.show">
|
<el-menu-item :index="item.original_name" @click="handleJump(item.name)" v-if="item.meta.show">
|
||||||
<div v-if="item.meta.icon" class="w-[16px] h-[16px] relative flex justify-center">
|
<div v-if="item.meta.icon" class="w-[16px] h-[16px] relative flex justify-center">
|
||||||
<el-image class="w-[16px] h-[16px] rounded-[50%] overflow-hidden" :src="item.meta.icon" fit="fill" v-if="isUrl(item.meta.icon)"/>
|
<el-image class="w-[16px] h-[16px] rounded-[50%] overflow-hidden" :src="item.meta.icon" fit="fill" v-if="isUrl(item.meta.icon)"/>
|
||||||
<icon :name="item.meta.icon" class="absolute top-[50%] -translate-y-[50%]" v-else />
|
<icon :name="item.meta.icon" class="absolute top-[50%] -translate-y-[50%]" v-else />
|
||||||
@ -38,8 +38,8 @@
|
|||||||
</el-scrollbar>
|
</el-scrollbar>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<el-scrollbar v-if="twoMenuData.length" class="two-menu w-[132px]">
|
<el-scrollbar v-if="twoMenuData.length" class="two-menu w-[152px]">
|
||||||
<div class="w-[132px] h-[64px] flex items-center justify-center text-[16px] border-b-[1px] border-solid border-[var(--el-border-color-lighter)]">
|
<div class="w-[152px] h-[64px] flex items-center justify-center text-[16px] border-b-[1px] border-solid border-[var(--el-border-color-lighter)]">
|
||||||
{{ route.matched[1].meta.title }}
|
{{ route.matched[1].meta.title }}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -57,12 +57,13 @@
|
|||||||
import { ref, watch, computed, onMounted, watchEffect } from 'vue'
|
import { ref, watch, computed, onMounted, watchEffect } from 'vue'
|
||||||
import { useRoute, useRouter } from 'vue-router'
|
import { useRoute, useRouter } from 'vue-router'
|
||||||
import useSystemStore from '@/stores/modules/system'
|
import useSystemStore from '@/stores/modules/system'
|
||||||
import { getShowApp, getShowMarketing } from '@/app/api/site'
|
|
||||||
import useUserStore from '@/stores/modules/user'
|
import useUserStore from '@/stores/modules/user'
|
||||||
import { img, isUrl } from '@/utils/common'
|
import { img, isUrl } from '@/utils/common'
|
||||||
import { findFirstValidRoute } from '@/router/routers'
|
|
||||||
import menuItem from './menu-item.vue'
|
import menuItem from './menu-item.vue'
|
||||||
import { cloneDeep } from 'lodash-es'
|
import { cloneDeep } from 'lodash-es'
|
||||||
|
import { getShowApp,getShowSpecialMenu} from '@/app/api/site'
|
||||||
|
import { findFirstValidRoute,formatRouters } from '@/router/routers'
|
||||||
|
import storage from '@/utils/storage'
|
||||||
|
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
@ -147,20 +148,35 @@ const marketingList = ref(null)
|
|||||||
// const loading = ref(true);
|
// const loading = ref(true);
|
||||||
const oneMenuActive = ref(route.matched[1].name)
|
const oneMenuActive = ref(route.matched[1].name)
|
||||||
|
|
||||||
|
|
||||||
const getAppList = async () => {
|
const getAppList = async () => {
|
||||||
const res = await getShowApp()
|
const res = await getShowApp()
|
||||||
appList.value = res.data
|
appList.value = res.data
|
||||||
// loading.value = false;
|
|
||||||
|
storage.set({ key: 'defaultAppList', data: appList.value })
|
||||||
}
|
}
|
||||||
const getMarketingList = async () => {
|
const specialList = ref<Record<string, any>[]>([])
|
||||||
const res = await getShowMarketing()
|
const getShowSpecialMenuList = async () => {
|
||||||
marketingList.value = res.data
|
const res = await getShowSpecialMenu()
|
||||||
|
// specialList.value = formatRouters(res.data.list)
|
||||||
|
specialList.value = res.data.list
|
||||||
|
storage.set({ key: 'specialAppList', data: specialList.value })
|
||||||
}
|
}
|
||||||
onMounted(async () => {
|
|
||||||
await getAppList() // 确保数据先加载
|
const specialMenuNames = ref<string[]>([])
|
||||||
await getMarketingList()
|
const specialMenuNamesLevel1 = ref<string[]>([])
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
getAppList()
|
||||||
|
getShowSpecialMenuList()
|
||||||
|
const processedSpecialMenus = handleSpecialMenus();
|
||||||
|
specialMenuNames.value = collectSpecialMenuNames(processedSpecialMenus)
|
||||||
|
specialMenuNamesLevel1.value = collectSpecialMenuNamesLevel1(processedSpecialMenus)
|
||||||
|
storage.set({ key: 'specialMenuNames', data: specialMenuNames.value })
|
||||||
|
storage.set({ key: 'specialMenuNamesLevel1', data: specialMenuNamesLevel1.value })
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
// 让二级菜单默认展开
|
// 让二级菜单默认展开
|
||||||
const menuOption = ref([])
|
const menuOption = ref([])
|
||||||
const secondMenuShowWayFn = () => {
|
const secondMenuShowWayFn = () => {
|
||||||
@ -173,75 +189,192 @@ const secondMenuShowWayFn = () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
watchEffect(() => {
|
// watchEffect(() => {
|
||||||
// if (!appList.value || loading.value) return; // 确保数据加载完毕
|
// // if (!appList.value || loading.value) return; // 确保数据加载完毕
|
||||||
const addonKeys = appList.value?.addon?.list?.map(item => item.key) ?? []
|
// const addonKeys = appList.value?.addon?.list?.map(item => item.key) ?? []
|
||||||
const toolKeys = appList.value?.tool?.list?.map(item => item.key) ?? []
|
// const toolKeys = appList.value?.tool?.list?.map(item => item.key) ?? []
|
||||||
const allKeys = [...addonKeys, ...toolKeys]
|
// const allKeys = [...addonKeys, ...toolKeys]
|
||||||
const marketingKeys = marketingList.value?.marketing?.list?.map(item => item.key) ?? []
|
// const marketingKeys = marketingList.value?.marketing?.list?.map(item => item.key) ?? []
|
||||||
const matchedName = route.matched[1]?.name
|
// const matchedName = route.matched[1]?.name
|
||||||
if (allKeys.includes(matchedName)) {
|
// if (allKeys.includes(matchedName)) {
|
||||||
oneMenuActive.value = 'addon'
|
// oneMenuActive.value = 'addon'
|
||||||
twoMenuData.value = route.matched[1]?.children ?? []
|
// twoMenuData.value = route.matched[1]?.children ?? []
|
||||||
} else if (marketingKeys.includes(matchedName)) {
|
// } else if (marketingKeys.includes(matchedName)) {
|
||||||
oneMenuActive.value = 'active'
|
// oneMenuActive.value = 'active'
|
||||||
twoMenuData.value = route.matched[1]?.children ?? []
|
// twoMenuData.value = route.matched[1]?.children ?? []
|
||||||
} else if (route.meta.attr !== '') {
|
// } else if (route.meta.attr !== '') {
|
||||||
oneMenuActive.value = route.matched[2]?.name
|
// oneMenuActive.value = route.matched[2]?.name
|
||||||
twoMenuData.value = route.matched[1]?.children ?? []
|
// twoMenuData.value = route.matched[1]?.children ?? []
|
||||||
|
// } else {
|
||||||
|
// // 多应用
|
||||||
|
// if (siteInfo?.apps.length > 1) {
|
||||||
|
// twoMenuData.value = route.matched[1]?.children
|
||||||
|
// oneMenuActive.value = route.matched[1]?.name
|
||||||
|
// } else {
|
||||||
|
// // 单应用
|
||||||
|
// const oneMenu = route.matched[1]
|
||||||
|
// if (oneMenu.meta.addon === '') {
|
||||||
|
// oneMenuActive.value = route.matched[1]?.name
|
||||||
|
// twoMenuData.value = route.matched[1]?.children ?? []
|
||||||
|
// } else {
|
||||||
|
// if (oneMenu.meta.addon === siteInfo?.apps[0]?.key) {
|
||||||
|
// oneMenuActive.value = route.matched[2]?.name
|
||||||
|
// twoMenuData.value = route.matched[2]?.children ?? []
|
||||||
|
// } else {
|
||||||
|
// oneMenuActive.value = route.matched[1]?.name
|
||||||
|
// twoMenuData.value = route.matched[1]?.children ?? []
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// secondMenuShowWayFn()
|
||||||
|
// })
|
||||||
|
|
||||||
|
// 从 addonKeys 中提取所有需要匹配的 key
|
||||||
|
const getAddonAllKeys = (addonData) => {
|
||||||
|
if (!addonData || typeof addonData !== 'object') return [];
|
||||||
|
const allKeys = [];
|
||||||
|
Object.values(addonData).forEach(category => {
|
||||||
|
if (Array.isArray(category.list)) {
|
||||||
|
category.list.forEach(item => {
|
||||||
|
if (item.key) allKeys.push(item.key);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return allKeys;
|
||||||
|
};
|
||||||
|
// 处理 specialMenusKeys 子菜单 show 的方法
|
||||||
|
const handleSpecialMenus = () => {
|
||||||
|
const specialMenusKeys = storage.get('specialAppList')
|
||||||
|
if (Array.isArray(specialMenusKeys) && specialMenusKeys.length) {
|
||||||
|
const processedSpecialMenus = JSON.parse(JSON.stringify(specialMenusKeys));
|
||||||
|
const activeAppKey = storage.get('activeAppKey');
|
||||||
|
|
||||||
|
// 收集所有特殊菜单的name
|
||||||
|
processedSpecialMenus.forEach(menu => {
|
||||||
|
if (menu.children && Array.isArray(menu.children)) {
|
||||||
|
const traverseChildren = (children) => {
|
||||||
|
children.forEach(child => {
|
||||||
|
if (child && child.is_show !== undefined) {
|
||||||
|
child.is_show = (child.menu_key === activeAppKey) ? 1 : 0;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
traverseChildren(menu.children);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// 过滤掉 children 为空的特殊菜单
|
||||||
|
const filteredSpecialMenus = processedSpecialMenus.filter(menu => {
|
||||||
|
return menu.children && menu.children.length > 0;
|
||||||
|
});
|
||||||
|
return formatRouters(filteredSpecialMenus);
|
||||||
|
}
|
||||||
|
return [];
|
||||||
|
};
|
||||||
|
|
||||||
|
// 提取所有特殊菜单的name
|
||||||
|
const collectSpecialMenuNames = (menus: any[]) => {
|
||||||
|
const names: string[] = []
|
||||||
|
const traverse = (children: any[]) => {
|
||||||
|
children.forEach(child => {
|
||||||
|
if (child.name) {
|
||||||
|
names.push(child.name)
|
||||||
|
}
|
||||||
|
// 递归处理子菜单
|
||||||
|
if (child.children && Array.isArray(child.children)) {
|
||||||
|
traverse(child.children)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
menus.forEach(menu => {
|
||||||
|
if (menu.children && Array.isArray(menu.children)) {
|
||||||
|
traverse(menu.children)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return names
|
||||||
|
}
|
||||||
|
|
||||||
|
// 提取所有一级特殊菜单的name
|
||||||
|
const collectSpecialMenuNamesLevel1 = (menus: any[]) =>{
|
||||||
|
const names: string[] = []
|
||||||
|
menus.forEach(menu => {
|
||||||
|
if (menu.name) {
|
||||||
|
names.push(menu.name)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return names
|
||||||
|
}
|
||||||
|
|
||||||
|
// 统一处理跳转逻辑
|
||||||
|
const handleJump = (routeName: string) => {
|
||||||
|
// 检查目标路由是否在特殊菜单列表中
|
||||||
|
const isInSpecialMenus = specialMenuNames.value.includes(routeName)
|
||||||
|
// 核心逻辑:如果不在特殊菜单中,就删除activeAppKey
|
||||||
|
if (!isInSpecialMenus) {
|
||||||
|
storage.remove('activeAppKey')
|
||||||
|
} else {
|
||||||
|
}
|
||||||
|
|
||||||
|
// 执行跳转
|
||||||
|
router.push({ name: routeName })
|
||||||
|
}
|
||||||
|
|
||||||
|
watch(route, () => {
|
||||||
|
if (route.meta.attr != '') {
|
||||||
|
oneMenuActive.value = route.matched[1].name
|
||||||
|
twoMenuData.value = route.matched[1].children ?? []
|
||||||
} else {
|
} else {
|
||||||
// 多应用
|
// 多应用
|
||||||
if (siteInfo?.apps.length > 1) {
|
if (siteInfo?.apps.length > 1) {
|
||||||
twoMenuData.value = route.matched[1]?.children
|
twoMenuData.value = route.matched[1].children
|
||||||
oneMenuActive.value = route.matched[1]?.name
|
oneMenuActive.value = route.matched[1].name
|
||||||
} else {
|
} else {
|
||||||
// 单应用
|
// 单应用
|
||||||
const oneMenu = route.matched[1]
|
const oneMenu = route.matched[1]
|
||||||
if (oneMenu.meta.addon === '') {
|
if (oneMenu.meta.addon == '') {
|
||||||
oneMenuActive.value = route.matched[1]?.name
|
oneMenuActive.value = route.matched[1].name
|
||||||
twoMenuData.value = route.matched[1]?.children ?? []
|
twoMenuData.value = route.matched[1].children ?? []
|
||||||
} else {
|
} else {
|
||||||
if (oneMenu.meta.addon === siteInfo?.apps[0]?.key) {
|
if (oneMenu.meta.addon == siteInfo?.apps[0].key) {
|
||||||
oneMenuActive.value = route.matched[2]?.name
|
oneMenuActive.value = route.matched[2].name
|
||||||
twoMenuData.value = route.matched[2]?.children ?? []
|
twoMenuData.value = route.matched[2].children ?? []
|
||||||
} else {
|
} else {
|
||||||
oneMenuActive.value = route.matched[1]?.name
|
oneMenuActive.value = route.matched[1].name
|
||||||
twoMenuData.value = route.matched[1]?.children ?? []
|
twoMenuData.value = route.matched[1].children ?? []
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
secondMenuShowWayFn()
|
secondMenuShowWayFn()
|
||||||
})
|
const addonKeys = storage.get('defaultAppList')
|
||||||
|
const addonAllKeys = getAddonAllKeys(addonKeys);
|
||||||
|
twoMenuData.value = twoMenuData.value.filter((child) =>{
|
||||||
|
return !child.name || !addonAllKeys.includes(child.name);
|
||||||
|
});
|
||||||
|
if(oneMenuActive.value == 'addon'){
|
||||||
|
// 处理特殊菜单并插入到 twoMenuData 中(与 addon_list 同级)
|
||||||
|
const processedSpecialMenus = handleSpecialMenus();
|
||||||
|
if (processedSpecialMenus.length) {
|
||||||
|
// 先找到 addon_list 在 twoMenuData 中的索引
|
||||||
|
const addonListIndex = twoMenuData.value.findIndex(
|
||||||
|
(item) => item.name === 'addon_list'
|
||||||
|
);
|
||||||
|
if (addonListIndex !== -1) {
|
||||||
|
// 将特殊菜单插入到 addon_list 后面(同级)
|
||||||
|
twoMenuData.value.splice(
|
||||||
|
addonListIndex + 1,
|
||||||
|
0,
|
||||||
|
...processedSpecialMenus
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
// 如果没有 addon_list,直接将特殊菜单添加到 twoMenuData 中
|
||||||
|
twoMenuData.value.push(...processedSpecialMenus);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// watch(route, () => {
|
}, { immediate: true })
|
||||||
|
|
||||||
// if (route.meta.attr != '') {
|
|
||||||
// oneMenuActive.value = route.matched[2].name
|
|
||||||
// twoMenuData.value = route.matched[1].children ?? []
|
|
||||||
// } else {
|
|
||||||
// // 多应用
|
|
||||||
// if (siteInfo?.apps.length > 1) {
|
|
||||||
// twoMenuData.value = route.matched[1].children
|
|
||||||
// oneMenuActive.value = route.matched[1].name
|
|
||||||
// } else {
|
|
||||||
// // 单应用
|
|
||||||
// const oneMenu = route.matched[1]
|
|
||||||
// if (oneMenu.meta.addon == '') {
|
|
||||||
// oneMenuActive.value = route.matched[1].name
|
|
||||||
// twoMenuData.value = route.matched[1].children ?? []
|
|
||||||
// } else {
|
|
||||||
// if (oneMenu.meta.addon == siteInfo?.apps[0].key) {
|
|
||||||
// oneMenuActive.value = route.matched[2].name
|
|
||||||
// twoMenuData.value = route.matched[2].children ?? []
|
|
||||||
// } else {
|
|
||||||
// oneMenuActive.value = route.matched[1].name
|
|
||||||
// twoMenuData.value = route.matched[1].children ?? []
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }, { immediate: true })
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
@ -287,7 +420,7 @@ watchEffect(() => {
|
|||||||
.two-menu {
|
.two-menu {
|
||||||
|
|
||||||
.aside-menu:not(.el-menu--collapse) {
|
.aside-menu:not(.el-menu--collapse) {
|
||||||
width: 132px;
|
width: 152px;
|
||||||
padding-top: 16px;
|
padding-top: 16px;
|
||||||
border: 0;
|
border: 0;
|
||||||
|
|
||||||
@ -343,6 +476,30 @@ watchEffect(() => {
|
|||||||
margin-left: 0 !important;
|
margin-left: 0 !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.el-sub-menu{
|
||||||
|
.el-sub-menu__title{
|
||||||
|
margin: 0 8px 2px;
|
||||||
|
height: 40px;
|
||||||
|
padding-left: 18px;
|
||||||
|
border-radius: 2px;
|
||||||
|
span{
|
||||||
|
height: 40px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
&:hover{
|
||||||
|
background-color: transparent;
|
||||||
|
color: var(--el-color-primary);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.el-menu-item{
|
||||||
|
padding-left: 40px !important;
|
||||||
|
span{
|
||||||
|
margin-left: 0 !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user