diff --git a/Api/Ewide.Core/Ewide.Core.xml b/Api/Ewide.Core/Ewide.Core.xml index 7e5c095..daef599 100644 --- a/Api/Ewide.Core/Ewide.Core.xml +++ b/Api/Ewide.Core/Ewide.Core.xml @@ -5148,6 +5148,16 @@ 关联显示父级 + + + 权限标识 + + + + + 备注 + + 排序,越小优先级越高 diff --git a/Api/Ewide.Core/Service/Menu/Dto/MenuTreeOutput.cs b/Api/Ewide.Core/Service/Menu/Dto/MenuTreeOutput.cs index 11fe065..c194b11 100644 --- a/Api/Ewide.Core/Service/Menu/Dto/MenuTreeOutput.cs +++ b/Api/Ewide.Core/Service/Menu/Dto/MenuTreeOutput.cs @@ -38,6 +38,16 @@ namespace Ewide.Core.Service /// public bool VisibleParent { get; set; } + /// + /// 权限标识 + /// + public string Permission { get; set; } + + /// + /// 备注 + /// + public string Remark { get; set; } + /// /// 排序,越小优先级越高 /// diff --git a/Api/Ewide.Core/Service/Menu/SysMenuService.cs b/Api/Ewide.Core/Service/Menu/SysMenuService.cs index b9d3572..52cc6cf 100644 --- a/Api/Ewide.Core/Service/Menu/SysMenuService.cs +++ b/Api/Ewide.Core/Service/Menu/SysMenuService.cs @@ -138,7 +138,7 @@ namespace Ewide.Core.Service var roleIdList = await _sysUserRoleService.GetUserRoleIdList(userId); var menuIdList = await _sysRoleMenuService.GetRoleMenuIdList(roleIdList); return await _sysMenuRep.DetachedEntities - .Where(u => u.VisibleParent) + .Where(u => (u.Type == 2 && u.VisibleParent) || u.Type < 2) .Where(u => menuIdList.Contains(u.Id)) .Where(u => u.Status == (int)CommonStatus.ENABLE) .Select(u => u.Application).ToListAsync(); @@ -460,6 +460,8 @@ namespace Ewide.Core.Service Title = u.Name, Type = u.Type, VisibleParent = u.VisibleParent, + Permission = u.Permission, + Remark = u.Remark, Sort = u.Sort }).ToListAsync(); return new TreeBuildUtil().DoTreeBuild(menus); diff --git a/web-react/package.json b/web-react/package.json index 8de0070..8f2e44b 100644 --- a/web-react/package.json +++ b/web-react/package.json @@ -19,6 +19,7 @@ "nprogress": "^0.2.0", "photoswipe": "^4.1.3", "react": "^17.0.2", + "react-color": "^2.19.3", "react-dom": "^17.0.2", "react-json-view": "^1.21.3", "react-monaco-editor": "^0.43.0", diff --git a/web-react/src/assets/style/app.less b/web-react/src/assets/style/app.less index f4849cc..1e27ce4 100644 --- a/web-react/src/assets/style/app.less +++ b/web-react/src/assets/style/app.less @@ -35,6 +35,7 @@ @import './lib/tree-layout.less'; @import './lib/authority-view.less'; @import './lib/icon-selector.less'; +@import './lib/color-selector.less'; @import './lib/anchor.less'; @import './lib/disabled.less'; @import './theme/primary.less'; diff --git a/web-react/src/assets/style/lib/authority-view.less b/web-react/src/assets/style/lib/authority-view.less index 7fd6b37..94c2f71 100644 --- a/web-react/src/assets/style/lib/authority-view.less +++ b/web-react/src/assets/style/lib/authority-view.less @@ -9,7 +9,12 @@ width: 150px; } .ant-descriptions { + clear: both; + margin-bottom: @padding-sm; + .ant-descriptions-view { + overflow: visible; + } &:last-child { margin-bottom: 0; } @@ -26,4 +31,23 @@ } } } + .ant-card-grid { + width: 25%; + margin-bottom: @padding-sm; + padding: @padding-xs; + + cursor: pointer; + } + .ant-card { + margin-bottom: 0; + + background-color: transparent; + &-body { + margin: -1px 0 0 -1px; + padding: 0; + } + .ant-card-grid { + margin-bottom: 0; + } + } } diff --git a/web-react/src/assets/style/lib/color-selector.less b/web-react/src/assets/style/lib/color-selector.less new file mode 100644 index 0000000..e030c16 --- /dev/null +++ b/web-react/src/assets/style/lib/color-selector.less @@ -0,0 +1,18 @@ +@import (reference) '../extend.less'; +.ant-select-dropdown { + .chrome-picker { + width: auto !important; + margin: -@padding-xxs 0; + + border-radius: 0 !important; + background: transparent !important; + box-shadow: none !important; + } +} +.color-selector--palette { + width: 32px; + height: 32px; + + border-radius: @border-radius-base; + box-shadow: inset 0 0 0 @border-width-base @border-color-base, inset 0 0 0 3px @white; +} diff --git a/web-react/src/components/authority-view/index.jsx b/web-react/src/components/authority-view/index.jsx index e6c220f..58cf93d 100644 --- a/web-react/src/components/authority-view/index.jsx +++ b/web-react/src/components/authority-view/index.jsx @@ -1,5 +1,5 @@ import React, { Component } from 'react' -import { Checkbox, Descriptions, Empty, Spin, Tooltip } from 'antd' +import { Card, Checkbox, Descriptions, Empty, Popover, Spin, Tooltip } from 'antd' import { AntIcon } from 'components' import { EMPTY_ID } from 'util/global' @@ -58,7 +58,7 @@ function renderDescriptions(data) { function renderItem(data) { return ( - + } > - {renderDescriptions.call(this, data.children)} + {renderDescriptions.call(this, data.children)} ) @@ -79,16 +79,26 @@ function renderItem(data) { function renderCheckbox(data) { return ( - - this.onChange(e, data)}> - {data.title} - - {data.visibleParent && data.type == 2 && ( - - - - )} - + + 没有说明} + > + this.onChange(e, data)} + > + {data.title} + + {data.visibleParent && data.type == 2 && ( + + + + )} + {data.permission} + + ) } diff --git a/web-react/src/components/color-selector/index.jsx b/web-react/src/components/color-selector/index.jsx new file mode 100644 index 0000000..3d67ab9 --- /dev/null +++ b/web-react/src/components/color-selector/index.jsx @@ -0,0 +1,55 @@ +import React, { Component } from 'react' +import { Col, Row, Select } from 'antd' +import { ChromePicker } from 'react-color' + +export default class index extends Component { + select = React.createRef() + + state = { + color: null, + } + + onChange(color) { + this.setState({ color: color.hex }) + } + + onChangeComplete(color) { + this.props.onChange && this.props.onChange(color.hex) + } + + render() { + const { color } = this.state + + const { value } = this.props + + return ( + + + ( + this.onChange(color)} + onChangeComplete={color => this.onChangeComplete(color)} + /> + )} + showArrow={false} + {...this.props} + value={value} + /> + + + { + this.select.current.focus() + }} + > + + + ) + } +} diff --git a/web-react/src/components/index.js b/web-react/src/components/index.js index 757a45b..1da9bcd 100644 --- a/web-react/src/components/index.js +++ b/web-react/src/components/index.js @@ -1,6 +1,7 @@ export { default as AntIcon } from 'components/ant-icon' export { default as AuthorityView } from './authority-view' export { default as Auth } from './authorized' +export { default as ColorSelector } from './color-selector' export { default as ComponentDynamic } from './component-dynamic' export { default as Container } from './container' export { default as IconSelector } from './icon-selector' diff --git a/web-react/src/components/query-table/index.jsx b/web-react/src/components/query-table/index.jsx index 750906b..db24757 100644 --- a/web-react/src/components/query-table/index.jsx +++ b/web-react/src/components/query-table/index.jsx @@ -1,5 +1,5 @@ import React, { Component } from 'react' -import { Form, Button, Table, Tooltip } from 'antd' +import { Form, Button, Table, Tooltip, Drawer } from 'antd' import { AntIcon } from 'components' import { isEqual } from 'lodash' @@ -34,15 +34,15 @@ const rowNoColumn = { * @returns */ function renderQueryBar() { - const { query, moreQuery, onQueryChange } = this.props + const { query, moreQuery, onQueryChange, queryInitialValues } = this.props return ( this.onQuery(value)} - initialValues={this.props.queryInitialValues} + initialValues={queryInitialValues} onValuesChange={(changedValues, allValues) => onQueryChange && onQueryChange(changedValues, allValues) } @@ -55,18 +55,62 @@ function renderQueryBar() { this.onResetQuery()} + onClick={() => this.onResetQuery(this.queryForm)} icon={} /> - {moreQuery && 更多查询条件} + {moreQuery && ( + this.onOpenMoreQuery()}> + 更多查询条件 + + )} ) } +function renderMoreQueryBody() { + const { moreQueryVisible } = this.state + + const { moreQuery } = this.props + + return ( + this.setState({ moreQueryVisible: false })} + > + + {moreQuery} + + + } + onClick={() => + this.onQuery(this.moreQueryForm.current.getFieldsValue()) + } + > + 查询 + + + this.onResetQuery(this.moreQueryForm)} + icon={} + /> + + + + + + ) +} + function renderTable(props, on) { return } @@ -79,10 +123,14 @@ export default class QueryTable extends Component { type: 'tree', // 数据 dataSource: [], + + moreQueryVisible: false, } // 查询表单实例 - form = React.createRef() + queryForm = React.createRef() + // 更多查询表单实例 + moreQueryForm = React.createRef() // 查询值 query = {} @@ -213,7 +261,13 @@ export default class QueryTable extends Component { * @param {*} values */ onQuery = values => { - this.query = values + const { queryInitialValues } = this.props + + this.query = { + ...queryInitialValues, + ...this.query, + ...values, + } this.onReloadData(true) } @@ -221,15 +275,23 @@ export default class QueryTable extends Component { * 重置查询 * 初始化表单字段值,加载数据,并返回到第一页 */ - onResetQuery = () => { - const { queryInitialValues, onQueryChange } = this.props + onResetQuery = from => { + const { queryInitialValues, onQueryChange, query, moreQuery } = this.props + + let queryValues = {} + if (from === this.queryForm) { + from.current.resetFields() + queryValues = moreQuery ? this.moreQueryForm.current.getFieldsValue() : {} + } else if (from === this.moreQueryForm) { + from.current.resetFields() + queryValues = query ? this.queryForm.current.getFieldsValue() : {} + } - this.form.current.resetFields() this.query = { ...queryInitialValues, + ...queryValues, } - const values = this.form.current.getFieldsValue() - onQueryChange && onQueryChange(values, values) + onQueryChange && onQueryChange(this.query) this.onReloadData(true) } @@ -277,12 +339,16 @@ export default class QueryTable extends Component { return false } + onOpenMoreQuery = () => { + this.setState({ moreQueryVisible: true }) + } + render() { const { rowNumber } = this const { loading, dataSource, type } = this.state - const { query, operator, columns, expandable, expandedRowRender } = this.props + const { query, moreQuery, operator, columns, expandable, expandedRowRender } = this.props const attrs = {} Object.keys(this.props).forEach(key => { @@ -337,6 +403,7 @@ export default class QueryTable extends Component { ) : ( renderTable.call(this, props, on) )} + {moreQuery && renderMoreQueryBody.call(this)} ) } diff --git a/web-react/src/pages/business/house/query/index.jsx b/web-react/src/pages/business/house/query/index.jsx new file mode 100644 index 0000000..95653c2 --- /dev/null +++ b/web-react/src/pages/business/house/query/index.jsx @@ -0,0 +1,267 @@ +import React, { Component } from 'react' +import { Button, Card, Form, Input, message as Message, Popconfirm, Radio, Select, Tag } from 'antd' +import { AntIcon, Auth, Container, QueryTable } from 'components' +import { api } from 'common/api' +import auth from 'components/authorized/handler' +import { isEqual } from 'lodash' +import getDictData from 'util/dic' +import { toCamelCase } from 'util/format' + +/** + * 注释段[\/**\/]为必须要改 + */ + +/** + * 配置页面所需接口函数 + */ +const apiAction = { + page: api.houseTaskPage, +} + +/** + * 用于弹窗标题 + * [必要] + */ +const name = '/**/' + +/** + * 统一配置权限标识 + * [必要] + */ +const authName = 'houseTask' + +export default class index extends Component { + state = { + codes: { + houseType: [], + houseIndustry: [], + }, + + type: '', + } + + // 表格实例 + table = React.createRef() + + // 新增窗口实例 + addForm = React.createRef() + // 编辑窗口实例 + editForm = React.createRef() + + columns = [ + { + title: '房屋编码', + dataIndex: 'houseCode', + sorter: true, + width: 300, + render: (text, record) => ( + <> + {`${record.areaName}-${record.roadName}-${record.commName}-${ + record.note + }-${record.no.toString().padStart(3, '0')}`} + + {text} + > + ), + }, + { + title: '房屋性质及行业', + dataIndex: 'type', + sorter: true, + width: 150, + render: text => this.bindCodeValue(text, 'house_type'), + }, + { + title: '地址', + dataIndex: 'address', + sorter: true, + }, + { + title: '任务截止时间', + dataIndex: 'endTime', + sorter: true, + width: 150, + }, + ] + + /** + * 构造函数,在渲染前动态添加操作字段等 + * @param {*} props + */ + constructor(props) { + super(props) + + const flag = auth({ [authName]: [['edit'], ['delete']] }) + } + + /** + * 阻止外部组件引发的渲染,提升性能 + * 可自行添加渲染条件 + * [必要] + * @param {*} props + * @param {*} state + * @returns + */ + shouldComponentUpdate(props, state) { + return !isEqual(this.state, state) + } + + /** + * 加载字典数据,之后开始加载表格数据 + * 如果必须要加载字典数据,可直接对表格设置autoLoad=true + */ + componentDidMount() { + const { onLoading, onLoadData } = this.table.current + onLoading() + getDictData('house_type', 'house_industry').then(codes => { + this.setState({ codes }, () => { + onLoadData() + }) + }) + } + + /** + * 调用加载数据接口,可在调用前对query进行处理 + * [异步,必要] + * @param {*} params + * @param {*} query + * @returns + */ + loadData = async (params, query) => { + const { data } = await apiAction.page({ + ...params, + ...query, + }) + return data + } + + /** + * 绑定字典数据 + * @param {*} code + * @param {*} name + * @returns + */ + bindCodeValue(code, name) { + name = toCamelCase(name) + const codes = this.state.codes[name] + if (codes) { + const c = codes.find(p => p.code == code) + if (c) { + return c.value + } + } + return null + } + + /** + * 打开新增/编辑弹窗 + * @param {*} modal + * @param {*} id + */ + onOpen(modal, id) { + modal.current.open({ id }) + } + + /** + * 对表格上的操作进行统一处理 + * [异步] + * @param {*} action + * @param {*} successMessage + */ + async onAction(action, successMessage) { + const { onLoading, onLoaded, onReloadData } = this.table.current + onLoading() + try { + if (action) { + await action + } + if (successMessage) { + Message.success(successMessage) + } + onReloadData() + } catch { + onLoaded() + } + } + + /** + * 删除 + * @param {*} id + */ + onDelete(id) { + this.onAction(apiAction.delete({ id }), '删除成功') + } + + //#region 自定义方法 + //#endregion + + render() { + const { codes, type } = this.state + + return ( + + + + + + + 全部 + {codes.houseType.map(item => ( + + {item.value} + + ))} + + + {type == 2 && ( + + + {codes.houseIndustry.map(item => ( + + {item.value} + + ))} + + + )} + + + + + + + + } + moreQuery={ + + + + + + } + operator={ + + } + onClick={() => this.onOpen(this.addForm)} + > + 新增{name} + + + } + /> + + + ) + } +} diff --git a/web-react/src/pages/system/app/form.jsx b/web-react/src/pages/system/app/form.jsx index 1dcbc36..f2e0fc4 100644 --- a/web-react/src/pages/system/app/form.jsx +++ b/web-react/src/pages/system/app/form.jsx @@ -1,7 +1,6 @@ import React, { Component } from 'react' import { Form, Input, InputNumber, Spin } from 'antd' -import { AntIcon, IconSelector } from 'components' -import { cloneDeep } from 'lodash' +import { AntIcon, ColorSelector, IconSelector } from 'components' import { api } from 'common/api' const initialValues = { @@ -107,6 +106,9 @@ export default class form extends Component { } /> + + + ( + + + + ), + }, { title: '应用名称', dataIndex: 'name', diff --git a/web-react/yarn.lock b/web-react/yarn.lock index cad1a03..1bb675f 100644 --- a/web-react/yarn.lock +++ b/web-react/yarn.lock @@ -1315,6 +1315,11 @@ dependencies: "@hapi/hoek" "^8.3.0" +"@icons/material@^0.2.4": + version "0.2.4" + resolved "https://registry.nlark.com/@icons/material/download/@icons/material-0.2.4.tgz#e90c9f71768b3736e76d7dd6783fc6c2afa88bc8" + integrity sha1-6QyfcXaLNzbnbX3WeD/Gwq+oi8g= + "@istanbuljs/load-nyc-config@^1.0.0": version "1.1.0" resolved "https://registry.nlark.com/@istanbuljs/load-nyc-config/download/@istanbuljs/load-nyc-config-1.1.0.tgz#fd3db1d59ecf7cf121e80650bb86712f9b55eced" @@ -7172,6 +7177,11 @@ locate-path@^5.0.0: dependencies: p-locate "^4.1.0" +lodash-es@^4.17.15: + version "4.17.21" + resolved "https://registry.npm.taobao.org/lodash-es/download/lodash-es-4.17.21.tgz?cache=0&sync_timestamp=1613836185353&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Flodash-es%2Fdownload%2Flodash-es-4.17.21.tgz#43e626c46e6591b7750beb2b50117390c609e3ee" + integrity sha1-Q+YmxG5lkbd1C+srUBFzkMYJ4+4= + lodash._reinterpolate@^3.0.0: version "3.0.0" resolved "https://registry.nlark.com/lodash._reinterpolate/download/lodash._reinterpolate-3.0.0.tgz#0ccf2d89166af03b3663c796538b75ac6e114d9d" @@ -7232,7 +7242,7 @@ lodash.uniq@^4.5.0: resolved "https://registry.npm.taobao.org/lodash.uniq/download/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773" integrity sha1-0CJTc662Uq3BvILklFM5qEJ1R3M= -"lodash@>=3.5 <5", lodash@^4.17.11, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.20, lodash@^4.17.21, lodash@^4.17.5, lodash@^4.7.0: +"lodash@>=3.5 <5", lodash@^4.0.1, lodash@^4.17.11, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.20, lodash@^4.17.21, lodash@^4.17.5, lodash@^4.7.0: version "4.17.21" resolved "https://registry.nlark.com/lodash/download/lodash-4.17.21.tgz?cache=0&sync_timestamp=1618847150612&other_urls=https%3A%2F%2Fregistry.nlark.com%2Flodash%2Fdownload%2Flodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" integrity sha1-Z5WRxWTDv/quhFTPCz3zcMPWkRw= @@ -7316,6 +7326,11 @@ map-visit@^1.0.0: dependencies: object-visit "^1.0.0" +material-colors@^1.2.1: + version "1.2.6" + resolved "https://registry.npm.taobao.org/material-colors/download/material-colors-1.2.6.tgz#6d1958871126992ceecc72f4bcc4d8f010865f46" + integrity sha1-bRlYhxEmmSzuzHL0vMTY8BCGX0Y= + md5.js@^1.3.4: version "1.3.5" resolved "https://registry.npm.taobao.org/md5.js/download/md5.js-1.3.5.tgz#b5d07b8e3216e3e27cd728d72f70d1e6a342005f" @@ -9130,7 +9145,7 @@ prompts@^2.0.1: kleur "^3.0.3" sisteransi "^1.0.5" -prop-types@^15.6.2, prop-types@^15.7.2: +prop-types@^15.5.10, prop-types@^15.6.2, prop-types@^15.7.2: version "15.7.2" resolved "https://registry.npm.taobao.org/prop-types/download/prop-types-15.7.2.tgz#52c41e75b8c87e72b9d9360e0206b99dcbffa6c5" integrity sha1-UsQedbjIfnK52TYOAga5ncv/psU= @@ -9658,6 +9673,19 @@ react-base16-styling@^0.6.0: lodash.flow "^3.3.0" pure-color "^1.2.0" +react-color@^2.19.3: + version "2.19.3" + resolved "https://registry.npm.taobao.org/react-color/download/react-color-2.19.3.tgz#ec6c6b4568312a3c6a18420ab0472e146aa5683d" + integrity sha1-7GxrRWgxKjxqGEIKsEcuFGqlaD0= + dependencies: + "@icons/material" "^0.2.4" + lodash "^4.17.15" + lodash-es "^4.17.15" + material-colors "^1.2.1" + prop-types "^15.5.10" + reactcss "^1.2.0" + tinycolor2 "^1.4.1" + react-dev-utils@^11.0.3: version "11.0.4" resolved "https://registry.npm.taobao.org/react-dev-utils/download/react-dev-utils-11.0.4.tgz?cache=0&sync_timestamp=1615231838520&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Freact-dev-utils%2Fdownload%2Freact-dev-utils-11.0.4.tgz#a7ccb60257a1ca2e0efe7a83e38e6700d17aa37a" @@ -9852,6 +9880,13 @@ react@^17.0.2: loose-envify "^1.1.0" object-assign "^4.1.1" +reactcss@^1.2.0: + version "1.2.3" + resolved "https://registry.npm.taobao.org/reactcss/download/reactcss-1.2.3.tgz#c00013875e557b1cf0dfd9a368a1c3dab3b548dd" + integrity sha1-wAATh15Vexzw39mjaKHD2rO1SN0= + dependencies: + lodash "^4.0.1" + read-pkg-up@^3.0.0: version "3.0.0" resolved "https://registry.nlark.com/read-pkg-up/download/read-pkg-up-3.0.0.tgz?cache=0&sync_timestamp=1618846971516&other_urls=https%3A%2F%2Fregistry.nlark.com%2Fread-pkg-up%2Fdownload%2Fread-pkg-up-3.0.0.tgz#3ed496685dba0f8fe118d0691dc51f4a1ff96f07" @@ -11248,6 +11283,11 @@ tiny-warning@^1.0.0, tiny-warning@^1.0.3: resolved "https://registry.npm.taobao.org/tiny-warning/download/tiny-warning-1.0.3.tgz#94a30db453df4c643d0fd566060d60a875d84754" integrity sha1-lKMNtFPfTGQ9D9VmBg1gqHXYR1Q= +tinycolor2@^1.4.1: + version "1.4.2" + resolved "https://registry.nlark.com/tinycolor2/download/tinycolor2-1.4.2.tgz#3f6a4d1071ad07676d7fa472e1fac40a719d8803" + integrity sha1-P2pNEHGtB2dtf6Ry4frECnGdiAM= + tmpl@1.0.x: version "1.0.4" resolved "https://registry.npm.taobao.org/tmpl/download/tmpl-1.0.4.tgz#23640dd7b42d00433911140820e5cf440e521dd1"