diff --git a/Api/Ewide.Application/Ewide.Application.xml b/Api/Ewide.Application/Ewide.Application.xml index 8225ae3..22433b6 100644 --- a/Api/Ewide.Application/Ewide.Application.xml +++ b/Api/Ewide.Application/Ewide.Application.xml @@ -1456,5 +1456,20 @@ 上报备注 + + + 所属街道 + + + + + 名称 + + + + + 机构Id + + diff --git a/Api/Ewide.Application/Service/HouseSafety/HouseCode/Dto/HouseCodeInput.cs b/Api/Ewide.Application/Service/HouseSafety/HouseCode/Dto/HouseCodeInput.cs index 8017786..d105eda 100644 --- a/Api/Ewide.Application/Service/HouseSafety/HouseCode/Dto/HouseCodeInput.cs +++ b/Api/Ewide.Application/Service/HouseSafety/HouseCode/Dto/HouseCodeInput.cs @@ -56,4 +56,10 @@ namespace Ewide.Application public string ZoonId { get; set; } public int Type { get; set; } } + + public class GetHouseCodeInput + { + [Required(ErrorMessage = "房屋编码ID不可为空")] + public string Id { get; set; } + } } diff --git a/Api/Ewide.Application/Service/HouseSafety/HouseCode/Dto/HouseCodeOutput.cs b/Api/Ewide.Application/Service/HouseSafety/HouseCode/Dto/HouseCodeOutput.cs index 08bb370..0316d8b 100644 --- a/Api/Ewide.Application/Service/HouseSafety/HouseCode/Dto/HouseCodeOutput.cs +++ b/Api/Ewide.Application/Service/HouseSafety/HouseCode/Dto/HouseCodeOutput.cs @@ -25,4 +25,18 @@ namespace Ewide.Application public string Lng { get; set; } public string Lat { get; set; } } + + public class GetHouseCodeOutput + { + public string Id { get; set; } + public int Type { get; set; } + public int Industry { get; set; } + public string AreaCode { get; set; } + public string ProjectId { get; set; } + public int No { get; set; } + public string ZoneId { get; set; } + public string Address { get; set; } + public string Lng { get; set; } + public string Lat { get; set; } + } } diff --git a/Api/Ewide.Application/Service/HouseSafety/HouseCode/HouseCodeService.cs b/Api/Ewide.Application/Service/HouseSafety/HouseCode/HouseCodeService.cs index fb526c0..e57b2b2 100644 --- a/Api/Ewide.Application/Service/HouseSafety/HouseCode/HouseCodeService.cs +++ b/Api/Ewide.Application/Service/HouseSafety/HouseCode/HouseCodeService.cs @@ -73,6 +73,16 @@ LEFT JOIN sys_area_code AA ON AA.AdCode = SUBSTR(CA.AdCode,1,6) "; return await _dapperRepository.QueryPageDataDynamic(sql, input, filterFields: new string[] {"Type", "Address", "HouseCode" }); } + [HttpGet("/houseCode/detail")] + public async Task GetHouserCode([FromQuery] GetHouseCodeInput input) + { + var houseCode = await _houseCodeRep.DetachedEntities.FirstOrDefaultAsync(p => p.Id == input.Id); + var areaCode = (await Db.GetRepository().DetachedEntities.FirstOrDefaultAsync(p => p.Id == houseCode.ProjectId)).AreaCode; + var result = houseCode.Adapt(); + result.AreaCode = areaCode; + return result; + } + /// /// 获取同一区域下的下一个编号 /// diff --git a/Api/Ewide.Application/Service/HouseSafety/HouseSelector/HouseSelectorService.cs b/Api/Ewide.Application/Service/HouseSafety/HouseSelector/HouseSelectorService.cs index f040d2e..46d1295 100644 --- a/Api/Ewide.Application/Service/HouseSafety/HouseSelector/HouseSelectorService.cs +++ b/Api/Ewide.Application/Service/HouseSafety/HouseSelector/HouseSelectorService.cs @@ -145,7 +145,7 @@ INNER JOIN (SELECT * FROM bs_house_member_relation WHERE SysUserId = @UserId) HM HouseCodeId = p }.Insert(); - var initTask = _bsHouseTaskRep.DetachedEntities.FirstOrDefault(t => t.TaskType == 0); + var initTask = _bsHouseTaskRep.DetachedEntities.FirstOrDefault(t =>t.HouseCodeId == p && t.TaskType == 0); if (initTask == null) { new BsHouseTask diff --git a/Api/Ewide.Application/Service/HouseSafety/HouseZone/Dto/HouseZoneInput.cs b/Api/Ewide.Application/Service/HouseSafety/HouseZone/Dto/HouseZoneInput.cs index 4c63c2d..1d69b94 100644 --- a/Api/Ewide.Application/Service/HouseSafety/HouseZone/Dto/HouseZoneInput.cs +++ b/Api/Ewide.Application/Service/HouseSafety/HouseZone/Dto/HouseZoneInput.cs @@ -1,4 +1,5 @@ -using System; +using Ewide.Core.Service; +using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.Linq; @@ -13,4 +14,28 @@ namespace Ewide.Application [MinLength(9, ErrorMessage = "区域编码长度必须为9位及以上")] public string AreaCode { get; set; } } + + public class AddHouseZoneInput : OrgInput + { + /// + /// 所属街道 + /// + [Required(ErrorMessage = "所属街道不能为空")] + public override string Pid { get; set; } + /// + /// 名称 + /// + [Required(ErrorMessage = "片区名称不能为空")] + public override string Name { get; set; } + + } + + public class UpdateHouseZoneInput : AddHouseZoneInput + { + /// + /// 机构Id + /// + [Required(ErrorMessage = "片区Id不能为空")] + public string Id { get; set; } + } } diff --git a/Api/Ewide.Application/Service/HouseSafety/HouseZone/HouseZoneService.cs b/Api/Ewide.Application/Service/HouseSafety/HouseZone/HouseZoneService.cs index 323136c..4b9d3f8 100644 --- a/Api/Ewide.Application/Service/HouseSafety/HouseZone/HouseZoneService.cs +++ b/Api/Ewide.Application/Service/HouseSafety/HouseZone/HouseZoneService.cs @@ -2,9 +2,11 @@ using Ewide.Core.Extension; using Ewide.Core.Service; using Furion.DatabaseAccessor; +using Furion.DatabaseAccessor.Extensions; using Furion.DependencyInjection; using Furion.DynamicApiController; using Furion.FriendlyException; +using Mapster; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; using System; @@ -104,9 +106,9 @@ namespace Ewide.Application.Service } [HttpGet("/houseZone/autoIncrement")] - public async Task AutoIncrement([FromQuery] string code) + public async Task AutoIncrement([FromQuery] string roadId) { - var road = await _sysOrgRep.DetachedEntities.FirstOrDefaultAsync(p => p.AreaCode == code); + var road = await _sysOrgRep.DetachedEntities.FirstOrDefaultAsync(p => p.Id == roadId && p.Type == 3); if (road == null) throw Oops.Oh("组织机构错误"); return await AutoIncrement(road); } @@ -119,7 +121,7 @@ namespace Ewide.Application.Service } [HttpPost("/houseZone/add")] - public async Task AddZone(AddOrgInput input) + public async Task AddZone(AddHouseZoneInput input) { /* * 区县市限定所属区域/上级机构是否为当前区 @@ -136,15 +138,14 @@ namespace Ewide.Application.Service var roles = await _userManager.GetUserRoleList(); if (roles.Any(p => p.Code == areaManager)) { - var road = await _sysOrgRep.DetachedEntities.FirstOrDefaultAsync(p => p.AreaCode == input.AreaCode); - if (!road.Pids.Contains(org.Id)) throw Oops.Oh("组织机构错误"); + var road = await _sysOrgRep.DetachedEntities.FirstOrDefaultAsync(p => p.Id == input.Pid); + if (!road.Pids.Contains(org.Id)) throw Oops.Oh("当前用户组织机构错误"); - input.Pid = road.Id; + input.AreaCode = road.AreaCode; input.Code = road.Code + (await AutoIncrement(road)).ToString().PadLeft(3, '0'); } else if (roles.Any(p => p.Code == roadManager)) { - input.Pid = org.Id; input.AreaCode = org.AreaCode; input.Code = org.Code + (await AutoIncrement(org)).ToString().PadLeft(3, '0'); @@ -152,7 +153,17 @@ namespace Ewide.Application.Service input.Type = (int)OrgType.片区; - await _sysOrgService.AddOrg(input); + AddOrgInput addOrgInput = input.Adapt(); + await _sysOrgService.AddOrg(addOrgInput); + } + + [HttpPost("/houseZone/edit")] + public async Task EditZone(UpdateHouseZoneInput input) + { + var zone = await _sysOrgRep.DetachedEntities.FirstOrDefaultAsync(z => z.Id == input.Id); + if(zone == null) throw Oops.Oh("修改失败:数据有误,刷新列表后再尝试修改"); + zone.Remark = input.Remark; + await zone.UpdateIncludeAsync(new[] { nameof(SysOrg.Remark) }, true); } } } diff --git a/Api/Ewide.Core/Service/File/Dto/FileOutput.cs b/Api/Ewide.Core/Service/File/Dto/FileOutput.cs index d9716dd..ae043d6 100644 --- a/Api/Ewide.Core/Service/File/Dto/FileOutput.cs +++ b/Api/Ewide.Core/Service/File/Dto/FileOutput.cs @@ -9,5 +9,6 @@ /// 文件Id /// public string Id { get; set; } + public System.DateTime CreatedTime { get; set; } } } diff --git a/Api/Ewide.Core/Service/File/SysFileService.cs b/Api/Ewide.Core/Service/File/SysFileService.cs index 1ab43b9..25c9447 100644 --- a/Api/Ewide.Core/Service/File/SysFileService.cs +++ b/Api/Ewide.Core/Service/File/SysFileService.cs @@ -1,4 +1,5 @@ -using Furion; +using Ewide.Core.Extension; +using Furion; using Furion.DatabaseAccessor; using Furion.DatabaseAccessor.Extensions; using Furion.DependencyInjection; @@ -50,8 +51,7 @@ namespace Ewide.Core.Service .Where(input.FileLocation > 0, u => u.FileLocation == input.FileLocation) .Where(fileBucket, u => EF.Functions.Like(u.FileBucket, $"%{input.FileBucket.Trim()}%")) .Where(fileOriginName, u => EF.Functions.Like(u.FileOriginName, $"%{input.FileOriginName.Trim()}%")) - .Select(u => u.Adapt()) - .ToPagedListAsync(input.PageIndex, input.PageSize); + .ToPageData(input); return PageDataResult.PageResult(files); } diff --git a/Api/Ewide.Core/Service/Org/Dto/OrgInput.cs b/Api/Ewide.Core/Service/Org/Dto/OrgInput.cs index accb321..bdabbbf 100644 --- a/Api/Ewide.Core/Service/Org/Dto/OrgInput.cs +++ b/Api/Ewide.Core/Service/Org/Dto/OrgInput.cs @@ -10,7 +10,7 @@ namespace Ewide.Core.Service /// /// 父Id /// - public string Pid { get; set; } + public virtual string Pid { get; set; } /// /// 父Ids diff --git a/Api/Ewide.Core/applicationconfig.json b/Api/Ewide.Core/applicationconfig.json index 1568099..0266d1e 100644 --- a/Api/Ewide.Core/applicationconfig.json +++ b/Api/Ewide.Core/applicationconfig.json @@ -93,6 +93,7 @@ "sysDictType:dropDowns", "sysFileInfo:upload", "sysFileInfo:download", + "sysFileInfo:detail", "sysFileInfo:preview", "sysUser:updateInfo", "sysUser:updatePwd", diff --git a/Web/src/pages/business/house/info/form/base/aspect.vue b/Web/src/pages/business/house/info/form/base/aspect.vue index 878c07e..90ae890 100644 --- a/Web/src/pages/business/house/info/form/base/aspect.vue +++ b/Web/src/pages/business/house/info/form/base/aspect.vue @@ -104,15 +104,25 @@ export default { if (record) { const fileList = !!record.houseInfo.facadePhoto ? record.houseInfo.facadePhoto.split(',') : []; for (let i = 0; i < fileList.length; i++) { - const file = await PreviewFile(fileList[i]); - const base64 = await BlobToBase64(file); - facadePhoto.push({ - uid: fileList[i], - response: fileList[i], // 用于和新上传的文件一同回传 - name: file.name, - url: base64, - status: 'done', - }); + try { + const file = await PreviewFile(fileList[i]); + const base64 = await BlobToBase64(file); + facadePhoto.push({ + uid: fileList[i], + response: fileList[i], // 用于和新上传的文件一同回传 + name: file.name, + url: base64, + status: 'done', + }); + } catch { + const { data: file } = await this.$api.sysFileInfoDetail({ id: fileList[i] }); + facadePhoto.push({ + uid: fileList[i], + response: '文件已丢失', + name: file.fileOriginName, + status: 'error', + }); + } } } defaultForm.houseInfo.facadePhoto = facadePhoto; diff --git a/Web/src/pages/business/house/info/form/base/attachments.vue b/Web/src/pages/business/house/info/form/base/attachments.vue index db16ab8..69162a5 100644 --- a/Web/src/pages/business/house/info/form/base/attachments.vue +++ b/Web/src/pages/business/house/info/form/base/attachments.vue @@ -173,15 +173,25 @@ export default { const fileList = !form.houseInfo[key] || form.houseInfo[key].length == 0 ? [] : form.houseInfo[key].split(','); for (let i = 0; i < fileList.length; i++) { - const file = await PreviewFile(fileList[i]); - const base64 = await BlobToBase64(file); - fileValue.push({ - uid: fileList[i], - response: fileList[i], // 用于和新上传的文件一同回传 - name: file.name, - url: base64, - status: 'done', - }); + try { + const file = await PreviewFile(fileList[i]); + const base64 = await BlobToBase64(file); + fileValue.push({ + uid: fileList[i], + response: fileList[i], // 用于和新上传的文件一同回传 + name: file.name, + url: base64, + status: 'done', + }); + } catch { + const { data: file } = await this.$api.sysFileInfoDetail({ id: fileList[i] }); + fileValue.push({ + uid: fileList[i], + response: '文件已丢失', + name: file.fileOriginName, + status: 'error', + }); + } } form.houseInfo[key] = fileValue; } diff --git a/web-react/craco.config.js b/web-react/craco.config.js index a83f185..59d3aa2 100644 --- a/web-react/craco.config.js +++ b/web-react/craco.config.js @@ -28,7 +28,7 @@ module.exports = { ], webpack: { plugins: [ - // new MonacoWebpackPlugin() + new MonacoWebpackPlugin() ] } } \ No newline at end of file diff --git a/web-react/public/seed/form-tabs/index.jsx b/web-react/public/seed/form-tabs/index.jsx new file mode 100644 index 0000000..470a4a0 --- /dev/null +++ b/web-react/public/seed/form-tabs/index.jsx @@ -0,0 +1,153 @@ +import React, { Component } from 'react' +import { Button, Tabs } from 'antd' +import { ComponentDynamic, Container } from 'components' +import { isEqual, merge } from 'lodash' + +const tabs = [ + { + title: '标题', + component: () => import('./tab'), + show: true, + }, +] + +export default class index extends Component { + state = { + actived: '0', + loading: true, + record: null, + saveDisabled: true, + saving: false, + } + + // 子表单实例集合 + children = [] + + // 整合提交数据 + formData = {} + + /** + * 阻止外部组件引发的渲染,提升性能 + * 可自行添加渲染条件 + * [必要] + * @param {*} props + * @param {*} state + * @returns + */ + shouldComponentUpdate(props, state) { + return !isEqual(this.state, state) + } + + /** + * DOM加载完成钩子,可在此获取详细数据赋值到record + */ + componentDidMount() {} + + /** + * 接收到所有子组件已加载完成,并启用提交按钮 + */ + call(child, index) { + this.children[index] = child + if (this.children.filter(p => p).length === tabs.filter(p => p.show).length) { + this.setState({ saveDisabled: false }) + } + } + + async onSubmit() { + for (const child of this.children) { + try { + const data = await child.getData() + merge(this.formData, data) + } catch (e) { + return e + } + } + + //#region 提交数据 + this.setState({ saving: true }) + this.setState({ saving: false }) + //#endregion + } + + render() { + const { id } = this.props + + const { actived, loading, record, saveDisabled, saving } = this.state + + return ( +
+
+ {/* 底部工具栏(需放在前面) */} +
+ +
+ + + + + +
+
+
+ {/* 顶部信息栏,不需要时刻删除 */} +
+
+ { + this.setState({ actived: activeKey }) + }} + > + {tabs.map( + (tab, i) => + tab.show && ( + + ) + )} + +
+ {tabs.map((tab, i) => { + if (tab.show) { + return ( +
+ this.call(child, i)} + /> +
+ ) + } + return <> + })} +
+
+
+
+ ) + } +} diff --git a/web-react/public/seed/form-tabs/tab/index.jsx b/web-react/public/seed/form-tabs/tab/index.jsx new file mode 100644 index 0000000..3d2912d --- /dev/null +++ b/web-react/public/seed/form-tabs/tab/index.jsx @@ -0,0 +1,126 @@ +import React, { Component } from 'react' +import ReactDOM from 'react-dom' +import { Anchor, Card, Col, Divider, Row, Spin } from 'antd' +import { AntIcon, ComponentDynamic, Container } from 'components' +import { isEqual, merge } from 'lodash' + +const parts = [ + { + // title: '标题', + component: () => import('./part'), + }, +] + +export default class index extends Component { + // 子表单实例集合 + children = [] + + // 整合提交数据 + formData = {} + + // 锚点挂载DOM + container = window + + /** + * 阻止外部组件引发的渲染,提升性能 + * 可自行添加渲染条件 + * [必要] + * @param {*} props + * @param {*} state + * @returns + */ + shouldComponentUpdate(props, state) { + return !isEqual(this.state, state) || this.props.loading !== props.loading + } + + /** + * 加载完成,通知父级组件并传递自身 + */ + call(child, index) { + this.children[index] = child + if (this.children.filter(p => p).length === parts.length) { + const { onRef } = this.props + if (onRef) onRef(this) + } + } + + /** + * 从下级组件获取表单数据,并传递给更上级组件 + * [异步,必要] + * @returns + */ + async getData() { + for (const child of this.children) { + const data = await child.getData() + merge(this.formData, data) + } + return this.formData + } + + /** + * 设置锚点容器 + * [非必要] + * @param {*} container + */ + setContainer = container => { + this.container = (ReactDOM.findDOMNode(container) || {}).parentNode + } + + /** + * 渲染 + * 当前渲染结构已完善,非必要可以不用修改 + * [必要] + * @returns + */ + render() { + const { id, loading } = this.props + + return ( + + + +
+
+ + {parts.map((item, i) => ( + +
+ {item.title &&
{item.title}
} + } + wrapperClassName={loading && 'h-400-min'} + > + {!loading && ( + this.call(child, i)} + /> + )} + +
+ {i < parts.length - 1 && } +
+ ))} +
+ + {/* 锚点,如果不需要可以删除以下节点 */} + + this.container} + offsetTop={24} + targetOffset={100} + wrapperStyle={{ backgroundColor: 'transparent' }} + onClick={e => e.preventDefault()} + > + {parts.map((part, i) => ( + + ))} + + + + + ) + } +} diff --git a/web-react/public/seed/form-tabs/tab/part.jsx b/web-react/public/seed/form-tabs/tab/part.jsx new file mode 100644 index 0000000..9b70bef --- /dev/null +++ b/web-react/public/seed/form-tabs/tab/part.jsx @@ -0,0 +1,115 @@ +import React, { Component } from 'react' +import { Form, Spin } from 'antd' +import { AntIcon } from 'components' +import { cloneDeep, isEqual } from 'lodash' + +const initialValues = {} + +const layout = { + labelCol: { flex: '150px' }, + wrapperCol: { flex: '1' }, +} + +export default class part extends Component { + state = { + loading: true, + codes: {}, + options: {}, + } + + // 表单实例 + form = React.createRef() + + // 初始化数据 + record = {} + + /** + * 阻止外部组件引发的渲染,提升性能 + * 可自行添加渲染条件 + * [必要] + * @param {*} props + * @param {*} state + * @returns + */ + shouldComponentUpdate(props, state) { + return !isEqual(this.state, state) + } + + /** + * DOM加载完成钩子,绑定数据 + */ + componentDidMount() { + this.fillData({ + record: this.props.record, + }) + } + + /** + * 加载完成,通知父级组件并传递自身 + */ + call() { + const { onRef } = this.props + if (onRef) onRef(this) + } + + /** + * 填充数据 + * 可以在设置this.record之后对其作出数据结构调整 + * [异步,必要] + * @param {*} params + */ + async fillData(params) { + this.record = cloneDeep(params.record) + //#region 从后端转换成前段所需格式 + //#endregion + this.form.current.setFieldsValue(this.record) + + this.setState({ loading: false }) + this.call() + } + + /** + * 获取数据 + * 可以对postData进行数据结构调整 + * [异步,必要] + * @returns + */ + async getData() { + const form = this.form.current + + const valid = await form.validateFields() + if (valid) { + const postData = form.getFieldsValue() + //#region 从前段转换后端所需格式 + //#endregion + return postData + } + } + + //#region 自定义方法 + /** + * 表单change事件处理,包括了所有字段的change + * [异步,非必要] + * @param {*} changedValues + * @param {*} allValues + */ + async onValuesChange(changedValues, allValues) {} + //#endregion + + render() { + const { loading } = this.state + + return ( + }> +
+ this.onValuesChange(changedValues, allValues) + } + >
+
+ ) + } +} diff --git a/web-react/public/seed/form/index.jsx b/web-react/public/seed/form/index.jsx new file mode 100644 index 0000000..780eb81 --- /dev/null +++ b/web-react/public/seed/form/index.jsx @@ -0,0 +1,169 @@ +import React, { Component } from 'react' +import ReactDOM from 'react-dom' +import { Anchor, Button, Card, Col, Divider, Row, Spin } from 'antd' +import { AntIcon, ComponentDynamic, Container } from 'components' +import { isEqual, merge } from 'lodash' + +const parts = [ + { + // title: '标题', + component: () => import('./part'), + }, +] + +export default class index extends Component { + state = { + loading: true, + record: null, + saveDisabled: true, + saving: false, + } + + // 子表单实例集合 + children = [] + + // 整合提交数据 + formData = {} + + // 锚点挂载DOM + container = window + + /** + * 阻止外部组件引发的渲染,提升性能 + * 可自行添加渲染条件 + * [必要] + * @param {*} props + * @param {*} state + * @returns + */ + shouldComponentUpdate(props, state) { + return !isEqual(this.state, state) + } + + /** + * DOM加载完成钩子,可在此获取详细数据赋值到record + */ + componentDidMount() {} + + call(child, index) { + this.children[index] = child + if (this.children.filter(p => p).length === tabs.filter(p => p.show).length) { + this.setState({ saveDisabled: false }) + } + } + + /** + * 提交 + * [异步,必要] + * @returns + */ + async onSubmit() { + for (const child of this.children) { + try { + const data = await child.getData() + merge(this.formData, data) + } catch (e) { + return e + } + } + + //#region 提交数据 + this.setState({ saving: true }) + this.setState({ saving: false }) + //#endregion + } + + /** + * 设置锚点容器 + * [非必要] + * @param {*} container + */ + setContainer = container => { + this.container = (ReactDOM.findDOMNode(container) || {}).parentNode + } + + /** + * 渲染 + * 当前渲染结构已完善,非必要可以不用修改 + * [必要] + * @returns + */ + render() { + const { id } = this.props + + const { loading, record, saveDisabled, saving } = this.state + + return ( +
+ + + +
+
+ + {parts.map((item, i) => ( + +
+ {item.title &&
{item.title}
} + } + wrapperClassName={loading && 'h-400-min'} + > + {!loading && ( + this.call(child, i)} + /> + )} + +
+ {i < parts.length - 1 && } +
+ ))} +
+ + {/* 锚点,如果不需要可以删除以下节点 */} + + this.container} + offsetTop={24} + targetOffset={100} + wrapperStyle={{ backgroundColor: 'transparent' }} + onClick={e => e.preventDefault()} + > + {parts.map((part, i) => ( + + ))} + + + + +
+ +
+ + + + + +
+
+
+
+ ) + } +} diff --git a/web-react/public/seed/form/part.jsx b/web-react/public/seed/form/part.jsx new file mode 100644 index 0000000..9b70bef --- /dev/null +++ b/web-react/public/seed/form/part.jsx @@ -0,0 +1,115 @@ +import React, { Component } from 'react' +import { Form, Spin } from 'antd' +import { AntIcon } from 'components' +import { cloneDeep, isEqual } from 'lodash' + +const initialValues = {} + +const layout = { + labelCol: { flex: '150px' }, + wrapperCol: { flex: '1' }, +} + +export default class part extends Component { + state = { + loading: true, + codes: {}, + options: {}, + } + + // 表单实例 + form = React.createRef() + + // 初始化数据 + record = {} + + /** + * 阻止外部组件引发的渲染,提升性能 + * 可自行添加渲染条件 + * [必要] + * @param {*} props + * @param {*} state + * @returns + */ + shouldComponentUpdate(props, state) { + return !isEqual(this.state, state) + } + + /** + * DOM加载完成钩子,绑定数据 + */ + componentDidMount() { + this.fillData({ + record: this.props.record, + }) + } + + /** + * 加载完成,通知父级组件并传递自身 + */ + call() { + const { onRef } = this.props + if (onRef) onRef(this) + } + + /** + * 填充数据 + * 可以在设置this.record之后对其作出数据结构调整 + * [异步,必要] + * @param {*} params + */ + async fillData(params) { + this.record = cloneDeep(params.record) + //#region 从后端转换成前段所需格式 + //#endregion + this.form.current.setFieldsValue(this.record) + + this.setState({ loading: false }) + this.call() + } + + /** + * 获取数据 + * 可以对postData进行数据结构调整 + * [异步,必要] + * @returns + */ + async getData() { + const form = this.form.current + + const valid = await form.validateFields() + if (valid) { + const postData = form.getFieldsValue() + //#region 从前段转换后端所需格式 + //#endregion + return postData + } + } + + //#region 自定义方法 + /** + * 表单change事件处理,包括了所有字段的change + * [异步,非必要] + * @param {*} changedValues + * @param {*} allValues + */ + async onValuesChange(changedValues, allValues) {} + //#endregion + + render() { + const { loading } = this.state + + return ( + }> +
+ this.onValuesChange(changedValues, allValues) + } + >
+
+ ) + } +} diff --git a/web-react/src/assets/style/lib/anchor.less b/web-react/src/assets/style/lib/anchor.less index e37e90f..198ee0d 100644 --- a/web-react/src/assets/style/lib/anchor.less +++ b/web-react/src/assets/style/lib/anchor.less @@ -9,3 +9,6 @@ border-radius: 0; background-color: @primary-color; } +.ant-anchor-link-active { + background: linear-gradient(90deg, #fff, transparent); +} diff --git a/web-react/src/assets/style/lib/disabled.less b/web-react/src/assets/style/lib/disabled.less index 317f584..c975e17 100644 --- a/web-react/src/assets/style/lib/disabled.less +++ b/web-react/src/assets/style/lib/disabled.less @@ -21,6 +21,29 @@ box-shadow: @btn-primary-shadow; text-shadow: @btn-text-shadow; } +.ant-btn-danger-disabled, +.ant-btn-danger.disabled, +.ant-btn-danger[disabled], +.ant-btn-danger-disabled:hover, +.ant-btn-danger.disabled:hover, +.ant-btn-danger[disabled]:hover, +.ant-btn-danger-disabled:focus, +.ant-btn-danger.disabled:focus, +.ant-btn-danger[disabled]:focus, +.ant-btn-danger-disabled:active, +.ant-btn-danger.disabled:active, +.ant-btn-danger[disabled]:active, +.ant-btn-danger-disabled.active, +.ant-btn-danger.disabled.active, +.ant-btn-danger[disabled].active { + opacity: .5; + color: @btn-danger-color; + border-color: @btn-danger-border; + background-color: @btn-danger-bg; + box-shadow: @btn-primary-shadow; + text-shadow: @btn-text-shadow; +} +.ant-radio-button-wrapper-disabled, .ant-radio-button-wrapper-disabled:first-child, .ant-radio-button-wrapper-disabled:hover { opacity: .5; diff --git a/web-react/src/assets/style/lib/form-page.less b/web-react/src/assets/style/lib/form-page.less index 5446b96..ae47a95 100644 --- a/web-react/src/assets/style/lib/form-page.less +++ b/web-react/src/assets/style/lib/form-page.less @@ -12,8 +12,9 @@ height: 100%; >.ant-tabs { - >.ant-tabs-bar { + >.ant-tabs-nav { margin-bottom: 0; + padding: 0 @padding-md; background-color: @white; diff --git a/web-react/src/assets/style/lib/form.less b/web-react/src/assets/style/lib/form.less index 7f8a7ee..771d844 100644 --- a/web-react/src/assets/style/lib/form.less +++ b/web-react/src/assets/style/lib/form.less @@ -333,6 +333,9 @@ margin-bottom: @padding-xs; border-left: @border-width-base @border-style-base @normal-color; + &.ant-radio-button-wrapper-checked { + border-left-color: @primary-color; + } &:not(:first-child) { &::before { content: none; diff --git a/web-react/src/assets/style/lib/table.less b/web-react/src/assets/style/lib/table.less index 2094f66..0e748c7 100644 --- a/web-react/src/assets/style/lib/table.less +++ b/web-react/src/assets/style/lib/table.less @@ -1,7 +1,12 @@ @import (reference) '../extend.less'; .yo-query-bar { - margin-bottom: @padding-md; + margin-bottom: @padding-xs; + .ant-form-inline { + .ant-form-item { + margin-bottom: @padding-xs; + } + } } .yo-action-bar { @@ -69,6 +74,10 @@ } } +.ant-table-sticky-scroll { + display: none; +} + .yo-table { .ant-table { margin: 0 !important; @@ -146,11 +155,15 @@ } } - .ant-table-bordered { - > .ant-table-container { - border-top: @border-width-base @border-style-base @table-border-color; - } + .ant-table-bordered { + >.ant-table-container { + border-top: @border-width-base @border-style-base @table-border-color; } + } + + &--row-no { + background-color: @table-header-bg; + } } .yo-table-actions { diff --git a/web-react/src/assets/style/lib/width-height.less b/web-react/src/assets/style/lib/width-height.less index ecf56c9..ae39ddc 100644 --- a/web-react/src/assets/style/lib/width-height.less +++ b/web-react/src/assets/style/lib/width-height.less @@ -3,13 +3,30 @@ .width-height (@i) when (@i <=20) { @n : @i * 50; + @px : @n * 1px; .w-@{n} { - width: @n * 1px !important; + width: @px !important; + } + + .w-@{n}-min { + min-width: @px !important; + } + + .w-@{n}-max { + max-width: @px !important; } .h-@{n} { - height: @n * 1px !important; + height: @px !important; + } + + .h-@{n}-min { + min-height: @px !important; + } + + .h-@{n}-max { + max-height: @px !important; } .w-@{n}-p { diff --git a/web-react/src/assets/style/main.less b/web-react/src/assets/style/main.less index e0f4171..7cf67c9 100644 --- a/web-react/src/assets/style/main.less +++ b/web-react/src/assets/style/main.less @@ -17,14 +17,73 @@ width: 100%; height: 100%; + + background-color: @layout-header-background; >.ant-spin-nested-loading { height: 100%; >div>.ant-spin { max-height: none; + @-webkit-keyframes borderScale { + 0% { + border: 5px solid white; + } + 50% { + border: 25px solid transparent; + } + 100% { + border: 5px solid white; + } + } + @keyframes borderScale { + 0% { + border: 5px solid white; + } + 50% { + border: 25px solid transparent; + } + 100% { + border: 5px solid white; + } + } + .loader-container { + position: absolute; + top: 50%; + left: 50%; + + box-sizing: content-box; + width: 200px; + height: 200px; + margin: 0 auto; + margin-right: -50%; + + transform: translate(-50%, -50%); + -webkit-animation: borderScale 1s infinite ease-in-out; + animation: borderScale 1s infinite ease-in-out; + + color: white; + border: 5px solid transparent; + border-radius: 50%; + >p { + font-family: 'Raleway', sans-serif; + font-size: 2em; + font-weight: bold; + + position: absolute; + top: 50%; + left: 50%; + + margin-right: -50%; + + transform: translate(-50%, -50%); + } + } } >.ant-spin-container { width: 100%; height: 100%; + &.ant-spin-blur { + opacity: 0; + } } } } @@ -104,49 +163,6 @@ &--avatar { box-shadow: 0 0 0 2px @white; } - &--name { - font-weight: bolder; - - position: absolute; - left: 32px + @padding-sm * 2; - - transition: @animation-duration-slow; - - opacity: 0; - } - &--dropdown { - width: 200px; - - transition: @animation-duration-base; - transform: scaleY(0); - transform-origin: top; - - opacity: 0; - .ant-dropdown-menu { - box-shadow: none; - } - } - } - &.open { - width: 200px; - .user-container-inner { - background-color: @white; - box-shadow: @box-shadow-base; - } - .user { - &--name { - opacity: 1; - } - } - } - &.drop { - .user { - &--dropdown { - transform: scaleY(1); - - opacity: 1; - } - } } } } @@ -485,7 +501,6 @@ flex-direction: column; width: 100%; - min-width: @container-width; height: 100%; @layout-header-height: 54px; @@ -593,3 +608,13 @@ } } } +.yo-user-popover { + width: 280px; + padding-top: 0; + .ant-popover-arrow { + display: none; + } + .ant-popover-inner-content { + padding: 0; + } +} diff --git a/web-react/src/common/api/requests/business/houseSafety/houseCode.js b/web-react/src/common/api/requests/business/houseSafety/houseCode.js index 8aa62a1..67c442a 100644 --- a/web-react/src/common/api/requests/business/houseSafety/houseCode.js +++ b/web-react/src/common/api/requests/business/houseSafety/houseCode.js @@ -3,6 +3,7 @@ const urls = { houseCodeEdit: ['/houseCode/edit', 'post'], houseCodePage: ['/houseCode/page', 'post'], houseCodeNo: '/houseCode/getNextNoByCode', + houseCodeDetail: '/houseCode/detail' } export default urls \ No newline at end of file diff --git a/web-react/src/common/api/requests/business/houseSafety/houseZone.js b/web-react/src/common/api/requests/business/houseSafety/houseZone.js index 0aa569f..d042f01 100644 --- a/web-react/src/common/api/requests/business/houseSafety/houseZone.js +++ b/web-react/src/common/api/requests/business/houseSafety/houseZone.js @@ -7,7 +7,8 @@ const urls = { houseZoneList: '/houseZone/list', houseZoneAutoIncrement: '/houseZone/autoIncrement', - houseZoneAdd: ['/houseZone/add', 'post'] + houseZoneAdd: ['/houseZone/add', 'post'], + houseZoneEdit: ['/houseZone/edit', 'post'] } export default urls \ No newline at end of file diff --git a/web-react/src/components/ant-icon/index.jsx b/web-react/src/components/ant-icon/index.jsx index 62e0e47..c2d1e22 100644 --- a/web-react/src/components/ant-icon/index.jsx +++ b/web-react/src/components/ant-icon/index.jsx @@ -2,22 +2,29 @@ import React, { Component } from 'react' import * as Icon from '@ant-design/icons' export default class AntIcon extends Component { - render() { const type = (this.props.type || '').toUpperCase() if (type) { - if (type.indexOf('OUTLINED') >= 0 || type.indexOf('FILLED') >= 0 || type.indexOf('TWOTONE') >= 0) { + if ( + type.indexOf('OUTLINED') >= 0 || + type.indexOf('FILLED') >= 0 || + type.indexOf('TWOTONE') >= 0 + ) { const I = Icon[this.props.type] - return + return I ? : false } else { - const t = type.split('-').map(p => { - return p[0] + p.slice(1).toLowerCase() - }).join('') + 'Outlined' + const t = + type + .split('-') + .map(p => { + return p[0] + p.slice(1).toLowerCase() + }) + .join('') + 'Outlined' const I = Icon[t] - return + return I ? : false } } return <> } -} \ No newline at end of file +} diff --git a/web-react/src/components/authorized/index.jsx b/web-react/src/components/authorized/index.jsx index a325429..24c0d0e 100644 --- a/web-react/src/components/authorized/index.jsx +++ b/web-react/src/components/authorized/index.jsx @@ -46,7 +46,6 @@ const { getState, subscribe } = store const stroePath = 'user' export default class Auth extends Component { - state = getState(stroePath) constructor(props) { @@ -62,11 +61,10 @@ export default class Auth extends Component { } render() { - const flag = auth.call(this.state, this.props.auth) if (flag) { - return this.props.children + return this.props.children || <> } return <> diff --git a/web-react/src/components/index.js b/web-react/src/components/index.js index e27657e..757a45b 100644 --- a/web-react/src/components/index.js +++ b/web-react/src/components/index.js @@ -6,7 +6,7 @@ export { default as Container } from './container' export { default as IconSelector } from './icon-selector' export { default as Image } from './image' export { default as ModalForm } from './modal-form' -export { default as PhotoSwipe } from './photo-swipe' +export { default as PhotoPreview } from './photo-preview' export { default as QueryList } from './query-list' export { default as QueryTable } from './query-table' export { default as QueryTableActions } from './query-table-actions' diff --git a/web-react/src/components/photo-preview/index.jsx b/web-react/src/components/photo-preview/index.jsx new file mode 100644 index 0000000..824a6ed --- /dev/null +++ b/web-react/src/components/photo-preview/index.jsx @@ -0,0 +1,74 @@ +import React, { Component } from 'react' +import 'photoswipe/dist/photoswipe.css' +import 'photoswipe/dist/default-skin/default-skin.css' +import PhotoSwipe from 'photoswipe' +import PhotoSwipeUI_Default from 'photoswipe/dist/photoswipe-ui-default' + +const defaultOptions = { + index: 0, + bgOpacity: 0.75, +} + +export default class PhotoPreview extends Component { + initPhotoSwipe = (items = [], options = {}) => { + const pswpElement = this.refs.pswp + const gallery = new PhotoSwipe(pswpElement, PhotoSwipeUI_Default, items, { + ...defaultOptions, + ...options, + }) + gallery.init() + } + + render() { + return ( +
+
+
+
+
+
+
+
+
+
+
+ + + +
+
+
+
+
+
+
+
+
+
+
+ + +
+
+
+
+
+
+ ) + } +} diff --git a/web-react/src/components/photo-swipe/index.jsx b/web-react/src/components/photo-swipe/index.jsx deleted file mode 100644 index c3c90aa..0000000 --- a/web-react/src/components/photo-swipe/index.jsx +++ /dev/null @@ -1,11 +0,0 @@ -import React, { Component } from 'react' - -export default class PhotoSwipe extends Component { - render() { - return ( -
- -
- ) - } -} diff --git a/web-react/src/components/query-table/index.jsx b/web-react/src/components/query-table/index.jsx index b842720..d0028bf 100644 --- a/web-react/src/components/query-table/index.jsx +++ b/web-react/src/components/query-table/index.jsx @@ -2,7 +2,7 @@ import React, { Component } from 'react' import { Form, Button, Table, Tooltip } from 'antd' import { AntIcon } from 'components' -const propsMap = ['autoLoad', 'loadData', 'pageIndex', 'pageSize'] +const propsMap = ['columns', 'autoLoad', 'loadData', 'pageIndex', 'pageSize'] const clearChildren = data => { data.forEach(p => { @@ -17,6 +17,17 @@ const clearChildren = data => { return data } +const rowNoColumn = { + title: '#', + dataIndex: 'rowNo', + width: 30, + fixed: true, + align: 'center', + ellipsis: true, + className: 'yo-table--row-no', + render: (text, record, index) => index + 1, +} + /** * 渲染查询栏 * @returns @@ -64,7 +75,7 @@ export default class QueryTable extends Component { // 加载状态 loading: false, // 表格类型 - type: '', + type: 'tree', // 数据 dataSource: [], } @@ -105,6 +116,8 @@ export default class QueryTable extends Component { this.loadData = typeof this.props.loadData === 'function' ? this.props.loadData : async () => {} + this.rowNumber = typeof this.props.rowNumber === 'boolean' ? this.props.rowNumber : true + if (this.props.pageIndex) { this.pageIndex = this.props.pageIndex this.pagination.current = this.pageIndex @@ -113,6 +126,19 @@ export default class QueryTable extends Component { this.pageSize = this.props.pageSize this.pagination.pageSize = this.pageSize } + + // 默认排序 + if (this.props.columns) { + for (const column of this.props.columns) { + if (column.defaultSortOrder) { + this.sorter = { + sortField: column.dataIndex, + sortOrder: column.defaultSortOrder, + } + break + } + } + } } /** @@ -247,7 +273,9 @@ export default class QueryTable extends Component { } render() { - const { loading, dataSource } = this.state + const { rowNumber } = this + + const { loading, dataSource, type } = this.state const { query, operator, columns } = this.props @@ -262,11 +290,19 @@ export default class QueryTable extends Component { loading, pagination: this.pagination, dataSource, - columns: (columns || []).filter(p => !p.hidden), + columns: (() => { + const c = [] + if (type !== 'tree' && rowNumber) { + c.push(rowNoColumn) + } + c.push(...(columns || [])) + return c.filter(p => !p.hidden) + })(), bordered: true, size: 'middle', rowKey: record => record.id || Math.random().toString(16).slice(2), sticky: true, + scroll: { x: 'max-content' }, ...attrs, } diff --git a/web-react/src/pages/business/house/code/form/index.jsx b/web-react/src/pages/business/house/code/form/index.jsx index 865b1b1..ddd7aa4 100644 --- a/web-react/src/pages/business/house/code/form/index.jsx +++ b/web-react/src/pages/business/house/code/form/index.jsx @@ -1,17 +1,21 @@ import React, { Component } from 'react' -import { Button, Card, message as Message, Modal } from 'antd' -import { ComponentDynamic, Container } from 'components' +import { Button, Card, Col, message as Message, Modal, Row, Spin } from 'antd' +import { AntIcon, ComponentDynamic, Container } from 'components' import { isEqual } from 'lodash' import { api } from 'common/api' -const parts = [{ - component: () => import('./part') -}] +const parts = [ + { + component: () => import('./part'), + }, +] export default class index extends Component { - state = { - saving: false + loading: true, + record: null, + + saving: false, } children = [] @@ -22,21 +26,35 @@ export default class index extends Component { return !isEqual(this.state, state) } + componentDidMount() { + // 获取详细数据 + const { id } = this.props.param + if (id) { + api.houseCodeDetail({ id }).then(({ data }) => { + this.setState({ + record: data, + loading: false, + }) + }) + } + } + async onSubmit() { for (const child of this.children) { try { const data = await child.getData() this.formData = { ...this.formData, - ...data + ...data, } - } catch { - return + } catch (e) { + return e } } + //#region 提交数据 this.setState({ saving: true }) - if (!this.props.param.record) { + if (!this.state.record) { // 新增 try { const { success } = await api.houseCodeAdd(this.formData) @@ -44,12 +62,10 @@ export default class index extends Component { Message.success('保存成功') Modal.confirm({ content: '已添加成功,是否继续添加?', - onOk: () => { - - }, + onOk: () => {}, onCancel: () => { window.closeContentWindow() - } + }, }) } } finally { @@ -58,7 +74,10 @@ export default class index extends Component { } else { // 编辑 try { - const { success } = await api.houseCodeEdit(this.formData) + const { success } = await api.houseCodeEdit({ + id: this.state.record.id, + ...this.formData, + }) if (success) { Message.success('保存成功') } @@ -66,35 +85,57 @@ export default class index extends Component { this.setState({ saving: false }) } } + //#endregion } render() { + const { id } = this.props - const { id, param } = this.props + const { loading, record, saving } = this.state return (
- -
-
- - { - parts.map((item, i) => ( -
- {item.title &&
{parts.title}
} - this.children.push(r)} /> -
- )) - } -
+ + + +
+
+ + {parts.map((item, i) => ( +
+ {item.title &&
{item.title}
} + } + wrapperClassName={loading && 'h-400-min'} + > + {!loading && ( + this.children.push(r)} + /> + )} + +
+ ))} +
+ +
- - + +
diff --git a/web-react/src/pages/business/house/code/form/part.jsx b/web-react/src/pages/business/house/code/form/part.jsx index ed86511..2494ff3 100644 --- a/web-react/src/pages/business/house/code/form/part.jsx +++ b/web-react/src/pages/business/house/code/form/part.jsx @@ -1,36 +1,49 @@ import React, { Component } from 'react' -import { Button, Cascader, Form, Input, InputNumber, Radio, Spin, Select, Row, Col, Tag, Alert, Tooltip } from 'antd' +import { + Button, + Cascader, + Form, + Input, + InputNumber, + Radio, + Spin, + Select, + Row, + Col, + Tag, + Alert, + Tooltip, +} from 'antd' import { AntIcon, Auth } from 'components' -import { cloneDeep } from 'lodash' +import { cloneDeep, isEqual } from 'lodash' import getDictData from 'util/dic' import { api } from 'common/api' import { CITY } from 'util/global' const initialValues = { type: 1, - industry: 1 + industry: 1, } const labelCol = { flex: '150px' } const wrapperCol = { flex: '1' } export default class form extends Component { - state = { // 加载状态 loading: true, codes: { - dicHouseType: [], - dicHouseIndustry: [] + houseType: [], + houseIndustry: [], }, options: { areaTree: [], projects: [], - zones: [] + zones: [], }, houseCode: '', - showIndustry: false + showIndustry: false, } // 表单实例 @@ -41,6 +54,10 @@ export default class form extends Component { // 初始化数据 record = {} + shouldComponentUpdate(props, state) { + return !isEqual(this.state, state) + } + /** * mount后回调 */ @@ -48,7 +65,9 @@ export default class form extends Component { if (this.props.onRef) { this.props.onRef(this) } - this.fillData(this.props.param) + this.fillData({ + record: this.props.record, + }) } componentWillUnmount() { @@ -59,10 +78,9 @@ export default class form extends Component { * 填充数据 * 可以在设置this.record之后对其作出数据结构调整 * [异步,必要] - * @param {*} params + * @param {*} params */ async fillData(params) { - this.record = cloneDeep(params.record) //#region 从后端转换成前段所需格式 await this.initMap() @@ -75,12 +93,12 @@ export default class form extends Component { areaCode.substr(0, 4), areaCode.substr(0, 6), areaCode.substr(0, 9), - areaCode + areaCode, ] // 获取项目和片区列表 const data = await this.getProjectsAndZones({ areaCode: this.record.areaCode, - type + type, }) Object.assign(options, data) // 定位 @@ -88,19 +106,19 @@ export default class form extends Component { this.setMarker(position) this.map.setCenter(position) } - const codes = await getDictData('dic_house_type', 'dic_house_industry') + const codes = await getDictData('house_type', 'house_industry') const { data: areaTree } = await api.getAreaTree() options.areaTree = areaTree this.setState({ codes, - options + options, }) this.showHouseCode() //#endregion this.form.current.setFieldsValue(this.record) this.setState({ - loading: false + loading: false, }) } @@ -108,7 +126,7 @@ export default class form extends Component { * 获取数据 * 可以对postData进行数据结构调整 * [异步,必要] - * @returns + * @returns */ async getData() { const form = this.form.current @@ -116,9 +134,6 @@ export default class form extends Component { const valid = await form.validateFields() if (valid) { const postData = form.getFieldsValue() - if (this.record) { - postData.id = this.record.id - } //#region 从前段转换后端所需格式 if (postData.areaCode) { postData.areaCode = postData.areaCode[3] @@ -130,7 +145,6 @@ export default class form extends Component { //#region 自定义方法 initMap() { - // eslint-disable-next-line no-undef const amap = AMap @@ -140,7 +154,7 @@ export default class form extends Component { const district = new amap.DistrictSearch({ subdistrict: 0, extensions: 'all', - level: 'city' + level: 'city', }) district.search(city, (status, result) => { @@ -152,15 +166,15 @@ export default class form extends Component { const geocoder = new amap.Geocoder({ city }) geocoder.getLocation(city, (status, result) => { - - if (status !== 'complete' || !(result.geocodes && result.geocodes.length)) return + if (status !== 'complete' || !(result.geocodes && result.geocodes.length)) + return this.citycode = result.geocodes[0].addressComponent.citycode this.map = new amap.Map(this.refs.map, { mask, zoom: 12, - center: result.geocodes[0].location + center: result.geocodes[0].location, }) this.map.on('click', e => { @@ -176,7 +190,7 @@ export default class form extends Component { path, strokeColor: '#ccc', strokeWeight: 4, - map: this.map + map: this.map, }) } @@ -186,13 +200,13 @@ export default class form extends Component { const auto = new amap.AutoComplete({ input: this.refs['map-search'].input, city, - citylimit: true + citylimit: true, }) const placeSearch = new amap.PlaceSearch({ city, citylimit: true, - pageSize: 1 + pageSize: 1, }) auto.on('select', ({ poi: { name: keywords, adcode } }) => { @@ -206,15 +220,13 @@ export default class form extends Component { } }) }) - }) }) }) } setMarker(position, geocoder) { - - const set = (position) => { + const set = position => { if (this.marker) { this.marker.setPosition(position) } else { @@ -242,7 +254,6 @@ export default class form extends Component { set(position) resolve(position) - } else { console.error('根据经纬度查询地址失败') @@ -260,7 +271,7 @@ export default class form extends Component { this.form.current.setFieldsValue({ address, lng, - lat + lat, }) } @@ -279,13 +290,13 @@ export default class form extends Component { if (mode.includes('projects')) { const { data: projects } = await api.houseProjectList({ areaCode: areaCode[3], - type + type, }) result.projects = projects } if (mode.includes('zones')) { const { data: zones } = await api.houseZoneList({ - areaCode: areaCode[3] + areaCode: areaCode[3], }) result.zones = zones } @@ -300,7 +311,7 @@ export default class form extends Component { this.setState({ loading: true }) const { data: no } = await api.houseCodeNo({ projectId }) this.form.current.setFieldsValue({ - no + no, }) this.setState({ loading: false }) } @@ -308,7 +319,7 @@ export default class form extends Component { showHouseCode(values) { if (this.record) { this.setState({ - houseCode: this.record.houseCode + houseCode: this.record.houseCode, }) } else if (values) { const { type, industry, areaCode, projectId, no } = values @@ -323,7 +334,8 @@ export default class form extends Component { if (!industry) { this.setState({ houseCode: '' }) } else { - const tag = this.state.codes.dicHouseIndustry.find(p => p.code == industry).extCode.tag + const tag = this.state.codes.houseIndustry.find(p => p.code == industry) + .extCode.tag houseCode += `-${tag}` this.setState({ houseCode }) } @@ -340,16 +352,16 @@ export default class form extends Component { if (changedValues.hasOwnProperty('type')) { this.setState({ showIndustry: changedValues.type == 2, - loading: true + loading: true, }) const data = await this.getProjectsAndZones() form.setFieldsValue({ projectId: undefined }) this.setState({ options: { ...this.state.options, - ...data + ...data, }, - loading: false + loading: false, }) this.showHouseCode(form.getFieldsValue()) } @@ -362,9 +374,9 @@ export default class form extends Component { this.setState({ options: { ...this.state.options, - ...data + ...data, }, - loading: false + loading: false, }) this.showHouseCode(form.getFieldsValue()) } @@ -392,15 +404,14 @@ export default class form extends Component { this.setState({ options: { ...this.state.options, - ...data + ...data, }, - loading: false + loading: false, }) } //#endregion render() { - const { loading, codes, options, showIndustry, houseCode } = this.state return ( @@ -409,61 +420,70 @@ export default class form extends Component { ref={this.form} // labelCol={labelCol} // wrapperCol={wrapperCol} - onValuesChange={(changedValues, allValues) => this.onValuesChange(changedValues, allValues)} + onValuesChange={(changedValues, allValues) => + this.onValuesChange(changedValues, allValues) + } layout="vertical" - > }>
- +
- - - {codes.dicHouseType.map(item => ( - {item.value} + + + {codes.houseType.map(item => ( + + {item.value} + ))} - { - showIndustry && - - - {codes.dicHouseIndustry.map(item => ( - {item.value} + {showIndustry && ( + + + {codes.houseIndustry.map(item => ( + + {item.value} + ))} - } + )} - + labels.join(' - ')} + displayRender={labels => labels.join(' - ')} fieldNames={{ label: 'name', value: 'code', - children: 'children' + children: 'children', }} options={options.areaTree} expandTrigger="hover" @@ -473,13 +493,20 @@ export default class form extends Component { - + @@ -488,8 +515,18 @@ export default class form extends Component { - - + + @@ -497,9 +534,12 @@ export default class form extends Component { - + value.padStart(3, '0')} + formatter={value => value.padStart(3, '0')} max={999} min={1} precision={0} @@ -509,14 +549,10 @@ export default class form extends Component { /> - { - showIndustry && - - - } + {showIndustry && -} - { - houseCode && + {houseCode && ( {houseCode} + > + {houseCode} + - } + )} 房屋编码说明} - description={<> - 房屋所在市—县(市、区)—街道(乡、镇)—社区、居(村)委会)—项目—实物幢序号。 根据省厅既有建筑物编号规则,房屋所在区域编号按照市、县(市、区)、街道(乡、镇)、社区、居(村)委会)、项目分类,其中市、县(市)区部分按照《中华人民共和国行政区划代码》(GB2260)标准编码,街道(乡、镇)按《县以下行政区划代码编码规则》(GB10114-88)标准编码,社区、居(村)委会部分按照统计局提供编码设定。各地上报各街道社区名称后,上述编号由系统自动生成。
各社区下辖项目由各地负责统一编码,住宅项目序号一般一个小区一号,采用3位数,001号起编,范围为001~999。实物幢序号由各地负责统一编码,以幢为单位,采用3位数,001号起编,范围为001~999。 - } /> + description={ + <> + 房屋所在市 + + + + + —县(市、区) + + + —街道(乡、镇) + + + + —社区、居(村)委会) + + + + —项目 + + + + —实物幢序号 + + + 。 + 根据省厅既有建筑物编号规则,房屋所在区域编号按照市、县(市、区)、街道(乡、镇)、社区、居(村)委会)、项目分类,其中市、县(市)区部分按照《中华人民共和国行政区划代码》(GB2260)标准编码,街道(乡、镇)按《县以下行政区划代码编码规则》(GB10114-88)标准编码,社区、居(村)委会部分按照统计局提供编码设定。各地上报各街道社区名称后,上述编号由系统自动生成。 +
+ 各社区下辖项目由各地负责统一编码,住宅项目序号一般一个小区一号,采用3位数,001号起编,范围为001~999。实物幢序号由各地负责统一编码,以幢为单位,采用3位数,001号起编,范围为001~999。 + + } + />
- - {options.zones.map(item => ( - {item.name} + + {item.name} + ))} @@ -554,26 +627,58 @@ export default class form extends Component { - + - - + + - - + + - - + + diff --git a/web-react/src/pages/business/house/code/index.jsx b/web-react/src/pages/business/house/code/index.jsx index 9582ffa..8c6bae5 100644 --- a/web-react/src/pages/business/house/code/index.jsx +++ b/web-react/src/pages/business/house/code/index.jsx @@ -1,5 +1,16 @@ import React, { Component } from 'react' -import { Button, Card, Cascader, Form, Input, InputNumber, Popconfirm, message as Message, Radio, Select } from 'antd' +import { + Button, + Card, + Cascader, + Form, + Input, + InputNumber, + Popconfirm, + message as Message, + Radio, + Select, +} from 'antd' import { isEqual } from 'lodash' import { AntIcon, Auth, Container, QueryTable, QueryTableActions } from 'components' import { api } from 'common/api' @@ -10,25 +21,24 @@ import { getSearchInfo } from 'util/query' // 配置页面所需接口函数 const apiAction = { - page: api.houseCodePage + page: api.houseCodePage, } // 用于弹窗标题 const name = '房屋编码' export default class index extends Component { - state = { codes: { - dicHouseType: [], - dicHouseIndustry: [] + houseType: [], + houseIndustry: [], }, options: { - areaTree: [] + areaTree: [], }, - type: '' + type: '', } // 表格实例 @@ -41,14 +51,19 @@ export default class index extends Component { dataIndex: 'houseCode', sorter: true, width: 300, - render: (text, record) => `${record.areaName}-${record.roadName}-${record.commName}-${record.note}-${record.no.toString().padStart(3, '0')}` + render: (text, record) => + `${record.areaName}-${record.roadName}-${record.commName}-${record.note}-${record.no + .toString() + .padStart(3, '0')}`, }, { title: '房屋性质及行业', dataIndex: 'type', sorter: true, width: 150, - render: (text, record) => this.bindCodeValue(text, 'dic_house_type') + (text === 2 ? `(${this.bindCodeValue(record.industry, 'dic_house_industry')})` : '') + render: (text, record) => + this.bindCodeValue(text, 'house_type') + + (text === 2 ? `(${this.bindCodeValue(record.industry, 'house_industry')})` : ''), }, { title: '地址', @@ -65,7 +80,7 @@ export default class index extends Component { /** * 构造函数,在渲染前动态添加操作字段等 - * @param {*} props + * @param {*} props */ constructor(props) { super(props) @@ -77,20 +92,22 @@ export default class index extends Component { title: '操作', width: 150, dataIndex: 'actions', - render: (text, record) => ( - - this.onOpen(record)}>编辑 - - - this.onDelete(record)} - > - 删除 - - - ) + render: (text, record) => ( + + + this.onOpen(record)}>编辑 + + + this.onDelete(record)} + > + 删除 + + + + ), }) } } @@ -99,9 +116,9 @@ export default class index extends Component { * 阻止外部组件引发的渲染,提升性能 * 可自行添加渲染条件 * [必要] - * @param {*} props - * @param {*} state - * @returns + * @param {*} props + * @param {*} state + * @returns */ shouldComponentUpdate(props, state) { return !isEqual(this.state, state) @@ -114,28 +131,30 @@ export default class index extends Component { componentDidMount() { const { onLoading, onLoadData } = this.table.current onLoading() - getDictData('dic_house_type', 'dic_house_industry').then(async (res) => { + getDictData('house_type', 'house_industry').then(async res => { const { data } = await api.getAreaTree() - this.setState({ - codes: res, - options: { - areaTree: data + this.setState( + { + codes: res, + options: { + areaTree: data, + }, + }, + () => { + onLoadData() } - }, () => { - onLoadData() - }) + ) }) } /** * 调用加载数据接口,可在调用前对query进行处理 * [异步,必要] - * @param {*} params - * @param {*} query - * @returns + * @param {*} params + * @param {*} query + * @returns */ loadData = async (params, query) => { - if (query.areaCode) { query.areaCode = query.areaCode.pop() } @@ -146,8 +165,8 @@ export default class index extends Component { no: '=', type: '=', address: 'like', - houseCode: 'like' - } + houseCode: 'like', + }, }) const { data } = await apiAction.page({ @@ -159,15 +178,15 @@ export default class index extends Component { /** * 绑定字典数据 - * @param {*} code - * @param {*} name - * @returns + * @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) + const c = codes.find(p => p.code == code) if (c) { return c.value } @@ -177,8 +196,8 @@ export default class index extends Component { /** * 打开新增/编辑弹窗 - * @param {*} modal - * @param {*} record + * @param {*} modal + * @param {*} record */ onOpen(record) { const path = 'business/house/code/form' @@ -187,11 +206,13 @@ export default class index extends Component { title: record ? '修改房屋编码' : '新增房屋编码', subTitle: record && - `${record.areaName}-${record.roadName}-${record.commName}-${record.note}-${record.no.toString().padStart(3, '0')}`, + `${record.areaName}-${record.roadName}-${record.commName}-${record.note}-${record.no + .toString() + .padStart(3, '0')}`, path, param: { - record - } + id: record.id, + }, }) // modal.current.open({ // record @@ -201,8 +222,8 @@ export default class index extends Component { /** * 对表格上的操作进行统一处理 * [异步] - * @param {*} action - * @param {*} successMessage + * @param {*} action + * @param {*} successMessage */ async onAction(action, successMessage) { const { onLoading, onLoaded, onReloadData } = this.table.current @@ -222,20 +243,16 @@ export default class index extends Component { /** * 删除 - * @param {*} record + * @param {*} record */ onDelete(record) { - this.onAction( - apiAction.delete(record), - '删除成功' - ) + this.onAction(apiAction.delete(record), '删除成功') } //#region 自定义方法 //#endregion render() { - const { options, codes, type } = this.state return ( @@ -248,9 +265,9 @@ export default class index extends Component { loadData={this.loadData} columns={this.columns} queryInitialValues={{ - type: '' + type: '', }} - onQueryChange={(values) => { + onQueryChange={values => { if (values.hasOwnProperty('type')) { this.setState({ type: values.type }) } @@ -259,11 +276,11 @@ export default class index extends Component { labels.join(' - ')} + displayRender={labels => labels.join(' - ')} fieldNames={{ label: 'name', value: 'code', - children: 'children' + children: 'children', }} options={options.areaTree} className="w-400" @@ -273,7 +290,7 @@ export default class index extends Component { value && value.padStart(3, '0')} + formatter={value => value && value.padStart(3, '0')} max={999} min={1} precision={0} @@ -284,31 +301,28 @@ export default class index extends Component { 全部 - { - codes.dicHouseType.map(item => ( - {item.value} - )) - } + {codes.houseType.map(item => ( + + {item.value} + + ))} - { - type == 2 && + {type == 2 && ( - + {codes.houseIndustry.map(item => ( + + {item.value} + + ))} - } + )} @@ -322,7 +336,9 @@ export default class index extends Component { + > + 新增{name} + } /> diff --git a/web-react/src/pages/business/house/info/form/base/aspect.jsx b/web-react/src/pages/business/house/info/form/base/aspect.jsx new file mode 100644 index 0000000..021ac46 --- /dev/null +++ b/web-react/src/pages/business/house/info/form/base/aspect.jsx @@ -0,0 +1,230 @@ +import React, { Component } from 'react' +import { Form, Spin, Upload } from 'antd' +import { AntIcon, PhotoPreview } from 'components' +import { cloneDeep, isEqual } from 'lodash' +import { BlobToBase64, GetFileName, PreviewFile } from 'util/file' +import { api } from 'common/api' + +const initialValues = {} + +const layout = { + labelCol: { flex: '150px' }, + wrapperCol: { flex: '1' }, +} + +export default class aspect extends Component { + state = { + loading: true, + codes: {}, + options: {}, + } + + // 表单实例 + form = React.createRef() + + photoPreview = React.createRef() + + // 初始化数据 + record = {} + + /** + * 阻止外部组件引发的渲染,提升性能 + * 可自行添加渲染条件 + * [必要] + * @param {*} props + * @param {*} state + * @returns + */ + shouldComponentUpdate(props, state) { + return !isEqual(this.state, state) + } + + /** + * 绑定数据 + */ + componentDidMount() { + this.fillData({ + record: this.props.record, + }) + } + + /** + * DOM加载完成钩子,在此将自身传递给父级 + */ + call() { + if (this.props.onRef) { + this.props.onRef(this) + } + } + + /** + * 填充数据 + * 可以在设置this.record之后对其作出数据结构调整 + * [异步,必要] + * @param {*} params + */ + async fillData(params) { + this.record = cloneDeep(params.record) + //#region 从后端转换成前段所需格式 + if (this.record) { + const { houseInfo } = this.record + const fileValue = [] + const fileList = + !houseInfo.facadePhoto || !houseInfo.facadePhoto.length + ? [] + : houseInfo.facadePhoto.split(',') + for (const fileId of fileList) { + try { + const file = await PreviewFile(fileId) + const base64 = await BlobToBase64(file) + fileValue.push({ + uid: fileId, + response: fileId, + name: file.name, + url: base64, + status: 'done', + }) + } catch { + const { data: file } = await api.sysFileInfoDetail({ id: fileId }) + fileValue.push({ + uid: fileId, + response: '文件已丢失', + name: file.fileOriginName, + status: 'error', + }) + } + } + + houseInfo.facadePhoto = fileValue + } + //#endregion + this.form.current && this.form.current.setFieldsValue(this.record) + + this.setState({ loading: false }) + this.call() + } + + /** + * 获取数据 + * 可以对postData进行数据结构调整 + * [异步,必要] + * @returns + */ + async getData() { + const form = this.form.current + + const valid = await form.validateFields() + if (valid) { + const postData = form.getFieldsValue() + //#region 从前段转换后端所需格式 + postData.houseInfo.facadePhoto = postData.houseInfo.facadePhoto + .map(item => (item.uid.startsWith('rc-upload') ? item.response : item.uid)) + .join(',') + //#endregion + return postData + } + } + + //#region 自定义方法 + /** + * 表单change事件处理,包括了所有字段的change + * [异步,非必要] + * @param {*} changedValues + * @param {*} allValues + */ + async onValuesChange(changedValues, allValues) {} + + async onFileUpload({ file, onProgress, onSuccess, onError }) { + onProgress({ + percent: 0, + }) + const fd = new FormData() + fd.append('file', file) + try { + const { data: fileId } = await api.sysFileInfoUpload(fd) + onSuccess(fileId) + } catch { + onError() + } + } + + async onFilePreview(file, key) { + const fileList = this.form.current + .getFieldValue(['houseInfo', key]) + .filter(p => p.status === 'done') + const items = [] + for (const _file of fileList) { + const img = new Image() + const src = _file.url || _file.thumbUrl + img.src = src + items.push({ + src, + w: img.naturalWidth, + h: img.naturalHeight, + }) + } + this.photoPreview.current.initPhotoSwipe(items, { + index: fileList.indexOf(file), + }) + } + + async onFileDownload(file) { + const { data, headers } = await api.sysFileInfoDownload({ id: file.response }) + const url = window.URL.createObjectURL(data) + const fileName = GetFileName(headers['content-disposition']) + const a = document.createElement('a') + a.href = url + a.download = fileName + a.click() + window.URL.revokeObjectURL(url) + a.remove() + } + //#endregion + + render() { + const { loading } = this.state + + return ( + }> +
+ this.onValuesChange(changedValues, allValues) + } + > + { + if (Array.isArray(e)) { + return e + } + return e && e.fileList + }} + > + this.onFileUpload(e)} + showUploadList={{ + showRemoveIcon: true, + showDownloadIcon: true, + }} + onPreview={file => this.onFilePreview(file, 'facadePhoto')} + onDownload={file => this.onFileDownload(file)} + > +
+ +
外立面照片
+
+
+
+
+ + +
+ ) + } +} diff --git a/web-react/src/pages/business/house/info/form/base/attachments.jsx b/web-react/src/pages/business/house/info/form/base/attachments.jsx new file mode 100644 index 0000000..b9ac6ce --- /dev/null +++ b/web-react/src/pages/business/house/info/form/base/attachments.jsx @@ -0,0 +1,238 @@ +import React, { Component } from 'react' +import { Button, Form, Spin, Upload } from 'antd' +import { AntIcon } from 'components' +import { cloneDeep, isEqual } from 'lodash' +import { BlobToBase64, GetFileName, PreviewFile } from 'util/file' +import { api } from 'common/api' + +const initialValues = {} + +const layout = { + labelCol: { flex: '150px' }, + wrapperCol: { flex: '1' }, +} + +const uploads = [ + { + key: 'anEntryDocument', + label: '立项文件', + }, + { + key: 'planningPermission', + label: '规划许可', + }, + { + key: 'completionRecord', + label: '竣工验收备案', + }, + { + key: 'monitorDocument', + label: '监理文件', + }, + { + key: 'identificationReport', + label: '鉴定报告', + }, + { + key: 'otherDocument', + label: '其他附件', + }, +] + +export default class attachments extends Component { + state = { + loading: true, + codes: {}, + options: {}, + } + + // 表单实例 + form = React.createRef() + + // 初始化数据 + record = {} + + /** + * 阻止外部组件引发的渲染,提升性能 + * 可自行添加渲染条件 + * [必要] + * @param {*} props + * @param {*} state + * @returns + */ + shouldComponentUpdate(props, state) { + return !isEqual(this.state, state) + } + + /** + * 绑定数据 + */ + componentDidMount() { + this.fillData({ + record: this.props.record, + }) + } + + /** + * DOM加载完成钩子,在此将自身传递给父级 + */ + call() { + if (this.props.onRef) { + this.props.onRef(this) + } + } + + /** + * 填充数据 + * 可以在设置this.record之后对其作出数据结构调整 + * [异步,必要] + * @param {*} params + */ + async fillData(params) { + this.record = cloneDeep(params.record) + //#region 从后端转换成前段所需格式 + if (this.record) { + const { houseInfo } = this.record + const keys = uploads.map(p => p.key) + for (const key of keys) { + const fileValue = [] + const fileList = + !houseInfo[key] || !houseInfo[key].length ? [] : houseInfo[key].split(',') + for (const fileId of fileList) { + try { + const file = await PreviewFile(fileId) + const base64 = await BlobToBase64(file) + fileValue.push({ + uid: fileId, + response: fileId, + name: file.name, + url: base64, + status: 'done', + }) + } catch { + const { data: file } = await api.sysFileInfoDetail({ id: fileId }) + fileValue.push({ + uid: fileId, + response: '文件已丢失', + name: file.fileOriginName, + status: 'error', + }) + } + } + + houseInfo[key] = fileValue + } + } + //#endregion + this.form.current && this.form.current.setFieldsValue(this.record) + + this.setState({ loading: false }) + this.call() + } + + /** + * 获取数据 + * 可以对postData进行数据结构调整 + * [异步,必要] + * @returns + */ + async getData() { + const form = this.form.current + + const valid = await form.validateFields() + if (valid) { + const postData = form.getFieldsValue() + //#region 从前段转换后端所需格式 + const { houseInfo } = postData + for (const key in houseInfo) { + houseInfo[key] = houseInfo[key] + .map(item => (item.uid.startsWith('rc-upload') ? item.response : item.uid)) + .join(',') + } + //#endregion + return postData + } + } + + //#region 自定义方法 + /** + * 表单change事件处理,包括了所有字段的change + * [异步,非必要] + * @param {*} changedValues + * @param {*} allValues + */ + async onValuesChange(changedValues, allValues) {} + + async onFileUpload({ file, onProgress, onSuccess, onError }) { + onProgress({ + percent: 0, + }) + const fd = new FormData() + fd.append('file', file) + try { + const { data: fileId } = await api.sysFileInfoUpload(fd) + onSuccess(fileId) + } catch { + onError() + } + } + + async onFileDownload(file) { + const { data, headers } = await api.sysFileInfoDownload({ id: file.response }) + const url = window.URL.createObjectURL(data) + const fileName = GetFileName(headers['content-disposition']) + const a = document.createElement('a') + a.href = url + a.download = fileName + a.click() + window.URL.revokeObjectURL(url) + a.remove() + } + //#endregion + + render() { + const { loading } = this.state + + return ( + }> +
+ this.onValuesChange(changedValues, allValues) + } + > + {uploads.map((item, i) => ( + { + if (Array.isArray(e)) { + return e + } + return e && e.fileList + }} + > + this.onFileUpload(e)} + showUploadList={{ + showRemoveIcon: true, + showDownloadIcon: true, + }} + onPreview={() => false} + onDownload={file => this.onFileDownload(file)} + > + + + + ))} +
+
+ ) + } +} diff --git a/web-react/src/pages/business/house/info/form/base/building.jsx b/web-react/src/pages/business/house/info/form/base/building.jsx index b193da6..a70abdc 100644 --- a/web-react/src/pages/business/house/info/form/base/building.jsx +++ b/web-react/src/pages/business/house/info/form/base/building.jsx @@ -1,357 +1,650 @@ import React, { Component } from 'react' -import { Row, Col, Form, Input, InputNumber, Radio, Checkbox, Switch, DatePicker, Spin } from 'antd' -import { cloneDeep, isEqual } from 'lodash' -import { api } from 'common/api' +import { + Button, + Row, + Col, + Form, + Input, + InputNumber, + Radio, + Checkbox, + Switch, + DatePicker, + Spin, +} from 'antd' +import { cloneDeep, first, isEqual, last, sortBy } from 'lodash' +import { AntIcon } from 'components' +import getDictData from 'util/dic' +import moment from 'moment' +import { CITY } from 'util/global' +import store from 'store' + +const { dispatch } = store const layout = { labelCol: { flex: '150px' }, - wrapperCol: { flex: '1' } + wrapperCol: { flex: '1' }, } -export default class building extends Component { +const checkboxKeys = ['insulationMaterial', 'wallMaterial'] +export default class building extends Component { state = { loading: true, codes: { landAttribute: [], houseStructureType: [], - houseSseismicGrade: [], + houseAseismicGrade: [], houseBaseInfo: [], houseInsulationMaterial: [], houseWallMaterial: [], houseFireproofGrade: [], houseBuildingCurtainWall: [], houseElevator: [], - } + }, + + showMap: false, + showKeepWarmMaterialText: false, } + form = React.createRef() - constructor(props) { - super(props) - - // 使父组件获取到当前组件实例 - if (props.onRef) { - props.onRef(this) - } - } - shouldComponentUpdate(props, state) { - // 在上级页签切换时,阻止当前组件渲染,以保证性能 return !isEqual(this.state, state) } componentDidMount() { - this.onFillData() - } - - onFillData = async () => { - await this.loadCodes() - this.setState({ - loading: false + this.fillData({ + record: this.props.record, }) } - onGetData = () => { - return new Promise((resolve, reject) => { - this.form.current.validateFields() - .then(values => { - const record = cloneDeep(values) - - resolve(record) - }).catch(err => { - reject(err) - }) + componentWillUnmount() { + dispatch({ + type: 'PATROL_REMOVE_INIT_GRADE_BY_COMPLETED_DATE', + id: this.props.id, }) } - loadCodes = async () => { - await api - .sysDictTypeDropDowns({ - code: [ - 'dic_land_attribute', - 'dic_house_structure_type', - 'dic_house_aseismic_grade', - 'dic_house_base_info', - 'dic_house_insulation_material', - 'dic_house_wall_material', - 'dic_house_fireproof_grade', - 'dic_house_building_curtain_wall', - 'dic_house_elevator', - ], - }) - .then( - ({ - data: { - dic_land_attribute, - dic_house_structure_type, - dic_house_aseismic_grade, - dic_house_base_info, - dic_house_insulation_material, - dic_house_wall_material, - dic_house_fireproof_grade, - dic_house_building_curtain_wall, - dic_house_elevator, + call() { + if (this.props.onRef) { + this.props.onRef(this) + } + } + + /** + * 填充数据 + * 可以在设置this.record之后对其作出数据结构调整 + * [异步,必要] + * @param {*} params + */ + async fillData(params) { + this.record = cloneDeep(params.record) + const _state = { loading: false } + //#region 从后端转换成前段所需格式 + if (this.record) { + const { houseInfo } = this.record + if (houseInfo.completedDate) { + houseInfo.completedDate = moment(houseInfo.completedDate) + dispatch({ + type: 'PATROL_INIT_GRADE_BY_COMPLETED_DATE', + date: { + id: this.props.id, + value: +houseInfo.completedDate.format('YYYY'), }, - }) => { - this.setState({ - codes: { - landAttribute: dic_land_attribute, - houseStructureType: dic_house_structure_type, - houseSseismicGrade: dic_house_aseismic_grade, - houseBaseInfo: dic_house_base_info, - houseInsulationMaterial: dic_house_insulation_material, - houseWallMaterial: dic_house_wall_material, - houseFireproofGrade: dic_house_fireproof_grade, - houseBuildingCurtainWall: dic_house_building_curtain_wall, - houseElevator: dic_house_elevator, - } - }) + }) + } + + // checkbox + checkboxKeys.forEach(key => { + if (houseInfo[key]) { + houseInfo[key] = houseInfo[key].split(',') } - ) + }) + + if (houseInfo.insulationMaterial) { + _state.showKeepWarmMaterialText = houseInfo.insulationMaterial.includes('100') + } + } + _state.codes = await getDictData( + 'land_attribute', + 'house_structure_type', + 'house_aseismic_grade', + 'house_base_info', + 'house_insulation_material', + 'house_wall_material', + 'house_fireproof_grade', + 'house_building_curtain_wall', + 'house_elevator' + ) + + //#endregion + this.form.current && this.form.current.setFieldsValue(this.record) + + this.setState(_state) + this.call() } + /** + * 获取数据 + * 可以对postData进行数据结构调整 + * [异步,必要] + * @returns + */ + async getData() { + const form = this.form.current + + const valid = await form.validateFields() + if (valid) { + const postData = form.getFieldsValue() + //#region 从前段转换后端所需格式 + const { houseInfo } = postData + if (houseInfo.completedDate) { + houseInfo.completedDate = houseInfo.completedDate.format('YYYY-MM-DD') + } + + // checkbox + checkboxKeys.forEach(key => { + if (houseInfo[key]) { + houseInfo[key] = sortBy(houseInfo[key], p => +p).join(',') + } + }) + //#endregion + return postData + } + } + + onValuesChange(changedValues, allValues) { + const form = this.form.current + const { houseInfo } = changedValues + if ( + houseInfo.hasOwnProperty('landFloorCount') || + houseInfo.hasOwnProperty('underFloorCount') + ) { + const { + houseInfo: { landFloorCount, underFloorCount }, + } = allValues + form.setFieldsValue({ + houseInfo: { + totalFloor: +landFloorCount + +underFloorCount, + }, + }) + } + + if (houseInfo.hasOwnProperty('insulationMaterial')) { + const value = this.checkedNone(houseInfo.insulationMaterial, 'insulationMaterial') + this.setState({ + showKeepWarmMaterialText: value.includes('100'), + }) + } + + if (houseInfo.hasOwnProperty('completedDate')) { + dispatch({ + type: 'PATROL_INIT_GRADE_BY_COMPLETED_DATE', + date: { + id: this.props.id, + value: +houseInfo.completedDate.format('YYYY'), + }, + }) + } + } + + //#region 自定义方法 + onShowMap() { + this.setState({ showMap: true }, async () => { + await this.initMap() + const { lng, lat } = this.record.houseCode + const position = [lng, lat] + this.setMarker(position) + this.map.setCenter(position) + }) + } + + initMap() { + // eslint-disable-next-line no-undef + const amap = AMap + + return new Promise(resolve => { + const city = CITY + + const district = new amap.DistrictSearch({ + subdistrict: 0, + extensions: 'all', + level: 'city', + }) + + district.search(city, (status, result) => { + const bounds = result.districtList[0].boundaries, + mask = [] + for (let i = 0; i < bounds.length; i += 1) { + mask.push([bounds[i]]) + } + + const geocoder = new amap.Geocoder({ city }) + geocoder.getLocation(city, (status, result) => { + if (status !== 'complete' || !(result.geocodes && result.geocodes.length)) + return + + this.citycode = result.geocodes[0].addressComponent.citycode + + this.map = new amap.Map(this.refs.map, { + mask, + zoom: 12, + center: result.geocodes[0].location, + }) + + this.map.on('click', e => { + this.setMarker(e.lnglat, geocoder) + }) + + this.map.on('complete', () => { + this.map.setFitView() + this.map.setZoom(12) + + for (const path of bounds) { + new amap.Polyline({ + path, + strokeColor: '#ccc', + strokeWeight: 4, + map: this.map, + }) + } + + resolve() + }) + + const auto = new amap.AutoComplete({ + input: this.refs['map-search'].input, + city, + citylimit: true, + }) + + const placeSearch = new amap.PlaceSearch({ + city, + citylimit: true, + pageSize: 1, + }) + + auto.on('select', ({ poi: { name: keywords, adcode } }) => { + placeSearch.search(keywords, async (status, result) => { + const { + poiList: { pois }, + } = result + for (const poi of pois) { + await this.setMarker(poi.location, geocoder) + this.map.setCenter(poi.location) + } + }) + }) + }) + }) + }) + } + + setMarker(position, geocoder) { + const set = position => { + if (this.marker) { + this.marker.setPosition(position) + } else { + this.marker = new amap.Marker({ + map: this.map, + icon: '//a.amap.com/jsapi_demos/static/demo-center/icons/poi-marker-default.png', + position, + offset: new amap.Pixel(-13, -30), + }) + } + } + + // eslint-disable-next-line no-undef + const amap = AMap + + return new Promise((resolve, reject) => { + if (geocoder) { + geocoder.getAddress(position, (status, result) => { + if (status === 'complete' && result.regeocode) { + if (result.regeocode.addressComponent.citycode !== this.citycode) { + // 如果选到了别的城市,则中断 + return + } + this.setPosition(result.regeocode.formattedAddress, position) + + set(position) + resolve(position) + } else { + console.error('根据经纬度查询地址失败') + + reject() + } + }) + } else { + set(position) + resolve(position) + } + }) + } + + setPosition(address, { lng, lat }) { + this.form.current.setFieldsValue({ houseCode: { address, lng, lat } }) + } + + checkedNone(value, key) { + const form = this.form.current + if (first(value) == 0 && value.length > 1) { + // 在'无'之后选中其他值 + value.shift() + form.setFieldsValue({ + houseInfo: { [key]: value }, + }) + } else if (last(value) == 0 && value.length > 1) { + // 在其他值之后选中'无' + value = ['0'] + form.setFieldsValue({ + houseInfo: { [key]: value }, + }) + } + return value + } + //#endregion + render() { + const { loading, codes, showMap, showKeepWarmMaterialText } = this.state + return ( - + }>
+ this.onValuesChange(changedValues, allValues) + } > - + - + - + - - - - { - this.state.codes.landAttribute.map(item => { - return ( - {item.value} - ) - }) - } - - - - - - - - - - - - - - - - - - - -
-
- -
-
-
-
- - - - + + + + {codes.landAttribute.map(item => { + return ( + + {item.value} + + ) + })} + + + + + + - { - this.state.codes.houseStructureType.map(item => { - return ( - {item.value} - ) - }) - } + + + + + + + + + + + + {showMap ? ( +
+
+ +
+
+
+ ) : ( + + )} +
+ + + {codes.houseStructureType.map(item => { + return ( + + {item.value} + + ) + })} + + + + + + + {codes.houseAseismicGrade.map(item => { + return ( + + {item.value} + + ) + })} - - - { - this.state.codes.houseSseismicGrade.map(item => { - return ( - {item.value} - ) - }) - } + + + {codes.houseBaseInfo.map(item => { + return ( + + {item.value} + + ) + })} - - - - { - this.state.codes.houseBaseInfo.map(item => { - return ( - {item.value} - ) - }) - } - + + + + + {codes.houseInsulationMaterial.map(item => { + return ( + + {item.value} + + ) + })} + + + {showKeepWarmMaterialText && ( + + + + )} + + + {codes.houseWallMaterial.map(item => { + return ( + + {item.value} + + ) + })} + + + + 外墙外保温材料 +
+ 防火等级 + + } + name={['houseInfo', 'fireproofGrade']} + > + + {codes.houseFireproofGrade.map(item => { + return ( + + {item.value} + + ) + })} + +
+ + + {codes.houseBuildingCurtainWall.map(item => { + return ( + + {item.value} + + ) + })} + + + + + + + + + + + + + + + + - - { - this.state.codes.houseInsulationMaterial.map(item => { - return ( - {item.value} - ) - }) - } - - - - - - - - { - this.state.codes.houseWallMaterial.map(item => { - return ( - {item.value} - ) - }) - } - - - - - 外墙外保温材料
防火等级}> - { - this.state.codes.houseFireproofGrade.map(item => { - return ( - {item.value} - ) - }) - } - -
- - - - - { - this.state.codes.houseBuildingCurtainWall.map(item => { - return ( - {item.value} - ) - }) - } + {codes.houseElevator.map(item => { + return ( + + {item.value} + + ) + })} - - - - - - - - - - - - - - - - - - { - this.state.codes.houseElevator.map(item => { - return ( - {item.value} - ) - }) - } - - - - - - + { /*$root.transfer.completedYear = date.format('YYYY') */ }} + onChange={date => { + /*$root.transfer.completedYear = date.format('YYYY') */ + }} className="w-100-p" placeholder="请选择竣工日期" /> - - + + - + + + @@ -360,14 +653,22 @@ export default class building extends Component { - - + + - + + + @@ -376,16 +677,22 @@ export default class building extends Component { - - + + - + + + @@ -394,16 +701,24 @@ export default class building extends Component { - - + + - + + + 单元 @@ -412,16 +727,24 @@ export default class building extends Component { - - + + - + + + @@ -430,185 +753,162 @@ export default class building extends Component { - + - + - + - + - + - + - -
- - - + + + + + - - - - - - - - - + +
层;
+
+ + + + - - - - - - - - - + +
层;
+
+ + + + - - - - - - -
+
+
+
+ +
-
- - - - 地上第 - - - - - - - - 层,至 - - - - - - - - 层为商业用房 - - - - - - - 地上 - - - - - - - - 层为车棚层 - - - - - - - 地上第 - - - - - - - - 层,至 - - - - - - - - 层为住宅 - - - -
+ + + 地上第 + + + + + + + + 层,至 + + + + + + + + 层为商业用房; + + + 地上 + + + + + + + + 层为车棚层; + + + 地上第 + + + + + + + + 层,至 + + + + + + + + 层为住宅 + +
diff --git a/web-react/src/pages/business/house/info/form/base/drawing.jsx b/web-react/src/pages/business/house/info/form/base/drawing.jsx new file mode 100644 index 0000000..4204403 --- /dev/null +++ b/web-react/src/pages/business/house/info/form/base/drawing.jsx @@ -0,0 +1,167 @@ +import React, { Component } from 'react' +import { Checkbox, Col, Form, Input, Row, Spin } from 'antd' +import { AntIcon } from 'components' +import { cloneDeep, isEqual, sortBy } from 'lodash' +import getDictData from 'util/dic' + +const initialValues = {} + +const layout = { + labelCol: { flex: '150px' }, + wrapperCol: { flex: '1' }, +} + +export default class drawing extends Component { + state = { + loading: true, + codes: { + houseStorageOfDrawings: [], + }, + options: {}, + + showDrawingMaterialText: false, + } + + // 表单实例 + form = React.createRef() + + // 初始化数据 + record = {} + + /** + * 阻止外部组件引发的渲染,提升性能 + * 可自行添加渲染条件 + * [必要] + * @param {*} props + * @param {*} state + * @returns + */ + shouldComponentUpdate(props, state) { + return !isEqual(this.state, state) + } + + /** + * DOM加载完成钩子,绑定数据 + */ + componentDidMount() { + this.fillData({ + record: this.props.record, + }) + } + + /** + * 加载完成,通知父级组件并传递自身 + */ + call() { + const { onRef } = this.props + if (onRef) onRef(this) + } + + /** + * 填充数据 + * 可以在设置this.record之后对其作出数据结构调整 + * [异步,必要] + * @param {*} params + */ + async fillData(params) { + this.record = cloneDeep(params.record) + //#region 从后端转换成前段所需格式 + if (this.record) { + const { houseInfo } = this.record + // checkbox + if (houseInfo.drawingMaterial) { + houseInfo.drawingMaterial = houseInfo.drawingMaterial.split(',') + } + this.setState({ + showDrawingMaterialText: + !!houseInfo.drawingMaterial && houseInfo.drawingMaterial.includes('100'), + }) + } + const codes = await getDictData('house_storage_of_drawings') + console.log(codes) + this.setState({ codes }) + //#endregion + this.form.current && this.form.current.setFieldsValue(this.record) + + this.setState({ loading: false }) + this.call() + } + + /** + * 获取数据 + * 可以对postData进行数据结构调整 + * [异步,必要] + * @returns + */ + async getData() { + const form = this.form.current + + const valid = await form.validateFields() + if (valid) { + const postData = form.getFieldsValue() + //#region 从前段转换后端所需格式 + const { houseInfo } = postData + // checkbox + if (houseInfo.drawingMaterial) { + houseInfo.drawingMaterial = sortBy(houseInfo.drawingMaterial, p => +p).join(',') + } + //#endregion + return postData + } + } + + //#region 自定义方法 + /** + * 表单change事件处理,包括了所有字段的change + * [异步,非必要] + * @param {*} changedValues + * @param {*} allValues + */ + async onValuesChange(changedValues, allValues) { + const { houseInfo } = changedValues + if (houseInfo.hasOwnProperty('drawingMaterial')) { + this.setState({ showDrawingMaterialText: houseInfo.drawingMaterial.includes('100') }) + } + } + //#endregion + + render() { + const { loading, codes, showDrawingMaterialText } = this.state + + return ( + }> + + this.onValuesChange(changedValues, allValues) + } + > + + + {codes.houseStorageOfDrawings.map(item => ( + + {item.value} + + ))} + + + {showDrawingMaterialText && ( + + + + )} + + + ) + } +} diff --git a/web-react/src/pages/business/house/info/form/base/identification.jsx b/web-react/src/pages/business/house/info/form/base/identification.jsx new file mode 100644 index 0000000..48da9a9 --- /dev/null +++ b/web-react/src/pages/business/house/info/form/base/identification.jsx @@ -0,0 +1,155 @@ +import React, { Component } from 'react' +import { Form, Radio, Spin } from 'antd' +import { AntIcon } from 'components' +import { cloneDeep, isEqual } from 'lodash' +import getDictData from 'util/dic' + +const initialValues = {} + +const layout = { + labelCol: { flex: '150px' }, + wrapperCol: { flex: '1' }, +} + +export default class identification extends Component { + state = { + loading: true, + codes: { + houseIdentification: [], + houseGovernment: [], + houseUsedStatus: [], + houseGrade: [], + }, + options: {}, + } + + // 表单实例 + form = React.createRef() + + // 初始化数据 + record = {} + + /** + * 阻止外部组件引发的渲染,提升性能 + * 可自行添加渲染条件 + * [必要] + * @param {*} props + * @param {*} state + * @returns + */ + shouldComponentUpdate(props, state) { + return !isEqual(this.state, state) + } + + /** + * DOM加载完成钩子,绑定数据 + */ + componentDidMount() { + this.fillData({ + record: this.props.record, + }) + } + + /** + * 加载完成,通知父级组件并传递自身 + */ + call() { + const { onRef } = this.props + if (onRef) onRef(this) + } + + /** + * 填充数据 + * 可以在设置this.record之后对其作出数据结构调整 + * [异步,必要] + * @param {*} params + */ + async fillData(params) { + this.record = cloneDeep(params.record) + //#region 从后端转换成前段所需格式 + const codes = await getDictData( + 'house_identification', + 'house_government', + 'house_used_status', + 'house_grade' + ) + this.setState({ codes }) + //#endregion + this.form.current && this.form.current.setFieldsValue(this.record) + + this.setState({ loading: false }) + this.call() + } + + /** + * 获取数据 + * 可以对postData进行数据结构调整 + * [异步,必要] + * @returns + */ + async getData() { + const form = this.form.current + + const valid = await form.validateFields() + if (valid) { + const postData = form.getFieldsValue() + //#region 从前段转换后端所需格式 + //#endregion + return postData + } + } + + //#region 自定义方法 + /** + * 表单change事件处理,包括了所有字段的change + * [异步,非必要] + * @param {*} changedValues + * @param {*} allValues + */ + async onValuesChange(changedValues, allValues) {} + //#endregion + + render() { + const { loading, codes } = this.state + + return ( + }> +
+ this.onValuesChange(changedValues, allValues) + } + > + + + {codes.houseUsedStatus.map(item => ( + + {item.value} + + ))} + + + + + {codes.houseGrade.map(item => ( + + {item.value} + + ))} + + +
+
+ ) + } +} diff --git a/web-react/src/pages/business/house/info/form/base/index.jsx b/web-react/src/pages/business/house/info/form/base/index.jsx index b16c947..4e857ba 100644 --- a/web-react/src/pages/business/house/info/form/base/index.jsx +++ b/web-react/src/pages/business/house/info/form/base/index.jsx @@ -1,8 +1,8 @@ import React, { Component } from 'react' import ReactDOM from 'react-dom' -import { Row, Col, Card, Anchor } from 'antd' -import { defaultsDeep } from 'lodash' -import { ComponentDynamic, Container } from 'components' +import { Row, Col, Card, Anchor, Spin, Divider } from 'antd' +import { merge } from 'lodash' +import { AntIcon, ComponentDynamic, Container } from 'components' const parts = [ { @@ -10,107 +10,113 @@ const parts = [ component: () => import('./building'), }, { - title: '建筑物基本信息', - component: () => import('./building'), + title: '权属情况', + component: () => import('./ownership'), }, { - title: '建筑物基本信息', - component: () => import('./building'), + title: '调查情况', + component: () => import('./investigation'), }, { - title: '建筑物基本信息', - component: () => import('./building'), + title: '鉴定治理', + component: () => import('./identification'), }, { - title: '建筑物基本信息', - component: () => import('./building'), + title: '图纸资料存档处', + component: () => import('./drawing'), }, { - title: '建筑物基本信息', - component: () => import('./building'), + title: '相关附件资料', + component: () => import('./attachments'), + }, + { + title: '建筑概貌', + component: () => import('./aspect'), + }, + { + title: '调查单位', + component: () => import('./unit'), }, ] export default class index extends Component { - container = window - forms = [] - constructor(props) { - super(props) + children = [] - // 使父组件获取到当前组件实例 - if (props.onRef) { - props.onRef(this) + formData = {} + + shouldComponentUpdate(props) { + return this.props.loading !== props.loading + } + + // 通知上层组件已加载完毕 + call(child, index) { + this.children[index] = child + if (this.children.filter(p => p).length === parts.length) { + if (this.props.onRef) { + this.props.onRef(this) + } } } - getContainer = () => { - return this.container - } - - setContainer = (container) => { + setContainer = container => { this.container = (ReactDOM.findDOMNode(container) || {}).parentNode } - onGetData = () => { - return new Promise(async (resolve, reject) => { - let formData = {}, - flag = true - for (let i = 0; i < this.forms.length; i++) { - const form = this.forms[i] - try { - const data = await form.onGetData() - formData = defaultsDeep(formData, data) - } catch (err) { - flag = false - reject(err) - } - } - if (flag) { - resolve(formData) - } - }) + async getData() { + for (const child of this.children) { + const data = await child.getData() + merge(this.formData, data) + } + + return this.formData } render() { + const { id, loading } = this.props + return (
+
- { - parts.map((part, i) => { - return ( -
- {part.title &&
{part.title}
} - this.forms.push(c)} /> -
- ) - }) - } + {parts.map((part, i) => ( + +
+ {part.title &&
{part.title}
} + } + wrapperClassName={loading && 'h-400-min'} + > + {!loading && ( + this.call(child, i)} + /> + )} + +
+ {i < parts.length - 1 && } +
+ ))}
this.container} offsetTop={24} - targetOffset={48} + targetOffset={100} wrapperStyle={{ backgroundColor: 'transparent' }} - onClick={(e) => e.preventDefault()} + onClick={e => e.preventDefault()} > - { - parts.map((part, i) => { - return ( - - ) - }) - } + {parts.map((part, i) => ( + + ))} diff --git a/web-react/src/pages/business/house/info/form/base/investigation.jsx b/web-react/src/pages/business/house/info/form/base/investigation.jsx new file mode 100644 index 0000000..cbc8794 --- /dev/null +++ b/web-react/src/pages/business/house/info/form/base/investigation.jsx @@ -0,0 +1,301 @@ +import React, { Component } from 'react' +import { Checkbox, Form, Input, Radio, Spin } from 'antd' +import { AntIcon } from 'components' +import { cloneDeep, first, isEqual, last, sortBy } from 'lodash' +import getDictData from 'util/dic' + +const initialValues = {} + +const layout = { + labelCol: { flex: '150px' }, + wrapperCol: { flex: '1' }, +} + +const checkboxKeys = [ + 'houseSite', + 'adjacentConstruction', + 'chemicalErosion', + 'repairAndReinforce', + 'historicalCalamity', + 'functionalChange', +] + +export default class investigation extends Component { + state = { + loading: true, + codes: { + houseHouseSite: [], + houseAdjacentConstruction: [], + houseChemicalErosion: [], + houseStructuralDismantling: [], + houseAddingLayer: [], + houseRepairAndReinforce: [], + houseHistoricalCalamity: [], + houseFunctionalChange: [], + }, + options: {}, + } + + // 表单实例 + form = React.createRef() + + // 初始化数据 + record = {} + + /** + * 阻止外部组件引发的渲染,提升性能 + * 可自行添加渲染条件 + * [必要] + * @param {*} props + * @param {*} state + * @returns + */ + shouldComponentUpdate(props, state) { + return !isEqual(this.state, state) + } + + /** + * DOM加载完成钩子,绑定数据 + */ + componentDidMount() { + this.fillData({ + record: this.props.record, + }) + } + + /** + * 加载完成,通知父级组件并传递自身 + */ + call() { + const { onRef } = this.props + if (onRef) onRef(this) + } + + /** + * 填充数据 + * 可以在设置this.record之后对其作出数据结构调整 + * [异步,必要] + * @param {*} params + */ + async fillData(params) { + this.record = cloneDeep(params.record) + //#region 从后端转换成前段所需格式 + if (this.record) { + const { houseInfo } = this.record + // checkbox + checkboxKeys.forEach(key => { + if (houseInfo[key]) { + houseInfo[key] = houseInfo[key].split(',') + } + }) + } + const codes = await getDictData( + 'house_house_site', + 'house_adjacent_construction', + 'house_chemical_erosion', + 'house_structural_dismantling', + 'house_adding_layer', + 'house_repair_and_reinforce', + 'house_historical_calamity', + 'house_functional_change' + ) + this.setState({ codes }) + //#endregion + this.form.current.setFieldsValue(this.record) + + this.setState({ loading: false }) + this.call() + } + + /** + * 获取数据 + * 可以对postData进行数据结构调整 + * [异步,必要] + * @returns + */ + async getData() { + const form = this.form.current + + const valid = await form.validateFields() + if (valid) { + const postData = form.getFieldsValue() + //#region 从前段转换后端所需格式 + const { houseInfo } = postData + // checkbox + checkboxKeys.forEach(key => { + if (houseInfo[key]) { + houseInfo[key] = sortBy(houseInfo[key], p => +p).join(',') + } + }) + //#endregion + return postData + } + } + + //#region 自定义方法 + /** + * 表单change事件处理,包括了所有字段的change + * [异步,非必要] + * @param {*} changedValues + * @param {*} allValues + */ + async onValuesChange(changedValues, allValues) { + const { houseInfo } = changedValues + const key = Object.keys(houseInfo).shift() + if ( + [ + 'adjacentConstruction', + 'chemicalErosion', + 'repairAndReinforce', + 'historicalCalamity', + 'functionalChange', + ].includes(key) + ) { + this.checkedNone(houseInfo[key], key) + } + } + + checkedNone(value, key) { + const form = this.form.current + if (first(value) == 0 && value.length > 1) { + // 在'无'之后选中其他值 + value.shift() + form.setFieldsValue({ + houseInfo: { [key]: value }, + }) + } else if (last(value) == 0 && value.length > 1) { + // 在其他值之后选中'无' + value = ['0'] + form.setFieldsValue({ + houseInfo: { [key]: value }, + }) + } + return value + } + //#endregion + + render() { + const { loading, codes } = this.state + + return ( + }> +
+ this.onValuesChange(changedValues, allValues) + } + > + + + {codes.houseHouseSite.map(item => ( + + {item.value} + + ))} + + + + + {codes.houseAdjacentConstruction.map(item => ( + + {item.value} + + ))} + + + + + {codes.houseChemicalErosion.map(item => ( + + {item.value} + + ))} + + + + + {codes.houseStructuralDismantling.map(item => ( + + {item.value} + + ))} + + + + + {codes.houseAddingLayer.map(item => ( + + {item.value} + + ))} + + + + + {codes.houseRepairAndReinforce.map(item => ( + + {item.value} + + ))} + + + + + {codes.houseHistoricalCalamity.map(item => ( + + {item.value} + + ))} + + + + + {codes.houseFunctionalChange.map(item => ( + + {item.value} + + ))} + + + + + +
+
+ ) + } +} diff --git a/web-react/src/pages/business/house/info/form/base/ownership.jsx b/web-react/src/pages/business/house/info/form/base/ownership.jsx new file mode 100644 index 0000000..36ee7bf --- /dev/null +++ b/web-react/src/pages/business/house/info/form/base/ownership.jsx @@ -0,0 +1,253 @@ +import React, { Component } from 'react' +import { Col, Form, Input, InputNumber, Radio, Row, Spin } from 'antd' +import { AntIcon } from 'components' +import { cloneDeep, isEqual } from 'lodash' +import getDictData from 'util/dic' + +const initialValues = {} + +const layout = { + labelCol: { flex: '150px' }, + wrapperCol: { flex: '1' }, +} + +export default class ownership extends Component { + state = { + loading: true, + codes: { + housePropertyRights: [], + }, + options: {}, + } + + // 表单实例 + form = React.createRef() + + // 初始化数据 + record = {} + + /** + * 阻止外部组件引发的渲染,提升性能 + * 可自行添加渲染条件 + * [必要] + * @param {*} props + * @param {*} state + * @returns + */ + shouldComponentUpdate(props, state) { + return !isEqual(this.state, state) + } + + /** + * DOM加载完成钩子,绑定数据 + */ + componentDidMount() { + this.fillData({ + record: this.props.record, + }) + } + + /** + * 加载完成,通知父级组件并传递自身 + */ + call() { + const { onRef } = this.props + if (onRef) onRef(this) + } + + /** + * 填充数据 + * 可以在设置this.record之后对其作出数据结构调整 + * [异步,必要] + * @param {*} params + */ + async fillData(params) { + this.record = cloneDeep(params.record) + //#region 从后端转换成前段所需格式 + const codes = await getDictData('house_property_rights') + this.setState({ codes }) + //#endregion + this.form.current && this.form.current.setFieldsValue(this.record) + + this.setState({ loading: false }) + this.call() + } + + /** + * 获取数据 + * 可以对postData进行数据结构调整 + * [异步,必要] + * @returns + */ + async getData() { + const form = this.form.current + + const valid = await form.validateFields() + if (valid) { + const postData = form.getFieldsValue() + //#region 从前段转换后端所需格式 + //#endregion + return postData + } + } + + //#region 自定义方法 + /** + * 表单change事件处理,包括了所有字段的change + * [异步,非必要] + * @param {*} changedValues + * @param {*} allValues + */ + async onValuesChange(changedValues, allValues) {} + //#endregion + + render() { + const { loading, codes } = this.state + + return ( + }> +
+ this.onValuesChange(changedValues, allValues) + } + > + + + {codes.housePropertyRights.map(item => ( + + {item.value} + + ))} + + + + + + + + + +
套;
+
+ + + + + + +
套;
+
+ + + + + + +
套;
+
+ +
+ + + + + + +
套;
+
+ + + + + + +
套;
+
+ + + + + + +
套;
+
+ +
+ + + + + + +
套;
+
+ + + + + + +
+
+ +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ ) + } +} diff --git a/web-react/src/pages/business/house/info/form/base/unit.jsx b/web-react/src/pages/business/house/info/form/base/unit.jsx new file mode 100644 index 0000000..59d5bb3 --- /dev/null +++ b/web-react/src/pages/business/house/info/form/base/unit.jsx @@ -0,0 +1,154 @@ +import React, { Component } from 'react' +import { Col, Form, Input, Row, Spin } from 'antd' +import { AntIcon } from 'components' +import { cloneDeep, isEqual } from 'lodash' + +const initialValues = {} + +const layout = { + labelCol: { flex: '150px' }, + wrapperCol: { flex: '1' }, +} + +export default class unit extends Component { + state = { + loading: true, + codes: {}, + options: {}, + } + + // 表单实例 + form = React.createRef() + + // 初始化数据 + record = {} + + /** + * 阻止外部组件引发的渲染,提升性能 + * 可自行添加渲染条件 + * [必要] + * @param {*} props + * @param {*} state + * @returns + */ + shouldComponentUpdate(props, state) { + return !isEqual(this.state, state) + } + + /** + * DOM加载完成钩子,绑定数据 + */ + componentDidMount() { + this.fillData({ + record: this.props.record, + }) + } + + /** + * 加载完成,通知父级组件并传递自身 + */ + call() { + const { onRef } = this.props + if (onRef) onRef(this) + } + + /** + * 填充数据 + * 可以在设置this.record之后对其作出数据结构调整 + * [异步,必要] + * @param {*} params + */ + async fillData(params) { + this.record = cloneDeep(params.record) + //#region 从后端转换成前段所需格式 + //#endregion + this.form.current && this.form.current.setFieldsValue(this.record) + + this.setState({ loading: false }) + this.call() + } + + /** + * 获取数据 + * 可以对postData进行数据结构调整 + * [异步,必要] + * @returns + */ + async getData() { + const form = this.form.current + + const valid = await form.validateFields() + if (valid) { + const postData = form.getFieldsValue() + //#region 从前段转换后端所需格式 + //#endregion + return postData + } + } + + //#region 自定义方法 + /** + * 表单change事件处理,包括了所有字段的change + * [异步,非必要] + * @param {*} changedValues + * @param {*} allValues + */ + async onValuesChange(changedValues, allValues) {} + //#endregion + + render() { + const { loading } = this.state + + return ( + }> +
+ this.onValuesChange(changedValues, allValues) + } + > + + + + + + + + + + + + + + + + + + + + + + +
+
+ ) + } +} diff --git a/web-react/src/pages/business/house/info/form/index.jsx b/web-react/src/pages/business/house/info/form/index.jsx index 9c4eeba..4c3a474 100644 --- a/web-react/src/pages/business/house/info/form/index.jsx +++ b/web-react/src/pages/business/house/info/form/index.jsx @@ -1,7 +1,8 @@ import React, { Component } from 'react' -import { Tabs, Button, message } from 'antd' -import { defaultsDeep } from 'lodash' -import { ComponentDynamic, Container } from 'components' +import { Button, Descriptions, message as Message, Spin, Tabs } from 'antd' +import { merge, isEqual } from 'lodash' +import { AntIcon, ComponentDynamic, Container } from 'components' +import { api } from 'common/api' const tabs = [ { @@ -38,107 +39,178 @@ const tabs = [ // active: false, // show: false, // }, - // { - // title: '巡查登记', - // name: 'patrol', - // path: 'patrol', - // active: false, - // show: true, - // }, + { + title: '巡查登记', + component: () => import('./patrol'), + active: false, + show: true, + }, ] export default class index extends Component { - state = { - actived: '0' - } - forms = [] + actived: '0', - onSubmit = async () => { - let formData = {}, - flag = true - for (let i = 0; i < this.forms.length; i++) { - const form = this.forms[i] + loading: true, + record: null, + + saveDisabled: true, + saving: false, + } + + children = [] + + formData = {} + + shouldComponentUpdate(props, state) { + return !isEqual(this.state, state) + } + + componentDidMount() { + // 获取详细数据 + const { taskId } = this.props.param + if (taskId) { + api.houseInfoGetByTaskId({ taskId }).then(({ data }) => { + this.setState({ + record: data, + loading: false, + }) + }) + } + } + + call(child, index) { + this.children[index] = child + if (this.children.filter(p => p).length === tabs.filter(p => p.show).length) { + this.setState({ saveDisabled: false }) + } + } + + async onSubmit() { + for (const child of this.children) { try { - const data = await form.onGetData() - formData = defaultsDeep(formData, data) - } catch (err) { - if (err) { - err.errorFields.forEach(p => { - message.error(p.errors[0]) - }) - } - flag = false + const data = await child.getData() + merge(this.formData, data) + } catch (e) { + return e } } - if (!flag) { - return - } + //#region 提交数据 + console.log(this.formData) + this.setState({ saving: true }) - console.log(formData) - - message.success('提交成功') + setTimeout(() => { + Message.success('提交成功') + this.setState({ saving: false }) + }, 3000) + //#endregion } render() { + const { loading, record, saveDisabled, saving } = this.state + return (
{/* 底部工具栏(需放在前面) */}
- +
+ - {/* 可以在工具栏中增加其他控件(只能在一行内) */} - - - - + +
-
+
+ + }> + + + {record && record.houseCode.areaName} + + + {record && record.houseCode.roadName} + + + {record && record.houseCode.commName} + + + {record && record.houseCode.zoneName} + + + {record && + `${record.houseCode.areaName}-${ + record.houseCode.roadName + }-${record.houseCode.commName}-${ + record.houseCode.projectFullName + }-${record.houseCode.no.toString().padStart(3, '0')}`} + + + {record && record.houseCode.houseCode} + + + + +
{ this.setState({ actived: activeKey }) }} + onChange={activeKey => { + this.setState({ actived: activeKey }) + }} > - { - tabs.map((tab, i) => { - if (tab.show) { - return ( - - ) - } - return <> - }) - } + {tabs.map((tab, i) => { + if (tab.show) { + return ( + + ) + } + return <> + })}
- { - tabs.map((tab, i) => { - if (tab.show) { - return ( -
- this.forms.push(c)} /> -
- ) - } - return <> - }) - } + {tabs.map((tab, i) => { + if (tab.show) { + return ( +
+ this.call(child, i)} + /> +
+ ) + } + return <> + })}
diff --git a/web-react/src/pages/business/house/info/form/patrol/base.jsx b/web-react/src/pages/business/house/info/form/patrol/base.jsx new file mode 100644 index 0000000..e231a6d --- /dev/null +++ b/web-react/src/pages/business/house/info/form/patrol/base.jsx @@ -0,0 +1,112 @@ +import React, { Component } from 'react' +import { Row, Col, Form, Input, DatePicker, Spin } from 'antd' +import { cloneDeep, isEqual } from 'lodash' +import { AntIcon } from 'components' +import moment from 'moment' +import { CITY } from 'util/global' + +const layout = { + labelCol: { flex: '150px' }, + wrapperCol: { flex: '1' }, +} + +export default class base extends Component { + state = { + loading: true, + } + + form = React.createRef() + + shouldComponentUpdate(props, state) { + return !isEqual(this.state, state) + } + + componentDidMount() { + this.fillData({ + record: this.props.record, + }) + } + + call() { + if (this.props.onRef) { + this.props.onRef(this) + } + } + + /** + * 填充数据 + * 可以在设置this.record之后对其作出数据结构调整 + * [异步,必要] + * @param {*} params + */ + async fillData(params) { + this.record = cloneDeep(params.record) + //#region 从后端转换成前段所需格式 + if (this.record) { + const { patrolDate } = this.record.patrolInfo + this.record.patrolInfo.patrolDate = patrolDate ? moment(patrolDate) : patrolDate + } + //#endregion + this.form.current && this.form.current.setFieldsValue(this.record) + + this.setState({ loading: false }) + this.call() + } + + /** + * 获取数据 + * 可以对postData进行数据结构调整 + * [异步,必要] + * @returns + */ + async getData() { + const form = this.form.current + + const valid = await form.validateFields() + if (valid) { + const postData = form.getFieldsValue() + //#region 从前段转换后端所需格式 + if (postData.patrolInfo.patrolDate) { + postData.patrolInfo.patrolDate = postData.patrolInfo.patrolDate.format('YYYY-MM-DD') + } + //#endregion + return postData + } + } + + render() { + const { loading } = this.state + return ( + }> +
+ + + + { + /*$root.transfer.completedYear = date.format('YYYY') */ + }} + className="w-100-p" + placeholder="请选择巡查日期" + /> + + + + + + + + +
+
+ ) + } +} diff --git a/web-react/src/pages/business/house/info/form/patrol/grade.jsx b/web-react/src/pages/business/house/info/form/patrol/grade.jsx new file mode 100644 index 0000000..6e23fc6 --- /dev/null +++ b/web-react/src/pages/business/house/info/form/patrol/grade.jsx @@ -0,0 +1,183 @@ +import React, { Component } from 'react' +import { Form, Tooltip, Radio, Spin } from 'antd' +import { AntIcon } from 'components' +import { cloneDeep, first, isEqual, last, sortBy } from 'lodash' +import getDictData from 'util/dic' +import store from 'store' + +const { getState, subscribe } = store + +const layout = { + labelCol: { flex: '150px' }, + wrapperCol: { flex: '1' }, +} + +export default class handling extends Component { + state = { + loading: true, + codes: { + housePatrolInitGrade: [], + housePatrolDamageGrade: [], + houseGrade: [], + }, + } + form = React.createRef() + + constructor(props) { + super(props) + + this.unsubscribe = subscribe('business', business => { + const initGrade = this.getInitGrade(business.completedDate) + this.form.current.setFieldsValue({ + patrolInfo: { + initGrade, + }, + }) + }) + } + + shouldComponentUpdate(props, state) { + return !isEqual(this.state, state) + } + + componentDidMount() { + this.fillData({ + record: this.props.record, + }) + } + + componentWillUnmount() { + this.unsubscribe() + } + + call() { + if (this.props.onRef) { + this.props.onRef(this) + } + } + + /** + * 填充数据 + * 可以在设置this.record之后对其作出数据结构调整 + * [异步,必要] + * @param {*} params + */ + async fillData(params) { + this.record = cloneDeep(params.record) + + const _state = { loading: false } + //#region 从后端转换成前段所需格式 + if (this.record) { + const { patrolInfo } = this.record + patrolInfo.initGrade = this.getInitGrade(getState('business').completedDate) + } + _state.codes = await getDictData( + 'house_patrol_init_grade', + 'house_patrol_damage_grade', + 'house_grade' + ) + //#endregion + this.form.current && this.form.current.setFieldsValue(this.record) + + this.setState(_state) + this.call() + } + + /** + * 获取数据 + * 可以对postData进行数据结构调整 + * [异步,必要] + * @returns + */ + async getData() { + const form = this.form.current + + const valid = await form.validateFields() + if (valid) { + const postData = form.getFieldsValue() + //#region 从前段转换后端所需格式 + + //#endregion + return postData + } + } + + getInitGrade(completedDate) { + const date = completedDate.find(p => p.id === this.props.id) + if (date) { + const { value: year } = date + if (year > 1999) { + return 1 + } + if (year > 1994 && year < 2000) { + return 2 + } + if (year > 1979 && year < 1995) { + return 3 + } + if (year < 1980) { + return 4 + } + } + } + + render() { + const { loading, codes, initGradeValue } = this.state + console.log(initGradeValue) + return ( + }> +
+ + + + {codes.housePatrolInitGrade.map(item => { + return ( + + {item.value} + + ) + })} + + + + + + + {codes.housePatrolDamageGrade.map(item => { + return ( + + {item.value} + + ) + })} + + + + + + {codes.houseGrade.map(item => { + return ( + + {item.value} + + ) + })} + + +
+
+ ) + } +} diff --git a/web-react/src/pages/business/house/info/form/patrol/handling.jsx b/web-react/src/pages/business/house/info/form/patrol/handling.jsx new file mode 100644 index 0000000..0bce3c6 --- /dev/null +++ b/web-react/src/pages/business/house/info/form/patrol/handling.jsx @@ -0,0 +1,131 @@ +import React, { Component } from 'react' +import { Form, Input, Radio, Spin } from 'antd' +import { AntIcon } from 'components' +import { cloneDeep, first, isEqual, last, sortBy } from 'lodash' +import getDictData from 'util/dic' + +const layout = { + labelCol: { flex: '150px' }, + wrapperCol: { flex: '1' }, +} + +export default class handling extends Component { + state = { + loading: true, + codes: { + housePatrolHandlingOpinion: [], + housePatrolRectifyReform: [], + }, + } + form = React.createRef() + + shouldComponentUpdate(props, state) { + return !isEqual(this.state, state) + } + + componentDidMount() { + this.fillData({ + record: this.props.record, + }) + } + + call() { + if (this.props.onRef) { + this.props.onRef(this) + } + } + + /** + * 填充数据 + * 可以在设置this.record之后对其作出数据结构调整 + * [异步,必要] + * @param {*} params + */ + async fillData(params) { + this.record = cloneDeep(params.record) + const _state = { loading: false } + //#region 从后端转换成前段所需格式 + _state.codes = await getDictData( + 'house_patrol_handling_opinion', + 'house_patrol_rectify_Reform' + ) + //#endregion + this.form.current && this.form.current.setFieldsValue(this.record) + + this.setState(_state) + this.call() + } + + /** + * 获取数据 + * 可以对postData进行数据结构调整 + * [异步,必要] + * @returns + */ + async getData() { + const form = this.form.current + + const valid = await form.validateFields() + if (valid) { + const postData = form.getFieldsValue() + //#region 从前段转换后端所需格式 + + //#endregion + return postData + } + } + render() { + const { loading, codes } = this.state + return ( + }> +
+ + + {codes.housePatrolHandlingOpinion.map(item => { + return ( + + {item.value} + + ) + })} + + + + + + + + + + {codes.housePatrolRectifyReform.map(item => { + return ( + + {item.value} + + ) + })} + + + + + + +
+
+ ) + } +} diff --git a/web-react/src/pages/business/house/info/form/patrol/index.jsx b/web-react/src/pages/business/house/info/form/patrol/index.jsx new file mode 100644 index 0000000..79f7503 --- /dev/null +++ b/web-react/src/pages/business/house/info/form/patrol/index.jsx @@ -0,0 +1,146 @@ +import React, { Component } from 'react' +import ReactDOM from 'react-dom' +import { Anchor, Card, Col, Divider, Row, Spin } from 'antd' +import { AntIcon, ComponentDynamic, Container } from 'components' +import { isEqual, merge } from 'lodash' + +const parts = [ + { + title: '巡查基本情况', + component: () => import('./base'), + }, + { + title: '房屋检查', + component: () => import('./inspection'), + }, + { + title: '等级划分', + component: () => import('./grade'), + }, + { + title: '处理情况', + component: () => import('./handling'), + }, + { + title: '本期巡查结果', + component: () => import('./result'), + }, +] + +export default class index extends Component { + // 子表单实例集合 + children = [] + + // 整合提交数据 + formData = {} + + // 锚点挂载DOM + container = window + + /** + * 阻止外部组件引发的渲染,提升性能 + * 可自行添加渲染条件 + * [必要] + * @param {*} props + * @param {*} state + * @returns + */ + shouldComponentUpdate(props, state) { + return !isEqual(this.state, state) || this.props.loading !== props.loading + } + + /** + * 加载完成,通知父级组件并传递自身 + */ + call(child, index) { + this.children[index] = child + if (this.children.filter(p => p).length === parts.length) { + const { onRef } = this.props + if (onRef) onRef(this) + } + } + + /** + * 从下级组件获取表单数据,并传递给更上级组件 + * [异步,必要] + * @returns + */ + async getData() { + for (const child of this.children) { + const data = await child.getData() + merge(this.formData, data) + } + return this.formData + } + + /** + * 设置锚点容器 + * [非必要] + * @param {*} container + */ + setContainer = container => { + this.container = (ReactDOM.findDOMNode(container) || {}).parentNode + } + + /** + * 渲染 + * 当前渲染结构已完善,非必要可以不用修改 + * [必要] + * @returns + */ + render() { + const { id, loading } = this.props + + return ( + + + +
+
+ + {parts.map((item, i) => ( + +
+ {item.title &&
{item.title}
} + } + wrapperClassName={loading && 'h-400-min'} + > + {!loading && ( + this.call(child, i)} + /> + )} + +
+ {i < parts.length - 1 && } +
+ ))} +
+ + {/* 锚点,如果不需要可以删除以下节点 */} + + this.container} + offsetTop={24} + targetOffset={100} + wrapperStyle={{ backgroundColor: 'transparent' }} + onClick={e => e.preventDefault()} + > + {parts.map((part, i) => ( + + ))} + + + + + ) + } +} diff --git a/web-react/src/pages/business/house/info/form/patrol/inspection.jsx b/web-react/src/pages/business/house/info/form/patrol/inspection.jsx new file mode 100644 index 0000000..088f61b --- /dev/null +++ b/web-react/src/pages/business/house/info/form/patrol/inspection.jsx @@ -0,0 +1,291 @@ +import React, { Component } from 'react' +import { Col, Form, Input, Row, Spin, Upload } from 'antd' +import { AntIcon, PhotoPreview } from 'components' +import { cloneDeep, isEqual } from 'lodash' +import { BlobToBase64, GetFileName, PreviewFile } from 'util/file' +import { api } from 'common/api' + +const initialValues = {} + +const layout = { + labelCol: { flex: '150px' }, + wrapperCol: { flex: '1' }, +} + +const imageUploads = [{ key: 'settlementTiltFiles' }, { key: 'otherInfoFiles' }] + +export default class inspection extends Component { + state = { + loading: true, + codes: {}, + options: {}, + } + + // 表单实例 + form = React.createRef() + + photoPreview = React.createRef() + + // 初始化数据 + record = {} + + /** + * 阻止外部组件引发的渲染,提升性能 + * 可自行添加渲染条件 + * [必要] + * @param {*} props + * @param {*} state + * @returns + */ + shouldComponentUpdate(props, state) { + return !isEqual(this.state, state) + } + + /** + * DOM加载完成钩子,绑定数据 + */ + componentDidMount() { + this.fillData({ + record: this.props.record, + }) + } + + /** + * 加载完成,通知父级组件并传递自身 + */ + call() { + const { onRef } = this.props + if (onRef) onRef(this) + } + + /** + * 填充数据 + * 可以在设置this.record之后对其作出数据结构调整 + * [异步,必要] + * @param {*} params + */ + async fillData(params) { + this.record = cloneDeep(params.record) + //#region 从后端转换成前段所需格式 + if (this.record) { + const { patrolInfo } = this.record + const keys = imageUploads.map(p => p.key) + for (const key of keys) { + const fileValue = [] + const fileList = + !patrolInfo[key] || !patrolInfo[key].length ? [] : patrolInfo[key].split(',') + for (const fileId of fileList) { + try { + const file = await PreviewFile(fileId) + const base64 = await BlobToBase64(file) + fileValue.push({ + uid: fileId, + response: fileId, + name: file.name, + url: base64, + status: 'done', + }) + } catch { + const { data: file } = await api.sysFileInfoDetail({ id: fileId }) + fileValue.push({ + uid: fileId, + response: '文件已丢失', + name: file.fileOriginName, + status: 'error', + }) + } + } + + patrolInfo[key] = fileValue + } + } + //#endregion + this.form.current && this.form.current.setFieldsValue(this.record) + + this.setState({ loading: false }) + this.call() + } + + /** + * 获取数据 + * 可以对postData进行数据结构调整 + * [异步,必要] + * @returns + */ + async getData() { + const form = this.form.current + + const valid = await form.validateFields() + if (valid) { + const postData = form.getFieldsValue() + //#region 从前段转换后端所需格式 + const { patrolInfo } = postData + const keys = imageUploads.map(p => p.key) + for (const key of keys) { + patrolInfo[key] = patrolInfo[key] + .map(item => (item.uid.startsWith('rc-upload') ? item.response : item.uid)) + .join(',') + } + //#endregion + return postData + } + } + + //#region 自定义方法 + /** + * 表单change事件处理,包括了所有字段的change + * [异步,非必要] + * @param {*} changedValues + * @param {*} allValues + */ + async onValuesChange(changedValues, allValues) {} + + async onFileUpload({ file, onProgress, onSuccess, onError }) { + onProgress({ + percent: 0, + }) + const fd = new FormData() + fd.append('file', file) + try { + const { data: fileId } = await api.sysFileInfoUpload(fd) + onSuccess(fileId) + } catch { + onError() + } + } + + async onFilePreview(file, key) { + const fileList = this.form.current + .getFieldValue(['patrolInfo', key]) + .filter(p => p.status === 'done') + const items = [] + for (const _file of fileList) { + const img = new Image() + const src = _file.url || _file.thumbUrl + img.src = src + items.push({ + src, + w: img.naturalWidth, + h: img.naturalHeight, + }) + } + this.photoPreview.current.initPhotoSwipe(items, { + index: fileList.indexOf(file), + }) + } + + async onFileDownload(file) { + const { data, headers } = await api.sysFileInfoDownload({ id: file.response }) + const url = window.URL.createObjectURL(data) + const fileName = GetFileName(headers['content-disposition']) + const a = document.createElement('a') + a.href = url + a.download = fileName + a.click() + window.URL.revokeObjectURL(url) + a.remove() + } + //#endregion + + render() { + const { loading } = this.state + + return ( + }> +
+ this.onValuesChange(changedValues, allValues) + } + > + + + + + + + + { + if (Array.isArray(e)) { + return e + } + return e && e.fileList + }} + > + this.onFileUpload(e)} + showUploadList={{ + showRemoveIcon: true, + showDownloadIcon: true, + }} + onPreview={file => + this.onFilePreview(file, 'settlementTiltFiles') + } + onDownload={file => this.onFileDownload(file)} + > +
+ +
沉降倾斜照片
+
+
+
+ + + + + + + + { + if (Array.isArray(e)) { + return e + } + return e && e.fileList + }} + > + this.onFileUpload(e)} + showUploadList={{ + showRemoveIcon: true, + showDownloadIcon: true, + }} + onPreview={file => this.onFilePreview(file, 'otherInfoFiles')} + onDownload={file => this.onFileDownload(file)} + > +
+ +
其他情况照片
+
+
+
+ +
+ + + +
+ + +
+ ) + } +} diff --git a/web-react/src/pages/business/house/info/form/patrol/result.jsx b/web-react/src/pages/business/house/info/form/patrol/result.jsx new file mode 100644 index 0000000..6fbd040 --- /dev/null +++ b/web-react/src/pages/business/house/info/form/patrol/result.jsx @@ -0,0 +1,102 @@ +import React, { Component } from 'react' +import { Form, Input, Radio, Spin } from 'antd' +import { AntIcon } from 'components' +import { cloneDeep, first, isEqual, last, sortBy } from 'lodash' + +const layout = { + labelCol: { flex: '150px' }, + wrapperCol: { flex: '1' }, +} + +export default class result extends Component { + state = { + loading: true, + codes: { + patrolResult: [ + { code: '0', value: '正常' }, + { code: '-1', value: '异常' }, + ], + }, + } + form = React.createRef() + + shouldComponentUpdate(props, state) { + return !isEqual(this.state, state) + } + + componentDidMount() { + this.fillData({ + record: this.props.record, + }) + } + + call() { + if (this.props.onRef) { + this.props.onRef(this) + } + } + + /** + * 填充数据 + * 可以在设置this.record之后对其作出数据结构调整 + * [异步,必要] + * @param {*} params + */ + async fillData(params) { + this.record = cloneDeep(params.record) + //#region 从后端转换成前段所需格式 + + //#endregion + this.form.current && this.form.current.setFieldsValue(this.record) + + this.setState({ loading: false }) + this.call() + } + + /** + * 获取数据 + * 可以对postData进行数据结构调整 + * [异步,必要] + * @returns + */ + async getData() { + const form = this.form.current + + const valid = await form.validateFields() + if (valid) { + const postData = form.getFieldsValue() + //#region 从前段转换后端所需格式 + + //#endregion + return postData + } + } + render() { + const { loading, codes } = this.state + + return ( + }> +
+ + + {codes.patrolResult.map(item => { + return ( + + {item.value} + + ) + })} + + + + + +
+
+ ) + } +} diff --git a/web-react/src/pages/business/house/member/selector/index.jsx b/web-react/src/pages/business/house/member/selector/index.jsx index de667fc..a9c08ed 100644 --- a/web-react/src/pages/business/house/member/selector/index.jsx +++ b/web-react/src/pages/business/house/member/selector/index.jsx @@ -45,7 +45,7 @@ export default class index extends Component { const { userId } = this.state return ( - + this.bindCodeValue(text, 'dic_house_type'), + render: text => this.bindCodeValue(text, 'house_type'), }, { title: '地址', @@ -91,7 +91,7 @@ export default class index extends Component { componentDidMount() { const { onLoading, onLoadData } = this.table.current onLoading() - getDictData('dic_house_type', 'dic_house_industry').then(codes => { + getDictData('house_type', 'house_industry').then(codes => { this.setState({ codes }, () => { onLoadData() }) @@ -218,7 +218,7 @@ export default class index extends Component { 全部 - {codes.dicHouseType.map(item => ( + {codes.houseType.map(item => ( {item.value} @@ -228,7 +228,7 @@ export default class index extends Component { {type == 2 && ( - {codes.dicHouseIndustry.map(item => ( + {codes.houseIndustry.map(item => ( {item.value} diff --git a/web-react/src/pages/business/house/project/form.jsx b/web-react/src/pages/business/house/project/form.jsx index 25446df..750743b 100644 --- a/web-react/src/pages/business/house/project/form.jsx +++ b/web-react/src/pages/business/house/project/form.jsx @@ -3,11 +3,11 @@ import { Cascader, Form, Input, InputNumber, Radio, Spin, TreeSelect } from 'ant import { AntIcon } from 'components' import { cloneDeep } from 'lodash' import { api } from 'common/api' -import { numberToChinese } from 'util/format'; +import { numberToChinese } from 'util/format' const initialValues = { sort: 100, - type: 1 + type: 1, } export default class form extends Component { state = { @@ -16,8 +16,8 @@ export default class form extends Component { exist: false, options: { - areaData: [] - } + areaData: [], + }, } areaCode = '' houseType = 1 @@ -35,14 +35,18 @@ export default class form extends Component { } /** - * 填充数据 - * 可以在设置this.record之后对其作出数据结构调整 - * [异步,必要] - * @param {*} params - */ + * 填充数据 + * 可以在设置this.record之后对其作出数据结构调整 + * [异步,必要] + * @param {*} params + */ async fillData(params) { - const areaCodeDefault = params.record ? params.record.areaCode : params.pid ? params.pid : ''; - this.houseType = params.record ? params.record.type : 1; + const areaCodeDefault = params.record + ? params.record.areaCode + : params.pid + ? params.pid + : '' + this.houseType = params.record ? params.record.type : 1 this.record = cloneDeep(params.record) this.initRecord = cloneDeep(params.record) //#region 从后端转换成前段所需格式 @@ -50,54 +54,54 @@ export default class form extends Component { this.setState({ exist: !!params.record, - options: { areaData } + options: { areaData }, }) - const areaCode = []; + const areaCode = [] const findCode = (data, level) => { - level = level || 0; + level = level || 0 for (let i = 0; i < data.length; i++) { - const item = data[i]; - areaCode[level] = item.code; + const item = data[i] + areaCode[level] = item.code if (item.code === areaCodeDefault) { - areaCode.length = level + 1; - return true; + areaCode.length = level + 1 + return true } if (item.children && item.children.length) { - const found = findCode(item.children, level + 1); + const found = findCode(item.children, level + 1) if (found) { - return true; + return true } } } - }; + } if (areaCodeDefault) { - findCode(areaData); + findCode(areaData) this.areaCode = areaCodeDefault - this.nextSort(this.areaCode, this.houseType); + this.nextSort(this.areaCode, this.houseType) } this.record = { pid: params.pid, ...this.record, - areaCode: areaCode.length == 4 ? areaCode : [] + areaCode: areaCode.length == 4 ? areaCode : [], } //#endregion this.form.current.setFieldsValue(this.record) this.setState({ - loading: false + loading: false, }) } /** * 获取数据 * 可以对postData进行数据结构调整 * [异步,必要] - * @returns + * @returns */ async getData() { const form = this.form.current @@ -117,78 +121,84 @@ export default class form extends Component { async loadAreaData() { const { data } = await api.getAreaTree() - const clearChiildren = (data) => { - data.forEach((item) => { + console.log(data) + const clearChiildren = data => { + data.forEach(item => { if (item.children && item.children.length) { - clearChiildren(item.children); + clearChiildren(item.children) } else { - delete item.children; + delete item.children } - }); - }; - clearChiildren(data); + }) + } + clearChiildren(data) return data } async nextSort(areaCode, houseType) { - this.loading = true; - if (!!this.initRecord && this.initRecord.areaCode == areaCode && this.initRecord.type == houseType) { + this.loading = true + if ( + !!this.initRecord && + this.initRecord.areaCode == areaCode && + this.initRecord.type == houseType + ) { this.form.current.setFieldsValue({ name: this.initRecord.name, - sort: this.initRecord.sort + sort: this.initRecord.sort, }) } else if (areaCode.length < 12) { this.form.current.setFieldsValue({ name: '', sort: 0, - areaCode: [] + areaCode: [], }) } else { - await api.houseProjectNextSort({ areaCode, type: houseType }) + await api + .houseProjectNextSort({ areaCode, type: houseType }) .then(({ data }) => { this.form.current.setFieldsValue({ name: `项目${numberToChinese(data)}`, - sort: data + sort: data, }) }) .catch(() => { this.form.current.setFieldsValue({ name: '', sort: 0, - areaCode: [] + areaCode: [], }) }) .finally(() => { - this.loading = false; - }); + this.loading = false + }) } } onHouseTypeChange(e) { - this.houseType = e.target.value; + this.houseType = e.target.value if (this.areaCode != '') { - this.nextSort(this.areaCode, this.houseType); + this.nextSort(this.areaCode, this.houseType) } } onAreaCodeChange(value) { this.areaCode = value[value.length - 1] if (this.houseType > 0) { - this.nextSort(this.areaCode, this.houseType); + this.nextSort(this.areaCode, this.houseType) } } render() { return ( -
+ }>
- - this.onHouseTypeChange(e)}> + + this.onHouseTypeChange(e)} + > 住宅 @@ -197,13 +207,17 @@ export default class form extends Component { - + this.onAreaCodeChange(val)} /> - - + + + placeholder="填写房屋所属单位的名称、道路的名称或大厦的名称,比如XX中学、XX大厦、XX小区等。登记项目时,应在项目备注中明确项目所指对象。" + />
) } -} \ No newline at end of file +} diff --git a/web-react/src/pages/business/house/project/index.jsx b/web-react/src/pages/business/house/project/index.jsx index 3a56823..7ecec2f 100644 --- a/web-react/src/pages/business/house/project/index.jsx +++ b/web-react/src/pages/business/house/project/index.jsx @@ -1,6 +1,14 @@ import React, { Component } from 'react' import { Button, Card, Form, Input, message as Message, Popconfirm } from 'antd' -import { AntIcon, Auth, Container, ModalForm, QueryTable, QueryTableActions, QueryTreeLayout } from 'components' +import { + AntIcon, + Auth, + Container, + ModalForm, + QueryTable, + QueryTableActions, + QueryTreeLayout, +} from 'components' import { api } from 'common/api' import auth from 'components/authorized/handler' import { toCamelCase } from 'util/format' @@ -13,17 +21,16 @@ const apiAction = { page: api.getHouseProjectPage, add: api.houseProejctAdd, edit: api.houseProejctEdit, - delete: api.houseProejctDelete + delete: api.houseProejctDelete, } const name = '项目' export default class index extends Component { - state = { codes: { - dicHouseType: [] - } + houseType: [], + }, } // 表格实例 @@ -37,57 +44,62 @@ export default class index extends Component { // 树选中节点 selectCode = undefined columns = [ - { title: '项目名称', dataIndex: 'name', + width: 150, sorter: true, }, { title: '社区', dataIndex: 'areaName', + width: 100, sorter: true, }, { title: '备注', dataIndex: 'note', + width: 150, sorter: true, }, { title: '类型', dataIndex: 'type', sorter: true, - render: text => (<>{this.bindCodeValue(text, 'dic_house_type')}) - } + width: 80, + render: text => <>{this.bindCodeValue(text, 'house_type')}, + }, ] /** * 构造函数,在渲染前动态添加操作字段等 - * @param {*} props + * @param {*} props */ constructor(props) { super(props) - const flag = auth({ sysArea: [['edit'], ['delete']] }) + const flag = auth({ houseProjectInfo: [['edit'], ['delete']] }) if (flag) { this.columns.push({ title: '操作', width: 150, dataIndex: 'actions', - render: (text, record) => ( - - this.onOpen(this.editForm, record)}>编辑 - - - this.onDelete(record)} - > - 删除 - - - ) + render: (text, record) => ( + + + this.onOpen(this.editForm, record)}>编辑 + + + this.onDelete(record)} + > + 删除 + + + + ), }) } } @@ -95,9 +107,9 @@ export default class index extends Component { * 阻止外部组件引发的渲染,提升性能 * 可自行添加渲染条件 * [必要] - * @param {*} props - * @param {*} state - * @returns + * @param {*} props + * @param {*} state + * @returns */ shouldComponentUpdate(props, state) { return !isEqual(this.state, state) @@ -109,26 +121,28 @@ export default class index extends Component { */ componentDidMount() { this.table.current.onLoading() - getDictData('dic_house_type').then(res => { - this.setState({ - codes: res - }, () => { - this.table.current.onLoadData() - }) + getDictData('house_type').then(res => { + this.setState( + { + codes: res, + }, + () => { + this.table.current.onLoadData() + } + ) }) } /** - * 调用加载数据接口,可在调用前对query进行处理 - * [异步,必要] - * @param {*} params - * @param {*} query - * @returns - */ + * 调用加载数据接口,可在调用前对query进行处理 + * [异步,必要] + * @param {*} params + * @param {*} query + * @returns + */ loadData = async (params, query) => { - query = { ...query, - pid: this.selectCode + pid: this.selectCode, } //首次加载根据code列升序排序 // if (!params.sortField) { @@ -155,7 +169,7 @@ export default class index extends Component { /** * 树节点选中事件 * [必要] - * @param {*} id + * @param {*} id */ onSelectTree(code) { this.selectCode = code @@ -164,15 +178,15 @@ export default class index extends Component { /** * 绑定字典数据 - * @param {*} code - * @param {*} name - * @returns + * @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) + const c = codes.find(p => +p.code === code) if (c) { return c.value } @@ -182,21 +196,21 @@ export default class index extends Component { /** * 打开新增/编辑弹窗 - * @param {*} modal - * @param {*} record + * @param {*} modal + * @param {*} record */ onOpen(modal, record) { modal.current.open({ pid: this.selectCode, - record + record, }) } /** * 对表格上的操作进行统一处理 * [异步] - * @param {*} action - * @param {*} successMessage + * @param {*} action + * @param {*} successMessage */ async onAction(action, successMessage) { this.table.current.onLoading() @@ -211,13 +225,10 @@ export default class index extends Component { /** * 删除 - * @param {*} record + * @param {*} record */ onDelete(record) { - this.onAction( - apiAction.delete(record), - '删除成功' - ) + this.onAction(apiAction.delete(record), '删除成功') } render() { @@ -225,7 +236,7 @@ export default class index extends Component { this.onSelectTree(key)} + onSelect={key => this.onSelectTree(key)} replaceFields={{ value: 'code', title: 'name', children: 'children' }} > @@ -250,12 +261,12 @@ export default class index extends Component { + > + 新增{name} + } - > - - + > ) } -} \ No newline at end of file +} diff --git a/web-react/src/pages/business/house/task/index.jsx b/web-react/src/pages/business/house/task/index.jsx new file mode 100644 index 0000000..a386e3c --- /dev/null +++ b/web-react/src/pages/business/house/task/index.jsx @@ -0,0 +1,265 @@ +import React, { Component } from 'react' +import { Button, Card, Form, Input, message as Message, Popconfirm, Radio, Select, Tag } from 'antd' +import { AntIcon, Auth, Container, ModalForm, QueryTable, QueryTableActions } 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 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({ houseInfo: 'getByTaskId' }) + + if (flag) { + this.columns.push({ + title: '操作', + width: 150, + dataIndex: 'actions', + render: (text, record) => ( + + + this.onOpen(record.id)}>登记 + + + ), + }) + } + } + + /** + * 阻止外部组件引发的渲染,提升性能 + * 可自行添加渲染条件 + * [必要] + * @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 {*} record + */ + onOpen(taskId) { + window.openContentWindow({ + title: '房屋登记', + path: 'business/house/info/form', + param: { + taskId, + }, + }) + } + + /** + * 对表格上的操作进行统一处理 + * [异步] + * @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() + } + } + + //#region 自定义方法 + //#endregion + + render() { + const { codes, type } = this.state + + return ( + +
+ + { + if (values.hasOwnProperty('type')) { + this.setState({ type: values.type }) + } + }} + query={ + + + + 全部 + {codes.houseType.map(item => ( + + {item.value} + + ))} + + + {type == 2 && ( + + + + )} + + + + + + + + } + /> + +
+ ) + } +} diff --git a/web-react/src/pages/business/house/zone/form.jsx b/web-react/src/pages/business/house/zone/form.jsx index bb68a43..c93e21c 100644 --- a/web-react/src/pages/business/house/zone/form.jsx +++ b/web-react/src/pages/business/house/zone/form.jsx @@ -1,24 +1,36 @@ import React, { Component } from 'react' -import { Cascader, Form, Input, InputNumber, Select, Spin, TreeSelect } from 'antd' +import { Form, Input, InputNumber, Spin, TreeSelect } from 'antd' import { AntIcon } from 'components' import { cloneDeep } from 'lodash' -import getDictData from 'util/dic' import { api } from 'common/api' -import { numberToChinese } from 'util/format'; +import { numberToChinese } from 'util/format' +import store from 'store' +const { getState, subscribe } = store +const storePath = 'user' const initialValues = { - sort: 100 + sort: 100, } export default class form extends Component { state = { // 加载状态 loading: true, - + exist: false, options: { - areaData: [] - } + orgData: [], + }, + user: getState(storePath), } + constructor(props) { + super(props) + + this.unsubscribe = subscribe(storePath, () => { + this.setState({ + user: getState(storePath), + }) + }) + } // 表单实例 form = React.createRef() @@ -31,67 +43,55 @@ export default class form extends Component { componentDidMount() { this.props.created && this.props.created(this) } - + componentWillUnmount() { + this.unsubscribe() + } /** - * 填充数据 - * 可以在设置this.record之后对其作出数据结构调整 - * [异步,必要] - * @param {*} params - */ + * 填充数据 + * 可以在设置this.record之后对其作出数据结构调整 + * [异步,必要] + * @param {*} params + */ async fillData(params) { - + const { user } = this.state this.record = cloneDeep(params.record) //#region 从后端转换成前段所需格式 - const areaData = await this.loadAreaData() + const orgData = await this.loadOrgData() this.setState({ - options: { areaData } + exist: !!params.record, + options: { orgData }, }) - const areaCode = []; - const findCode = (data, level) => { - level = level || 0; - for (let i = 0; i < data.length; i++) { - const item = data[i]; - areaCode[level] = item.code; - - if (item.code === params.record.areaCode) { - areaCode.length = level + 1; - return true; + //街道角色新增,不管左侧树选中与否,默认值均为本街道 + if (user.adminType === 2) { + user.roles.map(role => { + if (role.code == 'road_manager') { + params.orgId = user.loginEmpInfo.orgId } - - if (item.children && item.children.length) { - const found = findCode(item.children, level + 1); - if (found) { - return true; - } - } - } - }; - - if (params.record && params.record.areaCode) { - findCode(areaData); + }) } this.record = { pid: params.orgId, ...this.record, - areaCode } - this.record.areaCode = areaCode - //#endregion + //#endregion + if (!params.record && !!params.orgId) { + this.onOrgIdChanged(params.orgId) + } this.form.current.setFieldsValue(this.record) this.setState({ - loading: false + loading: false, }) } /** * 获取数据 * 可以对postData进行数据结构调整 * [异步,必要] - * @returns + * @returns */ async getData() { const form = this.form.current @@ -102,74 +102,64 @@ export default class form extends Component { if (this.record) { postData.id = this.record.id } - //#region 从前段转换后端所需格式 - postData.areaCode = postData.areaCode[postData.areaCode.length - 1] + //#region 从前段转换后端所需格 //#endregion return postData } } - async loadAreaData() { - const { data } = await api.getAreaTree({ level: 3 }) - const clearChiildren = (data) => { - data.forEach((item) => { - if (item.children && item.children.length) { - clearChiildren(item.children); - } else { - delete item.children; - } - }); - }; - clearChiildren(data); + async loadOrgData() { + const { data } = await api.getOrgTree({ type: 4 }) return data } - onAreaCodeChange(value) { - this.loading = true; - // const { data } = api.houseZoneAutoIncrement({ code: selectedOptions[selectedOptions.length - 1] }); + onOrgIdChanged(value) { + this.loading = true - api.houseZoneAutoIncrement({ code: value[value.length - 1] }) + api.houseZoneAutoIncrement({ roadId: value }) .then(({ data }) => { this.form.current.setFieldsValue({ - name: `片区${numberToChinese(data)}` + name: `片区${numberToChinese(data)}`, + sort: data, }) }) .catch(() => { this.form.current.setFieldsValue({ name: '', - areaCode: [] + sort: 0, }) }) .finally(() => { - this.loading = false; - }); + this.loading = false + }) } render() { return ( -
+ }>
- - this.onAreaCodeChange(val)} + + this.onOrgIdChanged(value)} + disabled={this.state.exist} /> - - + + + @@ -187,4 +178,4 @@ export default class form extends Component { ) } -} \ No newline at end of file +} diff --git a/web-react/src/pages/business/house/zone/index.jsx b/web-react/src/pages/business/house/zone/index.jsx index 74dcbc8..fb8857f 100644 --- a/web-react/src/pages/business/house/zone/index.jsx +++ b/web-react/src/pages/business/house/zone/index.jsx @@ -1,6 +1,14 @@ import React, { Component } from 'react' import { Button, Card, Form, Input, message as Message, Popconfirm } from 'antd' -import { AntIcon, Auth, Container, ModalForm, QueryTable, QueryTableActions, QueryTreeLayout } from 'components' +import { + AntIcon, + Auth, + Container, + ModalForm, + QueryTable, + QueryTableActions, + QueryTreeLayout, +} from 'components' import { api } from 'common/api' import auth from 'components/authorized/handler' import { toCamelCase } from 'util/format' @@ -12,14 +20,13 @@ const apiAction = { tree: api.getOrgTree, page: api.houseZonePage, add: api.houseZoneAdd, - edit: api.sysOrgEdit, - delete: api.sysOrgDelete + edit: api.houseZoneEdit, + delete: api.sysOrgDelete, } const name = '片区' export default class index extends Component { - // 树框架实例 treeLayout = React.createRef() @@ -58,32 +65,34 @@ export default class index extends Component { /** * 构造函数,在渲染前动态添加操作字段等 - * @param {*} props + * @param {*} props */ constructor(props) { super(props) - const flag = auth({ sysOrg: [['edit'], ['delete']] }) + const flag = auth({ houseZone: [['edit'], ['delete']] }) if (flag) { this.columns.push({ title: '操作', width: 150, dataIndex: 'actions', - render: (text, record) => ( - - this.onOpen(this.editForm, record)}>编辑 - - - this.onDelete(record)} - > - 删除 - - - ) + render: (text, record) => ( + + + this.onOpen(this.editForm, record)}>编辑 + + + this.onDelete(record)} + > + 删除 + + + + ), }) } } @@ -92,9 +101,9 @@ export default class index extends Component { * 阻止外部组件引发的渲染,提升性能 * 可自行添加渲染条件 * [必要] - * @param {*} props - * @param {*} state - * @returns + * @param {*} props + * @param {*} state + * @returns */ shouldComponentUpdate(props, state) { return !isEqual(this.state, state) @@ -111,14 +120,14 @@ export default class index extends Component { /** * 调用加载数据接口,可在调用前对query进行处理 * [异步,必要] - * @param {*} params - * @param {*} query - * @returns + * @param {*} params + * @param {*} query + * @returns */ loadData = async (params, query) => { query = { ...query, - pid: this.selectId + pid: this.selectId, } const { data } = await apiAction.page({ @@ -141,7 +150,7 @@ export default class index extends Component { /** * 树节点选中事件 * [必要] - * @param {*} id + * @param {*} id */ onSelectTree(id) { this.selectId = id @@ -150,15 +159,15 @@ export default class index extends Component { /** * 绑定字典数据 - * @param {*} code - * @param {*} name - * @returns + * @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) + const c = codes.find(p => p.code === code) if (c) { return c.value } @@ -168,21 +177,21 @@ export default class index extends Component { /** * 打开新增/编辑弹窗 - * @param {*} modal - * @param {*} record + * @param {*} modal + * @param {*} record */ onOpen(modal, record) { modal.current.open({ orgId: this.selectId, - record + record, }) } /** * 对表格上的操作进行统一处理 * [异步] - * @param {*} action - * @param {*} successMessage + * @param {*} action + * @param {*} successMessage */ async onAction(action, successMessage) { this.table.current.onLoading() @@ -202,13 +211,10 @@ export default class index extends Component { /** * 删除 - * @param {*} record + * @param {*} record */ onDelete(record) { - this.onAction( - apiAction.delete(record), - '删除成功' - ) + this.onAction(apiAction.delete(record), '删除成功') } //#region 自定义方法 @@ -220,7 +226,7 @@ export default class index extends Component { ref={this.treeLayout} loadData={this.loadTreeData} defaultExpanded={true} - onSelect={(key) => this.onSelectTree(key)} + onSelect={key => this.onSelectTree(key)} > @@ -239,7 +245,9 @@ export default class index extends Component { + > + 新增{name} + } /> diff --git a/web-react/src/pages/system/app/index.jsx b/web-react/src/pages/system/app/index.jsx index 8fdfcd0..458fb4c 100644 --- a/web-react/src/pages/system/app/index.jsx +++ b/web-react/src/pages/system/app/index.jsx @@ -15,18 +15,17 @@ const apiAction = { edit: api.sysAppEdit, delete: api.sysAppDelete, - setDefault: api.sysAppSetAsDefault + setDefault: api.sysAppSetAsDefault, } // 用于弹窗标题 const name = '应用' export default class index extends Component { - state = { codes: { - commonStatus: [] - } + commonStatus: [], + }, } // 表格实例 @@ -42,51 +41,58 @@ export default class index extends Component { { title: '应用名称', dataIndex: 'name', + width: 300, sorter: true, }, { title: '唯一编码', dataIndex: 'code', + width: 300, sorter: true, }, { title: '是否默认', dataIndex: 'active', + width: 200, sorter: true, - render: (text, record) => (<> - {text ? '是' : '否'} - { - !record.active && - - - this.onSetDefault(record)} - > - 设为默认 - - - - } - ) + render: (text, record) => ( + <> + {text ? '是' : '否'} + {!record.active && ( + + + + this.onSetDefault(record)} + > + 设为默认 + + + + )} + + ), }, { title: '状态', dataIndex: 'status', + width: 100, sorter: true, - render: text => (<>{this.bindCodeValue(text, 'common_status')}) + render: text => <>{this.bindCodeValue(text, 'common_status')}, }, { title: '排序', dataIndex: 'sort', + width: 100, sorter: true, }, ] /** * 构造函数,在渲染前动态添加操作字段等 - * @param {*} props + * @param {*} props */ constructor(props) { super(props) @@ -98,20 +104,22 @@ export default class index extends Component { title: '操作', width: 150, dataIndex: 'actions', - render: (text, record) => ( - - this.onOpen(this.editForm, record)}>编辑 - - - this.onDelete(record)} - > - 删除 - - - ) + render: (text, record) => ( + + + this.onOpen(this.editForm, record)}>编辑 + + + this.onDelete(record)} + > + 删除 + + + + ), }) } } @@ -120,9 +128,9 @@ export default class index extends Component { * 阻止外部组件引发的渲染,提升性能 * 可自行添加渲染条件 * [必要] - * @param {*} props - * @param {*} state - * @returns + * @param {*} props + * @param {*} state + * @returns */ shouldComponentUpdate(props, state) { return !isEqual(this.state, state) @@ -136,20 +144,23 @@ export default class index extends Component { const { onLoading, onLoadData } = this.table.current onLoading() getDictData('common_status').then(res => { - this.setState({ - codes: res - }, () => { - onLoadData() - }) + this.setState( + { + codes: res, + }, + () => { + onLoadData() + } + ) }) } /** * 调用加载数据接口,可在调用前对query进行处理 * [异步,必要] - * @param {*} params - * @param {*} query - * @returns + * @param {*} params + * @param {*} query + * @returns */ loadData = async (params, query) => { const { data } = await apiAction.page({ @@ -161,15 +172,15 @@ export default class index extends Component { /** * 绑定字典数据 - * @param {*} code - * @param {*} name - * @returns + * @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) + const c = codes.find(p => p.code == code) if (c) { return c.value } @@ -179,20 +190,20 @@ export default class index extends Component { /** * 打开新增/编辑弹窗 - * @param {*} modal - * @param {*} record + * @param {*} modal + * @param {*} record */ onOpen(modal, record) { modal.current.open({ - record + record, }) } /** * 对表格上的操作进行统一处理 * [异步] - * @param {*} action - * @param {*} successMessage + * @param {*} action + * @param {*} successMessage */ async onAction(action, successMessage) { const { onLoading, onLoaded, onReloadData } = this.table.current @@ -212,21 +223,15 @@ export default class index extends Component { /** * 删除 - * @param {*} record + * @param {*} record */ onDelete(record) { - this.onAction( - apiAction.delete(record), - '删除成功' - ) + this.onAction(apiAction.delete(record), '删除成功') } //#region 自定义方法 async onSetDefault(record) { - this.onAction( - apiAction.setDefault(record), - '设置成功' - ) + this.onAction(apiAction.setDefault(record), '设置成功') } //#endregion @@ -255,7 +260,9 @@ export default class index extends Component { + > + 新增{name} + } /> diff --git a/web-react/src/pages/system/area/index.jsx b/web-react/src/pages/system/area/index.jsx index ee8a43e..95e5f16 100644 --- a/web-react/src/pages/system/area/index.jsx +++ b/web-react/src/pages/system/area/index.jsx @@ -1,6 +1,14 @@ import React, { Component } from 'react' import { Button, Card, Form, Input, message as Message, Popconfirm } from 'antd' -import { AntIcon, Auth, Container, ModalForm, QueryTable, QueryTableActions, QueryTreeLayout } from 'components' +import { + AntIcon, + Auth, + Container, + ModalForm, + QueryTable, + QueryTableActions, + QueryTreeLayout, +} from 'components' import { api } from 'common/api' import auth from 'components/authorized/handler' import { toCamelCase } from 'util/format' @@ -13,17 +21,16 @@ const apiAction = { page: api.sysAreaPage, add: api.sysAreaAdd, edit: api.sysAreaEdit, - delete: api.sysAreaDelete + delete: api.sysAreaDelete, } const name = '区域' export default class index extends Component { - state = { codes: { - dicAreacodeType: [] - } + areacodeType: [], + }, } // 表格实例 @@ -41,37 +48,43 @@ export default class index extends Component { title: '区域类型', dataIndex: 'levelType', sorter: true, - render: text => (<>{this.bindCodeValue(text, 'dic_areacode_type')}) + width: 50, + render: text => <>{this.bindCodeValue(text, 'areacode_type')}, }, { title: '区域名称', dataIndex: 'name', + width: 100, sorter: true, }, { title: '区域编号', dataIndex: 'code', + width: 80, sorter: true, }, { title: '行政编号', dataIndex: 'adCode', + width: 80, sorter: true, }, { title: '描述', dataIndex: 'note', + width: 200, sorter: false, }, { title: '排序', dataIndex: 'sort', + width: 80, sorter: true, }, ] /** * 构造函数,在渲染前动态添加操作字段等 - * @param {*} props + * @param {*} props */ constructor(props) { super(props) @@ -83,20 +96,22 @@ export default class index extends Component { title: '操作', width: 150, dataIndex: 'actions', - render: (text, record) => ( - - this.onOpen(this.editForm, record)}>编辑 - - - this.onDelete(record)} - > - 删除 - - - ) + render: (text, record) => ( + + + this.onOpen(this.editForm, record)}>编辑 + + + this.onDelete(record)} + > + 删除 + + + + ), }) } } @@ -104,9 +119,9 @@ export default class index extends Component { * 阻止外部组件引发的渲染,提升性能 * 可自行添加渲染条件 * [必要] - * @param {*} props - * @param {*} state - * @returns + * @param {*} props + * @param {*} state + * @returns */ shouldComponentUpdate(props, state) { return !isEqual(this.state, state) @@ -118,31 +133,33 @@ export default class index extends Component { */ componentDidMount() { this.table.current.onLoading() - getDictData('dic_areacode_type').then(res => { - this.setState({ - codes: res - }, () => { - this.table.current.onLoadData() - }) + getDictData('areacode_type').then(res => { + this.setState( + { + codes: res, + }, + () => { + this.table.current.onLoadData() + } + ) }) } /** - * 调用加载数据接口,可在调用前对query进行处理 - * [异步,必要] - * @param {*} params - * @param {*} query - * @returns - */ + * 调用加载数据接口,可在调用前对query进行处理 + * [异步,必要] + * @param {*} params + * @param {*} query + * @returns + */ loadData = async (params, query) => { - query = { ...query, - pcode: this.selectCode + pcode: this.selectCode, } //首次加载根据code列升序排序 if (!params.sortField) { - params.sortField = 'code'; - params.sortOrder = 'ascend'; + params.sortField = 'code' + params.sortOrder = 'ascend' } const { data } = await apiAction.page({ ...params, @@ -164,7 +181,7 @@ export default class index extends Component { /** * 树节点选中事件 * [必要] - * @param {*} id + * @param {*} id */ onSelectTree(code) { this.selectCode = code @@ -173,15 +190,15 @@ export default class index extends Component { /** * 绑定字典数据 - * @param {*} code - * @param {*} name - * @returns + * @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) + const c = codes.find(p => +p.code === code) if (c) { return c.value } @@ -191,21 +208,21 @@ export default class index extends Component { /** * 打开新增/编辑弹窗 - * @param {*} modal - * @param {*} record + * @param {*} modal + * @param {*} record */ onOpen(modal, record) { modal.current.open({ pcode: this.pcode, - record + record, }) } /** * 对表格上的操作进行统一处理 * [异步] - * @param {*} action - * @param {*} successMessage + * @param {*} action + * @param {*} successMessage */ async onAction(action, successMessage) { this.table.current.onLoading() @@ -220,13 +237,10 @@ export default class index extends Component { /** * 删除 - * @param {*} record + * @param {*} record */ onDelete(record) { - this.onAction( - apiAction.delete(record), - '删除成功' - ) + this.onAction(apiAction.delete(record), '删除成功') } render() { @@ -234,7 +248,7 @@ export default class index extends Component { this.onSelectTree(key)} + onSelect={key => this.onSelectTree(key)} replaceFields={{ value: 'code', title: 'name', children: 'children' }} > @@ -259,12 +273,12 @@ export default class index extends Component { + > + 新增{name} + } - > - - + > ) } -} \ No newline at end of file +} diff --git a/web-react/src/pages/system/file/index.jsx b/web-react/src/pages/system/file/index.jsx new file mode 100644 index 0000000..f5db6e0 --- /dev/null +++ b/web-react/src/pages/system/file/index.jsx @@ -0,0 +1,398 @@ +import React, { Component } from 'react' +import { + Button, + Card, + Form, + Input, + message as Message, + Popconfirm, + Select, + Tag, + Tooltip, + Upload, +} from 'antd' +import { AntIcon, Auth, Container, PhotoPreview, QueryTable, QueryTableActions } 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' +import { ArrayBufferToBase64, GetFileName, PreviewFileArrayBuffer } from 'util/file' + +/** + * 注释段[\/**\/]为必须要改 + */ + +/** + * 配置页面所需接口函数 + */ +const apiAction = { + page: api.sysFileInfoPage, + delete: api.sysFileInfoDelete, +} + +/** + * 用于弹窗标题 + * [必要] + */ +const name = '/**/' + +/** + * 统一配置权限标识 + * [必要] + */ +const authName = 'sysFileInfo' + +export default class index extends Component { + state = { + codes: { + fileStorageLocation: [], + }, + + uploading: false, + } + + // 表格实例 + table = React.createRef() + + photoPreview = React.createRef() + + columns = [ + { + title: '文件名称', + dataIndex: 'fileOriginName', + width: 300, + ellipsis: { + showTitle: false, + }, + sorter: true, + render: text => {text}, + }, + { + title: '文件后缀', + dataIndex: 'fileSuffix', + width: 120, + sorter: true, + render: text => {text}, + }, + { + title: '文件大小', + dataIndex: 'fileSizeKb', + width: 120, + sorter: true, + render: text => ( + <> + {text} + KB + + ), + }, + { + title: '存储位置', + dataIndex: 'fileLocation', + width: 120, + sorter: true, + render: text => this.bindCodeValue(text, 'file_storage_location'), + }, + { + title: '文件仓库', + dataIndex: 'fileBucket', + width: 200, + ellipsis: { + showTitle: false, + }, + sorter: true, + render: text => {text}, + }, + { + title: '唯一标识id', + dataIndex: 'fileObjectName', + width: 250, + ellipsis: { + showTitle: false, + }, + sorter: true, + render: text => {text}, + }, + { + title: '上传时间', + dataIndex: 'createdTime', + width: 200, + sorter: true, + defaultSortOrder: 'descend', + }, + ] + + /** + * 构造函数,在渲染前动态添加操作字段等 + * @param {*} props + */ + constructor(props) { + super(props) + + const flag = auth({ [authName]: 'delete' }) + + if (flag) { + this.columns.push({ + title: '操作', + width: 150, + dataIndex: 'actions', + render: (text, record) => ( + + this.onFileDownload(record)}>下载 + + this.onDelete(record)} + > + 删除 + + + {['png', 'jpeg', 'jpg', 'gif', 'tif', 'bmp'].includes( + record.fileSuffix + ) && this.onFilePreview(record)}>预览} + + ), + }) + } + } + + /** + * 阻止外部组件引发的渲染,提升性能 + * 可自行添加渲染条件 + * [必要] + * @param {*} props + * @param {*} state + * @returns + */ + shouldComponentUpdate(props, state) { + return !isEqual(this.state, state) + } + + /** + * 加载字典数据,之后开始加载表格数据 + * 如果必须要加载字典数据,可直接对表格设置autoLoad=true + */ + componentDidMount() { + const { onLoading, onLoadData } = this.table.current + onLoading() + getDictData('file_storage_location').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 {*} record + */ + onOpen(modal, record) { + modal.current.open({ + record, + }) + } + + /** + * 对表格上的操作进行统一处理 + * [异步] + * @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 {*} record + */ + onDelete(record) { + this.onAction(apiAction.delete(record), '删除成功') + } + + //#region 自定义方法 + async onFileUpload({ file }) { + this.setState({ uploading: true }) + const table = this.table.current + table.onLoading() + const fd = new FormData() + fd.append('file', file) + try { + await api.sysFileInfoUpload(fd) + table.onReloadData() + } catch { + table.onLoaded() + } finally { + this.setState({ uploading: false }) + } + } + + async onFilePreview({ id }) { + const key = Math.random().toString(16).slice(2) + const hide = Message.loading({ + key, + content: '正在获取文件...', + duration: 0, + }) + const file = await PreviewFileArrayBuffer(id) + if (file) { + const base64 = await ArrayBufferToBase64(file) + var img = new Image() + img.onload = () => { + const items = [ + { + src: base64, + w: img.naturalWidth, + h: img.naturalHeight, + }, + ] + this.photoPreview.current.initPhotoSwipe(items) + + hide() + } + img.onerror = () => { + Message.error({ + key, + content: '获取文件失败', + }) + } + img.src = base64 + } else { + Message.error({ + key, + content: '获取文件失败', + }) + } + } + + async onFileDownload({ id }) { + const key = Math.random().toString(16).slice(2) + const hide = Message.loading({ + key, + content: '正在获取文件...', + duration: 0, + }) + try { + const { data, headers } = await api.sysFileInfoDownload({ id }) + const url = window.URL.createObjectURL(data) + const fileName = GetFileName(headers['content-disposition']) + const a = document.createElement('a') + a.href = url + a.download = fileName + a.click() + window.URL.revokeObjectURL(url) + a.remove() + hide() + } catch { + Message.error({ + key, + content: '下载文件失败', + }) + } + } + //#endregion + + render() { + const { codes, uploading } = this.state + + return ( + +
+ + + + + + + + + + + + + } + operator={ + + this.onFileUpload(e)} fileList={[]}> + + + + } + /> + + + +
+ ) + } +} diff --git a/web-react/src/store/reducer/business.js b/web-react/src/store/reducer/business.js new file mode 100644 index 0000000..cad0e2a --- /dev/null +++ b/web-react/src/store/reducer/business.js @@ -0,0 +1,33 @@ +const business = (state = {}, action) => { + switch (action.type) { + case 'PATROL_INIT_GRADE_BY_COMPLETED_DATE': + { + const completedDate = state.completedDate || [] + const { date } = action + const record = completedDate.find(p => p.id === date.id) + if (record) { + record.value = date.value + } else { + completedDate.push(date) + } + const _state = { ...state, completedDate } + return _state + } + case 'PATROL_REMOVE_INIT_GRADE_BY_COMPLETED_DATE': + { + const completedDate = state.completedDate || [] + const record = completedDate.find(p => p.id === action.id) + if (!record) { + return state + } else { + completedDate.splice(completedDate.indexOf(record), 1) + const _state = { ...state, completedDate } + return _state + } + } + default: + return state + } +} + +export default business \ No newline at end of file diff --git a/web-react/src/store/reducer/index.js b/web-react/src/store/reducer/index.js index cd269ca..17c4c65 100644 --- a/web-react/src/store/reducer/index.js +++ b/web-react/src/store/reducer/index.js @@ -3,12 +3,14 @@ import user from './user' import layout from './layout' import nav from './nav' import dictData from './dict-data' +import business from './business' const combine = combineReducers({ user, layout, nav, - dictData + dictData, + business }) export default combine \ No newline at end of file diff --git a/web-react/src/util/dic/index.js b/web-react/src/util/dic/index.js index 3c1ffbe..ba3e28e 100644 --- a/web-react/src/util/dic/index.js +++ b/web-react/src/util/dic/index.js @@ -7,14 +7,14 @@ const { getState, dispatch } = store const getDictData = async (...args) => { const dictData = getState('dictData') - let result = {} + let dict = {} const code = [] for (let i = 0; i < args.length; i++) { const codeName = toCamelCase(args[i]) if (!dictData.hasOwnProperty(codeName)) { code.push(args[i]) } else { - result[codeName] = dictData[codeName] + dict[codeName] = dictData[codeName] } } @@ -29,14 +29,17 @@ const getDictData = async (...args) => { value }) - result = { ...result, ...value } - - return result + dict = { ...dict, ...value } } catch { } } - return dictData + const result = {} + args.forEach(p => { + const codeName = toCamelCase(p) + result[codeName] = dict[codeName] + }) + return result } export default getDictData \ No newline at end of file diff --git a/web-react/src/views/login/index.jsx b/web-react/src/views/login/index.jsx index 031b9c3..e04b7ea 100644 --- a/web-react/src/views/login/index.jsx +++ b/web-react/src/views/login/index.jsx @@ -8,47 +8,46 @@ import { token } from 'common/token' import './index.less' export default class index extends Component { - state = { loading: false, focusUser: false, - focusPassword: false + focusPassword: false, } backgroundImage = require(`assets/image/login-bg-0${Math.floor(Math.random() * 4)}.jpg`) focus = { user: false, - password: false + password: false, } form = React.createRef() - onLogin = (values) => { - this.setState({ - loading: true - }) + onLogin = values => { + this.setState({ loading: true }) let { account, password } = values password = encryptByRSA(password, RSA_PUBLIC_KEY) - api.login({ account, password }).then(({ success, data, message }) => { - if (success) { - token.value = data - Message.success('登录成功') - this.props.history.replace('/') - } else { - Message.error(message) - } - }).catch(({ message }) => { - if (typeof message === 'object' && message[0]) { - Message.error(message[0].messages[0]) - } - }) + api.login({ account, password }) + .then(({ success, data, message }) => { + if (success) { + token.value = data + Message.success('登录成功') + this.props.history.replace('/') + } else { + Message.error(message) + } + }) + .catch(({ message }) => { + if (typeof message === 'object' && message[0]) { + Message.error(message[0].messages[0]) + } + this.setState({ loading: false }) + }) } render() { - return (
@@ -62,8 +61,14 @@ export default class index extends Component { label="用户名" > { this.setState({ focusUser: !!this.form.current.getFieldValue('account') }); }} - onFocus={() => { this.setState({ focusUser: true }) }} + onBlur={() => { + this.setState({ + focusUser: !!this.form.current.getFieldValue('account'), + }) + }} + onFocus={() => { + this.setState({ focusUser: true }) + }} size="large" autoComplete="off" /> @@ -75,8 +80,15 @@ export default class index extends Component { label="密码" > { this.setState({ focusPassword: !!this.form.current.getFieldValue('password') }) }} - onFocus={() => { this.setState({ focusPassword: true }) }} + onBlur={() => { + this.setState({ + focusPassword: + !!this.form.current.getFieldValue('password'), + }) + }} + onFocus={() => { + this.setState({ focusPassword: true }) + }} size="large" autoComplete="off" /> @@ -89,7 +101,9 @@ export default class index extends Component { htmlType="submit" size="large" type="primary" - >登录 + > + 登录 + diff --git a/web-react/src/views/main/_layout/content/index.jsx b/web-react/src/views/main/_layout/content/index.jsx index 2ebe6f2..f9aadc7 100644 --- a/web-react/src/views/main/_layout/content/index.jsx +++ b/web-react/src/views/main/_layout/content/index.jsx @@ -5,14 +5,15 @@ import 'nprogress/nprogress.css' import AntIcon from 'components/ant-icon' import { Container } from 'components' -NProgress.configure({ parent: '.ant-layout-content > .yo-tab-external-mount > .yo-tab-external-mount-content' }); +NProgress.configure({ + parent: '.ant-layout-content > .yo-tab-external-mount > .yo-tab-external-mount-content', +}) class ComponentDynamic extends Component { - state = { // 组件内部组件的key,用于刷新 key: null, - component: null + component: null, } shouldComponentUpdate() { @@ -32,8 +33,7 @@ class ComponentDynamic extends Component { // 在这里使用setTimeout调用,是防止打开窗口时卡顿 setTimeout(async () => { - - let component; + let component try { component = await import(`../../../../pages${this.props.path}`) @@ -41,38 +41,41 @@ class ComponentDynamic extends Component { component = await import('views/error/404') } - this.setState({ - key: Math.random().toString(16).slice(2), - component: component.default - }, () => { - NProgress.done() - }) - + this.setState( + { + key: Math.random().toString(16).slice(2), + component: component.default, + }, + () => { + NProgress.done() + } + ) }) } render() { if (this.state.component) { - return - - Ewide Core ©2021 v1.0 - - - } - /> + return ( + + + Ewide Core ©2021 v1.0 + + + } + /> + ) } return <> } } export default class index extends Component { - state = { - actived: '' + actived: '', } panes = [] @@ -83,13 +86,13 @@ export default class index extends Component { static getDerivedStateFromProps(props) { return { - actived: props.actived + actived: props.actived, } } onChange(activeKey) { this.props.parent.setState({ - actived: activeKey + actived: activeKey, }) } @@ -99,7 +102,7 @@ export default class index extends Component { } } - onReload = (key) => { + onReload = key => { key = key || this.state.actived const pane = this.panes.find(p => p.props.id === key) if (pane) { @@ -108,7 +111,6 @@ export default class index extends Component { } render() { - this.panes = [] return ( @@ -118,60 +120,85 @@ export default class index extends Component { type="editable-card" hideAdd activeKey={this.state.actived} - onChange={(activeKey) => this.onChange(activeKey)} + onChange={activeKey => this.onChange(activeKey)} onEdit={(targetKey, action) => this.onClose(targetKey, action)} > - { - this.props.panes.map(pane => { - return ( - - this.onReload(pane.key)}>重新加载 - - window.closeContentWindow(pane.key)}>关闭 - window.closeOtherContentWindow(pane.key)}>关闭其他标签页 - window.closeRightContentWindow(pane.key)}>关闭右侧标签页 - - } - > -
- {pane.icon && } - {pane.title} -
- - } - /> - ) - }) - } + {this.props.panes.map(pane => { + return ( + + this.onReload(pane.key)} + > + 重新加载 + + + + window.closeContentWindow(pane.key) + } + > + 关闭 + + + window.closeOtherContentWindow(pane.key) + } + > + 关闭其他标签页 + + + window.closeRightContentWindow(pane.key) + } + > + 关闭右侧标签页 + + + } + > +
+ {pane.icon && } + {pane.title} +
+ + } + /> + ) + })}
- { - this.props.panes.map(pane => { - return ( -
{ + return ( +
+ - this.panes.push(p)} - /> -
- ) - }) - } + param={pane.param} + onRef={p => this.panes.push(p)} + /> +
+ ) + })}
diff --git a/web-react/src/views/main/_layout/header/user.jsx b/web-react/src/views/main/_layout/header/user.jsx index 94f9e1b..b126243 100644 --- a/web-react/src/views/main/_layout/header/user.jsx +++ b/web-react/src/views/main/_layout/header/user.jsx @@ -1,6 +1,6 @@ import React, { Component } from 'react' import { withRouter } from 'react-router-dom' -import { Modal, message as Message } from 'antd' +import { Modal, message as Message, Dropdown, Button, Menu, Popover, Tag, Row, Col } from 'antd' import { isEqual } from 'lodash' import store from 'store' import { api } from 'common/api' @@ -11,12 +11,8 @@ const { getState, dispatch, subscribe } = store const storePath = 'user' -let userOpenTimer, userCloseTimer -let initDropdownHeight - class User extends Component { state = { - dropdownHeight: 0, user: getState(storePath), } @@ -30,40 +26,14 @@ class User extends Component { }) } - // shouldComponentUpdate(props, state) { - // return !isEqual(this.state, state) - // } - - componentDidMount() { - initDropdownHeight = this.refs.dropdown.scrollHeight + shouldComponentUpdate(props, state) { + return !isEqual(this.state, state) } componentWillUnmount() { this.unsubscribe() } - onOpen = e => { - clearTimeout(userCloseTimer) - this.refs.container.classList.add('open') - userOpenTimer = setTimeout(() => { - this.refs.container.classList.add('drop') - this.setState({ - dropdownHeight: initDropdownHeight, - }) - }, 300) - } - - onClose = e => { - clearTimeout(userOpenTimer) - this.refs.container.classList.remove('drop') - this.setState({ - dropdownHeight: 0, - }) - userCloseTimer = setTimeout(() => { - this.refs.container.classList.remove('open') - }, 300) - } - onAccountSetting = () => {} onLogout = () => { @@ -84,48 +54,74 @@ class User extends Component { }) } - render() { + renderMenu() { + const { user } = this.state + return ( -
{ - this.onOpen(e) - }} - onMouseLeave={e => { - this.onClose(e) - }} - ref="container" - > + <> +
+ + + {user.nickName || user.name} + + + {user.account} + + +

上次登录时间:{user.lastLoginTime}

+ {user.adminType === 1 && ( + + 超级管理员 + + )} + {user.roles && + user.roles.map(role => ( + + {role.name} + + ))} +
+ + + + + 个人中心 + + 其他菜单 + 其他菜单 + 其他菜单 + + this.onLogout()}> + + 退出登录 + + + + ) + } + + render() { + const { user } = this.state + + return ( +
-
- } - id={this.state.user.avatar} - /> - - {this.state.user.nickName || this.state.user.name} - -
-
-
    -
  • - - 个人中心 -
  • -
  • -
  • - - 退出登录 -
  • -
-
+
+ } + id={user.avatar} + /> +
+
) diff --git a/web-react/src/views/main/index.jsx b/web-react/src/views/main/index.jsx index 5694edc..665c59f 100644 --- a/web-react/src/views/main/index.jsx +++ b/web-react/src/views/main/index.jsx @@ -232,21 +232,24 @@ export default class index extends Component { } render() { - const antIcon = + const { loading, panes, actived } = this.state return (
- + +

Loading

+
+ } + >
- +