feat: shrimp-admin v0.1.0 — 从 hand-admin 抽离的框架前端
Shrimp 框架前端 — 一种消费 ShrimpServer + ShrimpTable 契约的经典 Vue3 +
Element Plus 实现。不是框架本身,只是按 shrimp-server 约定写的一份参考解法。
包含:
- ShrimpClient + VueAdmin(factory:createShrimpAdmin)
- 5 个 Shrimp* 组件 + 10 个内置 Field*.vue + PowerTree + LangSwitch
- 4 个内置模块:user / role / operationLog / FieldRelativeTime
- 完整后台壳 views(layout/login/sideM/topbar/userbar/navMenu/App/404/logo)
- composables/useTableUrlSync, locales(zh-cn/en 框架默认), style 全套
API 改造:
- createShrimpAdmin({ config, moduleSources, customFields, locales }) factory
- vueAdmin 改为 Proxy 单例,createShrimpAdmin 调用前访问会抛错
- import.meta.glob 业务字段/模块由消费侧传入(vite 静态扫描机制限制)
- 框架默认 locales 与消费侧 locales 在 i18n 初始化时合并
源码 ship 不打包,消费侧靠 vite 直接吃 .ts/.vue。
This commit is contained in:
@@ -0,0 +1,110 @@
|
||||
<template>
|
||||
<section class="aminui-wrapper">
|
||||
<div v-if="!vueAdmin.state.isMobile" class="aminui-side-split">
|
||||
<div class="aminui-side-split-top">
|
||||
<logo />
|
||||
</div>
|
||||
<div class="adminui-side-split-scroll">
|
||||
<div class="sidebar-menu-wrap">
|
||||
<el-scrollbar>
|
||||
<ul>
|
||||
<li v-for="item in vueAdmin.menuTree"
|
||||
:class="pmenu?.name == item.name ? 'active' : ''"
|
||||
@click="showMenu(item)">
|
||||
<el-icon>
|
||||
<component :is="item.icon || 'el-icon-menu'" />
|
||||
</el-icon>
|
||||
<p>{{ vueAdmin.t(item.label) }}</p>
|
||||
</li>
|
||||
</ul>
|
||||
</el-scrollbar>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="!vueAdmin.state.isMobile && nextMenu.length > 0" class="aminui-side">
|
||||
<div class="adminui-side-scroll">
|
||||
<el-scrollbar>
|
||||
<el-menu :default-active="$route.fullPath" router>
|
||||
<navMenu :navMenus="nextMenu"></navMenu>
|
||||
</el-menu>
|
||||
</el-scrollbar>
|
||||
</div>
|
||||
</div>
|
||||
<SideM v-if="vueAdmin.state.isMobile"></SideM>
|
||||
<div class="aminui-body el-container">
|
||||
<Topbar>
|
||||
<userbar></userbar>
|
||||
</Topbar>
|
||||
<div class="adminui-main" id="adminui-main">
|
||||
<router-view :key="$route.path" />
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import SideM from "./sideM.vue";
|
||||
import { ref, watch } from "vue";
|
||||
import navMenu from "./navMenu.vue";
|
||||
import Topbar from "./topbar.vue";
|
||||
import Userbar from "./userbar.vue";
|
||||
import { vueAdmin } from "../VueAdmin";
|
||||
import { type IMenuItem } from "shrimp-server/types";
|
||||
import Logo from "./logo.vue";
|
||||
|
||||
// 初始化选中菜单
|
||||
let currentPath = vueAdmin.router.currentRoute.value.path;
|
||||
let pick = vueAdmin.getMenuIndex(currentPath);
|
||||
let menu = pick >= 0 ? vueAdmin.menuTree[pick] : vueAdmin.menuTree[0];
|
||||
let pmenu = ref(menu);
|
||||
let nextMenu = ref(getMenu(vueAdmin.isGroupMenu(menu) ? menu.children : []));
|
||||
|
||||
function getMenu(children?: IMenuItem[]) {
|
||||
if (children == null) return [];
|
||||
return children.slice()
|
||||
}
|
||||
|
||||
function getFirstLeafPath(item: IMenuItem): string {
|
||||
if (item.path) return item.path
|
||||
if (item.moduleKey) return '/' + item.moduleKey
|
||||
if (item.children && item.children.length > 0) {
|
||||
return getFirstLeafPath(item.children[0])
|
||||
}
|
||||
return '/' + item.name
|
||||
}
|
||||
|
||||
function showMenu(route: any) {
|
||||
pmenu.value = route;
|
||||
nextMenu.value = getMenu(route.children);
|
||||
if (route.children == null || route.children.length == 0) {
|
||||
const path = route.path || '/' + route.name
|
||||
vueAdmin.router.push(path)
|
||||
} else {
|
||||
// 展开菜单的同时,跳到第一个可跳转的叶子节点
|
||||
const path = getFirstLeafPath(route.children[0])
|
||||
vueAdmin.router.push(path)
|
||||
}
|
||||
}
|
||||
|
||||
// 路由变化时同步更新 pmenu 和 nextMenu
|
||||
watch(() => vueAdmin.router.currentRoute.value.path, (path) => {
|
||||
const idx = vueAdmin.getMenuIndex(path)
|
||||
if (idx >= 0) {
|
||||
const m = vueAdmin.menuTree[idx]
|
||||
if (pmenu.value?.name !== m.name) {
|
||||
pmenu.value = m
|
||||
nextMenu.value = getMenu(vueAdmin.isGroupMenu(m) ? m.children : [])
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
<style scoped>
|
||||
.sidebar-menu-wrap {
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.sidebar-menu-wrap .el-scrollbar {
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user