feat: 插件使用fes-design

This commit is contained in:
wanchun 2021-12-27 14:25:22 +08:00
parent abb28212df
commit 6782f5eedc
15 changed files with 396 additions and 503 deletions

View File

@ -31,8 +31,9 @@
}, },
"peerDependencies": { "peerDependencies": {
"@ant-design/icons-vue": "^6.0.0", "@ant-design/icons-vue": "^6.0.0",
"@fesjs/fes": "^2.0.0",
"ant-design-vue": "^2.2.0", "ant-design-vue": "^2.2.0",
"@fesjs/fes": "^2.0.0",
"@fesjs/fes-design": "^0.1.2",
"vue": "^3.0.5" "vue": "^3.0.5"
} }
} }

Binary file not shown.

Before

Width:  |  Height:  |  Size: 33 KiB

After

Width:  |  Height:  |  Size: 50 KiB

View File

@ -1,4 +1,4 @@
import { unref, computed } from 'vue'; import { computed, ref } from 'vue';
// eslint-disable-next-line // eslint-disable-next-line
import { useAccess } from '../../plugin-access/core'; import { useAccess } from '../../plugin-access/core';
@ -9,32 +9,26 @@ if (!useAccess) {
} }
export const hasAccessByMenuItem = (item) => { export const hasAccessByMenuItem = (item) => {
let res; const hasChild = item.children && item.children.length;
if (item.path && (!item.children || item.children.length === 0)) { if (item.path && !hasChild) {
res = useAccess(item.path); return useAccess(item.path);
} else if (item.children && item.children.length > 0) { }
res = computed(() => item.children.some((child) => { if (hasChild) {
return computed(() => item.children.some((child) => {
const rst = hasAccessByMenuItem(child); const rst = hasAccessByMenuItem(child);
return rst && rst.value; return rst && rst.value;
})); }));
} }
return res; return ref(true);
}; };
const _addAccessTag = (arr) => { export const transform = menus => menus.map((menu) => {
if (Array.isArray(arr)) { const hasAccess = hasAccessByMenuItem(menu);
arr.forEach((item) => { if (!hasAccess.value) {
item.access = hasAccessByMenuItem(item); return false;
if (item.children && item.children.length > 0) {
_addAccessTag(item.children);
}
});
} }
}; if (menu.children) {
menu.children = transform(menu.children);
export const transform = (menus) => { }
const originData = unref(menus); return menu;
_addAccessTag(originData); }).filter(Boolean);
return originData;
};

View File

@ -1,7 +1,5 @@
import { unref, computed } from 'vue';
import { plugin } from '@@/core/coreExports'; import { plugin } from '@@/core/coreExports';
export const transTitle = (name) => { export const transTitle = (name) => {
const sharedLocale = plugin.getShared('locale'); const sharedLocale = plugin.getShared('locale');
if (sharedLocale) { if (sharedLocale) {
@ -12,22 +10,14 @@ export const transTitle = (name) => {
}; };
const _transform = (arr) => { export const transform = menus => menus.map((menu) => {
if (Array.isArray(arr)) { const copy = {
arr.forEach((item) => { ...menu,
if (item.title) { _label: menu.label,
item._title = item.title; label: transTitle(menu.label)
item.title = computed(() => transTitle(item._title)); };
} if (menu.children) {
if (item.children && item.children.length > 0) { copy.children = transform(menu.children);
_transform(item.children);
}
});
} }
}; return copy;
});
export const transform = (menus) => {
const originData = unref(menus);
_transform(originData);
return originData;
};

View File

@ -1,7 +1,7 @@
<template> <template>
<a-result status="403" title="403" sub-title="对不起您没有权限访问此页面"> <a-result status="403" title="403" sub-title="对不起您没有权限访问此页面">
<template #extra> <template #extra>
<a-button type="primary" @click="click">上一页</a-button> <f-button type="primary" @click="click">上一页</f-button>
</template> </template>
</a-result> </a-result>
</template> </template>
@ -14,13 +14,12 @@
import { useRouter } from '@@/core/coreExports'; import { useRouter } from '@@/core/coreExports';
import Result from 'ant-design-vue/lib/result'; import Result from 'ant-design-vue/lib/result';
import 'ant-design-vue/lib/result/style/css'; import 'ant-design-vue/lib/result/style/css';
import Button from 'ant-design-vue/lib/button'; import { FButton } from '@fesjs/fes-design';
import 'ant-design-vue/lib/button/style/css';
export default { export default {
components: { components: {
[Result.name]: Result, [Result.name]: Result,
[Button.name]: Button FButton
}, },
setup() { setup() {
const router = useRouter(); const router = useRouter();

View File

@ -1,7 +1,7 @@
<template> <template>
<a-result status="404" title="404" sub-title="对不起您访问的页面不存在"> <a-result status="404" title="404" sub-title="对不起您访问的页面不存在">
<template #extra> <template #extra>
<a-button type="primary" @click="click">上一页</a-button> <f-button type="primary" @click="click">上一页</f-button>
</template> </template>
</a-result> </a-result>
</template> </template>
@ -14,13 +14,12 @@
import { useRouter } from '@@/core/coreExports'; import { useRouter } from '@@/core/coreExports';
import Result from 'ant-design-vue/lib/result'; import Result from 'ant-design-vue/lib/result';
import 'ant-design-vue/lib/result/style/css'; import 'ant-design-vue/lib/result/style/css';
import Button from 'ant-design-vue/lib/button'; import { FButton } from '@fesjs/fes-design';
import 'ant-design-vue/lib/button/style/css';
export default { export default {
components: { components: {
[Result.name]: Result, [Result.name]: Result,
[Button.name]: Button FButton
}, },
setup() { setup() {
const router = useRouter(); const router = useRouter();

View File

@ -1,68 +1,126 @@
<template> <template>
<a-layout <f-layout v-if="routeLayout" class="main-layout">
v-if="routeLayout" <template v-if="navigation === 'side'">
:class="[ <f-aside
collapsed ? 'main-layout-collapsed' : '', v-if="routeLayout.side"
`main-layout-navigation-${navigation}`,
`main-layout-theme-${siderTheme}`
]"
class="main-layout"
>
<template v-if="navigation !== 'top' && routeLayout.side">
<div v-if="fixedSideBar" :style="siderFixedStuffStyle" class="layout-sider-fixed-stuff"></div>
<a-layout-sider
v-model:collapsed="collapsed" v-model:collapsed="collapsed"
:width="sideWidth" :fixed="fixedSideBar"
:class="[ class="layout-aside"
'layout-sider',
fixedSideBar ? 'layout-sider-fixed' : ''
]"
:theme="siderTheme"
collapsible collapsible
:inverted="theme === 'dark'"
> >
<div v-if="navigation !== 'mixin' && routeLayout.logo" class="layout-logo"> <div v-if="routeLayout.logo" class="layout-logo">
<img :src="logo" class="logo-img" /> <img :src="logo" class="logo-img" />
<h1 class="logo-name">{{title}}</h1> <div class="logo-name">{{title}}</div>
</div> </div>
<Menu :menus="menus" :theme="siderTheme" /> <Menu
</a-layout-sider> class="layout-menu"
</template> :menus="menus"
<a-layout class="child-layout"> :collapsed="collapsed"
<a-layout-header v-if="currentFixedHeader && routeLayout.top" class="layout-header"> mode="vertical"
</a-layout-header> :inverted="theme === 'dark'"
<a-layout-header />
v-if="routeLayout.top" </f-aside>
:style="headerFixedStyle" <f-layout>
:class="[currentFixedHeader ? 'layout-header-fixed' : '']" <f-header
class="layout-header" v-if="routeLayout.top"
> class="layout-header"
<div v-if="navigation === 'mixin' && routeLayout.logo" class="layout-logo"> :fixed="currentFixedHeader"
<img :src="logo" class="logo-img" /> >
<h1 class="logo-name">{{title}}</h1> <div class="layout-header-custom">
</div> <slot name="customHeader"></slot>
<template v-if="navigation === 'top'">
<div v-if="routeLayout.logo" class="layout-logo">
<img :src="logo" class="logo-img" />
<h1 class="logo-name">{{title}}</h1>
</div> </div>
<Menu :menus="menus" :theme="theme" class="layout-menu" mode="horizontal" /> <template v-if="locale">
</template> <slot name="locale"></slot>
</template>
</f-header>
<f-main class="layout-main">
<MultiTabProvider v-if="multiTabs" />
<router-view v-else></router-view>
</f-main>
<f-footer v-if="footer" class="layout-footer">
{{footer}}
</f-footer>
</f-layout>
</template>
<template v-if="navigation === 'top'">
<f-header
v-if="routeLayout.top"
class="layout-header"
:inverted="theme === 'dark'"
:fixed="currentFixedHeader"
>
<div v-if="routeLayout.logo" class="layout-logo">
<img :src="logo" class="logo-img" />
<div class="logo-name">{{title}}</div>
</div>
<Menu
class="layout-menu"
:menus="menus"
mode="horizontal"
:inverted="theme === 'dark'"
/>
<div class="layout-header-custom"> <div class="layout-header-custom">
<slot name="customHeader"></slot> <slot name="customHeader"></slot>
</div> </div>
<template v-if="locale"> <template v-if="locale">
<slot name="locale"></slot> <slot name="locale"></slot>
</template> </template>
</a-layout-header> </f-header>
<a-layout-content class="layout-content"> <f-main class="layout-main">
<MultiTabProvider v-if="multiTabs" /> <MultiTabProvider v-if="multiTabs" />
<router-view v-else></router-view> <router-view v-else></router-view>
</a-layout-content> </f-main>
<a-layout-footer v-if="footer" class="layout-footer"> <f-footer v-if="footer" class="layout-footer">
{{footer}} {{footer}}
</a-layout-footer> </f-footer>
</a-layout> </template>
</a-layout> <template v-if="navigation === 'mixin'">
<f-header
v-if="routeLayout.top"
class="layout-header"
:fixed="currentFixedHeader"
>
<div v-if="routeLayout.logo" class="layout-logo">
<img :src="logo" class="logo-img" />
<div class="logo-name">{{title}}</div>
</div>
<div class="layout-header-custom">
<slot name="customHeader"></slot>
</div>
<template v-if="locale">
<slot name="locale"></slot>
</template>
</f-header>
<f-layout>
<f-aside
v-if="routeLayout.side"
v-model:collapsed="collapsed"
:fixed="fixedSideBar"
collapsible
:inverted="theme === 'dark'"
class="layout-aside"
>
<Menu
class="layout-menu"
:menus="menus"
:collapsed="collapsed"
mode="vertical"
:inverted="theme === 'dark'"
/>
</f-aside>
<f-layout>
<f-main class="layout-main">
<MultiTabProvider v-if="multiTabs" />
<router-view v-else></router-view>
</f-main>
<f-footer v-if="footer" class="layout-footer">
{{footer}}
</f-footer>
</f-layout>
</f-layout>
</template>
</f-layout>
<div v-else class="content-wrapper"> <div v-else class="content-wrapper">
<router-view></router-view> <router-view></router-view>
</div> </div>
@ -71,19 +129,20 @@
<script> <script>
import { ref, computed } from 'vue'; import { ref, computed } from 'vue';
import { useRoute, plugin, ApplyPluginsType } from '@@/core/coreExports'; import { useRoute, plugin, ApplyPluginsType } from '@@/core/coreExports';
import Layout from 'ant-design-vue/lib/layout'; import {
import 'ant-design-vue/lib/layout/style/css'; FLayout, FAside, FMain, FFooter, FHeader
} from '@fesjs/fes-design';
import Menu from './Menu'; import Menu from './Menu';
import MultiTabProvider from './MultiTabProvider'; import MultiTabProvider from './MultiTabProvider';
import defaultLogo from '../assets/logo.png'; import defaultLogo from '../assets/logo.png';
export default { export default {
components: { components: {
[Layout.name]: Layout, FLayout,
[Layout.Sider.name]: Layout.Sider, FAside,
[Layout.Content.name]: Layout.Content, FMain,
[Layout.Header.name]: Layout.Header, FFooter,
[Layout.Footer.name]: Layout.Footer, FHeader,
Menu, Menu,
MultiTabProvider MultiTabProvider
}, },
@ -153,7 +212,9 @@ export default {
} else if (typeof metaLayoutConfig === 'object') { } else if (typeof metaLayoutConfig === 'object') {
config = { ...runtimeConfig, ...metaLayoutConfig }; config = { ...runtimeConfig, ...metaLayoutConfig };
} else { } else {
console.error('[plugin-layout]: meta layout must be object or boolean'); console.error(
'[plugin-layout]: meta layout must be object or boolean'
);
} }
// query layout false // query layout false
const routeQueryLayoutConfig = route.query.layout && JSON.parse(route.query.layout); const routeQueryLayoutConfig = route.query.layout && JSON.parse(route.query.layout);
@ -162,159 +223,94 @@ export default {
} else if (typeof routeQueryLayoutConfig === 'object') { } else if (typeof routeQueryLayoutConfig === 'object') {
config = { ...config, ...routeQueryLayoutConfig }; config = { ...config, ...routeQueryLayoutConfig };
} else if (routeQueryLayoutConfig !== undefined) { } else if (routeQueryLayoutConfig !== undefined) {
console.error('[plugin-layout]: query layout must be object or boolean'); console.error(
'[plugin-layout]: query layout must be object or boolean'
);
} }
return config; return config;
}); });
const siderTheme = computed(() => { const currentFixedHeader = computed(
if (props.navigation === 'mixin') { () => props.fixedHeader || props.navigation === 'mixin'
return 'light'; );
} const asideFixedStyle = computed(() => {
return props.theme; if (
}); routeLayout.value.top
const currentFixedHeader = computed(() => props.fixedHeader || props.navigation === 'mixin'); && props.navigation === 'mixin'
const siderFixedStuffStyle = computed(() => { && props.fixedSideBar
if (collapsed.value) { ) {
return { return {
width: '80px' top: '54px'
}; };
} }
return { return {};
width: `${props.sideWidth}px`
};
});
const headerFixedStyle = computed(() => {
if (!currentFixedHeader.value) {
return {};
}
if (props.navigation === 'side') {
return {
left: `${props.sideWidth}px`,
width: `calc(100% - ${props.sideWidth}px)`
};
}
return {
left: 0,
width: '100%'
};
}); });
// const sideTheme = computed(() => {
// if (props.navigation === 'mixin') {
// return 'light';
// }
// return props.theme;
// });
// const headerFixedStyle = computed(() => {
// if (!currentFixedHeader.value) {
// return {};
// }
// if (props.navigation === 'side') {
// return {
// left: `${props.sideWidth}px`,
// width: `calc(100% - ${props.sideWidth}px)`
// };
// }
// return {
// left: 0,
// width: '100%'
// };
// });
return { return {
siderTheme,
currentFixedHeader,
route, route,
routeLayout, routeLayout,
collapsed, collapsed,
siderFixedStuffStyle, currentFixedHeader,
headerFixedStyle asideFixedStyle
// sideTheme,
// currentFixedHeader,
// siderFixedStuffStyle,
// headerFixedStyle
}; };
} }
}; };
</script> </script>
<style lang="less">
.main-layout.main-layout-navigation-mixin{
.layout-sider{
.ant-layout-sider-trigger {
border-top: 1px solid #f0f0f0;
}
}
}
</style>
<style lang="less" scoped> <style lang="less" scoped>
.main-layout { .main-layout {
min-height: 100vh; min-height: 100vh;
&.main-layout-collapsed { .layout-header {
.layout-sider { display: flex;
.layout-logo { .layout-logo {
justify-content: center; display: flex;
.logo-name { justify-content: flex-start;
display: none; align-items: center;
} min-width: 165px;
height: 100%;
overflow: hidden;
transition: all 0.3s;
.logo-img {
height: 32px;
width: auto;
} }
} .logo-name {
}
&.main-layout-navigation-top {
.layout-header {
padding-left: 24px;
color: hsla(0,0%,100%,.65);
background: #001529;
.layout-menu {
line-height: 48px;
}
.layout-logo {
display: flex;
justify-content: flex-start;
align-items: center;
min-width: 165px;
height: 100%;
overflow: hidden; overflow: hidden;
transition: all .3s; margin: 0 0 0 12px;
.logo-img { font-weight: 600;
height: 32px; font-size: 18px;
width: auto; line-height: 32px;
}
.logo-name {
overflow: hidden;
margin: 0 0 0 12px;
color: #fff;
font-weight: 600;
font-size: 18px;
line-height: 32px;
}
} }
} }
} .layout-header-custom {
&.main-layout-navigation-mixin { flex: 1;
.layout-sider {
padding: 48px 0 0;
box-shadow: 2px 0 8px 0 rgba(29,35,41,.05);
}
.layout-header {
padding-left: 24px;
color: hsla(0,0%,100%,.65);
background: #001529;
.layout-menu {
line-height: 48px;
}
.layout-logo {
display: flex;
justify-content: flex-start;
align-items: center;
min-width: 165px;
height: 100%;
overflow: hidden;
transition: all .3s;
.logo-img {
height: 32px;
width: auto;
}
.logo-name {
overflow: hidden;
margin: 0 0 0 12px;
color: #fff;
font-weight: 600;
font-size: 18px;
line-height: 32px;
}
}
} }
} }
.layout-sider-fixed-stuff { .fes-layout-aside {
overflow: hidden;
transition: width 0.2s;
flex-shrink: 0;
}
.child-layout {
position: relative;
}
.layout-sider {
&.layout-sider-fixed {
position: fixed;
left: 0;
top: 0;
bottom: 0;
width: 200px;
}
.layout-logo { .layout-logo {
height: 32px; height: 32px;
margin: 16px; margin: 16px;
@ -328,55 +324,29 @@ export default {
.logo-name { .logo-name {
overflow: hidden; overflow: hidden;
margin: 0 0 0 12px; margin: 0 0 0 12px;
color: #fff;
font-weight: 600; font-weight: 600;
font-size: 18px; font-size: 18px;
line-height: 32px; line-height: 32px;
} }
} }
} .layout-menu {
.layout-header { margin-top: 24px;
position: relative;
z-index: 1;
display: flex;
align-items: center;
height: 48px;
line-height: 48px;
background: #fff;
box-shadow: 0 1px 4px rgba(0, 21, 41, 0.08);
padding: 0;
.layout-header-custom {
flex: 1;
} }
&.layout-header-fixed { &.is-collapsed {
position: fixed; .layout-logo {
top: 0; justify-content: center;
right: 0; .logo-name {
z-index: 10; display: none;
}
}
} }
} }
.layout-content,
.content-wrapper {
position: relative;
}
.layout-footer { .layout-footer {
text-align: center; text-align: center;
} }
&.main-layout-theme-light{ }
.logo-name{
color: rgba(0, 0, 0, 0.65) !important; .content-wrapper {
} position: relative;
&.main-layout-navigation-mixin{
.logo-name{
color: #fff !important;
}
}
&.main-layout-navigation-top{
.layout-header {
background: #fff;
color: rgba(0, 0, 0, 0.85);
}
}
}
} }
</style> </style>

View File

@ -1,67 +1,24 @@
<template> <template>
<a-menu <f-menu
:selectedKeys="selectedKeys" :modelValue="route.path"
:theme="theme" :inverted="inverted"
mode="inline" :mode="mode"
@click="onMenuClick" :options="fixedMenus"
> @select="onMenuClick"
<template v-for="(item, index) in fixedMenus" :key="index"> ></f-menu>
<template v-if="item.access">
<a-sub-menu v-if="item.children" :key="index" :title="item.title">
<template v-if="item.icon" #icon>
<MenuIcon :icon="item.icon" />
</template>
<template
v-for="(item1, index1) in item.children"
>
<template v-if="item1.access">
<a-sub-menu
v-if="item1.children"
:key="`${index}-${index1}`"
:title="item1.title"
>
<template
v-for="(item2) in item1.children"
>
<a-menu-item
v-if="item2.access"
:key="item2.path"
:title="item2.title"
>
{{item2.title}}
</a-menu-item>
</template>
</a-sub-menu>
<a-menu-item v-else :key="item1.path" :title="item1.title">
{{item1.title}}
</a-menu-item>
</template>
</template>
</a-sub-menu>
<a-menu-item v-else :key="item.path" :title="item.title">
<MenuIcon v-if="item.icon" :icon="item.icon" />
<span>{{item.title}}</span>
</a-menu-item>
</template>
</template>
</a-menu>
</template> </template>
<script> <script>
import { toRefs, computed } from 'vue'; import { computed, h } from 'vue';
import { FMenu } from '@fesjs/fes-design';
import { useRoute, useRouter } from '@@/core/coreExports'; import { useRoute, useRouter } from '@@/core/coreExports';
import Menu from 'ant-design-vue/lib/menu';
import 'ant-design-vue/lib/menu/style/css';
import MenuIcon from './MenuIcon'; import MenuIcon from './MenuIcon';
import { transform as transformByAccess } from '../helpers/pluginAccess'; import { transform as transformByAccess } from '../helpers/pluginAccess';
import { transform as transformByLocale } from '../helpers/pluginLocale'; import { transform as transformByLocale } from '../helpers/pluginLocale';
export default { export default {
components: { components: {
[Menu.name]: Menu, FMenu
[Menu.SubMenu.name]: Menu.SubMenu,
[Menu.Item.name]: Menu.Item,
MenuIcon
}, },
props: { props: {
menus: { menus: {
@ -70,18 +27,37 @@ export default {
return []; return [];
} }
}, },
theme: { mode: {
type: String, type: String,
default: 'dark' default: 'vertical'
},
inverted: {
type: Boolean,
default: false
} }
}, },
setup(props) { setup(props) {
const { menus } = toRefs(props);
const route = useRoute(); const route = useRoute();
const router = useRouter(); const router = useRouter();
const fixedMenus = transformByLocale(transformByAccess(menus)); const transform = menus => menus.map((menu) => {
const copy = {
...menu,
label: menu.title,
value: menu.path
};
if (menu.icon) {
copy.icon = () => h(MenuIcon, {
icon: menu.icon
});
}
if (menu.children) {
copy.children = transform(menu.children);
}
return copy;
});
const fixedMenus = computed(() => transformByLocale(transformByAccess(transform(props.menus))));
const onMenuClick = (e) => { const onMenuClick = (e) => {
const path = e.key; const path = e.value;
if (/^https?:\/\//.test(path)) { if (/^https?:\/\//.test(path)) {
window.open(path, '_blank'); window.open(path, '_blank');
} else if (/^\//.test(path)) { } else if (/^\//.test(path)) {
@ -92,15 +68,11 @@ export default {
); );
} }
}; };
const selectedKeys = computed(() => [route.path]);
return { return {
selectedKeys, route,
fixedMenus, fixedMenus,
onMenuClick onMenuClick
}; };
} }
}; };
</script> </script>
<style lang="less">
</style>

