Unverified Commit 628574c6 authored by Sendya's avatar Sendya

refactor: use vite build

parent a93aceec
module.exports = {
root: true,
env: {
node: true,
browser: true,
es2021: true,
},
extends: [
'plugin:vue/vue3-essential',
'eslint:recommended',
'@vue/typescript/recommended',
'@vue/prettier',
'@vue/prettier/@typescript-eslint',
'airbnb-base',
'plugin:@typescript-eslint/recommended',
'plugin:vue/vue3-recommended',
'plugin:prettier/recommended',
],
parserOptions: {
ecmaVersion: 2020,
// parser: '@typescript-eslint/parser',
ecmaVersion: 12,
parser: '@typescript-eslint/parser',
sourceType: 'module',
ecmaFeatures: {
jsx: true,
},
},
plugins: ['vue', '@typescript-eslint'],
rules: {
'import/extensions': 0,
'import/no-unresolved': 0,
'no-param-reassign': 0,
'consistent-return': 0,
'@typescript-eslint/no-explicit-any': 0,
'@typescript-eslint/no-inferrable-types': 0,
'@typescript-eslint/ban-ts-ignore': 'off',
'@typescript-eslint/no-var-requires': 'off',
'@typescript-eslint/explicit-function-return-type': 'off',
'@typescript-eslint/no-empty-function': 'off',
'@typescript-eslint/no-non-null-assertion': 'off',
'@typescript-eslint/no-unused-vars': ['error', { ignoreRestSiblings: true }],
'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
'@typescript-eslint/no-empty-function': 0,
'@typescript-eslint/no-non-null-assertion': 0,
'@typescript-eslint/explicit-module-boundary-types': 0,
},
};
.editorconfig
.eslintignore
**/*.svg
package.json
lib/
es/
dist/
_site/
coverage/
CNAME
LICENSE
yarn.lock
netlify.toml
yarn-error.log
**/assets
**/*.yml
*.sh
*.snap
.gitignore
.npmignore
.prettierignore
.DS_Store
.editorconfig
.eslintignore
**/*.yml
.gitattributes
.stylelintrc
.vcmrc
node_modules/**
lib/
es/
{
"singleQuote": true,
"trailingComma": "all",
"endOfLine" : "lf",
"endOfLine": "lf",
"printWidth": 100,
"proseWrap": "never",
"arrowParens": "avoid",
......@@ -14,4 +14,4 @@
}
}
]
}
}
\ No newline at end of file
......@@ -22,7 +22,9 @@ npm i @ant-design-vue/pro-layout@next -S
First, you should add the icons that you need into the library.
```js
import 'ant-design-vue/dist/antd.less';
import 'ant-design-vue/dist/antd.less'; // antd css
import '@ant-design-vue/pro-layout/dist/style.css'; // pro-layout css
import { createApp } from 'vue';
import ProLayout, { PageContainer } from '@ant-design-vue/pro-layout';
......@@ -35,19 +37,27 @@ After that, you can use pro-layout in your Vue components as simply as this:
```vue
<template>
<pro-layout v-bind="state">
<pro-layout
:locale="locale"
v-bind="state"
v-model:openKeys="state.openKeys"
v-model:collapsed="state.collapsed"
v-model:selectedKeys="state.selectedKeys"
>
<router-view />
</pro-layout>
</template>
<script>
import { defineComponent, reactive } from 'vue';
// import { getMenuData, clearMenuItem } from '@ant-design-vue/pro-layout';
const locale=(i18n: string) => i18n;
export default defineComponent({
setup() {
const state = reactive({
collapsed: false,
openKeys: ['/dashboard'],
selectedKeys: ['/welcome'],
......@@ -63,6 +73,7 @@ export default defineComponent({
});
return {
locale,
state,
};
},
......@@ -76,6 +87,7 @@ or `TSX`
import { defineComponent, reactive } from 'vue';
import { RouterView } from 'vue-router';
import ProLayout from '@ant-design-vue/pro-layout';
import '@ant-design-vue/pro-layout/dist/style.css'; // pro-layout css
export default defineComponent({
setup() {
......@@ -108,6 +120,6 @@ export default defineComponent({
## Build project
```bash
npm run compile # Build library
npm run build # Build library
npm run test # Runing Test
```
module.exports = {
env: {
test: {
presets: [['@babel/preset-env', { targets: { node: true } }]],
plugins: [
['@vue/babel-plugin-jsx', { mergeProps: false }],
'@babel/plugin-proposal-optional-chaining',
'@babel/plugin-transform-object-assign',
'@babel/plugin-proposal-object-rest-spread',
'@babel/plugin-proposal-export-default-from',
'@babel/plugin-proposal-export-namespace-from',
'@babel/plugin-proposal-class-properties',
'@babel/plugin-syntax-dynamic-import',
'@babel/plugin-transform-runtime',
'@babel/plugin-transform-modules-commonjs',
// 'transform-es2015-modules-commonjs',
],
},
},
};
<template>
<a-config-provider :getPopupContainer="getPopupContainer">
<router-view />
</a-config-provider>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
export default defineComponent({
name: 'App',
setup() {
const getPopupContainer = (el: Element, dialogContext: any) => {
if (dialogContext) {
return dialogContext.getDialogWrap();
} else {
return document.body;
}
}
return {
getPopupContainer,
}
}
});
</script>
<style>
#app {
height: 100%;
}
</style>
import type { App } from 'vue';
import * as Icons from '@ant-design/icons-vue';
const filterIcons = ['default', 'createFromIconfontCN', 'getTwoToneColor', 'setTwoToneColor'];
export default (app: App) => {
Object.keys(Icons)
.filter(k => !filterIcons.includes(k))
.forEach(k => {
app.component(Icons[k].displayName, Icons[k]);
});
};
import { createApp, onMounted, reactive, watch } from 'vue';
import { menus } from './menus';
import { BaseMenu, MenuTheme, MenuMode } from '../src';
import { Card, Space, Button, Switch } from 'ant-design-vue';
import { useMenu } from '../src/hooks/useMenu';
import { RouterLink } from './mock-router';
import * as Icon from '@ant-design/icons-vue';
import 'ant-design-vue/dist/antd.less';
const BaseMenuDemo = {
setup() {
const state = reactive({
theme: 'dark' as MenuTheme,
mode: 'inline' as MenuMode,
themeChecked: true,
modeChecked: true,
});
const [menuState] = useMenu({
collapsed: false,
openKeys: ['/dashboard'],
selectedKeys: ['/dashboard/monitor'],
});
onMounted(() => {
watch(
() => state.themeChecked,
val => {
state.theme = val ? 'dark' : 'light';
},
);
watch(
() => state.modeChecked,
val => {
state.mode = val ? 'inline' : 'horizontal';
},
);
});
return () => (
<div class="components">
<h2># BaseMenu</h2>
<Card style={{ marginBottom: '24px', background: 'rgb(244,244,244)' }}>
<Space size="middle">
<Button
disabled={state.mode !== 'inline'}
type="primary"
onClick={() => {
menuState.collapsed = !menuState.collapsed;
}}
>
{menuState.collapsed ? '展开' : '收起'}
</Button>
<Switch
checkedChildren="dark"
unCheckedChildren="light"
v-model={[state.themeChecked, 'checked']}
/>
<Switch
checkedChildren="inline"
unCheckedChildren="horizontal"
v-model={[state.modeChecked, 'checked']}
/>
</Space>
<div style={{ margin: '12px 0' }}>
<p>SelectedKeys: {JSON.stringify(menuState.selectedKeys)}</p>
<p>OpenKeys: {JSON.stringify(menuState.openKeys)}</p>
<p>Collapsed: {JSON.stringify(menuState.collapsed)}</p>
<p>MenuMode: {JSON.stringify(state.mode)}</p>
<p>MenuTheme: {JSON.stringify(state.theme)}</p>
</div>
</Card>
<div class="demo" style="background: rgb(244,244,244);">
<div class="container" style="width: 256px;">
<BaseMenu
menuData={menus}
theme={state.theme}
mode={state.mode}
collapsed={menuState.collapsed}
openKeys={menuState.openKeys}
selectedKeys={menuState.selectedKeys}
{...{
'onUpdate:openKeys': $event => {
menuState.openKeys = $event;
},
'onUpdate:selectedKeys': $event => {
menuState.selectedKeys = $event;
},
}}
/>
</div>
</div>
</div>
);
},
};
const app = createApp(BaseMenuDemo);
const filterIcons = ['default', 'createFromIconfontCN', 'getTwoToneColor', 'setTwoToneColor'];
Object.keys(Icon)
.filter(k => !filterIcons.includes(k))
.forEach(k => {
app.component(Icon[k].displayName, Icon[k]);
});
app.use(RouterLink).mount('#__vue-content>div');
@import '~ant-design-vue/es/style/themes/default.less';
.right-content {
height: 48px;
min-width: 100px;
padding: 0 12px;
float: right;
display: flex;
margin-left: auto;
overflow: hidden;
text-align: right;
> * {
height: 100%;
}
&.mix-dark,
&.mix-light {
color: @text-color-dark;
}
&.side-dark,
&.side-light,
&.top-light {
color: @text-color;
}
&.top-dark {
color: @text-color-dark;
}
.action {
display: flex;
align-items: center;
padding: 0 12px;
cursor: pointer;
transition: all 0.3s;
&:hover {
background: rgba(0, 0, 0, 0.055);
}
}
}
.action-overlay {
.ant-dropdown-menu-item {
min-width: 160px;
> span {
margin-right: 6px;
}
}
}
import { defineComponent } from 'vue';
import { useRoute } from 'vue-router';
import { PageContainer, Route } from '../../../src';
export default defineComponent({
setup() {
const route = useRoute();
return () => (
<div>
<h1>Child Form: {route.meta.title}</h1>
<pre>{JSON.stringify(route.meta, null, 4)}</pre>
</div>
);
},
});
import { defineComponent } from 'vue';
import { useRoute } from 'vue-router';
import { i18n } from '../../index';
import { PageContainer } from '../../../src';
export default defineComponent({
setup() {
const route = useRoute();
return () => (
<PageContainer title={i18n(`${route.meta.title}`)}>
<router-view />
</PageContainer>
);
},
});
import { defineComponent } from 'vue';
import { useRoute, useRouter } from 'vue-router';
import { Table, Button } from 'ant-design-vue';
import { PlusOutlined } from '@ant-design/icons-vue';
export default defineComponent({
setup() {
const router = useRouter();
const route = useRoute();
const columns = [
{ dataIndex: 'key', title: '#' },
{ dataIndex: 'title', title: '标题' },
{ dataIndex: 'content', title: '内容' },
{
dataIndex: 'action',
title: '操作',
customRender: ({ record }) => {
return (
<>
<a>编辑</a>
<a>删除</a>
</>
);
},
},
];
return () => (
<div>
<h1>Child Table: {route.meta.title}</h1>
<Button
style={{ marginBottom: '12px' }}
onClick={() => {
router.push({ path: '/form/child/page2' });
}}
>
<PlusOutlined />
新增
</Button>
<Table columns={columns} />
</div>
);
},
});
import { defineComponent } from 'vue';
import { useRoute } from 'vue-router';
import { PageContainer, Route } from '../../src';
export default defineComponent({
setup() {
const route = useRoute();
return () => (
<PageContainer
title={route.meta?.title}
breadcrumb={{
routes: [{ path: '/', breadcrumbName: 'home' }] as Route[],
}}
>
<h1>Form: {route.meta.title}</h1>
<pre>{JSON.stringify(route.meta, null, 4)}</pre>
</PageContainer>
);
},
});
import { defineComponent } from 'vue';
import { useRoute } from 'vue-router';
import { Button, Dropdown, Menu, Tag, Descriptions } from 'ant-design-vue';
import { PageContainer, Route } from '../../src';
import { EllipsisOutlined } from '@ant-design/icons-vue';
import { i18n } from '../index';
export default defineComponent({
setup() {
const route = useRoute();
return () => (
<PageContainer
title={route.meta.title}
subTitle={'page sub-title'}
breadcrumb={{
routes: route.matched.concat().map(r => {
return {
path: r.path,
breadcrumbName: i18n(r.meta?.title || ''),
} as Route;
}),
}}
tags={[<Tag color={'blue'}>Tag 1</Tag>, <Tag color={'pink'}>Tag 2</Tag>]}
tabList={[
{
tab: '已选择',
key: '1',
},
{
tab: '可点击',
key: '2',
},
{
tab: '禁用',
key: '3',
disabled: true,
},
]}
extra={[
<Button key="1">次要按钮</Button>,
<Button key="2">次要按钮</Button>,
<Button key="3" type="primary">
主要按钮
</Button>,
<Dropdown
key="dropdown"
trigger={['click']}
overlay={
<Menu>
<Menu.Item key="1">下拉菜单</Menu.Item>
<Menu.Item key="2">下拉菜单2</Menu.Item>
<Menu.Item key="3">下拉菜单3</Menu.Item>
</Menu>
}
>
<Button key="4" style={{ padding: '0 8px' }}>
<EllipsisOutlined />
</Button>
</Dropdown>,
]}
content={
<Descriptions column={2} style={{ marginBottom: -16 }}>
<Descriptions.Item label="创建人">曲丽丽</Descriptions.Item>
<Descriptions.Item label="关联表单">
<a>421421</a>
</Descriptions.Item>
<Descriptions.Item label="创建时间">2017-01-10</Descriptions.Item>
<Descriptions.Item label="单据备注">浙江省杭州市西湖区工专路</Descriptions.Item>
</Descriptions>
}
extraContent={<span>额外内容区,位于 content 的右侧</span>}
/* v-slots={{
tags: () => {
return <Tag>Tag1Slot</Tag>;
},
}} */
/* v-slots={{
extra: () => {
return [
<Button key="1">次要按钮</Button>,
<Button key="2">次要按钮</Button>,
<Button key="3" type="primary">
主要按钮
</Button>,
<Dropdown
key="dropdown"
trigger={['click']}
overlay={
<Menu>
<Menu.Item key="1">下拉菜单</Menu.Item>
<Menu.Item key="2">下拉菜单2</Menu.Item>
<Menu.Item key="3">下拉菜单3</Menu.Item>
</Menu>
}
>
<Button key="4" style={{ padding: '0 8px' }}>
<EllipsisOutlined />
</Button>
</Dropdown>,
];
},
content: () => {
return (
<Descriptions column={2} style={{ marginBottom: -16 }}>
<Descriptions.Item label="创建人">曲丽丽</Descriptions.Item>
<Descriptions.Item label="关联表单">
<a>421421</a>
</Descriptions.Item>
<Descriptions.Item label="创建时间">2017-01-10</Descriptions.Item>
<Descriptions.Item label="单据备注">浙江省杭州市西湖区工专路</Descriptions.Item>
</Descriptions>
);
},
extraContent: () => {
return <span>额外内容区,位于 content 的右侧</span>;
},
}} */
>
<pre>{JSON.stringify(route.meta, null, 4)}</pre>
<p>
<p>block</p>
...
<br />
long text..
</p>
<p>
<p>block</p>
...
<br />
long text..
</p>
<p>
<p>block</p>
...
<br />
long text..
</p>
<p>
<p>block</p>
...
<br />
long text..
</p>
<p>
<p>block</p>
...
<br />
long text..
</p>
<p>
<p>block</p>
...
<br />
long text..
</p>
<p>
<p>block</p>
...
<br />
long text..
</p>
</PageContainer>
);
},
});
import { defineComponent } from 'vue';
import { globalState as state } from '../state';
export default defineComponent({
setup() {
return () => (
<div>
<div>Welcome</div>
<p>
<p>block</p>
...
<br />
long text..
</p>
<p>
<p>block</p>
...
<br />
long text..
</p>
<p>
<p>block</p>
...
<br />
long text..
</p>
<p>
<p>block</p>
...
<br />
long text..
</p>
<p>
<p>block</p>
...
<br />
long text..
</p>
<p>
<p>block</p>
...
<br />
long text..
</p>
<p>
<p>block</p>
...
<br />
long text..
</p>
</div>
);
},
});
.browser-nav {
padding: 2px 6px;
background-color: #ebedf1;
&::before {
content: '';
display: inline-block;
width: 12px;
height: 12px;
border-radius: 50%;
background-color: #fd6458;
box-shadow: 20px 0 0 #ffbf2b, 40px 0 0 #24cc3d;
}
}
import { createApp, defineComponent, ref, h, onMounted } from 'vue';
import { RouterLink } from './mock-router';
import * as Icon from '@ant-design/icons-vue';
import './demoBox.less';
export const DemoBox = defineComponent({
setup(_, { slots }) {
const instance = ref();
const frameRef = ref();
const content = (): void => {
const children = slots?.default();
console.log('frameRef.value', frameRef.value);
const body = frameRef.value.contentDocument.body;
const head = frameRef.value.contentDocument.head;
const el = document.createElement('div');
el.className = 'demoBox'
body.appendChild(el);
// const styleLink = document.createElement('link');
// styleLink.rel = 'stylesheet';
// styleLink.type = 'text/css';
// styleLink.href = './index.css';
head.innerHTML = `
<link href="/node_modules/normalize.css/normalize.css" type="text/css" rel="stylesheet">
<link href="./index.css" type="text/css" rel="stylesheet">
`
const box = createApp({
render() {
return h('div', children);
},
}).use(RouterLink);
const filterIcons = ['default', 'createFromIconfontCN', 'getTwoToneColor', 'setTwoToneColor'];
Object.keys(Icon)
.filter(k => !filterIcons.includes(k))
.forEach(k => {
box.component(Icon[k].displayName, Icon[k]);
});
box.mount(el);
instance.value = box;
}
onMounted(() => {
content();
})
return {
frameRef,
content,
}
},
render () {
return (
<div class="browser-mockup with-url">
<div class="browser-nav">
</div>
<iframe ref="frameRef" height="450px" width="100%" style="border: 0;" />
</div>
)
}
});
import { defineComponent, createApp } from 'vue';
import { Layout, Menu } from 'ant-design-vue';
const View = defineComponent({
setup() {
return () => (
<Layout>
<Layout.Header>header</Layout.Header>
<Layout>
<Layout.Sider>
<Menu>
<Menu.Item>
<span>123</span>
</Menu.Item>
</Menu>
</Layout.Sider>
<Layout.Content>
<div>content</div>
</Layout.Content>
</Layout>
</Layout>
);
},
});
const app = createApp(View);
app.mount('#__vue-content>div');
import { Button, Card, Space, Switch } from 'ant-design-vue';
import 'ant-design-vue/dist/antd.less';
import { createApp, reactive } from 'vue';
import FooterToolbar, { FooterToolbarProps } from '../src/FooterToolbar';
import { useRouteContext } from '../src/RouteContext';
const DemoComponent = {
setup() {
const routeContext = useRouteContext();
const state = reactive({
name: '',
toolbarProps: {
extra: undefined,
renderContent: undefined,
} as FooterToolbarProps,
});
console.log('routeContext', routeContext);
return () => (
<div class="components">
<h2># Template</h2>
<Card style={{ marginBottom: '24px', background: 'rgb(244,244,244)' }}>
<Space size="middle">
<Button
type="primary"
onClick={() => {
state.name = new Date().getTime().toString();
routeContext.setHasFooterToolbar(!routeContext.hasFooterToolbar);
}}
>
{!routeContext.hasFooterToolbar ? 'Open' : 'Close'}
</Button>
<Switch
checkedChildren="w/ extra"
unCheckedChildren="w/o extra"
checked={!!state.toolbarProps.extra}
onClick={() => {
state.name = new Date().getTime().toString();
state.toolbarProps.extra = !state.toolbarProps.extra ? (
<img
src="https://gw.alipayobjects.com/zos/antfincdn/PmY%24TNNDBI/logo.svg"
alt="logo"
width="32"
height="32"
/>
) : undefined;
}}
/>
<Switch
checkedChildren="w/ renderContent"
unCheckedChildren="w/o renderContent"
checked={!!state.toolbarProps.renderContent}
onClick={() => {
state.name = new Date().getTime().toString();
state.toolbarProps.renderContent = !state.toolbarProps.renderContent
? () => 'home_toolbar'
: undefined;
}}
/>
</Space>
<div style={{ margin: '12px 0' }}>
state
<pre>{JSON.stringify(state, null, 2)}</pre>
routeContext:
<pre>{JSON.stringify(routeContext, null, 2)}</pre>
</div>
</Card>
<div>
{routeContext.hasFooterToolbar && (
<FooterToolbar {...state.toolbarProps}>
<Button type="primary">right</Button>
</FooterToolbar>
)}
</div>
</div>
);
},
};
const app = createApp(DemoComponent);
app.mount('#__vue-content>div');
import { createApp, defineComponent } from 'vue';
import { Card } from 'ant-design-vue';
import GlobalFooter from '../src/GlobalFooter';
import 'ant-design-vue/dist/antd.less';
const GlobalFooterDemo = defineComponent({
setup() {
return () => (
<div class="components" style={{ background: 'rgb(240, 240, 240)', paddingBottom: '20px' }}>
<Card style={{ marginBottom: '24px', background: 'rgb(244,244,244)' }}>
<h2># BasicLayout</h2>
</Card>
<GlobalFooter
links={[
{
key: '1',
title: 'Pro Layout',
href: 'https://www.github.com/vueComponent/pro-layout',
blankTarget: true,
},
{
key: '2',
title: 'Github',
href: 'https://www.github.com/vueComponent/ant-design-vue-pro',
blankTarget: true,
},
{
key: '3',
title: '@Sendya',
href: 'https://www.github.com/sendya/',
blankTarget: true,
},
]}
copyright={
<a href="https://github.com/vueComponent" target="_blank">
vueComponent
</a>
}
/>
</div>
);
},
});
createApp(GlobalFooterDemo).mount('#__vue-content>div');
import { createApp, defineComponent, reactive } from 'vue';
import { Card, Space, Button } from 'ant-design-vue';
import { ContentWidth } from '../src/typings';
import GridContent, { GridContentProps } from '../src/GridContent';
import 'ant-design-vue/dist/antd.less';
const GridContentDemo = defineComponent({
setup() {
const state = reactive<GridContentProps>({
contentWidth: 'Fixed',
});
return () => (
<div class="components" style={{ background: 'rgb(240, 240, 240)', paddingBottom: '20px' }}>
<Card style={{ marginBottom: '24px', background: 'rgb(244,244,244)' }}>
<Space size="middle">
ContentWidth:
<Button
type="primary"
onClick={() => {
state.contentWidth = state.contentWidth === 'Fixed' ? 'Fluid' : 'Fixed';
}}
>
{state.contentWidth}
</Button>
</Space>
</Card>
<GridContent
contentWidth={state.contentWidth}
style={{ background: 'rgb(220, 220, 220)', padding: '22px' }}
>
Content
<br />
...
<br />
...
<br />
...
<br />
...
<br />
...
<br />
</GridContent>
</div>
);
},
});
createApp(GridContentDemo).mount('#__vue-content>div');
import * as Icons from '@ant-design/icons-vue/es';
import type { App } from 'vue';
import type { IconType } from '@ant-design/icons-vue/es/components/Icon';
type AllIcon = {
[key: string]: IconType;
};
export const filterIcons = [
'default',
'createFromIconfontCN',
'getTwoToneColor',
'setTwoToneColor',
];
export default (app: App) => {
const allIcon: AllIcon = Icons as any;
Object.keys(Icons)
.filter(k => !filterIcons.includes(k))
.forEach(k => {
app.component(allIcon[k].displayName, allIcon[k]);
});
};
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<link href="index.css" type="text/css" rel="stylesheet">
<style>
#app {
height: 100%;
}
</style>
</head>
<body>
<noscript>
<strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
</noscript>
<div id="app">
</div>
</body>
<script src="index.js"></script>
</html>
\ No newline at end of file
.simple-wrap {
position: relative;
margin-bottom: 1rem;
line-height: 1;
&:after {
width: 100%;
height: 1px;
content: '';
position: absolute;
bottom: 0.125em;
left: 0;
background-color: hotpink;
z-index: -100;
}
&:before {
width: 100%;
height: 1px;
content: '';
position: absolute;
top: 0.125em;
left: 0;
background-color: hotpink;
z-index: -100;
}
}
.two-tone-wrap,
.all-icons-wrap {
.container {
display: flex;
flex-flow: row wrap;
width: 80vw;
margin: auto;
}
.card {
height: 90px;
margin: 12px 0 16px;
width: 16.6666%;
text-align: center;
}
.name-description {
display: block;
text-align: center;
transform: scale(0.83);
font-family: 'Lucida Console', Consolas;
white-space: nowrap;
}
.text {
margin: 0 0.5rem;
}
}
.ant-design-doc-set-wrap {
.container {
margin: 40px 0;
list-style: none;
overflow: hidden;
}
.container-item {
float: left;
width: 16.66%;
text-align: center;
list-style: none;
height: 100px;
transition: color 0.3s ease-in-out, background-color 0.3s ease-in-out;
position: relative;
margin: 3px 0;
border-radius: 4px;
overflow: hidden;
padding: 10px 0 0;
}
.title {
text-align: center;
}
}
\ No newline at end of file
import 'ant-design-vue/dist/antd.less';
import { createApp, defineComponent, watch, ref, watchEffect, onMounted, computed } from 'vue';
import { createRouter, createWebHashHistory, useRoute, useRouter, RouterLink } from 'vue-router';
import { Avatar, Button, Space, Select, Switch, Menu } from 'ant-design-vue';
import { UserOutlined, SmileOutlined } from '@ant-design/icons-vue';
import { default as ProLayout, FooterToolbar, WaterMark, getMenuData, Route } from '../src/';
import { globalState as state } from './state';
import './demo.less';
import registerIcons from './_util/icons';
// demo pages
import Page1 from './demo/page1';
import Welcome from './demo/welcome';
import FormPage from './demo/form';
import ChildPage from './demo/child/child-page';
import ChildTable from './demo/child/child-table';
import ChildForm from './demo/child/child-form';
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const noop = () => {};
const locales: Record<string, string> = {
home: '首页',
'views.dashboard.title': '页面1',
};
export const i18n = (key: string): string => {
return locales[key] || key;
};
const BasicLayout = defineComponent({
name: 'BasicLayout',
inheritAttrs: false,
setup() {
const { getRoutes } = useRouter();
const route = useRoute();
const { menuData } = getMenuData(getRoutes());
const breadcrumb = computed(() =>
route.matched.concat().map(item => {
return { path: item.path, breadcrumbName: item.meta.title } as Route;
}),
);
const updateSelectedMenu = () => {
console.log('route', route.matched.concat());
const matched = route.matched.concat().map(item => item.path);
matched.shift();
state.selectedKeys = matched;
};
const updateOpenKeys = () => {
state.openKeys = route.matched.concat().map(item => item.path);
};
onMounted(() => {
// if sider collapsed, set openKeys is null.
const cacheOpenKeys = ref<string[]>([]);
watch(
() => state.collapsed,
(collapsed: boolean) => {
if (collapsed) {
cacheOpenKeys.value = state.openKeys;
state.openKeys = [];
} else {
state.openKeys = cacheOpenKeys.value;
}
},
);
// watch route
watch(route, () => {
updateSelectedMenu();
updateOpenKeys();
});
});
return () => (
<ProLayout
layout={state.layout}
navTheme={state.navTheme}
locale={i18n}
isMobile={state.isMobile}
fixSiderbar={state.fixSiderbar}
fixedHeader={state.fixedHeader}
contentWidth={'Fluid'}
primaryColor={'#1890ff'}
contentStyle={{ minHeight: '300px' }}
siderWidth={state.sideWidth}
splitMenus={state.splitMenus}
menuData={menuData}
collapsed={state.collapsed}
openKeys={state.openKeys}
selectedKeys={state.selectedKeys}
onCollapse={$event => {
state.collapsed = $event;
}}
onOpenKeys={$event => {
state.openKeys = $event;
}}
// onSelect={updateSelectedMenu}
onMenuHeaderClick={e => {
console.log('onMenuHeaderClick', e);
}}
rightContentRender={props => (
<div
class={['right-content', `${props.layout}-${props.navTheme}`]}
style={{ marginRight: '16px' }}
>
<span>
<Avatar icon={<UserOutlined />} /> Sendya
</span>
</div>
)}
// menuHeaderRender={() => (
// <a>
// <img src="https://gw.alipayobjects.com/zos/antfincdn/PmY%24TNNDBI/logo.svg" />
// {state.collapsed && state.layout !== 'mix' ? null : <h1>Pro Preview</h1>}
// </a>
// )}
/* menuItemRender={item => {
return (
<Menu.Item inlineIndent={24} key={item.path}>
<router-link to={{ ...item.meta, path: item.path }}>
<SmileOutlined />
<span class="custom-menu-item">{i18n(`${item.meta.title}`)}</span>
</router-link>
</Menu.Item>
);
}}
subMenuItemRender={(item, children) => {
return (
<Menu.SubMenu title={<span>{i18n(`${item.meta.title}`)}</span>} key={item.path}>
{children}
</Menu.SubMenu>
);
}} */
breadcrumbRender={({ route: r, routes, paths }) =>
routes.indexOf(r) === routes.length - 1 ? (
<span>{i18n(r.breadcrumbName)}</span>
) : (
<RouterLink to={{ path: `/${paths.join('/')}` }}>{i18n(r.breadcrumbName)}</RouterLink>
)
}
breadcrumb={{
routes: breadcrumb.value,
}}
>
<router-view />
<FooterToolbar>
<Space>
<Button
onClick={() => {
state.navTheme = state.navTheme === 'dark' ? 'light' : 'dark';
}}
>
Theme Switch
</Button>
<Select
getPopupContainer={triggerNode => triggerNode.parentNode}
value={state.layout}
onChange={val => {
state.layout = val;
}}
style={{ width: '150px' }}
>
<Select.Option value="side">Side</Select.Option>
<Select.Option value="top">Top</Select.Option>
<Select.Option value="mix">Mix</Select.Option>
</Select>
<Switch
checkedChildren="Fixed Header"
unCheckedChildren="UnFixed Header"
checked={state.fixedHeader}
onChange={() => {
state.fixedHeader = !state.fixedHeader;
}}
/>
<Switch
checkedChildren="Fixed SideBar"
unCheckedChildren="UnFixed SideBar"
checked={state.fixSiderbar}
onChange={() => {
state.fixSiderbar = !state.fixSiderbar;
}}
/>
<Switch
checkedChildren="Split Menus"
unCheckedChildren="Un Split Menus"
checked={state.splitMenus}
onChange={() => {
state.splitMenus = !state.splitMenus;
}}
/>
</Space>
</FooterToolbar>
</ProLayout>
);
},
});
const SimpleDemo = {
setup() {
return () => (
<div style={{ height: '100%' }}>
<router-view />
</div>
);
},
};
const RouteView = defineComponent({
setup() {
return () => <router-view />;
},
});
const routes = [
{
path: '/welcome',
name: 'welcome',
meta: { title: 'Welcome', icon: 'SmileOutlined' },
component: Welcome,
},
{
path: '/dashboard',
name: 'dashboard',
meta: { title: 'views.dashboard.title', icon: 'SmileOutlined' },
redirect: '/dashboard/analysis',
component: RouteView,
children: [
{
path: '/dashboard/analysis',
name: 'analysis',
meta: { icon: 'SmileOutlined', title: 'Analysis' },
component: Page1,
},
{
path: '/dashboard/monitor',
name: 'monitor',
meta: { icon: 'SmileOutlined', title: 'Monitor' },
component: Page1,
},
{
path: '/dashboard/workplace',
name: 'workplace',
meta: { icon: 'SmileOutlined', title: 'Workplace' },
component: Page1,
},
],
},
{
path: '/form',
name: 'form',
meta: { title: 'Form', icon: 'SmileOutlined' },
redirect: '/form/basic-form',
component: RouteView,
children: [
{
path: '/form/basic-form',
name: 'basic-form',
meta: { icon: 'SmileOutlined', title: 'Basic Form' },
component: FormPage,
},
{
path: '/form/step-form',
name: 'step-form',
meta: { icon: 'SmileOutlined', title: 'Step Form' },
component: FormPage,
},
{
path: '/form/advanced-form',
name: 'advance-form',
meta: { icon: 'SmileOutlined', title: 'Advanced Form' },
component: FormPage,
},
{
path: '/form/child',
name: 'child-form',
meta: { icon: 'SmileOutlined', hideInMenu: true, title: 'Child Form' },
redirect: '/form/child/page1',
component: ChildPage,
children: [
{
path: '/form/child/page1',
name: 'child-page1-table',
meta: { title: 'Page1 Child Table' },
component: ChildTable,
},
{
path: '/form/child/page2',
name: 'child-page1-form',
meta: { title: 'Page2 Child Form' },
component: ChildForm,
},
],
},
],
},
];
const router = createRouter({
routes: [
{
path: '/',
name: 'index',
meta: { title: 'home' },
redirect: '/welcome',
component: BasicLayout,
children: routes,
},
],
history: createWebHashHistory(),
});
const app = createApp(SimpleDemo);
registerIcons(app);
app.use(router).use(ProLayout).mount('#app');
<template>
<pro-layout
v-model:collapsed="baseState.collapsed"
v-model:selectedKeys="baseState.selectedKeys"
v-model:openKeys="baseState.openKeys"
v-bind="state"
>
<!-- custom right-content -->
<template #rightContentRender>
<span style="color: #0f0">right</span>
</template>
<!-- content begin -->
<router-view />
<!-- content end -->
<FooterToolbar>
<a-space>
<span :disabled="state.layout !== 'mix'">
<span style="margin-right: 8px">SplitMenus:</span>
<a-switch
v-model:checked="state.splitMenus"
:disabled="state.layout !== 'mix'"
checked-children="ON"
un-checked-children="OFF"
/>
</span>
<a-select v-model:value="state.navTheme" style="width: 100px">
<a-select-option value="light">Light</a-select-option>
<a-select-option value="dark">Dark</a-select-option>
</a-select>
<a-select v-model:value="state.layout" style="width: 100px">
<a-select-option value="side">Side</a-select-option>
<a-select-option value="top">Top</a-select-option>
<a-select-option value="mix">Mix</a-select-option>
</a-select>
</a-space>
</FooterToolbar>
</pro-layout>
</template>
<script lang="ts">
import { defineComponent, reactive, watchEffect } from 'vue';
import { useRouter } from 'vue-router';
import { Button, Switch, Select, Space } from 'ant-design-vue';
import { getMenuData, clearMenuItem, FooterToolbar } from '@ant-design-vue/pro-layout';
import type { RouteContextProps } from '@ant-design-vue/pro-layout';
const i18n = (t: string) => t;
export default defineComponent({
name: 'BasicLayout',
components: {
FooterToolbar,
[Button.name]: Button,
[Switch.name]: Switch,
[Select.name]: Select,
[Select.Option.displayName!]: Select.Option,
[Space.name]: Space,
},
setup() {
const router = useRouter();
const { menuData } = getMenuData(clearMenuItem(router.getRoutes()));
const baseState = reactive<Omit<RouteContextProps, 'menuData'>>({
selectedKeys: [],
openKeys: [],
// default
collapsed: false,
});
const state = reactive({
menuData,
splitMenus: false,
title: 'ProLayout',
logo: 'https://alicdn.antdv.com/v2/assets/logo.1ef800a8.svg',
navTheme: 'light',
layout: 'mix',
});
watchEffect(() => {
if (router.currentRoute) {
baseState.selectedKeys = router.currentRoute.value.matched.concat().map(r => r.path);
}
});
return {
i18n,
baseState,
state,
};
},
});
</script>
import { createApp } from 'vue';
import router from './router';
import icons from './icons';
import App from './App.vue';
import { ConfigProvider }from 'ant-design-vue';
import ProLayout, { PageContainer } from '../src';
import 'ant-design-vue/es/style';
const app = createApp(App);
app.use(router);
app.use(icons);
app.use(ConfigProvider);
app.use(ProLayout);
app.use(PageContainer);
app.mount('#app');
export const menus = [
{
path: '/welcome',
name: 'welcome',
meta: { icon: 'SmileOutlined', title: 'Welcome' },
},
{
path: '/dashboard',
name: 'dashboard',
meta: { icon: 'dashboard-outlined', title: 'Dashboard' },
children: [
{
path: '/dashboard/analysis',
name: 'analysis',
meta: { icon: 'SmileOutlined', title: 'Analysis' },
},
{
path: '/dashboard/monitor',
name: 'monitor',
meta: { icon: 'SmileOutlined', title: 'Monitor' },
},
{
path: '/dashboard/workplace',
name: 'workplace',
meta: { icon: 'SmileOutlined', title: 'Workplace' },
},
],
},
{
path: '/form',
name: 'form',
meta: { title: 'Form', icon: 'SmileOutlined' },
children: [
{
path: '/form/basic-form',
name: 'basic-form',
meta: { icon: 'SmileOutlined', title: 'Basic Form' },
},
{
path: '/form/step-form',
name: 'step-form',
meta: { icon: 'SmileOutlined', title: 'Step Form' },
},
{
path: '/form/advanced-form',
name: 'advance-form',
meta: { icon: 'SmileOutlined', title: 'Advanced Form' },
},
],
},
];
import { defineComponent, toRefs, PropType } from 'vue';
import { withInstall } from 'ant-design-vue/es/_util/type';
export const useRoute = () => {
console.log('path', location.pathname);
return {
matched: [],
};
};
export const RouterLink = withInstall(
defineComponent({
name: 'RouterLink',
props: {
href: {
type: String,
default: null,
},
to: {
type: [Object, String] as PropType<Record<string, any> | string>,
default: () => undefined,
},
},
setup(props, { slots }) {
const { to, href } = toRefs(props);
const curHref =
(href.value && href.value) || typeof to.value === 'string'
? to.value
: to.value.name || to.value.path;
return () => <a href={`#${curHref}`}>{slots.default?.()}</a>;
},
}),
);
export const RouterView = withInstall(
defineComponent({
name: 'RouterView',
setup(_, { slots }) {
return () => slots.default?.();
},
}),
);
import { createApp, defineComponent, reactive } from 'vue';
import 'ant-design-vue/dist/antd.less';
import { Button, Descriptions, Space, Statistic, Tag, message } from 'ant-design-vue';
import { PageContainer } from '../src/';
import { LikeOutlined } from '@ant-design/icons-vue';
import './index.less';
const App = defineComponent({
name: 'App',
setup: function () {
const routes = [
{
path: 'index',
breadcrumbName: 'First-level Menu',
},
{
path: 'first',
breadcrumbName: 'Second-level Menu',
},
{
path: 'second',
breadcrumbName: 'Third-level Menu',
},
];
const state = reactive({
tabActiveKey: '2',
});
return () => (
<div class="demo-page-box" style="padding: 20px; background: #ccc;">
<div style="background: #fff; height: 500px;">
<PageContainer
title="Title"
subTitle="This is a subtitle"
footer={[
<Button key="3">重置</Button>,
<Button key="2" type="primary">
提交
</Button>,
]}
breadcrumb={{ routes }}
onBack={() => message.info('@back click')}
tags={['Tag 1', 'Tag 2'].map(tag => (
<Tag color="blue">{tag}</Tag>
))}
extra={[
<Button key="3">操作</Button>,
<Button key="2">操作</Button>,
<Button key="1" type="primary">
主操作
</Button>,
]}
content={
<Descriptions size="small" column={{ md: 2 }}>
<Descriptions.Item label="创建人">张三</Descriptions.Item>
<Descriptions.Item label="联系方式">
<a>421421</a>
</Descriptions.Item>
<Descriptions.Item label="创建时间">2017-01-10</Descriptions.Item>
<Descriptions.Item label="更新时间">2017-10-10</Descriptions.Item>
<Descriptions.Item label="备注">中国浙江省杭州市西湖区古翠路</Descriptions.Item>
</Descriptions>
}
extraContent={
<Space size={24}>
<Statistic title="Feedback" value={1128} prefix={<LikeOutlined />} />
<Statistic title="Unmerged" value={93} suffix="/ 100" />
</Space>
}
tabList={[
{ key: '1', tab: 'Details' },
{ key: '2', tab: 'Rule' },
{ key: '3', tab: 'Disabled', disabled: true },
]}
tabProps={{
type: 'card',
}}
tabActiveKey={state.tabActiveKey}
onTabChange={(key: string) => {
state.tabActiveKey = key;
}}
>
<div>Page Content</div>
</PageContainer>
</div>
</div>
);
},
});
const app = createApp(App);
app.mount('#__vue-content>div');
import { createRouter, createWebHistory } from 'vue-router';
import type { RouteRecordRaw } from 'vue-router';
import BasicLayout from './layouts/BasicLayout.vue';
const routes: RouteRecordRaw[] = [
{
path: '/',
name: 'index',
meta: { title: '' },
component: BasicLayout,
redirect: '/monitor',
children: [
{
path: '/dashboard',
name: 'dashboard',
meta: { title: 'Dashboard', icon: 'SettingOutlined' },
component: () => import('./views/page1.vue'),
},
{
path: '/monitor',
name: 'monitor',
meta: { title: 'Monitor', icon: 'DatabaseOutlined' },
component: () => import('./views/page2.vue'),
},
],
},
];
export default createRouter({
history: createWebHistory(),
routes: routes,
});
declare module '*.vue' {
import { DefineComponent } from 'vue'
const component: DefineComponent<{}, {}, any>
export default component
}
import { reactive } from 'vue';
import { RouteContextProps } from '../src/RouteContext';
export const globalState = reactive<RouteContextProps>({
collapsed: false,
openKeys: ['/dashboard'],
selectedKeys: ['/welcome'],
layout: 'mix',
navTheme: 'dark',
isMobile: false,
fixSiderbar: false,
fixedHeader: false,
menuData: [],
sideWidth: 208,
splitMenus: false,
hasSideMenu: false,
hasHeader: true,
hasFooterToolbar: false,
setHasFooterToolbar: (has: boolean) => (globalState.hasFooterToolbar = has),
});
import { createApp, reactive, watchEffect } from 'vue';
import { Card, Space, Button, Tag } from 'ant-design-vue';
import 'ant-design-vue/dist/antd.less';
const DemoComponent = {
setup() {
const state = reactive({
name: 'value',
});
return () => (
<div class="components">
<h2># Template</h2>
<Card style={{ marginBottom: '24px', background: 'rgb(244,244,244)' }}>
<Space size="middle">
<Button
type="primary"
onClick={() => {
state.name = new Date().getTime().toString()
}}
>
Change Value
</Button>
</Space>
<div style={{ margin: '12px 0' }}>
<p>state.name: { JSON.stringify(state.name) }</p>
</div>
</Card>
<div class="demo" style="background: rgb(244,244,244);">
<div class="container" style="width: 256px;">
demo in here.<br />
<Tag color="pink">{state.name}</Tag>
</div>
</div>
</div>
);
},
};
const app = createApp(DemoComponent);
app.mount('#__vue-content>div');
import { createApp, reactive } from 'vue';
import { default as ProLayout, RouteContextProps } from '../src/';
import { RouterLink } from './mock-router';
import { menus } from './menus';
import registerIcons from './_util/icons';
const SimpleDemo = {
setup() {
const appState = reactive<RouteContextProps>({
collapsed: false,
selectedKeys: [],
openKeys: [],
menuData: menus,
});
return () => (
<ProLayout
{...appState}
title="Pro Tests"
logo="https://alicdn.antdv.com/v2/assets/logo.1ef800a8.svg"
onSelect={(selectedKeys: string[] | false) => {
selectedKeys && (appState.selectedKeys = selectedKeys);
}}
onOpenKeys={(openKeys: string[] | false) => {
console.log('onOpenKeys', openKeys);
openKeys && (appState.openKeys = openKeys);
}}
onCollapse={collapsed => {
appState.collapsed = collapsed;
}}
footerRender={() => <div>custom-footer</div>}
v-slots={{
rightContentRender: () => (
<div class="custom-header-right-content">
<span>custom-right-content</span>
</div>
),
}}
>
<div style="min-height: 300px;">content</div>
</ProLayout>
);
},
};
const app = createApp(SimpleDemo);
app.use(ProLayout);
app.use(RouterLink);
app.use(registerIcons);
app.mount('#__vue-content>div');
<template>
<PageContainer title="Page 1" sub-title="is a sub-title.">
<span>page-content</span>
<a-button @click="handleClick">Button</a-button>
</PageContainer>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import { Button, message } from 'ant-design-vue';
export default defineComponent({
components:{
[Button.name]: Button,
},
setup() {
const handleClick= () => {
message.info('clicked')
}
return {
handleClick,
}
},
})
</script>
<template>
<PageContainer :title="$route.meta.title">
<template #tags>
<a-tag>tag1</a-tag>
<a-tag color="pink">tag2</a-tag>
</template>
<span>page2</span>
</PageContainer>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import { Tag } from 'ant-design-vue';
export default defineComponent({
components: {
[Tag.name]: Tag,
},
setup() {
return {}
},
})
</script>
/// <reference types="vite/client" />
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" href="/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>ProLayout</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/examples/main.ts"></script>
</body>
</html>
{
"name": "@ant-design-vue/pro-layout",
"version": "3.0.0-beta.12",
"main": "./lib/index.js",
"module": "./es/index.js",
"version": "3.1.0-alpha.0",
"license": "MIT",
"files": [
"dist",
"src"
],
"style": "./dist/style.css",
"main": "./dist/pro-layout.umd.js",
"module": "./dist/pro-layout.es.js",
"exports": {
".": {
"import": "./dist/pro-layout.es.js",
"require": "./dist/pro-layout.umd.js"
},
"./dist/style.css": "./dist/style.css"
},
"repository": {
"type": "git",
"url": "https://github.com/vueComponent/pro-layout"
......@@ -11,96 +24,44 @@
"tangjinzhou <415800467@qq.com>",
"Sendya <18x@loacg.com>"
],
"license": "MIT",
"scripts": {
"clean": "cross-env TS_NODE_PROJECT=scripts/tsconfig.json node -r ts-node/register ./scripts/cleanup.ts",
"start": "vc-tools run server",
"lint": "eslint src/ -c .eslintrc.js --ext .tsx,.ts",
"lint:fix": "eslint --fix src/ -c .eslintrc.js --ext .tsx,.ts",
"compile": "vc-tools run compile",
"test": "cross-env NODE_ENV=test jest --config .jest.js",
"prepublishOnly": "npm run lint && npm run compile"
"dev": "vite --force",
"build": "vue-tsc --noEmit && vite build",
"serve": "vite preview"
},
"peerDependencies": {
"ant-design-vue": "^2.2.0-rc.1",
"vue-router": "^4.0.3",
"vue": ">=3.1.3"
},
"devDependencies": {
"@ant-design-vue/tools": "^3.1.0",
"@babel/core": "^7.12.10",
"@babel/plugin-proposal-export-default-from": "^7.12.1",
"@babel/plugin-transform-modules-commonjs": "^7.12.1",
"@babel/plugin-transform-object-assign": "^7.12.1",
"@babel/plugin-transform-runtime": "^7.12.10",
"@babel/preset-env": "^7.12.11",
"@types/fs-extra": "^9.0.6",
"@types/jest": "^24.0.17",
"@types/node": "^13.13.15",
"@types/rimraf": "^3.0.0",
"@typescript-eslint/eslint-plugin": "^2.33.0",
"@typescript-eslint/parser": "^2.33.0",
"@vue/babel-plugin-jsx": "^1.0.2",
"@vue/cli-plugin-babel": "~4.5.0",
"@vue/cli-plugin-eslint": "~4.5.0",
"@vue/cli-plugin-typescript": "~4.5.0",
"@vue/cli-service": "~4.5.0",
"@vue/compiler-sfc": "^3.1.3",
"@vue/eslint-config-prettier": "^6.0.0",
"@vue/eslint-config-typescript": "^5.0.2",
"@vue/test-utils": "^2.0.0-rc.6",
"babel-jest": "^26.6.3",
"babel-plugin-transform-es2015-modules-commonjs": "^6.26.2",
"cross-env": "^7.0.3",
"eslint": "^7.16.0",
"eslint-config-prettier": "^7.1.0",
"eslint-plugin-prettier": "^3.3.0",
"eslint-plugin-vue": "^7.3.0",
"fs-extra": "^9.0.1",
"jest": "^26.6.3",
"jest-environment-jsdom-fifteen": "^1.0.2",
"jest-serializer-vue": "^2.0.2",
"jest-transform-stub": "^2.0.0",
"prettier": "^2.2.1",
"rimraf": "^3.0.2",
"ts-jest": "^26.4.4",
"ts-node": "^9.1.1",
"typescript": "~3.9.3",
"vue": "^3.1.3",
"vue-jest": "^5.0.0-alpha.10",
"ant-design-vue": ">=2.2.0",
"vue": ">=3.1.0",
"vue-router": "^4.0.3"
},
"dependencies": {
"@babel/runtime": "^7.11.2",
"ant-design-vue": "^2.2.0-rc.1",
"core-js": "^3.9.1",
"lodash-es": "^4.17.20",
"omit.js": "^2.0.2",
"vue": "^3.1.3",
"vue-types": "^3.0.1"
"ant-design-vue": "^2.2.1",
"lodash-es": "^4.17.21",
"vue": "^3.1.0",
"vue-router": "^4.0.10"
},
"files": [
"es",
"lib"
],
"config": {
"accesslog": false,
"port": 9528,
"entry": {
"@ant-design-vue/pro-layout": [
"./src/index.ts"
]
},
"parallel": ["es", "js"],
"css": {
"loaderOptions": {
"less": {
"options": {
"javascriptEnabled": true
}
}
}
}
"devDependencies": {
"@types/lodash-es": "^4.17.4",
"@types/node": "^16.0.1",
"@typescript-eslint/eslint-plugin": "^4.28.2",
"@typescript-eslint/parser": "^4.28.2",
"@vitejs/plugin-vue": "^1.2.4",
"@vitejs/plugin-vue-jsx": "^1.1.6",
"@vue/compiler-sfc": "^3.1.0",
"@vuedx/typescript-plugin-vue": "^0.7.4",
"eslint": "^7.30.0",
"eslint-config-airbnb-base": "^14.2.1",
"eslint-config-prettier": "^8.3.0",
"eslint-plugin-import": "^2.23.4",
"eslint-plugin-prettier": "^3.4.0",
"eslint-plugin-vue": "^7.13.0",
"less": "^4.1.1",
"prettier": "^2.3.2",
"typescript": "^4.3.2",
"vite": "^2.4.0",
"vite-dts": "^1.0.3",
"vite-plugin-dts": "^0.5.2",
"vue-tsc": "^0.0.24"
},
"description": "Ant Design Pro Layout of Vue, easy to use pro scaffolding."
}
module.exports = {
plugins: {
autoprefixer: {}
}
}
#!/bin/sh
ls | grep -v .jest.js | grep -v .eslintrc.js | grep -e '.d.ts' -e '.js$' | xargs rm
# keep one d.ts to make vscode auto import working with corrcet path
rm ./es || echo
rm ./lib || echo
import rimraf from 'rimraf';
import path from 'path';
const errorHandler = err => {
if (err !== null) {
console.warn(err);
}
};
rimraf(path.resolve(__dirname, '../es'), errorHandler);
rimraf(path.resolve(__dirname, '../lib'), errorHandler);
{
"compilerOptions": {
"module": "commonJS",
"esModuleInterop": true
}
}
\ No newline at end of file
import { computed, reactive, unref, provide, defineComponent, toRefs } from 'vue';
import { computed, reactive, unref, provide, defineComponent, toRefs, watch } from 'vue';
import type { CSSProperties, PropType, ExtractPropTypes } from 'vue';
import 'ant-design-vue/es/layout/style';
import Layout from 'ant-design-vue/es/layout';
import omit from 'omit.js';
import useMediaQuery from './hooks/useMediaQuery';
import { withInstall } from 'ant-design-vue/es/_util/type';
import { defaultSettingProps } from './defaultSettings';
import { BreadcrumbProps, getPrefixCls } from './RouteContext';
import { getPrefixCls, defaultRouteContext } from './RouteContext';
import type { BreadcrumbProps } from './RouteContext';
import { default as SiderMenuWrapper, siderMenuProps } from './SiderMenu';
import { WrapContent } from './WrapContent';
import { default as Header, headerViewProps } from './Header';
import { CustomRender, FormatMessage, WithFalse } from './typings';
import globalHeaderProps from './GlobalHeader/headerProps';
import { HeaderView as Header, headerViewProps } from './Header';
import { getPropsSlot, PropTypes } from './utils';
import omit from 'omit.js';
import useMediaQuery from './hooks/useMediaQuery';
import type { CustomRender, FormatMessage, WithFalse } from './typings';
import './BasicLayout.less';
export const basicLayoutProps = {
...defaultSettingProps,
...siderMenuProps,
...globalHeaderProps,
...headerViewProps,
pure: PropTypes.looseBool,
loading: PropTypes.looseBool,
locale: {
type: [Function, Object, Boolean] as PropType<WithFalse<FormatMessage>>,
default: (s: string) => s,
type: [Function, Boolean] as PropType<WithFalse<FormatMessage>>,
default() {
return (s: string) => s;
},
},
/**
* 是否禁用移动端模式,有的管理系统不需要移动端模式,此属性设置为true即可
......@@ -58,11 +67,11 @@ export const basicLayoutProps = {
contentStyle: PropTypes.style,
};
export type BasicLayoutProps = ExtractPropTypes<typeof basicLayoutProps>;
export type BasicLayoutProps = Partial<ExtractPropTypes<typeof basicLayoutProps>>;
const ProLayout = defineComponent({
name: 'ProLayout',
inheritAttrs: false,
// inheritAttrs: false,
emits: [
'update:collapsed',
'update:open-keys',
......@@ -175,7 +184,7 @@ const ProLayout = defineComponent({
const routeContext = reactive({
getPrefixCls,
// ...props,
locale: refProps.locale,
locale: refProps.locale.value || defaultRouteContext.locale,
breadcrumb: computed(() => {
return {
...refProps.breadcrumb,
......
import type { PropType } from 'vue';
import type { CustomRender, Theme, MenuDataItem, WithFalse } from '../typings';
import { defaultSettingProps } from '../defaultSettings';
import { PropTypes } from '../utils'
import { siderMenuProps } from '../SiderMenu/SiderMenu';
export default {
...defaultSettingProps,
prefixCls: PropTypes.string.def('ant-pro'),
collapsed: PropTypes.looseBool,
isMobile: PropTypes.looseBool,
logo: siderMenuProps.logo,
logoStyle: siderMenuProps.logoStyle,
headerTheme: {
type: String as PropType<Theme>,
default: 'dark',
},
menuData: {
type: Array as PropType<MenuDataItem[]>,
default: () => [],
},
splitMenus: siderMenuProps.splitMenus,
menuRender: {
type: [Object, Function] as PropType<
WithFalse<(props: any /* HeaderViewProps */, defaultDom: CustomRender) => CustomRender>
>,
default: () => undefined,
},
menuHeaderRender: siderMenuProps.menuHeaderRender,
rightContentRender: {
type: [Object, Function] as PropType<WithFalse<(props: any) => CustomRender>>,
default: () => undefined,
},
collapsedButtonRender: siderMenuProps.collapsedButtonRender,
matchMenuKeys: siderMenuProps.matchMenuKeys,
// events
onMenuHeaderClick: PropTypes.func,
onCollapse: siderMenuProps.onCollapse,
onOpenKeys: siderMenuProps.onOpenKeys,
onSelect: siderMenuProps.onSelect,
};
import { computed, FunctionalComponent, PropType, ExtractPropTypes } from 'vue';
import { defaultSettingProps } from '../defaultSettings';
import { CustomRender, Theme, MenuDataItem, WithFalse } from '../typings';
import { computed } from 'vue';
import type { FunctionalComponent, ExtractPropTypes } from 'vue';
import type { RouteRecordRaw } from 'vue-router';
import type { CustomRender } from '../typings';
import {
siderMenuProps,
SiderMenuProps,
defaultRenderLogo,
defaultRenderLogoAndTitle,
defaultRenderCollapsedButton,
} from '../SiderMenu/SiderMenu';
import type { SiderMenuProps } from '../SiderMenu/SiderMenu';
import { TopNavHeader } from '../TopNavHeader';
import { clearMenuItem, PropTypes } from '../utils';
import { clearMenuItem } from '../utils';
import { useRouteContext } from '../RouteContext';
import globalHeaderProps from './headerProps';
import './index.less';
export const globalHeaderProps = {
...defaultSettingProps,
prefixCls: PropTypes.string.def('ant-pro'),
collapsed: PropTypes.looseBool,
isMobile: PropTypes.looseBool,
logo: siderMenuProps.logo,
logoStyle: siderMenuProps.logoStyle,
headerTheme: {
type: String as PropType<Theme>,
default: 'dark',
},
menuData: {
type: Array as PropType<MenuDataItem[]>,
default: () => [],
},
splitMenus: siderMenuProps.splitMenus,
menuRender: {
type: [Object, Function] as PropType<
WithFalse<(props: any /* HeaderViewProps */, defaultDom: CustomRender) => CustomRender>
>,
default: () => undefined,
},
menuHeaderRender: siderMenuProps.menuHeaderRender,
rightContentRender: {
type: [Object, Function] as PropType<WithFalse<(props: any) => CustomRender>>,
default: () => undefined,
},
collapsedButtonRender: siderMenuProps.collapsedButtonRender,
matchMenuKeys: siderMenuProps.matchMenuKeys,
// events
onMenuHeaderClick: PropTypes.func,
onCollapse: siderMenuProps.onCollapse,
onOpenKeys: siderMenuProps.onOpenKeys,
onSelect: siderMenuProps.onSelect,
};
export type GlobalHeaderProps = ExtractPropTypes<typeof globalHeaderProps>;
const renderLogo = (
......@@ -97,7 +60,7 @@ export const GlobalHeader: FunctionalComponent<GlobalHeaderProps> = (props, { sl
const noChildrenMenuData = (menuData || []).map(item => ({
...item,
children: undefined,
}));
})) as RouteRecordRaw[];
const clearMenuData = clearMenuItem(noChildrenMenuData);
return (
<TopNavHeader
......@@ -146,5 +109,3 @@ export const GlobalHeader: FunctionalComponent<GlobalHeaderProps> = (props, { sl
);
};
GlobalHeader.emits = ['menuHeaderClick', 'collapse', 'openKeys', 'select'];
export default GlobalHeader;
import { defineComponent, computed, toRefs, PropType, ExtractPropTypes } from 'vue';
import { defineComponent, computed, toRefs } from 'vue';
import type { PropType, ExtractPropTypes } from 'vue';
import type { RouteRecordRaw } from 'vue-router';
import 'ant-design-vue/es/layout/style';
import Layout from 'ant-design-vue/es/layout';
import { GlobalHeader, globalHeaderProps, GlobalHeaderProps } from './GlobalHeader';
import { GlobalHeader } from './GlobalHeader';
import type { GlobalHeaderProps } from './GlobalHeader';
import globalHeaderProps from './GlobalHeader/headerProps';
import { TopNavHeader } from './TopNavHeader';
import { useRouteContext } from './RouteContext';
import { CustomRender, WithFalse } from './typings';
import type { CustomRender, WithFalse } from './typings';
import { clearMenuItem, PropTypes } from './utils';
import './Header.less';
......@@ -60,7 +64,7 @@ export const HeaderView = defineComponent({
);
// cache menu
const clearMenuData = computed(
() => (context.menuData && clearMenuItem(context.menuData)) || [],
() => (context.menuData && clearMenuItem(context.menuData as RouteRecordRaw[])) || [],
);
const className = computed(() => {
......@@ -131,5 +135,3 @@ export const HeaderView = defineComponent({
};
},
});
export default HeaderView;
......@@ -61,7 +61,7 @@ export const getPrefixCls = (suffixCls?: string, customizePrefixCls?: string) =>
};
// set default context
const defaultRouteContext = reactive({
export const defaultRouteContext = reactive({
getPrefixCls,
locale: (t: string) => t,
contentWidth: 'Fluid',
......
......@@ -42,7 +42,7 @@ export const baseMenuProps = {
default: () => 'ant-pro',
},
locale: {
type: [Function, Object, Boolean] as PropType<WithFalse<FormatMessage>>,
type: [Function, Boolean] as PropType<WithFalse<FormatMessage>>,
default: (t: string): string => t,
},
menuData: {
......
......@@ -185,7 +185,7 @@ const SiderMenu: FC<SiderMenuProps> = (props: SiderMenuProps) => {
const defaultMenuDom = (
<BaseMenu
prefixCls={getPrefixCls()}
locale={context.locale}
locale={props.locale || context.locale}
theme={sTheme.value === 'realDark' ? 'dark' : sTheme.value}
mode="inline"
menuData={hasSide.value ? flatMenuData.value : context.menuData}
......
import { ref, computed, FunctionalComponent, ExtractPropTypes } from 'vue';
import { siderMenuProps, SiderMenuProps, defaultRenderLogoAndTitle } from '../SiderMenu/SiderMenu';
import { ref, computed } from 'vue';
import type { FunctionalComponent, ExtractPropTypes } from 'vue';
import globalHeaderProps from '../GlobalHeader/headerProps';
import { siderMenuProps, defaultRenderLogoAndTitle } from '../SiderMenu/SiderMenu';
import type { SiderMenuProps } from '../SiderMenu/SiderMenu';
import BaseMenu from '../SiderMenu/BaseMenu';
import { globalHeaderProps } from '../GlobalHeader';
import { default as ResizeObserver } from 'ant-design-vue/es/vc-resize-observer';
import { FormatMessage } from '../typings';
import type { FormatMessage } from '../typings';
import { useRouteContext } from '../RouteContext';
import './index.less';
export const topNavHeaderProps = {
...siderMenuProps,
...globalHeaderProps,
};
export const topNavHeaderProps = Object.assign({}, siderMenuProps, globalHeaderProps);
export type TopNavHeaderProps = Partial<ExtractPropTypes<typeof topNavHeaderProps>> &
Partial<SiderMenuProps>;
......@@ -88,7 +88,7 @@ export const TopNavHeader: FunctionalComponent<TopNavHeaderProps> = props => {
<div style={{ flex: 1 }} class={`${prefixCls}-menu`}>
<BaseMenu
prefixCls={propPrefixCls}
locale={context.locale as FormatMessage}
locale={props.locale || context.locale}
theme={props.theme === 'realDark' ? 'dark' : props.theme}
mode={props.mode}
collapsed={props.collapsed}
......
......@@ -4,27 +4,26 @@ export * from './utils/getMenuData';
export { createContext, useContext } from './hooks/context';
export type { ContextType, CreateContext } from './hooks/context';
export { default as FooterToolbar } from './FooterToolbar';
export { default as SiderMenuWrapper } from './SiderMenu';
export { default as BaseMenu, baseMenuProps } from './SiderMenu/BaseMenu';
export type { BaseMenuProps } from './SiderMenu/BaseMenu';
export type { SiderMenuWrapperProps } from './SiderMenu';
export type { MenuMode, OpenEventHandler, SelectInfo } from './SiderMenu/typings';
export { GlobalHeader } from './GlobalHeader';
export { default as GlobalFooter } from './GlobalFooter';
export { default as GridContent } from './GridContent';
export { WrapContent } from './WrapContent';
// export {
// default as ProProvider,
// defaultProProviderProps,
// useProProvider,
// ProProviderData,
// } from './ProProvider';
export { default as PageContainer } from './PageContainer';
export { default as SiderMenuWrapper } from './SiderMenu';
export type { SiderMenuWrapperProps } from './SiderMenu';
export type { GlobalHeaderProps } from './GlobalHeader';
export type { GlobalFooterProps } from './GlobalFooter';
export { default as BaseMenu, baseMenuProps } from './SiderMenu/BaseMenu';
export type { BaseMenuProps } from './SiderMenu/BaseMenu';
export type { MenuMode, OpenEventHandler, SelectInfo } from './SiderMenu/typings';
export { default as PageContainer } from './PageContainer';
export { default as FooterToolbar } from './FooterToolbar';
export { default as WaterMark } from './WaterMark';
export type { WaterMarkProps } from './WaterMark';
export { default } from './BasicLayout';
export { default as ProLayout } from './BasicLayout';
export type { BasicLayoutProps } from './BasicLayout';
declare module '*.vue' {
import { DefineComponent } from 'vue'
const component: DefineComponent<{}, {}, any>
export default component
}
import { RouteRecordRaw } from 'vue-router';
import type { RouteRecordRaw } from 'vue-router';
export { clearMenuItem, flatMap, getMenuFirstChildren } from './index';
export type MenuData = {
menuData: RouteRecordRaw[];
......
import { Slots } from 'vue';
import type { RouteRecord, RouteRecordRaw } from 'vue-router';
import { MenuDataItem } from '../typings';
import PropTypes from 'ant-design-vue/es/_util/vue-types';
......@@ -18,9 +19,9 @@ export function warning(valid: boolean, message: string) {
warn(valid, `[@ant-design-vue/pro-layout] ${message}`);
}
export function clearMenuItem(menusData: MenuDataItem[]): MenuDataItem[] {
export function clearMenuItem(menusData: RouteRecord[] | RouteRecordRaw[]): RouteRecordRaw[] {
return menusData
.map(item => {
.map((item: RouteRecord | RouteRecordRaw) => {
const finalItem = { ...item };
if (!finalItem.name || finalItem.meta?.hideInMenu) {
return null;
......@@ -29,7 +30,7 @@ export function clearMenuItem(menusData: MenuDataItem[]): MenuDataItem[] {
if (finalItem && finalItem?.children) {
if (
!finalItem.meta?.hideChildInMenu &&
finalItem.children.some(child => child && child.name && !child.meta?.hideInMenu)
finalItem.children.some((child: RouteRecord | RouteRecordRaw) => child && child.name && !child.meta?.hideInMenu)
) {
return {
...item,
......@@ -40,17 +41,19 @@ export function clearMenuItem(menusData: MenuDataItem[]): MenuDataItem[] {
}
return finalItem;
})
.filter(item => item) as MenuDataItem[];
.filter(item => item) as RouteRecordRaw[];
}
export function flatMap(menusData: MenuDataItem[]): MenuDataItem[] {
export function flatMap(menusData: RouteRecord[]): MenuDataItem[] {
return menusData
.map(item => {
const finalItem = { ...item };
let finalItem = { ...item } as MenuDataItem;
if (!finalItem.name || finalItem.meta?.hideInMenu) {
return null;
}
delete finalItem.children;
if (finalItem.children) {
delete finalItem.children
}
return finalItem;
})
.filter(item => item) as any[];
......
/// <reference types="vite/client" />
{
"compilerOptions": {
"allowSyntheticDefaultImports": true,
"declaration": true,
"plugins": [
{
"name": "@vuedx/typescript-plugin-vue"
}
],
"target": "esnext",
"module": "esnext",
"target": "ES2018",
"moduleResolution": "node",
"strict": true,
"jsx": "preserve",
"moduleResolution": "node",
"sourceMap": true,
"resolveJsonModule": true,
"esModuleInterop": true,
"skipLibCheck": true,
"lib": [
"DOM",
"DOM.Iterable",
"ES6",
"ES2020.Symbol.WellKnown",
"ESNext.Symbol"
]
"lib": ["esnext", "dom"],
"types": ["vite/client", "node"],
"baseUrl": "./",
"paths": {
"@/*":["./src/*"],
"@ant-design-vue/pro-layout":["./src/index.ts"]
},
},
"include": ["./src", "./typings/"],
"typings": "./typings/index.d.ts",
"exclude": [
"node_modules",
"build",
"scripts",
"acceptance-tests",
"webpack",
"jest",
"src/setupTests.ts",
"tslint:latest",
"tslint-config-prettier"
]
"include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue", "examples/**/*.vue"]
}
import { resolve } from 'path';
import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
import vueJsx from '@vitejs/plugin-vue-jsx';
import dts from 'vite-dts';
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue(), vueJsx(), dts()],
resolve: {
alias: {
'@ant-design-vue/pro-layout': resolve(__dirname, 'src'),
'@': resolve(__dirname, 'src'),
},
},
build: {
lib: {
entry: resolve(__dirname, 'src/index.ts'),
name: 'ProLayout',
},
rollupOptions: {
// input: {
// main: resolve(__dirname, 'index.html'),
// },
external: ['vue'],
output: {
// Provide global variables to use in the UMD build
// for externalized deps
globals: {
vue: 'Vue',
},
},
},
},
optimizeDeps: {
include: [
/* '@ant-design-vue/pro-layout', */
'ant-design-vue/es',
'@ant-design/icons-vue',
'lodash-es',
],
},
css: {
postcss: {},
preprocessorOptions: {
less: {
// DO NOT REMOVE THIS LINE
javascriptEnabled: true,
},
},
},
});
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment