1
0
mirror of https://github.com/chillzhuang/Sword synced 2024-11-21 17:59:26 +08:00

🎉 init all

This commit is contained in:
smallchill 2019-11-19 17:34:16 +08:00
parent 99e6dfee83
commit 9921323463
51 changed files with 1979 additions and 785 deletions

172
README.md
View File

@ -1,70 +1,119 @@
## 简介 <p align="center">
* Sword 是 [SpringBlade](https://gitee.com/smallc/SpringBlade)前端UI项目基于react 、ant design、dva用于快速构建系统中后台业务。 <img src="https://img.shields.io/badge/Release-V2.5.1-green.svg" alt="Downloads">
* Sword 已入选 ant design 官方脚手架市场,地址:[scaffolds.ant.design](http://scaffold.ant.design/#/scaffolds/Sword) <img src="https://img.shields.io/badge/JDK-1.8+-green.svg" alt="Build Status">
<img src="https://img.shields.io/badge/license-Apache%202-blue.svg" alt="Build Status">
<img src="https://img.shields.io/badge/Spring%20Cloud-Greenwich.SR3-blue.svg" alt="Coverage Status">
<img src="https://img.shields.io/badge/Spring%20Boot-2.1.8.RELEASE-blue.svg" alt="Downloads">
<a target="_blank" href="https://bladex.vip">
<img src="https://img.shields.io/badge/Author-Small%20Chill-ff69b4.svg" alt="Downloads">
</a>
<a target="_blank" href="https://bladex.vip">
<img src="https://img.shields.io/badge/Copyright%20-@BladeX-%23ff3f59.svg" alt="Downloads">
</a>
</p>
## 文档 ## SpringBlade微服务开发平台
* 文档地址:[Sword开发手册](https://www.kancloud.cn/smallchill/sword) * 采用前后端分离的模式,前端开源两个框架:[Sword](https://gitee.com/smallc/Sword) (基于 React、Ant Design)、[Saber](https://gitee.com/smallc/Saber) (基于 Vue、Element-UI)
* 后端采用SpringCloud全家桶并同时对其基础组件做了高度的封装单独开源出一个框架[BladeTool](https://github.com/chillzhuang/blade-tool)
* [BladeTool](https://github.com/chillzhuang/blade-tool)已推送至Maven中央库直接引入即可减少了工程的臃肿也可更注重于业务开发
* 集成Sentinel从流量控制、熔断降级、系统负载等多个维度保护服务的稳定性。
* 注册中心、配置中心选型Nacos为工程瘦身的同时加强各模块之间的联动。
* 使用Traefik进行反向代理监听后台变化自动化应用新的配置文件。
* 极简封装了多租户底层用更少的代码换来拓展性更强的SaaS多租户系统。
* 借鉴OAuth2实现了多终端认证系统可控制子系统的token权限互相隔离。
* 借鉴Security封装了Secure模块采用JWT做Token认证可拓展集成Redis等细颗粒度控制方案。
* 稳定生产了一年经历了从Camden -> Greenwich的技术架构也经历了从fat jar -> docker -> k8s + jenkins的部署架构
* 项目分包明确,规范微服务的开发模式,使包与包之间的分工清晰。
## 架构图
<img src="https://gitee.com/smallc/SpringBlade/raw/master/pic/springblade-framework.png"/>
## 工程结构
```
SpringBlade
├── blade-auth -- 授权服务提供
├── blade-common -- 常用工具封装包
├── blade-gateway -- Spring Cloud 网关
├── blade-ops -- 运维中心
├ ├── blade-admin -- spring-cloud后台管理
├ ├── blade-develop -- 代码生成
├ ├── blade-resource -- 资源管理
├ ├── blade-seata-order -- seata分布式事务demo
├ ├── blade-seata-storage -- seata分布式事务demo
├── blade-service -- 业务模块
├ ├── blade-desk -- 工作台模块
├ ├── blade-log -- 日志模块
├ ├── blade-system -- 系统模块
├ └── blade-user -- 用户模块
├── blade-service-api -- 业务模块api封装
├ ├── blade-desk-api -- 工作台api
├ ├── blade-dict-api -- 字典api
├ ├── blade-system-api -- 系统api
└── └── blade-user-api -- 用户api
```
## 官网 ## 官网
* 官网地址:[https://bladex.vip](https://bladex.vip) * 官网地址:[https://bladex.vip](https://bladex.vip)
* 问答社区:[https://sns.bladex.vip](https://sns.bladex.vip)
* 会员计划:[SpringBlade会员计划](https://gitee.com/smallc/SpringBlade/wikis/SpringBlade会员计划)
* 交流一群:`477853168`
* 交流二群:`751253339`
## 在线演示 ## 在线演示
* Sword演示地址[https://sword.bladex.vip](https://sword.bladex.vip) * Saber-基于Vue[https://saber.bladex.vip](https://saber.bladex.vip)
* Saber演示地址[https://saber.bladex.vip](https://saber.bladex.vip) * Sword-基于React[https://sword.bladex.vip](https://sword.bladex.vip)
* Archer-全能代码生成系统:[https://archer.bladex.vip](https://archer.bladex.vip)
## 后端项目地址 ## 技术文档
* [Gitee地址](https://gitee.com/smallc/SpringBlade) * [开发手册一览](https://gitee.com/smallc/SpringBlade/wikis/SpringBlade开发手册)
* [Github地址](https://github.com/chillzhuang/SpringBlade) * [常见问题集锦](https://sns.bladex.vip/article-14966.html)
## 前端项目地址
* [Sword--基于React](https://gitee.com/smallc/Sword)
* [Saber--基于Vue](https://gitee.com/smallc/Saber)
## 特性
- :gem: **优雅美观**:基于 Ant Design 体系精心设计
- :triangular_ruler: **常见设计模式**:提炼自中后台应用的典型页面和场景
- :rocket: **最新技术栈**:使用 React/umi/dva/antd 等前端前沿技术开发
- :iphone: **响应式**:针对不同屏幕大小设计
- :art: **主题**:可配置的主题满足多样化的品牌诉求
- :globe_with_meridians: **国际化**:内建业界通用的国际化方案
- :zap: **最佳实践**:良好的工程实践助您持续产出高质量代码
- :1234: **Mock 数据**:实用的本地数据调试方案
- :white_check_mark: **UI 测试**:自动化测试保障前端产品质量
## 支持环境
现代浏览器及 IE11。
| [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/edge/edge_48x48.png" alt="IE / Edge" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>IE / Edge | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/firefox/firefox_48x48.png" alt="Firefox" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Firefox | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/chrome/chrome_48x48.png" alt="Chrome" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Chrome | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/safari/safari_48x48.png" alt="Safari" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Safari | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/opera/opera_48x48.png" alt="Opera" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Opera |
| --------- | --------- | --------- | --------- | --------- |
| IE11, Edge| last 2 versions| last 2 versions| last 2 versions| last 2 versions
## 项目地址
* 后端Gitee地址[https://gitee.com/smallc/SpringBlade](https://gitee.com/smallc/SpringBlade)
* 后端Github地址[https://github.com/chillzhuang/SpringBlade](https://github.com/chillzhuang/SpringBlade)
* 后端SpringBoot版[https://gitee.com/smallc/SpringBlade/tree/2.0-boot/](https://gitee.com/smallc/SpringBlade/tree/2.0-boot/)
* 前端框架Sword(基于React)[https://gitee.com/smallc/Sword](https://gitee.com/smallc/Sword)
* 前端框架Saber(基于Vue)[https://gitee.com/smallc/Saber](https://gitee.com/smallc/Saber)
* 核心框架项目地址:[https://github.com/chillzhuang/blade-tool](https://github.com/chillzhuang/blade-tool)
## 用户权益 ## 用户权益
* 允许免费用于学习、毕设、公司项目、私活等。 * 允许免费用于学习、毕设、公司项目、私活等。
* 代码文件需保留相关license信息。 * 代码文件需保留相关license信息。
* 禁止直接将本项目挂淘宝等商业平台出售。
## 禁止事项 * 非界面代码50%以上相似度的二次开源,二次开源需先联系作者。
* 直接将本项目挂淘宝等商业平台出售。
* 业务代码50%以上相似度的二次开源,二次开源需先联系作者。
注意若禁止条款被发现有权追讨19999的授权费。
## 如何启动 ## 如何启动
``` ```
$ git clone https://gitee.com/smallc/Sword.git $ git clone https://gitee.com/smallc/Sword.git
$ cd Sword $ cd Sword
$ npm install $ yarn install 或者 npm install
# mock模式 # mock模式
$ npm start $ yarn start 或者 npm start
# 服务模式 # 服务模式
$ npm run start:no-mock $ yarn run start:no-mock 或者 npm run start:no-mock
# 访问 http://localhost:88 # 访问 http://localhost:8888
# 推荐使用yarn
``` ```
# 界面 # 界面
## Sword界面一览
## [BladeX](https://bladex.vip/#/vip) 工作流一览
<table>
<tr>
<td><img src="https://gitee.com/smallc/SpringBlade/raw/master/pic/bladex-flow1.png"/></td>
<td><img src="https://gitee.com/smallc/SpringBlade/raw/master/pic/bladex-flow2.png"/></td>
</tr>
<tr>
<td><img src="https://gitee.com/smallc/SpringBlade/raw/master/pic/bladex-flow3.png"/></td>
<td><img src="https://gitee.com/smallc/SpringBlade/raw/master/pic/bladex-flow4.png"/></td>
</tr>
<tr>
<td><img src="https://gitee.com/smallc/SpringBlade/raw/master/pic/bladex-flow5.png"/></td>
<td><img src="https://gitee.com/smallc/SpringBlade/raw/master/pic/bladex-flow6.png"/></td>
</tr>
</table>
## [Sword](https://gitee.com/smallc/Sword) 界面一览
<table> <table>
<tr> <tr>
<td><img src="https://gitee.com/smallc/SpringBlade/raw/master/pic/sword-main.png"/></td> <td><img src="https://gitee.com/smallc/SpringBlade/raw/master/pic/sword-main.png"/></td>
@ -88,6 +137,22 @@ $ npm run start:no-mock
</tr> </tr>
</table> </table>
## [Saber](https://gitee.com/smallc/Saber) 界面一览
<table>
<tr>
<td><img src="https://gitee.com/smallc/SpringBlade/raw/master/pic/saber-user.png"/></td>
<td><img src="https://gitee.com/smallc/SpringBlade/raw/master/pic/saber-role.png"/></td>
</tr>
<tr>
<td><img src="https://gitee.com/smallc/SpringBlade/raw/master/pic/saber-dict.png"/></td>
<td><img src="https://gitee.com/smallc/SpringBlade/raw/master/pic/saber-dict-select.png"/></td>
</tr>
<tr>
<td><img src="https://gitee.com/smallc/SpringBlade/raw/master/pic/saber-log.png"/></td>
<td><img src="https://gitee.com/smallc/SpringBlade/raw/master/pic/saber-code.png"/></td>
</tr>
</table>
## 监控界面一览 ## 监控界面一览
<table> <table>
<tr> <tr>
@ -116,18 +181,5 @@ $ npm run start:no-mock
</tr> </tr>
</table> </table>
## Saber界面一览 ## 关注我们
<table> ![](https://images.gitee.com/uploads/images/2019/0330/065148_f0ada806_410595.jpeg)
<tr>
<td><img src="https://gitee.com/smallc/SpringBlade/raw/master/pic/saber-user.png"/></td>
<td><img src="https://gitee.com/smallc/SpringBlade/raw/master/pic/saber-role.png"/></td>
</tr>
<tr>
<td><img src="https://gitee.com/smallc/SpringBlade/raw/master/pic/saber-dict.png"/></td>
<td><img src="https://gitee.com/smallc/SpringBlade/raw/master/pic/saber-dict-select.png"/></td>
</tr>
<tr>
<td><img src="https://gitee.com/smallc/SpringBlade/raw/master/pic/saber-log.png"/></td>
<td><img src="https://gitee.com/smallc/SpringBlade/raw/master/pic/saber-code.png"/></td>
</tr>
</table>

View File

@ -52,18 +52,19 @@ export default [
}, },
{ {
path: '/account/settings', path: '/account/settings',
component: './Account/Settings/Info', //component: './Account/Settings/Info',
routes: [ routes: [
{ path: '/account/settings', redirect: '/account/settings/base' }, { path: '/account/settings', redirect: '/account/settings/base' },
{ path: '/account/settings/base', component: './Account/Settings/BaseView' }, { path: '/account/settings/base', component: './Account/Settings/BaseView' },
{ path: '/account/settings/security', component: './Account/Settings/SecurityView' }, { path: '/account/settings/password', component: './Account/Settings/PasswordView' },
{ path: '/account/settings/binding', component: './Account/Settings/BindingView' }, //{ path: '/account/settings/security', component: './Account/Settings/SecurityView' },
//{ path: '/account/settings/binding', component: './Account/Settings/BindingView' },
{ {
path: '/account/settings/notification', path: '/account/settings/notification',
component: './Account/Settings/NotificationView', component: './Account/Settings/NotificationView',
}, },
], ],
}, }
], ],
}, },
{ {
@ -226,6 +227,23 @@ export default [
{ path: '/tool/code/view/:id', component: './System/Code/CodeView' }, { path: '/tool/code/view/:id', component: './System/Code/CodeView' },
], ],
}, },
{
path: '/tool/datasource',
routes: [
{ path: '/tool/datasource', redirect: '/tool/datasource/list' },
{ path: '/tool/datasource/list', component: './System/DataSource/DataSource' },
{ path: '/tool/datasource/add', component: './System/DataSource/DataSourceAdd' },
{ path: '/tool/datasource/add/:id', component: './System/DataSource/DataSourceAdd' },
{
path: '/tool/datasource/edit/:id',
component: './System/DataSource/DataSourceEdit',
},
{
path: '/tool/datasource/view/:id',
component: './System/DataSource/DataSourceView',
},
],
},
], ],
}, },
{ {

View File

@ -6,21 +6,21 @@ function getFakeList(req, res) {
data.push({ data.push({
id: '1', id: '1',
deptName: '刀锋科技', deptName: '刀锋科技',
tenantCode: '000000', tenantId: '000000',
fullName: '江苏刀锋科技有限公司', fullName: '江苏刀锋科技有限公司',
sort: '1', sort: '1',
children: [ children: [
{ {
id: '2', id: '2',
deptName: '常州刀锋', deptName: '常州刀锋',
tenantCode: '000000', tenantId: '000000',
fullName: '常州刀锋科技有限公司', fullName: '常州刀锋科技有限公司',
sort: '1', sort: '1',
}, },
{ {
id: '3', id: '3',
deptName: '南京刀锋', deptName: '南京刀锋',
tenantCode: '000000', tenantId: '000000',
fullName: '南京刀锋科技有限公司', fullName: '南京刀锋科技有限公司',
sort: '2', sort: '2',
}, },
@ -36,7 +36,7 @@ function getFakeDetail(req, res) {
id: 2, id: 2,
parentId: 1, parentId: 1,
parentName: '江苏刀锋', parentName: '江苏刀锋',
tenantCode: '000000', tenantId: '000000',
deptName: '常州刀锋', deptName: '常州刀锋',
fullName: '常州刀锋科技有限公司', fullName: '常州刀锋科技有限公司',
sort: 1, sort: 1,

View File

@ -7,14 +7,14 @@ function getFakeList(req, res) {
{ {
id: '1', id: '1',
roleName: '超级管理员', roleName: '超级管理员',
tenantCode: '000000', tenantId: '000000',
roleAlias: 'administrator', roleAlias: 'administrator',
sort: '1', sort: '1',
children: [ children: [
{ {
id: '2', id: '2',
roleName: '管理员', roleName: '管理员',
tenantCode: '000001', tenantId: '000001',
roleAlias: 'admin', roleAlias: 'admin',
sort: '1', sort: '1',
}, },
@ -23,21 +23,21 @@ function getFakeList(req, res) {
{ {
id: '3', id: '3',
roleName: '用户', roleName: '用户',
tenantCode: '000002', tenantId: '000002',
roleAlias: 'user', roleAlias: 'user',
sort: '2', sort: '2',
children: [ children: [
{ {
id: '4', id: '4',
roleName: '普通用户', roleName: '普通用户',
tenantCode: '000003', tenantId: '000003',
roleAlias: 'user', roleAlias: 'user',
sort: '1', sort: '1',
}, },
{ {
id: '5', id: '5',
roleName: '访客', roleName: '访客',
tenantCode: '000004', tenantId: '000004',
roleAlias: 'guest', roleAlias: 'guest',
sort: '2', sort: '2',
}, },
@ -54,7 +54,7 @@ function getFakeDetail(req, res) {
id: 2, id: 2,
parentId: 1, parentId: 1,
parentName: '超级管理员', parentName: '超级管理员',
tenantCode: '000000', tenantId: '000000',
roleName: '用户', roleName: '用户',
roleAlias: 'user', roleAlias: 'user',
sort: 1, sort: 1,

View File

@ -6,7 +6,7 @@ function getFakeList(req, res) {
list.push( list.push(
{ {
id: '1', id: '1',
tenantCode: '000000', tenantId: '000000',
tenantName: '管理组', tenantName: '管理组',
linkman: 'Chill', linkman: 'Chill',
contactNumber: '66666666666', contactNumber: '66666666666',
@ -14,7 +14,7 @@ function getFakeList(req, res) {
}, },
{ {
id: '2', id: '2',
tenantCode: '000001', tenantId: '000001',
tenantName: '用户组', tenantName: '用户组',
linkman: 'Bill', linkman: 'Bill',
contactNumber: '23333333333', contactNumber: '23333333333',
@ -36,7 +36,7 @@ function getFakeDetail(req, res) {
const json = { code: 200, success: true, msg: '操作成功' }; const json = { code: 200, success: true, msg: '操作成功' };
json.data = { json.data = {
id: '1', id: '1',
tenantCode: '000000', tenantId: '000000',
tenantName: '管理组', tenantName: '管理组',
linkman: 'Chill', linkman: 'Chill',
contactNumber: '66666666666', contactNumber: '66666666666',
@ -54,11 +54,11 @@ function getFakeTenantSelect(req, res) {
const json = { code: 200, success: true, msg: '操作成功' }; const json = { code: 200, success: true, msg: '操作成功' };
json.data = [ json.data = [
{ {
tenantCode: '000000', tenantId: '000000',
tenantName: '管理组', tenantName: '管理组',
}, },
{ {
tenantCode: '000001', tenantId: '000001',
tenantName: '用户组', tenantName: '用户组',
}, },
]; ];

View File

@ -6,7 +6,7 @@ function getFakeList(req, res) {
list.push( list.push(
{ {
id: '1', id: '1',
tenantCode: '000000', tenantId: '000000',
account: 'admin', account: 'admin',
name: '超级管理员', name: '超级管理员',
realName: '管理员', realName: '管理员',
@ -18,7 +18,7 @@ function getFakeList(req, res) {
}, },
{ {
id: '2', id: '2',
tenantCode: '000001', tenantId: '000001',
account: 'user', account: 'user',
name: '系统用户', name: '系统用户',
realName: '用户', realName: '用户',
@ -44,7 +44,7 @@ function getFakeDetail(req, res) {
const json = { code: 200, success: true, msg: '操作成功' }; const json = { code: 200, success: true, msg: '操作成功' };
json.data = { json.data = {
id: '1', id: '1',
tenantCode: '000000', tenantId: '000000',
account: 'admin', account: 'admin',
name: '超级管理员', name: '超级管理员',
realName: '管理员', realName: '管理员',
@ -74,6 +74,7 @@ const proxy = {
'POST /api/blade-user/grant': fakeSuccess, 'POST /api/blade-user/grant': fakeSuccess,
'POST /api/blade-user/reset-password': fakeSuccess, 'POST /api/blade-user/reset-password': fakeSuccess,
'POST /api/blade-user/submit': fakeSuccess, 'POST /api/blade-user/submit': fakeSuccess,
'POST /api/blade-user/update': fakeSuccess,
'POST /api/blade-user/remove': fakeSuccess, 'POST /api/blade-user/remove': fakeSuccess,
// 支持值为 Object 和 Array // 支持值为 Object 和 Array

View File

@ -1,6 +1,6 @@
{ {
"name": "sword", "name": "sword",
"version": "1.0.0", "version": "2.5.4",
"description": "An out-of-box UI solution for enterprise applications", "description": "An out-of-box UI solution for enterprise applications",
"private": true, "private": true,
"scripts": { "scripts": {

View File

@ -7,6 +7,13 @@ export function CODE_LIST(payload) {
}; };
} }
export function CODE_INIT() {
return {
type: `${CODE_NAMESPACE}/fetchInit`,
payload: { code: 'yes_no' },
};
}
export function CODE_DETAIL(id) { export function CODE_DETAIL(id) {
return { return {
type: `${CODE_NAMESPACE}/fetchDetail`, type: `${CODE_NAMESPACE}/fetchDetail`,

36
src/actions/datasource.js Normal file
View File

@ -0,0 +1,36 @@
export const DATASOURCE_NAMESPACE = 'datasource';
export function DATASOURCE_LIST(payload) {
return {
type: `${DATASOURCE_NAMESPACE}/fetchList`,
payload,
};
}
export function DATASOURCE_DETAIL(id) {
return {
type: `${DATASOURCE_NAMESPACE}/fetchDetail`,
payload: { id },
};
}
export function DATASOURCE_CLEAR_DETAIL() {
return {
type: `${DATASOURCE_NAMESPACE}/clearDetail`,
payload: {},
};
}
export function DATASOURCE_SUBMIT(payload) {
return {
type: `${DATASOURCE_NAMESPACE}/submit`,
payload,
};
}
export function DATASOURCE_REMOVE(payload) {
return {
type: `${DATASOURCE_NAMESPACE}/remove`,
payload,
};
}

View File

@ -43,6 +43,13 @@ export function USER_SUBMIT(payload) {
}; };
} }
export function USER_UPDATE(payload) {
return {
type: `${USER_NAMESPACE}/update`,
payload,
};
}
export function USER_REMOVE(payload) { export function USER_REMOVE(payload) {
return { return {
type: `${USER_NAMESPACE}/remove`, type: `${USER_NAMESPACE}/remove`,

View File

@ -96,14 +96,14 @@ export default class GlobalHeaderRight extends PureComponent {
selectedKeys={[]} selectedKeys={[]}
onClick={onMenuClick} onClick={onMenuClick}
> >
<Menu.Item key="userCenter">
<Icon type="user" />
<FormattedMessage id="menu.account.center" defaultMessage="account center" />
</Menu.Item>
<Menu.Item key="userinfo"> <Menu.Item key="userinfo">
<Icon type="setting" /> <Icon type="setting" />
<FormattedMessage id="menu.account.settings" defaultMessage="account settings" /> <FormattedMessage id="menu.account.settings" defaultMessage="account settings" />
</Menu.Item> </Menu.Item>
<Menu.Item key="password">
<Icon type="user" />
<FormattedMessage id="menu.account.password" defaultMessage="password settings" />
</Menu.Item>
<Menu.Divider /> <Menu.Divider />
<Menu.Item key="logout"> <Menu.Item key="logout">
<Icon type="logout" /> <Icon type="logout" />

View File

@ -12,7 +12,7 @@ export interface ILoginProps {
export default class Login extends React.Component<ILoginProps, any> { export default class Login extends React.Component<ILoginProps, any> {
public static Tab: typeof LoginTab; public static Tab: typeof LoginTab;
public static TenantCode: typeof LoginItem; public static TenantId: typeof LoginItem;
public static UserName: typeof LoginItem; public static UserName: typeof LoginItem;
public static Password: typeof LoginItem; public static Password: typeof LoginItem;
public static Mobile: typeof LoginItem; public static Mobile: typeof LoginItem;

View File

@ -3,17 +3,17 @@ import { Icon } from 'antd';
import styles from './index.less'; import styles from './index.less';
export default { export default {
TenantCode: { TenantId: {
props: { props: {
size: 'large', size: 'large',
id: 'tenantCode', id: 'tenantId',
prefix: <Icon type="home" className={styles.prefixIcon} />, prefix: <Icon type="home" className={styles.prefixIcon} />,
placeholder: 'admin', placeholder: 'admin',
}, },
rules: [ rules: [
{ {
required: true, required: true,
message: 'Please enter tenantcode!', message: 'Please enter tenantId!',
}, },
], ],
}, },

View File

@ -52,10 +52,12 @@ export default class PageHeader extends PureComponent {
<div className={styles.detail}> <div className={styles.detail}>
{logo && <div className={styles.logo}>{logo}</div>} {logo && <div className={styles.logo}>{logo}</div>}
<div className={styles.main}> <div className={styles.main}>
<div className={styles.row}> {title && (
{title && <h1 className={styles.title}>{title}</h1>} <div className={styles.row}>
{action && <div className={styles.action}>{action}</div>} <h1 className={styles.title}>{title}</h1>
</div> {action && <div className={styles.action}>{action}</div>}
</div>
)}
<div className={styles.row}> <div className={styles.row}>
{content && <div className={styles.content}>{content}</div>} {content && <div className={styles.content}>{content}</div>}
{extraContent && <div className={styles.extraContent}>{extraContent}</div>} {extraContent && <div className={styles.extraContent}>{extraContent}</div>}

View File

@ -34,7 +34,9 @@ export default class Grid extends PureComponent {
const { form } = this.props; const { form } = this.props;
form.validateFields(async (err, fieldsValue) => { form.validateFields(async (err, fieldsValue) => {
if (err) return; if (err) {
return;
}
const values = { const values = {
...fieldsValue, ...fieldsValue,
@ -57,7 +59,9 @@ export default class Grid extends PureComponent {
formValues: {}, formValues: {},
selectedRows: [], selectedRows: [],
}); });
if (onReset) onReset(); if (onReset) {
onReset();
}
this.refreshTable(); this.refreshTable();
}; };
@ -79,7 +83,9 @@ export default class Grid extends PureComponent {
size, size,
...formValues, ...formValues,
}; };
if (onSearch) onSearch(params); if (onSearch) {
onSearch(params);
}
}; };
handleSelectRows = rows => { handleSelectRows = rows => {
@ -105,12 +111,13 @@ export default class Grid extends PureComponent {
}); });
}; };
handelToolBarClick = btn => { handleToolBarClick = btn => {
const { selectedRows } = this.state;
const keys = this.getSelectKeys(); const keys = this.getSelectKeys();
this.handelClick(btn, keys); this.handleClick(btn, keys, selectedRows);
}; };
handelClick = (btn, keys = []) => { handleClick = (btn, keys = [], rows) => {
const { path, alias } = btn; const { path, alias } = btn;
const { btnCallBack } = this.props; const { btnCallBack } = this.props;
const refresh = (temp = true) => this.refreshTable(temp); const refresh = (temp = true) => this.refreshTable(temp);
@ -176,7 +183,7 @@ export default class Grid extends PureComponent {
return; return;
} }
if (btnCallBack) { if (btnCallBack) {
btnCallBack({ btn, keys, refresh }); btnCallBack({ btn, keys, rows, refresh });
} }
}; };
@ -195,12 +202,13 @@ export default class Grid extends PureComponent {
renderSearchForm, renderSearchForm,
renderLeftButton, renderLeftButton,
renderRightButton, renderRightButton,
renderActionButton,
} = this.props; } = this.props;
let { columns } = this.props; let { columns } = this.props;
const actionButtons = buttons.filter(button => button.action === 2 || button.action === 3); const actionButtons = buttons.filter(button => button.action === 2 || button.action === 3);
if (columns && Array.isArray(columns) && actionButtons.length > 0) { if (columns && Array.isArray(columns) && (actionButtons.length > 0 || renderActionButton)) {
const key = pkField || rowKey || 'id'; const key = pkField || rowKey || 'id';
columns = [ columns = [
...columns, ...columns,
@ -209,17 +217,24 @@ export default class Grid extends PureComponent {
width: actionColumnWidth || 200, width: actionColumnWidth || 200,
render: (text, record) => ( render: (text, record) => (
<Fragment> <Fragment>
{actionButtons.map((button, index) => ( <div style={{ textAlign: 'center' }}>
<Fragment key={button.code}> {actionButtons.map((button, index) => (
{index > 0 ? <Divider type="vertical" /> : null} <Fragment key={button.code}>
<a {index > 0 ? <Divider type="vertical" /> : null}
title={formatMessage({ id: `button.${button.alias}.name` })} <a
onClick={() => this.handelClick(button, [record[childPkField || key]])} title={formatMessage({ id: `button.${button.alias}.name` })}
> onClick={() =>
<FormattedMessage id={`button.${button.alias}.name`} /> this.handleClick(button, [record[childPkField || key]], [record])
</a> }
</Fragment> >
))} <FormattedMessage id={`button.${button.alias}.name`} />
</a>
</Fragment>
))}
{renderActionButton
? renderActionButton([record[childPkField || key]], [record])
: null}
</div>
</Fragment> </Fragment>
), ),
}, },
@ -236,7 +251,7 @@ export default class Grid extends PureComponent {
buttons={buttons} buttons={buttons}
renderLeftButton={renderLeftButton} renderLeftButton={renderLeftButton}
renderRightButton={renderRightButton} renderRightButton={renderRightButton}
onClick={this.handelToolBarClick} onClick={this.handleToolBarClick}
/> />
<StandardTable <StandardTable
rowKey={rowKey || 'id'} rowKey={rowKey || 'id'}
@ -249,6 +264,7 @@ export default class Grid extends PureComponent {
scroll={scroll} scroll={scroll}
tblProps={tblProps} tblProps={tblProps}
size="middle" size="middle"
bordered
/> />
</div> </div>
</Card> </Card>

View File

@ -62,8 +62,11 @@ class HeaderView extends React.Component {
return; return;
} }
if (key === 'userinfo') { if (key === 'userinfo') {
message.success('即将开放'); router.push('/account/settings/base');
// router.push('/account/settings/base'); return;
}
if (key === 'password') {
router.push('/account/settings/password');
return; return;
} }
if (key === 'triggerError') { if (key === 'triggerError') {

View File

@ -1,5 +1,5 @@
export default { export default {
'app.login.tenantCode': 'tenantCode', 'app.login.tenantId': 'tenantId',
'app.login.userName': 'userName', 'app.login.userName': 'userName',
'app.login.password': 'password', 'app.login.password': 'password',
'app.login.message-invalid-credentials': 'Invalid username or passwordadmin/ant.design', 'app.login.message-invalid-credentials': 'Invalid username or passwordadmin/ant.design',
@ -21,7 +21,7 @@ export default {
'app.register-result.view-mailbox': 'View mailbox', 'app.register-result.view-mailbox': 'View mailbox',
'validation.email.required': 'Please enter your email!', 'validation.email.required': 'Please enter your email!',
'validation.email.wrong-format': 'The email address is in the wrong format!', 'validation.email.wrong-format': 'The email address is in the wrong format!',
'validation.userName.tenantCode': 'Please enter your tenantCode!', 'validation.userName.tenantId': 'Please enter your tenantId!',
'validation.userName.required': 'Please enter your userName!', 'validation.userName.required': 'Please enter your userName!',
'validation.password.required': 'Please enter your password!', 'validation.password.required': 'Please enter your password!',
'validation.password.twice': 'The passwords entered twice do not match!', 'validation.password.twice': 'The passwords entered twice do not match!',

View File

@ -24,6 +24,7 @@ export default {
'menu.monitor.doc': 'api doc', 'menu.monitor.doc': 'api doc',
'menu.tool': 'develop', 'menu.tool': 'develop',
'menu.tool.code': 'code generate', 'menu.tool.code': 'code generate',
'menu.tool.datasource': 'datasource',
'menu.result': 'Result', 'menu.result': 'Result',
'menu.result.success': 'Success', 'menu.result.success': 'Success',
'menu.result.fail': 'Fail', 'menu.result.fail': 'Fail',
@ -34,6 +35,7 @@ export default {
'menu.exception.trigger': 'Trigger', 'menu.exception.trigger': 'Trigger',
'menu.account': 'Account', 'menu.account': 'Account',
'menu.account.center': 'Account Center', 'menu.account.center': 'Account Center',
'menu.account.password': 'Modify Password',
'menu.account.settings': 'Account Settings', 'menu.account.settings': 'Account Settings',
'menu.account.trigger': 'Trigger Error', 'menu.account.trigger': 'Trigger Error',
'menu.account.logout': 'Logout', 'menu.account.logout': 'Logout',

View File

@ -9,6 +9,8 @@ export default {
'app.settings.basic.email-message': 'Please input your email!', 'app.settings.basic.email-message': 'Please input your email!',
'app.settings.basic.nickname': 'Nickname', 'app.settings.basic.nickname': 'Nickname',
'app.settings.basic.nickname-message': 'Please input your Nickname!', 'app.settings.basic.nickname-message': 'Please input your Nickname!',
'app.settings.basic.realname': 'Realname',
'app.settings.basic.realname-message': 'Please input your Realname!',
'app.settings.basic.profile': 'Personal profile', 'app.settings.basic.profile': 'Personal profile',
'app.settings.basic.profile-message': 'Please input your personal profile!', 'app.settings.basic.profile-message': 'Please input your personal profile!',
'app.settings.basic.profile-placeholder': 'Brief introduction to yourself', 'app.settings.basic.profile-placeholder': 'Brief introduction to yourself',
@ -21,6 +23,12 @@ export default {
'app.settings.basic.phone': 'Phone Number', 'app.settings.basic.phone': 'Phone Number',
'app.settings.basic.phone-message': 'Please input your phone!', 'app.settings.basic.phone-message': 'Please input your phone!',
'app.settings.basic.update': 'Update Information', 'app.settings.basic.update': 'Update Information',
'app.settings.password.old': 'Old Password',
'app.settings.password.old-message': 'Please input your Old Password!',
'app.settings.password.new': 'New Password',
'app.settings.password.new-message': 'Please input your new Password!',
'app.settings.password.new1': 'New Password1',
'app.settings.password.new1-message': 'Please comfirm your new Password!',
'app.settings.security.strong': 'Strong', 'app.settings.security.strong': 'Strong',
'app.settings.security.medium': 'Medium', 'app.settings.security.medium': 'Medium',
'app.settings.security.weak': 'Weak', 'app.settings.security.weak': 'Weak',

View File

@ -1,5 +1,5 @@
export default { export default {
'app.login.tenantCode': '租户编号', 'app.login.tenantId': '租户ID',
'app.login.userName': '用户名', 'app.login.userName': '用户名',
'app.login.password': '密码', 'app.login.password': '密码',
'app.login.message-invalid-credentials': '账户或密码错误admin/ant.design', 'app.login.message-invalid-credentials': '账户或密码错误admin/ant.design',
@ -21,7 +21,7 @@ export default {
'app.register-result.view-mailbox': '查看邮箱', 'app.register-result.view-mailbox': '查看邮箱',
'validation.email.required': '请输入邮箱地址!', 'validation.email.required': '请输入邮箱地址!',
'validation.email.wrong-format': '邮箱地址格式错误!', 'validation.email.wrong-format': '邮箱地址格式错误!',
'validation.tenantCode.required': '请输入租户编号!', 'validation.tenantId.required': '请输入租户ID!',
'validation.userName.required': '请输入用户名!', 'validation.userName.required': '请输入用户名!',
'validation.password.required': '请输入密码!', 'validation.password.required': '请输入密码!',
'validation.password.twice': '两次输入的密码不匹配!', 'validation.password.twice': '两次输入的密码不匹配!',

View File

@ -24,6 +24,7 @@ export default {
'menu.monitor.doc': '接口文档', 'menu.monitor.doc': '接口文档',
'menu.tool': '研发工具', 'menu.tool': '研发工具',
'menu.tool.code': '代码生成', 'menu.tool.code': '代码生成',
'menu.tool.datasource': '数据源管理',
'menu.result': '结果页', 'menu.result': '结果页',
'menu.result.success': '成功页', 'menu.result.success': '成功页',
'menu.result.fail': '失败页', 'menu.result.fail': '失败页',
@ -34,6 +35,7 @@ export default {
'menu.exception.trigger': '触发错误', 'menu.exception.trigger': '触发错误',
'menu.account': '个人页', 'menu.account': '个人页',
'menu.account.center': '个人中心', 'menu.account.center': '个人中心',
'menu.account.password': '密码修改',
'menu.account.settings': '个人设置', 'menu.account.settings': '个人设置',
'menu.account.trigger': '触发报错', 'menu.account.trigger': '触发报错',
'menu.account.logout': '退出登录', 'menu.account.logout': '退出登录',

View File

@ -9,6 +9,8 @@ export default {
'app.settings.basic.email-message': '请输入您的邮箱!', 'app.settings.basic.email-message': '请输入您的邮箱!',
'app.settings.basic.nickname': '昵称', 'app.settings.basic.nickname': '昵称',
'app.settings.basic.nickname-message': '请输入您的昵称!', 'app.settings.basic.nickname-message': '请输入您的昵称!',
'app.settings.basic.realname': '姓名',
'app.settings.basic.realname-message': '请输入您的姓名!',
'app.settings.basic.profile': '个人简介', 'app.settings.basic.profile': '个人简介',
'app.settings.basic.profile-message': '请输入个人简介!', 'app.settings.basic.profile-message': '请输入个人简介!',
'app.settings.basic.profile-placeholder': '个人简介', 'app.settings.basic.profile-placeholder': '个人简介',
@ -21,6 +23,12 @@ export default {
'app.settings.basic.phone': '联系电话', 'app.settings.basic.phone': '联系电话',
'app.settings.basic.phone-message': '请输入您的联系电话!', 'app.settings.basic.phone-message': '请输入您的联系电话!',
'app.settings.basic.update': '更新基本信息', 'app.settings.basic.update': '更新基本信息',
'app.settings.password.old': '旧密码',
'app.settings.password.old-message': '请输入你的旧密码!',
'app.settings.password.new': '新密码',
'app.settings.password.new-message': '请输入你的新密码!',
'app.settings.password.new1': '确认密码',
'app.settings.password.new1-message': '请输入你的确认密码!',
'app.settings.security.strong': '强', 'app.settings.security.strong': '强',
'app.settings.security.medium': '中', 'app.settings.security.medium': '中',
'app.settings.security.weak': '弱', 'app.settings.security.weak': '弱',

View File

@ -1,5 +1,5 @@
export default { export default {
'app.login.tenantCode': '租戶編號', 'app.login.tenantId': '租戶編號',
'app.login.userName': '賬戶', 'app.login.userName': '賬戶',
'app.login.password': '密碼', 'app.login.password': '密碼',
'app.login.message-invalid-credentials': '賬戶或密碼錯誤admin/ant.design', 'app.login.message-invalid-credentials': '賬戶或密碼錯誤admin/ant.design',
@ -21,7 +21,7 @@ export default {
'app.register-result.view-mailbox': '查看郵箱', 'app.register-result.view-mailbox': '查看郵箱',
'validation.email.required': '請輸入郵箱地址!', 'validation.email.required': '請輸入郵箱地址!',
'validation.email.wrong-format': '郵箱地址格式錯誤!', 'validation.email.wrong-format': '郵箱地址格式錯誤!',
'validation.tenantCode.required': '請輸入租戶編號!', 'validation.tenantId.required': '請輸入租戶編號!',
'validation.userName.required': '請輸入賬戶!', 'validation.userName.required': '請輸入賬戶!',
'validation.password.required': '請輸入密碼!', 'validation.password.required': '請輸入密碼!',
'validation.password.twice': '兩次輸入的密碼不匹配!', 'validation.password.twice': '兩次輸入的密碼不匹配!',

View File

@ -24,6 +24,7 @@ export default {
'menu.monitor.doc': '接口文檔', 'menu.monitor.doc': '接口文檔',
'menu.tool': '研發工具', 'menu.tool': '研發工具',
'menu.tool.code': '代碼生成', 'menu.tool.code': '代碼生成',
'menu.tool.datasource': '數據源管理',
'menu.result': '結果頁', 'menu.result': '結果頁',
'menu.result.success': '成功頁', 'menu.result.success': '成功頁',
'menu.result.fail': '失敗頁', 'menu.result.fail': '失敗頁',
@ -34,6 +35,7 @@ export default {
'menu.exception.trigger': '觸發錯誤', 'menu.exception.trigger': '觸發錯誤',
'menu.account': '個人頁', 'menu.account': '個人頁',
'menu.account.center': '個人中心', 'menu.account.center': '個人中心',
'menu.account.password': '密碼修改',
'menu.account.settings': '個人設置', 'menu.account.settings': '個人設置',
'menu.account.trigger': '觸發報錯', 'menu.account.trigger': '觸發報錯',
'menu.account.logout': '退出登錄', 'menu.account.logout': '退出登錄',

View File

@ -9,6 +9,8 @@ export default {
'app.settings.basic.email-message': '請輸入您的郵箱!', 'app.settings.basic.email-message': '請輸入您的郵箱!',
'app.settings.basic.nickname': '昵稱', 'app.settings.basic.nickname': '昵稱',
'app.settings.basic.nickname-message': '請輸入您的昵稱!', 'app.settings.basic.nickname-message': '請輸入您的昵稱!',
'app.settings.basic.realname': '姓名',
'app.settings.basic.realname-message': '請輸入您的姓名!',
'app.settings.basic.profile': '個人簡介', 'app.settings.basic.profile': '個人簡介',
'app.settings.basic.profile-message': '請輸入個人簡介!', 'app.settings.basic.profile-message': '請輸入個人簡介!',
'app.settings.basic.profile-placeholder': '個人簡介', 'app.settings.basic.profile-placeholder': '個人簡介',
@ -21,6 +23,12 @@ export default {
'app.settings.basic.phone': '聯系電話', 'app.settings.basic.phone': '聯系電話',
'app.settings.basic.phone-message': '請輸入您的聯系電話!', 'app.settings.basic.phone-message': '請輸入您的聯系電話!',
'app.settings.basic.update': '更新基本信息', 'app.settings.basic.update': '更新基本信息',
'app.settings.password.old': '舊密碼',
'app.settings.password.old-message': '請輸入妳的舊密碼!',
'app.settings.password.new': '新密碼',
'app.settings.password.new-message': '請輸入妳的新密碼!',
'app.settings.password.new1': '確認密碼',
'app.settings.password.new1-message': '請輸入妳的確認密碼!',
'app.settings.security.strong': '強', 'app.settings.security.strong': '強',
'app.settings.security.medium': '中', 'app.settings.security.medium': '中',
'app.settings.security.weak': '弱', 'app.settings.security.weak': '弱',

View File

@ -2,6 +2,8 @@ import { message } from 'antd';
import router from 'umi/router'; import router from 'umi/router';
import { CODE_NAMESPACE } from '../actions/code'; import { CODE_NAMESPACE } from '../actions/code';
import { list, submit, detail, remove } from '../services/code'; import { list, submit, detail, remove } from '../services/code';
import { select } from '../services/datasource';
import { dict } from '../services/dict';
export default { export default {
namespace: CODE_NAMESPACE, namespace: CODE_NAMESPACE,
@ -10,6 +12,10 @@ export default {
list: [], list: [],
pagination: {}, pagination: {},
}, },
init: {
source: [],
category: [],
},
detail: {}, detail: {},
}, },
effects: { effects: {
@ -29,6 +35,19 @@ export default {
}); });
} }
}, },
*fetchInit({ payload }, { call, put }) {
const responseS = yield call(select, payload);
const responseC = yield call(dict, payload);
if (responseS.success && responseC.success) {
yield put({
type: 'saveInit',
payload: {
source: responseS.data,
category: responseC.data,
},
});
}
},
*fetchDetail({ payload }, { call, put }) { *fetchDetail({ payload }, { call, put }) {
const response = yield call(detail, payload); const response = yield call(detail, payload);
if (response.success) { if (response.success) {
@ -71,6 +90,12 @@ export default {
data: action.payload, data: action.payload,
}; };
}, },
saveInit(state, action) {
return {
...state,
init: action.payload,
};
},
saveDetail(state, action) { saveDetail(state, action) {
return { return {
...state, ...state,

87
src/models/datasource.js Normal file
View File

@ -0,0 +1,87 @@
import { message } from 'antd';
import router from 'umi/router';
import { DATASOURCE_NAMESPACE } from '../actions/datasource';
import { list, submit, detail, remove } from '../services/datasource';
export default {
namespace: DATASOURCE_NAMESPACE,
state: {
data: {
list: [],
pagination: false,
},
detail: {},
},
effects: {
*fetchList({ payload }, { call, put }) {
const response = yield call(list, payload);
if (response.success) {
yield put({
type: 'saveList',
payload: {
list: response.data.records,
pagination: {
total: response.data.total,
current: response.data.current,
pageSize: response.data.size,
},
},
});
}
},
*fetchDetail({ payload }, { call, put }) {
const response = yield call(detail, payload);
if (response.success) {
yield put({
type: 'saveDetail',
payload: {
detail: response.data,
},
});
}
},
*clearDetail({ payload }, { put }) {
yield put({
type: 'removeDetail',
payload: { payload },
});
},
*submit({ payload }, { call }) {
const response = yield call(submit, payload);
if (response.success) {
message.success('提交成功');
router.push('/tool/datasource');
}
},
*remove({ payload }, { call }) {
const {
data: { keys },
success,
} = payload;
const response = yield call(remove, { ids: keys });
if (response.success) {
success();
}
},
},
reducers: {
saveList(state, action) {
return {
...state,
data: action.payload,
};
},
saveDetail(state, action) {
return {
...state,
detail: action.payload.detail,
};
},
removeDetail(state) {
return {
...state,
detail: {},
};
},
},
};

View File

@ -1,7 +1,7 @@
import { message } from 'antd'; import { message } from 'antd';
import router from 'umi/router'; import router from 'umi/router';
import { USER_NAMESPACE } from '../actions/user'; import { USER_NAMESPACE } from '../actions/user';
import { query as queryUsers, list, submit, detail, remove, grant } from '../services/user'; import { query as queryUsers, list, submit, update, detail, remove, grant } from '../services/user';
import { select as tenants } from '../services/tenant'; import { select as tenants } from '../services/tenant';
import { tree as roles } from '../services/role'; import { tree as roles } from '../services/role';
import { tree as depts } from '../services/dept'; import { tree as depts } from '../services/dept';
@ -116,6 +116,13 @@ export default {
router.push('/system/user'); router.push('/system/user');
} }
}, },
*update({ payload }, { call }) {
const response = yield call(update, payload);
if (response.success) {
message.success('提交成功');
router.push('/system/user');
}
},
*remove({ payload }, { call }) { *remove({ payload }, { call }) {
const { const {
data: { keys }, data: { keys },

View File

@ -1,106 +1,161 @@
import React, { Component, Fragment } from 'react'; import React, { Component } from 'react';
import { formatMessage, FormattedMessage } from 'umi/locale'; import { formatMessage } from 'umi/locale';
import { Form, Input, Upload, Select, Button } from 'antd'; import { Form, Input, Upload, Button, message, Icon, Card } from 'antd';
import { connect } from 'dva'; import Panel from '../../../components/Panel';
import styles from './BaseView.less'; import { getUserInfo, update } from '../../../services/user';
import GeographicView from './GeographicView'; import { getToken } from '../../../utils/authority';
import PhoneView from './PhoneView';
// import { getTimeDistance } from '@/utils/utils';
const FormItem = Form.Item; const FormItem = Form.Item;
const { Option } = Select;
// 头像组件 方便以后独立,增加裁剪之类的功能
const AvatarView = ({ avatar }) => (
<Fragment>
<div className={styles.avatar_title}>
<FormattedMessage id="app.settings.basic.avatar" defaultMessage="Avatar" />
</div>
<div className={styles.avatar}>
<img src={avatar} alt="avatar" />
</div>
<Upload fileList={[]}>
<div className={styles.button_view}>
<Button icon="upload">
<FormattedMessage id="app.settings.basic.change-avatar" defaultMessage="Change avatar" />
</Button>
</div>
</Upload>
</Fragment>
);
const validatorGeographic = (rule, value, callback) => {
const { province, city } = value;
if (!province.key) {
callback('Please input your province!');
}
if (!city.key) {
callback('Please input your city!');
}
callback();
};
const validatorPhone = (rule, value, callback) => {
const values = value.split('-');
if (!values[0]) {
callback('Please input your area code!');
}
if (!values[1]) {
callback('Please input your phone number!');
}
callback();
};
@connect(({ user }) => ({
currentUser: user.currentUser,
}))
@Form.create() @Form.create()
class BaseView extends Component { class BaseView extends Component {
state = {
userId: '',
avatar: '',
loading: false,
};
componentDidMount() { componentDidMount() {
this.setBaseInfo(); this.setBaseInfo();
} }
setBaseInfo = () => { setBaseInfo = () => {
const { currentUser, form } = this.props; const { form } = this.props;
Object.keys(form.getFieldsValue()).forEach(key => { getUserInfo().then(resp => {
const obj = {}; if (resp.success) {
obj[key] = currentUser[key] || null; const userInfo = resp.data;
form.setFieldsValue(obj); Object.keys(form.getFieldsValue()).forEach(key => {
const obj = {};
obj[key] = userInfo[key] || null;
form.setFieldsValue(obj);
});
this.setState({ userId: userInfo.id, avatar: userInfo.avatar });
} else {
message.error(resp.msg || '获取数据失败');
}
}); });
}; };
getAvatarURL() { beforeUpload = file => {
const { currentUser } = this.props; const isJpgOrPng = file.type === 'image/jpeg' || file.type === 'image/png';
if (currentUser.avatar) { if (!isJpgOrPng) {
return currentUser.avatar; message.error('You can only upload JPG/PNG file!');
} }
const url = 'https://gw.alipayobjects.com/zos/rmsportal/BiazfanxmamNRoxxVxka.png'; const isLt2M = file.size / 1024 / 1024 < 2;
return url; if (!isLt2M) {
} message.error('Image must smaller than 2MB!');
}
return isJpgOrPng && isLt2M;
};
getViewDom = ref => { handleChange = info => {
this.view = ref; if (info.file.status === 'uploading') {
this.setState({ loading: true });
return;
}
if (info.file.status === 'done') {
this.setState({ loading: false, avatar: info.file.response.data.link });
}
};
handleSubmit = e => {
e.preventDefault();
const { form } = this.props;
const { userId, avatar } = this.state;
form.validateFieldsAndScroll((err, values) => {
if (!err) {
const params = {
id: userId,
...values,
avatar,
};
update(params).then(resp => {
if (resp.success) {
message.success(resp.msg);
} else {
message.error(resp.msg || '提交失败');
}
});
}
});
}; };
render() { render() {
const { const {
form: { getFieldDecorator }, form: { getFieldDecorator },
} = this.props; } = this.props;
const { avatar, loading } = this.state;
const formItemLayout = {
labelCol: {
xs: { span: 24 },
sm: { span: 7 },
},
wrapperCol: {
xs: { span: 24 },
sm: { span: 12 },
md: { span: 10 },
},
};
const uploadProp = {
action: '/api/blade-resource/oss/endpoint/put-file',
headers: {
'Blade-Auth': getToken(),
},
};
const uploadButton = (
<div>
<Icon type={loading ? 'loading' : 'plus'} />
<div className="ant-upload-text">上传头像</div>
</div>
);
const action = (
<Button type="primary" onClick={this.handleSubmit}>
提交
</Button>
);
return ( return (
<div className={styles.baseView} ref={this.getViewDom}> <Panel title="个人设置" back="/" action={action}>
<div className={styles.left}> <Form style={{ marginTop: 8 }} hideRequiredMark>
<Form layout="vertical" onSubmit={this.handleSubmit} hideRequiredMark> <Card title="基本信息" bordered={false}>
<FormItem label={formatMessage({ id: 'app.settings.basic.email' })}> <FormItem
{getFieldDecorator('email', { {...formItemLayout}
label={formatMessage({ id: 'app.settings.basic.avatar' })}
>
{getFieldDecorator('avatar', {
rules: [ rules: [
{ {
required: true, required: true,
message: formatMessage({ id: 'app.settings.basic.email-message' }, {}), message: formatMessage({ id: 'app.settings.basic.avatar' }, {}),
}, },
], ],
})(<Input />)} })(
<Upload
name="file"
listType="picture-card"
className="avatar-uploader"
showUploadList={false}
beforeUpload={this.beforeUpload}
onChange={this.handleChange}
{...uploadProp}
>
{avatar ? (
<img src={avatar} alt="avatar" style={{ width: '100%' }} />
) : (
uploadButton
)}
</Upload>
)}
</FormItem> </FormItem>
<FormItem label={formatMessage({ id: 'app.settings.basic.nickname' })}> <FormItem
{...formItemLayout}
label={formatMessage({ id: 'app.settings.basic.nickname' })}
>
{getFieldDecorator('name', { {getFieldDecorator('name', {
rules: [ rules: [
{ {
@ -110,81 +165,42 @@ class BaseView extends Component {
], ],
})(<Input />)} })(<Input />)}
</FormItem> </FormItem>
<FormItem label={formatMessage({ id: 'app.settings.basic.profile' })}> <FormItem
{getFieldDecorator('profile', { {...formItemLayout}
label={formatMessage({ id: 'app.settings.basic.realname' })}
>
{getFieldDecorator('realName', {
rules: [ rules: [
{ {
required: true, required: true,
message: formatMessage({ id: 'app.settings.basic.profile-message' }, {}), message: formatMessage({ id: 'app.settings.basic.realname-message' }, {}),
},
],
})(
<Input.TextArea
placeholder={formatMessage({ id: 'app.settings.basic.profile-placeholder' })}
rows={4}
/>
)}
</FormItem>
<FormItem label={formatMessage({ id: 'app.settings.basic.country' })}>
{getFieldDecorator('country', {
rules: [
{
required: true,
message: formatMessage({ id: 'app.settings.basic.country-message' }, {}),
},
],
})(
<Select style={{ maxWidth: 220 }}>
<Option value="China">中国</Option>
</Select>
)}
</FormItem>
<FormItem label={formatMessage({ id: 'app.settings.basic.geographic' })}>
{getFieldDecorator('geographic', {
rules: [
{
required: true,
message: formatMessage({ id: 'app.settings.basic.geographic-message' }, {}),
},
{
validator: validatorGeographic,
},
],
})(<GeographicView />)}
</FormItem>
<FormItem label={formatMessage({ id: 'app.settings.basic.address' })}>
{getFieldDecorator('address', {
rules: [
{
required: true,
message: formatMessage({ id: 'app.settings.basic.address-message' }, {}),
}, },
], ],
})(<Input />)} })(<Input />)}
</FormItem> </FormItem>
<FormItem label={formatMessage({ id: 'app.settings.basic.phone' })}> <FormItem {...formItemLayout} label={formatMessage({ id: 'app.settings.basic.phone' })}>
{getFieldDecorator('phone', { {getFieldDecorator('phone', {
rules: [ rules: [
{ {
required: true, required: true,
message: formatMessage({ id: 'app.settings.basic.phone-message' }, {}), message: formatMessage({ id: 'app.settings.basic.phone-message' }, {}),
}, },
{ validator: validatorPhone },
], ],
})(<PhoneView />)} })(<Input />)}
</FormItem> </FormItem>
<Button type="primary"> <FormItem {...formItemLayout} label={formatMessage({ id: 'app.settings.basic.email' })}>
<FormattedMessage {getFieldDecorator('email', {
id="app.settings.basic.update" rules: [
defaultMessage="Update Information" {
/> required: true,
</Button> message: formatMessage({ id: 'app.settings.basic.email-message' }, {}),
</Form> },
</div> ],
<div className={styles.right}> })(<Input />)}
<AvatarView avatar={this.getAvatarURL()} /> </FormItem>
</div> </Card>
</div> </Form>
</Panel>
); );
} }
} }

View File

@ -0,0 +1,98 @@
import React, { Component } from 'react';
import { formatMessage } from 'umi/locale';
import { Form, Input, Button, Card, message } from 'antd';
import Panel from '../../../components/Panel';
import { updatePassword } from '../../../services/user';
const FormItem = Form.Item;
@Form.create()
class PasswordView extends Component {
handleSubmit = e => {
e.preventDefault();
const { form } = this.props;
form.validateFieldsAndScroll((err, values) => {
if (!err) {
updatePassword(values).then(resp => {
if (resp.success) {
message.success(resp.msg);
}
});
}
});
};
render() {
const {
form: { getFieldDecorator },
} = this.props;
const formItemLayout = {
labelCol: {
xs: { span: 24 },
sm: { span: 7 },
},
wrapperCol: {
xs: { span: 24 },
sm: { span: 12 },
md: { span: 10 },
},
};
const action = (
<Button type="primary" onClick={this.handleSubmit}>
提交
</Button>
);
return (
<Panel title="密码修改" back="/" action={action}>
<Form style={{ marginTop: 8 }} hideRequiredMark>
<Card title="基本信息" bordered={false}>
<FormItem
{...formItemLayout}
label={formatMessage({ id: 'app.settings.password.old' })}
>
{getFieldDecorator('oldPassword', {
rules: [
{
required: true,
message: formatMessage({ id: 'app.settings.password.old-message' }, {}),
},
],
})(<Input type="password" />)}
</FormItem>
<FormItem
{...formItemLayout}
label={formatMessage({ id: 'app.settings.password.new' })}
>
{getFieldDecorator('newPassword', {
rules: [
{
required: true,
message: formatMessage({ id: 'app.settings.password.new-message' }, {}),
},
],
})(<Input type="password" />)}
</FormItem>
<FormItem
{...formItemLayout}
label={formatMessage({ id: 'app.settings.password.new1' })}
>
{getFieldDecorator('newPassword1', {
rules: [
{
required: true,
message: formatMessage({ id: 'app.settings.password.new1-message' }, {}),
},
],
})(<Input type="password" />)}
</FormItem>
</Card>
</Form>
</Panel>
);
}
}
export default PasswordView;

View File

@ -1,286 +1,223 @@
import React, { PureComponent } from 'react'; import React, { PureComponent } from 'react';
import moment from 'moment'; import { Card, Col, Collapse, Row } from 'antd';
import { connect } from 'dva'; import styles from '../../layouts/Sword.less';
import Link from 'umi/link';
import { Row, Col, Card, List, Avatar } from 'antd';
import EditableLinkGroup from '@/components/EditableLinkGroup';
import PageHeaderWrapper from '@/components/PageHeaderWrapper'; import PageHeaderWrapper from '@/components/PageHeaderWrapper';
import styles from './Workplace.less'; const { Panel } = Collapse;
const links = [
{
title: '操作一',
href: '',
},
{
title: '操作二',
href: '',
},
{
title: '操作三',
href: '',
},
{
title: '操作四',
href: '',
},
{
title: '操作五',
href: '',
},
{
title: '操作六',
href: '',
},
];
@connect(({ user, project, activities, loading }) => ({
currentUser: user.currentUser,
project,
activities,
currentUserLoading: loading.effects['user/fetchCurrent'],
projectLoading: loading.effects['project/fetchNotice'],
activitiesLoading: loading.effects['activities/fetchList'],
}))
class Workplace extends PureComponent { class Workplace extends PureComponent {
componentDidMount() {
const { dispatch } = this.props;
dispatch({
type: 'user/fetchCurrent',
});
dispatch({
type: 'project/fetchNotice',
});
dispatch({
type: 'activities/fetchList',
});
}
renderActivities() {
const {
activities: { list },
} = this.props;
return list.map(item => {
const events = item.template.split(/@\{([^{}]*)\}/gi).map(key => {
if (item[key]) {
return (
<a href={item[key].link} key={item[key].name}>
{item[key].name}
</a>
);
}
return key;
});
return (
<List.Item key={item.id}>
<List.Item.Meta
avatar={<Avatar src={item.user.avatar} />}
title={
<span>
<a className={styles.username}>{item.user.name}</a>
&nbsp;
<span className={styles.event}>{events}</span>
</span>
}
description={
<span className={styles.datetime} title={item.updatedAt}>
{moment(item.updatedAt).fromNow()}
</span>
}
/>
</List.Item>
);
});
}
render() { render() {
const {
currentUser,
currentUserLoading,
project: { notice },
projectLoading,
activitiesLoading,
} = this.props;
const pageHeaderContent =
currentUser && Object.keys(currentUser).length ? (
<div className={styles.pageHeaderContent}>
<div className={styles.avatar}>
<Avatar size="large" src={currentUser.avatar} />
</div>
<div className={styles.content}>
<div className={styles.contentTitle}>
您好
{currentUser.name}
祝您开心每一天
</div>
<div>我们用代码编写梦想用梦想改变世界</div>
</div>
</div>
) : null;
const extraContent = (
<div className={styles.extraContent}>
<div className={styles.statItem}>
<p>项目数</p>
<p>56</p>
</div>
<div className={styles.statItem}>
<p>团队内排名</p>
<p>
8<span> / 24</span>
</p>
</div>
<div className={styles.statItem}>
<p>项目访问</p>
<p>2,223</p>
</div>
</div>
);
return ( return (
<PageHeaderWrapper <PageHeaderWrapper>
loading={currentUserLoading} <Card className={styles.card} bordered={false}>
content={pageHeaderContent} <Row gutter={24}>
extraContent={extraContent} <Col span={24}>
> <div style={{ textAlign: 'center' }}>
<Row gutter={24}> <img src="https://img.shields.io/badge/Release-V2.5.4-green.svg" alt="Downloads" />
<Col xl={16} lg={24} md={24} sm={24} xs={24}> <img src="https://img.shields.io/badge/JDK-1.8+-green.svg" alt="Build Status" />
<Card <img
style={{ marginBottom: 24 }} src="https://img.shields.io/badge/Spring%20Cloud-Greenwich.SR3-blue.svg"
bodyStyle={{ padding: 0 }} alt="Coverage Status"
bordered={false} />
className={styles.activeCard} <img
title="简介" src="https://img.shields.io/badge/Spring%20Boot-2.1.9.RELEASE-blue.svg"
loading={activitiesLoading} alt="Downloads"
> />
<List loading={activitiesLoading} size="large"> <a href="https://bladex.vip">
<div className={styles.activitiesList}> <img
<List.Item key="desc-1"> src="https://img.shields.io/badge/Sword%20Author-Small%20Chill-ff69b4.svg"
<List.Item.Meta alt="Downloads"
title={ />
<span> </a>
<a className={styles.username}>SpringBlade 2.0 </a> <a href="https://bladex.vip">
<span className={styles.event}> <img
是一个基于 Spring Boot 2 & Spring Cloud Finchley & Mybatis src="https://img.shields.io/badge/Copyright%20-@BladeX-%23ff3f59.svg"
等核心技术用于快速构建中大型系统的基础框架 alt="Downloads"
</span> />
</span> </a>
}
/>
</List.Item>
<List.Item key="desc-2">
<List.Item.Meta
title={
<span>
<a className={styles.username}>SpringBlade 企业版系列 </a>
<span className={styles.event}>
已通过长时间生产环境的考验现将其拆分出基础模块进行开源当前版本为2.0.0
</span>
</span>
}
/>
</List.Item>
<List.Item key="desc-2">
<List.Item.Meta
title={
<span>
<a className={styles.username}>SpringBlade </a>
<span className={styles.event}>
技术交流群号477853168欢迎大家加入
</span>
</span>
}
/>
</List.Item>
</div>
</List>
</Card>
<Card
className={styles.projectList}
title="所使用技术"
bordered={false}
loading={projectLoading}
bodyStyle={{ padding: 0 }}
>
{notice.map(item => (
<Card.Grid className={styles.projectGrid} key={item.id}>
<Card bodyStyle={{ padding: 0 }} bordered={false}>
<Card.Meta
title={
<div className={styles.cardTitle}>
<Avatar size="small" src={item.logo} />
<Link to="/">{item.title}</Link>
</div>
}
description={item.description}
/>
</Card>
</Card.Grid>
))}
</Card>
</Col>
<Col xl={8} lg={24} md={24} sm={24} xs={24}>
<Card
style={{ marginBottom: 24 }}
title="快速开始 / 便捷导航"
bordered={false}
bodyStyle={{ padding: 0 }}
>
<EditableLinkGroup onAdd={() => {}} links={links} linkElement={Link} />
</Card>
<Card
bodyStyle={{ paddingTop: 12, paddingBottom: 12 }}
bordered={false}
title="团队"
loading={projectLoading}
>
<div className={styles.members}>
<Row gutter={48}>
<Col span={12} key="members-item-1">
<Link to="/">
<Avatar
src="https://gw.alipayobjects.com/zos/rmsportal/BiazfanxmamNRoxxVxka.png"
size="small"
/>
<span className={styles.member}>ChillZhuang</span>
</Link>
</Col>
<Col span={12} key="members-item-2">
<Link to="/">
<Avatar
src="https://gw.alipayobjects.com/zos/rmsportal/BiazfanxmamNRoxxVxka.png"
size="small"
/>
<span className={styles.member}>DreamLu</span>
</Link>
</Col>
<Col span={12} key="members-item-3">
<Link to="/">
<Avatar
src="https://gw.alipayobjects.com/zos/rmsportal/BiazfanxmamNRoxxVxka.png"
size="small"
/>
<span className={styles.member}>LengLeng</span>
</Link>
</Col>
<Col span={12} key="members-item-4">
<Link to="/">
<Avatar
src="https://gw.alipayobjects.com/zos/rmsportal/BiazfanxmamNRoxxVxka.png"
size="small"
/>
<span className={styles.member}>LiXunHuan</span>
</Link>
</Col>
</Row>
</div> </div>
</Card> </Col>
</Col> </Row>
</Row> <Row gutter={24}>
<Col span={16}>
<Collapse bordered={false} defaultActiveKey={['1','2','3','5']}>
<Panel header="欢迎使用Sword" key="1">
<div>1.Sword是SpringBlade前端UI系统</div>
<div>2.对现有的ant design pro库进行二次封装</div>
<div>3.100%兼容原生ant design pro库</div>
<div>4.配合后端代码生成系统可以快速搭建完整的功能模块</div>
<div>5.使用Sword可以大幅度提升开发效率不再为重复工作发愁</div>
</Panel>
<Panel header="什么是BladeX" key="2">
<div>1.BladeX是一款精心设计的微服务架构提供 SpringCloud 全套解决方案</div>
<div>2.开源中国首批完美集成 SpringCloud Alibaba 系列组件的微服务架构</div>
<div>3.基于稳定生产的商业项目升级优化而来更加贴近企业级的需求</div>
<div>4.追求企业开发更加高效部署更加方便生产更加稳定</div>
<div>5.GVP-码云最有价值开源项目</div>
<div>6.BladeX授权地址:<a href="https://bladex.vip/#/vip">点击授权</a></div>
</Panel>
<Panel header="为何需要BladeX" key="3">
<div>1.经历过较长的线上生产积累了很多企业痛点的解决方案</div>
<div>2.一套代码兼容MySqlOraclePostgreSQL适应企业各种不同场景的需求</div>
<div>3.集成了很多企业急切所需的例如多租户Oauth2授权认证工作流分布式事务等等功能</div>
<div>4.深度定制了Flowable工作流完美支持SpringCloud分布式服务的场景以远程调用的方式进行操作</div>
<div>5.升级了核心驱动新功能完全可以开箱即用而开源版需要自己再花时间进行集成需要花掉更多的时间成本</div>
<div>6.拥抱微服务时代很多企业由于项目转型或升级传统的技术已然不能满足反而会花更多成本而BladeX就是为此而生</div>
<div>7.同时提供SpringCloud版本和SpringBoot版本两个版本的api可以与Sword和Saber无缝对接为小型项目至大型项目保驾护航</div>
<div>8.授权购买即永久源码没有混淆后续升级完全免费企业只需花很少的钱即可获得一整套成熟的解决方案你还在等什么</div>
</Panel>
<Panel header="拥有的核心功能" key="4">
<div>1.前后端分离-采用前后端分离模式前端提供两套架构Sword基于ReactSaber基于Vue</div>
<div>2. 分布式单体式后端架构-提供两套后端架构基于SpringCloud的分布式架构以及基于SpringBoot的单体式架构</div>
<div>3.API完全兼容-两套后端架构与两套前端架构共四套架构可以任意组合所有API完全兼容</div>
<div>4.前后端代码生成-定制针对两套前端与后端的代码生成模板轻松生成整个模块的前后端代码减少重复工作量</div>
<div>5.组件化插件化架构-针对功能深度定制各个starter引入开箱即用为整个架构解耦提升效率</div>
<div>6.Nacos-集成阿里巴巴的Nacos完成统一的服务注册与配置</div>
<div>7.Sentinel-集成Sentinel从流量控制熔断降级系统负载等多个维度保护服务的稳定性</div>
<div>8.Dubbo-完美集成Dubbo最新版支持远程RPC调用</div>
<div>9.多租户系统-完整的SaaS多租户架构</div>
<div>10.Oauth2-集成Oauth2协议完美支持多终端的接入与认证授权</div>
<div>11.工作流-深度定制SpringCloud分布式场景的Flowable工作流为复杂流程保驾护航同时提供SpringBoot集成版本</div>
<div>12.独立流程设计器-提供独立的完全汉化的流程设计器轻松定制流程模型</div>
<div>13.动态网关-集成基于Nacos的轻量级高拓展性动态网关</div>
<div>14.动态聚合文档-实现基于Nacos的Swagger SpringCloud聚合文档</div>
<div>15.分布式文件服务-集成minioqiniualioss等优秀的第三方提供便捷的文件上传与管理</div>
<div>16.多租户对象存储系统-在SaaS系统中各租户可自行配置文件上传至自己的私有OSS</div>
<div>17.权限管理-精心设计的权限管理方案角色权限精确到按钮</div>
<div>18.动态数据权限-高度灵活的动态数据权限提供注解+Web可视化两种配置方式Web配置无需重启直接生效</div>
<div>19.动态接口权限-高度灵活的动态接口权限提供注解+Web可视化两种配置方式Web配置无需重启直接生效</div>
<div>20.多租户顶部菜单配置-提供给每个租户独立的顶部菜单配置模块可以自定义顶部菜单切换</div>
<div>21.主流数据库兼容-一套代码完全兼容MysqlPostgresqlOracle三大主流数据库</div>
<div>22.动态网关鉴权-基于Nacos的动态网关鉴权可在线配置实时生效</div>
<div>23.全能代码生成器-支持自定义模型模版 业务建模支持多种模板引擎在线配置大幅度提升开发效率不再为重复工作发愁</div>
<div>24.Seata分布式事务-定制集成Seata支持分布式事务无代码侵入不失灵活与简洁</div>
<div>25.未完待续...</div>
</Panel>
<Panel header="软件定制开发合作" key="5">
<div>1.接BladeX系列架构的定制服务</div>
<div>2.接3个月以内工期的reactvuespringbootspringcloudapp小程序等软件定制服务</div>
<div>3.有意向请联系唯一指定QQ:85088620</div>
</Panel>
</Collapse>
</Col>
<Col span={8}>
<Collapse bordered={false} defaultActiveKey={['13']}>
<Panel header="2.5.4发布 增加多数据源示例工程" key="13">
<div>1.增加示例工程增加多种常见场景的解决方案</div>
<div>2.增加不同包名运行的示例</div>
<div>3.增加多数据源调用运行的示例</div>
<div>4.增加自定义加载Naocs配置文件的示例</div>
<div>5.增加根据Nacos命名空间读取配置注册服务的示例</div>
<div>6.修复Condition类没有过滤分页字段的问题</div>
<div>7.拆分CommonConstant出LauncherConstant</div>
</Panel>
<Panel header="2.5.3发布 集成分布式链路追踪" key="12">
<div>1.封装集成zipkin支持分布式链路追踪</div>
<div>2.seata升级至0.9.0解决部分分布式事务遇到的bug</div>
<div>3.springboot版本升级至2.1.9</div>
</Panel>
<Panel header="2.5.2发布 增加个人中心" key="11">
<div>1.增加个人中心支持用户信息自定义修改</div>
<div>2.增加网关鉴权配置示例</div>
<div>3.token的SIGN_KEY修改为一致</div>
<div>4.admin模块增加对seata服务的过滤</div>
<div>5.blade-tool增加部分工具类方法</div>
</Panel>
<Panel header="2.5.1发布 增加网关动态鉴权" key="10">
<div>1.增加网关动态鉴权</div>
<div>2.secure安全模块token校验默认关闭交由网关处理</div>
<div>3.boot版本开启secure token校验功能</div>
<div>4.优化blade-gateway代码逻辑</div>
<div>5.修复blade-resource无法启动的问题</div>
</Panel>
<Panel header="2.5.0发布 集成seata支持分布式事务" key="9">
<div>1.封装集成seata支持分布式事务</div>
<div>2.重写blade-core-cloud模块增强cloud场景支持</div>
<div>3.增加hystrix自动fallback功能</div>
<div>4.升级springboot至2.1.8.RELEASE</div>
<div>5.升级springcloud至Greenwich.SR3</div>
</Panel>
<Panel header="2.4.1发布 代码生成增加多数据源,强化代码生成功能" key="8">
<div>1.升级SpringBoot至2.1.7</div>
<div>2.代码生成增加多数据源配置</div>
<div>3.增强代码生成功能支持可选基础业务包装器配置</div>
<div>4.优化代码生成模板</div>
</Panel>
<Panel header="2.4.0发布 升级AlibabaCloud毕业版本" key="7">
<div>1.升级AlibabaCloud毕业版本</div>
<div>2.升级支持Naocs 1.1.0Sentinel 1.6.3</div>
<div>3.租户系统的tenantCode统一更改为tenantId</div>
<div>4.优化代码生成模板</div>
<div>5.优化mybatis-plus新版配置</div>
<div>6.修复排序字段sql注入问题</div>
</Panel>
<Panel header="2.3.3发布 重构令牌逻辑, 增强令牌功能" key="6">
<div>1.重构令牌发放逻辑可自定义令牌类型增强可拓展性</div>
<div>2.增加动态配置token过期时间令牌续期功能</div>
<div>3.增加GateWay动态聚合文档功能简化配置</div>
<div>4.优化Wrapper定义代码更加简洁</div>
<div>5.Swagger增加多包扫描</div>
<div>6.使用 Swagger-Bootstrap-UI 最新版排序注解</div>
<div>7.升级 SpringBoot 2.1.6SpringCloud Greenwich.SR2</div>
<div>8.升级 Mybatis-Plus 3.1.2</div>
<div>9.修复排序字段可能导致的sql注入问题</div>
<div>10.修复部分缓存清除失效的问题</div>
</Panel>
<Panel header="2.3.2发布 增加 OSS 封装及单元测试封装" key="5">
<div>1.增加七牛云oss-starter</div>
<div>2.增加blade-resource模块对外提供服务支持分布式下的oss场景</div>
<div>3.LauncherService增加排序功能</div>
<div>4.增加单元测试starter可在启动过程中便捷地指定profile以及启动参数</div>
<div>5.增加指定启动参数的单元测试demo</div>
<div>6.优化docker脚本配置</div>
</Panel>
<Panel header="2.3.1发布 升级业务架构" key="4">
<div>1.升级 SpringBoot 2.1.5</div>
<div>2.前端框架Saber升级 element-ui 2.8.2</div>
<div>3.Saber业务代码升级</div>
<div>4.优化Saber代码生成模板</div>
<div>5.统一日志业务表基础字段</div>
<div>6.优化租户过滤逻辑</div>
<div>7.BaseEntity放开主键限制子类可自定义主键类型</div>
<div>8.XssFilter增加放行配置可配置放行微信api接口</div>
</Panel>
<Panel header="2.3.0发布 升级SpringCloud Greenwich与SpringBoot" key="3">
<div>1.升级 SpringCloud Greenwich</div>
<div>2.升级 SpringCloud Alibaba 组件版本为0.9.0.RELEASE支持最新版本的nacos与sentinel</div>
<div>3.升级 SpringBoot 2.1.4</div>
<div>4.升级 mysql 驱动版本</div>
<div>5.优化 LauncherService 关于环境的判断逻辑</div>
<div>6.修复 blade-core-log 在部分情况下获取request为空的问题</div>
<div>7.修复多租户插件判断租户过滤的逻辑</div>
<div>8.修复请求日志打印插件部分格式空指针的问题</div>
<div>9.降低nacos心跳日志等级关闭心跳日志显示</div>
</Panel>
<Panel header="2.2.0发布 升级为多终端令牌认证系统" key="2">
<div>1.增加多终端令牌认证系统</div>
<div>2.增加多租户开关</div>
<div>3.修复部分模块没有筛选已删除的问题</div>
<div>4.调整角色分配会越权的问题</div>
<div>5.优化部署脚本</div>
</Panel>
<Panel header="2.1.0发布 升级为SaaS多租户系统" key="1">
<div>1.升级为SaaS多租户系统</div>
<div>2.优化代码生成逻辑</div>
<div>3.代码生成增加菜单sql</div>
<div>4.增加SysClient提供系统信息远程调用</div>
<div>5.优化部署脚本增加前端部署实例</div>
<div>6.增加父子角色过滤使得角色无法越权配置</div>
</Panel>
<Panel header="2.0.0发布 全面升级为SpringCloud微服务架构" key="0">
<div>1.SpringBlade 2.0 是由一个商业级项目升级优化而来的SpringCloud微服务架构采用Java8 API重构了业务代码完全遵循阿里巴巴编码规范</div>
<div>2.采用Spring Boot 2 Spring Cloud Greenwich Mybatis 等核心技术用于快速搭建企业级的微服务系统平台</div>
<div>3.SpringBlade 致力于创造新颖的开发模式将开发中遇到的痛点生产中所踩的坑整理归纳并将解决方案都融合到框架中</div>
</Panel>
</Collapse>
</Col>
</Row>
</Card>
</PageHeaderWrapper> </PageHeaderWrapper>
); );
} }

View File

@ -1,12 +1,12 @@
import React, { Component } from 'react'; import React, { Component } from 'react';
import { connect } from 'dva'; import { connect } from 'dva';
import { formatMessage, FormattedMessage } from 'umi/locale'; import { formatMessage, FormattedMessage } from 'umi/locale';
import { Checkbox, Alert } from 'antd'; import { Checkbox, Alert, notification, Icon } from 'antd';
import Login from '../../components/Login'; import Login from '../../components/Login';
import styles from './Login.less'; import styles from './Login.less';
import { tenantMode } from '../../defaultSettings'; import { tenantMode } from '../../defaultSettings';
const { Tab, TenantCode, UserName, Password, Submit } = Login; const { Tab, TenantId, UserName, Password, Submit } = Login;
@connect(({ login, loading }) => ({ @connect(({ login, loading }) => ({
login, login,
@ -18,6 +18,44 @@ class LoginPage extends Component {
autoLogin: true, autoLogin: true,
}; };
componentWillMount() {
notification.config({
placement: 'bottomRight',
});
notification.destroy();
notification.open({
message: '手册信息',
description: (
<div>
<p>Sword开发手册<a target="_blank" href="https://www.kancloud.cn/smallchill/sword">点击查看</a></p>
<p>SpringBlade开发手册<a target="_blank" href="https://www.kancloud.cn/smallchill/blade">点击查看</a></p>
</div>
),
icon: <Icon type="smile" style={{ color: '#108ee9' }} />,
duration: 0,
});
setTimeout(() => {
notification.open({
message: '授权信息',
description: (
<div>
<p>欢迎使用Sword!</p>
<p>该系统可用BladeX增强开发</p>
<p>若要商用强烈推荐高度定制的商业化框架具体授权信息请访问如下地址</p>
<p>
BladeX 授权地址
<a target="_blank" href="https://bladex.vip/#/vip">
点击授权
</a>
</p>
</div>
),
icon: <Icon type="smile" style={{ color: '#108ee9' }} />,
duration: 0,
});
}, 500);
}
onTabChange = type => { onTabChange = type => {
this.setState({ type }); this.setState({ type });
}; };
@ -82,19 +120,21 @@ class LoginPage extends Component {
!submitting && !submitting &&
this.renderMessage(formatMessage({ id: 'app.login.message-invalid-credentials' }))} this.renderMessage(formatMessage({ id: 'app.login.message-invalid-credentials' }))}
{tenantMode ? ( {tenantMode ? (
<TenantCode <TenantId
name="tenantCode" name="tenantId"
placeholder={`${formatMessage({ id: 'app.login.tenantCode' })}: 000000`} defaultValue="000000"
placeholder={`${formatMessage({ id: 'app.login.tenantId' })}: 000000`}
rules={[ rules={[
{ {
required: true, required: true,
message: formatMessage({ id: 'validation.tenantCode.required' }), message: formatMessage({ id: 'validation.tenantId.required' }),
}, },
]} ]}
/> />
) : null} ) : null}
<UserName <UserName
name="account" name="account"
defaultValue="admin"
placeholder={`${formatMessage({ id: 'app.login.userName' })}: admin`} placeholder={`${formatMessage({ id: 'app.login.userName' })}: admin`}
rules={[ rules={[
{ {
@ -105,6 +145,7 @@ class LoginPage extends Component {
/> />
<Password <Password
name="password" name="password"
defaultValue="admin"
placeholder={`${formatMessage({ id: 'app.login.password' })}: admin`} placeholder={`${formatMessage({ id: 'app.login.password' })}: admin`}
rules={[ rules={[
{ {

View File

@ -1,10 +1,10 @@
import React, { PureComponent } from 'react'; import React, { Fragment, PureComponent } from 'react';
import { connect } from 'dva'; import { connect } from 'dva';
import { Button, Col, Form, Input, message, Modal, Row } from 'antd'; import { Button, Col, Divider, Form, Input, message, Modal, Row } from "antd";
import Panel from '../../../components/Panel'; import Panel from '../../../components/Panel';
import Grid from '../../../components/Sword/Grid'; import Grid from '../../../components/Sword/Grid';
import { CODE_LIST } from '../../../actions/code'; import { CODE_LIST } from '../../../actions/code';
import { genCodes } from '../../../services/code'; import { genCodes, copyCodes } from '../../../services/code';
const FormItem = Form.Item; const FormItem = Form.Item;
@ -16,12 +16,14 @@ const FormItem = Form.Item;
class Code extends PureComponent { class Code extends PureComponent {
state = { state = {
selectedRows: [], selectedRows: [],
params: {},
}; };
// ============ 查询 =============== // ============ 查询 ===============
handleSearch = params => { handleSearch = params => {
const { dispatch } = this.props; const { dispatch } = this.props;
dispatch(CODE_LIST(params)); dispatch(CODE_LIST(params));
this.setState({ params });
}; };
onSelectRow = rows => { onSelectRow = rows => {
@ -60,6 +62,36 @@ class Code extends PureComponent {
}); });
}; };
copyCode = keys => {
const { params } = this.state;
const { dispatch } = this.props;
if (keys.length === 0) {
message.warn('请先选择一条数据!');
return;
}
if (keys.length > 1) {
message.warn('只能选择一条数据!');
return;
}
Modal.confirm({
title: '代码配置复制确认',
content: '是否复制选中模块的配置?',
okText: '确定',
okType: 'danger',
cancelText: '取消',
async onOk() {
const response = await copyCodes({ id: keys[0] });
if (response.success) {
message.success(response.msg);
dispatch(CODE_LIST(params));
} else {
message.error(response.msg || '复制失败');
}
},
onCancel() {},
});
};
// ============ 查询表单 =============== // ============ 查询表单 ===============
renderSearchForm = onReset => { renderSearchForm = onReset => {
const { form } = this.props; const { form } = this.props;
@ -102,6 +134,19 @@ class Code extends PureComponent {
</Button> </Button>
); );
renderActionButton = (keys, rows) => (
<Fragment key="copy">
<Divider type="vertical" />
<a
onClick={() => {
this.copyCode(keys, rows);
}}
>
复制
</a>
</Fragment>
);
render() { render() {
const code = 'code'; const code = 'code';
@ -147,6 +192,7 @@ class Code extends PureComponent {
onSearch={this.handleSearch} onSearch={this.handleSearch}
renderSearchForm={this.renderSearchForm} renderSearchForm={this.renderSearchForm}
renderLeftButton={this.renderLeftButton} renderLeftButton={this.renderLeftButton}
renderActionButton={this.renderActionButton}
loading={loading} loading={loading}
data={data} data={data}
columns={columns} columns={columns}

View File

@ -1,17 +1,24 @@
import React, { PureComponent } from 'react'; import React, { PureComponent } from 'react';
import { Form, Input, Card, Button } from 'antd'; import { Form, Input, Card, Button, Row, Col, Radio, Select } from 'antd';
import { connect } from 'dva'; import { connect } from 'dva';
import Panel from '../../../components/Panel'; import Panel from '../../../components/Panel';
import styles from '../../../layouts/Sword.less'; import styles from '../../../layouts/Sword.less';
import { CODE_SUBMIT } from '../../../actions/code'; import { CODE_INIT, CODE_SUBMIT } from '../../../actions/code';
const FormItem = Form.Item; const FormItem = Form.Item;
const RadioGroup = Radio.Group;
@connect(({ loading }) => ({ @connect(({ code, loading }) => ({
code,
submitting: loading.effects['code/submit'], submitting: loading.effects['code/submit'],
})) }))
@Form.create() @Form.create()
class CodeAdd extends PureComponent { class CodeAdd extends PureComponent {
componentWillMount() {
const { dispatch } = this.props;
dispatch(CODE_INIT());
}
handleSubmit = e => { handleSubmit = e => {
e.preventDefault(); e.preventDefault();
const { dispatch, form } = this.props; const { dispatch, form } = this.props;
@ -25,18 +32,27 @@ class CodeAdd extends PureComponent {
render() { render() {
const { const {
form: { getFieldDecorator }, form: { getFieldDecorator },
code: { init },
submitting, submitting,
} = this.props; } = this.props;
const { source, category } = init;
const formItemLayout = { const formItemLayout = {
labelCol: { labelCol: {
xs: { span: 24 }, span: 8,
sm: { span: 7 },
}, },
wrapperCol: { wrapperCol: {
xs: { span: 24 }, span: 16,
sm: { span: 12 }, },
md: { span: 10 }, };
const formAllItemLayout = {
labelCol: {
span: 4,
},
wrapperCol: {
span: 20,
}, },
}; };
@ -48,88 +64,178 @@ class CodeAdd extends PureComponent {
return ( return (
<Panel title="新增" back="/tool/code" action={action}> <Panel title="新增" back="/tool/code" action={action}>
<Form hideRequiredMark style={{ marginTop: 8 }}> <Form style={{ marginTop: 8 }}>
<Card className={styles.card} bordered={false}> <Card className={styles.card} bordered={false}>
<FormItem {...formItemLayout} label="模块名"> <Row gutter={24}>
{getFieldDecorator('codeName', { <Col span={20}>
rules: [ <FormItem {...formAllItemLayout} label="数据源">
{ {getFieldDecorator('datasourceId', {
required: true, rules: [
message: '请输入模块名', {
}, required: true,
], message: '请选择数据源',
})(<Input placeholder="请输入模块名" />)} },
</FormItem> ],
<FormItem {...formItemLayout} label="服务名"> })(
{getFieldDecorator('serviceName', { <Select placeholder="请选择数据源">
rules: [ {source.map(d => (
{ <Select.Option key={d.id} value={d.id}>
required: true, {d.name}
message: '请输入服务名', </Select.Option>
}, ))}
], </Select>
})(<Input placeholder="请输入服务名" />)} )}
</FormItem> </FormItem>
<FormItem {...formItemLayout} label="表名"> </Col>
{getFieldDecorator('tableName', { </Row>
rules: [ <Row gutter={24}>
{ <Col span={10}>
required: true, <FormItem {...formItemLayout} label="模块名">
message: '请输入表名', {getFieldDecorator('codeName', {
}, rules: [
], {
})(<Input placeholder="请输入表名" />)} required: true,
</FormItem> message: '请输入模块名',
<FormItem {...formItemLayout} label="表前缀名"> },
{getFieldDecorator('tablePrefix', { ],
rules: [ })(<Input placeholder="请输入模块名" />)}
{ </FormItem>
required: true, </Col>
message: '请输入表前缀名', <Col span={10}>
}, <FormItem {...formItemLayout} label="服务名">
], {getFieldDecorator('serviceName', {
})(<Input placeholder="请输入表前缀名" />)} rules: [
</FormItem> {
<FormItem {...formItemLayout} label="主键名"> required: true,
{getFieldDecorator('pkName', { message: '请输入服务名',
rules: [ },
{ ],
required: true, })(<Input placeholder="请输入服务名" />)}
message: '请输入主键名', </FormItem>
}, </Col>
], </Row>
})(<Input placeholder="请输入主键名" />)} <Row gutter={24}>
</FormItem> <Col span={10}>
<FormItem {...formItemLayout} label="包名"> <FormItem {...formItemLayout} label="表名">
{getFieldDecorator('packageName', { {getFieldDecorator('tableName', {
rules: [ rules: [
{ {
required: true, required: true,
message: '请输入包名', message: '请输入表名',
}, },
], ],
})(<Input placeholder="请输入包名" />)} })(<Input placeholder="请输入表名" />)}
</FormItem> </FormItem>
<FormItem {...formItemLayout} label="后端生成路径"> </Col>
{getFieldDecorator('apiPath', { <Col span={10}>
rules: [ <FormItem {...formItemLayout} label="表前缀名">
{ {getFieldDecorator('tablePrefix', {
required: true, rules: [
message: '请输入后端生成路径', {
}, required: true,
], message: '请输入表前缀名',
})(<Input placeholder="请输入后端生成路径" />)} },
</FormItem> ],
<FormItem {...formItemLayout} label="前端生成路径"> })(<Input placeholder="请输入表前缀名" />)}
{getFieldDecorator('webPath', { </FormItem>
rules: [ </Col>
{ </Row>
required: true, <Row gutter={24}>
message: '请输入前端生成路径', <Col span={10}>
}, <FormItem {...formItemLayout} label="主键名">
], {getFieldDecorator('pkName', {
})(<Input placeholder="请输入前端生成路径" />)} rules: [
</FormItem> {
required: true,
message: '请输入主键名',
},
],
})(<Input placeholder="请输入主键名" />)}
</FormItem>
</Col>
<Col span={10}>
<FormItem {...formItemLayout} label="包名">
{getFieldDecorator('packageName', {
rules: [
{
required: true,
message: '请输入包名',
},
],
})(<Input placeholder="请输入包名" />)}
</FormItem>
</Col>
</Row>
<Row gutter={24}>
<Col span={10}>
<FormItem {...formItemLayout} label="基础业务">
{getFieldDecorator('baseMode', {
rules: [
{
required: true,
message: '请配置基础业务',
},
],
})(
<RadioGroup name="baseMode">
{category.map(d => (
<Radio key={d.dictKey} value={d.dictKey}>
{d.dictValue}
</Radio>
))}
</RadioGroup>
)}
</FormItem>
</Col>
<Col span={10}>
<FormItem {...formItemLayout} label="包装器">
{getFieldDecorator('wrapMode', {
rules: [
{
required: true,
message: '请配置包装器',
},
],
})(
<RadioGroup name="wrapMode">
{category.map(d => (
<Radio key={d.dictKey} value={d.dictKey}>
{d.dictValue}
</Radio>
))}
</RadioGroup>
)}
</FormItem>
</Col>
</Row>
<Row gutter={24}>
<Col span={20}>
<FormItem {...formAllItemLayout} label="后端生成路径">
{getFieldDecorator('apiPath', {
rules: [
{
required: true,
message: '请输入后端生成路径',
},
],
})(<Input placeholder="请输入后端生成路径" />)}
</FormItem>
</Col>
</Row>
<Row gutter={24}>
<Col span={20}>
<FormItem {...formAllItemLayout} label="前端生成路径">
{getFieldDecorator('webPath', {
rules: [
{
required: true,
message: '请输入前端生成路径',
},
],
})(<Input placeholder="请输入前端生成路径" />)}
</FormItem>
</Col>
</Row>
</Card> </Card>
</Form> </Form>
</Panel> </Panel>

View File

@ -1,11 +1,12 @@
import React, { PureComponent } from 'react'; import React, { PureComponent } from 'react';
import { Form, Input, Card, Button } from 'antd'; import { Form, Input, Card, Button, Col, Row, Select, Radio } from 'antd';
import { connect } from 'dva'; import { connect } from 'dva';
import Panel from '../../../components/Panel'; import Panel from '../../../components/Panel';
import styles from '../../../layouts/Sword.less'; import styles from '../../../layouts/Sword.less';
import { CODE_DETAIL, CODE_SUBMIT } from '../../../actions/code'; import { CODE_DETAIL, CODE_INIT, CODE_SUBMIT } from '../../../actions/code';
const FormItem = Form.Item; const FormItem = Form.Item;
const RadioGroup = Radio.Group;
@connect(({ code, loading }) => ({ @connect(({ code, loading }) => ({
code, code,
@ -21,6 +22,7 @@ class CodeEdit extends PureComponent {
}, },
} = this.props; } = this.props;
dispatch(CODE_DETAIL(id)); dispatch(CODE_DETAIL(id));
dispatch(CODE_INIT());
} }
handleSubmit = e => { handleSubmit = e => {
@ -38,7 +40,6 @@ class CodeEdit extends PureComponent {
id, id,
...values, ...values,
}; };
console.log(params);
dispatch(CODE_SUBMIT(params)); dispatch(CODE_SUBMIT(params));
} }
}); });
@ -47,19 +48,27 @@ class CodeEdit extends PureComponent {
render() { render() {
const { const {
form: { getFieldDecorator }, form: { getFieldDecorator },
code: { detail }, code: { detail, init },
submitting, submitting,
} = this.props; } = this.props;
const { source, category } = init;
const formItemLayout = { const formItemLayout = {
labelCol: { labelCol: {
xs: { span: 24 }, span: 8,
sm: { span: 7 },
}, },
wrapperCol: { wrapperCol: {
xs: { span: 24 }, span: 16,
sm: { span: 12 }, },
md: { span: 10 }, };
const formAllItemLayout = {
labelCol: {
span: 4,
},
wrapperCol: {
span: 20,
}, },
}; };
@ -71,96 +80,190 @@ class CodeEdit extends PureComponent {
return ( return (
<Panel title="修改" back="/tool/code" action={action}> <Panel title="修改" back="/tool/code" action={action}>
<Form hideRequiredMark style={{ marginTop: 8 }}> <Form style={{ marginTop: 8 }}>
<Card className={styles.card} bordered={false}> <Card className={styles.card} bordered={false}>
<FormItem {...formItemLayout} label="模块名"> <Row gutter={24}>
{getFieldDecorator('codeName', { <Col span={20}>
rules: [ <FormItem {...formAllItemLayout} label="数据源">
{ {getFieldDecorator('datasourceId', {
required: true, rules: [
message: '请输入模块名', {
}, required: true,
], message: '请选择数据源',
initialValue: detail.codeName, },
})(<Input placeholder="请输入模块名" />)} ],
</FormItem> initialValue: detail.datasourceId,
<FormItem {...formItemLayout} label="服务名"> })(
{getFieldDecorator('serviceName', { <Select placeholder="请选择数据源">
rules: [ {source.map(d => (
{ <Select.Option key={d.id} value={d.id}>
required: true, {d.name}
message: '请输入服务名', </Select.Option>
}, ))}
], </Select>
initialValue: detail.serviceName, )}
})(<Input placeholder="请输入服务名" />)} </FormItem>
</FormItem> </Col>
<FormItem {...formItemLayout} label="表名"> </Row>
{getFieldDecorator('tableName', { <Row gutter={24}>
rules: [ <Col span={10}>
{ <FormItem {...formItemLayout} label="模块名">
required: true, {getFieldDecorator('codeName', {
message: '请输入表名', rules: [
}, {
], required: true,
initialValue: detail.tableName, message: '请输入模块名',
})(<Input placeholder="请输入表名" />)} },
</FormItem> ],
<FormItem {...formItemLayout} label="表前缀名"> initialValue: detail.codeName,
{getFieldDecorator('tablePrefix', { })(<Input placeholder="请输入模块名" />)}
rules: [ </FormItem>
{ </Col>
required: true, <Col span={10}>
message: '请输入表前缀名', <FormItem {...formItemLayout} label="服务名">
}, {getFieldDecorator('serviceName', {
], rules: [
initialValue: detail.tablePrefix, {
})(<Input placeholder="请输入表前缀名" />)} required: true,
</FormItem> message: '请输入服务名',
<FormItem {...formItemLayout} label="主键名"> },
{getFieldDecorator('pkName', { ],
rules: [ initialValue: detail.serviceName,
{ })(<Input placeholder="请输入服务名" />)}
required: true, </FormItem>
message: '请输入主键名', </Col>
}, </Row>
], <Row gutter={24}>
initialValue: detail.pkName, <Col span={10}>
})(<Input placeholder="请输入主键名" />)} <FormItem {...formItemLayout} label="表名">
</FormItem> {getFieldDecorator('tableName', {
<FormItem {...formItemLayout} label="包名"> rules: [
{getFieldDecorator('packageName', { {
rules: [ required: true,
{ message: '请输入表名',
required: true, },
message: '请输入包名', ],
}, initialValue: detail.tableName,
], })(<Input placeholder="请输入表名" />)}
initialValue: detail.packageName, </FormItem>
})(<Input placeholder="请输入包名" />)} </Col>
</FormItem> <Col span={10}>
<FormItem {...formItemLayout} label="后端生成路径"> <FormItem {...formItemLayout} label="表前缀名">
{getFieldDecorator('apiPath', { {getFieldDecorator('tablePrefix', {
rules: [ rules: [
{ {
required: true, required: true,
message: '请输入后端生成路径', message: '请输入表前缀名',
}, },
], ],
initialValue: detail.apiPath, initialValue: detail.tablePrefix,
})(<Input placeholder="请输入后端生成路径" />)} })(<Input placeholder="请输入表前缀名" />)}
</FormItem> </FormItem>
<FormItem {...formItemLayout} label="前端生成路径"> </Col>
{getFieldDecorator('webPath', { </Row>
rules: [ <Row gutter={24}>
{ <Col span={10}>
required: true, <FormItem {...formItemLayout} label="主键名">
message: '请输入前端生成路径', {getFieldDecorator('pkName', {
}, rules: [
], {
initialValue: detail.webPath, required: true,
})(<Input placeholder="请输入前端生成路径" />)} message: '请输入主键名',
</FormItem> },
],
initialValue: detail.pkName,
})(<Input placeholder="请输入主键名" />)}
</FormItem>
</Col>
<Col span={10}>
<FormItem {...formItemLayout} label="包名">
{getFieldDecorator('packageName', {
rules: [
{
required: true,
message: '请输入包名',
},
],
initialValue: detail.packageName,
})(<Input placeholder="请输入包名" />)}
</FormItem>
</Col>
</Row>
<Row gutter={24}>
<Col span={10}>
<FormItem {...formItemLayout} label="基础业务">
{getFieldDecorator('baseMode', {
rules: [
{
required: true,
message: '请配置基础业务',
},
],
initialValue: detail.baseMode,
})(
<RadioGroup name="baseMode">
{category.map(d => (
<Radio key={d.dictKey} value={d.dictKey}>
{d.dictValue}
</Radio>
))}
</RadioGroup>
)}
</FormItem>
</Col>
<Col span={10}>
<FormItem {...formItemLayout} label="包装器">
{getFieldDecorator('wrapMode', {
rules: [
{
required: true,
message: '请配置包装器',
},
],
initialValue: detail.wrapMode,
})(
<RadioGroup name="wrapMode">
{category.map(d => (
<Radio key={d.dictKey} value={d.dictKey}>
{d.dictValue}
</Radio>
))}
</RadioGroup>
)}
</FormItem>
</Col>
</Row>
<Row gutter={24}>
<Col span={20}>
<FormItem {...formAllItemLayout} label="后端生成路径">
{getFieldDecorator('apiPath', {
rules: [
{
required: true,
message: '请输入后端生成路径',
},
],
initialValue: detail.apiPath,
})(<Input placeholder="请输入后端生成路径" />)}
</FormItem>
</Col>
</Row>
<Row gutter={24}>
<Col span={20}>
<FormItem {...formAllItemLayout} label="前端生成路径">
{getFieldDecorator('webPath', {
rules: [
{
required: true,
message: '请输入前端生成路径',
},
],
initialValue: detail.webPath,
})(<Input placeholder="请输入前端生成路径" />)}
</FormItem>
</Col>
</Row>
</Card> </Card>
</Form> </Form>
</Panel> </Panel>

View File

@ -1,6 +1,6 @@
import React, { PureComponent } from 'react'; import React, { PureComponent } from 'react';
import router from 'umi/router'; import router from 'umi/router';
import { Form, Card, Button } from 'antd'; import { Form, Card, Button, Row, Col } from 'antd';
import { connect } from 'dva'; import { connect } from 'dva';
import Panel from '../../../components/Panel'; import Panel from '../../../components/Panel';
import styles from '../../../layouts/Sword.less'; import styles from '../../../layouts/Sword.less';
@ -39,13 +39,19 @@ class CodeView extends PureComponent {
const formItemLayout = { const formItemLayout = {
labelCol: { labelCol: {
xs: { span: 24 }, span: 8,
sm: { span: 7 },
}, },
wrapperCol: { wrapperCol: {
xs: { span: 24 }, span: 16,
sm: { span: 12 }, },
md: { span: 10 }, };
const formAllItemLayout = {
labelCol: {
span: 4,
},
wrapperCol: {
span: 20,
}, },
}; };
@ -57,32 +63,58 @@ class CodeView extends PureComponent {
return ( return (
<Panel title="查看" back="/tool/code" action={action}> <Panel title="查看" back="/tool/code" action={action}>
<Form hideRequiredMark style={{ marginTop: 8 }}> <Form style={{ marginTop: 8 }}>
<Card className={styles.card} bordered={false}> <Card className={styles.card} bordered={false}>
<FormItem {...formItemLayout} label="模块名"> <Row gutter={24}>
<span>{detail.codeName}</span> <Col span={10}>
</FormItem> <FormItem {...formItemLayout} label="模块名">
<FormItem {...formItemLayout} label="服务名"> <span>{detail.codeName}</span>
<span>{detail.serviceName}</span> </FormItem>
</FormItem> </Col>
<FormItem {...formItemLayout} label="表名"> <Col span={10}>
<span>{detail.tableName}</span> <FormItem {...formItemLayout} label="服务名">
</FormItem> <span>{detail.serviceName}</span>
<FormItem {...formItemLayout} label="表前缀名"> </FormItem>
<span>{detail.tablePrefix}</span> </Col>
</FormItem> </Row>
<FormItem {...formItemLayout} label="主键名"> <Row gutter={24}>
<span>{detail.pkName}</span> <Col span={10}>
</FormItem> <FormItem {...formItemLayout} label="表名">
<FormItem {...formItemLayout} label="包名"> <span>{detail.tableName}</span>
<span>{detail.packageName}</span> </FormItem>
</FormItem> </Col>
<FormItem {...formItemLayout} label="后端生成路径"> <Col span={10}>
<span>{detail.apiPath}</span> <FormItem {...formItemLayout} label="表前缀名">
</FormItem> <span>{detail.tablePrefix}</span>
<FormItem {...formItemLayout} label="前端生成路径"> </FormItem>
<span>{detail.webPath}</span> </Col>
</FormItem> </Row>
<Row gutter={24}>
<Col span={10}>
<FormItem {...formItemLayout} label="主键名">
<span>{detail.pkName}</span>
</FormItem>
</Col>
<Col span={10}>
<FormItem {...formItemLayout} label="包名">
<span>{detail.packageName}</span>
</FormItem>
</Col>
</Row>
<Row gutter={24}>
<Col span={20}>
<FormItem {...formAllItemLayout} label="后端生成路径">
<span>{detail.apiPath}</span>
</FormItem>
</Col>
</Row>
<Row gutter={24}>
<Col span={20}>
<FormItem {...formAllItemLayout} label="前端生成路径">
<span>{detail.webPath}</span>
</FormItem>
</Col>
</Row>
</Card> </Card>
</Form> </Form>
</Panel> </Panel>

View File

@ -0,0 +1,91 @@
import React, { PureComponent } from 'react';
import { connect } from 'dva';
import { Button, Col, Form, Input, Row } from 'antd';
import Panel from '../../../components/Panel';
import { DATASOURCE_LIST } from '../../../actions/datasource';
import Grid from '../../../components/Sword/Grid';
const FormItem = Form.Item;
@connect(({ datasource, loading }) => ({
datasource,
loading: loading.models.datasource,
}))
@Form.create()
class DataSource extends PureComponent {
// ============ 查询 ===============
handleSearch = params => {
const { dispatch } = this.props;
dispatch(DATASOURCE_LIST(params));
};
// ============ 查询表单 ===============
renderSearchForm = onReset => {
const { form } = this.props;
const { getFieldDecorator } = form;
return (
<Row gutter={{ md: 8, lg: 24, xl: 48 }}>
<Col md={6} sm={24}>
<FormItem label="查询名称">
{getFieldDecorator('name')(<Input placeholder="查询名称" />)}
</FormItem>
</Col>
<Col>
<div style={{ float: 'right' }}>
<Button type="primary" htmlType="submit">
查询
</Button>
<Button style={{ marginLeft: 8 }} onClick={onReset}>
重置
</Button>
</div>
</Col>
</Row>
);
};
render() {
const code = 'datasource';
const {
form,
loading,
datasource: { data },
} = this.props;
const columns = [
{
title: '名称',
dataIndex: 'name',
},
{
title: '驱动类',
dataIndex: 'driverClass',
},
{
title: '用户名',
dataIndex: 'username',
},
{
title: '备注',
dataIndex: 'remark',
},
];
return (
<Panel>
<Grid
code={code}
form={form}
onSearch={this.handleSearch}
renderSearchForm={this.renderSearchForm}
loading={loading}
data={data}
columns={columns}
/>
</Panel>
);
}
}
export default DataSource;

View File

@ -0,0 +1,132 @@
import React, { PureComponent } from 'react';
import { Form, Input, Card, Button, Select } from 'antd';
import { connect } from 'dva';
import Panel from '../../../components/Panel';
import styles from '../../../layouts/Sword.less';
import { DATASOURCE_SUBMIT } from '../../../actions/datasource';
const FormItem = Form.Item;
@connect(({ loading }) => ({
submitting: loading.effects['datasource/submit'],
}))
@Form.create()
class DataSourceAdd extends PureComponent {
handleSubmit = e => {
e.preventDefault();
const { dispatch, form } = this.props;
form.validateFieldsAndScroll((err, values) => {
if (!err) {
dispatch(DATASOURCE_SUBMIT(values));
}
});
};
render() {
const {
form: { getFieldDecorator },
submitting,
} = this.props;
const formItemLayout = {
labelCol: {
xs: { span: 24 },
sm: { span: 7 },
},
wrapperCol: {
xs: { span: 24 },
sm: { span: 12 },
md: { span: 10 },
},
};
const action = (
<Button type="primary" onClick={this.handleSubmit} loading={submitting}>
提交
</Button>
);
return (
<Panel title="新增" back="/tool/datasource" action={action}>
<Form hideRequiredMark style={{ marginTop: 8 }}>
<Card className={styles.card} bordered={false}>
<FormItem {...formItemLayout} label="名称">
{getFieldDecorator('name', {
rules: [
{
required: true,
message: '请输入名称',
},
],
})(<Input placeholder="请输入名称" />)}
</FormItem>
<FormItem {...formItemLayout} label="驱动类">
{getFieldDecorator('driverClass', {
rules: [
{
required: true,
message: '请输入驱动类',
},
],
})(
<Select placeholder="请选择驱动类">
<Select.Option key={1} value="com.mysql.cj.jdbc.Driver">
com.mysql.cj.jdbc.Driver
</Select.Option>
<Select.Option key={2} value="org.postgresql.Driver">
org.postgresql.Driver
</Select.Option>
<Select.Option key={3} value="oracle.jdbc.OracleDriver">
oracle.jdbc.OracleDriver
</Select.Option>
</Select>
)}
</FormItem>
<FormItem {...formItemLayout} label="连接地址">
{getFieldDecorator('url', {
rules: [
{
required: true,
message: '请输入连接地址',
},
],
})(<Input placeholder="请输入连接地址" />)}
</FormItem>
<FormItem {...formItemLayout} label="用户名">
{getFieldDecorator('username', {
rules: [
{
required: true,
message: '请输入用户名',
},
],
})(<Input placeholder="请输入用户名" />)}
</FormItem>
<FormItem {...formItemLayout} label="密码">
{getFieldDecorator('password', {
rules: [
{
required: true,
message: '请输入密码',
},
],
})(<Input placeholder="请输入密码" />)}
</FormItem>
<FormItem {...formItemLayout} label="备注">
{getFieldDecorator('remark', {
rules: [
{
required: true,
message: '请输入备注',
},
],
})(<Input placeholder="请输入备注" />)}
</FormItem>
</Card>
</Form>
</Panel>
);
}
}
export default DataSourceAdd;

View File

@ -0,0 +1,161 @@
import React, { PureComponent } from 'react';
import { Form, Input, Card, Button, Select } from 'antd';
import { connect } from 'dva';
import Panel from '../../../components/Panel';
import styles from '../../../layouts/Sword.less';
import { DATASOURCE_DETAIL, DATASOURCE_SUBMIT } from '../../../actions/datasource';
const FormItem = Form.Item;
@connect(({ datasource, loading }) => ({
datasource,
submitting: loading.effects['datasource/submit'],
}))
@Form.create()
class DataSourceEdit extends PureComponent {
componentWillMount() {
const {
dispatch,
match: {
params: { id },
},
} = this.props;
dispatch(DATASOURCE_DETAIL(id));
}
handleSubmit = e => {
e.preventDefault();
const {
dispatch,
match: {
params: { id },
},
form,
} = this.props;
form.validateFieldsAndScroll((err, values) => {
if (!err) {
const params = {
id,
...values,
};
console.log(params);
dispatch(DATASOURCE_SUBMIT(params));
}
});
};
render() {
const {
form: { getFieldDecorator },
datasource: { detail },
submitting,
} = this.props;
const formItemLayout = {
labelCol: {
xs: { span: 24 },
sm: { span: 7 },
},
wrapperCol: {
xs: { span: 24 },
sm: { span: 12 },
md: { span: 10 },
},
};
const action = (
<Button type="primary" onClick={this.handleSubmit} loading={submitting}>
提交
</Button>
);
return (
<Panel title="修改" back="/tool/datasource" action={action}>
<Form hideRequiredMark style={{ marginTop: 8 }}>
<Card className={styles.card} bordered={false}>
<FormItem {...formItemLayout} label="名称">
{getFieldDecorator('name', {
rules: [
{
required: true,
message: '请输入名称',
},
],
initialValue: detail.name,
})(<Input placeholder="请输入名称" />)}
</FormItem>
<FormItem {...formItemLayout} label="驱动类">
{getFieldDecorator('driverClass', {
rules: [
{
required: true,
message: '请输入驱动类',
},
],
initialValue: detail.driverClass,
})(
<Select placeholder="请选择驱动类">
<Select.Option key={1} value="com.mysql.cj.jdbc.Driver">
com.mysql.cj.jdbc.Driver
</Select.Option>
<Select.Option key={2} value="org.postgresql.Driver">
org.postgresql.Driver
</Select.Option>
<Select.Option key={3} value="oracle.jdbc.OracleDriver">
oracle.jdbc.OracleDriver
</Select.Option>
</Select>
)}
</FormItem>
<FormItem {...formItemLayout} label="连接地址">
{getFieldDecorator('url', {
rules: [
{
required: true,
message: '请输入连接地址',
},
],
initialValue: detail.url,
})(<Input placeholder="请输入连接地址" />)}
</FormItem>
<FormItem {...formItemLayout} label="用户名">
{getFieldDecorator('username', {
rules: [
{
required: true,
message: '请输入用户名',
},
],
initialValue: detail.username,
})(<Input placeholder="请输入用户名" />)}
</FormItem>
<FormItem {...formItemLayout} label="密码">
{getFieldDecorator('password', {
rules: [
{
required: true,
message: '请输入密码',
},
],
initialValue: detail.password,
})(<Input placeholder="请输入密码" />)}
</FormItem>
<FormItem {...formItemLayout} label="备注">
{getFieldDecorator('remark', {
rules: [
{
required: true,
message: '请输入备注',
},
],
initialValue: detail.remark,
})(<Input placeholder="请输入备注" />)}
</FormItem>
</Card>
</Form>
</Panel>
);
}
}
export default DataSourceEdit;

View File

@ -0,0 +1,86 @@
import React, { PureComponent } from 'react';
import router from 'umi/router';
import { Form, Card, Button } from 'antd';
import { connect } from 'dva';
import Panel from '../../../components/Panel';
import styles from '../../../layouts/Sword.less';
import { DATASOURCE_DETAIL } from '../../../actions/datasource';
const FormItem = Form.Item;
@connect(({ datasource }) => ({
datasource,
}))
@Form.create()
class DataSourceView extends PureComponent {
componentWillMount() {
const {
dispatch,
match: {
params: { id },
},
} = this.props;
dispatch(DATASOURCE_DETAIL(id));
}
handleEdit = () => {
const {
match: {
params: { id },
},
} = this.props;
router.push(`/tool/datasource/edit/${id}`);
};
render() {
const {
datasource: { detail },
} = this.props;
const formItemLayout = {
labelCol: {
xs: { span: 24 },
sm: { span: 7 },
},
wrapperCol: {
xs: { span: 24 },
sm: { span: 12 },
md: { span: 10 },
},
};
const action = (
<Button type="primary" onClick={this.handleEdit}>
修改
</Button>
);
return (
<Panel title="查看" back="/tool/datasource" action={action}>
<Form hideRequiredMark style={{ marginTop: 8 }}>
<Card className={styles.card} bordered={false}>
<FormItem {...formItemLayout} label="名称">
<span>{detail.name}</span>
</FormItem>
<FormItem {...formItemLayout} label="驱动类">
<span>{detail.driverClass}</span>
</FormItem>
<FormItem {...formItemLayout} label="连接地址">
<span>{detail.url}</span>
</FormItem>
<FormItem {...formItemLayout} label="用户名">
<span>{detail.username}</span>
</FormItem>
<FormItem {...formItemLayout} label="密码">
<span>{detail.password}</span>
</FormItem>
<FormItem {...formItemLayout} label="备注">
<span>{detail.remark}</span>
</FormItem>
</Card>
</Form>
</Panel>
);
}
}
export default DataSourceView;

View File

@ -33,8 +33,8 @@ class Dept extends PureComponent {
</FormItem> </FormItem>
</Col> </Col>
<Col md={6} sm={24}> <Col md={6} sm={24}>
<FormItem label="租户编号"> <FormItem label="租户ID">
{getFieldDecorator('tenantCode')(<Input placeholder="请输入角色名称" />)} {getFieldDecorator('tenantId')(<Input placeholder="请输入角色名称" />)}
</FormItem> </FormItem>
</Col> </Col>
<Col md={6} sm={24}> <Col md={6} sm={24}>
@ -67,8 +67,8 @@ class Dept extends PureComponent {
const columns = [ const columns = [
{ {
title: '租户编号', title: '租户ID',
dataIndex: 'tenantCode', dataIndex: 'tenantId',
}, },
{ {
title: '部门名称', title: '部门名称',

View File

@ -124,8 +124,8 @@ class Role extends PureComponent {
</FormItem> </FormItem>
</Col> </Col>
<Col md={6} sm={24}> <Col md={6} sm={24}>
<FormItem label="租户编号"> <FormItem label="租户ID">
{getFieldDecorator('tenantCode')(<Input placeholder="请输入角色名称" />)} {getFieldDecorator('tenantId')(<Input placeholder="请输入角色名称" />)}
</FormItem> </FormItem>
</Col> </Col>
<Col md={6} sm={24}> <Col md={6} sm={24}>
@ -178,8 +178,8 @@ class Role extends PureComponent {
const columns = [ const columns = [
{ {
title: '租户编号', title: '租户ID',
dataIndex: 'tenantCode', dataIndex: 'tenantId',
}, },
{ {
title: '角色名称', title: '角色名称',

View File

@ -27,8 +27,8 @@ class Tenant extends PureComponent {
return ( return (
<Row gutter={{ md: 8, lg: 24, xl: 48 }}> <Row gutter={{ md: 8, lg: 24, xl: 48 }}>
<Col md={6} sm={24}> <Col md={6} sm={24}>
<FormItem label="租户编号"> <FormItem label="租户ID">
{getFieldDecorator('tenantCode')(<Input placeholder="请输入租户编号" />)} {getFieldDecorator('tenantId')(<Input placeholder="请输入租户ID" />)}
</FormItem> </FormItem>
</Col> </Col>
<Col md={6} sm={24}> <Col md={6} sm={24}>
@ -66,8 +66,8 @@ class Tenant extends PureComponent {
const columns = [ const columns = [
{ {
title: '租户编号', title: '租户ID',
dataIndex: 'tenantCode', dataIndex: 'tenantId',
}, },
{ {
title: '租户名称', title: '租户名称',

View File

@ -59,8 +59,8 @@ class TenantView extends PureComponent {
<Panel title="查看" back="/system/tenant" action={action}> <Panel title="查看" back="/system/tenant" action={action}>
<Form hideRequiredMark style={{ marginTop: 8 }}> <Form hideRequiredMark style={{ marginTop: 8 }}>
<Card className={styles.card} bordered={false}> <Card className={styles.card} bordered={false}>
<FormItem {...formItemLayout} label="租户编号"> <FormItem {...formItemLayout} label="租户ID">
<span>{detail.tenantCode}</span> <span>{detail.tenantId}</span>
</FormItem> </FormItem>
<FormItem {...formItemLayout} label="租户名称"> <FormItem {...formItemLayout} label="租户名称">
<span>{detail.tenantName}</span> <span>{detail.tenantName}</span>

View File

@ -175,8 +175,8 @@ class User extends PureComponent {
const columns = [ const columns = [
{ {
title: '租户编号', title: '租户ID',
dataIndex: 'tenantCode', dataIndex: 'tenantId',
}, },
{ {
title: '登录账号', title: '登录账号',

View File

@ -46,7 +46,7 @@ class UserAdd extends PureComponent {
handleChange = value => { handleChange = value => {
const { dispatch, form } = this.props; const { dispatch, form } = this.props;
form.resetFields(['roleId', 'deptId']); form.resetFields(['roleId', 'deptId']);
dispatch(USER_CHANGE_INIT({ tenantCode: value })); dispatch(USER_CHANGE_INIT({ tenantId: value }));
}; };
render() { render() {
@ -104,7 +104,7 @@ class UserAdd extends PureComponent {
<Row gutter={24}> <Row gutter={24}>
<Col span={20}> <Col span={20}>
<FormItem {...formAllItemLayout} label="所属租户"> <FormItem {...formAllItemLayout} label="所属租户">
{getFieldDecorator('tenantCode', { {getFieldDecorator('tenantId', {
rules: [ rules: [
{ {
required: true, required: true,
@ -122,7 +122,7 @@ class UserAdd extends PureComponent {
placeholder="请选择所属租户" placeholder="请选择所属租户"
> >
{tenantList.map(d => ( {tenantList.map(d => (
<Select.Option key={d.tenantCode} value={d.tenantCode}> <Select.Option key={d.tenantId} value={d.tenantId}>
{d.tenantName} {d.tenantName}
</Select.Option> </Select.Option>
))} ))}

View File

@ -5,7 +5,7 @@ import { connect } from 'dva';
import Panel from '../../../components/Panel'; import Panel from '../../../components/Panel';
import func from '../../../utils/Func'; import func from '../../../utils/Func';
import styles from '../../../layouts/Sword.less'; import styles from '../../../layouts/Sword.less';
import { USER_CHANGE_INIT, USER_DETAIL, USER_INIT, USER_SUBMIT } from '../../../actions/user'; import { USER_CHANGE_INIT, USER_DETAIL, USER_INIT, USER_UPDATE } from '../../../actions/user';
import { tenantMode } from '../../../defaultSettings'; import { tenantMode } from '../../../defaultSettings';
const FormItem = Form.Item; const FormItem = Form.Item;
@ -45,7 +45,7 @@ class UserEdit extends PureComponent {
deptId: func.join(values.deptId), deptId: func.join(values.deptId),
birthday: func.format(values.birthday), birthday: func.format(values.birthday),
}; };
dispatch(USER_SUBMIT(params)); dispatch(USER_UPDATE(params));
} }
}); });
}; };
@ -53,7 +53,7 @@ class UserEdit extends PureComponent {
handleChange = value => { handleChange = value => {
const { dispatch, form } = this.props; const { dispatch, form } = this.props;
form.resetFields(['roleId', 'deptId']); form.resetFields(['roleId', 'deptId']);
dispatch(USER_CHANGE_INIT({ tenantCode: value })); dispatch(USER_CHANGE_INIT({ tenantId: value }));
}; };
render() { render() {
@ -113,14 +113,14 @@ class UserEdit extends PureComponent {
<Row gutter={24}> <Row gutter={24}>
<Col span={20}> <Col span={20}>
<FormItem {...formAllItemLayout} label="所属租户"> <FormItem {...formAllItemLayout} label="所属租户">
{getFieldDecorator('tenantCode', { {getFieldDecorator('tenantId', {
rules: [ rules: [
{ {
required: true, required: true,
message: '请选择所属租户', message: '请选择所属租户',
}, },
], ],
initialValue: detail.tenantCode, initialValue: detail.tenantId,
})( })(
<Select <Select
showSearch showSearch
@ -132,7 +132,7 @@ class UserEdit extends PureComponent {
placeholder="请选择所属租户" placeholder="请选择所属租户"
> >
{tenantList.map(d => ( {tenantList.map(d => (
<Select.Option key={d.tenantCode} value={d.tenantCode}> <Select.Option key={d.tenantId} value={d.tenantId}>
{d.tenantName} {d.tenantName}
</Select.Option> </Select.Option>
))} ))}

View File

@ -77,7 +77,7 @@ class UserView extends PureComponent {
<Row gutter={24}> <Row gutter={24}>
<Col span={20}> <Col span={20}>
<FormItem {...formAllItemLayout} label="所属租户"> <FormItem {...formAllItemLayout} label="所属租户">
<span>{detail.tenantCode}</span> <span>{detail.tenantId}</span>
</FormItem> </FormItem>
</Col> </Col>
</Row> </Row>

View File

@ -32,3 +32,10 @@ export async function genCodes(params) {
body: func.toFormData(params), body: func.toFormData(params),
}); });
} }
export async function copyCodes(params) {
return request('/api/blade-develop/code/copy', {
method: 'POST',
body: func.toFormData(params),
});
}

View File

@ -0,0 +1,29 @@
import { stringify } from 'qs';
import func from '../utils/Func';
import request from '../utils/request';
export async function list(params) {
return request(`/api/blade-develop/datasource/list?${stringify(params)}`);
}
export async function select(params) {
return request(`/api/blade-develop/datasource/select?${stringify(params)}`);
}
export async function submit(params) {
return request('/api/blade-develop/datasource/submit', {
method: 'POST',
body: params,
});
}
export async function detail(params) {
return request(`/api/blade-develop/datasource/detail?${stringify(params)}`);
}
export async function remove(params) {
return request('/api/blade-develop/datasource/remove', {
method: 'POST',
body: func.toFormData(params),
});
}

View File

@ -51,6 +51,24 @@ export async function submit(params) {
}); });
} }
export async function update(params) {
return request('/api/blade-user/update', {
method: 'POST',
body: params,
});
}
export async function detail(params) { export async function detail(params) {
return request(`/api/blade-user/detail?${stringify(params)}`); return request(`/api/blade-user/detail?${stringify(params)}`);
} }
export async function getUserInfo() {
return request('/api/blade-user/info');
}
export async function updatePassword(params) {
return request('/api/blade-user/update-password', {
method: 'POST',
body: func.toFormData(params),
});
}