This commit is contained in:
harrywan 2021-11-03 14:50:19 +08:00
commit bd4eba275f
88 changed files with 9982 additions and 723 deletions

View File

@ -2,9 +2,9 @@ name: Deploy Docs
on: on:
push: push:
branches: branches:
- master - vue3
paths: paths:
- 'packages/fes-doc/**/**' - 'docs/**/**'
jobs: jobs:
build-and-deploy: build-and-deploy:
runs-on: ubuntu-latest runs-on: ubuntu-latest
@ -14,7 +14,7 @@ jobs:
- name: Build and Deploy - name: Build and Deploy
uses: JamesIves/github-pages-deploy-action@master uses: JamesIves/github-pages-deploy-action@master
env: env:
ACCESS_TOKEN: ${{ secrets.ACCESS_TOKEN }} ACCESS_TOKEN: ${{ secrets.QLIN_GITEE_TOKEN }}
BRANCH: gh-pages BRANCH: master
FOLDER: packages/fes-doc/docs/.vuepress/dist FOLDER: docs/.vuepress/dist
BUILD_SCRIPT: cd packages/fes-doc && npm install && npm run build && cd ../../ BUILD_SCRIPT: npm install && npm run docs:build

View File

@ -104,3 +104,16 @@ For bugs and feature requests, [please create an issue](https://github.com/WeBan
3. Commit your changes: `git commit -am 'Add some feature'` 3. Commit your changes: `git commit -am 'Add some feature'`
4. Push to the branch: `git push origin my-new-feature` 4. Push to the branch: `git push origin my-new-feature`
5. Submit a pull request :D 5. Submit a pull request :D
## 社区活动
### Fesjs 社区有奖征文活动
为了 Fes.js 开源项目更好的运转,同时回馈开源社区,社区推出有奖征文活动!欢迎大家投递实践经验,给社区用户,更广泛的开发者提供借鉴。
经验输出也可以帮助到你系统沉淀自有项目,梳理工作思路,也能够帮助你的技术博客做宣传。优秀的实践案例将有机会邀请参与项目社区技术会议分享,赶快来参与吧。
请戳https://mp.weixin.qq.com/s/nV4NG_OUUrdgtft8g_IW4g

View File

@ -172,7 +172,8 @@ export default function ({ cgiMock, mockjs, utils }) {
- utils.file(path)从项目根目录根据path寻找文件返回文件流。 - utils.file(path)从项目根目录根据path寻找文件返回文件流。
## 配置 Mock ## 配置 Mock
详见配置 #mock
详见配置 [mock](../reference/config/#mock)。
## 关闭 Mock ## 关闭 Mock
可以通过配置关闭。 可以通过配置关闭。

View File

@ -31,6 +31,7 @@ export default {
### top ### top
<!-- ![top](/top.png) --> <!-- ![top](/top.png) -->
<img :src="$withBase('top.png')" alt="top"> <img :src="$withBase('top.png')" alt="top">
### mixin ### mixin
<!-- ![mixin](/mixin.png) --> <!-- ![mixin](/mixin.png) -->
<img :src="$withBase('mixin.png')" alt="mixin"> <img :src="$withBase('mixin.png')" alt="mixin">
@ -69,6 +70,8 @@ export default {
title: "Fes.js", title: "Fes.js",
// 底部文字 // 底部文字
footer: 'Created by MumbelFe', footer: 'Created by MumbelFe',
// 主题light
theme: 'dark'
// 是否开启 tabs // 是否开启 tabs
multiTabs: false, multiTabs: false,
// 布局类型 // 布局类型
@ -98,6 +101,13 @@ export default {
- **详情**:页面底部的文字。 - **详情**:页面底部的文字。
### theme
- **类型**`String`
- **默认值**`dark`
- **详情**:主题,可选有 `dark``light`
### navigation ### navigation
- **类型**`String` - **类型**`String`

View File

@ -23,5 +23,19 @@ Fes.js 中约定 `src/global.css` 为全局样式,如果存在此文件,会
</style> </style>
``` ```
## CSS Modules
支持 `Vue` 的 [CSS Modules](https://vue-loader.vuejs.org/zh/guide/css-modules.html#%E7%94%A8%E6%B3%95) 用法,可以直接使用:
```vue
<style module>
.layout-content {
max-width: 1000px;
}
```
如果想直接引入CSS文件的话则CSS文件名需要包含`.module`,比如:
```js
import style from '@/styles/index.module.css'
console.log(style)
```
## CSS 预处理器 ## CSS 预处理器
Fes.js 内置支持 `less`,不支持 `sass``stylus`,但如果有需求,可以通过 `chainWebpack` 配置或者 `fes-plugin` 插件的形式支持。 Fes.js 内置支持 `less`,不支持 `sass``stylus`,但如果有需求,可以通过 `chainWebpack` 配置或者 `fes-plugin` 插件的形式支持。

View File

@ -172,7 +172,7 @@ export default function ({ cgiMock, mockjs, utils }) {
- utils.file(path)从项目根目录根据path寻找文件返回文件流。 - utils.file(path)从项目根目录根据path寻找文件返回文件流。
## 配置 Mock ## 配置 Mock
详见配置 #mock 详见配置 [mock](../reference/config/#mock)
## 关闭 Mock ## 关闭 Mock
可以通过配置关闭。 可以通过配置关闭。

View File

@ -144,6 +144,24 @@ export default {
} }
``` ```
## extraBabelPlugins
- 类型: `array`
- 默认值: `[]`
- 详情:
配置额外的 babel 插件。
- 示例:
```js
export default {
extraBabelPlugins: [
['import', { libraryName: 'ant-design-vue', libraryDirectory: 'es', style: 'css' }],
],
}
```
## extraPostCSSPlugins ## extraPostCSSPlugins
- 类型: `array` - 类型: `array`
@ -278,6 +296,12 @@ export default {
配置 webpack 的 publicPath。当打包的时候webpack 会在静态文件路径前面添加 `publicPath` 的值,当你需要修改静态文件地址时,比如使用 CDN 部署,把 `publicPath` 的值设为 CDN 的值就可以。 配置 webpack 的 publicPath。当打包的时候webpack 会在静态文件路径前面添加 `publicPath` 的值,当你需要修改静态文件地址时,比如使用 CDN 部署,把 `publicPath` 的值设为 CDN 的值就可以。
## router
- 类型: `object`
- 默认值: `{ mode: 'hash' }`
- 详情: 配置路由,具体请查看指南中关于路由的介绍
## singular ## singular
- 类型: `boolean` - 类型: `boolean`
- 默认值: `false` - 默认值: `false`

View File

@ -14,27 +14,6 @@
- 可配置页面是否需要 layout。 - 可配置页面是否需要 layout。
## 布局类型
配置参数是 `navigation`, 内容默认是 `side`
```js
export default {
layout: {
navigation: 'side
}
}
```
### side
<!-- ![side](/side.png) -->
<img :src="$withBase('side.png')" alt="side">
### top
<!-- ![top](/top.png) -->
<img :src="$withBase('top.png')" alt="top">
### mixin
<!-- ![mixin](/mixin.png) -->
<img :src="$withBase('mixin.png')" alt="mixin">
## 启用方式 ## 启用方式
`package.json` 中引入依赖: `package.json` 中引入依赖:
```json ```json
@ -46,17 +25,53 @@ export default {
} }
``` ```
## 布局类型
配置参数是 `navigation`, 布局有三种类型 `side``mixin``top` 默认是 `side`
```js
export default {
layout: {
navigation: 'side'
}
}
```
### side
<!-- ![side](/side.png) -->
<img :src="$withBase('side.png')" alt="side">
### top
<!-- ![top](/top.png) -->
<img :src="$withBase('top.png')" alt="top">
### mixin
<!-- ![mixin](/mixin.png) -->
<img :src="$withBase('mixin.png')" alt="mixin">
### 页面禁用布局 ### 页面禁用布局
Fes.js 渲染路由时,如果路由元信息存在配置 `layout``false`,则表示禁用此配置,用户只需要如下配置: 布局是默认开启的,但是可能某些页面不需要展示布局样式,比如登录页面。我们只需要在页面的`.vue`中添加如下配置:
```vue ```vue
<config> <config lang="json">
{ {
"layout": false "layout": false
} }
</config> </config>
<script>
</script>
``` ```
如果只是不想展示`side`,则:
<config lang="json">
{
"layout": {
"side": false
}
}
</config>
```
`layout`的可选配置有:
- **side** 左侧区域
- **top** 头部区域
- **logo**logo和标题区域。
## 配置 ## 配置
@ -69,6 +84,8 @@ export default {
title: "Fes.js", title: "Fes.js",
// 底部文字 // 底部文字
footer: 'Created by MumbelFe', footer: 'Created by MumbelFe',
// 主题light
theme: 'dark'
// 是否开启 tabs // 是否开启 tabs
multiTabs: false, multiTabs: false,
// 布局类型 // 布局类型
@ -98,6 +115,13 @@ export default {
- **详情**:页面底部的文字。 - **详情**:页面底部的文字。
### theme
- **类型**`String`
- **默认值**`dark`
- **详情**:主题,可选有 `dark``light`
### navigation ### navigation
- **类型**`String` - **类型**`String`
@ -164,13 +188,13 @@ export default {
- 图标使用[antv icon](https://www.antdv.com/components/icon-cn/)在这里使用组件type。 - 图标使用[antv icon](https://www.antdv.com/components/icon-cn/)在这里使用组件type。
```js ```js
{ {
name: "user" icon: "user"
} }
``` ```
- 图使用本地或者远程svg图片。 - 图使用本地或者远程svg图片。
```js ```js
{ {
name: "/wine-outline.svg" icon: "/wine-outline.svg"
} }
``` ```
@ -191,7 +215,7 @@ export const layout = {
- **默认值**`null` - **默认值**`null`
- **详情**布局的 Header 部位提供组件自定义功能。 - **详情**top的区域部分位置提供组件自定义功能。
#### unAccessHandler #### unAccessHandler
- **类型**`Function` - **类型**`Function`

View File

@ -45,7 +45,7 @@ export default {
}; };
``` ```
```js ```js
// src/locales/zh-CN.js // src/locales/en-US.js
export default { export default {
menu: { menu: {
interface: 'interface' interface: 'interface'

View File

@ -19,8 +19,7 @@
```js ```js
export default { export default {
request: { request: {
dataField: 'result', dataField: 'result'
base: '',
}, },
} }
``` ```
@ -34,13 +33,15 @@ export default {
`dataField` 对应接口统一格式中的数据字段,比如接口如果统一的规范是 `{ success: boolean, result: any}` ,那么就不需要配置,这样你通过 `useRequest` 消费的时候会生成一个默认的 `formatResult`,直接返回 `result` 中的数据,方便使用。如果你的后端接口不符合这个规范,可以自行配置 `dataField`。配置为 `''`(空字符串)的时候不做处理。 `dataField` 对应接口统一格式中的数据字段,比如接口如果统一的规范是 `{ success: boolean, result: any}` ,那么就不需要配置,这样你通过 `useRequest` 消费的时候会生成一个默认的 `formatResult`,直接返回 `result` 中的数据,方便使用。如果你的后端接口不符合这个规范,可以自行配置 `dataField`。配置为 `''`(空字符串)的时候不做处理。
#### base #### base(即将废弃)
- 类型: `string` - 类型: `string`
- 默认值: `''` - 默认值: `''`
- 详情: - 详情:
`base` 接口前缀。 `base` 接口前缀。
⚠️警告,这个字段将在下个版本废弃,推荐使用 [axios baseURL](https://github.com/axios/axios)。
### 运行时配置 ### 运行时配置
`app.js` 中进行运行时配置。 `app.js` 中进行运行时配置。
@ -51,6 +52,8 @@ export const request = {
responseDataAdaptor: (data) => { responseDataAdaptor: (data) => {
}, },
// 关闭 response data 校验(只判断 xhr status
closeResDataCheck: false,
// 请求拦截器 // 请求拦截器
requestInterceptors: [], requestInterceptors: [],
// 相应拦截器 // 相应拦截器
@ -63,7 +66,6 @@ export const request = {
// 特殊 code 处理逻辑 // 特殊 code 处理逻辑
}, },
404(error) { 404(error) {
}, },
default(error) { default(error) {
// 异常统一处理 // 异常统一处理
@ -73,6 +75,32 @@ export const request = {
...otherConfigs ...otherConfigs
} }
``` ```
#### skipErrorHandler
- 类型: `boolean | string | number | array<string | number>`
- 默认值: ``
- 详情:
指定当前请求的某些错误状态不走 `errorHandler`,单独进行处理。如果设置为 `true`,当前请求的错误处理都不走 `errorHandler`
- 示列:
```js
import {request} from '@fesjs/fes';
request('/api/login', null, {
skipErrorHandler: '110'
}).then((res) => {
// do something
}).catch((err) => {
// 这里处理 code 为 110 的异常
// 此时 errorHandler[110] 函数不会生效,也不会执行 errorHandler.default
})
```
## 使用 ## 使用
### 发起一个普通 post 请求 ### 发起一个普通 post 请求
@ -90,6 +118,27 @@ request('/api/login', {
}) })
``` ```
### merge 重复请求
连续发送多个请求,会被合并成一个请求,不会报 `REPEAT` 接口错误。
当发生 `REPEAT` 请求异常,并且确保自身代码合理的情况下,可以使用该配置。
```js
import {request} from '@fesjs/fes';
request('/api/login', {
username: 'robby',
password: '123456'
}, {
mergeRequest: true, // 在一个请求没有回来前,重复发送的请求会合并成一个请求
}).then((res) => {
// do something
}).catch((err) => {
// 处理异常
})
```
### 请求节流 ### 请求节流
```js ```js
@ -118,7 +167,7 @@ request('/api/login', {
}, { }, {
cache: { cache: {
cacheType: 'ram', // ram: 内存session: sessionStoragelocallocalStorage cacheType: 'ram', // ram: 内存session: sessionStoragelocallocalStorage
cacheTime: 1000 * 60 * 3 // 缓存时间默认3min cacheTime: 1000 * 60 * 3 // 缓存时间默认3min
}, },
}).then((res) => { }).then((res) => {
// do something // do something
@ -129,6 +178,7 @@ request('/api/login', {
`cache``true`,则默认使用 `ram` 缓存类型,缓存时间 3min。 `cache``true`,则默认使用 `ram` 缓存类型,缓存时间 3min。
### 结合 use 使用 ### 结合 use 使用
```js ```js

View File

@ -1,4 +1,4 @@
# @fesjs/plugin-access # @fesjs/plugin-sass

View File

@ -1,5 +1,5 @@
{ {
"version": "2.0.0-rc.18", "version": "independent",
"changelog": { "changelog": {
"repo": "WeBankFinTech/fes.js", "repo": "WeBankFinTech/fes.js",
"cacheDir": ".changelog", "cacheDir": ".changelog",

View File

@ -11,7 +11,7 @@
"clean": "lerna clean", "clean": "lerna clean",
"bootstrap": "lerna bootstrap", "bootstrap": "lerna bootstrap",
"build": "father-build --watch", "build": "father-build --watch",
"ver": "lerna version prerelease --preid rc --no-changelog --no-commit-hooks --no-private", "ver": "lerna version patch --no-changelog --no-commit-hooks --no-private",
"release": "father-build && lerna publish from-git", "release": "father-build && lerna publish from-git",
"docs:dev": "vuepress dev docs --clean-cache", "docs:dev": "vuepress dev docs --clean-cache",
"docs:build": "vuepress build docs --clean-cache" "docs:build": "vuepress build docs --clean-cache"

View File

@ -1,6 +1,6 @@
{ {
"name": "@fesjs/create-fes-app", "name": "@fesjs/create-fes-app",
"version": "2.0.0-rc.18", "version": "2.0.1",
"description": "create a app base on fes.js", "description": "create a app base on fes.js",
"main": "lib/index.js", "main": "lib/index.js",
"files": [ "files": [

View File

@ -1,5 +1,5 @@
// fes.config.js 只负责管理 cli 相关的配置 // fes.config.js 只负责管理 cli 相关的配置
import pxtoviewport from 'postcss-px-to-viewport'; import pxtoviewport from '@ttou/postcss-px-to-viewport';
export default { export default {

View File

@ -0,0 +1,4 @@
{
"singleQuote": true,
"trailingComma": "none"
}

View File

@ -1,6 +1,6 @@
{ {
"name": "@fesjs/template-h5", "name": "@fesjs/template-h5",
"version": "2.0.0-rc.0", "version": "2.0.0",
"description": "fes 移动端项目模版", "description": "fes 移动端项目模版",
"scripts": { "scripts": {
"build": "fes build", "build": "fes build",
@ -40,13 +40,13 @@
"access": "public" "access": "public"
}, },
"devDependencies": { "devDependencies": {
"@webank/eslint-config-webank": "0.2.10", "@webank/eslint-config-webank": "0.3.0",
"postcss-px-to-viewport": "1.1.1" "@ttou/postcss-px-to-viewport": "1.1.1"
}, },
"dependencies": { "dependencies": {
"@fesjs/fes": "^2.0.0-rc.0", "@fesjs/fes": "^2.0.0",
"@fesjs/plugin-icon": "^2.0.0-rc.0", "@fesjs/plugin-icon": "^2.0.0",
"@fesjs/plugin-request": "^2.0.0-rc.0", "@fesjs/plugin-request": "^2.0.0",
"vue": "^3.0.5" "vue": "^3.0.5"
}, },
"private": true "private": true

View File

@ -2,9 +2,6 @@
export default { export default {
define: {
__DEV__: false
},
publicPath: './', publicPath: './',
access: { access: {
roles: { roles: {
@ -17,13 +14,8 @@ export default {
multiTabs: false, multiTabs: false,
menus: [{ menus: [{
name: 'index' name: 'index'
}, {
name: 'onepiece'
}] }]
}, },
locale: {
legacy: true
},
devServer: { devServer: {
port: 8000 port: 8000
}, },

View File

@ -1,4 +1,5 @@
.DS_Store .DS_Store
.cache
# dependencies # dependencies
/node_modules /node_modules

View File

@ -0,0 +1,4 @@
{
"singleQuote": true,
"trailingComma": "none"
}

View File

@ -1,5 +0,0 @@
import sum from '@/utils/sum';
test('adds 1 + 2 to equal 3', () => {
expect(sum(1, 2)).toBe(3);
});

View File

@ -1,6 +1,6 @@
{ {
"name": "@fesjs/template", "name": "@fesjs/template",
"version": "2.0.0-rc.0", "version": "2.0.0",
"description": "fes项目模版", "description": "fes项目模版",
"scripts": { "scripts": {
"build": "fes build", "build": "fes build",
@ -43,21 +43,16 @@
"access": "public" "access": "public"
}, },
"devDependencies": { "devDependencies": {
"@webank/eslint-config-webank": "0.2.10" "@webank/eslint-config-webank": "0.3.0"
}, },
"dependencies": { "dependencies": {
"@fesjs/fes": "^2.0.0-rc.0", "@fesjs/fes": "^2.0.0",
"@fesjs/plugin-access": "^2.0.0-rc.0", "@fesjs/plugin-access": "^2.0.0",
"@fesjs/plugin-layout": "^2.0.0-rc.0", "@fesjs/plugin-layout": "^2.0.0",
"@fesjs/plugin-locale": "^2.0.0-rc.0", "@fesjs/plugin-model": "^2.0.0",
"@fesjs/plugin-model": "^2.0.0-rc.0", "@fesjs/plugin-enums": "^2.0.0",
"@fesjs/plugin-enums": "^2.0.0-rc.0", "ant-design-vue": "^2.2.0",
"@fesjs/plugin-jest": "^2.0.0-rc.0", "vue": "^3.1.0"
"@fesjs/plugin-vuex": "^2.0.0-rc.0",
"ant-design-vue": "^2.0.0",
"vue": "^3.0.5",
"vuex": "^4.0.0"
}, },
"private": true "private": true
} }

View File

@ -11,6 +11,7 @@ export const beforeRender = {
return new Promise((resolve) => { return new Promise((resolve) => {
setTimeout(() => { setTimeout(() => {
setRole('admin'); setRole('admin');
// 初始化应用的全局状态,可以通过 useModel('@@initialState') 获取,具体用法看@/components/UserCenter 文件
resolve({ resolve({
userName: 'harrywan' userName: 'harrywan'
}); });

View File

@ -0,0 +1 @@
// 放工具函数

View File

@ -1,5 +1,5 @@
<template> <template>
<div>{{initialState.userName}}</div> <div class="right">{{initialState.userName}}</div>
</template> </template>
<script> <script>
import { useModel } from '@fesjs/fes'; import { useModel } from '@fesjs/fes';
@ -13,3 +13,9 @@ export default {
} }
}; };
</script> </script>
<style scope>
.right {
text-align: right;
padding: 0 20px;
}
</style>

View File

@ -1,11 +0,0 @@
export default {
test: 'test',
'navBar.lang': 'Languages',
'layout.user.link.help': 'Help',
'layout.user.link.privacy': 'Privacy',
'layout.user.link.terms': 'Terms',
'app.preview.down.block': 'Download this page to your local project',
'app.welcome.link.fetch-blocks': 'Get all block',
'app.welcome.link.block-list': 'Quickly build standard, pages based on `block` development'
};

View File

@ -1,11 +0,0 @@
export default {
'navbar.lang': 'Bahasa',
'layout.user.link.help': 'Bantuan',
'layout.user.link.privacy': 'Privasi',
'layout.user.link.terms': 'Ketentuan',
'app.preview.down.block': 'Unduh halaman ini dalam projek lokal anda',
'app.welcome.link.fetch-blocks': 'Dapatkan semua blok',
'app.welcome.link.block-list':
'Buat standar dengan cepat, halaman-halaman berdasarkan pengembangan `block`'
};

View File

@ -1,8 +0,0 @@
export default {
'navBar.lang': 'Idiomas',
'layout.user.link.help': 'ajuda',
'layout.user.link.privacy': 'política de privacidade',
'layout.user.link.terms': 'termos de serviços',
'app.preview.down.block': 'Download this page to your local project'
};

View File

@ -1,11 +0,0 @@
export default {
test: '测试',
'navBar.lang': '语言',
'layout.user.link.help': '帮助',
'layout.user.link.privacy': '隐私',
'layout.user.link.terms': '条款',
'app.preview.down.block': '下载此页面到本地项目',
'app.welcome.link.fetch-blocks': '获取全部区块',
'app.welcome.link.block-list': '基于 block 开发,快速构建标准页面'
};

View File

@ -1,8 +0,0 @@
export default {
'navBar.lang': '語言',
'layout.user.link.help': '幫助',
'layout.user.link.privacy': '隱私',
'layout.user.link.terms': '條款',
'app.preview.down.block': '下載此頁面到本地項目'
};

View File

@ -1,100 +1,43 @@
<template> <template>
<div class="haizekuo"> <div style="padding: 32px;">
<div>国际化 {{t("test")}}</div> <h3>fes & 拉夫德鲁 </h3>
fes & 拉夫德鲁 <br />
<access :id="accessId"> accessOnepicess1 <input /> </access>
<div v-access="accessId"> accessOnepicess2 <input /> </div>
<input />
<h4>数据字典</h4> <h4>数据字典</h4>
<div v-for="item in enumsGet('status')" :key="item.key">{{item.value}}{{item.key}}</div> <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> <section>
<h4>Vuex <button @click="increment">click me{{count}}</button></h4> 计数器
<button @click="increment">click me{{count}}</button>
</section>
</div> </div>
</template> </template>
<script>
import { ref } from 'vue';
import {
enums
} from '@fesjs/fes';
export default {
setup() {
const fes = ref('fes upgrade to vue3');
const count = ref(0);
const increment = () => {
count.value++;
};
return {
fes,
increment,
count,
enumsGet: enums.get
};
}
};
</script>
<config> <config>
{ {
"name": "index", "name": "index",
"title": "首页" "title": "首页"
} }
</config> </config>
<script>
import { ref, onMounted, computed } from 'vue';
import { useStore } from 'vuex';
import {
useAccess, useRouter, useI18n, locale, enums
} from '@fesjs/fes';
export default {
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(roles);
const store = useStore();
console.log('store==>', store);
onMounted(() => {
console.log(router);
setTimeout(() => {
locale.setLocale({ lang: 'en-US' });
locale.addLocale({ lang: 'ja-JP', messages: { test: 'テスト' } });
console.log(locale.getAllLocales());
}, 2000);
setTimeout(() => {
accessId.value = '11';
}, 4000);
// router.push('/onepiece');
});
return {
accessId,
fes,
accessOnepicess,
t: localI18n.t,
enumsGet: enums.get,
roles,
count: computed(() => store.state.counter.count),
increment: () => store.commit('counter/increment')
};
}
};
</script>
<style scoped>
.haizekuo {
/* background: url('../images/icon.png'); */
}
</style>

View File

@ -1,21 +0,0 @@
<template>
<div>{{fes}}</div>
</template>
<config>
{
"name": "onepiece",
"title": "onepiece"
}
</config>
<script>
import { ref } from 'vue';
export default {
setup() {
const fes = ref('fes upgrade to vue3');
return {
fes
};
}
};
</script>

View File

@ -1,10 +0,0 @@
<template>
<div>test</div>
</template>
<script>
import { } from '@fesjs/fes';
export default {
};
</script>

View File

@ -1,23 +0,0 @@
export default {
namespaced: true,
state: () => ({
count: 0
}),
mutations: {
increment(state) {
state.count++;
}
},
getters: {
doubleCount(state) {
return state.count * 2;
}
},
actions: {
asyncIncrement({ commit }) {
setTimeout(() => {
commit('increment');
}, 2000);
}
}
};

View File

@ -1,3 +0,0 @@
import { createLogger } from 'vuex';
export default createLogger();

View File

@ -1,25 +0,0 @@
export default {
namespaced: true,
state: () => ({
name: 'aring',
age: 20,
count: 0
}),
mutations: {
increment(state) {
state.count++;
}
},
getters: {
doubleCount(state) {
return state.count * 2;
}
},
actions: {
asyncIncrement({ commit }) {
setTimeout(() => {
commit('increment');
}, 2000);
}
}
};

View File

@ -1,3 +0,0 @@
export default function sum(a, b) {
return a + b;
}

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
{ {
"name": "@fesjs/compiler", "name": "@fesjs/compiler",
"version": "2.0.0-rc.5", "version": "2.0.0",
"description": "@fesjs/compiler", "description": "@fesjs/compiler",
"main": "lib/index.js", "main": "lib/index.js",
"files": [ "files": [

View File

@ -1,6 +1,6 @@
{ {
"name": "@fesjs/plugin-access", "name": "@fesjs/plugin-access",
"version": "2.0.0-rc.1", "version": "2.0.0",
"description": "@fesjs/plugin-access", "description": "@fesjs/plugin-access",
"main": "lib/index.js", "main": "lib/index.js",
"files": [ "files": [
@ -26,8 +26,11 @@
"publishConfig": { "publishConfig": {
"access": "public" "access": "public"
}, },
"dependencies": {
"lodash": "^4.17.15"
},
"peerDependencies": { "peerDependencies": {
"@fesjs/fes": "^2.0.0-rc.0", "@fesjs/fes": "^2.0.0",
"vue": "^3.0.5" "vue": "^3.0.5"
} }
} }

View File

@ -1,6 +1,7 @@
import { reactive, unref, computed, inject } from "vue"; import { reactive, unref, computed, inject } from "vue";
import createDirective from "./createDirective"; import createDirective from "./createDirective";
import createComponent from "./createComponent"; import createComponent from "./createComponent";
import isPlainObject from "lodash/isPlainObject";
const accessKey = Symbol("plugin-access"); const accessKey = Symbol("plugin-access");
@ -50,16 +51,21 @@ const setAccess = (accessIds) => {
if (isPromise(accessIds)) { if (isPromise(accessIds)) {
return _syncSetAccessIds(accessIds); return _syncSetAccessIds(accessIds);
} }
if(isPlainObject(accessIds)){
if(accessIds.accessIds){
setAccess(accessIds.accessIds);
}
if(accessIds.roleId){
setRole(accessIds.roleId);
}
return
}
if (!Array.isArray(accessIds)) { if (!Array.isArray(accessIds)) {
throw new Error("[plugin-access]: argument to the setAccess() must be array or promise"); throw new Error("[plugin-access]: argument to the setAccess() must be array or promise or object");
} }
state.currentAccessIds = accessIds; state.currentAccessIds = accessIds;
}; };
const getAccess = () => {
return state.currentAccessIds.slice(0)
}
const _syncSetRoleId = (promise) => { const _syncSetRoleId = (promise) => {
rolePromiseList.push(promise); rolePromiseList.push(promise);
promise promise
@ -143,7 +149,7 @@ export const access = {
isDataReady, isDataReady,
setRole, setRole,
setAccess, setAccess,
getAccess, getAccess: getAllowAccessIds,
}; };
export const useAccess = (path) => { export const useAccess = (path) => {

View File

@ -1,6 +1,6 @@
{ {
"name": "@fesjs/plugin-enums", "name": "@fesjs/plugin-enums",
"version": "2.0.0-rc.1", "version": "2.0.0",
"description": "@fesjs/plugin-enums", "description": "@fesjs/plugin-enums",
"main": "lib/index.js", "main": "lib/index.js",
"files": [ "files": [
@ -27,7 +27,7 @@
"access": "public" "access": "public"
}, },
"peerDependencies": { "peerDependencies": {
"@fesjs/fes": "^2.0.0-rc.0", "@fesjs/fes": "^2.0.0",
"vue": "^3.0.5" "vue": "^3.0.5"
} }
} }

View File

@ -22,11 +22,11 @@ Object.keys(_ENUMS).forEach(key => {
function get(name, key, opt = { dir: 'value', extend: []}) { function get(name, key, opt = { dir: 'value', extend: []}) {
if (Object.prototype.toString.call(key) === '[object Object]') { if (Object.prototype.toString.call(key) === '[object Object]') {
opt = key opt = key
key = '' key = null
} }
let list = ENUMS[name] || [] let list = ENUMS[name] || []
let value let value
if (key) { if (key !== undefined && key !== null) {
let res = list.filter(item => item.key === key)[0] let res = list.filter(item => item.key === key)[0]
if (!res) return key if (!res) return key
value = parseValueDir(res.value, opt.dir) || key value = parseValueDir(res.value, opt.dir) || key
@ -81,7 +81,7 @@ function concat(name, _enum, opt = { keyName: '', valueName: '', before: false,
} else { } else {
list = list.concat(partList) list = list.concat(partList)
} }
return readonly(format(list, extend)) return readonly(format(list, opt.extend))
} }
/** /**
@ -111,7 +111,7 @@ function format(_enum = [], extend = []) {
* @param dir * @param dir
*/ */
function parseValueDir(value, dir='value') { function parseValueDir(value, dir='value') {
if (!['object', 'function'].includes(typeof value) || !value || !dir) return value if (!['object', 'function'].includes(typeof value) || !value || !dir || dir === 'value') return value
if (dir.startsWith('[')) { if (dir.startsWith('[')) {
let key = dir.slice(1, dir.indexOf(']')) let key = dir.slice(1, dir.indexOf(']'))
return parseValueDir(value[key], dir.slice(dir.indexOf(']') + 1)) return parseValueDir(value[key], dir.slice(dir.indexOf(']') + 1))

View File

@ -1,6 +1,6 @@
{ {
"name": "@fesjs/plugin-icon", "name": "@fesjs/plugin-icon",
"version": "2.0.0-rc.1", "version": "2.0.0",
"description": "@fesjs/plugin-icon", "description": "@fesjs/plugin-icon",
"main": "lib/index.js", "main": "lib/index.js",
"files": [ "files": [

View File

@ -1,6 +1,6 @@
{ {
"name": "@fesjs/plugin-jest", "name": "@fesjs/plugin-jest",
"version": "2.0.0-rc.5", "version": "2.0.0",
"description": "@fesjs/plugin-jest", "description": "@fesjs/plugin-jest",
"main": "lib/index.js", "main": "lib/index.js",
"files": [ "files": [
@ -31,7 +31,7 @@
}, },
"dependencies": { "dependencies": {
"@babel/core": "7.11.6", "@babel/core": "7.11.6",
"@fesjs/compiler": "^2.0.0-rc.5", "@fesjs/compiler": "^2.0.0",
"@umijs/babel-preset-umi": "3.2.24", "@umijs/babel-preset-umi": "3.2.24",
"babel-core": "^7.0.0-bridge.0", "babel-core": "^7.0.0-bridge.0",
"babel-jest": "^26.6.3", "babel-jest": "^26.6.3",

View File

@ -1,6 +1,6 @@
{ {
"name": "@fesjs/plugin-layout", "name": "@fesjs/plugin-layout",
"version": "2.0.0-rc.18", "version": "2.0.0",
"description": "@fesjs/plugin-layout", "description": "@fesjs/plugin-layout",
"main": "lib/index.js", "main": "lib/index.js",
"files": [ "files": [
@ -31,7 +31,7 @@
}, },
"peerDependencies": { "peerDependencies": {
"@ant-design/icons-vue": "^5.1.6", "@ant-design/icons-vue": "^5.1.6",
"@fesjs/fes": "^2.0.0-rc.0", "@fesjs/fes": "^2.0.0",
"ant-design-vue": "2.0.0", "ant-design-vue": "2.0.0",
"vue": "^3.0.5" "vue": "^3.0.5"
} }

View File

@ -6,7 +6,7 @@ const matchName = (config, name) => {
for (let i = 0; i < config.length; i++) { for (let i = 0; i < config.length; i++) {
const item = config[i]; const item = config[i];
if (item.meta && item.meta.name === name) { if (item.meta && item.meta.name === name) {
res = item.meta || {}; res = item.meta;
res.path = item.path; res.path = item.path;
break; break;
} }
@ -68,10 +68,10 @@ export function getIconsFromMenu(data) {
return []; return [];
} }
let icons = []; let icons = [];
(data || []).forEach((item = { path: '/' }) => { data.forEach((item = { path: '/' }) => {
if (item.icon) { if (item.icon) {
const { icon } = item; const { icon } = item;
if (icon && icon.type === 'icon') { if (icon.type === 'icon') {
icons.push(icon.name); icons.push(icon.name);
} }
} }

View File

@ -1,13 +1,14 @@
<template> <template>
<a-layout <a-layout
v-if="routeHasLayout" v-if="routeLayout"
:class="[ :class="[
collapsed ? 'main-layout-collapsed' : '', collapsed ? 'main-layout-collapsed' : '',
`main-layout-navigation-${navigation}` `main-layout-navigation-${navigation}`,
`main-layout-theme-${siderTheme}`
]" ]"
class="main-layout" class="main-layout"
> >
<template v-if="navigation !== 'top'"> <template v-if="navigation !== 'top' && routeLayout.side">
<div v-if="fixedSideBar" :style="siderFixedStuffStyle" class="layout-sider-fixed-stuff"></div> <div v-if="fixedSideBar" :style="siderFixedStuffStyle" class="layout-sider-fixed-stuff"></div>
<a-layout-sider <a-layout-sider
v-model:collapsed="collapsed" v-model:collapsed="collapsed"
@ -19,7 +20,7 @@
:theme="siderTheme" :theme="siderTheme"
collapsible collapsible
> >
<div v-if="navigation !== 'mixin'" class="layout-logo"> <div v-if="navigation !== 'mixin' && routeLayout.logo" class="layout-logo">
<img :src="logo" class="logo-img" /> <img :src="logo" class="logo-img" />
<h1 class="logo-name">{{title}}</h1> <h1 class="logo-name">{{title}}</h1>
</div> </div>
@ -27,19 +28,20 @@
</a-layout-sider> </a-layout-sider>
</template> </template>
<a-layout class="child-layout"> <a-layout class="child-layout">
<a-layout-header v-if="currentFixedHeader" class="layout-header"> <a-layout-header v-if="currentFixedHeader && routeLayout.top" class="layout-header">
</a-layout-header> </a-layout-header>
<a-layout-header <a-layout-header
v-if="routeLayout.top"
:style="headerFixedStyle" :style="headerFixedStyle"
:class="[currentFixedHeader ? 'layout-header-fixed' : '']" :class="[currentFixedHeader ? 'layout-header-fixed' : '']"
class="layout-header" class="layout-header"
> >
<div v-if="navigation === 'mixin'" class="layout-logo"> <div v-if="navigation === 'mixin' && routeLayout.logo" class="layout-logo">
<img :src="logo" class="logo-img" /> <img :src="logo" class="logo-img" />
<h1 class="logo-name">{{title}}</h1> <h1 class="logo-name">{{title}}</h1>
</div> </div>
<template v-if="navigation === 'top'"> <template v-if="navigation === 'top'">
<div 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> <h1 class="logo-name">{{title}}</h1>
</div> </div>
@ -134,9 +136,32 @@ export default {
setup(props) { setup(props) {
const collapsed = ref(false); const collapsed = ref(false);
const route = useRoute(); const route = useRoute();
const routeHasLayout = computed(() => { const routeLayoutDefault = {
const _routeLayout = route.meta.layout; side: true,
return _routeLayout === undefined ? true : _routeLayout; top: true,
logo: true
};
const routeLayout = computed(() => {
let config;
// meta layout true
const metaLayoutConfig = route.meta.layout === undefined ? true : route.meta.layout;
if (typeof metaLayoutConfig === 'boolean') {
config = metaLayoutConfig ? routeLayoutDefault : false;
} else if (typeof metaLayoutConfig === 'object') {
config = { ...routeLayoutDefault, ...metaLayoutConfig };
} else {
console.error('[plugin-layout]: meta layout must be object or boolean');
}
// query layout false
const routeQueryLayoutConfig = route.query.layout && JSON.parse(route.query.layout);
if (typeof routeQueryLayoutConfig === 'boolean') {
config = routeQueryLayoutConfig ? routeLayoutDefault : false;
} else if (typeof routeQueryLayoutConfig === 'object') {
config = { ...config, ...routeQueryLayoutConfig };
} else if (routeQueryLayoutConfig !== undefined) {
console.error('[plugin-layout]: query layout must be object or boolean');
}
return config;
}); });
const siderTheme = computed(() => { const siderTheme = computed(() => {
if (props.navigation === 'mixin') { if (props.navigation === 'mixin') {
@ -174,7 +199,7 @@ export default {
siderTheme, siderTheme,
currentFixedHeader, currentFixedHeader,
route, route,
routeHasLayout, routeLayout,
collapsed, collapsed,
siderFixedStuffStyle, siderFixedStuffStyle,
headerFixedStyle headerFixedStyle
@ -334,5 +359,21 @@ export default {
.layout-footer { .layout-footer {
text-align: center; text-align: center;
} }
&.main-layout-theme-light{
.logo-name{
color: rgba(0, 0, 0, 0.65) !important;
}
&.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,6 +1,6 @@
{ {
"name": "@fesjs/plugin-locale", "name": "@fesjs/plugin-locale",
"version": "2.0.0-rc.8", "version": "2.0.0",
"description": "@fesjs/plugin-locale", "description": "@fesjs/plugin-locale",
"main": "lib/index.js", "main": "lib/index.js",
"files": [ "files": [
@ -32,7 +32,7 @@
}, },
"peerDependencies": { "peerDependencies": {
"@ant-design/icons-vue": "^5.1.6", "@ant-design/icons-vue": "^5.1.6",
"@fesjs/fes": "^2.0.0-rc.0", "@fesjs/fes": "^2.0.0",
"ant-design-vue": "2.0.0", "ant-design-vue": "2.0.0",
"vue": "^3.0.5" "vue": "^3.0.5"
} }

View File

@ -1,6 +1,6 @@
{ {
"name": "@fesjs/plugin-model", "name": "@fesjs/plugin-model",
"version": "2.0.0-rc.8", "version": "2.0.0",
"description": "@fesjs/plugin-model", "description": "@fesjs/plugin-model",
"main": "lib/index.js", "main": "lib/index.js",
"files": [ "files": [
@ -30,7 +30,7 @@
"@umijs/utils": "3.3.3" "@umijs/utils": "3.3.3"
}, },
"peerDependencies": { "peerDependencies": {
"@fesjs/fes": "^2.0.0-rc.0", "@fesjs/fes": "^2.0.0",
"vue": "^3.0.5" "vue": "^3.0.5"
} }
} }

View File

@ -43,10 +43,10 @@
"access": "public" "access": "public"
}, },
"devDependencies": { "devDependencies": {
"@webank/eslint-config-webank": "0.2.10" "@webank/eslint-config-webank": "0.3.0"
}, },
"dependencies": { "dependencies": {
"@fesjs/fes": "^2.0.0-rc.0", "@fesjs/fes": "^2.0.0",
"vue": "^3.0.5", "vue": "^3.0.5",
"ant-design-vue": "2.0.0" "ant-design-vue": "2.0.0"
}, },

View File

@ -10,3 +10,22 @@ export const beforeRender = {
}); });
} }
}; };
export const qiankun = {
// 应用加载之前
async bootstrap(props) {
console.log('app1 bootstrap', props);
},
// 应用 render 之前触发
async mount(props) {
console.log('app1 mount', props);
},
// 当 props 更新时触发
async update(props) {
console.log('app1 update', props);
},
// 应用卸载之后触发
async unmount(props) {
console.log('app1 unmount', props);
}
};

View File

@ -5,8 +5,8 @@
</template> </template>
<config> <config>
{ {
"name": "index", "name": "test",
"title": "home" "title": "test"
} }
</config> </config>
<script> <script>

View File

@ -43,10 +43,10 @@
"access": "public" "access": "public"
}, },
"devDependencies": { "devDependencies": {
"@webank/eslint-config-webank": "0.2.10" "@webank/eslint-config-webank": "0.3.0"
}, },
"dependencies": { "dependencies": {
"@fesjs/fes": "^2.0.0-rc.0", "@fesjs/fes": "^2.0.0",
"vue": "^3.0.5", "vue": "^3.0.5",
"ant-design-vue": "2.0.0" "ant-design-vue": "2.0.0"
}, },

View File

@ -1,5 +1,7 @@
import { access as accessApi } from '@fesjs/fes'; import { access as accessApi } from '@fesjs/fes';
import PageLoading from '@/components/PageLoading'; import PageLoading from '@/components/PageLoading';
import Antdv from 'ant-design-vue';
import 'ant-design-vue/dist/antd.css';
export const beforeRender = { export const beforeRender = {
loading: <PageLoading />, loading: <PageLoading />,
@ -13,3 +15,7 @@ export const beforeRender = {
}); });
} }
}; };
export const onAppCreated = ({ app }) => {
app.use(Antdv);
};

View File

@ -1,8 +1,12 @@
<template> <template>
<div> <div>
main main
<a-tabs v-model:activeKey="activeKey">
<a-tab-pane key="1" tab="Tab 1"><MicroAppWithMemoHistory key="1" name="app1" url="/app1" /></a-tab-pane>
<a-tab-pane key="2" tab="Tab 2"><MicroAppWithMemoHistory key="2" name="app1" url="/app1/test" /></a-tab-pane>
<a-tab-pane key="3" tab="Tab 3">Content of Tab Pane 3</a-tab-pane>
</a-tabs>
</div> </div>
<MicroAppWithMemoHistory name="app1" :url="url" />
</template> </template>
<config> <config>
{ {
@ -24,6 +28,7 @@ export default {
}, 3000); }, 3000);
}); });
return { return {
activeKey: ref('1'),
url url
}; };
} }

View File

@ -1,6 +1,6 @@
{ {
"name": "@fesjs/plugin-qiankun", "name": "@fesjs/plugin-qiankun",
"version": "2.0.0-rc.17", "version": "2.0.0",
"description": "@fesjs/plugin-qiankun", "description": "@fesjs/plugin-qiankun",
"main": "lib/index.js", "main": "lib/index.js",
"files": [ "files": [
@ -38,7 +38,7 @@
"npm-run-all": "^4.1.5" "npm-run-all": "^4.1.5"
}, },
"peerDependencies": { "peerDependencies": {
"@webank/fes": "^2.0.0-rc.0", "@webank/fes": "^2.0.0",
"vue": "^3.0.5" "vue": "^3.0.5"
} }
} }

View File

@ -45,7 +45,7 @@ function modifyRoutesWithAttachMode({
export default function modifyRoutes({ api, namespace }) { export default function modifyRoutes({ api, namespace }) {
api.modifyRoutes((routes) => { api.modifyRoutes((routes) => {
const { router, base } = api.config; const { router, base } = api.config;
const masterHistoryType = (router && router?.mode) || defaultHistoryType; const masterHistoryType = (router && router.mode) || defaultHistoryType;
modifyRoutesWithAttachMode({ modifyRoutesWithAttachMode({
routes, routes,

View File

@ -165,7 +165,7 @@ export default function (api) {
api.addEntryCode( api.addEntryCode(
() => ` () => `
export const bootstrap = qiankun_genBootstrap(completeClientRender, app); export const bootstrap = qiankun_genBootstrap(clientRender, app);
export const mount = qiankun_genMount('#${api.config.mountElementId}'); export const mount = qiankun_genMount('#${api.config.mountElementId}');
export const unmount = qiankun_genUnmount(); export const unmount = qiankun_genUnmount();
export const update = qiankun_genUpdate(); export const update = qiankun_genUpdate();

View File

@ -38,10 +38,12 @@ function getSlaveRuntime() {
// 子应用生命周期钩子Bootstrap // 子应用生命周期钩子Bootstrap
export function genBootstrap(oldRender, appPromise) { export function genBootstrap(oldRender, appPromise) {
return async (props) => { return async (props) => {
if (typeof props !== 'undefined') {
const slaveRuntime = getSlaveRuntime(); const slaveRuntime = getSlaveRuntime();
if (slaveRuntime.bootstrap) { if (slaveRuntime.bootstrap) {
await slaveRuntime.bootstrap(props); await slaveRuntime.bootstrap(props);
} }
}
render = oldRender; render = oldRender;
if (isPromise(appPromise)) { if (isPromise(appPromise)) {
cacheAppPromise = appPromise; cacheAppPromise = appPromise;
@ -61,8 +63,6 @@ export function genMount(mountElementId) {
if (slaveRuntime.mount) { if (slaveRuntime.mount) {
await slaveRuntime.mount(props); await slaveRuntime.mount(props);
} }
}
// 更新 clientRender 配置 // 更新 clientRender 配置
const clientRenderOpts = { const clientRenderOpts = {
// 支持通过 props 注入 container 来限定子应用 mountElementId 的查找范围 // 支持通过 props 注入 container 来限定子应用 mountElementId 的查找范围
@ -79,6 +79,7 @@ export function genMount(mountElementId) {
if(props.onRouterInit){ if(props.onRouterInit){
history.onRouterInit = props.onRouterInit; history.onRouterInit = props.onRouterInit;
} }
}
// 第一次 mount 会自动触发 render非第一次 mount 则需手动触发 // 第一次 mount 会自动触发 render非第一次 mount 则需手动触发
if (hasMountedAtLeastOnce) { if (hasMountedAtLeastOnce) {

View File

@ -1,4 +1,4 @@
import { createMemoryHistory } from '@@/core/coreExports'; import { createMemoryHistory, getHistory } from '@@/core/coreExports';
import qiankunRender, { clientRenderOptsStack, history } from './lifecycles'; import qiankunRender, { clientRenderOptsStack, history } from './lifecycles';
@ -14,16 +14,18 @@ export function modifyClientRenderOpts(memo) {
}; };
} }
export function modifyHistroy(memo) { export function modifyCreateHistroy(memo) {
if (history.url) { if (history.url) {
const memoHistroy = createMemoryHistory(); return createMemoryHistory
memoHistroy.push(history.url)
return memoHistroy
} }
return memo; return memo;
} }
export function onRouterCreated({ router }) { export function onRouterCreated({ router }) {
if(history.url) {
const memoryHistory = getHistory();
memoryHistory.push(history.url)
}
if(history.onRouterInit){ if(history.onRouterInit){
history.onRouterInit(router) history.onRouterInit(router)
} }

View File

@ -1,6 +1,6 @@
{ {
"name": "@fesjs/plugin-request", "name": "@fesjs/plugin-request",
"version": "2.0.0-rc.16", "version": "2.0.0",
"description": "@fesjs/plugin-request", "description": "@fesjs/plugin-request",
"main": "lib/index.js", "main": "lib/index.js",
"files": [ "files": [
@ -27,10 +27,11 @@
"access": "public" "access": "public"
}, },
"peerDependencies": { "peerDependencies": {
"@fesjs/fes": "^2.0.0-rc.0", "@fesjs/fes": "^2.0.0",
"vue": "^3.0.5" "vue": "^3.0.5"
}, },
"dependencies": { "dependencies": {
"@fesjs/compiler": "^2.0.0",
"axios": "0.21.1" "axios": "0.21.1"
} }
} }

View File

@ -1,6 +1,9 @@
import { Logger } from '@fesjs/compiler';
import { readFileSync } from 'fs'; import { readFileSync } from 'fs';
import { join } from 'path'; import { join } from 'path';
const logger = new Logger('fes:plugin-request');
export default (api) => { export default (api) => {
api.addRuntimePluginKey(() => 'request'); api.addRuntimePluginKey(() => 'request');
// 配置 // 配置
@ -31,6 +34,11 @@ export default (api) => {
api.onGenerateFiles(() => { api.onGenerateFiles(() => {
// 文件写出 // 文件写出
const { dataField = '', base = '' } = api.config.request; const { dataField = '', base = '' } = api.config.request;
if (base) {
// DEPRECATED
logger.warn('[DEPRECATED]: reqeust base 即将废弃,建议使用 axios baseURL代替https://github.com/axios/axios');
}
api.writeTmpFile({ api.writeTmpFile({
path: absoluteFilePath, path: absoluteFilePath,
content: requestTemplate content: requestTemplate

View File

@ -46,7 +46,7 @@ const CACHE_TYPE = {
const CACHE_DATA_MAP = new Map(); const CACHE_DATA_MAP = new Map();
function genInnerKey(key, cacheType) { function genInnerKey(key, cacheType = 'ram') {
if (cacheType !== CACHE_TYPE.ram) { if (cacheType !== CACHE_TYPE.ram) {
return `${CACHE_KEY_PREFIX}${key}`; return `${CACHE_KEY_PREFIX}${key}`;
} }
@ -121,6 +121,57 @@ function getCacheData({ key, cacheType = 'ram' }) {
} }
} }
// 存储缓存队列
const cacheStartFlag = new Map();
const cachingQueue = new Map();
/**
* 等上一次请求结果
* 1. 如果上一次请求成功直接使用上一次的请求结果
* 2. 如果上一次请求失败重启本次请求
*/
function handleCachingStart(ctx, config) {
const _key = genInnerKey(ctx.key, config.cache.cacheType);
const caching = cacheStartFlag.get(_key);
if (caching) {
return new Promise((resolve) => {
const queue = cachingQueue.get(_key) || [];
cachingQueue.set(_key, queue.concat(resolve));
});
}
cacheStartFlag.set(_key, true);
}
// 有请求成功的
function handleCachingQueueSuccess(ctx, config) {
// 移除首次缓存 flag
const _key = genInnerKey(ctx.key, config.cache.cacheType);
const queue = cachingQueue.get(_key);
if (queue && queue.length > 0) {
queue.forEach((resolve) => {
resolve({
response: ctx.response
});
});
}
cachingQueue.delete(_key);
cacheStartFlag.delete(_key);
}
// 处理请求失败
function handleCachingQueueError(ctx, config) {
const _key = genInnerKey(ctx.key, config.cache.cacheType);
const queue = cachingQueue.get(_key);
if (queue && queue.length > 0) {
const firstResolve = queue.shift();
firstResolve();
cachingQueue.set(_key, queue);
} else {
cachingQueue.delete(_key);
cacheStartFlag.delete(_key);
}
}
export default async (ctx, next) => { export default async (ctx, next) => {
const { config } = ctx; const { config } = ctx;
if (config.cache) { if (config.cache) {
@ -131,17 +182,28 @@ export default async (ctx, next) => {
}; };
return; return;
} }
const result = await handleCachingStart(ctx, config);
if (result) {
Object.keys(result).forEach((key) => {
ctx[key] = result[key];
});
return;
}
} }
await next(); await next();
if (config.cache) { if (config.cache) {
const requestdata = checkHttpRequestHasBody(config.method) ? config.data : config.params; const requestdata = checkHttpRequestHasBody(config.method) ? config.data : config.params;
if (ctx.response && canCache(requestdata) && canCache(ctx.response.data)) { if (!ctx.error && ctx.response && canCache(requestdata) && canCache(ctx.response.data)) {
handleCachingQueueSuccess(ctx, config);
setCacheData({ setCacheData({
key: ctx.key, key: ctx.key,
data: ctx.response.data, data: ctx.response.data,
...config.cache ...config.cache
}); });
} else {
handleCachingQueueError(ctx, config);
} }
} }
}; };

View File

@ -1,17 +1,63 @@
const requestMap = new Map(); const requestMap = new Map();
const mergeRequestMap = new Map();
const requestQueue = new Map();
function handleCachingStart(ctx) {
const isRequesting = mergeRequestMap.get(ctx.key);
if (isRequesting) {
return new Promise((resolve) => {
const queue = requestQueue.get(ctx.key) || [];
requestQueue.set(ctx.key, queue.concat(resolve));
});
}
mergeRequestMap.set(ctx.key, true);
}
function handleRepeatRequest(ctx) {
const queue = requestQueue.get(ctx.key);
if (queue && queue.length > 0) {
queue.forEach((resolve) => {
if (ctx.error) {
resolve({
error: ctx.error
});
} else {
resolve({
response: ctx.response
});
}
});
}
requestQueue.delete(ctx.key);
mergeRequestMap.delete(ctx.key);
}
export default async (ctx, next) => { export default async (ctx, next) => {
const key = ctx.key; if (ctx.config.mergeRequest) {
if (requestMap.get(key)) { const result = await handleCachingStart(ctx);
if (result) {
Object.keys(result).forEach((key) => {
ctx[key] = result[key];
});
return;
}
} else {
if (requestMap.get(ctx.key) && !ctx.config.mergeRequest) {
ctx.error = { ctx.error = {
type: 'REPEAT', type: 'REPEAT',
msg: '重复请求' msg: '重复请求'
}; };
return; return;
} }
requestMap.set(key, true); requestMap.set(ctx.key, true);
}
await next(); await next();
requestMap.delete(key); if (ctx.config.mergeRequest) {
handleRepeatRequest(ctx);
} else {
requestMap.delete(ctx.key);
}
}; };

View File

@ -65,11 +65,12 @@ function getRequestInstance() {
addRequestInterceptors(instance, requestInterceptors); addRequestInterceptors(instance, requestInterceptors);
addResponseInterceptors(instance, responseInterceptors); addResponseInterceptors(instance, responseInterceptors);
// 洋葱模型内部应该这是对数据的处理,避免有副作用调用
scheduler.use(paramsProcess) scheduler.use(paramsProcess)
.use(genRequestKey) .use(genRequestKey)
.use(cacheControl)
.use(preventRepeatReq) .use(preventRepeatReq)
.use(throttle) .use(throttle)
.use(cacheControl)
.use(axiosMiddleware) .use(axiosMiddleware)
.use(resDataAdaptor) .use(resDataAdaptor)
.use(resErrorProcess) .use(resErrorProcess)
@ -87,7 +88,7 @@ function getRequestInstance() {
}; };
} }
// FEATURE 后续优化,使用 axios baseURL // DEPRECATED 废弃,使用 axios baseURL
function handleApiPathBase(url, options = {}) { function handleApiPathBase(url, options = {}) {
if (url.startsWith('http')) return url; if (url.startsWith('http')) return url;
@ -120,6 +121,52 @@ function createContext(userConfig) {
}; };
} }
function getResponseCode(response) {
if (response) {
if (response._rawData) return response._rawData.code;
if (response.data) return response.data.code;
}
return null;
}
function skipErrorHandlerToObj(skipErrorHandler = []) {
if (!Array.isArray(skipErrorHandler)) {
skipErrorHandler = [skipErrorHandler];
}
return skipErrorHandler.reduce((acc, cur) => {
acc[cur] = true;
return acc;
}, {});
}
function handleRequestError({
errorHandler = {},
error,
response,
config
}) {
// 跳过所有错误类型处理
if (config.skipErrorHandler === true) return;
const skipObj = skipErrorHandlerToObj(config.skipErrorHandler);
const resCode = getResponseCode(response);
let errorKey = 'default';
if (resCode && errorHandler[resCode]) {
errorKey = resCode;
} else if (error.type && errorHandler[error.type]) {
errorKey = error.type;
} else if (error.response && errorHandler[error.response.status]) {
errorKey = error.response.status;
}
if (!skipObj[errorKey] && errorHandler[errorKey]) {
return errorHandler[errorKey](error);
}
}
export const request = (url, data, options = {}) => { export const request = (url, data, options = {}) => {
if (typeof options === 'string') { if (typeof options === 'string') {
options = { options = {
@ -132,10 +179,11 @@ export const request = (url, data, options = {}) => {
const userConfig = userConfigHandler(url, data, options); const userConfig = userConfigHandler(url, data, options);
const context = createContext(userConfig); const context = createContext(userConfig);
return currentRequestInstance.request(context).then(() => { return currentRequestInstance.request(context).then(async () => {
if (!context.error) { if (!context.error) {
return context.config.useResonse ? context.response : context.response.data; return context.config.useResonse ? context.response : context.response.data;
} }
await handleRequestError(context);
return Promise.reject(context.error); return Promise.reject(context.error);
}); });
}; };

View File

@ -1,40 +1,16 @@
import { isObject } from './helpers'; import { isObject } from './helpers';
function handleAbnormalCode(errorHandler = {}, code, response) { // 错误处理等副作用网上提
if (errorHandler[code]) {
errorHandler[code](response.data);
} else if (errorHandler.default) {
// 处理其他异常
errorHandler.default({
response
});
}
}
function handleRequestError(errorHandler = {}, error) {
if (error.type) {
errorHandler[error.type] && errorHandler[error.type](error);
} else if (error.response) {
errorHandler[error.response.status] && errorHandler[error.response.status](error);
} else if (errorHandler.default) {
errorHandler.default(error);
}
}
export default async (ctx, next) => { export default async (ctx, next) => {
const { const {
error, response,
errorHandler = {}, config
response
} = ctx; } = ctx;
if (response && isObject(response.data)) { if (!config.closeResDataCheck && response && isObject(response.data)) {
const code = response.data.code; const code = response.data.code;
if (code !== '0') { if (code !== '0') {
handleAbnormalCode(errorHandler, code, response);
ctx.error = response; // code 不为零进入 reject ctx.error = response; // code 不为零进入 reject
} }
} else if (error) {
handleRequestError(errorHandler, error);
} }
await next(); await next();

View File

@ -1,6 +1,6 @@
{ {
"name": "@fesjs/plugin-sass", "name": "@fesjs/plugin-sass",
"version": "2.0.0-rc.18", "version": "2.0.0",
"description": "@fesjs/plugin-sass", "description": "@fesjs/plugin-sass",
"main": "lib/index.js", "main": "lib/index.js",
"files": [ "files": [
@ -31,6 +31,6 @@
"sass-loader": "^11.0.1" "sass-loader": "^11.0.1"
}, },
"peerDependencies": { "peerDependencies": {
"@fesjs/fes": "^2.0.0-rc.0" "@fesjs/fes": "^2.0.0"
} }
} }

View File

@ -1,6 +1,6 @@
{ {
"name": "@fesjs/plugin-vuex", "name": "@fesjs/plugin-vuex",
"version": "2.0.0-rc.16", "version": "2.0.0",
"description": "@fesjs/plugin-vuex", "description": "@fesjs/plugin-vuex",
"main": "lib/index.js", "main": "lib/index.js",
"files": [ "files": [
@ -30,7 +30,7 @@
"@umijs/utils": "3.3.3" "@umijs/utils": "3.3.3"
}, },
"peerDependencies": { "peerDependencies": {
"@fesjs/fes": "^2.0.0-rc.0", "@fesjs/fes": "^2.0.0",
"vue": "^3.0.5", "vue": "^3.0.5",
"vuex": "^4.0.0" "vuex": "^4.0.0"
} }

View File

@ -1,6 +1,6 @@
{ {
"name": "@fesjs/preset-built-in", "name": "@fesjs/preset-built-in",
"version": "2.0.0-rc.18", "version": "2.0.1",
"description": "@fesjs/preset-built-in", "description": "@fesjs/preset-built-in",
"main": "lib/index.js", "main": "lib/index.js",
"types": "lib/index.d.ts", "types": "lib/index.d.ts",
@ -32,7 +32,7 @@
"@babel/plugin-proposal-pipeline-operator": "^7.12.13", "@babel/plugin-proposal-pipeline-operator": "^7.12.13",
"@babel/plugin-transform-runtime": "^7.12.13", "@babel/plugin-transform-runtime": "^7.12.13",
"@babel/preset-env": "^7.12.13", "@babel/preset-env": "^7.12.13",
"@fesjs/compiler": "^2.0.0-rc.5", "@fesjs/compiler": "^2.0.0",
"@soda/friendly-errors-webpack-plugin": "^1.8.0", "@soda/friendly-errors-webpack-plugin": "^1.8.0",
"@umijs/utils": "3.3.3", "@umijs/utils": "3.3.3",
"@vue/babel-plugin-jsx": "^1.0.2", "@vue/babel-plugin-jsx": "^1.0.2",
@ -48,7 +48,7 @@
"copy-webpack-plugin": "^7.0.0", "copy-webpack-plugin": "^7.0.0",
"core-js": "^3.8.3", "core-js": "^3.8.3",
"css-loader": "^5.0.1", "css-loader": "^5.0.1",
"css-minimizer-webpack-plugin": "^1.2.0", "css-minimizer-webpack-plugin": "^3.0.0",
"deepmerge": "^4.2.2", "deepmerge": "^4.2.2",
"envinfo": "^7.7.3", "envinfo": "^7.7.3",
"file-loader": "^6.2.0", "file-loader": "^6.2.0",
@ -58,7 +58,7 @@
"less-loader": "^8.0.0", "less-loader": "^8.0.0",
"mini-css-extract-plugin": "^1.3.5", "mini-css-extract-plugin": "^1.3.5",
"mockjs": "^1.1.0", "mockjs": "^1.1.0",
"postcss": "^8.2.4", "postcss": "8.3.0",
"postcss-flexbugs-fixes": "^5.0.2", "postcss-flexbugs-fixes": "^5.0.2",
"postcss-loader": "^4.2.0", "postcss-loader": "^4.2.0",
"postcss-safe-parser": "^5.0.2", "postcss-safe-parser": "^5.0.2",

View File

@ -39,7 +39,7 @@ export default function (api) {
try { try {
// clear output path before exec build // clear output path before exec build
if (process.env.CLEAR_OUTPUT !== 'none') { if (process.env.CLEAR_OUTPUT !== 'none') {
if (paths.absOutputPath && existsSync(paths.absOutputPath || '')) { if (paths.absOutputPath && existsSync(paths.absOutputPath)) {
logger.debug(`Clear OutputPath: ${paths.absNodeModulesPath}`); logger.debug(`Clear OutputPath: ${paths.absNodeModulesPath}`);
rimraf.sync(paths.absOutputPath); rimraf.sync(paths.absOutputPath);
} }

View File

@ -21,8 +21,7 @@ function createRules({
options, options,
browserslist browserslist
}) { }) {
const rule = webpackConfig.module.rule(lang).test(test); function applyLoaders(rule, isCSSModules) {
if (isDev) { if (isDev) {
rule.use('extra-css-loader') rule.use('extra-css-loader')
.loader(require.resolve('style-loader')) .loader(require.resolve('style-loader'))
@ -37,9 +36,22 @@ function createRules({
rule.use('css-loader') rule.use('css-loader')
.loader(require.resolve('css-loader')) .loader(require.resolve('css-loader'))
.options({ .options(
...config.cssLoader deepmerge(
}); {
importLoaders: 1,
// https://webpack.js.org/loaders/css-loader/#onlylocals
...(isCSSModules
? {
modules: {
localIdentName: '[local]___[hash:base64:5]'
}
}
: {})
},
config.cssLoader || {}
)
);
rule.use('postcss-loader') rule.use('postcss-loader')
.loader(require.resolve('postcss-loader')) .loader(require.resolve('postcss-loader'))
@ -60,6 +72,11 @@ function createRules({
.loader(require.resolve(loader)) .loader(require.resolve(loader))
.options(options); .options(options);
} }
}
const rule = webpackConfig.module.rule(lang).test(test);
applyLoaders(rule.oneOf('css-modules').resourceQuery(/module/), true);
applyLoaders(rule.oneOf('css'), false);
} }
export default function createCssWebpackConfig({ export default function createCssWebpackConfig({
@ -102,9 +119,7 @@ export default function createCssWebpackConfig({
if (!isDev) { if (!isDev) {
webpackConfig.optimization webpackConfig.optimization
.minimizer('css') .minimizer('css')
.use(require.resolve('css-minimizer-webpack-plugin'), [{ .use(require.resolve('css-minimizer-webpack-plugin'), [{}]);
sourceMap: config.devtool !== false
}]);
} }
return (options) => { return (options) => {

View File

@ -28,7 +28,7 @@ export default function (api) {
// 修改路由 // 修改路由
'patchRoutes', 'patchRoutes',
// 修改histror // 修改histror
'modifyHistroy', 'modifyCreateHistroy',
// 生成router时触发 // 生成router时触发
'onRouterCreated' 'onRouterCreated'
] ]

View File

@ -31,7 +31,7 @@ const renderClient = (opts = {}) => {
plugin.applyPlugins({ plugin.applyPlugins({
key: 'onAppCreated', key: 'onAppCreated',
type: ApplyPluginsType.event, type: ApplyPluginsType.event,
args: { app }, args: { app, routes },
}); });
if (rootElement) { if (rootElement) {
@ -40,29 +40,7 @@ const renderClient = (opts = {}) => {
return app; return app;
} }
const getClientRender = (args = {}) => plugin.applyPlugins({ const beforeRender = async ({rootElement}) => {
key: 'render',
type: ApplyPluginsType.compose,
initialValue: () => {
const opts = plugin.applyPlugins({
key: 'modifyClientRenderOpts',
type: ApplyPluginsType.modify,
initialValue: {
initialState: args.initialState,
routes: args.routes || getRoutes(),
plugin,
rootElement: '{{{ rootElement }}}',
{{#enableTitle}}
defaultTitle: `{{{ defaultTitle }}}`,
{{/enableTitle}}
},
});
return renderClient(opts);
},
args,
});
const beforeRender = async () => {
const beforeRenderConfig = plugin.applyPlugins({ const beforeRenderConfig = plugin.applyPlugins({
key: "beforeRender", key: "beforeRender",
type: ApplyPluginsType.modify, type: ApplyPluginsType.modify,
@ -74,7 +52,7 @@ const beforeRender = async () => {
let initialState = {}; let initialState = {};
if (typeof beforeRenderConfig.action === "function") { if (typeof beforeRenderConfig.action === "function") {
const app = createApp(beforeRenderConfig.loading); const app = createApp(beforeRenderConfig.loading);
app.mount('{{{ rootElement }}}'); app.mount(rootElement);
try { try {
initialState = await beforeRenderConfig.action(); initialState = await beforeRenderConfig.action();
} catch(e){ } catch(e){
@ -86,14 +64,31 @@ const beforeRender = async () => {
return initialState; return initialState;
}; };
const completeClientRender = async () => { const getClientRender = (args = {}) => plugin.applyPlugins({
const initialState = await beforeRender(); key: 'render',
const clientRender = getClientRender({initialState}); type: ApplyPluginsType.compose,
const app = clientRender(); initialValue: async () => {
return app; const opts = plugin.applyPlugins({
}; key: 'modifyClientRenderOpts',
type: ApplyPluginsType.modify,
initialValue: {
routes: args.routes || getRoutes(),
plugin,
rootElement: '{{{ rootElement }}}',
{{#enableTitle}}
defaultTitle: `{{{ defaultTitle }}}`,
{{/enableTitle}}
},
});
const initialState = await beforeRender(opts);
return renderClient({...opts, initialState});
},
args,
});
const app = completeClientRender(); const clientRender = getClientRender();
const app = clientRender();
{{{ entryCode }}} {{{ entryCode }}}

View File

@ -94,24 +94,21 @@ const genRoutes = function (parentRoutes, path, parentRoutePath, config) {
// 路由名称 // 路由名称
const routeName = getRouteName(parentRoutePath, fileName); const routeName = getRouteName(parentRoutePath, fileName);
const componentPath = getComponentPath(parentRoutePath, fileName, config); const componentPath = getComponentPath(parentRoutePath, fileName, config);
const routeMeta = routeMetaBlock?.content ? JSON.parse(routeMetaBlock.content) : {};
const routeConfig = {
path: routePath,
component: componentPath,
name: routeMeta.name || routeName,
meta: routeMeta
};
if (hasLayout) { if (hasLayout) {
if (fileName === 'layout') { if (fileName === 'layout') {
layoutRoute.component = componentPath; layoutRoute.component = componentPath;
} else { } else {
layoutRoute.children.push({ layoutRoute.children.push(routeConfig);
path: routePath,
component: componentPath,
name: routeName,
meta: routeMetaBlock?.content ? JSON.parse(routeMetaBlock.content) : {}
});
} }
} else { } else {
parentRoutes.push({ parentRoutes.push(routeConfig);
path: routePath,
component: componentPath,
name: routeName,
meta: routeMetaBlock?.content ? JSON.parse(routeMetaBlock.content) : {}
});
} }
} }
}); });
@ -166,7 +163,7 @@ const rank = function (routes) {
rank(item.children); rank(item.children);
} }
}); });
routes = routes.sort((a, b) => b.count - a.count); routes.sort((a, b) => b.count - a.count);
}; };
const getRoutes = function ({ config, absPagesPath }) { const getRoutes = function ({ config, absPagesPath }) {

View File

@ -3,31 +3,34 @@ import { plugin } from '@@/core/coreExports';
export function getRoutes() { export function getRoutes() {
const routes = {{{ routes }}}; const routes = {{{ routes }}};
plugin.applyPlugins({
key: 'patchRoutes',
type: ApplyPluginsType.event,
args: { routes },
});
return routes; return routes;
} }
const ROUTER_BASE = '{{{ routerBase }}}'; const ROUTER_BASE = '{{{ routerBase }}}';
let router = null; let router = null;
let history = null; let history = null;
export const createRouter = () => { export const createRouter = (routes) => {
if (router) { if (router) {
return router; return router;
} }
history = plugin.applyPlugins({ const createHistory = plugin.applyPlugins({
key: 'modifyHistroy', key: 'modifyCreateHistroy',
type: ApplyPluginsType.modify, type: ApplyPluginsType.modify,
initialValue: {{{ CREATE_HISTORY }}}(ROUTER_BASE), args: {
base: ROUTER_BASE
},
initialValue: {{{ CREATE_HISTORY }}},
});
history = createHistory(ROUTER_BASE);
// 修改routes
plugin.applyPlugins({
key: 'patchRoutes',
type: ApplyPluginsType.event,
args: { routes },
}); });
router = createVueRouter({ router = createVueRouter({
history, history,
routes: getRoutes() routes
}); });
plugin.applyPlugins({ plugin.applyPlugins({

View File

@ -1,6 +1,6 @@
import { createRouter } from "./routes"; import { createRouter } from "./routes";
export function onAppCreated({ app }) { export function onAppCreated({ app, routes }) {
const router = createRouter(); const router = createRouter(routes);
app.use(router); app.use(router);
} }

View File

@ -1,6 +1,6 @@
{ {
"name": "@fesjs/runtime", "name": "@fesjs/runtime",
"version": "2.0.0-rc.5", "version": "2.0.0",
"description": "@fesjs/runtime", "description": "@fesjs/runtime",
"main": "dist/index.js", "main": "dist/index.js",
"files": [ "files": [

View File

@ -1,5 +1,5 @@
// fes.config.js 只负责管理 cli 相关的配置 // fes.config.js 只负责管理 cli 相关的配置
import pxtoviewport from 'postcss-px-to-viewport'; import pxtoviewport from '@ttou/postcss-px-to-viewport';
export default { export default {
@ -10,7 +10,7 @@ export default {
publicPath: '/', publicPath: '/',
request: { request: {
base: '/ras-mas', base: '/ras-mas',
dataField: '' dataField: 'result'
}, },
html: { html: {
title: '拉夫德鲁' title: '拉夫德鲁'

View File

@ -41,13 +41,14 @@
"access": "public" "access": "public"
}, },
"devDependencies": { "devDependencies": {
"@webank/eslint-config-webank": "0.2.10", "@webank/eslint-config-webank": "0.3.0",
"postcss-px-to-viewport": "1.1.1" "@ttou/postcss-px-to-viewport": "1.1.4"
}, },
"dependencies": { "dependencies": {
"@fesjs/fes": "^2.0.0-rc.0", "debounce": "1.2.1",
"@fesjs/plugin-icon": "^2.0.0-rc.0", "@fesjs/fes": "^2.0.0",
"@fesjs/plugin-request": "^2.0.0-rc.0", "@fesjs/plugin-icon": "^2.0.0",
"@fesjs/plugin-request": "^2.0.0",
"vue": "^3.0.5" "vue": "^3.0.5"
}, },
"private": true "private": true

View File

@ -1,13 +1,18 @@
export const request = { export const request = {
errorHandler: { errorHandler: {
111(responseData) { 111() {
console.log(responseData); console.log('root:111');
}, },
404() { 500() {
console.log('to 404 page'); console.log('500 error');
}, },
default(error) { default(error) {
console.log(error.response.data); console.log('default error');
console.log(error);
} }
} }
}; };
export function patchRoutes() {
console.log('patchRoutes');
}

View File

@ -0,0 +1,4 @@
import { request } from '@fesjs/fes';
import { debounce } from 'debounce';
export const debounceRequest = debounce(request, 300);

View File

@ -2,38 +2,96 @@
<div class="onepiece"> <div class="onepiece">
fes & 拉夫德鲁<br /> fes & 拉夫德鲁<br />
<fes-icon :spin="true" class="one-icon" type="smile" @click="clickIcon" /> <fes-icon :spin="true" class="one-icon" type="smile" @click="clickIcon" />
<div v-if="loading" class="loading">loading</div>
<div v-else class="data">{{data}}</div>
</div> </div>
</template> </template>
<config> <config>
{ {
"title": "首页", "title": "首页",
"name": "testIndex",
"layout": "false" "layout": "false"
} }
</config> </config>
<script> <script>
import { ref, onMounted } from 'vue'; import { ref } from 'vue';
import { useRouter, useRequest } from '@fesjs/fes'; import { request } from '@fesjs/fes';
export default { export default {
setup() { setup() {
const fes = ref('fes upgrade to vue3'); const fes = ref('fes upgrade to vue3');
const rotate = ref(90); const rotate = ref(90);
const router = useRouter();
onMounted(() => {
console.log(router);
console.log('mounted1!!');
});
const clickIcon = () => { const clickIcon = () => {
console.log('click Icon'); console.log('click Icon');
}; };
const { loading, data } = useRequest('/api', null, { // request('/api', null, {
dataField: false // mergeRequest: true
// }).then((res) => {
// console.log(res);
// });
// request('/api', null, {
// mergeRequest: true
// }).then((res) => {
// console.log(res);
// });
// request('/api', null, {
// mergeRequest: true
// }).then((res) => {
// console.log(res);
// });
// request('/api', null, {
// throttle: 3000,
// cache: true
// }).then((res) => {
// console.log(res);
// });
// setTimeout(() => {
// request('/api', null, {
// throttle: 3000,
// cache: true
// }).then((res) => {
// console.log(res);
// });
// }, 1000);
// setTimeout(() => {
// request('/api', null, {
// throttle: 3000,
// cache: true
// }).then((res) => {
// console.log(res);
// });
// request('/api', null, {
// throttle: 3000,
// cache: true
// }).then((res) => {
// console.log(res);
// });
// }, 3200);
request('/api', null, {
cache: true
}).then((res) => {
console.log(res);
}); });
request('/api', null, {
cache: true
}).then((res) => {
console.log(res);
});
request('/api', null, {
cache: true
}).then((res) => {
console.log(res);
});
// request('/api', null, {
// // skipErrorHandler: [500]
// }).then((res) => {
// console.log(res);
// }).catch((err) => {
// console.log('inner error', err);
// });
return { return {
loading,
data,
fes, fes,
rotate, rotate,
clickIcon clickIcon

View File

@ -35,6 +35,7 @@ export default {
footer: "Created by MumbleFe", footer: "Created by MumbleFe",
multiTabs: false, multiTabs: false,
navigation: "mixin", navigation: "mixin",
theme: 'light',
menus: [ menus: [
{ {
name: "index", name: "index",
@ -81,4 +82,7 @@ export default {
strict: true, strict: true,
}, },
dynamicImport: true, dynamicImport: true,
extraBabelPlugins: [
['import', { libraryName: 'ant-design-vue', libraryDirectory: 'es', style: 'css' }, 'ant-design-vue'],
]
}; };

View File

@ -43,20 +43,20 @@
"access": "public" "access": "public"
}, },
"devDependencies": { "devDependencies": {
"@webank/eslint-config-webank": "0.2.10" "@webank/eslint-config-webank": "0.3.0"
}, },
"dependencies": { "dependencies": {
"@fesjs/fes": "^2.0.0-rc.0", "@fesjs/fes": "^2.0.0",
"@fesjs/plugin-access": "^2.0.0-rc.0", "@fesjs/plugin-access": "^2.0.0",
"@fesjs/plugin-layout": "^2.0.0-rc.0", "@fesjs/plugin-layout": "^2.0.0",
"@fesjs/plugin-locale": "^2.0.0-rc.0", "@fesjs/plugin-locale": "^2.0.0",
"@fesjs/plugin-model": "^2.0.0-rc.0", "@fesjs/plugin-model": "^2.0.0",
"@fesjs/plugin-enums": "^2.0.0-rc.0", "@fesjs/plugin-enums": "^2.0.0",
"@fesjs/plugin-jest": "^2.0.0-rc.0", "@fesjs/plugin-jest": "^2.0.0",
"@fesjs/plugin-vuex": "^2.0.0-rc.0", "@fesjs/plugin-vuex": "^2.0.0",
"@fesjs/plugin-request": "^2.0.0-rc.0", "@fesjs/plugin-request": "^2.0.0",
"@fesjs/plugin-qiankun": "^2.0.0-rc.0", "@fesjs/plugin-qiankun": "^2.0.0",
"@fesjs/plugin-sass": "^2.0.0-rc.0", "@fesjs/plugin-sass": "^2.0.0",
"ant-design-vue": "2.0.0", "ant-design-vue": "2.0.0",
"vue": "^3.0.5", "vue": "^3.0.5",
"vuex": "^4.0.0" "vuex": "^4.0.0"

View File

@ -1,5 +1,7 @@
<template> <template>
<div class="haizekuo"> <div :class="$style.red">
<a-input placeholder="请输入。。。" />
<a-button type="primary">Primary</a-button>
<div>国际化 {{t("test")}}</div> <div>国际化 {{t("test")}}</div>
fes & 拉夫德鲁 <br /> fes & 拉夫德鲁 <br />
<access :id="accessId"> accessOnepicess1 <input /> </access> <access :id="accessId"> accessOnepicess1 <input /> </access>
@ -22,8 +24,16 @@ import { ref, onMounted } from 'vue';
import { import {
useAccess, useRouter, useI18n, locale, enums, request useAccess, useRouter, useI18n, locale, enums, request
} from '@fesjs/fes'; } from '@fesjs/fes';
import { Button, Input } from 'ant-design-vue';
export default { export default {
components: {
[Button.name]: Button,
[Input.name]: Input,
},
mounted(){
console.log("$style:", this.$style)
},
setup() { setup() {
const fes = ref('fes upgrade to vue3'); const fes = ref('fes upgrade to vue3');
const accessOnepicess = useAccess('/onepiece1'); const accessOnepicess = useAccess('/onepiece1');
@ -62,8 +72,11 @@ export default {
} }
] ]
}); });
console.log(roles); console.log('enums roles=>', roles);
console.log(enums.get('status', { 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: [ extend: [
{ {
key: 'name', key: 'name',
@ -120,8 +133,11 @@ export default {
}; };
</script> </script>
<style scoped> <style module>
.haizekuo { .red {
/* background: url('../images/icon.png'); */ color: red;
}
.bold {
font-weight: bold;
} }
</style> </style>

View File

@ -1,6 +1,6 @@
{ {
"name": "@fesjs/fes", "name": "@fesjs/fes",
"version": "2.0.0-rc.18", "version": "2.0.1",
"description": "一个好用的前端管理台快速开发框架", "description": "一个好用的前端管理台快速开发框架",
"preferGlobal": true, "preferGlobal": true,
"scripts": { "scripts": {
@ -39,9 +39,9 @@
"strong" "strong"
], ],
"dependencies": { "dependencies": {
"@fesjs/compiler": "^2.0.0-rc.5", "@fesjs/compiler": "^2.0.0",
"@fesjs/preset-built-in": "^2.0.0-rc.18", "@fesjs/preset-built-in": "^2.0.1",
"@fesjs/runtime": "^2.0.0-rc.5", "@fesjs/runtime": "^2.0.0",
"@umijs/utils": "3.3.3", "@umijs/utils": "3.3.3",
"resolve-cwd": "^3.0.0" "resolve-cwd": "^3.0.0"
}, },

782
yarn.lock

File diff suppressed because it is too large Load Diff