diff --git a/README.md b/README.md index 1bbb96a..d71c3d0 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,15 @@ ## 简介 Sword 是 [SpringBlade](https://gitee.com/smallc/SpringBlade)前端UI项目,基于react 、ant design、dva,用于快速构建系统中后台业务。 +## 文档 +* 文档地址:[Sword开发手册](https://www.kancloud.cn/smallchill/sword) + ## 官网 * 官网地址:[https://bladex.vip](https://bladex.vip) ## 在线演示 * Sword演示地址:[https://sword.bladex.vip](https://sword.bladex.vip) -* Saber演示地址:[https://saber.avue.top](https://saber.avue.top) +* Saber演示地址:[https://saber.bladex.vip](https://saber.bladex.vip) ## 后端项目地址 * [Gitee地址](https://gitee.com/smallc/SpringBlade) diff --git a/config/router.config.js b/config/router.config.js index 6a6733b..8fc624d 100644 --- a/config/router.config.js +++ b/config/router.config.js @@ -15,7 +15,7 @@ export default [ path: '/', component: '../layouts/BasicLayout', Routes: ['src/pages/Authorized'], - authority: ['admin', 'user'], + authority: ['administrator', 'admin', 'user', 'test'], routes: [ // dashboard { path: '/', redirect: '/dashboard/workplace' }, @@ -156,6 +156,16 @@ export default [ { path: '/system/param/view/:id', component: './System/Param/ParamView' }, ], }, + { + path: '/system/tenant', + routes: [ + { path: '/system/tenant', redirect: '/system/tenant/list' }, + { path: '/system/tenant/list', component: './System/Tenant/Tenant' }, + { path: '/system/tenant/add', component: './System/Tenant/TenantAdd' }, + { path: '/system/tenant/edit/:id', component: './System/Tenant/TenantEdit' }, + { path: '/system/tenant/view/:id', component: './System/Tenant/TenantView' }, + ], + }, ], }, { diff --git a/mock/dept.js b/mock/dept.js index 355ad80..64d1755 100644 --- a/mock/dept.js +++ b/mock/dept.js @@ -6,18 +6,21 @@ function getFakeList(req, res) { data.push({ id: '1', deptName: '刀锋科技', + tenantCode: '000000', fullName: '江苏刀锋科技有限公司', sort: '1', children: [ { id: '2', deptName: '常州刀锋', + tenantCode: '000000', fullName: '常州刀锋科技有限公司', sort: '1', }, { id: '3', deptName: '南京刀锋', + tenantCode: '000000', fullName: '南京刀锋科技有限公司', sort: '2', }, @@ -33,6 +36,7 @@ function getFakeDetail(req, res) { id: 2, parentId: 1, parentName: '江苏刀锋', + tenantCode: '000000', deptName: '常州刀锋', fullName: '常州刀锋科技有限公司', sort: 1, diff --git a/mock/menu.js b/mock/menu.js index 8478fb5..e79ae7e 100644 --- a/mock/menu.js +++ b/mock/menu.js @@ -54,6 +54,11 @@ function getFakeRoutes(req, res) { code: 'param', name: '参数管理', }, + { + path: '/system/tenant', + code: 'tenant', + name: '租户管理', + }, ], }, { @@ -397,6 +402,43 @@ function getFakeButtons(req, res) { }, ], }, + { + code: 'tenant', + children: [ + { + code: 'tenant_add', + name: '新增', + path: '/system/tenant/add', + source: 'plus', + action: 1, + alias: 'add', + }, + { + code: 'tenant_edit', + name: '修改', + path: '/system/tenant/edit', + source: 'form', + action: 2, + alias: 'edit', + }, + { + code: 'tenant_delete', + name: '删除', + path: '/api/blade-system/tenant/remove', + source: 'delete', + action: 3, + alias: 'delete', + }, + { + code: 'tenant_view', + name: '查看', + path: '/system/tenant/view', + source: 'file-text', + action: 2, + alias: 'view', + }, + ], + }, { code: 'log_usual', children: [ diff --git a/mock/notice.js b/mock/notice.js index cc936a2..0bbf6da 100644 --- a/mock/notice.js +++ b/mock/notice.js @@ -15,105 +15,105 @@ const proxy = { title: '博客标题1', categoryName: '批转通知', content: '博客内容1', - date: '2018-05-08 12:00:00', + releaseTime: '2018-05-08 12:00:00', }, { id: '2', title: '博客标题2', categoryName: '发布通知', content: '博客内容2', - date: '2018-06-08 12:00:00', + releaseTime: '2018-06-08 12:00:00', }, { id: '3', title: '博客标题3', categoryName: '任免通知', content: '博客内容3', - date: '2018-07-08 12:00:00', + releaseTime: '2018-07-08 12:00:00', }, { id: '4', title: '博客标题4', categoryName: '指示通知', content: '博客内容4', - date: '2018-08-08 12:00:00', + releaseTime: '2018-08-08 12:00:00', }, { id: '5', title: '博客标题5', categoryName: '转发通知', content: '博客内容5', - date: '2018-09-08 12:00:00', + releaseTime: '2018-09-08 12:00:00', }, { id: '6', title: '博客标题1', categoryName: '批转通知', content: '博客内容1', - date: '2018-05-08 12:00:00', + releaseTime: '2018-05-08 12:00:00', }, { id: '7', title: '博客标题2', categoryName: '发布通知', content: '博客内容2', - date: '2018-06-08 12:00:00', + releaseTime: '2018-06-08 12:00:00', }, { id: '8', title: '博客标题3', categoryName: '任免通知', content: '博客内容3', - date: '2018-07-08 12:00:00', + releaseTime: '2018-07-08 12:00:00', }, { id: '9', title: '博客标题4', categoryName: '指示通知', content: '博客内容4', - date: '2018-08-08 12:00:00', + releaseTime: '2018-08-08 12:00:00', }, { id: '10', title: '博客标题5', categoryName: '转发通知', content: '博客内容5', - date: '2018-09-08 12:00:00', + releaseTime: '2018-09-08 12:00:00', }, { id: '11', title: '博客标题1', categoryName: '批转通知', content: '博客内容1', - date: '2018-05-08 12:00:00', + releaseTime: '2018-05-08 12:00:00', }, { id: '12', title: '博客标题2', categoryName: '发布通知', content: '博客内容2', - date: '2018-06-08 12:00:00', + releaseTime: '2018-06-08 12:00:00', }, { id: '13', title: '博客标题3', categoryName: '任免通知', content: '博客内容3', - date: '2018-07-08 12:00:00', + releaseTime: '2018-07-08 12:00:00', }, { id: '14', title: '博客标题4', categoryName: '指示通知', content: '博客内容4', - date: '2018-08-08 12:00:00', + releaseTime: '2018-08-08 12:00:00', }, { id: '15', title: '博客标题5', categoryName: '转发通知', content: '博客内容5', - date: '2018-09-08 12:00:00', + releaseTime: '2018-09-08 12:00:00', }, ], }, @@ -138,7 +138,7 @@ const proxy = { title: '通知标题详情', category: '3', categoryName: '转发通知', - date: '2018-12-31 23:33:33', + releaseTime: '2018-12-31 23:33:33', content: '通知公告内容详情', }, message: 'success', diff --git a/mock/role.js b/mock/role.js index 96e80e1..0cdb98b 100644 --- a/mock/role.js +++ b/mock/role.js @@ -7,12 +7,14 @@ function getFakeList(req, res) { { id: '1', roleName: '超级管理员', + tenantCode: '000000', roleAlias: 'administrator', sort: '1', children: [ { id: '2', roleName: '管理员', + tenantCode: '000001', roleAlias: 'admin', sort: '1', }, @@ -21,18 +23,21 @@ function getFakeList(req, res) { { id: '3', roleName: '用户', + tenantCode: '000002', roleAlias: 'user', sort: '2', children: [ { id: '4', roleName: '普通用户', + tenantCode: '000003', roleAlias: 'user', sort: '1', }, { id: '5', roleName: '访客', + tenantCode: '000004', roleAlias: 'guest', sort: '2', }, @@ -49,6 +54,7 @@ function getFakeDetail(req, res) { id: 2, parentId: 1, parentName: '超级管理员', + tenantCode: '000000', roleName: '用户', roleAlias: 'user', sort: 1, diff --git a/mock/tenant.js b/mock/tenant.js new file mode 100644 index 0000000..ce4da02 --- /dev/null +++ b/mock/tenant.js @@ -0,0 +1,75 @@ +import { delay } from 'roadhog-api-doc'; + +function getFakeList(req, res) { + const json = { code: 200, success: true, msg: '操作成功' }; + const list = []; + list.push( + { + id: '1', + tenantCode: '000000', + tenantName: '管理组', + linkman: 'Chill', + contactNumber: '66666666666', + address: '管理组地址', + }, + { + id: '2', + tenantCode: '000001', + tenantName: '用户组', + linkman: 'Bill', + contactNumber: '23333333333', + address: '用户组地址', + } + ); + json.data = { + total: 10, + size: 10, + current: 2, + searchCount: true, + pages: 1, + records: list, + }; + return res.json(json); +} + +function getFakeDetail(req, res) { + const json = { code: 200, success: true, msg: '操作成功' }; + json.data = { + id: '1', + tenantCode: '000000', + tenantName: '管理组', + linkman: 'Chill', + contactNumber: '66666666666', + address: '管理组地址', + }; + return res.json(json); +} + +function fakeSuccess(req, res) { + const json = { code: 200, success: true, msg: '操作成功' }; + return res.json(json); +} + +function getFakeTenantSelect(req, res) { + const json = { code: 200, success: true, msg: '操作成功' }; + json.data = [ + { + tenantCode: '000000', + tenantName: '管理组', + }, + { + tenantCode: '000001', + tenantName: '用户组', + }, + ]; + return res.json(json); +} + +const proxy = { + 'GET /api/blade-system/tenant/list': getFakeList, + 'GET /api/blade-system/tenant/select': getFakeTenantSelect, + 'GET /api/blade-system/tenant/detail': getFakeDetail, + 'POST /api/blade-system/tenant/submit': fakeSuccess, + 'POST /api/blade-system/tenant/remove': fakeSuccess, +}; +export default delay(proxy, 500); diff --git a/mock/user.js b/mock/user.js index bf8e928..caf7ebe 100644 --- a/mock/user.js +++ b/mock/user.js @@ -6,6 +6,7 @@ function getFakeList(req, res) { list.push( { id: '1', + tenantCode: '000000', account: 'admin', name: '超级管理员', realName: '管理员', @@ -17,6 +18,7 @@ function getFakeList(req, res) { }, { id: '2', + tenantCode: '000001', account: 'user', name: '系统用户', realName: '用户', @@ -40,8 +42,9 @@ function getFakeList(req, res) { function getFakeDetail(req, res) { const json = { code: 200, success: true, msg: '操作成功' }; - const detail = { + json.data = { id: '1', + tenantCode: '000000', account: 'admin', name: '超级管理员', realName: '管理员', @@ -56,7 +59,6 @@ function getFakeDetail(req, res) { birthday: '2018-12-31 23:33:33', statusName: '启用', }; - json.data = detail; return res.json(json); } diff --git a/src/actions/tenant.js b/src/actions/tenant.js new file mode 100644 index 0000000..dbf253c --- /dev/null +++ b/src/actions/tenant.js @@ -0,0 +1,36 @@ +export const TENANT_NAMESPACE = 'tenant'; + +export function TENANT_LIST(payload) { + return { + type: `${TENANT_NAMESPACE}/fetchList`, + payload, + }; +} + +export function TENANT_DETAIL(id) { + return { + type: `${TENANT_NAMESPACE}/fetchDetail`, + payload: { id }, + }; +} + +export function TENANT_CLEAR_DETAIL() { + return { + type: `${TENANT_NAMESPACE}/clearDetail`, + payload: {}, + }; +} + +export function TENANT_SUBMIT(payload) { + return { + type: `${TENANT_NAMESPACE}/submit`, + payload, + }; +} + +export function TENANT_REMOVE(payload) { + return { + type: `${TENANT_NAMESPACE}/remove`, + payload, + }; +} diff --git a/src/actions/user.js b/src/actions/user.js index afd683f..36e396f 100644 --- a/src/actions/user.js +++ b/src/actions/user.js @@ -14,6 +14,13 @@ export function USER_INIT() { }; } +export function USER_CHANGE_INIT(payload) { + return { + type: `${USER_NAMESPACE}/fetchChangeInit`, + payload, + }; +} + export function USER_DETAIL(id) { return { type: `${USER_NAMESPACE}/fetchDetail`, diff --git a/src/components/Login/index.d.ts b/src/components/Login/index.d.ts index 6a6f67f..e4588fe 100644 --- a/src/components/Login/index.d.ts +++ b/src/components/Login/index.d.ts @@ -12,6 +12,7 @@ export interface ILoginProps { export default class Login extends React.Component { public static Tab: typeof LoginTab; + public static TenantCode: typeof LoginItem; public static UserName: typeof LoginItem; public static Password: typeof LoginItem; public static Mobile: typeof LoginItem; diff --git a/src/components/Login/map.js b/src/components/Login/map.js index dfa8819..850b79b 100644 --- a/src/components/Login/map.js +++ b/src/components/Login/map.js @@ -3,6 +3,20 @@ import { Icon } from 'antd'; import styles from './index.less'; export default { + TenantCode: { + props: { + size: 'large', + id: 'tenantCode', + prefix: , + placeholder: 'admin', + }, + rules: [ + { + required: true, + message: 'Please enter tenantcode!', + }, + ], + }, UserName: { props: { size: 'large', diff --git a/src/components/Sword/ToolBar.js b/src/components/Sword/ToolBar.js index 351df36..67cdacc 100644 --- a/src/components/Sword/ToolBar.js +++ b/src/components/Sword/ToolBar.js @@ -30,10 +30,10 @@ export default class ToolBar extends PureComponent { ))} {renderLeftButton ? renderLeftButton() : null} + {renderRightButton ? ( +
{renderRightButton()}
+ ) : null} - {renderRightButton ? ( -
renderRightButton()
- ) : null} ); } diff --git a/src/layouts/UserLayout.js b/src/layouts/UserLayout.js index 5b60b86..151f09e 100644 --- a/src/layouts/UserLayout.js +++ b/src/layouts/UserLayout.js @@ -51,10 +51,12 @@ const UserLayout = ({ children }) => (
logo - Ant Design + Sword 企业级开发平台
-
Ant Design 是西湖区最具影响力的 Web 设计规范
+
+ Sword是SpringBlade前端UI项目,基于react 、ant design、umi、dva等流行技术栈。 +
{children} diff --git a/src/locales/en-US/login.js b/src/locales/en-US/login.js index 82c1c20..a7bc864 100644 --- a/src/locales/en-US/login.js +++ b/src/locales/en-US/login.js @@ -1,4 +1,5 @@ export default { + 'app.login.tenantCode': 'tenantCode', 'app.login.userName': 'userName', 'app.login.password': 'password', 'app.login.message-invalid-credentials': 'Invalid username or password(admin/ant.design)', @@ -20,6 +21,7 @@ export default { 'app.register-result.view-mailbox': 'View mailbox', 'validation.email.required': 'Please enter your email!', 'validation.email.wrong-format': 'The email address is in the wrong format!', + 'validation.userName.tenantCode': 'Please enter your tenantCode!', 'validation.userName.required': 'Please enter your userName!', 'validation.password.required': 'Please enter your password!', 'validation.password.twice': 'The passwords entered twice do not match!', diff --git a/src/locales/en-US/menu.js b/src/locales/en-US/menu.js index 86fa122..194b0b2 100644 --- a/src/locales/en-US/menu.js +++ b/src/locales/en-US/menu.js @@ -13,6 +13,7 @@ export default { 'menu.system.menu': 'menu', 'menu.system.role': 'role', 'menu.system.param': 'parameter', + 'menu.system.tenant': 'tenant', 'menu.monitor': 'monitor', 'menu.monitor.log': 'log', 'menu.monitor.log.log_usual': 'usual log', diff --git a/src/locales/zh-CN/login.js b/src/locales/zh-CN/login.js index eb22720..67696d6 100644 --- a/src/locales/zh-CN/login.js +++ b/src/locales/zh-CN/login.js @@ -1,4 +1,5 @@ export default { + 'app.login.tenantCode': '租户编号', 'app.login.userName': '用户名', 'app.login.password': '密码', 'app.login.message-invalid-credentials': '账户或密码错误(admin/ant.design)', @@ -20,6 +21,7 @@ export default { 'app.register-result.view-mailbox': '查看邮箱', 'validation.email.required': '请输入邮箱地址!', 'validation.email.wrong-format': '邮箱地址格式错误!', + 'validation.tenantCode.required': '请输入租户编号!', 'validation.userName.required': '请输入用户名!', 'validation.password.required': '请输入密码!', 'validation.password.twice': '两次输入的密码不匹配!', diff --git a/src/locales/zh-CN/menu.js b/src/locales/zh-CN/menu.js index c2a0c5d..5e3f773 100644 --- a/src/locales/zh-CN/menu.js +++ b/src/locales/zh-CN/menu.js @@ -13,6 +13,7 @@ export default { 'menu.system.menu': '菜单管理', 'menu.system.role': '角色管理', 'menu.system.param': '参数管理', + 'menu.system.tenant': '租户管理', 'menu.monitor': '系统监控', 'menu.monitor.log': '日志管理', 'menu.monitor.log.log_usual': '通用日志', diff --git a/src/locales/zh-TW/login.js b/src/locales/zh-TW/login.js index ec5706a..2505951 100644 --- a/src/locales/zh-TW/login.js +++ b/src/locales/zh-TW/login.js @@ -1,4 +1,5 @@ export default { + 'app.login.tenantCode': '租戶編號', 'app.login.userName': '賬戶', 'app.login.password': '密碼', 'app.login.message-invalid-credentials': '賬戶或密碼錯誤(admin/ant.design)', @@ -20,6 +21,7 @@ export default { 'app.register-result.view-mailbox': '查看郵箱', 'validation.email.required': '請輸入郵箱地址!', 'validation.email.wrong-format': '郵箱地址格式錯誤!', + 'validation.tenantCode.required': '請輸入租戶編號!', 'validation.userName.required': '請輸入賬戶!', 'validation.password.required': '請輸入密碼!', 'validation.password.twice': '兩次輸入的密碼不匹配!', diff --git a/src/locales/zh-TW/menu.js b/src/locales/zh-TW/menu.js index 40149cc..ff3c00b 100644 --- a/src/locales/zh-TW/menu.js +++ b/src/locales/zh-TW/menu.js @@ -13,6 +13,7 @@ export default { 'menu.system.menu': '菜單管理', 'menu.system.role': '角色管理', 'menu.system.param': '參數管理', + 'menu.system.tenant': '租戶管理', 'menu.monitor': '系統監控', 'menu.monitor.log': '日志管理', 'menu.monitor.log.log_usual': '通用日志', diff --git a/src/models/menu.js b/src/models/menu.js index 9c4ed69..5de8afb 100644 --- a/src/models/menu.js +++ b/src/models/menu.js @@ -18,6 +18,94 @@ import { getRoutes, setRoutes, getButtons, setButtons } from '../utils/authority import { MENU_NAMESPACE } from '../actions/menu'; import { formatRoutes, formatButtons } from '../utils/utils'; +const { check } = Authorized; + +// Conversion router to menu. +function formatter(data, parentAuthority, parentName) { + return data + .map(item => { + if (!item.name || !item.path) { + return null; + } + + let locale = 'menu'; + if (parentName) { + locale = `${parentName}.${item.name}`; + } else { + locale = `menu.${item.name}`; + } + // if enableMenuLocale use item.name, + // close menu international + const name = menu.disableLocal + ? item.name + : formatMessage({ id: locale, defaultMessage: item.name }); + const result = { + ...item, + name, + locale, + authority: item.authority || parentAuthority, + }; + if (item.routes) { + const children = formatter(item.routes, item.authority, locale); + // Reduce memory usage + result.children = children; + } + delete result.routes; + return result; + }) + .filter(item => item); +} + +const memoizeOneFormatter = memoizeOne(formatter, isEqual); + +/** + * get SubMenu or Item + */ +const getSubMenu = item => { + // doc: add hideChildrenInMenu + if (item.children && !item.hideChildrenInMenu && item.children.some(child => child.name)) { + return { + ...item, + children: filterMenuData(item.children), // eslint-disable-line + }; + } + return item; +}; + +/** + * filter menuData + */ +const filterMenuData = menuData => { + if (!menuData) { + return []; + } + return menuData + .filter(item => item.name && !item.hideInMenu) + .map(item => check(item.authority, getSubMenu(item))) + .filter(item => item); +}; +/** + * 获取面包屑映射 + * @param {Object} menuData 菜单配置 + */ +const getBreadcrumbNameMap = menuData => { + const routerMap = {}; + + const flattenMenuData = data => { + data.forEach(menuItem => { + if (menuItem.children) { + flattenMenuData(menuItem.children); + } + // Reduce memory usage + routerMap[menuItem.path] = menuItem; + }); + }; + flattenMenuData(menuData); + return routerMap; +}; + +const memoizeOneGetBreadcrumbNameMap = memoizeOne(getBreadcrumbNameMap, isEqual); + export default { namespace: MENU_NAMESPACE, @@ -165,91 +253,3 @@ export default { }, }, }; - -const { check } = Authorized; - -// Conversion router to menu. -function formatter(data, parentAuthority, parentName) { - return data - .map(item => { - if (!item.name || !item.path) { - return null; - } - - let locale = 'menu'; - if (parentName) { - locale = `${parentName}.${item.name}`; - } else { - locale = `menu.${item.name}`; - } - // if enableMenuLocale use item.name, - // close menu international - const name = menu.disableLocal - ? item.name - : formatMessage({ id: locale, defaultMessage: item.name }); - const result = { - ...item, - name, - locale, - authority: item.authority || parentAuthority, - }; - if (item.routes) { - const children = formatter(item.routes, item.authority, locale); - // Reduce memory usage - result.children = children; - } - delete result.routes; - return result; - }) - .filter(item => item); -} - -const memoizeOneFormatter = memoizeOne(formatter, isEqual); - -/** - * get SubMenu or Item - */ -const getSubMenu = item => { - // doc: add hideChildrenInMenu - if (item.children && !item.hideChildrenInMenu && item.children.some(child => child.name)) { - return { - ...item, - children: filterMenuData(item.children), // eslint-disable-line - }; - } - return item; -}; - -/** - * filter menuData - */ -const filterMenuData = menuData => { - if (!menuData) { - return []; - } - return menuData - .filter(item => item.name && !item.hideInMenu) - .map(item => check(item.authority, getSubMenu(item))) - .filter(item => item); -}; -/** - * 获取面包屑映射 - * @param {Object} menuData 菜单配置 - */ -const getBreadcrumbNameMap = menuData => { - const routerMap = {}; - - const flattenMenuData = data => { - data.forEach(menuItem => { - if (menuItem.children) { - flattenMenuData(menuItem.children); - } - // Reduce memory usage - routerMap[menuItem.path] = menuItem; - }); - }; - flattenMenuData(menuData); - return routerMap; -}; - -const memoizeOneGetBreadcrumbNameMap = memoizeOne(getBreadcrumbNameMap, isEqual); diff --git a/src/models/tenant.js b/src/models/tenant.js new file mode 100644 index 0000000..a49e163 --- /dev/null +++ b/src/models/tenant.js @@ -0,0 +1,87 @@ +import { message } from 'antd'; +import router from 'umi/router'; +import { TENANT_NAMESPACE } from '../actions/tenant'; +import { list, submit, detail, remove } from '../services/tenant'; + +export default { + namespace: TENANT_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('/system/tenant'); + } + }, + *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: {}, + }; + }, + }, +}; diff --git a/src/models/user.js b/src/models/user.js index 96d5b40..2c9ef01 100644 --- a/src/models/user.js +++ b/src/models/user.js @@ -2,6 +2,7 @@ import { message } from 'antd'; import router from 'umi/router'; import { USER_NAMESPACE } from '../actions/user'; import { query as queryUsers, list, submit, detail, remove, grant } from '../services/user'; +import { select as tenants } from '../services/tenant'; import { tree as roles } from '../services/role'; import { tree as depts } from '../services/dept'; import { getCurrentUser } from '../utils/authority'; @@ -19,6 +20,7 @@ export default { init: { roleTree: [], deptTree: [], + tenantList: [], }, detail: {}, }, @@ -57,9 +59,24 @@ export default { *fetchInit({ payload }, { call, put }) { const responseRole = yield call(roles, payload); const responseDept = yield call(depts, payload); - if (responseRole.success && responseDept.success) { + const responseTenant = yield call(tenants, payload); + if (responseRole.success && responseDept.success && responseTenant.success) { yield put({ type: 'saveInit', + payload: { + roleTree: responseRole.data, + deptTree: responseDept.data, + tenantList: responseTenant.data, + }, + }); + } + }, + *fetchChangeInit({ payload }, { call, put }) { + const responseRole = yield call(roles, payload); + const responseDept = yield call(depts, payload); + if (responseRole.success && responseDept.success) { + yield put({ + type: 'saveChangeInit', payload: { roleTree: responseRole.data, deptTree: responseDept.data, @@ -146,6 +163,14 @@ export default { init: action.payload, }; }, + saveChangeInit(state, action) { + const newState = state; + newState.init.roleTree = action.payload.roleTree; + newState.init.deptTree = action.payload.deptTree; + return { + ...newState, + }; + }, saveDetail(state, action) { return { ...state, diff --git a/src/pages/Desk/Notice/Notice.js b/src/pages/Desk/Notice/Notice.js index cf226ab..7bf7f04 100644 --- a/src/pages/Desk/Notice/Notice.js +++ b/src/pages/Desk/Notice/Notice.js @@ -122,7 +122,7 @@ class Notice extends PureComponent { }, { title: formatMessage({ id: 'desk.notice.date' }), - dataIndex: 'date', + dataIndex: 'releaseTime', }, ]; diff --git a/src/pages/Desk/Notice/NoticeAdd.js b/src/pages/Desk/Notice/NoticeAdd.js index 4fc86b9..34e3892 100644 --- a/src/pages/Desk/Notice/NoticeAdd.js +++ b/src/pages/Desk/Notice/NoticeAdd.js @@ -29,7 +29,7 @@ class NoticeAdd extends PureComponent { const params = { ...values, - date: func.format(values.date), + releaseTime: func.format(values.releaseTime), }; dispatch(NOTICE_SUBMIT(params)); @@ -100,7 +100,7 @@ class NoticeAdd extends PureComponent { )} }> - {getFieldDecorator('date', { + {getFieldDecorator('releaseTime', { rules: [ { required: true, diff --git a/src/pages/Desk/Notice/NoticeEdit.js b/src/pages/Desk/Notice/NoticeEdit.js index 5e778f9..73365cd 100644 --- a/src/pages/Desk/Notice/NoticeEdit.js +++ b/src/pages/Desk/Notice/NoticeEdit.js @@ -41,7 +41,7 @@ class NoticeAdd extends PureComponent { const params = { id, ...values, - date: func.format(values.date), + releaseTime: func.format(values.releaseTime), }; dispatch(NOTICE_SUBMIT(params)); }); @@ -113,14 +113,14 @@ class NoticeAdd extends PureComponent { )} }> - {getFieldDecorator('date', { + {getFieldDecorator('releaseTime', { rules: [ { required: true, message: formatMessage({ id: 'desk.notice.date.validation' }), }, ], - initialValue: moment(detail.date, 'YYYY-MM-DD HH:mm:ss'), + initialValue: moment(detail.releaseTime, 'YYYY-MM-DD HH:mm:ss'), })( {detail.categoryName} }> - {detail.date} + {detail.releaseTime} }> {detail.content} diff --git a/src/pages/Login/Login.js b/src/pages/Login/Login.js index 4565118..9652781 100644 --- a/src/pages/Login/Login.js +++ b/src/pages/Login/Login.js @@ -2,10 +2,10 @@ import React, { Component } from 'react'; import { connect } from 'dva'; import { formatMessage, FormattedMessage } from 'umi/locale'; import { Checkbox, Alert } from 'antd'; -import Login from '@/components/Login'; +import Login from '../../components/Login'; import styles from './Login.less'; -const { Tab, UserName, Password, Submit } = Login; +const { Tab, TenantCode, UserName, Password, Submit } = Login; @connect(({ login, loading }) => ({ login, @@ -80,6 +80,16 @@ class LoginPage extends Component { login.type === 'account' && !submitting && this.renderMessage(formatMessage({ id: 'app.login.message-invalid-credentials' }))} + - + {getFieldDecorator('deptName')()} - + + + {getFieldDecorator('tenantCode')()} + + + {getFieldDecorator('fullName')()} @@ -64,6 +69,10 @@ class Dept extends PureComponent { title: '部门名称', dataIndex: 'deptName', }, + { + title: '租户编号', + dataIndex: 'tenantCode', + }, { title: '部门全称', dataIndex: 'fullName', diff --git a/src/pages/System/Dept/DeptAdd.js b/src/pages/System/Dept/DeptAdd.js index c419285..be782d6 100644 --- a/src/pages/System/Dept/DeptAdd.js +++ b/src/pages/System/Dept/DeptAdd.js @@ -109,12 +109,6 @@ class DeptAdd extends PureComponent { {getFieldDecorator('parentId', { - rules: [ - { - required: true, - message: '请选择上级部门', - }, - ], initialValue: detail.id, })( {getFieldDecorator('parentId', { - rules: [ - { - required: true, - message: '请选择上级部门', - }, - ], initialValue: detail.parentId, })( - + {getFieldDecorator('roleName')()} - + + + {getFieldDecorator('tenantCode')()} + + + {getFieldDecorator('roleAlias')()} @@ -175,6 +180,10 @@ class Role extends PureComponent { title: '角色名称', dataIndex: 'roleName', }, + { + title: '租户编号', + dataIndex: 'tenantCode', + }, { title: '角色别名', dataIndex: 'roleAlias', diff --git a/src/pages/System/Tenant/Tenant.js b/src/pages/System/Tenant/Tenant.js new file mode 100644 index 0000000..c0aba03 --- /dev/null +++ b/src/pages/System/Tenant/Tenant.js @@ -0,0 +1,105 @@ +import React, { PureComponent } from 'react'; +import { connect } from 'dva'; +import { Button, Col, Form, Input, Row } from 'antd'; +import Panel from '../../../components/Panel'; +import { TENANT_LIST } from '../../../actions/tenant'; +import Grid from '../../../components/Sword/Grid'; + +const FormItem = Form.Item; + +@connect(({ tenant, loading }) => ({ + tenant, + loading: loading.models.tenant, +})) +@Form.create() +class Tenant extends PureComponent { + // ============ 查询 =============== + handleSearch = params => { + const { dispatch } = this.props; + dispatch(TENANT_LIST(params)); + }; + + // ============ 查询表单 =============== + renderSearchForm = onReset => { + const { form } = this.props; + const { getFieldDecorator } = form; + + return ( + + + + {getFieldDecorator('tenantCode')()} + + + + + {getFieldDecorator('tenantName')()} + + + + + {getFieldDecorator('contactNumber')()} + + + +
+ + +
+ +
+ ); + }; + + render() { + const code = 'tenant'; + + const { + form, + loading, + tenant: { data }, + } = this.props; + + const columns = [ + { + title: '租户编号', + dataIndex: 'tenantCode', + }, + { + title: '租户名称', + dataIndex: 'tenantName', + }, + { + title: '联系人', + dataIndex: 'linkman', + }, + { + title: '联系电话', + dataIndex: 'contactNumber', + }, + { + title: '联系地址', + dataIndex: 'address', + }, + ]; + + return ( + + + + ); + } +} +export default Tenant; diff --git a/src/pages/System/Tenant/TenantAdd.js b/src/pages/System/Tenant/TenantAdd.js new file mode 100644 index 0000000..c1d5ec4 --- /dev/null +++ b/src/pages/System/Tenant/TenantAdd.js @@ -0,0 +1,96 @@ +import React, { PureComponent } from 'react'; +import { Form, Input, Card, Button } from 'antd'; +import { connect } from 'dva'; +import Panel from '../../../components/Panel'; +import styles from '../../../layouts/Sword.less'; +import { TENANT_SUBMIT } from '../../../actions/tenant'; + +const FormItem = Form.Item; +const { TextArea } = Input; + +@connect(({ loading }) => ({ + submitting: loading.effects['tenant/submit'], +})) +@Form.create() +class TenantAdd extends PureComponent { + handleSubmit = e => { + e.preventDefault(); + const { dispatch, form } = this.props; + form.validateFieldsAndScroll((err, values) => { + if (!err) { + dispatch(TENANT_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 = ( + + ); + + return ( + +
+ + + {getFieldDecorator('tenantName', { + rules: [ + { + required: true, + message: '请输入租户名称', + }, + ], + })()} + + + {getFieldDecorator('linkman', { + rules: [ + { + required: true, + message: '请输入联系人', + }, + ], + })()} + + + {getFieldDecorator('contactNumber', { + rules: [ + { + required: true, + message: '请输入联系电话', + }, + ], + })()} + + + {getFieldDecorator('address')( +