View File

@ -1,5 +1,4 @@
<script> <script>
import { ref, onBeforeMount } from 'vue'; import { ref, onBeforeMount } from 'vue';
// eslint-disable-next-line import/extensions // eslint-disable-next-line import/extensions
import Icons from '../icons'; import Icons from '../icons';
@ -33,8 +32,10 @@ export default {
} }
if (AText.value) { if (AText.value) {
return ( return (
<span className={'fes-layout-icon anticon'} innerHTML={AText.value}> <span
</span> className={'fes-layout-icon anticon'}
innerHTML={AText.value}
></span>
); );
} }
return null; return null;
@ -43,7 +44,7 @@ export default {
}; };
</script> </script>
<style> <style>
.fes-layout-icon{ .fes-layout-icon {
display: inline-block; display: inline-block;
color: inherit; color: inherit;
font-style: normal; font-style: normal;
@ -56,6 +57,7 @@ export default {
min-width: 14px; min-width: 14px;
margin-right: 10px; margin-right: 10px;
font-size: 14px; font-size: 14px;
transition: font-size 0.15s cubic-bezier(0.215, 0.61, 0.355, 1), margin 0.3s cubic-bezier(0.645, 0.045, 0.355, 1); transition: font-size 0.15s cubic-bezier(0.215, 0.61, 0.355, 1),
margin 0.3s cubic-bezier(0.645, 0.045, 0.355, 1);
} }
</style> </style>

View File

@ -33,7 +33,7 @@
"peerDependencies": { "peerDependencies": {
"@ant-design/icons-vue": "^6.0.0", "@ant-design/icons-vue": "^6.0.0",
"@fesjs/fes": "^2.0.0", "@fesjs/fes": "^2.0.0",
"ant-design-vue": "^2.2.0", "@fesjs/fes-design": "^0.1.2",
"vue": "^3.0.5" "vue": "^3.0.5"
} }
} }

View File

@ -1,41 +1,42 @@
<template> <template>
<a-dropdown> <FTooltip v-model="isOpened">
<div class="lang-icon"><GlobalOutlined /></div> <div class="lang-icon">
<template #overlay> <GlobalOutlined />
<a-menu :selectedKeys="selectedKeys" @click="handleClick"> </div>
<a-menu-item <template #content>
<FScrollbar height="274" class="lang-container">
<div
v-for="item in configs" v-for="item in configs"
:key="item.lang" :key="item.lang"
class="lang-item" :class="[
'lang-option',
item.lang === locale && 'is-selected'
]"
@click="handleSelect(item)"
> >
<span class="lang-item-icon">{{item.icon}}</span> <span>{{item.icon}}</span>
<span class="lang-item-label">{{item.label}}</span> <span>{{item.label}}</span>
</a-menu-item> </div>
</a-menu> </FScrollbar>
</template> </template>
</a-dropdown> </FTooltip>
</template> </template>
<script> <script>
import Dropdown from 'ant-design-vue/lib/dropdown'; import { FTooltip, FScrollbar } from '@fesjs/fes-design';
import Menu from 'ant-design-vue/lib/menu';
import 'ant-design-vue/lib/dropdown/style/css';
import 'ant-design-vue/lib/menu/style/css';
import { GlobalOutlined } from '@ant-design/icons-vue'; import { GlobalOutlined } from '@ant-design/icons-vue';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import { computed } from 'vue'; import { computed, ref } from 'vue';
import langUConfigMap from '../langUConfigMap'; import langUConfigMap from '../langUConfigMap';
export default { export default {
components: { components: {
[Dropdown.name]: Dropdown, FTooltip,
[Menu.name]: Menu, FScrollbar,
[Menu.Item.name]: Menu.Item,
GlobalOutlined GlobalOutlined
}, },
setup() { setup() {
const { messages, locale } = useI18n(); const { messages, locale } = useI18n();
const selectedKeys = computed(() => [locale.value]);
const configs = computed(() => { const configs = computed(() => {
const arr = []; const arr = [];
Object.keys(messages.value) Object.keys(messages.value)
@ -45,30 +46,46 @@ export default {
}); });
return arr; return arr;
}); });
const handleClick = ({ key }) => { const isOpened = ref(false);
locale.value = key; const handleSelect = ({ lang }) => {
window.localStorage.setItem('fes_locale', key); locale.value = lang;
isOpened.value = false;
window.localStorage.setItem('fes_locale', lang);
}; };
return { return {
handleClick, handleSelect,
selectedKeys, locale,
configs configs,
isOpened
}; };
} }
}; };
</script> </script>
<style lang="less"> <style lang="less" scoped>
.lang-icon { .lang-icon {
margin: 0 8px; margin: 0 8px;
padding: 0 4px; padding: 0 4px;
cursor: pointer; cursor: pointer;
} }
.lang-item { .lang-container {
display: flex; width: 180px;
align-items: center; .lang-option {
.lang-item-label { display: flex;
margin-left: 8px; align-items: center;
justify-content: space-between;
height: 32px;
padding: 0 8px;
color: #0f1222;
line-height: 32px;
background: #ffffff;
cursor: pointer;
transition: all 0.2s cubic-bezier(0.9, 0, 0.3, 0.7);
&:hover,
&.is-selected {
color: #5384ff;
background: #f5f8ff;
}
} }
} }
</style> </style>

View File

@ -34,8 +34,8 @@ export default {
title: "Fes.js", title: "Fes.js",
footer: "Created by MumbleFe", footer: "Created by MumbleFe",
multiTabs: true, multiTabs: true,
navigation: "mixin", navigation: "side",
theme: 'light', theme: 'dark',
menus: [ menus: [
{ {
name: "index", name: "index",
@ -55,7 +55,7 @@ export default {
], ],
}, },
{ {
// name: "setting", name: "setting",
title: "setting", title: "setting",
children: [ children: [
{ {

View File

@ -59,6 +59,7 @@
"@fesjs/plugin-sass": "^2.0.0", "@fesjs/plugin-sass": "^2.0.0",
"@fesjs/plugin-monaco-editor": "^2.0.0-beta.0", "@fesjs/plugin-monaco-editor": "^2.0.0-beta.0",
"@fesjs/plugin-windicss": "^2.0.0", "@fesjs/plugin-windicss": "^2.0.0",
"@fesjs/fes-design": "^0.1.2",
"ant-design-vue": "^2.2.0", "ant-design-vue": "^2.2.0",
"vue": "^3.0.5", "vue": "^3.0.5",
"vuex": "^4.0.0" "vuex": "^4.0.0"

View File

@ -1,16 +1,6 @@
<template> <template>
<div :class="$style.red"> <div>
<a-input placeholder="请输入。。。" /> home
<a-button type="primary">Primary</a-button>
<div class="m-2">国际化 {{t("test")}}</div>
fes & 拉夫德鲁 <br />
<access :id="accessId"> accessOnepicess1 <input /> </access>
<div v-access="accessId"> accessOnepicess2 <input /> </div>
<input />
<h4>数据字典</h4>
<div v-for="item in enumsGet('status')" :key="item.key">{{item.value}}{{item.key}}</div>
<div v-for="item in roles" :key="item.key">{{item.name}}{{item.disabled}}</div>
<div>{{enumsGet('roles', '2', { dir: 'eName' })}}</div>
</div> </div>
</template> </template>
<config> <config>
@ -20,124 +10,15 @@
} }
</config> </config>
<script> <script>
import { ref, onMounted } from 'vue';
import {
useAccess, useRouter, useI18n, locale, enums, request
} from '@fesjs/fes';
import { Button, Input } from 'ant-design-vue';
export default { export default {
components: { components: {
[Button.name]: Button,
[Input.name]: Input
}, },
setup() { setup() {
const fes = ref('fes upgrade to vue3');
const accessOnepicess = useAccess('/onepiece1');
const localI18n = useI18n();
const router = useRouter();
const accessId = ref('/onepiece1');
enums.push('roles', [
{
id: '1',
cName: '系统管理员',
eName: 'System',
perm: ['1', '2', '3']
},
{
id: '2',
cName: '业务管理员',
eName: 'Business',
perm: ['1', '2']
},
{
id: '3',
cName: '普通用户',
eName: 'User',
perm: ['1']
}
], { keyName: 'id' });
const roles = enums.get('roles', {
extend: [
{
key: 'name',
dir: 'cName'
},
{
key: 'disabled',
transfer: item => item.value.perm.some(i => i >= 2)
}
]
});
console.log('enums roles=>', roles);
console.log('enums roles[1]=>', enums.get('roles', '1'));
console.log('enums status[0]=> ', enums.get('status', 0));
console.log('enums status concat', enums.concat('status', [['3', '普通的']], { extend: [{ key: 'name', dir: 'value' }] }));
console.log('enums status get extend=>', enums.get('status', {
extend: [
{
key: 'name',
dir: 'value'
},
{
key: 'disabled',
transfer: item => item.key === '0'
}
]
}));
onMounted(() => {
console.log(router);
setTimeout(() => {
locale.setLocale({ locale: 'en-US' });
locale.addLocale({ locale: 'ja-JP', messages: { test: 'テスト' } });
console.log(locale.getAllLocales());
}, 2000);
setTimeout(() => {
accessId.value = '11';
}, 4000);
console.log('测试 mock!!');
request('/v2/file').then((data) => {
console.log(data);
}).catch((err) => {
console.log(err);
});
request('/v2/movie/in_theaters_mock', { a: 1 }, 'get').then((data) => {
console.log(data);
}).catch((err) => {
console.log(err);
});
console.log('测试 proxy!!');
request('/v2/movie/in_theaters_proxy', { a: 1 }, {
method: 'get',
headers: { Accept: '*/*' }
}).then((resp) => {
console.log(resp);
}).catch((err) => {
console.log(err);
});
});
return { return {
accessId,
fes,
accessOnepicess,
t: localI18n.t,
enumsGet: enums.get,
roles
}; };
},
mounted() {
console.log('$style:', this.$style);
} }
}; };
</script> </script>
<style module> <style module>
.red {
color: red;
}
.bold {
font-weight: bold;
}
</style> </style>

View File

@ -1142,6 +1142,13 @@
dependencies: dependencies:
regenerator-runtime "^0.13.4" regenerator-runtime "^0.13.4"
"@babel/runtime@^7.16.3":
version "7.16.5"
resolved "https://registry.npmmirror.com/@babel/runtime/download/@babel/runtime-7.16.5.tgz#7f3e34bf8bdbbadf03fbb7b1ea0d929569c9487a"
integrity sha512-TXWihFIS3Pyv5hzR7j6ihmeLkZfrXGxAr5UfSl8CHf+6q/wpiYDkUau0czckpYG8QmnCIuPpdLtuA9VmuGGyMA==
dependencies:
regenerator-runtime "^0.13.4"
"@babel/template@^7.0.0", "@babel/template@^7.14.5", "@babel/template@^7.3.3": "@babel/template@^7.0.0", "@babel/template@^7.14.5", "@babel/template@^7.3.3":
version "7.14.5" version "7.14.5"
resolved "http://10.107.103.115:8001/@babel/template/download/@babel/template-7.14.5.tgz#a9bc9d8b33354ff6e55a9c60d1109200a68974f4" resolved "http://10.107.103.115:8001/@babel/template/download/@babel/template-7.14.5.tgz#a9bc9d8b33354ff6e55a9c60d1109200a68974f4"
@ -1393,6 +1400,21 @@
minimatch "^3.0.4" minimatch "^3.0.4"
strip-json-comments "^3.1.1" strip-json-comments "^3.1.1"
"@fesjs/fes-design@^0.1.2":
version "0.1.2"
resolved "https://registry.npmmirror.com/@fesjs/fes-design/download/@fesjs/fes-design-0.1.2.tgz#be1751561f10585bbb95de72e8863072112f640a"
integrity sha512-JIUd2uIC0O2E2JCUCm1pxT4ZSQg0z1+rFmtN0zS7OO0usZDKN0T/LSnivDc3K7QSAGMTeOYTgQCb6bLqf8jAfQ==
dependencies:
"@babel/runtime" "^7.16.3"
"@juggle/resize-observer" "^3.3.1"
"@popperjs/core" "^2.4.0"
"@vue/shared" "^3.2.19"
"@vueuse/core" "^6.7.5"
async-validator "^4.0.1"
lodash-es "^4.17.21"
normalize-wheel "^1.0.1"
stickybits "^3.7.9"
"@hapi/hoek@^9.0.0": "@hapi/hoek@^9.0.0":
version "9.2.0" version "9.2.0"
resolved "http://10.107.103.115:8001/@hapi/hoek/download/@hapi/hoek-9.2.0.tgz#f3933a44e365864f4dad5db94158106d511e8131" resolved "http://10.107.103.115:8001/@hapi/hoek/download/@hapi/hoek-9.2.0.tgz#f3933a44e365864f4dad5db94158106d511e8131"
@ -1665,6 +1687,11 @@
"@types/yargs" "^16.0.0" "@types/yargs" "^16.0.0"
chalk "^4.0.0" chalk "^4.0.0"
"@juggle/resize-observer@^3.3.1":
version "3.3.1"
resolved "https://registry.npmmirror.com/@juggle/resize-observer/download/@juggle/resize-observer-3.3.1.tgz#b50a781709c81e10701004214340f25475a171a0"
integrity sha1-tQp4FwnIHhBwEAQhQ0DyVHWhcaA=
"@lerna/add@4.0.0": "@lerna/add@4.0.0":
version "4.0.0" version "4.0.0"
resolved "http://10.107.103.115:8001/@lerna/add/download/@lerna/add-4.0.0.tgz#c36f57d132502a57b9e7058d1548b7a565ef183f" resolved "http://10.107.103.115:8001/@lerna/add/download/@lerna/add-4.0.0.tgz#c36f57d132502a57b9e7058d1548b7a565ef183f"
@ -2526,6 +2553,11 @@
resolved "http://10.107.103.115:8001/@polka/url/download/@polka/url-1.0.0-next.17.tgz#25fdbdfd282c2f86ddf3fcefbd98be99cd2627e2" resolved "http://10.107.103.115:8001/@polka/url/download/@polka/url-1.0.0-next.17.tgz#25fdbdfd282c2f86ddf3fcefbd98be99cd2627e2"
integrity sha1-Jf29/SgsL4bd8/zvvZi+mc0mJ+I= integrity sha1-Jf29/SgsL4bd8/zvvZi+mc0mJ+I=
"@popperjs/core@^2.4.0":
version "2.11.0"
resolved "https://registry.npmmirror.com/@popperjs/core/download/@popperjs/core-2.11.0.tgz#6734f8ebc106a0860dff7f92bf90df193f0935d7"
integrity sha512-zrsUxjLOKAzdewIDRWy9nsV1GQsKBCWaGwsZQlCgr6/q+vjyZhFgqedLfFBuI9anTPEUT4APq9Mu0SZBTzIcGQ==
"@rollup/plugin-babel@^5.2.0": "@rollup/plugin-babel@^5.2.0":
version "5.3.0" version "5.3.0"
resolved "http://10.107.103.115:8001/@rollup/plugin-babel/download/@rollup/plugin-babel-5.3.0.tgz#9cb1c5146ddd6a4968ad96f209c50c62f92f9879" resolved "http://10.107.103.115:8001/@rollup/plugin-babel/download/@rollup/plugin-babel-5.3.0.tgz#9cb1c5146ddd6a4968ad96f209c50c62f92f9879"
@ -3070,6 +3102,11 @@
resolved "http://10.107.103.115:8001/@vue/shared/download/@vue/shared-3.2.2.tgz#6104185ebd57af5a14ac51c1f491b2205fc24054" resolved "http://10.107.103.115:8001/@vue/shared/download/@vue/shared-3.2.2.tgz#6104185ebd57af5a14ac51c1f491b2205fc24054"
integrity sha1-YQQYXr1Xr1oUrFHB9JGyIF/CQFQ= integrity sha1-YQQYXr1Xr1oUrFHB9JGyIF/CQFQ=
"@vue/shared@^3.2.19":
version "3.2.26"
resolved "https://registry.npmmirror.com/@vue/shared/download/@vue/shared-3.2.26.tgz#7acd1621783571b9a82eca1f041b4a0a983481d9"
integrity sha512-vPV6Cq+NIWbH5pZu+V+2QHE9y1qfuTq49uNWw4f7FDEeZaDU2H2cx5jcUZOAKW7qTrUS4k6qZPbMy1x4N96nbA==
"@vuepress/bundler-webpack@2.0.0-beta.24": "@vuepress/bundler-webpack@2.0.0-beta.24":
version "2.0.0-beta.24" version "2.0.0-beta.24"
resolved "http://10.107.103.115:8001/@vuepress/bundler-webpack/download/@vuepress/bundler-webpack-2.0.0-beta.24.tgz#61fd0860568d68ddd9111edb5cc82dfecd3c12d4" resolved "http://10.107.103.115:8001/@vuepress/bundler-webpack/download/@vuepress/bundler-webpack-2.0.0-beta.24.tgz#61fd0860568d68ddd9111edb5cc82dfecd3c12d4"
@ -3342,6 +3379,14 @@
"@vueuse/shared" "6.0.0-beta.3" "@vueuse/shared" "6.0.0-beta.3"
vue-demi "*" vue-demi "*"
"@vueuse/core@^6.7.5":
version "6.9.2"
resolved "https://registry.npmmirror.com/@vueuse/core/download/@vueuse/core-6.9.2.tgz#76b16d01f33cf367dd1a2d7f2e31d106443ceb8a"
integrity sha512-FRwl4ccSFuHZBHLGgS9TMv/+Dd6XFaL4o9nph2qtgQIV+z29RBFokw08XjHfykiENRzB01MjYHJ7iRUnsIFQXg==
dependencies:
"@vueuse/shared" "6.9.2"
vue-demi "*"
"@vueuse/shared@6.0.0-beta.3": "@vueuse/shared@6.0.0-beta.3":
version "6.0.0-beta.3" version "6.0.0-beta.3"
resolved "http://10.107.103.115:8001/@vueuse/shared/download/@vueuse/shared-6.0.0-beta.3.tgz#2e52a1e573983d3b806054948b0eca01ced7c504" resolved "http://10.107.103.115:8001/@vueuse/shared/download/@vueuse/shared-6.0.0-beta.3.tgz#2e52a1e573983d3b806054948b0eca01ced7c504"
@ -3349,6 +3394,13 @@
dependencies: dependencies:
vue-demi "*" vue-demi "*"
"@vueuse/shared@6.9.2":
version "6.9.2"
resolved "https://registry.npmmirror.com/@vueuse/shared/download/@vueuse/shared-6.9.2.tgz#97e4369fa7262ebc96fe1d6e210268f30b037005"
integrity sha512-lAiMh6XROs0kSKVd0Yb/6GKoQMxC1fYrFDi6opvQWISPtcqRNluRrQxLUZ3WTI78ovtoKRLktjhkFAtydcfFDg==
dependencies:
vue-demi "*"
"@webank/eslint-config-webank@0.3.1": "@webank/eslint-config-webank@0.3.1":
version "0.3.1" version "0.3.1"
resolved "https://registry.npmjs.org/@webank/eslint-config-webank/-/eslint-config-webank-0.3.1.tgz#71e803808ff239eb66ed04954edab144617467dd" resolved "https://registry.npmjs.org/@webank/eslint-config-webank/-/eslint-config-webank-0.3.1.tgz#71e803808ff239eb66ed04954edab144617467dd"
@ -3949,6 +4001,11 @@ async-validator@^3.3.0:
resolved "http://10.107.103.115:8001/async-validator/download/async-validator-3.5.2.tgz#68e866a96824e8b2694ff7a831c1a25c44d5e500" resolved "http://10.107.103.115:8001/async-validator/download/async-validator-3.5.2.tgz#68e866a96824e8b2694ff7a831c1a25c44d5e500"
integrity sha1-aOhmqWgk6LJpT/eoMcGiXETV5QA= integrity sha1-aOhmqWgk6LJpT/eoMcGiXETV5QA=
async-validator@^4.0.1:
version "4.0.7"
resolved "https://registry.npmmirror.com/async-validator/download/async-validator-4.0.7.tgz?cache=0&sync_timestamp=1634529502627&other_urls=https%3A%2F%2Fregistry.npmmirror.com%2Fasync-validator%2Fdownload%2Fasync-validator-4.0.7.tgz#034a0fd2103a6b2ebf010da75183bec299247afe"
integrity sha1-A0oP0hA6ay6/AQ2nUYO+wpkkev4=
async@^2.6.2: async@^2.6.2:
version "2.6.3" version "2.6.3"
resolved "http://10.107.103.115:8001/async/download/async-2.6.3.tgz#d72625e2344a3656e3a3ad4fa749fa83299d82ff" resolved "http://10.107.103.115:8001/async/download/async-2.6.3.tgz#d72625e2344a3656e3a3ad4fa749fa83299d82ff"
@ -8911,7 +8968,7 @@ locate-path@^6.0.0:
dependencies: dependencies:
p-locate "^5.0.0" p-locate "^5.0.0"
lodash-es@^4.17.15: lodash-es@^4.17.15, lodash-es@^4.17.21:
version "4.17.21" version "4.17.21"
resolved "http://10.107.103.115:8001/lodash-es/download/lodash-es-4.17.21.tgz#43e626c46e6591b7750beb2b50117390c609e3ee" resolved "http://10.107.103.115:8001/lodash-es/download/lodash-es-4.17.21.tgz#43e626c46e6591b7750beb2b50117390c609e3ee"
integrity sha1-Q+YmxG5lkbd1C+srUBFzkMYJ4+4= integrity sha1-Q+YmxG5lkbd1C+srUBFzkMYJ4+4=
@ -9787,6 +9844,11 @@ normalize-url@^6.0.1, normalize-url@^6.1.0:
resolved "http://10.107.103.115:8001/normalize-url/download/normalize-url-6.1.0.tgz#40d0885b535deffe3f3147bec877d05fe4c5668a" resolved "http://10.107.103.115:8001/normalize-url/download/normalize-url-6.1.0.tgz#40d0885b535deffe3f3147bec877d05fe4c5668a"
integrity sha1-QNCIW1Nd7/4/MUe+yHfQX+TFZoo= integrity sha1-QNCIW1Nd7/4/MUe+yHfQX+TFZoo=
normalize-wheel@^1.0.1:
version "1.0.1"
resolved "https://registry.npmmirror.com/normalize-wheel/download/normalize-wheel-1.0.1.tgz#aec886affdb045070d856447df62ecf86146ec45"
integrity sha1-rsiGr/2wRQcNhWRH32Ls+GFG7EU=
npm-bundled@^1.1.1: npm-bundled@^1.1.1:
version "1.1.2" version "1.1.2"
resolved "http://10.107.103.115:8001/npm-bundled/download/npm-bundled-1.1.2.tgz#944c78789bd739035b70baa2ca5cc32b8d860bc1" resolved "http://10.107.103.115:8001/npm-bundled/download/npm-bundled-1.1.2.tgz#944c78789bd739035b70baa2ca5cc32b8d860bc1"
@ -12269,6 +12331,11 @@ std-env@^2.2.1:
dependencies: dependencies:
ci-info "^3.0.0" ci-info "^3.0.0"
stickybits@^3.7.9:
version "3.7.9"
resolved "https://registry.npmmirror.com/stickybits/download/stickybits-3.7.9.tgz#0e469ffb960e1e661bd308ba4da370dc439902bf"
integrity sha1-Dkaf+5YOHmYb0wi6TaNw3EOZAr8=
strict-uri-encode@^2.0.0: strict-uri-encode@^2.0.0:
version "2.0.0" version "2.0.0"
resolved "http://10.107.103.115:8001/strict-uri-encode/download/strict-uri-encode-2.0.0.tgz#b9c7330c7042862f6b142dc274bbcc5866ce3546" resolved "http://10.107.103.115:8001/strict-uri-encode/download/strict-uri-encode-2.0.0.tgz#b9c7330c7042862f6b142dc274bbcc5866ce3546"