为什么都没了
This commit is contained in:
148
framework/web-react/src/pages/business/house/code/form/index.jsx
Normal file
148
framework/web-react/src/pages/business/house/code/form/index.jsx
Normal file
@@ -0,0 +1,148 @@
|
||||
import React, { Component } from 'react'
|
||||
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'),
|
||||
},
|
||||
]
|
||||
|
||||
export default class index extends Component {
|
||||
state = {
|
||||
loading: true,
|
||||
record: null,
|
||||
|
||||
saving: false,
|
||||
}
|
||||
|
||||
children = []
|
||||
|
||||
formData = {}
|
||||
|
||||
shouldComponentUpdate(props, state) {
|
||||
return !isEqual(this.state, state)
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
// 获取详细数据
|
||||
const { id } = this.props.param
|
||||
if (id) {
|
||||
api.houseCodeDetail({ id }).then(({ data }) => {
|
||||
this.setState({
|
||||
record: data,
|
||||
loading: false,
|
||||
})
|
||||
})
|
||||
} else {
|
||||
this.setState({ loading: false })
|
||||
}
|
||||
}
|
||||
|
||||
async onSubmit() {
|
||||
for (const child of this.children) {
|
||||
try {
|
||||
const data = await child.getData()
|
||||
this.formData = {
|
||||
...this.formData,
|
||||
...data,
|
||||
}
|
||||
} catch (e) {
|
||||
return e
|
||||
}
|
||||
}
|
||||
|
||||
//#region 提交数据
|
||||
this.setState({ saving: true })
|
||||
if (!this.state.record) {
|
||||
// 新增
|
||||
try {
|
||||
const { success } = await api.houseCodeAdd(this.formData)
|
||||
if (success) {
|
||||
Message.success('保存成功')
|
||||
Modal.confirm({
|
||||
content: '已添加成功,是否继续添加?',
|
||||
onOk: () => {},
|
||||
onCancel: () => {
|
||||
window.closeContentWindow()
|
||||
},
|
||||
})
|
||||
}
|
||||
} finally {
|
||||
this.setState({ saving: false })
|
||||
}
|
||||
} else {
|
||||
// 编辑
|
||||
try {
|
||||
const { success } = await api.houseCodeEdit({
|
||||
id: this.state.record.id,
|
||||
...this.formData,
|
||||
})
|
||||
if (success) {
|
||||
Message.success('保存成功')
|
||||
}
|
||||
} finally {
|
||||
this.setState({ saving: false })
|
||||
}
|
||||
}
|
||||
//#endregion
|
||||
}
|
||||
|
||||
render() {
|
||||
const { id } = this.props
|
||||
|
||||
const { loading, record, saving } = this.state
|
||||
|
||||
return (
|
||||
<div className="yo-form-page">
|
||||
<Container mode="fluid" ref={this.setContainer}>
|
||||
<Row gutter={16} type="flex">
|
||||
<Col flex="1">
|
||||
<br />
|
||||
<div className="yo-adorn--house-top" />
|
||||
<Card className="yo-form-page--body">
|
||||
{parts.map((item, i) => (
|
||||
<section key={i} id={`form-${i}-${id}`}>
|
||||
{item.title && <h5>{item.title}</h5>}
|
||||
<Spin
|
||||
spinning={loading}
|
||||
indicator={<AntIcon type="loading" />}
|
||||
wrapperClassName={loading && 'h-400-min'}
|
||||
>
|
||||
{!loading && (
|
||||
<ComponentDynamic
|
||||
is={item.component}
|
||||
record={record}
|
||||
onRef={r => this.children.push(r)}
|
||||
/>
|
||||
)}
|
||||
</Spin>
|
||||
</section>
|
||||
))}
|
||||
</Card>
|
||||
</Col>
|
||||
</Row>
|
||||
</Container>
|
||||
<div className="yo-form-page--bar">
|
||||
<Container mode="fluid">
|
||||
<div className="yo-form-page--bar-inner">
|
||||
<span></span>
|
||||
<span>
|
||||
<Button onClick={() => window.closeContentWindow()}>取消</Button>
|
||||
<Button
|
||||
loading={saving}
|
||||
type="primary"
|
||||
onClick={() => this.onSubmit()}
|
||||
>
|
||||
保存
|
||||
</Button>
|
||||
</span>
|
||||
</div>
|
||||
</Container>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
744
framework/web-react/src/pages/business/house/code/form/part.jsx
Normal file
744
framework/web-react/src/pages/business/house/code/form/part.jsx
Normal file
@@ -0,0 +1,744 @@
|
||||
import React, { Component } from 'react'
|
||||
import {
|
||||
Button,
|
||||
Cascader,
|
||||
Form,
|
||||
Input,
|
||||
InputNumber,
|
||||
Radio,
|
||||
Spin,
|
||||
Select,
|
||||
Row,
|
||||
Col,
|
||||
Tag,
|
||||
Alert,
|
||||
Tooltip,
|
||||
} from 'antd'
|
||||
import { AntIcon, Auth } from 'components'
|
||||
import { cloneDeep, isEqual } from 'lodash'
|
||||
import getDictData from 'util/dic'
|
||||
import { api } from 'common/api'
|
||||
import { CITY } from 'util/global'
|
||||
import auth from 'components/authorized/handler'
|
||||
import store from 'store'
|
||||
|
||||
const { getState } = store
|
||||
|
||||
const initialValues = {
|
||||
type: 1,
|
||||
industry: 1,
|
||||
}
|
||||
|
||||
const labelCol = { flex: '150px' }
|
||||
const wrapperCol = { flex: '1' }
|
||||
|
||||
export default class form extends Component {
|
||||
state = {
|
||||
// 加载状态
|
||||
loading: true,
|
||||
codes: {
|
||||
houseType: [],
|
||||
houseIndustry: [],
|
||||
},
|
||||
options: {
|
||||
areaTree: [],
|
||||
projects: [],
|
||||
zones: [],
|
||||
},
|
||||
|
||||
houseCode: '',
|
||||
showIndustry: false,
|
||||
}
|
||||
|
||||
theme = getState('layout').theme
|
||||
|
||||
nav = getState('nav').nav
|
||||
|
||||
// 表单实例
|
||||
form = React.createRef()
|
||||
|
||||
iconSelector = React.createRef()
|
||||
|
||||
// 初始化数据
|
||||
record = {}
|
||||
|
||||
shouldComponentUpdate(props, state) {
|
||||
return !isEqual(this.state, state)
|
||||
}
|
||||
|
||||
/**
|
||||
* mount后回调
|
||||
*/
|
||||
componentDidMount() {
|
||||
if (this.props.onRef) {
|
||||
this.props.onRef(this)
|
||||
}
|
||||
this.fillData({
|
||||
record: this.props.record,
|
||||
})
|
||||
|
||||
console.log(this.nav)
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
if (this.map) this.map.destroy()
|
||||
}
|
||||
|
||||
/**
|
||||
* 填充数据
|
||||
* 可以在设置this.record之后对其作出数据结构调整
|
||||
* [异步,必要]
|
||||
* @param {*} params
|
||||
*/
|
||||
async fillData(params) {
|
||||
this.record = cloneDeep(params.record)
|
||||
//#region 从后端转换成前段所需格式
|
||||
await this.initMap()
|
||||
const options = { ...this.state.options }
|
||||
const { data: areaTree } = await api.getAreaTree()
|
||||
options.areaTree = areaTree
|
||||
// 有数据
|
||||
if (this.record) {
|
||||
const { type, areaCode } = this.record
|
||||
// areaCode需要从字符串转为数组
|
||||
this.record.areaCode = [
|
||||
areaCode.substr(0, 4),
|
||||
areaCode.substr(0, 6),
|
||||
areaCode.substr(0, 9),
|
||||
areaCode,
|
||||
]
|
||||
switch (areaTree[0].adCode.length) {
|
||||
case 6:
|
||||
this.record.areaCode = this.record.areaCode.slice(1)
|
||||
break
|
||||
case 9:
|
||||
this.record.areaCode = this.record.areaCode.slice(2)
|
||||
break
|
||||
default:
|
||||
break
|
||||
}
|
||||
// 获取项目和片区列表
|
||||
const data = await this.getProjectsAndZones({
|
||||
areaCode: this.record.areaCode,
|
||||
type,
|
||||
})
|
||||
Object.assign(options, data)
|
||||
// 定位
|
||||
const position = [this.record.lng, this.record.lat]
|
||||
this.setMarker(position)
|
||||
this.map.setCenter(position)
|
||||
}
|
||||
const codes = await getDictData('house_type', 'house_industry')
|
||||
|
||||
this.setState({
|
||||
codes,
|
||||
options,
|
||||
})
|
||||
|
||||
this.showHouseCode()
|
||||
//#endregion
|
||||
this.form.current.setFieldsValue(this.record)
|
||||
|
||||
this.setState({
|
||||
loading: false,
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取数据
|
||||
* 可以对postData进行数据结构调整
|
||||
* [异步,必要]
|
||||
* @returns
|
||||
*/
|
||||
async getData() {
|
||||
const form = this.form.current
|
||||
|
||||
const valid = await form.validateFields()
|
||||
if (valid) {
|
||||
const postData = form.getFieldsValue()
|
||||
//#region 从前段转换后端所需格式
|
||||
if (postData.areaCode) {
|
||||
postData.areaCode = postData.areaCode[postData.areaCode.length - 1]
|
||||
}
|
||||
//#endregion
|
||||
return postData
|
||||
}
|
||||
}
|
||||
|
||||
//#region 自定义方法
|
||||
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,
|
||||
mapStyle: `amap://styles/${this.theme}`,
|
||||
})
|
||||
|
||||
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({
|
||||
address,
|
||||
lng,
|
||||
lat,
|
||||
})
|
||||
}
|
||||
|
||||
async getProjectsAndZones({ areaCode, type } = {}, mode = ['projects', 'zones']) {
|
||||
if (!areaCode || !type) {
|
||||
const value = this.form.current.getFieldsValue()
|
||||
areaCode = value.areaCode
|
||||
type = value.type
|
||||
}
|
||||
|
||||
if (!(areaCode && areaCode.length > 0 && areaCode[areaCode.length - 1].length === 12))
|
||||
return null
|
||||
|
||||
try {
|
||||
const result = {}
|
||||
|
||||
if (mode.includes('projects')) {
|
||||
const { data: projects } = await api.houseProjectList({
|
||||
areaCode: areaCode[areaCode.length - 1],
|
||||
type,
|
||||
})
|
||||
result.projects = projects
|
||||
}
|
||||
if (mode.includes('zones')) {
|
||||
const { data: zones } = await api.houseZoneList({
|
||||
areaCode: areaCode[areaCode.length - 1],
|
||||
})
|
||||
result.zones = zones
|
||||
}
|
||||
|
||||
return result
|
||||
} catch {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
async getCodeNo(projectId) {
|
||||
this.setState({ loading: true })
|
||||
const { data: no } = await api.houseCodeNo({ projectId })
|
||||
this.form.current.setFieldsValue({
|
||||
no,
|
||||
})
|
||||
this.setState({ loading: false })
|
||||
}
|
||||
|
||||
showHouseCode(values) {
|
||||
if (this.record) {
|
||||
this.setState({
|
||||
houseCode: this.record.houseCode,
|
||||
})
|
||||
} else if (values) {
|
||||
const { type, industry, areaCode, projectId, no } = values
|
||||
if (
|
||||
areaCode &&
|
||||
areaCode.length > 0 &&
|
||||
areaCode[areaCode.length - 1].length === 12 &&
|
||||
projectId &&
|
||||
no
|
||||
) {
|
||||
let houseCode = areaCode[areaCode.length - 1]
|
||||
const projectSort = this.state.options.projects.find(p => p.id === projectId).sort
|
||||
houseCode += projectSort.toString().padStart(3, '0')
|
||||
houseCode += no.toString().padStart(3, '0')
|
||||
if (type == 1) {
|
||||
this.setState({ houseCode })
|
||||
} else {
|
||||
if (!industry) {
|
||||
this.setState({ houseCode: '' })
|
||||
} else {
|
||||
const tag = this.state.codes.houseIndustry.find(p => p.code == industry)
|
||||
.extCode.tag
|
||||
houseCode += `-${tag}`
|
||||
this.setState({ houseCode })
|
||||
}
|
||||
}
|
||||
} else {
|
||||
this.setState({ houseCode: '' })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async onValuesChange(changedValues, allValues) {
|
||||
const form = this.form.current
|
||||
// 房屋性质
|
||||
if (changedValues.hasOwnProperty('type')) {
|
||||
this.setState({
|
||||
showIndustry: changedValues.type == 2,
|
||||
loading: true,
|
||||
})
|
||||
const data = await this.getProjectsAndZones()
|
||||
form.setFieldsValue({ projectId: undefined })
|
||||
this.setState({
|
||||
options: {
|
||||
...this.state.options,
|
||||
...data,
|
||||
},
|
||||
loading: false,
|
||||
})
|
||||
this.showHouseCode(form.getFieldsValue())
|
||||
}
|
||||
|
||||
// 所属区域
|
||||
else if (changedValues.hasOwnProperty('areaCode')) {
|
||||
const flag = auth({ houseCode: 'getNextNoByCode' })
|
||||
if (flag) {
|
||||
this.setState({ loading: true })
|
||||
const data = await this.getProjectsAndZones()
|
||||
form.setFieldsValue({ projectId: undefined, zoneId: undefined })
|
||||
this.setState({
|
||||
options: {
|
||||
...this.state.options,
|
||||
...data,
|
||||
},
|
||||
loading: false,
|
||||
})
|
||||
this.showHouseCode(form.getFieldsValue())
|
||||
}
|
||||
}
|
||||
|
||||
// 所属项目
|
||||
else if (changedValues.hasOwnProperty('projectId')) {
|
||||
await this.getCodeNo(changedValues.projectId)
|
||||
this.showHouseCode(form.getFieldsValue())
|
||||
}
|
||||
|
||||
// 所属行业
|
||||
else if (changedValues.hasOwnProperty('industry')) {
|
||||
this.showHouseCode(allValues)
|
||||
}
|
||||
|
||||
// 编号
|
||||
else if (changedValues.hasOwnProperty('no')) {
|
||||
this.showHouseCode(allValues)
|
||||
}
|
||||
}
|
||||
|
||||
async onReloadProjectsOrZones(mode) {
|
||||
this.setState({ loading: true })
|
||||
const data = await this.getProjectsAndZones({}, [mode])
|
||||
this.setState({
|
||||
options: {
|
||||
...this.state.options,
|
||||
...data,
|
||||
},
|
||||
loading: false,
|
||||
})
|
||||
}
|
||||
//#endregion
|
||||
|
||||
render() {
|
||||
const { loading, codes, options, showIndustry, houseCode } = this.state
|
||||
|
||||
return (
|
||||
<Form
|
||||
initialValues={initialValues}
|
||||
ref={this.form}
|
||||
// labelCol={labelCol}
|
||||
// wrapperCol={wrapperCol}
|
||||
onValuesChange={(changedValues, allValues) =>
|
||||
this.onValuesChange(changedValues, allValues)
|
||||
}
|
||||
layout="vertical"
|
||||
>
|
||||
<Spin spinning={loading} indicator={<AntIcon type="loading" />}>
|
||||
<Row gutter={16}>
|
||||
<Col span={10}>
|
||||
<div className="yo-map-container">
|
||||
<div className="yo-map--search">
|
||||
<Input.Search
|
||||
allowClear
|
||||
placeholder="请输入关键字"
|
||||
ref="map-search"
|
||||
/>
|
||||
</div>
|
||||
<div className="h-700" ref="map"></div>
|
||||
</div>
|
||||
</Col>
|
||||
<Col span={14}>
|
||||
<Form.Item
|
||||
label="房屋性质"
|
||||
name="type"
|
||||
rules={[{ required: true, message: '请选择房屋性质' }]}
|
||||
>
|
||||
<Radio.Group disabled={!!this.record} buttonStyle="solid">
|
||||
{codes.houseType.map(item => (
|
||||
<Radio.Button key={item.code} value={+item.code}>
|
||||
{item.value}
|
||||
</Radio.Button>
|
||||
))}
|
||||
</Radio.Group>
|
||||
</Form.Item>
|
||||
{showIndustry && (
|
||||
<Form.Item
|
||||
label="所属行业、系统"
|
||||
name="industry"
|
||||
rules={[{ required: true, message: '请选择所属行业、系统' }]}
|
||||
>
|
||||
<Radio.Group disabled={!!this.record} buttonStyle="solid">
|
||||
{codes.houseIndustry.map(item => (
|
||||
<Radio.Button key={item.code} value={+item.code}>
|
||||
{item.value}
|
||||
</Radio.Button>
|
||||
))}
|
||||
</Radio.Group>
|
||||
</Form.Item>
|
||||
)}
|
||||
<Form.Item label="房屋编码" required className="mb-none">
|
||||
<Row gutter={16}>
|
||||
<Col flex="100%">
|
||||
<Form.Item
|
||||
name="areaCode"
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
message: '请选择房屋所在区域',
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Cascader
|
||||
allowClear={false}
|
||||
displayRender={labels => labels.join(' - ')}
|
||||
fieldNames={{
|
||||
label: 'name',
|
||||
value: 'code',
|
||||
children: 'children',
|
||||
}}
|
||||
options={options.areaTree}
|
||||
expandTrigger="hover"
|
||||
/>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col flex="1">
|
||||
<Row gutter={8}>
|
||||
<Col flex="1">
|
||||
<Form.Item
|
||||
name="projectId"
|
||||
rules={[
|
||||
{ required: true, message: '请选择项目' },
|
||||
]}
|
||||
>
|
||||
<Select placeholder="请选择项目">
|
||||
{options.projects.map(item => (
|
||||
<Select.Option
|
||||
key={item.id}
|
||||
value={item.id}
|
||||
>
|
||||
{item.name}({item.note})
|
||||
</Select.Option>
|
||||
))}
|
||||
</Select>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Auth auth="houseProjectInfo:page">
|
||||
<Col>
|
||||
<Button.Group>
|
||||
<Button
|
||||
onClick={() =>
|
||||
window.openContentWindowByMenuName(
|
||||
'bs_house_project_manage'
|
||||
)
|
||||
}
|
||||
>
|
||||
项目管理
|
||||
</Button>
|
||||
<Tooltip
|
||||
placement="top"
|
||||
title="重新加载项目列表"
|
||||
>
|
||||
<Button
|
||||
icon={<AntIcon type="reload" />}
|
||||
onClick={() =>
|
||||
this.onReloadProjectsOrZones(
|
||||
'projects'
|
||||
)
|
||||
}
|
||||
></Button>
|
||||
</Tooltip>
|
||||
</Button.Group>
|
||||
</Col>
|
||||
</Auth>
|
||||
</Row>
|
||||
</Col>
|
||||
<Col flex="1">
|
||||
<Form.Item
|
||||
name="no"
|
||||
rules={[{ required: true, message: '请输入房屋序号' }]}
|
||||
>
|
||||
<InputNumber
|
||||
formatter={value => value.padStart(3, '0')}
|
||||
max={999}
|
||||
min={1}
|
||||
precision={0}
|
||||
step={1}
|
||||
className="w-100-p"
|
||||
placeholder="请输入房屋序号"
|
||||
/>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
{showIndustry && <Col>-</Col>}
|
||||
</Row>
|
||||
</Form.Item>
|
||||
{houseCode && (
|
||||
<Form.Item>
|
||||
<Tag
|
||||
color="purple"
|
||||
style={{
|
||||
fontSize: '24px',
|
||||
fontWeight: 'bold',
|
||||
letterSpacing: '10px',
|
||||
padding: '12px',
|
||||
}}
|
||||
>
|
||||
{houseCode}
|
||||
</Tag>
|
||||
</Form.Item>
|
||||
)}
|
||||
<Alert
|
||||
message={<b>房屋编码说明</b>}
|
||||
description={
|
||||
<>
|
||||
房屋所在市
|
||||
<AntIcon type="border" />
|
||||
<AntIcon type="border" />
|
||||
<AntIcon type="border" />
|
||||
<AntIcon type="border" />
|
||||
—县(市、区)
|
||||
<AntIcon type="border" />
|
||||
<AntIcon type="border" />
|
||||
—街道(乡、镇)
|
||||
<AntIcon type="border" />
|
||||
<AntIcon type="border" />
|
||||
<AntIcon type="border" />
|
||||
—社区、居(村)委会)
|
||||
<AntIcon type="border" />
|
||||
<AntIcon type="border" />
|
||||
<AntIcon type="border" />
|
||||
—项目
|
||||
<AntIcon type="border" />
|
||||
<AntIcon type="border" />
|
||||
<AntIcon type="border" />
|
||||
—实物幢序号
|
||||
<AntIcon type="border" />
|
||||
<AntIcon type="border" />
|
||||
<AntIcon type="border" />。
|
||||
根据省厅既有建筑物编号规则,房屋所在区域编号按照市、县(市、区)、街道(乡、镇)、社区、居(村)委会)、项目分类,其中市、县(市)区部分按照《中华人民共和国行政区划代码》(GB2260)标准编码,街道(乡、镇)按《县以下行政区划代码编码规则》(GB10114-88)标准编码,社区、居(村)委会部分按照统计局提供编码设定。各地上报各街道社区名称后,上述编号由系统自动生成。
|
||||
<br />
|
||||
各社区下辖项目由各地负责统一编码,住宅项目序号一般一个小区一号,采用3位数,001号起编,范围为001~999。实物幢序号由各地负责统一编码,以幢为单位,采用3位数,001号起编,范围为001~999。
|
||||
</>
|
||||
}
|
||||
/>
|
||||
<br />
|
||||
<Form.Item label="所属片区" required>
|
||||
<Row gutter={8}>
|
||||
<Col flex="1">
|
||||
<Form.Item
|
||||
name="zoneId"
|
||||
rules={[{ required: true, message: '请选择所属片区' }]}
|
||||
noStyle
|
||||
>
|
||||
<Select
|
||||
placeholder="请选择所属片区"
|
||||
className="w-100-p"
|
||||
>
|
||||
{options.zones.map(item => (
|
||||
<Select.Option key={item.id} value={item.id}>
|
||||
{item.name}
|
||||
</Select.Option>
|
||||
))}
|
||||
</Select>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Auth auth="houseZone:page">
|
||||
<Col>
|
||||
<Button.Group>
|
||||
<Button
|
||||
onClick={() =>
|
||||
window.openContentWindowByMenuName(
|
||||
'bs_house_zone_manage'
|
||||
)
|
||||
}
|
||||
>
|
||||
片区管理
|
||||
</Button>
|
||||
<Tooltip placement="top" title="重新加载片区列表">
|
||||
<Button
|
||||
icon={<AntIcon type="reload" />}
|
||||
onClick={() =>
|
||||
this.onReloadProjectsOrZones('zones')
|
||||
}
|
||||
></Button>
|
||||
</Tooltip>
|
||||
</Button.Group>
|
||||
</Col>
|
||||
</Auth>
|
||||
</Row>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label="房屋地址"
|
||||
name="address"
|
||||
rules={[{ required: true, message: '请输入房屋地址' }]}
|
||||
>
|
||||
<Input
|
||||
autoComplete="off"
|
||||
placeholder="请输入房屋地址或在地图上选择地点"
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form.Item label="地理坐标" required>
|
||||
<Row gutter={16}>
|
||||
<Col span={12}>
|
||||
<Form.Item
|
||||
name="lng"
|
||||
className="mb-none"
|
||||
rules={[
|
||||
{ required: true, message: '请在地图中选择坐标' },
|
||||
]}
|
||||
>
|
||||
<Input
|
||||
disabled
|
||||
placeholder="请在地图中选择坐标"
|
||||
prefix="经度"
|
||||
/>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={12}>
|
||||
<Form.Item
|
||||
name="lat"
|
||||
className="mb-none"
|
||||
rules={[
|
||||
{ required: true, message: '请在地图中选择坐标' },
|
||||
]}
|
||||
>
|
||||
<Input
|
||||
disabled
|
||||
placeholder="请在地图中选择坐标"
|
||||
prefix="纬度"
|
||||
/>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
</Row>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
</Row>
|
||||
</Spin>
|
||||
</Form>
|
||||
)
|
||||
}
|
||||
}
|
||||
369
framework/web-react/src/pages/business/house/code/index.jsx
Normal file
369
framework/web-react/src/pages/business/house/code/index.jsx
Normal file
@@ -0,0 +1,369 @@
|
||||
import React, { Component } from 'react'
|
||||
import {
|
||||
Button,
|
||||
Card,
|
||||
Cascader,
|
||||
Form,
|
||||
Input,
|
||||
Tag,
|
||||
Popconfirm,
|
||||
message as Message,
|
||||
Radio,
|
||||
Select,
|
||||
Drawer,
|
||||
} from 'antd'
|
||||
import { isEqual } from 'lodash'
|
||||
import { AntIcon, Auth, Container, HouseLog, QueryTable, QueryTableActions } from 'components'
|
||||
import { api } from 'common/api'
|
||||
import getDictData from 'util/dic'
|
||||
import auth from 'components/authorized/handler'
|
||||
import { toCamelCase } from 'util/format'
|
||||
import { getSearchInfo } from 'util/query'
|
||||
|
||||
// 配置页面所需接口函数
|
||||
const apiAction = {
|
||||
page: api.houseCodePage,
|
||||
}
|
||||
|
||||
// 用于弹窗标题
|
||||
const name = '房屋编码'
|
||||
|
||||
export default class index extends Component {
|
||||
state = {
|
||||
codes: {
|
||||
houseType: [],
|
||||
houseIndustry: [],
|
||||
},
|
||||
|
||||
options: {
|
||||
areaTree: [],
|
||||
},
|
||||
|
||||
type: '',
|
||||
|
||||
visibleLog: false,
|
||||
}
|
||||
|
||||
// 表格实例
|
||||
table = React.createRef()
|
||||
|
||||
// 表格字段
|
||||
columns = [
|
||||
{
|
||||
title: '房屋编码',
|
||||
dataIndex: 'houseCode',
|
||||
sorter: true,
|
||||
width: 400,
|
||||
render: (text, record) => (
|
||||
<>
|
||||
{`${record.areaName}-${record.roadName}-${record.commName}-${
|
||||
record.fullProjName
|
||||
}-${record.no.toString().padStart(3, '0')}`}
|
||||
<br />
|
||||
<Tag color="purple">{text}</Tag>
|
||||
</>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: '房屋性质及行业',
|
||||
dataIndex: 'type',
|
||||
sorter: true,
|
||||
width: 150,
|
||||
render: (text, record) =>
|
||||
this.bindCodeValue(text, 'house_type') +
|
||||
(text === 2 ? `(${this.bindCodeValue(record.industry, 'house_industry')})` : ''),
|
||||
},
|
||||
{
|
||||
title: '地址',
|
||||
dataIndex: 'address',
|
||||
sorter: true,
|
||||
},
|
||||
{
|
||||
title: '登记时间',
|
||||
width: 180,
|
||||
dataIndex: 'createdTime',
|
||||
sorter: true,
|
||||
},
|
||||
]
|
||||
|
||||
/**
|
||||
* 构造函数,在渲染前动态添加操作字段等
|
||||
* @param {*} props
|
||||
*/
|
||||
constructor(props) {
|
||||
super(props)
|
||||
|
||||
const flag = auth({ houseCode: [['edit'], ['delete']] })
|
||||
|
||||
if (flag) {
|
||||
this.columns.push({
|
||||
title: '操作',
|
||||
width: 180,
|
||||
dataIndex: 'actions',
|
||||
render: (text, record) => (
|
||||
<QueryTableActions>
|
||||
<Auth auth="houseCode:edit">
|
||||
<a onClick={() => this.onOpen(record)}>编辑</a>
|
||||
</Auth>
|
||||
<Auth auth="houseCode:delete">
|
||||
<Popconfirm
|
||||
placement="topRight"
|
||||
title="是否确认删除"
|
||||
onConfirm={() => this.onDelete(record)}
|
||||
>
|
||||
<a>删除</a>
|
||||
</Popconfirm>
|
||||
</Auth>
|
||||
<a onClick={() => this.onShowLog(record.id)}>日志</a>
|
||||
</QueryTableActions>
|
||||
),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 阻止外部组件引发的渲染,提升性能
|
||||
* 可自行添加渲染条件
|
||||
* [必要]
|
||||
* @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(async res => {
|
||||
const { data } = await api.getAreaTree()
|
||||
this.setState(
|
||||
{
|
||||
codes: res,
|
||||
options: {
|
||||
areaTree: data,
|
||||
},
|
||||
},
|
||||
() => {
|
||||
onLoadData()
|
||||
}
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 调用加载数据接口,可在调用前对query进行处理
|
||||
* [异步,必要]
|
||||
* @param {*} params
|
||||
* @param {*} query
|
||||
* @returns
|
||||
*/
|
||||
loadData = async (params, query) => {
|
||||
if (query.areaCode) {
|
||||
query.areaCode = query.areaCode.pop()
|
||||
}
|
||||
|
||||
const searchInfo = getSearchInfo({
|
||||
query,
|
||||
queryType: {
|
||||
areaCode: '=',
|
||||
houseCode: 'like',
|
||||
type: '=',
|
||||
address: 'like',
|
||||
},
|
||||
})
|
||||
|
||||
const { data } = await apiAction.page({
|
||||
...params,
|
||||
searchInfo,
|
||||
})
|
||||
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(record) {
|
||||
const path = 'business/house/code/form'
|
||||
window.openContentWindow({
|
||||
key: record ? record.id : path,
|
||||
title: record ? '修改房屋编码' : '新增房屋编码',
|
||||
subTitle:
|
||||
record &&
|
||||
`${record.areaName}-${record.roadName}-${record.commName}-${record.note}-${record.no
|
||||
.toString()
|
||||
.padStart(3, '0')}`,
|
||||
path,
|
||||
param: {
|
||||
id: record && record.id,
|
||||
},
|
||||
})
|
||||
// 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 自定义方法
|
||||
onShowLog(id) {
|
||||
this.setState({ visibleLog: id })
|
||||
}
|
||||
//#endregion
|
||||
|
||||
render() {
|
||||
const { options, codes, type, visibleLog } = this.state
|
||||
|
||||
return (
|
||||
<Container mode="fluid">
|
||||
<br />
|
||||
<Card bordered={false}>
|
||||
<QueryTable
|
||||
ref={this.table}
|
||||
autoLoad={false}
|
||||
loadData={this.loadData}
|
||||
columns={this.columns}
|
||||
queryInitialValues={{
|
||||
type: '',
|
||||
}}
|
||||
onQueryChange={values => {
|
||||
if (values.hasOwnProperty('type')) {
|
||||
this.setState({ type: values.type })
|
||||
}
|
||||
}}
|
||||
query={
|
||||
<Auth auth="houseCode:page">
|
||||
<Form.Item name="areaCode">
|
||||
<Cascader
|
||||
displayRender={labels => labels.join(' - ')}
|
||||
fieldNames={{
|
||||
label: 'name',
|
||||
value: 'code',
|
||||
children: 'children',
|
||||
}}
|
||||
options={options.areaTree}
|
||||
className="w-400"
|
||||
expandTrigger="hover"
|
||||
placeholder="请选择所在区域"
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form.Item label="房屋唯一编码" name="houseCode">
|
||||
<Input autoComplete="off" placeholder="请输入房屋唯一编码" />
|
||||
</Form.Item>
|
||||
{/* <Form.Item label="编号" name="no">
|
||||
<InputNumber
|
||||
formatter={value => value && value.padStart(3, '0')}
|
||||
max={999}
|
||||
min={1}
|
||||
precision={0}
|
||||
step={1}
|
||||
placeholder="请输入房屋序号"
|
||||
/>
|
||||
</Form.Item> */}
|
||||
<Form.Item label="房屋性质" name="type">
|
||||
<Radio.Group buttonStyle="solid">
|
||||
<Radio.Button value="">全部</Radio.Button>
|
||||
{codes.houseType.map(item => (
|
||||
<Radio.Button key={item.code} value={item.code}>
|
||||
{item.value}
|
||||
</Radio.Button>
|
||||
))}
|
||||
</Radio.Group>
|
||||
</Form.Item>
|
||||
{type == 2 && (
|
||||
<Form.Item label="行业" name="industry">
|
||||
<Select
|
||||
allowClear
|
||||
className="w-150"
|
||||
placeholder="请选择行业"
|
||||
>
|
||||
{codes.houseIndustry.map(item => (
|
||||
<Select.Option key={item.code} value={item.code}>
|
||||
{item.value}
|
||||
</Select.Option>
|
||||
))}
|
||||
</Select>
|
||||
</Form.Item>
|
||||
)}
|
||||
<Form.Item label="地址" name="address">
|
||||
<Input autoComplete="off" placeholder="请输入地址" />
|
||||
</Form.Item>
|
||||
</Auth>
|
||||
}
|
||||
operator={
|
||||
<Auth auth="houseCode:add">
|
||||
<Button
|
||||
icon={<AntIcon type="plus" />}
|
||||
onClick={() => this.onOpen()}
|
||||
>
|
||||
新增{name}
|
||||
</Button>
|
||||
</Auth>
|
||||
}
|
||||
/>
|
||||
</Card>
|
||||
<Drawer
|
||||
width={600}
|
||||
visible={visibleLog}
|
||||
onClose={() => this.setState({ visibleLog: false })}
|
||||
destroyOnClose
|
||||
>
|
||||
<HouseLog id={visibleLog} />
|
||||
</Drawer>
|
||||
</Container>
|
||||
)
|
||||
}
|
||||
}
|
||||
180
framework/web-react/src/pages/business/house/company/form.jsx
Normal file
180
framework/web-react/src/pages/business/house/company/form.jsx
Normal file
@@ -0,0 +1,180 @@
|
||||
import React, { Component } from 'react'
|
||||
import { Button, Col, Form, Input, Row, Select, Spin } from 'antd'
|
||||
import { AntIcon } from 'components'
|
||||
import { api } from 'common/api'
|
||||
import getDictData from 'util/dic'
|
||||
|
||||
const initialValues = {}
|
||||
|
||||
export default class form extends Component {
|
||||
state = {
|
||||
// 加载状态
|
||||
loading: true,
|
||||
|
||||
codes: {
|
||||
houseCompanyType: [],
|
||||
},
|
||||
}
|
||||
|
||||
// 表单实例
|
||||
form = React.createRef()
|
||||
|
||||
// 初始化数据
|
||||
record = {}
|
||||
|
||||
/**
|
||||
* mount后回调
|
||||
*/
|
||||
componentDidMount() {
|
||||
this.props.created && this.props.created(this)
|
||||
}
|
||||
|
||||
/**
|
||||
* 填充数据
|
||||
* 可以在设置this.record之后对其作出数据结构调整
|
||||
* [异步,必要]
|
||||
* @param {*} params
|
||||
*/
|
||||
async fillData(params) {
|
||||
const state = { loading: false }
|
||||
//#region 从后端转换成前段所需格式,也可以在此处调用获取详细数据接口
|
||||
state.codes = await getDictData('house_company_type')
|
||||
if (params.id) {
|
||||
this.record = (await api.houseCompanyDetail({ id: params.id })).data
|
||||
const { type, info } = this.record
|
||||
if (type) {
|
||||
this.record.type = type.split(',')
|
||||
}
|
||||
}
|
||||
//#endregion
|
||||
this.form.current.setFieldsValue(this.record)
|
||||
|
||||
this.setState(state)
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取数据
|
||||
* 可以对postData进行数据结构调整
|
||||
* [异步,必要]
|
||||
* @returns
|
||||
*/
|
||||
async getData() {
|
||||
const form = this.form.current
|
||||
|
||||
const valid = await form.validateFields()
|
||||
if (valid) {
|
||||
const postData = form.getFieldsValue()
|
||||
if (this.record) {
|
||||
postData.id = this.record.id
|
||||
}
|
||||
//#region 从前段转换后端所需格式
|
||||
const { type, info } = postData
|
||||
if (Array.isArray(type)) {
|
||||
postData.type = type.join(',')
|
||||
}
|
||||
//#endregion
|
||||
return postData
|
||||
}
|
||||
}
|
||||
|
||||
//#region 自定义方法
|
||||
//#endregion
|
||||
|
||||
render() {
|
||||
const { codes } = this.state
|
||||
|
||||
return (
|
||||
<Form initialValues={initialValues} ref={this.form} className="yo-form">
|
||||
<Spin spinning={this.state.loading} indicator={<AntIcon type="loading" />}>
|
||||
<div className="yo-form-group">
|
||||
<Form.Item
|
||||
label="名称"
|
||||
name="name"
|
||||
rules={[{ required: true, message: '请输入名称' }]}
|
||||
>
|
||||
<Input autoComplete="off" placeholder="请输入名称" />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label="类型"
|
||||
name="type"
|
||||
rules={[{ required: true, message: '请选择类型' }]}
|
||||
>
|
||||
<Select mode="multiple" placeholder="请选择类型">
|
||||
{codes.houseCompanyType.map(item => (
|
||||
<Select.Option key={item.code} value={item.code}>
|
||||
{item.value}
|
||||
</Select.Option>
|
||||
))}
|
||||
</Select>
|
||||
</Form.Item>
|
||||
<div className="yo-form--fluid">
|
||||
<Form.List name="info">
|
||||
{(fields, { add, remove }) => (
|
||||
<>
|
||||
{fields.map(({ key, name, fieldKey, ...restField }) => (
|
||||
<Row key={key} align="middle">
|
||||
<Col flex="30px" className="text-right">
|
||||
<AntIcon
|
||||
type="minus-circle"
|
||||
onClick={() => remove(name)}
|
||||
/>
|
||||
</Col>
|
||||
<Col flex="187px">
|
||||
<Form.Item
|
||||
{...restField}
|
||||
name={[name, 'name']}
|
||||
fieldKey={[fieldKey, 'name']}
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
message: '请输入信息名称',
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Input
|
||||
autoComplete="off"
|
||||
placeholder="请输入信息名称"
|
||||
style={{ minWidth: 0 }}
|
||||
/>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col flex="1">
|
||||
<Form.Item
|
||||
{...restField}
|
||||
name={[name, 'value']}
|
||||
fieldKey={[fieldKey, 'value']}
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
message: '请输入信息内容',
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Input
|
||||
autoComplete="off"
|
||||
placeholder="请输入信息内容"
|
||||
/>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
</Row>
|
||||
))}
|
||||
<Form.Item>
|
||||
<Button
|
||||
type="dashed"
|
||||
onClick={() => add()}
|
||||
block
|
||||
icon={<AntIcon type="plus" />}
|
||||
>
|
||||
添加单位信息
|
||||
</Button>
|
||||
</Form.Item>
|
||||
</>
|
||||
)}
|
||||
</Form.List>
|
||||
</div>
|
||||
</div>
|
||||
</Spin>
|
||||
</Form>
|
||||
)
|
||||
}
|
||||
}
|
||||
295
framework/web-react/src/pages/business/house/company/index.jsx
Normal file
295
framework/web-react/src/pages/business/house/company/index.jsx
Normal file
@@ -0,0 +1,295 @@
|
||||
import React, { Component } from 'react'
|
||||
import {
|
||||
Button,
|
||||
Card,
|
||||
Descriptions,
|
||||
Form,
|
||||
Input,
|
||||
message as Message,
|
||||
Popconfirm,
|
||||
Select,
|
||||
} 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'
|
||||
import FormBody from './form'
|
||||
|
||||
/**
|
||||
* 注释段[\/**\/]为必须要改
|
||||
*/
|
||||
|
||||
/**
|
||||
* 配置页面所需接口函数
|
||||
*/
|
||||
const apiAction = {
|
||||
page: api.houseCompanyPage,
|
||||
add: api.houseCompanyAdd,
|
||||
edit: api.houseCompanyEdit,
|
||||
delete: api.houseCompanyDelete,
|
||||
}
|
||||
|
||||
/**
|
||||
* 用于弹窗标题
|
||||
* [必要]
|
||||
*/
|
||||
const name = '单位'
|
||||
|
||||
/**
|
||||
* 统一配置权限标识
|
||||
* [必要]
|
||||
*/
|
||||
const authName = 'houseCompany'
|
||||
|
||||
export default class index extends Component {
|
||||
state = {
|
||||
codes: {
|
||||
houseCompanyType: [],
|
||||
},
|
||||
}
|
||||
|
||||
// 表格实例
|
||||
table = React.createRef()
|
||||
|
||||
// 新增窗口实例
|
||||
addForm = React.createRef()
|
||||
// 编辑窗口实例
|
||||
editForm = React.createRef()
|
||||
|
||||
columns = [
|
||||
{
|
||||
title: '名称',
|
||||
width: 300,
|
||||
dataIndex: 'name',
|
||||
},
|
||||
{
|
||||
title: '类型',
|
||||
width: 300,
|
||||
dataIndex: 'type',
|
||||
render: text =>
|
||||
text
|
||||
.split(',')
|
||||
.map(p => this.bindCodeValue(p, 'house_company_type'))
|
||||
.join(' / '),
|
||||
},
|
||||
]
|
||||
|
||||
/**
|
||||
* 构造函数,在渲染前动态添加操作字段等
|
||||
* @param {*} props
|
||||
*/
|
||||
constructor(props) {
|
||||
super(props)
|
||||
|
||||
const flag = auth({ [authName]: [['edit'], ['delete']] })
|
||||
|
||||
if (flag) {
|
||||
this.columns.push({
|
||||
title: '操作',
|
||||
width: 150,
|
||||
dataIndex: 'actions',
|
||||
render: (text, { id }) => (
|
||||
<QueryTableActions>
|
||||
<Auth auth={{ [authName]: 'edit' }}>
|
||||
<a onClick={() => this.onOpen(this.editForm, id)}>编辑</a>
|
||||
</Auth>
|
||||
<Auth auth={{ [authName]: 'delete' }}>
|
||||
<Popconfirm
|
||||
placement="topRight"
|
||||
title="是否确认删除"
|
||||
onConfirm={() => this.onDelete(id)}
|
||||
>
|
||||
<a>删除</a>
|
||||
</Popconfirm>
|
||||
</Auth>
|
||||
</QueryTableActions>
|
||||
),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 阻止外部组件引发的渲染,提升性能
|
||||
* 可自行添加渲染条件
|
||||
* [必要]
|
||||
* @param {*} props
|
||||
* @param {*} state
|
||||
* @returns
|
||||
*/
|
||||
shouldComponentUpdate(props, state) {
|
||||
return !isEqual(this.state, state)
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载字典数据,之后开始加载表格数据
|
||||
* 如果必须要加载字典数据,可直接对表格设置autoLoad=true
|
||||
*/
|
||||
componentDidMount() {
|
||||
const { onLoading, onLoadData } = this.table.current
|
||||
onLoading()
|
||||
api.houseCompanyList({ type: '1' })
|
||||
getDictData('house_company_type').then(codes => {
|
||||
this.setState({ codes }, () => {
|
||||
onLoadData()
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 调用加载数据接口,可在调用前对query进行处理
|
||||
* [异步,必要]
|
||||
* @param {*} params
|
||||
* @param {*} query
|
||||
* @returns
|
||||
*/
|
||||
loadData = async (params, query) => {
|
||||
const { data } = await apiAction.page({
|
||||
...params,
|
||||
...query,
|
||||
})
|
||||
return data
|
||||
}
|
||||
|
||||
/**
|
||||
* 绑定字典数据
|
||||
* @param {*} code
|
||||
* @param {*} name
|
||||
* @returns
|
||||
*/
|
||||
bindCodeValue(code, name) {
|
||||
name = toCamelCase(name)
|
||||
const codes = this.state.codes[name]
|
||||
if (codes) {
|
||||
const c = codes.find(p => p.code == code)
|
||||
if (c) {
|
||||
return c.value
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
/**
|
||||
* 打开新增/编辑弹窗
|
||||
* @param {*} modal
|
||||
* @param {*} id
|
||||
*/
|
||||
onOpen(modal, id) {
|
||||
modal.current.open({ id })
|
||||
}
|
||||
|
||||
/**
|
||||
* 对表格上的操作进行统一处理
|
||||
* [异步]
|
||||
* @param {*} action
|
||||
* @param {*} successMessage
|
||||
*/
|
||||
async onAction(action, successMessage) {
|
||||
const { onLoading, onLoaded, onReloadData } = this.table.current
|
||||
onLoading()
|
||||
try {
|
||||
if (action) {
|
||||
await action
|
||||
}
|
||||
if (successMessage) {
|
||||
Message.success(successMessage)
|
||||
}
|
||||
onReloadData()
|
||||
} catch {
|
||||
onLoaded()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除
|
||||
* @param {*} id
|
||||
*/
|
||||
onDelete(id) {
|
||||
this.onAction(apiAction.delete({ id }), '删除成功')
|
||||
}
|
||||
|
||||
//#region 自定义方法
|
||||
//#endregion
|
||||
|
||||
render() {
|
||||
const { codes } = this.state
|
||||
|
||||
return (
|
||||
<Container mode="fluid">
|
||||
<br />
|
||||
<Card bordered={false}>
|
||||
<QueryTable
|
||||
ref={this.table}
|
||||
autoLoad={false}
|
||||
loadData={this.loadData}
|
||||
columns={this.columns}
|
||||
query={
|
||||
<Auth auth={{ [authName]: 'page' }}>
|
||||
<Form.Item label="名称" name="name">
|
||||
<Input
|
||||
autoComplete="off"
|
||||
placeholder="请输入名称"
|
||||
className="w-200"
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form.Item label="类型" name="type">
|
||||
<Select placeholder="请选择类型" className="w-200" allowClear>
|
||||
{codes.houseCompanyType.map(item => (
|
||||
<Select.Option key={item.code} value={item.code}>
|
||||
{item.value}
|
||||
</Select.Option>
|
||||
))}
|
||||
</Select>
|
||||
</Form.Item>
|
||||
</Auth>
|
||||
}
|
||||
operator={
|
||||
<Auth auth={{ [authName]: 'add' }}>
|
||||
<Button
|
||||
icon={<AntIcon type="plus" />}
|
||||
onClick={() => this.onOpen(this.addForm)}
|
||||
>
|
||||
新增{name}
|
||||
</Button>
|
||||
</Auth>
|
||||
}
|
||||
expandedRowRender={record => (
|
||||
<Descriptions bordered labelStyle={{ width: 200 }}>
|
||||
{record.info.map((item, i) => (
|
||||
<Descriptions.Item key={i} label={item.name}>
|
||||
{item.value}
|
||||
</Descriptions.Item>
|
||||
))}
|
||||
</Descriptions>
|
||||
)}
|
||||
/>
|
||||
</Card>
|
||||
|
||||
<Auth auth={{ [authName]: 'add' }}>
|
||||
<ModalForm
|
||||
width={600}
|
||||
title={`新增${name}`}
|
||||
action={apiAction.add}
|
||||
ref={this.addForm}
|
||||
onSuccess={() => this.table.current.onReloadData()}
|
||||
>
|
||||
<FormBody />
|
||||
</ModalForm>
|
||||
</Auth>
|
||||
|
||||
<Auth auth={{ [authName]: 'edit' }}>
|
||||
<ModalForm
|
||||
width={600}
|
||||
title={`编辑${name}`}
|
||||
action={apiAction.edit}
|
||||
ref={this.editForm}
|
||||
onSuccess={() => this.table.current.onReloadData()}
|
||||
>
|
||||
<FormBody />
|
||||
</ModalForm>
|
||||
</Auth>
|
||||
</Container>
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -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 (
|
||||
<Spin spinning={loading} indicator={<AntIcon type="loading" />}>
|
||||
<Form
|
||||
initialValues={initialValues}
|
||||
ref={this.form}
|
||||
{...layout}
|
||||
onValuesChange={(changedValues, allValues) =>
|
||||
this.onValuesChange(changedValues, allValues)
|
||||
}
|
||||
>
|
||||
<Form.Item
|
||||
label="外立面照片"
|
||||
name={['houseInfo', 'facadePhoto']}
|
||||
valuePropName="fileList"
|
||||
getValueFromEvent={e => {
|
||||
if (Array.isArray(e)) {
|
||||
return e
|
||||
}
|
||||
return e && e.fileList
|
||||
}}
|
||||
>
|
||||
<Upload
|
||||
listType="picture-card"
|
||||
customRequest={e => this.onFileUpload(e)}
|
||||
showUploadList={{
|
||||
showRemoveIcon: true,
|
||||
showDownloadIcon: true,
|
||||
}}
|
||||
onPreview={file => this.onFilePreview(file, 'facadePhoto')}
|
||||
onDownload={file => this.onFileDownload(file)}
|
||||
>
|
||||
<div>
|
||||
<AntIcon type="plus" />
|
||||
<div className="ant-upload-text">外立面照片</div>
|
||||
</div>
|
||||
</Upload>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
|
||||
<PhotoPreview ref={this.photoPreview} />
|
||||
</Spin>
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -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 (
|
||||
<Spin spinning={loading} indicator={<AntIcon type="loading" />}>
|
||||
<Form
|
||||
initialValues={initialValues}
|
||||
ref={this.form}
|
||||
{...layout}
|
||||
onValuesChange={(changedValues, allValues) =>
|
||||
this.onValuesChange(changedValues, allValues)
|
||||
}
|
||||
>
|
||||
{uploads.map((item, i) => (
|
||||
<Form.Item
|
||||
key={i}
|
||||
label={item.label}
|
||||
name={['houseInfo', item.key]}
|
||||
valuePropName="fileList"
|
||||
getValueFromEvent={e => {
|
||||
if (Array.isArray(e)) {
|
||||
return e
|
||||
}
|
||||
return e && e.fileList
|
||||
}}
|
||||
>
|
||||
<Upload
|
||||
customRequest={e => this.onFileUpload(e)}
|
||||
showUploadList={{
|
||||
showRemoveIcon: true,
|
||||
showDownloadIcon: true,
|
||||
}}
|
||||
onPreview={() => false}
|
||||
onDownload={file => this.onFileDownload(file)}
|
||||
>
|
||||
<Button type="dashed" icon={<AntIcon type="upload" />}>
|
||||
上传{item.label}
|
||||
</Button>
|
||||
</Upload>
|
||||
</Form.Item>
|
||||
))}
|
||||
</Form>
|
||||
</Spin>
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,955 @@
|
||||
import React, { Component } from 'react'
|
||||
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 initialValues = {
|
||||
houseInfo: {
|
||||
houseUsedStatus: 1,
|
||||
landAttribute: 1,
|
||||
curtainWall: 0,
|
||||
faceBrick: 0,
|
||||
coating: 0,
|
||||
painting: 0,
|
||||
elevator: '0',
|
||||
},
|
||||
}
|
||||
|
||||
const { getState, dispatch } = store
|
||||
|
||||
const layout = {
|
||||
labelCol: { flex: '150px' },
|
||||
wrapperCol: { flex: '1' },
|
||||
}
|
||||
|
||||
const checkboxKeys = ['insulationMaterial', 'wallMaterial']
|
||||
|
||||
export default class building extends Component {
|
||||
state = {
|
||||
loading: true,
|
||||
codes: {
|
||||
houseUsedStatus: [],
|
||||
landAttribute: [],
|
||||
houseStructureType: [],
|
||||
houseAseismicGrade: [],
|
||||
houseBaseInfo: [],
|
||||
houseInsulationMaterial: [],
|
||||
houseWallMaterial: [],
|
||||
houseFireproofGrade: [],
|
||||
houseBuildingCurtainWall: [],
|
||||
houseElevator: [],
|
||||
},
|
||||
|
||||
showMap: false,
|
||||
showKeepWarmMaterialText: false,
|
||||
|
||||
theme: getState('layout').theme,
|
||||
}
|
||||
|
||||
form = React.createRef()
|
||||
|
||||
shouldComponentUpdate(props, state) {
|
||||
return !isEqual(this.state, state)
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.fillData({
|
||||
record: this.props.record,
|
||||
})
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
dispatch({
|
||||
type: 'PATROL_REMOVE_INIT_GRADE_BY_COMPLETED_DATE',
|
||||
id: this.props.id,
|
||||
})
|
||||
}
|
||||
|
||||
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'),
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// 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(
|
||||
'house_used_status',
|
||||
'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) {
|
||||
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,
|
||||
mapStyle: `amap://styles/${this.state.theme}`,
|
||||
})
|
||||
|
||||
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 (
|
||||
<Spin spinning={loading} indicator={<AntIcon type="loading" />}>
|
||||
<Form
|
||||
initialValues={initialValues}
|
||||
{...layout}
|
||||
ref={this.form}
|
||||
onValuesChange={(changedValues, allValues) =>
|
||||
this.onValuesChange(changedValues, allValues)
|
||||
}
|
||||
>
|
||||
<Row gutter={16}>
|
||||
<Col span={12}>
|
||||
<Form.Item
|
||||
name={['houseInfo', 'buildingName']}
|
||||
label="幢名称"
|
||||
rules={[{ required: true, message: '请输入幢名称' }]}
|
||||
>
|
||||
<Input autoComplete="off" placeholder="请输入幢名称" />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={12}>
|
||||
<Form.Item
|
||||
label="坐落地址"
|
||||
name={['houseCode', 'address']}
|
||||
rules={[{ required: true, message: '请输入坐落地址' }]}
|
||||
>
|
||||
<Input autoComplete="off" placeholder="请输入坐落地址" />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={12}>
|
||||
<Form.Item
|
||||
label="房屋使用状态"
|
||||
name={['houseInfo', 'houseUsedStatus']}
|
||||
rules={[{ required: true, message: '请选择房屋使用状态' }]}
|
||||
>
|
||||
<Radio.Group buttonStyle="solid">
|
||||
{codes.houseUsedStatus.map(item => (
|
||||
<Radio.Button key={item.code} value={+item.code}>
|
||||
{item.value}
|
||||
</Radio.Button>
|
||||
))}
|
||||
</Radio.Group>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={12}>
|
||||
<Form.Item
|
||||
label="土地性质"
|
||||
name={['houseInfo', 'landAttribute']}
|
||||
rules={[{ required: true, message: '请选择土地性质' }]}
|
||||
>
|
||||
<Radio.Group buttonStyle="solid">
|
||||
{codes.landAttribute.map(item => {
|
||||
return (
|
||||
<Radio.Button key={item.code} value={+item.code}>
|
||||
{item.value}
|
||||
</Radio.Button>
|
||||
)
|
||||
})}
|
||||
</Radio.Group>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
</Row>
|
||||
<Form.Item className="mb-none" label="地理坐标" required>
|
||||
<Row gutter={16}>
|
||||
<Col span={12}>
|
||||
<Form.Item
|
||||
name={['houseCode', 'lng']}
|
||||
rules={[{ required: true, message: '请在地图上选择坐标' }]}
|
||||
>
|
||||
<Input
|
||||
autoComplete="off"
|
||||
className="yo-input-prefix-2"
|
||||
disabled
|
||||
placeholder="请在地图上选择坐标"
|
||||
prefix="经度"
|
||||
/>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={12}>
|
||||
<Form.Item
|
||||
name={['houseCode', 'lat']}
|
||||
rules={[{ required: true, message: '请在地图上选择坐标' }]}
|
||||
>
|
||||
<Input
|
||||
autoComplete="off"
|
||||
className="yo-input-prefix-2"
|
||||
disabled
|
||||
placeholder="请在地图上选择坐标"
|
||||
prefix="纬度"
|
||||
/>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
</Row>
|
||||
</Form.Item>
|
||||
<Form.Item colon={false} label={true}>
|
||||
{showMap ? (
|
||||
<div className="yo-map-container">
|
||||
<div className="yo-map--search">
|
||||
<Input.Search
|
||||
autoComplete="off"
|
||||
allowClear
|
||||
placeholder="请输入关键字"
|
||||
ref="map-search"
|
||||
/>
|
||||
</div>
|
||||
<div className="h-500" ref="map"></div>
|
||||
</div>
|
||||
) : (
|
||||
<Button
|
||||
shape="round"
|
||||
size="large"
|
||||
icon={<AntIcon type="picture" />}
|
||||
onClick={() => this.onShowMap()}
|
||||
>
|
||||
查看地图
|
||||
</Button>
|
||||
)}
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label="结构类型"
|
||||
name={['houseInfo', 'structureType']}
|
||||
rules={[{ required: true, message: '请选择结构类型' }]}
|
||||
>
|
||||
<Radio.Group buttonStyle="solid" placeholder="请选择结构类型">
|
||||
{codes.houseStructureType.map(item => {
|
||||
return (
|
||||
<Radio.Button key={item.code} value={+item.code}>
|
||||
{item.value}
|
||||
</Radio.Button>
|
||||
)
|
||||
})}
|
||||
</Radio.Group>
|
||||
</Form.Item>
|
||||
<Row gutter={16}>
|
||||
<Col span={12}>
|
||||
<Form.Item
|
||||
label="抗震等级"
|
||||
name={['houseInfo', 'seismicGrade']}
|
||||
rules={[{ required: true, message: '请选择抗震等级' }]}
|
||||
>
|
||||
<Radio.Group buttonStyle="solid">
|
||||
{codes.houseAseismicGrade.map(item => {
|
||||
return (
|
||||
<Radio.Button key={item.code} value={+item.code}>
|
||||
{item.value}
|
||||
</Radio.Button>
|
||||
)
|
||||
})}
|
||||
</Radio.Group>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={12}>
|
||||
<Form.Item
|
||||
label="基础情况"
|
||||
name={['houseInfo', 'baseInfo']}
|
||||
rules={[{ required: true, message: '请选择基础情况' }]}
|
||||
>
|
||||
<Radio.Group buttonStyle="solid">
|
||||
{codes.houseBaseInfo.map(item => {
|
||||
return (
|
||||
<Radio.Button key={item.code} value={+item.code}>
|
||||
{item.value}
|
||||
</Radio.Button>
|
||||
)
|
||||
})}
|
||||
</Radio.Group>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
<Form.Item label="外墙保温材料" name={['houseInfo', 'insulationMaterial']}>
|
||||
<Checkbox.Group>
|
||||
{codes.houseInsulationMaterial.map(item => {
|
||||
return (
|
||||
<Checkbox key={item.code} value={item.code}>
|
||||
{item.value}
|
||||
</Checkbox>
|
||||
)
|
||||
})}
|
||||
</Checkbox.Group>
|
||||
</Form.Item>
|
||||
{showKeepWarmMaterialText && (
|
||||
<Form.Item
|
||||
colon={false}
|
||||
label=" "
|
||||
name={['houseInfo', 'keepWarmMaterialText']}
|
||||
>
|
||||
<Input.TextArea autoSize placeholder="请输入其他外墙保温材料" />
|
||||
</Form.Item>
|
||||
)}
|
||||
<Form.Item label="外墙墙体材料" name={['houseInfo', 'wallMaterial']}>
|
||||
<Checkbox.Group>
|
||||
{codes.houseWallMaterial.map(item => {
|
||||
return (
|
||||
<Checkbox key={item.code} value={item.code}>
|
||||
{item.value}
|
||||
</Checkbox>
|
||||
)
|
||||
})}
|
||||
</Checkbox.Group>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={
|
||||
<span>
|
||||
外墙外保温材料
|
||||
<br />
|
||||
防火等级
|
||||
</span>
|
||||
}
|
||||
name={['houseInfo', 'fireproofGrade']}
|
||||
>
|
||||
<Radio.Group buttonStyle="solid">
|
||||
{codes.houseFireproofGrade.map(item => {
|
||||
return (
|
||||
<Radio.Button key={item.code} value={item.code}>
|
||||
{item.value}
|
||||
</Radio.Button>
|
||||
)
|
||||
})}
|
||||
</Radio.Group>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label="建筑幕墙"
|
||||
name={['houseInfo', 'curtainWall']}
|
||||
rules={[{ required: true, message: '请选择建筑幕墙' }]}
|
||||
>
|
||||
<Radio.Group buttonStyle="solid">
|
||||
{codes.houseBuildingCurtainWall.map(item => {
|
||||
return (
|
||||
<Radio.Button key={item.code} value={+item.code}>
|
||||
{item.value}
|
||||
</Radio.Button>
|
||||
)
|
||||
})}
|
||||
</Radio.Group>
|
||||
</Form.Item>
|
||||
<Row gutter={16}>
|
||||
<Col span={8}>
|
||||
<Form.Item
|
||||
label="有无外墙面砖"
|
||||
name={['houseInfo', 'faceBrick']}
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={8}>
|
||||
<Form.Item
|
||||
label="有无外墙粉刷"
|
||||
name={['houseInfo', 'whiteWash']}
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={8}>
|
||||
<Form.Item
|
||||
label="有无外墙涂料"
|
||||
name={['houseInfo', 'coating']}
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={24}>
|
||||
<Form.Item
|
||||
label="电梯"
|
||||
name={['houseInfo', 'elevator']}
|
||||
rules={[{ required: true, message: '请选择电梯' }]}
|
||||
>
|
||||
<Radio.Group buttonStyle="solid">
|
||||
{codes.houseElevator.map(item => {
|
||||
return (
|
||||
<Radio.Button key={item.code} value={item.code}>
|
||||
{item.value}
|
||||
</Radio.Button>
|
||||
)
|
||||
})}
|
||||
</Radio.Group>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={8}>
|
||||
<Form.Item
|
||||
label="竣工日期"
|
||||
name={['houseInfo', 'completedDate']}
|
||||
rules={[{ required: true, message: '请选择竣工日期' }]}
|
||||
>
|
||||
<DatePicker
|
||||
onChange={date => {
|
||||
/*$root.transfer.completedYear = date.format('YYYY') */
|
||||
}}
|
||||
className="w-100-p"
|
||||
placeholder="请选择竣工日期"
|
||||
/>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={8}>
|
||||
<Form.Item label="设计使用年限" required>
|
||||
<Row align="middle">
|
||||
<Col flex="1">
|
||||
<Form.Item
|
||||
name={['houseInfo', 'usefulYear']}
|
||||
rules={[
|
||||
{ required: true, message: '请输入设计使用年限' },
|
||||
]}
|
||||
noStyle
|
||||
>
|
||||
<InputNumber
|
||||
min={0}
|
||||
className="w-100-p"
|
||||
placeholder="请输入设计使用年限"
|
||||
/>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col>
|
||||
<span className="yo-addon">年</span>
|
||||
</Col>
|
||||
</Row>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={8}>
|
||||
<Form.Item label="总建筑面积" required>
|
||||
<Row align="middle">
|
||||
<Col flex="1">
|
||||
<Form.Item
|
||||
name={['houseInfo', 'totalArea']}
|
||||
rules={[
|
||||
{ required: true, message: '请输入总建筑面积' },
|
||||
]}
|
||||
noStyle
|
||||
>
|
||||
<InputNumber
|
||||
min={0}
|
||||
className="w-100-p"
|
||||
placeholder="请输入总建筑面积"
|
||||
/>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col>
|
||||
<span className="yo-addon">m²</span>
|
||||
</Col>
|
||||
</Row>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={24}>
|
||||
<Form.Item label="建筑层数" className="mb-none">
|
||||
<Row>
|
||||
<Col>
|
||||
<Form.Item colon={false} label="地上" required>
|
||||
<Form.Item
|
||||
name={['houseInfo', 'landFloorCount']}
|
||||
rules={[
|
||||
{ required: true, message: '请输入地上层' },
|
||||
]}
|
||||
noStyle
|
||||
>
|
||||
<InputNumber
|
||||
min={0}
|
||||
precision={0}
|
||||
step={1}
|
||||
className="w-150"
|
||||
placeholder="请输入地上层"
|
||||
/>
|
||||
</Form.Item>
|
||||
<div className="ant-form-text">层;</div>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col>
|
||||
<Form.Item colon={false} label="地下" required>
|
||||
<Form.Item
|
||||
name={['houseInfo', 'underFloorCount']}
|
||||
rules={[
|
||||
{ required: true, message: '请输入地下层' },
|
||||
]}
|
||||
noStyle
|
||||
>
|
||||
<InputNumber
|
||||
min={0}
|
||||
precision={0}
|
||||
step={1}
|
||||
className="w-150"
|
||||
placeholder="请输入地下层"
|
||||
/>
|
||||
</Form.Item>
|
||||
<div className="ant-form-text">层;</div>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col>
|
||||
<Form.Item colon={false} label="总共">
|
||||
<Form.Item name={['houseInfo', 'totalFloor']} noStyle>
|
||||
<InputNumber
|
||||
disabled
|
||||
min={0}
|
||||
precision={0}
|
||||
step={1}
|
||||
className="w-150"
|
||||
placeholder="请输入总层数"
|
||||
/>
|
||||
</Form.Item>
|
||||
<div className="ant-form-text">层</div>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
</Row>
|
||||
</Form.Item>
|
||||
<Form.Item label="其中">
|
||||
<Row align="middle">
|
||||
<Col>
|
||||
<span className="yo-addon">地上第</span>
|
||||
</Col>
|
||||
<Col>
|
||||
<Form.Item name={['houseInfo', 'landBsFloorStart']} noStyle>
|
||||
<InputNumber min={0} step={1} placeholder="几" />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col>
|
||||
<span className="yo-addon">层,至</span>
|
||||
</Col>
|
||||
<Col>
|
||||
<Form.Item name={['houseInfo', 'landBsFloorEnd']} noStyle>
|
||||
<InputNumber min={0} step={1} placeholder="几" />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col>
|
||||
<span className="yo-addon">层为商业用房;</span>
|
||||
</Col>
|
||||
<Col>
|
||||
<span className="yo-addon">地上</span>
|
||||
</Col>
|
||||
<Col>
|
||||
<Form.Item
|
||||
name={['houseInfo', 'landBikeFloorStart']}
|
||||
noStyle
|
||||
>
|
||||
<InputNumber min={0} step={1} placeholder="几" />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col>
|
||||
<span className="yo-addon">层为车棚层;</span>
|
||||
</Col>
|
||||
<Col>
|
||||
<span className="yo-addon">地上第</span>
|
||||
</Col>
|
||||
<Col>
|
||||
<Form.Item
|
||||
name={['houseInfo', 'landResidenceFloorStart']}
|
||||
noStyle
|
||||
>
|
||||
<InputNumber min={0} step={1} placeholder="几" />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col>
|
||||
<span className="yo-addon">层,至</span>
|
||||
</Col>
|
||||
<Col>
|
||||
<Form.Item
|
||||
name={['houseInfo', 'landResidenceFloorEnd']}
|
||||
noStyle
|
||||
>
|
||||
<InputNumber min={0} step={1} placeholder="几" />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col>
|
||||
<span className="yo-addon">层为住宅</span>
|
||||
</Col>
|
||||
</Row>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={8}>
|
||||
<Form.Item label="总户数" required>
|
||||
<Row align="middle">
|
||||
<Col flex="1">
|
||||
<Form.Item
|
||||
name={['houseInfo', 'houseHolds']}
|
||||
rules={[{ required: true, message: '请输入总户数' }]}
|
||||
noStyle
|
||||
>
|
||||
<InputNumber
|
||||
min={0}
|
||||
precision={0}
|
||||
step={1}
|
||||
className="w-100-p"
|
||||
placeholder="请输入总户数"
|
||||
/>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col>
|
||||
<span className="yo-addon">户</span>
|
||||
</Col>
|
||||
</Row>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={8}>
|
||||
<Form.Item label="房屋单元数" required>
|
||||
<Row align="middle">
|
||||
<Col flex="1">
|
||||
<Form.Item
|
||||
name={['houseInfo', 'units']}
|
||||
rules={[
|
||||
{ required: true, message: '请输入房屋单元数' },
|
||||
]}
|
||||
noStyle
|
||||
>
|
||||
<InputNumber
|
||||
min={0}
|
||||
precision={0}
|
||||
step={1}
|
||||
className="w-100-p"
|
||||
placeholder="请输入房屋单元数"
|
||||
/>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col>
|
||||
<span className="yo-addon">单元</span>
|
||||
</Col>
|
||||
</Row>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={8}>
|
||||
<Form.Item label="每层每单元户数" required>
|
||||
<Row align="middle">
|
||||
<Col flex="1">
|
||||
<Form.Item
|
||||
name={['houseInfo', 'unitFloorHolds']}
|
||||
rules={[
|
||||
{ required: true, message: '请输入每层每单元户数' },
|
||||
]}
|
||||
noStyle
|
||||
>
|
||||
<InputNumber
|
||||
min={0}
|
||||
precision={0}
|
||||
step={1}
|
||||
className="w-100-p"
|
||||
placeholder="请输入每层每单元户数"
|
||||
/>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col>
|
||||
<span className="yo-addon">户</span>
|
||||
</Col>
|
||||
</Row>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={8}>
|
||||
<Form.Item label="建设单位" name={['houseInfo', 'buildingUnit']}>
|
||||
<Input autoComplete="off" placeholder="请输入建设单位" />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={8}>
|
||||
<Form.Item
|
||||
label="建设单位联系人"
|
||||
name={['houseInfo', 'buildingUnitUser']}
|
||||
>
|
||||
<Input autoComplete="off" placeholder="请输入建设单位联系人" />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={8}>
|
||||
<Form.Item
|
||||
label="建设单位联系电话"
|
||||
name={['houseInfo', 'buildingUnitTel']}
|
||||
>
|
||||
<Input autoComplete="off" placeholder="请输入建设单位联系电话" />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={8}>
|
||||
<Form.Item label="设计单位" name={['houseInfo', 'desingerUnit']}>
|
||||
<Input autoComplete="off" placeholder="请输入设计单位" />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={8}>
|
||||
<Form.Item label="施工单位" name={['houseInfo', 'constructionUnit']}>
|
||||
<Input autoComplete="off" placeholder="请输入施工单位" />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={8}>
|
||||
<Form.Item label="监理单位" name={['houseInfo', 'monitorUnit']}>
|
||||
<Input autoComplete="off" placeholder="请输入监理单位" />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
</Row>
|
||||
</Form>
|
||||
</Spin>
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -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 (
|
||||
<Spin spinning={loading} indicator={<AntIcon type="loading" />}>
|
||||
<Form
|
||||
initialValues={initialValues}
|
||||
ref={this.form}
|
||||
{...layout}
|
||||
onValuesChange={(changedValues, allValues) =>
|
||||
this.onValuesChange(changedValues, allValues)
|
||||
}
|
||||
>
|
||||
<Form.Item
|
||||
label="图纸资料存档处"
|
||||
name={['houseInfo', 'drawingMaterial']}
|
||||
rules={[{ required: true, message: '请选择图纸资料存档处' }]}
|
||||
>
|
||||
<Checkbox.Group>
|
||||
{codes.houseStorageOfDrawings.map(item => (
|
||||
<Checkbox key={item.code} value={item.code}>
|
||||
{item.value}
|
||||
</Checkbox>
|
||||
))}
|
||||
</Checkbox.Group>
|
||||
</Form.Item>
|
||||
{showDrawingMaterialText && (
|
||||
<Form.Item
|
||||
colon={false}
|
||||
label=" "
|
||||
name={['houseInfo', 'drawingMaterialText']}
|
||||
>
|
||||
<Input.TextArea autoSize placeholder="请输入其他图纸资料存档处" />
|
||||
</Form.Item>
|
||||
)}
|
||||
</Form>
|
||||
</Spin>
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -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 (
|
||||
<Spin spinning={loading} indicator={<AntIcon type="loading" />}>
|
||||
<Form
|
||||
initialValues={initialValues}
|
||||
ref={this.form}
|
||||
{...layout}
|
||||
onValuesChange={(changedValues, allValues) =>
|
||||
this.onValuesChange(changedValues, allValues)
|
||||
}
|
||||
>
|
||||
<Form.Item
|
||||
label="房屋使用状态"
|
||||
name={['houseInfo', 'houseUsedStatus']}
|
||||
rules={[{ required: true, message: '请选择房屋使用状态' }]}
|
||||
>
|
||||
<Radio.Group buttonStyle="solid">
|
||||
{codes.houseUsedStatus.map(item => (
|
||||
<Radio.Button key={item.code} value={+item.code}>
|
||||
{item.value}
|
||||
</Radio.Button>
|
||||
))}
|
||||
</Radio.Group>
|
||||
</Form.Item>
|
||||
{/* <Form.Item
|
||||
label="综合等级"
|
||||
name={['houseInfo', 'houseGrade']}
|
||||
rules={[{ required: true, message: '请选择综合等级' }]}
|
||||
>
|
||||
<Radio.Group buttonStyle="solid">
|
||||
{codes.houseGrade.map(item => (
|
||||
<Radio.Button key={item.code} value={+item.code}>
|
||||
{item.value}
|
||||
</Radio.Button>
|
||||
))}
|
||||
</Radio.Group>
|
||||
</Form.Item> */}
|
||||
</Form>
|
||||
</Spin>
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
import React, { Component } from 'react'
|
||||
import ReactDOM from 'react-dom'
|
||||
import { Row, Col, Card, Anchor, Spin, Divider } from 'antd'
|
||||
import { merge } from 'lodash'
|
||||
import { AntIcon, ComponentDynamic, Container } from 'components'
|
||||
|
||||
const parts = [
|
||||
{
|
||||
title: '建筑物基本信息',
|
||||
component: () => import('./building'),
|
||||
},
|
||||
{
|
||||
title: '权属情况',
|
||||
component: () => import('./ownership'),
|
||||
},
|
||||
// {
|
||||
// title: '调查情况',
|
||||
// component: () => import('./investigation'),
|
||||
// },
|
||||
// {
|
||||
// title: '鉴定治理',
|
||||
// component: () => import('./identification'),
|
||||
// },
|
||||
{
|
||||
title: '图纸资料存档处',
|
||||
component: () => import('./drawing'),
|
||||
},
|
||||
{
|
||||
title: '相关附件资料',
|
||||
component: () => import('./attachments'),
|
||||
},
|
||||
{
|
||||
title: '建筑概貌',
|
||||
component: () => import('./aspect'),
|
||||
},
|
||||
{
|
||||
title: '调查单位',
|
||||
component: () => import('./unit'),
|
||||
},
|
||||
]
|
||||
|
||||
export default class index extends Component {
|
||||
container = window
|
||||
|
||||
children = []
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
setContainer = container => {
|
||||
this.container = (ReactDOM.findDOMNode(container) || {}).parentNode
|
||||
}
|
||||
|
||||
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 (
|
||||
<Container mode="fluid" ref={this.setContainer}>
|
||||
<Row gutter={16} type="flex">
|
||||
<Col flex="1">
|
||||
<br />
|
||||
<div className="yo-adorn--house-top" />
|
||||
<Card className="yo-form-page--body">
|
||||
{parts.map((part, i) => (
|
||||
<React.Fragment key={i}>
|
||||
<section id={`form-${i}-${id}`}>
|
||||
{part.title && <h5>{part.title}</h5>}
|
||||
<Spin
|
||||
spinning={loading}
|
||||
indicator={<AntIcon type="loading" />}
|
||||
wrapperClassName={loading && 'h-400-min'}
|
||||
>
|
||||
{!loading && (
|
||||
<ComponentDynamic
|
||||
is={part.component}
|
||||
{...this.props}
|
||||
onRef={child => this.call(child, i)}
|
||||
/>
|
||||
)}
|
||||
</Spin>
|
||||
</section>
|
||||
{i < parts.length - 1 && <Divider />}
|
||||
</React.Fragment>
|
||||
))}
|
||||
</Card>
|
||||
</Col>
|
||||
<Col flex="240px">
|
||||
<Anchor
|
||||
getContainer={() => this.container}
|
||||
offsetTop={24}
|
||||
targetOffset={100}
|
||||
wrapperStyle={{ backgroundColor: 'transparent' }}
|
||||
onClick={e => e.preventDefault()}
|
||||
>
|
||||
{parts.map((part, i) => (
|
||||
<Anchor.Link key={i} href={`#form-${i}-${id}`} title={part.title} />
|
||||
))}
|
||||
</Anchor>
|
||||
</Col>
|
||||
</Row>
|
||||
</Container>
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,312 @@
|
||||
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 = {
|
||||
houseInfo: {
|
||||
houseSite: ['1'],
|
||||
adjacentConstruction: ['0'],
|
||||
chemicalErosion: ['0'],
|
||||
structuralDismantling: 0,
|
||||
addingLayer: 0,
|
||||
repairAndReinforce: ['0'],
|
||||
historicalCalamity: ['0'],
|
||||
functionalChange: ['0'],
|
||||
},
|
||||
}
|
||||
|
||||
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 (
|
||||
<Spin spinning={loading} indicator={<AntIcon type="loading" />}>
|
||||
<Form
|
||||
initialValues={initialValues}
|
||||
ref={this.form}
|
||||
{...layout}
|
||||
onValuesChange={(changedValues, allValues) =>
|
||||
this.onValuesChange(changedValues, allValues)
|
||||
}
|
||||
>
|
||||
<Form.Item
|
||||
label="房屋场地"
|
||||
name={['houseInfo', 'houseSite']}
|
||||
rules={[{ required: true, message: '请选择房屋场地' }]}
|
||||
>
|
||||
<Checkbox.Group>
|
||||
{codes.houseHouseSite.map(item => (
|
||||
<Checkbox key={item.code} value={item.code}>
|
||||
{item.value}
|
||||
</Checkbox>
|
||||
))}
|
||||
</Checkbox.Group>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label="相邻施工"
|
||||
name={['houseInfo', 'adjacentConstruction']}
|
||||
rules={[{ required: true, message: '请选择相邻施工' }]}
|
||||
>
|
||||
<Checkbox.Group>
|
||||
{codes.houseAdjacentConstruction.map(item => (
|
||||
<Checkbox key={item.code} value={item.code}>
|
||||
{item.value}
|
||||
</Checkbox>
|
||||
))}
|
||||
</Checkbox.Group>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label="化学侵蚀"
|
||||
name={['houseInfo', 'chemicalErosion']}
|
||||
rules={[{ required: true, message: '请选择化学侵蚀' }]}
|
||||
>
|
||||
<Checkbox.Group>
|
||||
{codes.houseChemicalErosion.map(item => (
|
||||
<Checkbox key={item.code} value={item.code}>
|
||||
{item.value}
|
||||
</Checkbox>
|
||||
))}
|
||||
</Checkbox.Group>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label="结构拆改"
|
||||
name={['houseInfo', 'structuralDismantling']}
|
||||
rules={[{ required: true, message: '请选择结构拆改' }]}
|
||||
>
|
||||
<Radio.Group buttonStyle="solid">
|
||||
{codes.houseStructuralDismantling.map(item => (
|
||||
<Radio.Button key={item.code} value={+item.code}>
|
||||
{item.value}
|
||||
</Radio.Button>
|
||||
))}
|
||||
</Radio.Group>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label="加层改造"
|
||||
name={['houseInfo', 'addingLayer']}
|
||||
rules={[{ required: true, message: '请选择加层改造' }]}
|
||||
>
|
||||
<Radio.Group buttonStyle="solid">
|
||||
{codes.houseAddingLayer.map(item => (
|
||||
<Radio.Button key={item.code} value={+item.code}>
|
||||
{item.value}
|
||||
</Radio.Button>
|
||||
))}
|
||||
</Radio.Group>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label="修缮加固"
|
||||
name={['houseInfo', 'repairAndReinforce']}
|
||||
rules={[{ required: true, message: '请选择修缮加固' }]}
|
||||
>
|
||||
<Checkbox.Group>
|
||||
{codes.houseRepairAndReinforce.map(item => (
|
||||
<Checkbox key={item.code} value={item.code}>
|
||||
{item.value}
|
||||
</Checkbox>
|
||||
))}
|
||||
</Checkbox.Group>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label="历史灾害"
|
||||
name={['houseInfo', 'historicalCalamity']}
|
||||
rules={[{ required: true, message: '请选择历史灾害' }]}
|
||||
>
|
||||
<Checkbox.Group>
|
||||
{codes.houseHistoricalCalamity.map(item => (
|
||||
<Checkbox key={item.code} value={item.code}>
|
||||
{item.value}
|
||||
</Checkbox>
|
||||
))}
|
||||
</Checkbox.Group>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label="使用功能变更"
|
||||
name={['houseInfo', 'functionalChange']}
|
||||
rules={[{ required: true, message: '请选择使用功能变更' }]}
|
||||
>
|
||||
<Checkbox.Group>
|
||||
{codes.houseFunctionalChange.map(item => (
|
||||
<Checkbox key={item.code} value={item.code}>
|
||||
{item.value}
|
||||
</Checkbox>
|
||||
))}
|
||||
</Checkbox.Group>
|
||||
</Form.Item>
|
||||
<Form.Item label="其他调查内容" name={['houseInfo', 'otherContents']}>
|
||||
<Input.TextArea autoSize placeholder="请输入其他调查内容" />
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</Spin>
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,305 @@
|
||||
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 = {
|
||||
houseInfo: {
|
||||
straightHouseCount: 0,
|
||||
selfHouseCount: 0,
|
||||
privateHouseCount: 0,
|
||||
businessCount: 0,
|
||||
changeHouseCount: 0,
|
||||
resettlementHouseCount: 0,
|
||||
otherCount: 0,
|
||||
houseCount: 0,
|
||||
},
|
||||
}
|
||||
|
||||
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) {
|
||||
const form = this.form.current
|
||||
const { houseInfo } = changedValues
|
||||
|
||||
if (
|
||||
houseInfo.hasOwnProperty('straightHouseCount') ||
|
||||
houseInfo.hasOwnProperty('selfHouseCount') ||
|
||||
houseInfo.hasOwnProperty('otherCount') ||
|
||||
houseInfo.hasOwnProperty('businessCount') ||
|
||||
houseInfo.hasOwnProperty('changeHouseCount') ||
|
||||
houseInfo.hasOwnProperty('resettlementHouseCount') ||
|
||||
houseInfo.hasOwnProperty('privateHouseCount')
|
||||
) {
|
||||
const {
|
||||
houseInfo: {
|
||||
straightHouseCount,
|
||||
selfHouseCount,
|
||||
otherCount,
|
||||
businessCount,
|
||||
changeHouseCount,
|
||||
resettlementHouseCount,
|
||||
privateHouseCount,
|
||||
},
|
||||
} = allValues
|
||||
form.setFieldsValue({
|
||||
houseInfo: {
|
||||
houseCount:
|
||||
+straightHouseCount +
|
||||
+selfHouseCount +
|
||||
+otherCount +
|
||||
+businessCount +
|
||||
+changeHouseCount +
|
||||
+resettlementHouseCount +
|
||||
+privateHouseCount,
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
//#endregion
|
||||
|
||||
render() {
|
||||
const { loading, codes } = this.state
|
||||
|
||||
return (
|
||||
<Spin spinning={loading} indicator={<AntIcon type="loading" />}>
|
||||
<Form
|
||||
initialValues={initialValues}
|
||||
ref={this.form}
|
||||
{...layout}
|
||||
onValuesChange={(changedValues, allValues) =>
|
||||
this.onValuesChange(changedValues, allValues)
|
||||
}
|
||||
>
|
||||
<Form.Item
|
||||
label="产权性质"
|
||||
name={['houseInfo', 'propertyRights']}
|
||||
rules={[{ required: true, message: '请选择产权性质' }]}
|
||||
>
|
||||
<Radio.Group buttonStyle="solid">
|
||||
{codes.housePropertyRights.map(item => (
|
||||
<Radio.Button key={item.code} value={item.code}>
|
||||
{item.value}
|
||||
</Radio.Button>
|
||||
))}
|
||||
</Radio.Group>
|
||||
</Form.Item>
|
||||
<Form.Item label="房屋包含的住宅总套数" className="mb-none">
|
||||
<Row>
|
||||
<Col>
|
||||
<Form.Item colon={false} label="直管公房" required>
|
||||
<Form.Item
|
||||
name={['houseInfo', 'straightHouseCount']}
|
||||
rules={[{ required: true, message: '请输入直管公房' }]}
|
||||
noStyle
|
||||
>
|
||||
<InputNumber min={0} step={1} placeholder="几" />
|
||||
</Form.Item>
|
||||
<div className="ant-form-text">套;</div>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col>
|
||||
<Form.Item colon={false} label="单位自管公房" required>
|
||||
<Form.Item
|
||||
name={['houseInfo', 'selfHouseCount']}
|
||||
rules={[{ required: true, message: '请输入单位自管公房' }]}
|
||||
noStyle
|
||||
>
|
||||
<InputNumber min={0} step={1} placeholder="几" />
|
||||
</Form.Item>
|
||||
<div className="ant-form-text">套;</div>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col>
|
||||
<Form.Item colon={false} label="其他">
|
||||
<Form.Item name={['houseInfo', 'otherCount']} noStyle>
|
||||
<InputNumber min={0} step={1} placeholder="几" />
|
||||
</Form.Item>
|
||||
<div className="ant-form-text">套;</div>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row>
|
||||
<Col>
|
||||
<Form.Item colon={false} label="商品房" required>
|
||||
<Form.Item
|
||||
name={['houseInfo', 'businessCount']}
|
||||
rules={[{ required: true, message: '请输入商品房' }]}
|
||||
noStyle
|
||||
>
|
||||
<InputNumber min={0} step={1} placeholder="几" />
|
||||
</Form.Item>
|
||||
<div className="ant-form-text">套;</div>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col>
|
||||
<Form.Item colon={false} label="房改房" required>
|
||||
<Form.Item
|
||||
name={['houseInfo', 'changeHouseCount']}
|
||||
rules={[{ required: true, message: '请输入房改房' }]}
|
||||
noStyle
|
||||
>
|
||||
<InputNumber min={0} step={1} placeholder="几" />
|
||||
</Form.Item>
|
||||
<div className="ant-form-text">套;</div>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col>
|
||||
<Form.Item colon={false} label="拆迁安置房" required>
|
||||
<Form.Item
|
||||
name={['houseInfo', 'resettlementHouseCount']}
|
||||
rules={[{ required: true, message: '请输入拆迁安置房' }]}
|
||||
noStyle
|
||||
>
|
||||
<InputNumber min={0} step={1} placeholder="几" />
|
||||
</Form.Item>
|
||||
<div className="ant-form-text">套;</div>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row>
|
||||
<Col>
|
||||
<Form.Item colon={false} label="私房">
|
||||
<Form.Item name={['houseInfo', 'privateHouseCount']} noStyle>
|
||||
<InputNumber min={0} step={1} placeholder="几" />
|
||||
</Form.Item>
|
||||
<div className="ant-form-text">套;</div>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col>
|
||||
<Form.Item colon={false} label="总共">
|
||||
<Form.Item name={['houseInfo', 'houseCount']} noStyle>
|
||||
<InputNumber disabled min={0} step={1} placeholder="几" />
|
||||
</Form.Item>
|
||||
<div className="ant-form-text">套</div>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
</Row>
|
||||
</Form.Item>
|
||||
<Form.Item label="产权单位" name={['houseInfo', 'propertyUnit']}>
|
||||
<Input autoComplete="off" placeholder="请输入产权单位" />
|
||||
</Form.Item>
|
||||
<Row gutter={16}>
|
||||
<Col span={12}>
|
||||
<Form.Item label="负责人" name={['houseInfo', 'propertyUnitUser']}>
|
||||
<Input autoComplete="off" placeholder="请输入负责人" />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={12}>
|
||||
<Form.Item
|
||||
label="负责人电话"
|
||||
name={['houseInfo', 'propertyUnitUserTel']}
|
||||
>
|
||||
<Input autoComplete="off" placeholder="请输入负责人电话" />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={12}>
|
||||
<Form.Item label="联系人" name={['houseInfo', 'propertyUnitConent']}>
|
||||
<Input autoComplete="off" placeholder="请输入联系人" />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={12}>
|
||||
<Form.Item
|
||||
label="联系人电话"
|
||||
name={['houseInfo', 'propertyUnitConentTel']}
|
||||
>
|
||||
<Input autoComplete="off" placeholder="请输入联系人电话" />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
</Row>
|
||||
</Form>
|
||||
</Spin>
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -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 (
|
||||
<Spin spinning={loading} indicator={<AntIcon type="loading" />}>
|
||||
<Form
|
||||
initialValues={initialValues}
|
||||
ref={this.form}
|
||||
{...layout}
|
||||
onValuesChange={(changedValues, allValues) =>
|
||||
this.onValuesChange(changedValues, allValues)
|
||||
}
|
||||
>
|
||||
<Row gutter={16}>
|
||||
<Col span={12}>
|
||||
<Form.Item
|
||||
label="调查登记机构"
|
||||
name={['houseInfo', 'investigateAgency']}
|
||||
rules={[{ required: true, message: '请输入调查登记机构' }]}
|
||||
>
|
||||
<Input autoComplete="off" placeholder="请输入调查登记机构" />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={12}>
|
||||
<Form.Item
|
||||
label="调查人员"
|
||||
name={['houseInfo', 'investigateUser']}
|
||||
rules={[{ required: true, message: '请输入调查人员' }]}
|
||||
>
|
||||
<Input autoComplete="off" placeholder="请输入调查人员" />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={12}>
|
||||
<Form.Item
|
||||
label="审核人员"
|
||||
name={['houseInfo', 'offlineAuditor']}
|
||||
rules={[{ required: true, message: '请输入审核人员' }]}
|
||||
>
|
||||
<Input autoComplete="off" placeholder="请输入审核人员" />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={12}>
|
||||
<Form.Item
|
||||
label="主管部门"
|
||||
name={['houseInfo', 'competentDepartment']}
|
||||
rules={[{ required: true, message: '请输入主管部门' }]}
|
||||
>
|
||||
<Input autoComplete="off" placeholder="请输入主管部门" />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
</Row>
|
||||
</Form>
|
||||
</Spin>
|
||||
)
|
||||
}
|
||||
}
|
||||
369
framework/web-react/src/pages/business/house/info/form/index.jsx
Normal file
369
framework/web-react/src/pages/business/house/info/form/index.jsx
Normal file
@@ -0,0 +1,369 @@
|
||||
import React, { Component } from 'react'
|
||||
import { Form, Button, Input, Descriptions, message as Message, Modal, Spin, Tabs } from 'antd'
|
||||
import { merge, isEqual, pickBy } from 'lodash'
|
||||
import { AntIcon, ComponentDynamic, Container, Auth } from 'components'
|
||||
import { api } from 'common/api'
|
||||
|
||||
const tabs = [
|
||||
{
|
||||
title: '房屋基本情况',
|
||||
component: () => import('./base'),
|
||||
active: true,
|
||||
show: true,
|
||||
},
|
||||
// {
|
||||
// title: '幕墙信息',
|
||||
// name: 'curtainWall',
|
||||
// path: 'curtainWall',
|
||||
// active: false,
|
||||
// show: false,
|
||||
// },
|
||||
// {
|
||||
// title: '面砖信息',
|
||||
// name: 'faceBrick',
|
||||
// path: 'faceBrick',
|
||||
// active: false,
|
||||
// show: false,
|
||||
// },
|
||||
// {
|
||||
// title: '墙面粉刷信息',
|
||||
// name: 'whiteWash',
|
||||
// path: 'whiteWash',
|
||||
// active: false,
|
||||
// show: false,
|
||||
// },
|
||||
// {
|
||||
// title: '墙面涂料信息',
|
||||
// name: 'coating',
|
||||
// path: 'coating',
|
||||
// active: false,
|
||||
// show: false,
|
||||
// },
|
||||
{
|
||||
title: '巡查登记',
|
||||
component: () => import('./patrol'),
|
||||
active: false,
|
||||
show: true,
|
||||
},
|
||||
]
|
||||
const actions = {
|
||||
save: {
|
||||
action: 'houseInfoSave',
|
||||
remark: '保存',
|
||||
after: 'reload',
|
||||
},
|
||||
submit: {
|
||||
action: 'houseInfoSubmitToCheck',
|
||||
remark: '提交',
|
||||
after: 'close',
|
||||
},
|
||||
check: {
|
||||
action: 'houseInfoCheck',
|
||||
remark: '审核',
|
||||
after: 'close',
|
||||
},
|
||||
}
|
||||
|
||||
export default class index extends Component {
|
||||
state = {
|
||||
actived: '0',
|
||||
|
||||
loading: true,
|
||||
record: null,
|
||||
|
||||
saveDisabled: true,
|
||||
saving: false,
|
||||
taskStatus: 0,
|
||||
}
|
||||
|
||||
children = []
|
||||
|
||||
formData = {}
|
||||
checkForm = React.createRef()
|
||||
|
||||
shouldComponentUpdate(props, state) {
|
||||
return !isEqual(this.state, state)
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
// 获取详细数据
|
||||
this.onLoadInitData()
|
||||
}
|
||||
|
||||
call(child, index) {
|
||||
this.children[index] = child
|
||||
if (this.children.filter(p => p).length === tabs.filter(p => p.show).length) {
|
||||
this.setState({ saveDisabled: false })
|
||||
}
|
||||
}
|
||||
|
||||
onLoadInitData() {
|
||||
this.setState({
|
||||
loading: true,
|
||||
})
|
||||
const { taskId } = this.props.param
|
||||
if (taskId) {
|
||||
api.houseInfoGetByTaskId({ taskId }).then(({ data }) => {
|
||||
// 删除空节点
|
||||
for (const key in data) {
|
||||
data[key] = pickBy(data[key], p => p !== null && p !== undefined)
|
||||
}
|
||||
this.setState({
|
||||
taskStatus: data.patrolInfo.status,
|
||||
record: data,
|
||||
loading: false,
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
async onSave() {
|
||||
await this.onPostData(actions.save)
|
||||
}
|
||||
|
||||
async onSubmit() {
|
||||
Modal.confirm({
|
||||
content: '确认提交审核吗?',
|
||||
onOk: () => {
|
||||
this.onPostData(actions.submit)
|
||||
},
|
||||
onCancel: () => {},
|
||||
})
|
||||
}
|
||||
|
||||
async onCheck(pass_or_back) {
|
||||
const form = this.checkForm.current
|
||||
const valid = await form.validateFields()
|
||||
|
||||
Modal.confirm({
|
||||
content: '审核结果即将提交,请确认',
|
||||
onOk: () => {
|
||||
if (valid) {
|
||||
var checkRecord = {
|
||||
taskCheckRecord: {
|
||||
taskId: this.props.param.taskId,
|
||||
passOrBack: +pass_or_back,
|
||||
content: form.getFieldValue(['taskCheckRecord', 'content']),
|
||||
},
|
||||
}
|
||||
this.onPostData(actions.check, checkRecord)
|
||||
}
|
||||
},
|
||||
onCancel: () => {},
|
||||
})
|
||||
}
|
||||
|
||||
async onPostData(action, append) {
|
||||
for (const child of this.children) {
|
||||
try {
|
||||
const data = await child.getData()
|
||||
merge(this.formData, data)
|
||||
} catch (e) {
|
||||
return e
|
||||
}
|
||||
}
|
||||
|
||||
//#region 提交数据
|
||||
|
||||
if (append) {
|
||||
this.formData = {
|
||||
...this.formData,
|
||||
...append,
|
||||
}
|
||||
}
|
||||
if (this.formData.houseCode) {
|
||||
this.formData.houseCode.id = this.state.record.houseCode.id
|
||||
}
|
||||
|
||||
if (this.formData.patrolInfo && this.props.param.taskId) {
|
||||
this.formData.patrolInfo.id = this.props.param.taskId
|
||||
}
|
||||
|
||||
this.setState({ saving: true })
|
||||
|
||||
if (action) {
|
||||
try {
|
||||
const { success } = await api[action.action](this.formData)
|
||||
if (success) {
|
||||
Message.success(action.remark + '成功')
|
||||
this.setState({ saving: false })
|
||||
if (this.props.param.table.current) {
|
||||
this.props.param.table.current.onReloadData()
|
||||
}
|
||||
switch (action.after) {
|
||||
case 'close':
|
||||
window.closeContentWindow()
|
||||
break
|
||||
default:
|
||||
this.onLoadInitData()
|
||||
break
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
this.setState({ saving: false })
|
||||
}
|
||||
}
|
||||
|
||||
// setTimeout(() => {
|
||||
// Message.success('提交成功')
|
||||
// this.setState({ saving: false })
|
||||
// }, 3000)
|
||||
//#endregion
|
||||
}
|
||||
|
||||
render() {
|
||||
const { loading, record, saveDisabled, saving } = this.state
|
||||
|
||||
return (
|
||||
<div className="yo-form-page">
|
||||
<div className="yo-form-page-layout">
|
||||
{/* 底部工具栏(需放在前面) */}
|
||||
<div className="yo-form-page--bar yo-form-page--bar--with-tab">
|
||||
<Container mode="fluid">
|
||||
<div className="yo-form-page--bar-inner">
|
||||
<span>
|
||||
<Auth auth={{ houseInfo: 'check' }}>
|
||||
{this.state.taskStatus == 3 && (
|
||||
<Form ref={this.checkForm} layout="inline">
|
||||
<Form.Item
|
||||
label="审核意见"
|
||||
name={['taskCheckRecord', 'content']}
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
message: '请输入审核意见',
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Input.TextArea
|
||||
autoSize
|
||||
autoComplete="off"
|
||||
placeholder="请输入审核意见"
|
||||
className="w-500"
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form.Item>
|
||||
<Button
|
||||
type="primary"
|
||||
onClick={() => this.onCheck(6)}
|
||||
>
|
||||
通过
|
||||
</Button>
|
||||
<Button
|
||||
type="danger"
|
||||
onClick={() => this.onCheck(-1)}
|
||||
>
|
||||
退回
|
||||
</Button>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
)}
|
||||
</Auth>
|
||||
</span>
|
||||
<span>
|
||||
{this.state.taskStatus >= -1 && this.state.taskStatus < 3 && (
|
||||
<Button
|
||||
disabled={saveDisabled}
|
||||
loading={saving}
|
||||
type="primary"
|
||||
onClick={() => this.onSave()}
|
||||
>
|
||||
保存
|
||||
</Button>
|
||||
)}
|
||||
{this.state.taskStatus == 2 && (
|
||||
<Button type="primary" onClick={() => this.onSubmit()}>
|
||||
提交审核
|
||||
</Button>
|
||||
)}
|
||||
<Button onClick={() => window.closeContentWindow()}>
|
||||
取消
|
||||
</Button>
|
||||
</span>
|
||||
</div>
|
||||
</Container>
|
||||
</div>
|
||||
<div className="yo-form-page--header" style={{ paddingBottom: 0 }}>
|
||||
<Container mode="fluid">
|
||||
<Spin spinning={loading} indicator={<AntIcon type="loading" />}>
|
||||
<Descriptions column={4}>
|
||||
<Descriptions.Item label="区县(市)">
|
||||
{record && record.houseCode.areaName}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="街道(乡镇)">
|
||||
{record && record.houseCode.roadName}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="社区">
|
||||
{record && record.houseCode.commName}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="片区">
|
||||
{record && record.houseCode.zoneName}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item span="2" label="编号">
|
||||
{record &&
|
||||
`${record.houseCode.areaName}-${
|
||||
record.houseCode.roadName
|
||||
}-${record.houseCode.commName}-${
|
||||
record.houseCode.fullProjName
|
||||
}-${record.houseCode.no.toString().padStart(3, '0')}`}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item span="2" label="编码">
|
||||
{record && record.houseCode.houseCode}
|
||||
</Descriptions.Item>
|
||||
</Descriptions>
|
||||
</Spin>
|
||||
</Container>
|
||||
</div>
|
||||
<div className="yo-tab-external-mount">
|
||||
<Tabs
|
||||
activeKey={this.state.actived}
|
||||
animated={false}
|
||||
onChange={activeKey => {
|
||||
this.setState({ actived: activeKey })
|
||||
}}
|
||||
>
|
||||
{tabs.map((tab, i) => {
|
||||
if (tab.show) {
|
||||
return (
|
||||
<Tabs.TabPane
|
||||
key={i}
|
||||
forceRender={false}
|
||||
tab={tab.title}
|
||||
></Tabs.TabPane>
|
||||
)
|
||||
}
|
||||
return <></>
|
||||
})}
|
||||
</Tabs>
|
||||
<div className="yo-tab-external-mount-content">
|
||||
{tabs.map((tab, i) => {
|
||||
if (tab.show) {
|
||||
return (
|
||||
<div
|
||||
key={i}
|
||||
className={[
|
||||
this.state.actived === i.toString()
|
||||
? 'yo-tab-external-tabpane-active'
|
||||
: 'yo-tab-external-tabpane-inactive',
|
||||
'yo-tab-external-tabpane',
|
||||
].join(' ')}
|
||||
>
|
||||
<ComponentDynamic
|
||||
is={tab.component}
|
||||
id={this.props.id}
|
||||
record={record}
|
||||
loading={loading}
|
||||
onRef={child => this.call(child, i)}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
return <></>
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -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 (
|
||||
<Spin spinning={loading} indicator={<AntIcon type="loading" />}>
|
||||
<Form {...layout} ref={this.form}>
|
||||
<Row gutter={16}>
|
||||
<Col span={12}>
|
||||
<Form.Item
|
||||
name={['patrolInfo', 'patrolDate']}
|
||||
label="巡查日期"
|
||||
rules={[{ required: true, message: '请选择巡查日期' }]}
|
||||
>
|
||||
<DatePicker
|
||||
onChange={date => {
|
||||
/*$root.transfer.completedYear = date.format('YYYY') */
|
||||
}}
|
||||
className="w-100-p"
|
||||
placeholder="请选择巡查日期"
|
||||
/>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={12}>
|
||||
<Form.Item
|
||||
label="巡查人/单位"
|
||||
name={['patrolInfo', 'patrolUser']}
|
||||
rules={[{ required: true, message: '请输入巡查人/单位' }]}
|
||||
>
|
||||
<Input autoComplete="off" placeholder="请输入巡查人/单位" />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
</Row>
|
||||
</Form>
|
||||
</Spin>
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,182 @@
|
||||
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
|
||||
if (this.record.houseInfo.completedDate)
|
||||
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
|
||||
return (
|
||||
<Spin spinning={loading} indicator={<AntIcon type="loading" />}>
|
||||
<Form {...layout} ref={this.form}>
|
||||
<Form.Item
|
||||
label="初始等级"
|
||||
name={['patrolInfo', 'initGrade']}
|
||||
tooltip="初始等级无法手动更改,由房屋详情的竣工日期决定:2000年之后竣工的为一级,1995年~1999年竣工的为二级,1980年~1994年竣工的为三级,早于1980年竣工的为四级。选择房屋竣工日期后,初始等级会自动填充。"
|
||||
rules={[{ required: true, message: '请选择初始等级' }]}
|
||||
>
|
||||
<Radio.Group disabled buttonStyle="solid">
|
||||
{codes.housePatrolInitGrade.map(item => {
|
||||
return (
|
||||
<Radio.Button key={item.code} value={+item.code}>
|
||||
{item.value}
|
||||
</Radio.Button>
|
||||
)
|
||||
})}
|
||||
</Radio.Group>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
label="损坏等级"
|
||||
name={['patrolInfo', 'damageGrade']}
|
||||
rules={[{ required: true, message: '请选择损坏等级' }]}
|
||||
>
|
||||
<Radio.Group buttonStyle="solid">
|
||||
{codes.housePatrolDamageGrade.map(item => {
|
||||
return (
|
||||
<Radio.Button key={item.code} value={+item.code}>
|
||||
{item.value}
|
||||
</Radio.Button>
|
||||
)
|
||||
})}
|
||||
</Radio.Group>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
label="综合等级"
|
||||
name={['patrolInfo', 'comprehensiveGrade']}
|
||||
rules={[{ required: true, message: '请选择综合等级' }]}
|
||||
>
|
||||
<Radio.Group buttonStyle="solid">
|
||||
{codes.houseGrade.map(item => {
|
||||
return (
|
||||
<Radio.Button key={item.code} value={+item.code}>
|
||||
{item.value}
|
||||
</Radio.Button>
|
||||
)
|
||||
})}
|
||||
</Radio.Group>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</Spin>
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -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 (
|
||||
<Spin spinning={loading} indicator={<AntIcon type="loading" />}>
|
||||
<Form {...layout} ref={this.form}>
|
||||
<Form.Item
|
||||
label="处理建议"
|
||||
name={['patrolInfo', 'handlingOpinion']}
|
||||
rules={[{ required: true, message: '请选择处理建议' }]}
|
||||
>
|
||||
<Radio.Group buttonStyle="solid">
|
||||
{codes.housePatrolHandlingOpinion.map(item => {
|
||||
return (
|
||||
<Radio.Button key={item.code} value={+item.code}>
|
||||
{item.value}
|
||||
</Radio.Button>
|
||||
)
|
||||
})}
|
||||
</Radio.Group>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item label="处理建议备注" name={['patrolInfo', 'handlingOpinionRemark']}>
|
||||
<Input.TextArea
|
||||
autoSize={{ minRows: 3 }}
|
||||
placeholder="请输入处理建议备注"
|
||||
/>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
label="整改情况"
|
||||
name={['patrolInfo', 'rectifyAndReform']}
|
||||
rules={[{ required: true, message: '请选择整改情况' }]}
|
||||
>
|
||||
<Radio.Group buttonStyle="solid">
|
||||
{codes.housePatrolRectifyReform.map(item => {
|
||||
return (
|
||||
<Radio.Button key={item.code} value={+item.code}>
|
||||
{item.value}
|
||||
</Radio.Button>
|
||||
)
|
||||
})}
|
||||
</Radio.Group>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item label="整改情况备注" name={['patrolInfo', 'rectifyAndReformRemark']}>
|
||||
<Input.TextArea
|
||||
autoSize={{ minRows: 3 }}
|
||||
placeholder="请输入整改情况备注"
|
||||
/>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</Spin>
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,150 @@
|
||||
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('./investigation'),
|
||||
},
|
||||
{
|
||||
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 (
|
||||
<Container mode="fluid" ref={this.setContainer}>
|
||||
<Row gutter={16}>
|
||||
<Col flex="1">
|
||||
<br />
|
||||
<div className="yo-adorn--house-top" />
|
||||
<Card className="yo-form-page--body">
|
||||
{parts.map((item, i) => (
|
||||
<React.Fragment key={i}>
|
||||
<section id={`form-patrol-${i}-${id}`}>
|
||||
{item.title && <h5>{item.title}</h5>}
|
||||
<Spin
|
||||
spinning={loading}
|
||||
indicator={<AntIcon type="loading" />}
|
||||
wrapperClassName={loading && 'h-400-min'}
|
||||
>
|
||||
{!loading && (
|
||||
<ComponentDynamic
|
||||
is={item.component}
|
||||
{...this.props}
|
||||
onRef={child => this.call(child, i)}
|
||||
/>
|
||||
)}
|
||||
</Spin>
|
||||
</section>
|
||||
{i < parts.length - 1 && <Divider />}
|
||||
</React.Fragment>
|
||||
))}
|
||||
</Card>
|
||||
</Col>
|
||||
{/* 锚点,如果不需要可以删除以下节点 */}
|
||||
<Col flex="240px">
|
||||
<Anchor
|
||||
getContainer={() => this.container}
|
||||
offsetTop={24}
|
||||
targetOffset={100}
|
||||
wrapperStyle={{ backgroundColor: 'transparent' }}
|
||||
onClick={e => e.preventDefault()}
|
||||
>
|
||||
{parts.map((part, i) => (
|
||||
<Anchor.Link
|
||||
key={i}
|
||||
href={`#form-patrol-${i}-${id}`}
|
||||
title={part.title}
|
||||
/>
|
||||
))}
|
||||
</Anchor>
|
||||
</Col>
|
||||
</Row>
|
||||
</Container>
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -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 (
|
||||
<Spin spinning={loading} indicator={<AntIcon type="loading" />}>
|
||||
<Form
|
||||
initialValues={initialValues}
|
||||
ref={this.form}
|
||||
{...layout}
|
||||
onValuesChange={(changedValues, allValues) =>
|
||||
this.onValuesChange(changedValues, allValues)
|
||||
}
|
||||
>
|
||||
<Row gutter={16}>
|
||||
<Col span={12}>
|
||||
<Form.Item label="沉降倾斜情况" name={['patrolInfo', 'settlementTilt']}>
|
||||
<Input.TextArea
|
||||
autoSize={{ minRows: 4.6 }}
|
||||
placeholder="请输入沉降倾斜情况"
|
||||
/>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={12}>
|
||||
<Form.Item
|
||||
name={['patrolInfo', 'settlementTiltFiles']}
|
||||
valuePropName="fileList"
|
||||
getValueFromEvent={e => {
|
||||
if (Array.isArray(e)) {
|
||||
return e
|
||||
}
|
||||
return e && e.fileList
|
||||
}}
|
||||
>
|
||||
<Upload
|
||||
listType="picture-card"
|
||||
customRequest={e => this.onFileUpload(e)}
|
||||
showUploadList={{
|
||||
showRemoveIcon: true,
|
||||
showDownloadIcon: true,
|
||||
}}
|
||||
onPreview={file =>
|
||||
this.onFilePreview(file, 'settlementTiltFiles')
|
||||
}
|
||||
onDownload={file => this.onFileDownload(file)}
|
||||
>
|
||||
<div>
|
||||
<AntIcon type="plus" />
|
||||
<div className="ant-upload-text">沉降倾斜照片</div>
|
||||
</div>
|
||||
</Upload>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={12}>
|
||||
<Form.Item label="其他情况" name={['patrolInfo', 'otherInfo']}>
|
||||
<Input.TextArea
|
||||
autoSize={{ minRows: 4.6 }}
|
||||
placeholder="请输入其他情况"
|
||||
/>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={12}>
|
||||
<Form.Item
|
||||
name={['patrolInfo', 'otherInfoFiles']}
|
||||
valuePropName="fileList"
|
||||
getValueFromEvent={e => {
|
||||
if (Array.isArray(e)) {
|
||||
return e
|
||||
}
|
||||
return e && e.fileList
|
||||
}}
|
||||
>
|
||||
<Upload
|
||||
listType="picture-card"
|
||||
customRequest={e => this.onFileUpload(e)}
|
||||
showUploadList={{
|
||||
showRemoveIcon: true,
|
||||
showDownloadIcon: true,
|
||||
}}
|
||||
onPreview={file => this.onFilePreview(file, 'otherInfoFiles')}
|
||||
onDownload={file => this.onFileDownload(file)}
|
||||
>
|
||||
<div>
|
||||
<AntIcon type="plus" />
|
||||
<div className="ant-upload-text">其他情况照片</div>
|
||||
</div>
|
||||
</Upload>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
</Row>
|
||||
<Form.Item label="主要安全隐患综述" name={['patrolInfo', 'mainSafety']}>
|
||||
<Input.TextArea
|
||||
autoSize={{ minRows: 4.6 }}
|
||||
placeholder="请输入主要安全隐患综述"
|
||||
/>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
|
||||
<PhotoPreview ref={this.photoPreview} />
|
||||
</Spin>
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,312 @@
|
||||
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 = {
|
||||
patrolInfo: {
|
||||
houseSite: ['1'],
|
||||
adjacentConstruction: ['0'],
|
||||
chemicalErosion: ['0'],
|
||||
structuralDismantling: 0,
|
||||
addingLayer: 0,
|
||||
repairAndReinforce: ['0'],
|
||||
historicalCalamity: ['0'],
|
||||
functionalChange: ['0'],
|
||||
},
|
||||
}
|
||||
|
||||
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 { patrolInfo } = this.record
|
||||
// checkbox
|
||||
checkboxKeys.forEach(key => {
|
||||
if (patrolInfo[key]) {
|
||||
patrolInfo[key] = patrolInfo[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 { patrolInfo } = postData
|
||||
// checkbox
|
||||
checkboxKeys.forEach(key => {
|
||||
if (patrolInfo[key]) {
|
||||
patrolInfo[key] = sortBy(patrolInfo[key], p => +p).join(',')
|
||||
}
|
||||
})
|
||||
//#endregion
|
||||
return postData
|
||||
}
|
||||
}
|
||||
|
||||
//#region 自定义方法
|
||||
/**
|
||||
* 表单change事件处理,包括了所有字段的change
|
||||
* [异步,非必要]
|
||||
* @param {*} changedValues
|
||||
* @param {*} allValues
|
||||
*/
|
||||
async onValuesChange(changedValues, allValues) {
|
||||
const { patrolInfo } = changedValues
|
||||
const key = Object.keys(patrolInfo).shift()
|
||||
if (
|
||||
[
|
||||
'adjacentConstruction',
|
||||
'chemicalErosion',
|
||||
'repairAndReinforce',
|
||||
'historicalCalamity',
|
||||
'functionalChange',
|
||||
].includes(key)
|
||||
) {
|
||||
this.checkedNone(patrolInfo[key], key)
|
||||
}
|
||||
}
|
||||
|
||||
checkedNone(value, key) {
|
||||
const form = this.form.current
|
||||
if (first(value) == 0 && value.length > 1) {
|
||||
// 在'无'之后选中其他值
|
||||
value.shift()
|
||||
form.setFieldsValue({
|
||||
patrolInfo: { [key]: value },
|
||||
})
|
||||
} else if (last(value) == 0 && value.length > 1) {
|
||||
// 在其他值之后选中'无'
|
||||
value = ['0']
|
||||
form.setFieldsValue({
|
||||
patrolInfo: { [key]: value },
|
||||
})
|
||||
}
|
||||
return value
|
||||
}
|
||||
//#endregion
|
||||
|
||||
render() {
|
||||
const { loading, codes } = this.state
|
||||
|
||||
return (
|
||||
<Spin spinning={loading} indicator={<AntIcon type="loading" />}>
|
||||
<Form
|
||||
initialValues={initialValues}
|
||||
ref={this.form}
|
||||
{...layout}
|
||||
onValuesChange={(changedValues, allValues) =>
|
||||
this.onValuesChange(changedValues, allValues)
|
||||
}
|
||||
>
|
||||
<Form.Item
|
||||
label="房屋场地"
|
||||
name={['patrolInfo', 'houseSite']}
|
||||
rules={[{ required: true, message: '请选择房屋场地' }]}
|
||||
>
|
||||
<Checkbox.Group>
|
||||
{codes.houseHouseSite.map(item => (
|
||||
<Checkbox key={item.code} value={item.code}>
|
||||
{item.value}
|
||||
</Checkbox>
|
||||
))}
|
||||
</Checkbox.Group>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label="相邻施工"
|
||||
name={['patrolInfo', 'adjacentConstruction']}
|
||||
rules={[{ required: true, message: '请选择相邻施工' }]}
|
||||
>
|
||||
<Checkbox.Group>
|
||||
{codes.houseAdjacentConstruction.map(item => (
|
||||
<Checkbox key={item.code} value={item.code}>
|
||||
{item.value}
|
||||
</Checkbox>
|
||||
))}
|
||||
</Checkbox.Group>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label="化学侵蚀"
|
||||
name={['patrolInfo', 'chemicalErosion']}
|
||||
rules={[{ required: true, message: '请选择化学侵蚀' }]}
|
||||
>
|
||||
<Checkbox.Group>
|
||||
{codes.houseChemicalErosion.map(item => (
|
||||
<Checkbox key={item.code} value={item.code}>
|
||||
{item.value}
|
||||
</Checkbox>
|
||||
))}
|
||||
</Checkbox.Group>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label="结构拆改"
|
||||
name={['patrolInfo', 'structuralDismantling']}
|
||||
rules={[{ required: true, message: '请选择结构拆改' }]}
|
||||
>
|
||||
<Radio.Group buttonStyle="solid">
|
||||
{codes.houseStructuralDismantling.map(item => (
|
||||
<Radio.Button key={item.code} value={+item.code}>
|
||||
{item.value}
|
||||
</Radio.Button>
|
||||
))}
|
||||
</Radio.Group>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label="加层改造"
|
||||
name={['patrolInfo', 'addingLayer']}
|
||||
rules={[{ required: true, message: '请选择加层改造' }]}
|
||||
>
|
||||
<Radio.Group buttonStyle="solid">
|
||||
{codes.houseAddingLayer.map(item => (
|
||||
<Radio.Button key={item.code} value={+item.code}>
|
||||
{item.value}
|
||||
</Radio.Button>
|
||||
))}
|
||||
</Radio.Group>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label="修缮加固"
|
||||
name={['patrolInfo', 'repairAndReinforce']}
|
||||
rules={[{ required: true, message: '请选择修缮加固' }]}
|
||||
>
|
||||
<Checkbox.Group>
|
||||
{codes.houseRepairAndReinforce.map(item => (
|
||||
<Checkbox key={item.code} value={item.code}>
|
||||
{item.value}
|
||||
</Checkbox>
|
||||
))}
|
||||
</Checkbox.Group>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label="历史灾害"
|
||||
name={['patrolInfo', 'historicalCalamity']}
|
||||
rules={[{ required: true, message: '请选择历史灾害' }]}
|
||||
>
|
||||
<Checkbox.Group>
|
||||
{codes.houseHistoricalCalamity.map(item => (
|
||||
<Checkbox key={item.code} value={item.code}>
|
||||
{item.value}
|
||||
</Checkbox>
|
||||
))}
|
||||
</Checkbox.Group>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label="使用功能变更"
|
||||
name={['patrolInfo', 'functionalChange']}
|
||||
rules={[{ required: true, message: '请选择使用功能变更' }]}
|
||||
>
|
||||
<Checkbox.Group>
|
||||
{codes.houseFunctionalChange.map(item => (
|
||||
<Checkbox key={item.code} value={item.code}>
|
||||
{item.value}
|
||||
</Checkbox>
|
||||
))}
|
||||
</Checkbox.Group>
|
||||
</Form.Item>
|
||||
<Form.Item label="其他调查内容" name={['patrolInfo', 'otherContents']}>
|
||||
<Input.TextArea autoSize placeholder="请输入其他调查内容" />
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</Spin>
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -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 (
|
||||
<Spin spinning={loading} indicator={<AntIcon type="loading" />}>
|
||||
<Form {...layout} ref={this.form}>
|
||||
<Form.Item
|
||||
label="正常与否"
|
||||
name={['patrolInfo', 'patrolResult']}
|
||||
rules={[{ required: true, message: '请选择本期巡查结果' }]}
|
||||
>
|
||||
<Radio.Group buttonStyle="solid">
|
||||
{codes.patrolResult.map(item => {
|
||||
return (
|
||||
<Radio.Button key={item.code} value={+item.code}>
|
||||
{item.value}
|
||||
</Radio.Button>
|
||||
)
|
||||
})}
|
||||
</Radio.Group>
|
||||
</Form.Item>
|
||||
<Form.Item label="异常情况描述" name={['patrolInfo', 'patrolResultRemark']}>
|
||||
<Input.TextArea autoSize placeholder="请输入异常情况描述" />
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</Spin>
|
||||
)
|
||||
}
|
||||
}
|
||||
15
framework/web-react/src/pages/business/house/info/index.jsx
Normal file
15
framework/web-react/src/pages/business/house/info/index.jsx
Normal file
@@ -0,0 +1,15 @@
|
||||
import React, { Component } from 'react'
|
||||
import { Button } from 'antd'
|
||||
|
||||
export default class index extends Component {
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<Button onClick={() => window.openContentWindow({
|
||||
title: '房屋表单',
|
||||
path: 'business/house/info/form',
|
||||
})}>打开表单</Button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
115
framework/web-react/src/pages/business/house/member/data.jsx
Normal file
115
framework/web-react/src/pages/business/house/member/data.jsx
Normal file
@@ -0,0 +1,115 @@
|
||||
import React, { Component } from 'react'
|
||||
import { Form, Spin, TreeSelect } from 'antd'
|
||||
import { AntIcon } from 'components'
|
||||
import { cloneDeep } from 'lodash'
|
||||
import { api } from 'common/api'
|
||||
|
||||
export default class dataForm extends Component {
|
||||
state = {
|
||||
// 加载状态
|
||||
loading: true,
|
||||
|
||||
options: {
|
||||
orgData: [],
|
||||
areaData: [],
|
||||
orgCheckedKeys: [],
|
||||
},
|
||||
}
|
||||
|
||||
// 表单实例
|
||||
form = React.createRef()
|
||||
|
||||
// 初始化数据
|
||||
id = ''
|
||||
|
||||
/**
|
||||
* mount后回调
|
||||
*/
|
||||
componentDidMount() {
|
||||
this.props.created && this.props.created(this)
|
||||
}
|
||||
async fillData(params) {
|
||||
this.id = params.id
|
||||
//#region 从后端转换成前段所需格式
|
||||
const orgData = await this.loadOrgData()
|
||||
const areaData = await this.loadAreaData()
|
||||
const orgCheckedKeys = await this.loadMemberOwn(this.id)
|
||||
this.setState({
|
||||
options: {
|
||||
orgData,
|
||||
areaData,
|
||||
orgCheckedKeys,
|
||||
},
|
||||
})
|
||||
this.form.current.setFieldsValue({
|
||||
id: this.id,
|
||||
grantOrgIdList: orgCheckedKeys,
|
||||
grantAreaCodeList: [],
|
||||
})
|
||||
|
||||
this.setState({
|
||||
loading: false,
|
||||
})
|
||||
}
|
||||
/**
|
||||
* 获取数据
|
||||
* 可以对postData进行数据结构调整
|
||||
* [异步,必要]
|
||||
* @returns
|
||||
*/
|
||||
async getData() {
|
||||
const form = this.form.current
|
||||
|
||||
const valid = await form.validateFields()
|
||||
if (valid) {
|
||||
const postData = form.getFieldsValue()
|
||||
if (this.id) {
|
||||
postData.id = this.id
|
||||
}
|
||||
//#region 从前段转换后端所需格式
|
||||
//#endregion
|
||||
return postData
|
||||
}
|
||||
}
|
||||
|
||||
//#region 自定义方法
|
||||
async loadOrgData() {
|
||||
const { data } = await api.getOrgTree()
|
||||
return data
|
||||
}
|
||||
|
||||
async loadAreaData() {
|
||||
const { data } = await api.getAreaTree()
|
||||
return data
|
||||
}
|
||||
async loadMemberOwn(id) {
|
||||
const { data } = await api.houseMemberOwnData({ id })
|
||||
return data
|
||||
}
|
||||
render() {
|
||||
return (
|
||||
<Form ref={this.form} className="yo-form">
|
||||
<Spin spinning={this.state.loading} indicator={<AntIcon type="loading" />}>
|
||||
<div className="yo-form-group">
|
||||
<Form.Item label="选择机构" name="grantOrgIdList">
|
||||
<TreeSelect
|
||||
showCheckedStrategy="SHOW_PARENT"
|
||||
treeData={this.state.options.orgData}
|
||||
placeholder="请选择机构"
|
||||
treeCheckable
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form.Item label="选择区域" name="grantAreaCodeList">
|
||||
<TreeSelect
|
||||
showCheckedStrategy="SHOW_PARENT"
|
||||
treeData={this.state.options.areaData}
|
||||
placeholder="请选择所属区域"
|
||||
treeCheckable
|
||||
/>
|
||||
</Form.Item>
|
||||
</div>
|
||||
</Spin>
|
||||
</Form>
|
||||
)
|
||||
}
|
||||
}
|
||||
294
framework/web-react/src/pages/business/house/member/form.jsx
Normal file
294
framework/web-react/src/pages/business/house/member/form.jsx
Normal file
@@ -0,0 +1,294 @@
|
||||
import React, { Component } from 'react'
|
||||
import { Form, Input, DatePicker, Select, Radio, Spin, TreeSelect } from 'antd'
|
||||
import { AntIcon } from 'components'
|
||||
import { cloneDeep } from 'lodash'
|
||||
import { api } from 'common/api'
|
||||
import moment from 'moment'
|
||||
|
||||
const initialValues = {
|
||||
sex: 0,
|
||||
sysEmpParam: {},
|
||||
}
|
||||
|
||||
export default class form extends Component {
|
||||
state = {
|
||||
// 加载状态
|
||||
loading: true,
|
||||
lockRole: false,
|
||||
options: {
|
||||
orgData: [],
|
||||
roleData: [],
|
||||
},
|
||||
}
|
||||
|
||||
// 表单实例
|
||||
form = React.createRef()
|
||||
|
||||
// 初始化数据
|
||||
record = {}
|
||||
|
||||
/**
|
||||
* mount后回调
|
||||
*/
|
||||
componentDidMount() {
|
||||
this.props.created && this.props.created(this)
|
||||
}
|
||||
|
||||
/**
|
||||
* 填充数据
|
||||
* 可以在设置this.record之后对其作出数据结构调整
|
||||
* [异步,必要]
|
||||
* @param {*} params
|
||||
*/
|
||||
async fillData(params) {
|
||||
//#region 从后端转换成前段所需格式
|
||||
if (params.id) {
|
||||
this.record = (await api.houseMemberDetail({ id: params.id })).data
|
||||
}
|
||||
const orgData = await this.loadOrgData()
|
||||
const roleData = await this.LoadRoleData()
|
||||
|
||||
// 日期特殊处理
|
||||
if (this.record.birthday) {
|
||||
this.record.birthday = moment(this.record.birthday)
|
||||
}
|
||||
|
||||
// 提交的时候是"param",而获取下来却是"info",在这里转换一下
|
||||
if (this.record.sysEmpInfo) {
|
||||
this.record.sysEmpParam = this.record.sysEmpInfo
|
||||
delete this.record.sysEmpInfo
|
||||
} else if (!this.record.sysEmpParam) {
|
||||
this.record.sysEmpParam = {
|
||||
extIds: [],
|
||||
}
|
||||
}
|
||||
|
||||
if (params.orgId) {
|
||||
this.record.sysEmpParam.orgId = params.orgId
|
||||
}
|
||||
const defaultRole = params.id
|
||||
? await this.loadOwnRole(params.id)
|
||||
: await this.loadDefaultRole(params.orgId)
|
||||
if (defaultRole.constructor === Array) {
|
||||
this.record.roleId = defaultRole[0]
|
||||
} else {
|
||||
this.record.roleId = defaultRole.id
|
||||
}
|
||||
const lockRole = this.doLockRole(defaultRole)
|
||||
|
||||
this.setState({
|
||||
options: {
|
||||
orgData,
|
||||
roleData,
|
||||
},
|
||||
lockRole,
|
||||
})
|
||||
|
||||
this.record = {
|
||||
...this.record,
|
||||
}
|
||||
//#endregion
|
||||
this.form.current.setFieldsValue(this.record)
|
||||
|
||||
this.setState({
|
||||
loading: false,
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取数据
|
||||
* 可以对postData进行数据结构调整
|
||||
* [异步,必要]
|
||||
* @returns
|
||||
*/
|
||||
async getData() {
|
||||
const form = this.form.current
|
||||
|
||||
const valid = await form.validateFields()
|
||||
if (valid) {
|
||||
const postData = form.getFieldsValue()
|
||||
if (this.record) {
|
||||
postData.id = this.record.id
|
||||
}
|
||||
//#region 从前段转换后端所需格式
|
||||
//console.log(postData)
|
||||
//#endregion
|
||||
return postData
|
||||
}
|
||||
}
|
||||
|
||||
//#region 自定义方法
|
||||
async loadOrgData() {
|
||||
const { data } = await api.getOrgTree()
|
||||
return data
|
||||
}
|
||||
|
||||
async LoadRoleData() {
|
||||
const { data } = await api.houseMemberDefaultRoleList()
|
||||
return data
|
||||
}
|
||||
async loadOwnRole(id) {
|
||||
const { data } = await api.houseMemberOwnRole({ id })
|
||||
return data
|
||||
}
|
||||
async loadDefaultRole(orgId) {
|
||||
const { data } = await api.houseMemberDefaultRole({ orgId })
|
||||
return data
|
||||
}
|
||||
async onOrgChange(orgId) {
|
||||
this.setState({ loading: true })
|
||||
const defaultRole = await this.loadDefaultRole(orgId)
|
||||
const lockRole = this.doLockRole(defaultRole)
|
||||
this.setState({ loading: false, lockRole })
|
||||
}
|
||||
|
||||
doLockRole(defaultRole) {
|
||||
if (defaultRole.constructor === Array) {
|
||||
this.form.current.setFieldsValue({
|
||||
roleId: defaultRole[0].id,
|
||||
})
|
||||
return true
|
||||
} else {
|
||||
this.form.current.setFieldsValue({
|
||||
roleId: defaultRole.id,
|
||||
})
|
||||
return defaultRole.code === 'zone_manager'
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Form initialValues={initialValues} ref={this.form} className="yo-form">
|
||||
<Spin spinning={this.state.loading} indicator={<AntIcon type="loading" />}>
|
||||
<div className="yo-form-group">
|
||||
<Form.Item
|
||||
label="所属组织机构"
|
||||
name={['sysEmpParam', 'orgId']}
|
||||
rules={[{ required: true, message: '所属组织机构' }]}
|
||||
>
|
||||
<TreeSelect
|
||||
treeData={this.state.options.orgData}
|
||||
dropdownStyle={{ maxHeight: '300px', overflow: 'auto' }}
|
||||
treeDefaultExpandAll
|
||||
placeholder="请选择所属组织机构"
|
||||
onChange={value => this.onOrgChange(value)}
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label="角色"
|
||||
name="roleId"
|
||||
rules={[{ required: true, message: '请选择角色' }]}
|
||||
tooltip="片区内第一个用户必定为片区监管员,创建后不可更改角色"
|
||||
>
|
||||
<Select placeholder="请选择角色" disabled={this.state.lockRole}>
|
||||
{this.state.options.roleData.map(item => {
|
||||
return (
|
||||
<Select.Option key={item.id} value={item.id}>
|
||||
{item.name}
|
||||
</Select.Option>
|
||||
)
|
||||
})}
|
||||
</Select>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label="账号"
|
||||
name="account"
|
||||
rules={[{ required: true, message: '请输入账号', trigger: 'blur' }]}
|
||||
>
|
||||
<Input autoComplete="off" placeholder="请输入账号" />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label="姓名"
|
||||
name="name"
|
||||
rules={[{ required: true, message: '请输入姓名', trigger: 'blur' }]}
|
||||
>
|
||||
<Input autoComplete="off" placeholder="请输入姓名" />
|
||||
</Form.Item>
|
||||
{/* {this.props.mode == 'add' && (
|
||||
<>
|
||||
<Form.Item
|
||||
label="密码"
|
||||
name="password"
|
||||
rules={[
|
||||
{ required: true, message: '请输入密码', trigger: 'blur' },
|
||||
]}
|
||||
>
|
||||
<Input.Password autoComplete="off" placeholder="请输入密码" />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label="确认密码"
|
||||
name="confirm"
|
||||
rules={[
|
||||
{ required: true, message: '请确认密码', trigger: 'blur' },
|
||||
]}
|
||||
>
|
||||
<Input.Password autoComplete="off" placeholder="请确认密码" />
|
||||
</Form.Item>
|
||||
</>
|
||||
)} */}
|
||||
<Form.Item label="昵称" name="nickName">
|
||||
<Input autoComplete="off" placeholder="请输入昵称" />
|
||||
</Form.Item>
|
||||
<Form.Item label="生日" name="birthday">
|
||||
<DatePicker className="w-100-p" />
|
||||
</Form.Item>
|
||||
<Form.Item label="性别" name="sex">
|
||||
<Radio.Group>
|
||||
<Radio.Button value={0}>
|
||||
<AntIcon className="mr-xxs" type="stop" />
|
||||
<span>保密</span>
|
||||
</Radio.Button>
|
||||
<Radio.Button value={1}>
|
||||
<AntIcon
|
||||
style={{ color: '#1890ff' }}
|
||||
className="mr-xxs"
|
||||
type="man"
|
||||
/>
|
||||
<span>男</span>
|
||||
</Radio.Button>
|
||||
<Radio.Button value={2}>
|
||||
<AntIcon
|
||||
style={{ color: '#eb2f96' }}
|
||||
className="mr-xxs"
|
||||
type="woman"
|
||||
/>
|
||||
<span>女</span>
|
||||
</Radio.Button>
|
||||
</Radio.Group>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label="邮箱"
|
||||
name="email"
|
||||
rules={[
|
||||
{
|
||||
pattern: /^\w{3,}(\.\w+)*@[A-z0-9]+(\.[A-z]{2,5}){1,2}$/,
|
||||
message: '邮箱格式不正确',
|
||||
trigger: 'blur',
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Input autoComplete="off" placeholder="请输入邮箱" />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label="手机号"
|
||||
name="phone"
|
||||
rules={[
|
||||
{
|
||||
pattern:
|
||||
/^((13[0-9])|(14[5,7])|(15[^4,\\D])|(17[0,1,3,6-8])|(18[0-9])|(19[8,9])|(166))[0-9]{8}$/,
|
||||
message: '手机号格式不正确',
|
||||
trigger: 'blur',
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Input autoComplete="off" placeholder="请输入手机号" maxLength={11} />
|
||||
</Form.Item>
|
||||
<Form.Item label="电话" name="tel">
|
||||
<Input autoComplete="off" placeholder="请输入电话" />
|
||||
</Form.Item>
|
||||
</div>
|
||||
</Spin>
|
||||
</Form>
|
||||
)
|
||||
}
|
||||
}
|
||||
417
framework/web-react/src/pages/business/house/member/index.jsx
Normal file
417
framework/web-react/src/pages/business/house/member/index.jsx
Normal file
@@ -0,0 +1,417 @@
|
||||
import React, { Component } from 'react'
|
||||
import {
|
||||
Button,
|
||||
Card,
|
||||
Descriptions,
|
||||
Divider,
|
||||
Form,
|
||||
Input,
|
||||
List,
|
||||
message as Message,
|
||||
Popconfirm,
|
||||
Select,
|
||||
Switch,
|
||||
Tag,
|
||||
} from 'antd'
|
||||
import {
|
||||
AntIcon,
|
||||
Auth,
|
||||
Container,
|
||||
Image,
|
||||
ModalForm,
|
||||
QueryList,
|
||||
QueryTableActions,
|
||||
QueryTreeLayout,
|
||||
} from 'components'
|
||||
import { api } from 'common/api'
|
||||
import { toCamelCase } from 'util/format'
|
||||
import { isEqual } from 'lodash'
|
||||
import getDictData from 'util/dic'
|
||||
import FormBody from './form'
|
||||
import Selector from './selector'
|
||||
import DataForm from './data'
|
||||
|
||||
// 配置页面所需接口函数
|
||||
const apiAction = {
|
||||
tree: api.getOrgTree,
|
||||
page: api.houseMemberPage,
|
||||
add: api.houseMemberAdd,
|
||||
edit: api.houseMemberEdit,
|
||||
delete: api.houseMemberDelete,
|
||||
|
||||
changeStatus: api.houseMemberChangeStatus,
|
||||
resetPwd: api.sysUserResetPwd,
|
||||
|
||||
grantData: api.houseMemberGrantData,
|
||||
}
|
||||
|
||||
// 用于弹窗标题
|
||||
const name = '人员'
|
||||
|
||||
export default class index extends Component {
|
||||
state = {
|
||||
codes: {
|
||||
sex: [],
|
||||
commonStatus: [],
|
||||
},
|
||||
}
|
||||
|
||||
// 表格实例
|
||||
list = React.createRef()
|
||||
|
||||
// 新增窗口实例
|
||||
addForm = React.createRef()
|
||||
// 编辑窗口实例
|
||||
editForm = React.createRef()
|
||||
|
||||
dataForm = React.createRef()
|
||||
// 树选中节点
|
||||
selectId = undefined
|
||||
|
||||
selectorModal = React.createRef()
|
||||
|
||||
/**
|
||||
* 阻止外部组件引发的渲染,提升性能
|
||||
* 可自行添加渲染条件
|
||||
* [必要]
|
||||
* @param {*} props
|
||||
* @param {*} state
|
||||
* @returns
|
||||
*/
|
||||
shouldComponentUpdate(props, state) {
|
||||
return !isEqual(this.state, state)
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载字典数据,之后开始加载表格数据
|
||||
* 如果必须要加载字典数据,可直接对表格设置autoLoad=true
|
||||
*/
|
||||
componentDidMount() {
|
||||
this.list.current.onLoading()
|
||||
getDictData('sex', 'common_status').then(codes => {
|
||||
this.setState({ codes }, () => {
|
||||
this.list.current.onLoadData()
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 调用加载数据接口,可在调用前对query进行处理
|
||||
* [异步,必要]
|
||||
* @param {*} params
|
||||
* @param {*} query
|
||||
* @returns
|
||||
*/
|
||||
loadData = async (params, query) => {
|
||||
query = {
|
||||
...query,
|
||||
sysEmpParam: {
|
||||
orgId: this.selectId,
|
||||
},
|
||||
}
|
||||
|
||||
const { data } = await apiAction.page({
|
||||
...params,
|
||||
...query,
|
||||
})
|
||||
return data
|
||||
}
|
||||
|
||||
/**
|
||||
* 调用树结构数据接口
|
||||
* [异步,必要]
|
||||
* @returns
|
||||
*/
|
||||
loadTreeData = async () => {
|
||||
const { data } = await apiAction.tree()
|
||||
return data
|
||||
}
|
||||
|
||||
/**
|
||||
* 树节点选中事件
|
||||
* [必要]
|
||||
* @param {*} id
|
||||
*/
|
||||
onSelectTree(id) {
|
||||
this.selectId = id
|
||||
this.list.current.onReloadData()
|
||||
}
|
||||
|
||||
/**
|
||||
* 绑定字典数据
|
||||
* @param {*} code
|
||||
* @param {*} name
|
||||
* @returns
|
||||
*/
|
||||
bindCodeValue(code, name) {
|
||||
name = toCamelCase(name)
|
||||
const codes = this.state.codes[name]
|
||||
if (codes) {
|
||||
const c = codes.find(p => p.code == code)
|
||||
if (c) {
|
||||
return c.value
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
/**
|
||||
* 打开新增/编辑弹窗
|
||||
* @param {*} modal
|
||||
* @param {*} id
|
||||
*/
|
||||
onOpen(modal, id) {
|
||||
modal.current.open({
|
||||
orgId: this.selectId,
|
||||
id,
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 对表格上的操作进行统一处理
|
||||
* [异步]
|
||||
* @param {*} action
|
||||
* @param {*} successMessage
|
||||
*/
|
||||
async onAction(action, successMessage) {
|
||||
this.list.current.onLoading()
|
||||
try {
|
||||
await action
|
||||
Message.success(successMessage)
|
||||
this.list.current.onReloadData()
|
||||
} catch {
|
||||
this.list.current.onLoaded()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除
|
||||
* @param {*} id
|
||||
*/
|
||||
onDelete(id) {
|
||||
this.onAction(apiAction.delete({ id }), '删除成功')
|
||||
}
|
||||
|
||||
//#region 自定义方法
|
||||
renderItem(record) {
|
||||
const {
|
||||
id,
|
||||
account,
|
||||
name,
|
||||
nickName,
|
||||
avatar,
|
||||
sex,
|
||||
phone,
|
||||
email,
|
||||
status,
|
||||
roleCode,
|
||||
roleName,
|
||||
orgName,
|
||||
} = record
|
||||
return (
|
||||
<List.Item
|
||||
key={id}
|
||||
actions={[
|
||||
<QueryTableActions>
|
||||
<Auth auth="houseMember:edit">
|
||||
<a onClick={() => this.onOpen(this.editForm, id)}>编辑</a>
|
||||
</Auth>
|
||||
<Auth auth="houseMember:delete">
|
||||
<Popconfirm
|
||||
placement="topRight"
|
||||
title="是否确认删除"
|
||||
onConfirm={() => this.onDelete(id)}
|
||||
>
|
||||
<a>删除</a>
|
||||
</Popconfirm>
|
||||
</Auth>
|
||||
<Auth auth="houseMember:resetPwd">
|
||||
<a onClick={() => this.onResetPassword(id)}>重置密码</a>
|
||||
</Auth>
|
||||
<Auth auth="houseMember:grantData">
|
||||
<a onClick={() => this.onOpen(this.dataForm, id)}>授权额外数据</a>
|
||||
</Auth>
|
||||
</QueryTableActions>,
|
||||
]}
|
||||
>
|
||||
<List.Item.Meta
|
||||
avatar={
|
||||
<>
|
||||
<Image
|
||||
type="avatar"
|
||||
shape="square"
|
||||
size={48}
|
||||
id={avatar}
|
||||
icon={<AntIcon type="user" />}
|
||||
/>
|
||||
{roleCode && roleCode.includes('house_security_manager') && (
|
||||
<Button
|
||||
size="small"
|
||||
type="primary"
|
||||
className="block w-100-p mt-xxs"
|
||||
onClick={() => this.onOpen(this.selectorModal, id)}
|
||||
>
|
||||
选房
|
||||
</Button>
|
||||
)}
|
||||
</>
|
||||
}
|
||||
title={
|
||||
<>
|
||||
{nickName || name}
|
||||
{roleName &&
|
||||
roleName.split(',').map((item, i) => (
|
||||
<span key={i}>
|
||||
<Divider type="vertical" />
|
||||
<Tag color="pink">{item}</Tag>
|
||||
</span>
|
||||
))}
|
||||
</>
|
||||
}
|
||||
description={account}
|
||||
/>
|
||||
<Descriptions className="flex-1" column={2}>
|
||||
<Descriptions.Item label="部门">{orgName}</Descriptions.Item>
|
||||
<Descriptions.Item label="性别">
|
||||
{this.bindCodeValue(sex, 'sex')}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="手机">{phone || '未设置'}</Descriptions.Item>
|
||||
<Descriptions.Item label="邮箱">{email || '未设置'}</Descriptions.Item>
|
||||
</Descriptions>
|
||||
<div className="yo-list-content--h">
|
||||
<Auth auth="houseMember:changeStatus">
|
||||
<div className="yo-list-content--h--item text-center">
|
||||
<Switch
|
||||
checked={!status}
|
||||
checkedChildren={this.bindCodeValue(0, 'common_status')}
|
||||
unCheckedChildren={this.bindCodeValue(1, 'common_status')}
|
||||
onChange={checked => this.onSetUserStatus(id, checked)}
|
||||
/>
|
||||
</div>
|
||||
</Auth>
|
||||
</div>
|
||||
</List.Item>
|
||||
)
|
||||
}
|
||||
|
||||
onSetUserStatus(id, checked) {
|
||||
this.onAction(
|
||||
apiAction.changeStatus({
|
||||
id,
|
||||
status: +!checked,
|
||||
}),
|
||||
'设置成功'
|
||||
)
|
||||
}
|
||||
|
||||
onResetPassword(id) {
|
||||
this.onAction(apiAction.resetPwd({ id }), '重置成功')
|
||||
}
|
||||
//#endregion
|
||||
|
||||
render() {
|
||||
return (
|
||||
<QueryTreeLayout
|
||||
loadData={this.loadTreeData}
|
||||
defaultExpanded={true}
|
||||
onSelect={key => this.onSelectTree(key)}
|
||||
>
|
||||
<Container mode="fluid">
|
||||
<Card bordered={false}>
|
||||
<QueryList
|
||||
ref={this.list}
|
||||
autoLoad={false}
|
||||
loadData={this.loadData}
|
||||
query={
|
||||
<Auth auth="houseMember:page">
|
||||
<Form.Item label="关键词" name="searchValue">
|
||||
<Input
|
||||
autoComplete="off"
|
||||
placeholder="请输入姓名、账号、手机号"
|
||||
className="w-200"
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form.Item label="状态" name="searchStatus">
|
||||
<Select
|
||||
allowClear
|
||||
placeholder="请选择状态"
|
||||
className="w-200"
|
||||
>
|
||||
{this.state.codes.commonStatus.map(item => {
|
||||
return (
|
||||
<Select.Option
|
||||
key={item.code}
|
||||
value={item.code}
|
||||
>
|
||||
{item.value}
|
||||
</Select.Option>
|
||||
)
|
||||
})}
|
||||
</Select>
|
||||
</Form.Item>
|
||||
<Form.Item label="区域范围" name="treeNodeDataScope">
|
||||
<Select
|
||||
allowClear
|
||||
placeholder="请选择状态"
|
||||
className="w-200"
|
||||
defaultValue={1}
|
||||
>
|
||||
<Select.Option value={1}>只看本级</Select.Option>
|
||||
<Select.Option value={2}>查看本级及以下</Select.Option>
|
||||
</Select>
|
||||
</Form.Item>
|
||||
</Auth>
|
||||
}
|
||||
operator={
|
||||
<Button
|
||||
icon={<AntIcon type="plus" />}
|
||||
onClick={() => this.onOpen(this.addForm)}
|
||||
>
|
||||
新增
|
||||
{name}
|
||||
</Button>
|
||||
}
|
||||
renderItem={record => this.renderItem(record)}
|
||||
/>
|
||||
</Card>
|
||||
</Container>
|
||||
|
||||
<ModalForm
|
||||
title={`新增${name}`}
|
||||
action={apiAction.add}
|
||||
ref={this.addForm}
|
||||
onSuccess={() => this.list.current.onReloadData()}
|
||||
>
|
||||
<FormBody mode="add" />
|
||||
</ModalForm>
|
||||
|
||||
<ModalForm
|
||||
title={`编辑${name}`}
|
||||
action={apiAction.edit}
|
||||
ref={this.editForm}
|
||||
onSuccess={() => this.list.current.onReloadData()}
|
||||
>
|
||||
<FormBody mode="edit" />
|
||||
</ModalForm>
|
||||
|
||||
<ModalForm
|
||||
title="数据授权"
|
||||
action={apiAction.grantData}
|
||||
ref={this.dataForm}
|
||||
onSuccess={() => this.list.current.onReloadData()}
|
||||
>
|
||||
<DataForm />
|
||||
</ModalForm>
|
||||
|
||||
<ModalForm
|
||||
bodyStyle={{ padding: 0 }}
|
||||
footer={false}
|
||||
width="80%"
|
||||
ref={this.selectorModal}
|
||||
>
|
||||
<Selector />
|
||||
</ModalForm>
|
||||
</QueryTreeLayout>
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
import React, { Component } from 'react'
|
||||
import { Tabs } from 'antd'
|
||||
import SelectorList from './selector-list'
|
||||
import SelectedList from './selected-list'
|
||||
|
||||
export default class index extends Component {
|
||||
state = {
|
||||
userId: '',
|
||||
}
|
||||
|
||||
selectorList = React.createRef()
|
||||
selectedList = React.createRef()
|
||||
|
||||
/**
|
||||
* mount后回调
|
||||
*/
|
||||
componentDidMount() {
|
||||
this.props.created && this.props.created(this)
|
||||
}
|
||||
|
||||
/**
|
||||
* 填充数据
|
||||
* 可以在设置this.record之后对其作出数据结构调整
|
||||
* [异步,必要]
|
||||
* @param {*} params
|
||||
*/
|
||||
async fillData(params) {
|
||||
this.setState({
|
||||
userId: params.id,
|
||||
})
|
||||
}
|
||||
|
||||
//#region 自定义方法
|
||||
onReloadAll() {
|
||||
if (this.selectorList.current) {
|
||||
this.selectorList.current.table.current.onReloadData()
|
||||
}
|
||||
if (this.selectedList.current) {
|
||||
this.selectedList.current.table.current.onReloadData()
|
||||
}
|
||||
}
|
||||
//#endregion
|
||||
|
||||
render() {
|
||||
const { userId } = this.state
|
||||
|
||||
return (
|
||||
<Tabs tabBarStyle={{ padding: '0 24px' }}>
|
||||
<Tabs.TabPane key="1" tab="选房">
|
||||
<SelectorList
|
||||
userId={userId}
|
||||
ref={this.selectorList}
|
||||
onReloadAll={() => this.onReloadAll()}
|
||||
/>
|
||||
</Tabs.TabPane>
|
||||
<Tabs.TabPane key="2" tab="已选">
|
||||
<SelectedList
|
||||
userId={userId}
|
||||
ref={this.selectedList}
|
||||
onReloadAll={() => this.onReloadAll()}
|
||||
/>
|
||||
</Tabs.TabPane>
|
||||
</Tabs>
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,263 @@
|
||||
import React, { Component } from 'react'
|
||||
import { Button, Card, Form, Input, InputNumber, message as Message, Radio, Select } from 'antd'
|
||||
import { AntIcon, Auth, Container, QueryTable } from 'components'
|
||||
import { api } from 'common/api'
|
||||
import { isEqual } from 'lodash'
|
||||
import getDictData from 'util/dic'
|
||||
import { toCamelCase } from 'util/format'
|
||||
import { getSearchInfo } from 'util/query'
|
||||
|
||||
/**
|
||||
* 注释段[\/**\/]为必须要改
|
||||
*/
|
||||
|
||||
/**
|
||||
* 配置页面所需接口函数
|
||||
*/
|
||||
const apiAction = {
|
||||
page: api.houseSelectedPage,
|
||||
}
|
||||
|
||||
/**
|
||||
* 统一配置权限标识
|
||||
* [必要]
|
||||
*/
|
||||
const authName = 'houseSelector'
|
||||
|
||||
export default class index extends Component {
|
||||
state = {
|
||||
codes: {
|
||||
houseType: [],
|
||||
houseIndustry: [],
|
||||
},
|
||||
|
||||
saving: false,
|
||||
|
||||
type: '',
|
||||
|
||||
selectedRowKeys: [],
|
||||
}
|
||||
|
||||
// 表格实例
|
||||
table = React.createRef()
|
||||
|
||||
columns = [
|
||||
{
|
||||
title: '房屋编码',
|
||||
dataIndex: 'houseCode',
|
||||
sorter: true,
|
||||
width: 300,
|
||||
render: (text, record) =>
|
||||
`${record.areaName}-${record.roadName}-${record.commName}-${
|
||||
record.fullProjName
|
||||
}-${record.no.toString().padStart(3, '0')}`,
|
||||
},
|
||||
{
|
||||
title: '房屋性质及行业',
|
||||
dataIndex: 'type',
|
||||
sorter: true,
|
||||
width: 150,
|
||||
render: text => this.bindCodeValue(text, 'house_type'),
|
||||
},
|
||||
{
|
||||
title: '地址',
|
||||
dataIndex: 'address',
|
||||
sorter: true,
|
||||
},
|
||||
{
|
||||
title: '登记时间',
|
||||
dataIndex: 'createdTime',
|
||||
sorter: true,
|
||||
width: 150,
|
||||
},
|
||||
]
|
||||
|
||||
/**
|
||||
* 阻止外部组件引发的渲染,提升性能
|
||||
* 可自行添加渲染条件
|
||||
* [必要]
|
||||
* @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 searchInfo = getSearchInfo({
|
||||
query,
|
||||
queryType: { type: '=' },
|
||||
})
|
||||
|
||||
const { data } = await apiAction.page({
|
||||
...params,
|
||||
searchInfo,
|
||||
userId: this.props.userId,
|
||||
})
|
||||
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 {*} 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 自定义方法
|
||||
async onHouseSelectRevoke() {
|
||||
const { selectedRowKeys } = this.state
|
||||
const { userId, onReloadAll } = this.props
|
||||
this.setState({ saving: true })
|
||||
await this.onAction(
|
||||
api.houseSelectRevoke({
|
||||
ids: selectedRowKeys,
|
||||
userId,
|
||||
}),
|
||||
'撤销成功'
|
||||
)
|
||||
this.setState({
|
||||
saving: false,
|
||||
selectedRowKeys: [],
|
||||
})
|
||||
if (onReloadAll) {
|
||||
onReloadAll()
|
||||
}
|
||||
}
|
||||
//#endregion
|
||||
|
||||
render() {
|
||||
const { codes, saving, type, selectedRowKeys } = this.state
|
||||
|
||||
return (
|
||||
<Card bordered={false} className="mb-none">
|
||||
<QueryTable
|
||||
ref={this.table}
|
||||
autoLoad={false}
|
||||
loadData={this.loadData}
|
||||
columns={this.columns}
|
||||
rowSelection={{
|
||||
selectedRowKeys,
|
||||
onChange: selectedRowKeys => this.setState({ selectedRowKeys }),
|
||||
}}
|
||||
queryInitialValues={{
|
||||
type: '',
|
||||
}}
|
||||
onQueryChange={values => {
|
||||
if (values.hasOwnProperty('type')) {
|
||||
this.setState({ type: values.type })
|
||||
}
|
||||
}}
|
||||
query={
|
||||
<Auth auth={{ [authName]: 'selectorPage' }}>
|
||||
<Form.Item label="编号" name="no">
|
||||
<InputNumber
|
||||
formatter={value => value && value.padStart(3, '0')}
|
||||
max={999}
|
||||
min={1}
|
||||
precision={0}
|
||||
step={1}
|
||||
placeholder="请输入房屋序号"
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form.Item label="房屋性质" name="type">
|
||||
<Radio.Group buttonStyle="solid">
|
||||
<Radio.Button value="">全部</Radio.Button>
|
||||
{codes.houseType.map(item => (
|
||||
<Radio.Button key={item.code} value={item.code}>
|
||||
{item.value}
|
||||
</Radio.Button>
|
||||
))}
|
||||
</Radio.Group>
|
||||
</Form.Item>
|
||||
{type == 2 && (
|
||||
<Form.Item label="行业" name="industry">
|
||||
<Select allowClear className="w-150" placeholder="请选择行业">
|
||||
{codes.houseIndustry.map(item => (
|
||||
<Select.Option key={item.code} value={item.code}>
|
||||
{item.value}
|
||||
</Select.Option>
|
||||
))}
|
||||
</Select>
|
||||
</Form.Item>
|
||||
)}
|
||||
<Form.Item label="地址" name="address">
|
||||
<Input autoComplete="off" placeholder="请输入地址" />
|
||||
</Form.Item>
|
||||
<Form.Item label="房屋唯一编码" name="houseCode">
|
||||
<Input autoComplete="off" placeholder="请输入房屋唯一编码" />
|
||||
</Form.Item>
|
||||
</Auth>
|
||||
}
|
||||
operator={
|
||||
<Auth auth={{ [authName]: 'select' }}>
|
||||
<Button
|
||||
loading={saving}
|
||||
disabled={!selectedRowKeys.length}
|
||||
type="danger"
|
||||
onClick={() => this.onHouseSelectRevoke()}
|
||||
>
|
||||
撤销
|
||||
</Button>
|
||||
</Auth>
|
||||
}
|
||||
/>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,263 @@
|
||||
import React, { Component } from 'react'
|
||||
import { Button, Card, Form, Input, InputNumber, message as Message, Radio, Select } from 'antd'
|
||||
import { AntIcon, Auth, Container, QueryTable } from 'components'
|
||||
import { api } from 'common/api'
|
||||
import { isEqual } from 'lodash'
|
||||
import getDictData from 'util/dic'
|
||||
import { toCamelCase } from 'util/format'
|
||||
import { getSearchInfo } from 'util/query'
|
||||
|
||||
/**
|
||||
* 注释段[\/**\/]为必须要改
|
||||
*/
|
||||
|
||||
/**
|
||||
* 配置页面所需接口函数
|
||||
*/
|
||||
const apiAction = {
|
||||
page: api.houseSelectorPage,
|
||||
}
|
||||
|
||||
/**
|
||||
* 统一配置权限标识
|
||||
* [必要]
|
||||
*/
|
||||
const authName = 'houseSelector'
|
||||
|
||||
export default class index extends Component {
|
||||
state = {
|
||||
codes: {
|
||||
houseType: [],
|
||||
houseIndustry: [],
|
||||
},
|
||||
|
||||
saving: false,
|
||||
|
||||
type: '',
|
||||
|
||||
selectedRowKeys: [],
|
||||
}
|
||||
|
||||
// 表格实例
|
||||
table = React.createRef()
|
||||
|
||||
columns = [
|
||||
{
|
||||
title: '房屋编码',
|
||||
dataIndex: 'houseCode',
|
||||
sorter: true,
|
||||
width: 300,
|
||||
render: (text, record) =>
|
||||
`${record.areaName}-${record.roadName}-${record.commName}-${
|
||||
record.fullProjName
|
||||
}-${record.no.toString().padStart(3, '0')}`,
|
||||
},
|
||||
{
|
||||
title: '房屋性质及行业',
|
||||
dataIndex: 'type',
|
||||
sorter: true,
|
||||
width: 150,
|
||||
render: text => this.bindCodeValue(text, 'house_type'),
|
||||
},
|
||||
{
|
||||
title: '地址',
|
||||
dataIndex: 'address',
|
||||
sorter: true,
|
||||
},
|
||||
{
|
||||
title: '登记时间',
|
||||
dataIndex: 'createdTime',
|
||||
sorter: true,
|
||||
width: 150,
|
||||
},
|
||||
]
|
||||
|
||||
/**
|
||||
* 阻止外部组件引发的渲染,提升性能
|
||||
* 可自行添加渲染条件
|
||||
* [必要]
|
||||
* @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 searchInfo = getSearchInfo({
|
||||
query,
|
||||
queryType: { type: '=' },
|
||||
})
|
||||
|
||||
const { data } = await apiAction.page({
|
||||
...params,
|
||||
searchInfo,
|
||||
userId: this.props.userId,
|
||||
})
|
||||
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 {*} 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 自定义方法
|
||||
async onHouseSelect() {
|
||||
const { selectedRowKeys } = this.state
|
||||
const { userId, onReloadAll } = this.props
|
||||
this.setState({ saving: true })
|
||||
await this.onAction(
|
||||
api.houseSelect({
|
||||
ids: selectedRowKeys,
|
||||
userId,
|
||||
}),
|
||||
'选房成功'
|
||||
)
|
||||
this.setState({
|
||||
saving: false,
|
||||
selectedRowKeys: [],
|
||||
})
|
||||
if (onReloadAll) {
|
||||
onReloadAll()
|
||||
}
|
||||
}
|
||||
//#endregion
|
||||
|
||||
render() {
|
||||
const { codes, saving, type, selectedRowKeys } = this.state
|
||||
|
||||
return (
|
||||
<Card bordered={false} className="mb-none">
|
||||
<QueryTable
|
||||
ref={this.table}
|
||||
autoLoad={false}
|
||||
loadData={this.loadData}
|
||||
columns={this.columns}
|
||||
rowSelection={{
|
||||
selectedRowKeys,
|
||||
onChange: selectedRowKeys => this.setState({ selectedRowKeys }),
|
||||
}}
|
||||
queryInitialValues={{
|
||||
type: '',
|
||||
}}
|
||||
onQueryChange={values => {
|
||||
if (values.hasOwnProperty('type')) {
|
||||
this.setState({ type: values.type })
|
||||
}
|
||||
}}
|
||||
query={
|
||||
<Auth auth={{ [authName]: 'selectorPage' }}>
|
||||
<Form.Item label="编号" name="no">
|
||||
<InputNumber
|
||||
formatter={value => value && value.padStart(3, '0')}
|
||||
max={999}
|
||||
min={1}
|
||||
precision={0}
|
||||
step={1}
|
||||
placeholder="请输入房屋序号"
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form.Item label="房屋性质" name="type">
|
||||
<Radio.Group buttonStyle="solid">
|
||||
<Radio.Button value="">全部</Radio.Button>
|
||||
{codes.houseType.map(item => (
|
||||
<Radio.Button key={item.code} value={item.code}>
|
||||
{item.value}
|
||||
</Radio.Button>
|
||||
))}
|
||||
</Radio.Group>
|
||||
</Form.Item>
|
||||
{type == 2 && (
|
||||
<Form.Item label="行业" name="industry">
|
||||
<Select allowClear className="w-150" placeholder="请选择行业">
|
||||
{codes.houseIndustry.map(item => (
|
||||
<Select.Option key={item.code} value={item.code}>
|
||||
{item.value}
|
||||
</Select.Option>
|
||||
))}
|
||||
</Select>
|
||||
</Form.Item>
|
||||
)}
|
||||
<Form.Item label="地址" name="address">
|
||||
<Input autoComplete="off" placeholder="请输入地址" />
|
||||
</Form.Item>
|
||||
<Form.Item label="房屋唯一编码" name="houseCode">
|
||||
<Input autoComplete="off" placeholder="请输入房屋唯一编码" />
|
||||
</Form.Item>
|
||||
</Auth>
|
||||
}
|
||||
operator={
|
||||
<Auth auth={{ [authName]: 'select' }}>
|
||||
<Button
|
||||
loading={saving}
|
||||
disabled={!selectedRowKeys.length}
|
||||
type="primary"
|
||||
onClick={() => this.onHouseSelect()}
|
||||
>
|
||||
确认选择
|
||||
</Button>
|
||||
</Auth>
|
||||
}
|
||||
/>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
}
|
||||
273
framework/web-react/src/pages/business/house/project/form.jsx
Normal file
273
framework/web-react/src/pages/business/house/project/form.jsx
Normal file
@@ -0,0 +1,273 @@
|
||||
import React, { Component } from 'react'
|
||||
import { Cascader, Form, Input, InputNumber, Radio, Spin, TreeSelect } from 'antd'
|
||||
import { AntIcon } from 'components'
|
||||
import { cloneDeep } from 'lodash'
|
||||
import { api } from 'common/api'
|
||||
import { numberToChinese } from 'util/format'
|
||||
|
||||
const initialValues = {
|
||||
sort: 100,
|
||||
type: 1,
|
||||
}
|
||||
export default class form extends Component {
|
||||
state = {
|
||||
// 加载状态
|
||||
loading: true,
|
||||
exist: false,
|
||||
|
||||
options: {
|
||||
areaData: [],
|
||||
},
|
||||
}
|
||||
areaCode = ''
|
||||
houseType = 1
|
||||
// 表单实例
|
||||
form = React.createRef()
|
||||
|
||||
// 初始化数据
|
||||
record = {}
|
||||
initRecord = {}
|
||||
/**
|
||||
* mount后回调
|
||||
*/
|
||||
componentDidMount() {
|
||||
this.props.created && this.props.created(this)
|
||||
}
|
||||
|
||||
/**
|
||||
* 填充数据
|
||||
* 可以在设置this.record之后对其作出数据结构调整
|
||||
* [异步,必要]
|
||||
* @param {*} params
|
||||
*/
|
||||
async fillData(params) {
|
||||
let areaCodeDefault = params.pid ? params.pid : ''
|
||||
this.houseType = params.record ? params.record.type : 1
|
||||
if (params.id) {
|
||||
this.setState({
|
||||
loading: true,
|
||||
})
|
||||
|
||||
api.houseProjectGetById({ projectId: params.id }).then(({ data }) => {
|
||||
areaCodeDefault = data.areaCode
|
||||
this.record = data
|
||||
|
||||
this.setState({
|
||||
loading: false,
|
||||
})
|
||||
})
|
||||
}
|
||||
// this.record = cloneDeep(params.record)
|
||||
this.initRecord = cloneDeep(params.record)
|
||||
//#region 从后端转换成前段所需格式
|
||||
const areaData = await this.loadAreaData()
|
||||
|
||||
this.setState({
|
||||
exist: !!params.id,
|
||||
options: { areaData },
|
||||
})
|
||||
|
||||
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 === areaCodeDefault) {
|
||||
areaCode.length = level + 1
|
||||
return true
|
||||
}
|
||||
|
||||
if (item.children && item.children.length) {
|
||||
const found = findCode(item.children, level + 1)
|
||||
if (found) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (areaCodeDefault) {
|
||||
findCode(areaData)
|
||||
this.areaCode = areaCodeDefault
|
||||
if (!this.state.exist) {
|
||||
this.nextSort(this.areaCode, this.houseType)
|
||||
}
|
||||
}
|
||||
|
||||
this.record = {
|
||||
pid: params.pid,
|
||||
...this.record,
|
||||
areaCode:
|
||||
areaCode.length > 0 && areaCode[areaCode.length - 1].length == 12 ? areaCode : [],
|
||||
}
|
||||
//#endregion
|
||||
|
||||
this.form.current.setFieldsValue(this.record)
|
||||
|
||||
this.setState({
|
||||
loading: false,
|
||||
})
|
||||
}
|
||||
/**
|
||||
* 获取数据
|
||||
* 可以对postData进行数据结构调整
|
||||
* [异步,必要]
|
||||
* @returns
|
||||
*/
|
||||
async getData() {
|
||||
const form = this.form.current
|
||||
|
||||
const valid = await form.validateFields()
|
||||
if (valid) {
|
||||
const postData = form.getFieldsValue()
|
||||
if (this.record) {
|
||||
postData.id = this.record.id
|
||||
}
|
||||
//#region 从前段转换后端所需格式
|
||||
postData.areaCode = postData.areaCode[postData.areaCode.length - 1]
|
||||
//#endregion
|
||||
return postData
|
||||
}
|
||||
}
|
||||
|
||||
async loadAreaData() {
|
||||
const { data } = await api.getAreaTree()
|
||||
console.log(data)
|
||||
const clearChiildren = data => {
|
||||
data.forEach(item => {
|
||||
if (item.children && item.children.length) {
|
||||
clearChiildren(item.children)
|
||||
} else {
|
||||
delete item.children
|
||||
}
|
||||
})
|
||||
}
|
||||
clearChiildren(data)
|
||||
return data
|
||||
}
|
||||
|
||||
async nextSort(areaCode, 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,
|
||||
})
|
||||
} else if (areaCode.length < 12) {
|
||||
this.form.current.setFieldsValue({
|
||||
name: '',
|
||||
sort: 0,
|
||||
areaCode: [],
|
||||
})
|
||||
} else {
|
||||
await api
|
||||
.houseProjectNextSort({ areaCode, type: houseType })
|
||||
.then(({ data }) => {
|
||||
this.form.current.setFieldsValue({
|
||||
name: `项目${numberToChinese(data)}`,
|
||||
sort: data,
|
||||
})
|
||||
})
|
||||
.catch(() => {
|
||||
this.form.current.setFieldsValue({
|
||||
name: '',
|
||||
sort: 0,
|
||||
areaCode: [],
|
||||
})
|
||||
})
|
||||
.finally(() => {
|
||||
this.loading = false
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
onHouseTypeChange(e) {
|
||||
this.houseType = e.target.value
|
||||
if (this.areaCode != '') {
|
||||
this.nextSort(this.areaCode, this.houseType)
|
||||
}
|
||||
}
|
||||
|
||||
onAreaCodeChange(value) {
|
||||
this.areaCode = value[value.length - 1]
|
||||
if (this.houseType > 0) {
|
||||
this.nextSort(this.areaCode, this.houseType)
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Form initialValues={initialValues} ref={this.form} className="yo-form">
|
||||
<Spin spinning={this.state.loading} indicator={<AntIcon type="loading" />}>
|
||||
<div className="yo-form-group">
|
||||
<Form.Item label="类型" name="type">
|
||||
<Radio.Group
|
||||
disabled={this.state.exist}
|
||||
buttonStyle="solid"
|
||||
onChange={e => this.onHouseTypeChange(e)}
|
||||
>
|
||||
<Radio.Button value={1}>
|
||||
<span>住宅</span>
|
||||
</Radio.Button>
|
||||
<Radio.Button value={2}>
|
||||
<span>非住宅</span>
|
||||
</Radio.Button>
|
||||
</Radio.Group>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label="所属区域"
|
||||
name="areaCode"
|
||||
rules={[{ required: true, message: '请选择所属区域' }]}
|
||||
>
|
||||
<Cascader
|
||||
disabled={this.state.exist}
|
||||
options={this.state.options.areaData}
|
||||
fieldNames={{
|
||||
label: 'name',
|
||||
value: 'code',
|
||||
children: 'children',
|
||||
}}
|
||||
expandTrigger="hover"
|
||||
// changeOnSelect
|
||||
placeholder="请选择所属区域"
|
||||
onChange={(val, selectedOptions) => this.onAreaCodeChange(val)}
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label="项目名称"
|
||||
name="name"
|
||||
rules={[{ required: true, message: '片区名称', trigger: 'blur' }]}
|
||||
>
|
||||
<Input
|
||||
autoComplete="off"
|
||||
placeholder="选择所属区域和类型之后自动生成"
|
||||
disabled={true}
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form.Item label="序号" name="sort">
|
||||
<InputNumber
|
||||
max={1000}
|
||||
min={0}
|
||||
placeholder="选择所属区域和类型之后自动生成"
|
||||
className="w-100-p"
|
||||
disabled={true}
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form.Item label="备注" name="note">
|
||||
<Input.TextArea
|
||||
rows="4"
|
||||
placeholder="填写房屋所属单位的名称、道路的名称或大厦的名称,比如XX中学、XX大厦、XX小区等。登记项目时,应在项目备注中明确项目所指对象。"
|
||||
/>
|
||||
</Form.Item>
|
||||
</div>
|
||||
</Spin>
|
||||
</Form>
|
||||
)
|
||||
}
|
||||
}
|
||||
313
framework/web-react/src/pages/business/house/project/index.jsx
Normal file
313
framework/web-react/src/pages/business/house/project/index.jsx
Normal file
@@ -0,0 +1,313 @@
|
||||
import React, { Component } from 'react'
|
||||
import { Button, Radio, Card, Form, Input, message as Message, Popconfirm } from 'antd'
|
||||
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'
|
||||
import { isEqual } from 'lodash'
|
||||
import getDictData from 'util/dic'
|
||||
import FormBody from './form'
|
||||
|
||||
const apiAction = {
|
||||
tree: api.getAreaTree,
|
||||
page: api.getHouseProjectPage,
|
||||
add: api.houseProejctAdd,
|
||||
edit: api.houseProejctEdit,
|
||||
delete: api.houseProejctDelete,
|
||||
}
|
||||
|
||||
const name = '项目'
|
||||
|
||||
export default class index extends Component {
|
||||
state = {
|
||||
codes: {
|
||||
houseType: [],
|
||||
},
|
||||
type: 1,
|
||||
}
|
||||
|
||||
// 表格实例
|
||||
table = React.createRef()
|
||||
|
||||
// 新增窗口实例
|
||||
addForm = React.createRef()
|
||||
// 编辑窗口实例
|
||||
editForm = React.createRef()
|
||||
|
||||
// 树选中节点
|
||||
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,
|
||||
width: 80,
|
||||
render: text => <>{this.bindCodeValue(text, 'house_type')}</>,
|
||||
},
|
||||
]
|
||||
/**
|
||||
* 构造函数,在渲染前动态添加操作字段等
|
||||
* @param {*} props
|
||||
*/
|
||||
constructor(props) {
|
||||
super(props)
|
||||
|
||||
const flag = auth({ houseProjectInfo: [['edit'], ['delete']] })
|
||||
|
||||
if (flag) {
|
||||
this.columns.push({
|
||||
title: '操作',
|
||||
width: 150,
|
||||
dataIndex: 'actions',
|
||||
render: (text, record) => (
|
||||
<QueryTableActions>
|
||||
<Auth auth="houseProjectInfo:edit">
|
||||
<a onClick={() => this.onOpen(this.editForm, record.id)}>编辑</a>
|
||||
</Auth>
|
||||
<Auth auth="houseProjectInfo:delete">
|
||||
<Popconfirm
|
||||
placement="topRight"
|
||||
title="是否确认删除"
|
||||
onConfirm={() => this.onDelete(record)}
|
||||
>
|
||||
<a>删除</a>
|
||||
</Popconfirm>
|
||||
</Auth>
|
||||
</QueryTableActions>
|
||||
),
|
||||
})
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 阻止外部组件引发的渲染,提升性能
|
||||
* 可自行添加渲染条件
|
||||
* [必要]
|
||||
* @param {*} props
|
||||
* @param {*} state
|
||||
* @returns
|
||||
*/
|
||||
shouldComponentUpdate(props, state) {
|
||||
return !isEqual(this.state, state)
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载字典数据,之后开始加载表格数据
|
||||
* 如果必须要加载字典数据,可直接对表格设置autoLoad=true
|
||||
*/
|
||||
componentDidMount() {
|
||||
this.table.current.onLoading()
|
||||
getDictData('house_type').then(res => {
|
||||
this.setState(
|
||||
{
|
||||
codes: res,
|
||||
},
|
||||
() => {
|
||||
this.table.current.onLoadData()
|
||||
}
|
||||
)
|
||||
})
|
||||
}
|
||||
/**
|
||||
* 调用加载数据接口,可在调用前对query进行处理
|
||||
* [异步,必要]
|
||||
* @param {*} params
|
||||
* @param {*} query
|
||||
* @returns
|
||||
*/
|
||||
loadData = async (params, query) => {
|
||||
query = {
|
||||
...query,
|
||||
pid: this.selectCode,
|
||||
}
|
||||
//首次加载根据code列升序排序
|
||||
// if (!params.sortField) {
|
||||
// params.sortField = 'code';
|
||||
// params.sortOrder = 'ascend';
|
||||
// }
|
||||
const { data } = await apiAction.page({
|
||||
...params,
|
||||
...query,
|
||||
})
|
||||
return data
|
||||
}
|
||||
|
||||
/**
|
||||
* 调用树结构数据接口
|
||||
* [异步,必要]
|
||||
* @returns
|
||||
*/
|
||||
loadTreeData = async () => {
|
||||
const { data } = await apiAction.tree()
|
||||
return data
|
||||
}
|
||||
|
||||
/**
|
||||
* 树节点选中事件
|
||||
* [必要]
|
||||
* @param {*} id
|
||||
*/
|
||||
onSelectTree(code) {
|
||||
this.selectCode = code
|
||||
this.table.current.onReloadData()
|
||||
}
|
||||
|
||||
/**
|
||||
* 绑定字典数据
|
||||
* @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, id) {
|
||||
modal.current.open({
|
||||
pid: this.selectCode,
|
||||
// record,
|
||||
id,
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 对表格上的操作进行统一处理
|
||||
* [异步]
|
||||
* @param {*} action
|
||||
* @param {*} successMessage
|
||||
*/
|
||||
async onAction(action, successMessage) {
|
||||
this.table.current.onLoading()
|
||||
try {
|
||||
await action
|
||||
Message.success(successMessage)
|
||||
this.table.current.onReloadData()
|
||||
} catch {
|
||||
this.table.current.onLoaded()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除
|
||||
* @param {*} record
|
||||
*/
|
||||
onDelete(record) {
|
||||
this.onAction(apiAction.delete(record), '删除成功')
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<QueryTreeLayout
|
||||
loadData={this.loadTreeData}
|
||||
defaultExpanded={true}
|
||||
onSelect={key => this.onSelectTree(key)}
|
||||
replaceFields={{ value: 'code', title: 'name', children: 'children' }}
|
||||
>
|
||||
<Container mode="fluid">
|
||||
<Card bordered={false}>
|
||||
<QueryTable
|
||||
ref={this.table}
|
||||
autoLoad={false}
|
||||
loadData={this.loadData}
|
||||
columns={this.columns}
|
||||
queryInitialValues={{
|
||||
type: 1,
|
||||
}}
|
||||
onQueryChange={values => {
|
||||
if (values.hasOwnProperty('type')) {
|
||||
this.setState({ type: values.type })
|
||||
}
|
||||
}}
|
||||
query={
|
||||
<Auth auth="houseProjectInfo:page">
|
||||
<Form.Item label="项目类型" name="type">
|
||||
<Radio.Group buttonStyle="solid">
|
||||
<Radio.Button value={0}>全部</Radio.Button>
|
||||
<Radio.Button value={1}>
|
||||
<span>住宅</span>
|
||||
</Radio.Button>
|
||||
<Radio.Button value={2}>
|
||||
<span>非住宅</span>
|
||||
</Radio.Button>
|
||||
</Radio.Group>
|
||||
</Form.Item>
|
||||
<Form.Item label="项目名称" name="name">
|
||||
<Input autoComplete="off" placeholder="请输入项目名称" />
|
||||
</Form.Item>
|
||||
<Form.Item label="项目备注" name="note">
|
||||
<Input autoComplete="off" placeholder="请输入项目备注" />
|
||||
</Form.Item>
|
||||
</Auth>
|
||||
}
|
||||
operator={
|
||||
<Auth auth="houseProjectInfo:add">
|
||||
<Button
|
||||
icon={<AntIcon type="plus" />}
|
||||
onClick={() => this.onOpen(this.addForm)}
|
||||
>
|
||||
新增{name}
|
||||
</Button>
|
||||
</Auth>
|
||||
}
|
||||
></QueryTable>
|
||||
</Card>
|
||||
</Container>
|
||||
<ModalForm
|
||||
title={`新增${name}`}
|
||||
action={apiAction.add}
|
||||
ref={this.addForm}
|
||||
onSuccess={() => this.table.current.onReloadData()}
|
||||
>
|
||||
<FormBody />
|
||||
</ModalForm>
|
||||
|
||||
<ModalForm
|
||||
title={`编辑${name}`}
|
||||
action={apiAction.edit}
|
||||
ref={this.editForm}
|
||||
onSuccess={() => this.table.current.onReloadData()}
|
||||
>
|
||||
<FormBody />
|
||||
</ModalForm>
|
||||
</QueryTreeLayout>
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
import React, { Component } from 'react'
|
||||
import { Card } from 'antd'
|
||||
import Container from 'components/container'
|
||||
import { api } from 'common/api'
|
||||
import ReactJson from 'react-json-view'
|
||||
|
||||
export default class detail extends Component {
|
||||
state = {
|
||||
loading: false,
|
||||
record: null,
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
// 获取详细数据
|
||||
const { id } = this.props.param
|
||||
if (id) {
|
||||
api.houseQueryDetail({ id }).then(({ data }) => {
|
||||
this.setState({
|
||||
record: data,
|
||||
loading: false,
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Container>
|
||||
<Card>
|
||||
<ReactJson src={this.state.record} />
|
||||
</Card>
|
||||
</Container>
|
||||
)
|
||||
}
|
||||
}
|
||||
571
framework/web-react/src/pages/business/house/query/index.jsx
Normal file
571
framework/web-react/src/pages/business/house/query/index.jsx
Normal file
@@ -0,0 +1,571 @@
|
||||
import React, { Component } from 'react'
|
||||
import {
|
||||
Button,
|
||||
Card,
|
||||
Checkbox,
|
||||
Col,
|
||||
DatePicker,
|
||||
Form,
|
||||
Input,
|
||||
InputNumber,
|
||||
message as Message,
|
||||
Row,
|
||||
Tag,
|
||||
} from 'antd'
|
||||
import {
|
||||
AntIcon,
|
||||
Auth,
|
||||
Container,
|
||||
InputNumberRange,
|
||||
QueryTable,
|
||||
QueryTableActions,
|
||||
} from 'components'
|
||||
import { api } from 'common/api'
|
||||
import auth from 'components/authorized/handler'
|
||||
import { first, isEqual, last } from 'lodash'
|
||||
import getDictData from 'util/dic'
|
||||
import { toCamelCase } from 'util/format'
|
||||
import { getSearchDateRange, getSearchInfo, QueryType } from 'util/query'
|
||||
|
||||
/**
|
||||
* 注释段[\/**\/]为必须要改
|
||||
*/
|
||||
|
||||
/**
|
||||
* 配置页面所需接口函数
|
||||
*/
|
||||
const apiAction = {
|
||||
page: api.houseQueryPage,
|
||||
}
|
||||
|
||||
/**
|
||||
* 用于弹窗标题
|
||||
* [必要]
|
||||
*/
|
||||
const name = '/**/'
|
||||
|
||||
/**
|
||||
* 统一配置权限标识
|
||||
* [必要]
|
||||
*/
|
||||
const authName = 'houseQuery'
|
||||
|
||||
export default class index extends Component {
|
||||
state = {
|
||||
codes: {
|
||||
houseStatus: [],
|
||||
houseType: [],
|
||||
houseIndustry: [],
|
||||
houseUsedStatus: [],
|
||||
housePropertyRights: [],
|
||||
landAttribute: [],
|
||||
houseBaseInfo: [],
|
||||
houseStructureType: [],
|
||||
houseStorageOfDrawings: [],
|
||||
houseGrade: [],
|
||||
},
|
||||
|
||||
showDrawingMaterialText: false,
|
||||
}
|
||||
|
||||
// 表格实例
|
||||
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')}`}
|
||||
<br />
|
||||
<Tag color="purple">{text}</Tag>
|
||||
</>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: '房屋性质及行业',
|
||||
dataIndex: 'type',
|
||||
sorter: true,
|
||||
width: 150,
|
||||
render: text => this.bindCodeValue(text, 'house_type'),
|
||||
},
|
||||
{
|
||||
title: '地址',
|
||||
dataIndex: 'address',
|
||||
sorter: true,
|
||||
},
|
||||
{
|
||||
title: '建档状态',
|
||||
dataIndex: 'state',
|
||||
sorter: true,
|
||||
width: 100,
|
||||
render: text => this.bindCodeValue(text, 'house_status'),
|
||||
},
|
||||
]
|
||||
|
||||
/**
|
||||
* 构造函数,在渲染前动态添加操作字段等
|
||||
* @param {*} props
|
||||
*/
|
||||
constructor(props) {
|
||||
super(props)
|
||||
|
||||
const flag = auth({ [authName]: 'detail' })
|
||||
|
||||
if (flag) {
|
||||
this.columns.push({
|
||||
title: '操作',
|
||||
width: 150,
|
||||
dataIndex: 'actions',
|
||||
render: (text, record) => (
|
||||
<QueryTableActions>
|
||||
<Auth auth={{ [authName]: 'detail' }}>
|
||||
<a onClick={() => this.onOpen(record.id)}>查看</a>
|
||||
</Auth>
|
||||
</QueryTableActions>
|
||||
),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 阻止外部组件引发的渲染,提升性能
|
||||
* 可自行添加渲染条件
|
||||
* [必要]
|
||||
* @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_status',
|
||||
'house_type',
|
||||
'house_industry',
|
||||
'house_used_status',
|
||||
'house_property_rights',
|
||||
'land_attribute',
|
||||
'house_base_info',
|
||||
'house_structure_type',
|
||||
'house_storage_of_drawings',
|
||||
'house_grade'
|
||||
).then(codes => {
|
||||
this.setState({ codes }, () => {
|
||||
onLoadData()
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 调用加载数据接口,可在调用前对query进行处理
|
||||
* [异步,必要]
|
||||
* @param {*} params
|
||||
* @param {*} query
|
||||
* @returns
|
||||
*/
|
||||
loadData = async (params, query) => {
|
||||
query.completedDate = getSearchDateRange(query.completedDate)
|
||||
query.createdTime = getSearchDateRange(query.createdTime)
|
||||
|
||||
const searchInfo = getSearchInfo({
|
||||
query,
|
||||
queryType: {
|
||||
areaCode: QueryType.Like,
|
||||
completedDate: [QueryType.GreaterThanOrEqual, QueryType.LessThan],
|
||||
createdTime: [QueryType.GreaterThanOrEqual, QueryType.LessThan],
|
||||
totalArea: [QueryType.GreaterThanOrEqual, QueryType.LessThanOrEqual],
|
||||
totalFloor: [QueryType.GreaterThanOrEqual, QueryType.LessThanOrEqual],
|
||||
},
|
||||
})
|
||||
|
||||
const { data } = await apiAction.page({
|
||||
...params,
|
||||
searchInfo,
|
||||
})
|
||||
return data
|
||||
}
|
||||
|
||||
/**
|
||||
* 绑定字典数据
|
||||
* @param {*} code
|
||||
* @param {*} name
|
||||
* @returns
|
||||
*/
|
||||
bindCodeValue(code, name) {
|
||||
name = toCamelCase(name)
|
||||
const codes = this.state.codes[name]
|
||||
if (codes) {
|
||||
const c = codes.find(p => p.code == code)
|
||||
if (c) {
|
||||
return c.value
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
/**
|
||||
* 打开新增/编辑弹窗
|
||||
* @param {*} modal
|
||||
* @param {*} id
|
||||
*/
|
||||
onOpen(id) {
|
||||
window.openContentWindow({
|
||||
title: '房屋详情',
|
||||
path: 'business/house/query/detail',
|
||||
param: { id },
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 对表格上的操作进行统一处理
|
||||
* [异步]
|
||||
* @param {*} action
|
||||
* @param {*} successMessage
|
||||
*/
|
||||
async onAction(action, successMessage) {
|
||||
const { onLoading, onLoaded, onReloadData } = this.table.current
|
||||
onLoading()
|
||||
try {
|
||||
if (action) {
|
||||
await action
|
||||
}
|
||||
if (successMessage) {
|
||||
Message.success(successMessage)
|
||||
}
|
||||
onReloadData()
|
||||
} catch {
|
||||
onLoaded()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除
|
||||
* @param {*} id
|
||||
*/
|
||||
onDelete(id) {
|
||||
this.onAction(apiAction.delete({ id }), '删除成功')
|
||||
}
|
||||
|
||||
//#region 自定义方法
|
||||
rednerMoreQuery() {
|
||||
const { codes, showDrawingMaterialText } = this.state
|
||||
|
||||
return (
|
||||
<Row gutter={16}>
|
||||
<Col span={24}>
|
||||
<Form.Item label="使用状态" name="houseUsedStatus">
|
||||
<Checkbox.Group>
|
||||
{codes.houseUsedStatus.map(item => (
|
||||
<Checkbox key={item.code} value={+item.code}>
|
||||
{item.value}
|
||||
</Checkbox>
|
||||
))}
|
||||
</Checkbox.Group>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={12}>
|
||||
<Form.Item label="有无幕墙" name="curtainWall">
|
||||
<Checkbox.Group>
|
||||
<Checkbox value="">全部</Checkbox>
|
||||
<Checkbox value={0}>无</Checkbox>
|
||||
<Checkbox value={1}>有</Checkbox>
|
||||
</Checkbox.Group>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={12}>
|
||||
<Form.Item label="有无面砖" name="faceBrick">
|
||||
<Checkbox.Group>
|
||||
<Checkbox value="">全部</Checkbox>
|
||||
<Checkbox value={0}>无</Checkbox>
|
||||
<Checkbox value={1}>有</Checkbox>
|
||||
</Checkbox.Group>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={12}>
|
||||
<Form.Item label="有无涂料" name="coating">
|
||||
<Checkbox.Group>
|
||||
<Checkbox value="">全部</Checkbox>
|
||||
<Checkbox value={0}>无</Checkbox>
|
||||
<Checkbox value={1}>有</Checkbox>
|
||||
</Checkbox.Group>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={12}>
|
||||
<Form.Item label="有无粉刷" name="painting">
|
||||
<Checkbox.Group>
|
||||
<Checkbox value="">全部</Checkbox>
|
||||
<Checkbox value={0}>无</Checkbox>
|
||||
<Checkbox value={1}>有</Checkbox>
|
||||
</Checkbox.Group>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={12}>
|
||||
<Form.Item label="安全管理员">
|
||||
<Input autoComplete="off" placeholder="请输入安全管理员" />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={12}>
|
||||
<Form.Item label="片区监管员">
|
||||
<Input autoComplete="off" placeholder="请输入片区监管员" />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={24}>
|
||||
<Form.Item label="产权性质" name="propertyRights">
|
||||
<Checkbox.Group>
|
||||
<Checkbox value="">全部</Checkbox>
|
||||
{codes.housePropertyRights.map(item => (
|
||||
<Checkbox key={item.code} value={+item.code}>
|
||||
{item.value}
|
||||
</Checkbox>
|
||||
))}
|
||||
</Checkbox.Group>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={12}>
|
||||
<Form.Item label="土地性质" name="landAttribute">
|
||||
<Checkbox.Group>
|
||||
<Checkbox value="">全部</Checkbox>
|
||||
{codes.landAttribute.map(item => (
|
||||
<Checkbox key={item.code} value={+item.code}>
|
||||
{item.value}
|
||||
</Checkbox>
|
||||
))}
|
||||
</Checkbox.Group>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={12}>
|
||||
<Form.Item label="基础情况" name="baseInfo">
|
||||
<Checkbox.Group>
|
||||
<Checkbox value="">全部</Checkbox>
|
||||
{codes.houseBaseInfo.map(item => (
|
||||
<Checkbox key={item.code} value={+item.code}>
|
||||
{item.value}
|
||||
</Checkbox>
|
||||
))}
|
||||
</Checkbox.Group>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={24}>
|
||||
<Form.Item label="结构类型" name="structureType">
|
||||
<Checkbox.Group>
|
||||
<Checkbox value="">全部</Checkbox>
|
||||
{codes.houseStructureType.map(item => (
|
||||
<Checkbox key={item.code} value={+item.code}>
|
||||
{item.value}
|
||||
</Checkbox>
|
||||
))}
|
||||
</Checkbox.Group>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={24}>
|
||||
<Form.Item label="图纸资料存档处" name="drawingMaterial">
|
||||
<Form.Item name="drawingMaterial" className="mb-none">
|
||||
<Checkbox.Group>
|
||||
{codes.houseStorageOfDrawings.map(item => (
|
||||
<Checkbox key={item.code} value={item.code}>
|
||||
{item.value}
|
||||
</Checkbox>
|
||||
))}
|
||||
</Checkbox.Group>
|
||||
</Form.Item>
|
||||
{showDrawingMaterialText && (
|
||||
<Form.Item name="drawingMaterialText" className="mb-none mt-xs">
|
||||
<Input.TextArea autoSize placeholder="请输入其他图纸资料存档处" />
|
||||
</Form.Item>
|
||||
)}
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={12}>
|
||||
<Form.Item label="竣工日期" name="completedDate">
|
||||
<DatePicker.RangePicker
|
||||
className="w-100-p"
|
||||
placeholder={['请选择竣工开始日期', '请选择竣工结束日期']}
|
||||
/>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={12}>
|
||||
<Form.Item label="填表日期" name="createdTime">
|
||||
<DatePicker.RangePicker
|
||||
className="w-100-p"
|
||||
placeholder={['请选择填表开始日期', '请选择填表结束日期']}
|
||||
/>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={12}>
|
||||
<Form.Item label="总建筑面积" name="totalArea">
|
||||
<InputNumberRange
|
||||
min={0}
|
||||
unit="m²"
|
||||
placeholder={['请输入最小总建筑面积', '请输入最大总建筑面积']}
|
||||
/>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={12}>
|
||||
<Form.Item label="总层数" name="totalFloor">
|
||||
<InputNumberRange
|
||||
min={0}
|
||||
precision={0}
|
||||
step={1}
|
||||
unit="层"
|
||||
placeholder={['请输入最小总层数', '请输入最大总层数']}
|
||||
/>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={12}>
|
||||
<Form.Item label="建设单位" name="buildingUnit">
|
||||
<Input autoComplete="off" placeholder="请输入建设单位" />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={12}>
|
||||
<Form.Item label="设计单位" name="desingerUnit">
|
||||
<Input autoComplete="off" placeholder="请输入设计单位" />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={12}>
|
||||
<Form.Item label="施工单位" name="constructionUnit">
|
||||
<Input autoComplete="off" placeholder="请输入施工单位" />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={12}>
|
||||
<Form.Item label="监理单位" name="monitorUnit">
|
||||
<Input autoComplete="off" placeholder="请输入监理单位" />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={12}>
|
||||
<Form.Item label="产权单位" name="propertyUnit">
|
||||
<Input autoComplete="off" placeholder="请输入产权单位" />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={24}>
|
||||
<Form.Item label="综合等级" name="houseGrade">
|
||||
<Checkbox.Group>
|
||||
<Checkbox value="">全部</Checkbox>
|
||||
{codes.houseGrade.map(item => (
|
||||
<Checkbox key={item.code} value={+item.code}>
|
||||
{item.value}
|
||||
</Checkbox>
|
||||
))}
|
||||
</Checkbox.Group>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
</Row>
|
||||
)
|
||||
}
|
||||
|
||||
onQueryChange(changedValues, allValues) {
|
||||
console.log(changedValues)
|
||||
if (changedValues.hasOwnProperty('drawingMaterial') && changedValues.drawingMaterial) {
|
||||
this.setState({
|
||||
showDrawingMaterialText: changedValues.drawingMaterial.includes('100'),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// 全部
|
||||
const { codes } = this.state
|
||||
const key = first(Object.keys(changedValues))
|
||||
const mapCount = {
|
||||
curtainWall: 2,
|
||||
faceBrick: 2,
|
||||
coating: 2,
|
||||
painting: 2,
|
||||
propertyRights: codes.housePropertyRights.length,
|
||||
landAttribute: codes.landAttribute.length,
|
||||
baseInfo: codes.houseBaseInfo.length,
|
||||
structureType: codes.houseStructureType.length,
|
||||
houseGrade: codes.houseGrade.length,
|
||||
}
|
||||
if (Object.keys(mapCount).includes(key)) {
|
||||
return {
|
||||
[key]: this.checkedNone(changedValues[key], mapCount[key]),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
checkedNone(value, count) {
|
||||
if (first(value) == '' && value.length > 1) {
|
||||
// 在'无'之后选中其他值
|
||||
value.shift()
|
||||
} else if ((last(value) == '' && value.length > 1) || value.length === count) {
|
||||
// 在其他值之后选中'无'
|
||||
value = ['']
|
||||
}
|
||||
return value
|
||||
}
|
||||
//#endregion
|
||||
|
||||
render() {
|
||||
const { codes, type } = this.state
|
||||
|
||||
return (
|
||||
<Container mode="fluid">
|
||||
<br />
|
||||
<Card bordered={false}>
|
||||
<QueryTable
|
||||
ref={this.table}
|
||||
autoLoad={false}
|
||||
loadData={this.loadData}
|
||||
columns={this.columns}
|
||||
queryInitialValues={{
|
||||
houseUsedStatus: [1, 2],
|
||||
curtainWall: [''],
|
||||
faceBrick: [''],
|
||||
coating: [''],
|
||||
painting: [''],
|
||||
propertyRights: [''],
|
||||
landAttribute: [''],
|
||||
baseInfo: [''],
|
||||
structureType: [''],
|
||||
houseGrade: [''],
|
||||
}}
|
||||
query={
|
||||
<Auth auth={{ [authName]: 'page' }}>
|
||||
<Form.Item label="地址" name="address">
|
||||
<Input autoComplete="off" placeholder="请输入地址" />
|
||||
</Form.Item>
|
||||
<Form.Item label="房屋唯一编码" name="houseCode">
|
||||
<Input autoComplete="off" placeholder="请输入房屋唯一编码" />
|
||||
</Form.Item>
|
||||
</Auth>
|
||||
}
|
||||
moreQuery={
|
||||
<Auth auth={{ [authName]: 'page' }}>{this.rednerMoreQuery()}</Auth>
|
||||
}
|
||||
onQueryChange={(changedValues, allValues) =>
|
||||
this.onQueryChange(changedValues, allValues)
|
||||
}
|
||||
operator={
|
||||
<Auth auth={{ [authName]: 'add' }}>
|
||||
<Button
|
||||
icon={<AntIcon type="plus" />}
|
||||
onClick={() => this.onOpen(this.addForm)}
|
||||
>
|
||||
新增{name}
|
||||
</Button>
|
||||
</Auth>
|
||||
}
|
||||
/>
|
||||
</Card>
|
||||
</Container>
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,338 @@
|
||||
import React, { Component } from 'react'
|
||||
import {
|
||||
Button,
|
||||
Card,
|
||||
Drawer,
|
||||
Form,
|
||||
Input,
|
||||
message as Message,
|
||||
Popconfirm,
|
||||
Radio,
|
||||
Select,
|
||||
Tag,
|
||||
} from 'antd'
|
||||
import {
|
||||
AntIcon,
|
||||
Auth,
|
||||
Container,
|
||||
HouseLog,
|
||||
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'
|
||||
import { getSearchInfo } from 'util/query'
|
||||
|
||||
/**
|
||||
* 注释段[\/**\/]为必须要改
|
||||
*/
|
||||
|
||||
/**
|
||||
* 配置页面所需接口函数
|
||||
*/
|
||||
const apiAction = {
|
||||
page: api.houseTaskPage,
|
||||
}
|
||||
|
||||
/**
|
||||
* 统一配置权限标识
|
||||
* [必要]
|
||||
*/
|
||||
const authName = 'houseTask'
|
||||
|
||||
export default class index extends Component {
|
||||
state = {
|
||||
codes: {
|
||||
status: [
|
||||
{ code: 3, value: '审核中' },
|
||||
{ code: 6, value: '审核通过' },
|
||||
],
|
||||
houseType: [],
|
||||
houseIndustry: [],
|
||||
},
|
||||
|
||||
type: '',
|
||||
|
||||
visibleLog: false,
|
||||
}
|
||||
|
||||
// 表格实例
|
||||
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.fullProjName
|
||||
}-${record.no.toString().padStart(3, '0')}`}
|
||||
<br />
|
||||
<Tag color="purple">{text}</Tag>
|
||||
</>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: '房屋性质及行业',
|
||||
dataIndex: 'type',
|
||||
sorter: true,
|
||||
width: 150,
|
||||
render: (text, record) =>
|
||||
this.bindCodeValue(text, 'house_type') +
|
||||
(text === 2 ? `(${this.bindCodeValue(record.industry, 'house_industry')})` : ''),
|
||||
},
|
||||
{
|
||||
title: '地址',
|
||||
dataIndex: 'address',
|
||||
sorter: true,
|
||||
},
|
||||
{
|
||||
title: '任务截止时间',
|
||||
dataIndex: 'endTime',
|
||||
sorter: true,
|
||||
width: 150,
|
||||
},
|
||||
{
|
||||
title: '审核状态',
|
||||
dataIndex: 'status',
|
||||
sorter: true,
|
||||
width: 100,
|
||||
render: text => this.bindCodeValue(text, 'status'),
|
||||
},
|
||||
]
|
||||
|
||||
/**
|
||||
* 构造函数,在渲染前动态添加操作字段等
|
||||
* @param {*} props
|
||||
*/
|
||||
constructor(props) {
|
||||
super(props)
|
||||
|
||||
const flag = auth({ houseInfo: 'getByTaskId' })
|
||||
|
||||
if (flag) {
|
||||
this.columns.push({
|
||||
title: '操作',
|
||||
width: 150,
|
||||
dataIndex: 'actions',
|
||||
render: (text, record) => (
|
||||
<QueryTableActions>
|
||||
<Auth auth={{ houseInfo: 'getByTaskId' }}>
|
||||
<a onClick={() => this.onOpen(record.id)}>
|
||||
{record.state === 3 ? `审核` : `查看`}
|
||||
</a>
|
||||
</Auth>
|
||||
<a onClick={() => this.onShowLog(record.id)}>日志</a>
|
||||
</QueryTableActions>
|
||||
),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 阻止外部组件引发的渲染,提升性能
|
||||
* 可自行添加渲染条件
|
||||
* [必要]
|
||||
* @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: { ...this.state.codes, ...codes } }, () => {
|
||||
onLoadData()
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 调用加载数据接口,可在调用前对query进行处理
|
||||
* [异步,必要]
|
||||
* @param {*} params
|
||||
* @param {*} query
|
||||
* @returns
|
||||
*/
|
||||
loadData = async (params, query) => {
|
||||
const searchInfo = getSearchInfo({
|
||||
query,
|
||||
queryType: {
|
||||
type: '=',
|
||||
industry: '=',
|
||||
address: 'like',
|
||||
houseCode: 'like',
|
||||
status: '=',
|
||||
},
|
||||
})
|
||||
|
||||
const { data } = await apiAction.page({
|
||||
...params,
|
||||
searchInfo,
|
||||
})
|
||||
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,
|
||||
table: this.table,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 对表格上的操作进行统一处理
|
||||
* [异步]
|
||||
* @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 自定义方法
|
||||
onShowLog(id) {
|
||||
this.setState({ visibleLog: id })
|
||||
}
|
||||
//#endregion
|
||||
|
||||
render() {
|
||||
const { codes, type, visibleLog } = this.state
|
||||
|
||||
return (
|
||||
<Container mode="fluid">
|
||||
<br />
|
||||
<Card bordered={false}>
|
||||
<QueryTable
|
||||
ref={this.table}
|
||||
autoLoad={false}
|
||||
loadData={this.loadData}
|
||||
columns={this.columns}
|
||||
queryInitialValues={{
|
||||
type: '',
|
||||
status: '',
|
||||
}}
|
||||
onQueryChange={values => {
|
||||
if (values.hasOwnProperty('type')) {
|
||||
this.setState({ type: values.type })
|
||||
}
|
||||
}}
|
||||
query={
|
||||
<Auth auth={{ [authName]: 'page' }}>
|
||||
<Form.Item label="房屋性质" name="type">
|
||||
<Radio.Group buttonStyle="solid">
|
||||
<Radio.Button value="">全部</Radio.Button>
|
||||
{codes.houseType.map(item => (
|
||||
<Radio.Button key={item.code} value={item.code}>
|
||||
{item.value}
|
||||
</Radio.Button>
|
||||
))}
|
||||
</Radio.Group>
|
||||
</Form.Item>
|
||||
{type == 2 && (
|
||||
<Form.Item label="行业" name="industry">
|
||||
<Select
|
||||
allowClear
|
||||
className="w-150"
|
||||
placeholder="请选择行业"
|
||||
>
|
||||
{codes.houseIndustry.map(item => (
|
||||
<Select.Option key={item.code} value={item.code}>
|
||||
{item.value}
|
||||
</Select.Option>
|
||||
))}
|
||||
</Select>
|
||||
</Form.Item>
|
||||
)}
|
||||
<Form.Item label="地址" name="address">
|
||||
<Input autoComplete="off" placeholder="请输入地址" />
|
||||
</Form.Item>
|
||||
<Form.Item label="房屋唯一编码" name="houseCode">
|
||||
<Input autoComplete="off" placeholder="请输入房屋唯一编码" />
|
||||
</Form.Item>
|
||||
<Form.Item label="审核状态" name="status">
|
||||
<Radio.Group buttonStyle="solid">
|
||||
<Radio.Button value="">全部</Radio.Button>
|
||||
{codes.status.map(item => (
|
||||
<Radio.Button key={item.code} value={item.code}>
|
||||
{item.value}
|
||||
</Radio.Button>
|
||||
))}
|
||||
</Radio.Group>
|
||||
</Form.Item>
|
||||
</Auth>
|
||||
}
|
||||
/>
|
||||
</Card>
|
||||
|
||||
<Drawer
|
||||
width={600}
|
||||
visible={visibleLog}
|
||||
onClose={() => this.setState({ visibleLog: false })}
|
||||
destroyOnClose
|
||||
>
|
||||
<HouseLog taskId={visibleLog} />
|
||||
</Drawer>
|
||||
</Container>
|
||||
)
|
||||
}
|
||||
}
|
||||
331
framework/web-react/src/pages/business/house/task/index.jsx
Normal file
331
framework/web-react/src/pages/business/house/task/index.jsx
Normal file
@@ -0,0 +1,331 @@
|
||||
import React, { Component } from 'react'
|
||||
import { Card, Checkbox, Drawer, Form, Input, message as Message, Radio, Select, Tag } from 'antd'
|
||||
import { Auth, Container, HouseLog, 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 { getSearchInfo } from 'util/query'
|
||||
import { checkboxCheckedNone } from 'util/tool'
|
||||
|
||||
/**
|
||||
* 注释段[\/**\/]为必须要改
|
||||
*/
|
||||
|
||||
/**
|
||||
* 配置页面所需接口函数
|
||||
*/
|
||||
const apiAction = {
|
||||
page: api.houseTaskPage,
|
||||
}
|
||||
|
||||
/**
|
||||
* 统一配置权限标识
|
||||
* [必要]
|
||||
*/
|
||||
const authName = 'houseTask'
|
||||
|
||||
export default class index extends Component {
|
||||
state = {
|
||||
codes: {
|
||||
houseStatus: [],
|
||||
houseType: [],
|
||||
houseIndustry: [],
|
||||
},
|
||||
|
||||
type: '',
|
||||
|
||||
visibleLog: false,
|
||||
}
|
||||
|
||||
// 表格实例
|
||||
table = React.createRef()
|
||||
|
||||
// 新增窗口实例
|
||||
addForm = React.createRef()
|
||||
// 编辑窗口实例
|
||||
editForm = React.createRef()
|
||||
|
||||
columns = [
|
||||
{
|
||||
title: '房屋编码',
|
||||
dataIndex: 'houseCode',
|
||||
sorter: true,
|
||||
width: 400,
|
||||
render: (text, record) => (
|
||||
<>
|
||||
{`${record.areaName}-${record.roadName}-${record.commName}-${
|
||||
record.fullProjName
|
||||
}-${record.no.toString().padStart(3, '0')}`}
|
||||
<br />
|
||||
<Tag color="purple">{text}</Tag>
|
||||
</>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: '房屋性质及行业',
|
||||
dataIndex: 'type',
|
||||
sorter: true,
|
||||
width: 150,
|
||||
render: (text, record) =>
|
||||
this.bindCodeValue(text, 'house_type') +
|
||||
(text === 2 ? `(${this.bindCodeValue(record.industry, 'house_industry')})` : ''),
|
||||
},
|
||||
{
|
||||
title: '地址',
|
||||
dataIndex: 'address',
|
||||
sorter: true,
|
||||
},
|
||||
{
|
||||
title: '任务截止时间',
|
||||
dataIndex: 'endTime',
|
||||
sorter: true,
|
||||
width: 150,
|
||||
},
|
||||
{
|
||||
title: '建档状态',
|
||||
dataIndex: 'state',
|
||||
sorter: true,
|
||||
width: 100,
|
||||
render: text => this.bindCodeValue(text, 'house_status'),
|
||||
},
|
||||
]
|
||||
|
||||
/**
|
||||
* 构造函数,在渲染前动态添加操作字段等
|
||||
* @param {*} props
|
||||
*/
|
||||
constructor(props) {
|
||||
super(props)
|
||||
|
||||
const flag = auth({ houseInfo: 'getByTaskId' })
|
||||
|
||||
if (flag) {
|
||||
this.columns.push({
|
||||
title: '操作',
|
||||
width: 150,
|
||||
dataIndex: 'actions',
|
||||
render: (text, record) => (
|
||||
<QueryTableActions>
|
||||
<Auth auth={{ houseInfo: 'getByTaskId' }}>
|
||||
<a onClick={() => this.onOpen(record.id)}>
|
||||
{record.state === -1 || record.state === 1 || record.state === 2
|
||||
? `修改`
|
||||
: record.state === 3 || record.state === 6
|
||||
? `查看`
|
||||
: `登记`}
|
||||
</a>
|
||||
</Auth>
|
||||
<a onClick={() => this.onShowLog(record.id)}>日志</a>
|
||||
</QueryTableActions>
|
||||
),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 阻止外部组件引发的渲染,提升性能
|
||||
* 可自行添加渲染条件
|
||||
* [必要]
|
||||
* @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_status', 'house_type', 'house_industry').then(codes => {
|
||||
this.setState({ codes: { ...this.state.codes, ...codes } }, () => {
|
||||
onLoadData()
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 调用加载数据接口,可在调用前对query进行处理
|
||||
* [异步,必要]
|
||||
* @param {*} params
|
||||
* @param {*} query
|
||||
* @returns
|
||||
*/
|
||||
loadData = async (params, query) => {
|
||||
const searchInfo = getSearchInfo({
|
||||
query,
|
||||
queryType: {
|
||||
type: '=',
|
||||
industry: '=',
|
||||
address: 'like',
|
||||
houseCode: 'like',
|
||||
state: '=',
|
||||
},
|
||||
})
|
||||
|
||||
const { data } = await apiAction.page({
|
||||
...params,
|
||||
searchInfo,
|
||||
})
|
||||
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,
|
||||
table: this.table,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 对表格上的操作进行统一处理
|
||||
* [异步]
|
||||
* @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 自定义方法
|
||||
onShowLog(id) {
|
||||
this.setState({ visibleLog: id })
|
||||
}
|
||||
//#endregion
|
||||
|
||||
render() {
|
||||
const { codes, type, visibleLog } = this.state
|
||||
|
||||
return (
|
||||
<Container mode="fluid">
|
||||
<br />
|
||||
<Card bordered={false}>
|
||||
<QueryTable
|
||||
ref={this.table}
|
||||
autoLoad={false}
|
||||
loadData={this.loadData}
|
||||
columns={this.columns}
|
||||
queryInitialValues={{
|
||||
type: '',
|
||||
state: [-1, 0, 1, 2],
|
||||
}}
|
||||
onQueryChange={values => {
|
||||
if (values.hasOwnProperty('type')) {
|
||||
this.setState({ type: values.type })
|
||||
}
|
||||
if (values.hasOwnProperty('state')) {
|
||||
const value = checkboxCheckedNone({
|
||||
value: values.state,
|
||||
length: codes.houseStatus.length,
|
||||
required: true,
|
||||
})
|
||||
return {
|
||||
state: value,
|
||||
}
|
||||
}
|
||||
}}
|
||||
query={
|
||||
<Auth auth={{ [authName]: 'page' }}>
|
||||
<Form.Item label="房屋性质" name="type">
|
||||
<Radio.Group buttonStyle="solid">
|
||||
<Radio.Button value="">全部</Radio.Button>
|
||||
{codes.houseType.map(item => (
|
||||
<Radio.Button key={item.code} value={item.code}>
|
||||
{item.value}
|
||||
</Radio.Button>
|
||||
))}
|
||||
</Radio.Group>
|
||||
</Form.Item>
|
||||
{type == 2 && (
|
||||
<Form.Item label="行业" name="industry">
|
||||
<Select
|
||||
allowClear
|
||||
className="w-150"
|
||||
placeholder="请选择行业"
|
||||
>
|
||||
{codes.houseIndustry.map(item => (
|
||||
<Select.Option key={item.code} value={item.code}>
|
||||
{item.value}
|
||||
</Select.Option>
|
||||
))}
|
||||
</Select>
|
||||
</Form.Item>
|
||||
)}
|
||||
<Form.Item label="地址" name="address">
|
||||
<Input autoComplete="off" placeholder="请输入地址" />
|
||||
</Form.Item>
|
||||
<Form.Item label="房屋唯一编码" name="houseCode">
|
||||
<Input autoComplete="off" placeholder="请输入房屋唯一编码" />
|
||||
</Form.Item>
|
||||
<Form.Item label="建档状态" name="state">
|
||||
<Checkbox.Group>
|
||||
<Checkbox value="">全部</Checkbox>
|
||||
{codes.houseStatus.map(item => (
|
||||
<Checkbox key={item.code} value={+item.code}>
|
||||
{item.value}
|
||||
</Checkbox>
|
||||
))}
|
||||
</Checkbox.Group>
|
||||
</Form.Item>
|
||||
</Auth>
|
||||
}
|
||||
/>
|
||||
</Card>
|
||||
|
||||
<Drawer
|
||||
width={600}
|
||||
visible={visibleLog}
|
||||
onClose={() => this.setState({ visibleLog: false })}
|
||||
destroyOnClose
|
||||
>
|
||||
<HouseLog taskId={visibleLog} />
|
||||
</Drawer>
|
||||
</Container>
|
||||
)
|
||||
}
|
||||
}
|
||||
194
framework/web-react/src/pages/business/house/zone/form.jsx
Normal file
194
framework/web-react/src/pages/business/house/zone/form.jsx
Normal file
@@ -0,0 +1,194 @@
|
||||
import React, { Component } from 'react'
|
||||
import { Form, Input, InputNumber, Spin, TreeSelect } from 'antd'
|
||||
import { AntIcon } from 'components'
|
||||
import { cloneDeep, pickBy } from 'lodash'
|
||||
import { api } from 'common/api'
|
||||
import { numberToChinese } from 'util/format'
|
||||
import store from 'store'
|
||||
|
||||
const { getState, subscribe } = store
|
||||
const storePath = 'user'
|
||||
const initialValues = {
|
||||
sort: 100,
|
||||
}
|
||||
export default class form extends Component {
|
||||
state = {
|
||||
// 加载状态
|
||||
loading: true,
|
||||
exist: false,
|
||||
options: {
|
||||
orgData: [],
|
||||
},
|
||||
user: getState(storePath),
|
||||
}
|
||||
|
||||
constructor(props) {
|
||||
super(props)
|
||||
|
||||
this.unsubscribe = subscribe(storePath, () => {
|
||||
this.setState({
|
||||
user: getState(storePath),
|
||||
})
|
||||
})
|
||||
}
|
||||
// 表单实例
|
||||
form = React.createRef()
|
||||
|
||||
// 初始化数据
|
||||
record = {}
|
||||
|
||||
/**
|
||||
* mount后回调
|
||||
*/
|
||||
componentDidMount() {
|
||||
this.props.created && this.props.created(this)
|
||||
}
|
||||
componentWillUnmount() {
|
||||
this.unsubscribe()
|
||||
}
|
||||
/**
|
||||
* 填充数据
|
||||
* 可以在设置this.record之后对其作出数据结构调整
|
||||
* [异步,必要]
|
||||
* @param {*} params
|
||||
*/
|
||||
async fillData(params) {
|
||||
const { user } = this.state
|
||||
if (params.id) {
|
||||
this.setState({
|
||||
loading: true,
|
||||
})
|
||||
|
||||
api.houseZoneGetById({ zoneId: params.id }).then(({ data }) => {
|
||||
this.record = data
|
||||
|
||||
this.setState({
|
||||
loading: false,
|
||||
})
|
||||
})
|
||||
}
|
||||
// this.record = cloneDeep(params.record)
|
||||
//#region 从后端转换成前段所需格式
|
||||
const orgData = await this.loadOrgData()
|
||||
|
||||
this.setState({
|
||||
exist: !!params.id,
|
||||
options: { orgData },
|
||||
})
|
||||
|
||||
//街道角色新增,不管左侧树选中与否,默认值均为本街道
|
||||
if (user.adminType === 2) {
|
||||
user.roles.map(role => {
|
||||
if (role.code == 'road_manager') {
|
||||
params.orgId = user.loginEmpInfo.orgId
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
this.record = {
|
||||
pid: params.orgId,
|
||||
...this.record,
|
||||
}
|
||||
|
||||
//#endregion
|
||||
if (!params.id && !!params.orgId) {
|
||||
this.onOrgIdChanged(params.orgId)
|
||||
}
|
||||
this.form.current.setFieldsValue(this.record)
|
||||
|
||||
this.setState({
|
||||
loading: false,
|
||||
})
|
||||
}
|
||||
/**
|
||||
* 获取数据
|
||||
* 可以对postData进行数据结构调整
|
||||
* [异步,必要]
|
||||
* @returns
|
||||
*/
|
||||
async getData() {
|
||||
const form = this.form.current
|
||||
|
||||
const valid = await form.validateFields()
|
||||
if (valid) {
|
||||
const postData = form.getFieldsValue()
|
||||
if (this.record) {
|
||||
postData.id = this.record.id
|
||||
}
|
||||
//#region 从前段转换后端所需格
|
||||
//#endregion
|
||||
return postData
|
||||
}
|
||||
}
|
||||
|
||||
async loadOrgData() {
|
||||
const { data } = await api.getOrgTree({ type: 4 })
|
||||
return data
|
||||
}
|
||||
|
||||
onOrgIdChanged(value) {
|
||||
this.loading = true
|
||||
|
||||
api.houseZoneAutoIncrement({ roadId: value })
|
||||
.then(({ data }) => {
|
||||
this.form.current.setFieldsValue({
|
||||
name: `片区${numberToChinese(data)}`,
|
||||
sort: data,
|
||||
})
|
||||
})
|
||||
.catch(() => {
|
||||
this.form.current.setFieldsValue({
|
||||
name: '',
|
||||
sort: 0,
|
||||
})
|
||||
})
|
||||
.finally(() => {
|
||||
this.loading = false
|
||||
})
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Form initialValues={initialValues} ref={this.form} className="yo-form">
|
||||
<Spin spinning={this.state.loading} indicator={<AntIcon type="loading" />}>
|
||||
<div className="yo-form-group">
|
||||
<Form.Item
|
||||
label="所属街道"
|
||||
name="pid"
|
||||
rules={[{ required: true, message: '请选择所属街道' }]}
|
||||
>
|
||||
<TreeSelect
|
||||
treeData={this.state.options.orgData}
|
||||
dropdownStyle={{ maxHeight: '300px', overflow: 'auto' }}
|
||||
treeDefaultExpandAll
|
||||
placeholder="请选择所属街道"
|
||||
onChange={(value, label, extra) => this.onOrgIdChanged(value)}
|
||||
disabled={this.state.exist}
|
||||
/>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
label="片区名称"
|
||||
name="name"
|
||||
rules={[{ required: true, message: '片区名称', trigger: 'blur' }]}
|
||||
>
|
||||
<Input autoComplete="off" placeholder="请输入机构名称" disabled />
|
||||
</Form.Item>
|
||||
<Form.Item label="排序" name="sort">
|
||||
<InputNumber
|
||||
max={1000}
|
||||
min={0}
|
||||
placeholder="请输入排序"
|
||||
className="w-100-p"
|
||||
disabled
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form.Item label="备注" name="remark">
|
||||
<Input.TextArea placeholder="请输入备注" />
|
||||
</Form.Item>
|
||||
</div>
|
||||
</Spin>
|
||||
</Form>
|
||||
)
|
||||
}
|
||||
}
|
||||
277
framework/web-react/src/pages/business/house/zone/index.jsx
Normal file
277
framework/web-react/src/pages/business/house/zone/index.jsx
Normal file
@@ -0,0 +1,277 @@
|
||||
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 { api } from 'common/api'
|
||||
import auth from 'components/authorized/handler'
|
||||
import { toCamelCase } from 'util/format'
|
||||
import { isEqual } from 'lodash'
|
||||
import getDictData from 'util/dic'
|
||||
import FormBody from './form'
|
||||
|
||||
const apiAction = {
|
||||
tree: api.getOrgTree,
|
||||
page: api.houseZonePage,
|
||||
add: api.houseZoneAdd,
|
||||
edit: api.houseZoneEdit,
|
||||
delete: api.sysOrgDelete,
|
||||
}
|
||||
|
||||
const name = '片区'
|
||||
|
||||
export default class index extends Component {
|
||||
// 树框架实例
|
||||
treeLayout = React.createRef()
|
||||
|
||||
// 表格实例
|
||||
table = React.createRef()
|
||||
|
||||
// 新增窗口实例
|
||||
addForm = React.createRef()
|
||||
// 编辑窗口实例
|
||||
editForm = React.createRef()
|
||||
|
||||
// 树选中节点
|
||||
selectId = undefined
|
||||
|
||||
// 表格字段
|
||||
columns = [
|
||||
{
|
||||
title: '片区名称',
|
||||
width: '400px',
|
||||
dataIndex: 'name',
|
||||
sorter: true,
|
||||
},
|
||||
{
|
||||
title: '排序',
|
||||
width: '80px',
|
||||
dataIndex: 'sort',
|
||||
sorter: true,
|
||||
},
|
||||
{
|
||||
title: '备注',
|
||||
width: '80px',
|
||||
dataIndex: 'remark',
|
||||
sorter: true,
|
||||
},
|
||||
]
|
||||
|
||||
/**
|
||||
* 构造函数,在渲染前动态添加操作字段等
|
||||
* @param {*} props
|
||||
*/
|
||||
constructor(props) {
|
||||
super(props)
|
||||
|
||||
const flag = auth({ houseZone: [['edit'], ['delete']] })
|
||||
|
||||
if (flag) {
|
||||
this.columns.push({
|
||||
title: '操作',
|
||||
width: 150,
|
||||
dataIndex: 'actions',
|
||||
render: (text, record) => (
|
||||
<QueryTableActions>
|
||||
<Auth auth="houseZone:edit">
|
||||
<a onClick={() => this.onOpen(this.editForm, record.id)}>编辑</a>
|
||||
</Auth>
|
||||
<Auth auth="houseZone:delete">
|
||||
<Popconfirm
|
||||
placement="topRight"
|
||||
title="是否确认删除"
|
||||
onConfirm={() => this.onDelete(record)}
|
||||
>
|
||||
<a>删除</a>
|
||||
</Popconfirm>
|
||||
</Auth>
|
||||
</QueryTableActions>
|
||||
),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 阻止外部组件引发的渲染,提升性能
|
||||
* 可自行添加渲染条件
|
||||
* [必要]
|
||||
* @param {*} props
|
||||
* @param {*} state
|
||||
* @returns
|
||||
*/
|
||||
shouldComponentUpdate(props, state) {
|
||||
return !isEqual(this.state, state)
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载字典数据,之后开始加载表格数据
|
||||
* 如果必须要加载字典数据,可直接对表格设置autoLoad=true
|
||||
*/
|
||||
componentDidMount() {
|
||||
this.table.current.onLoading()
|
||||
}
|
||||
|
||||
/**
|
||||
* 调用加载数据接口,可在调用前对query进行处理
|
||||
* [异步,必要]
|
||||
* @param {*} params
|
||||
* @param {*} query
|
||||
* @returns
|
||||
*/
|
||||
loadData = async (params, query) => {
|
||||
query = {
|
||||
...query,
|
||||
pid: this.selectId,
|
||||
}
|
||||
|
||||
const { data } = await apiAction.page({
|
||||
...params,
|
||||
...query,
|
||||
})
|
||||
return data
|
||||
}
|
||||
|
||||
/**
|
||||
* 调用树结构数据接口
|
||||
* [异步,必要]
|
||||
* @returns
|
||||
*/
|
||||
loadTreeData = async () => {
|
||||
const { data } = await apiAction.tree({ type: 4 })
|
||||
return data
|
||||
}
|
||||
|
||||
/**
|
||||
* 树节点选中事件
|
||||
* [必要]
|
||||
* @param {*} id
|
||||
*/
|
||||
onSelectTree(id) {
|
||||
this.selectId = id
|
||||
this.table.current.onReloadData()
|
||||
}
|
||||
|
||||
/**
|
||||
* 绑定字典数据
|
||||
* @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, id) {
|
||||
modal.current.open({
|
||||
orgId: this.selectId,
|
||||
// record,
|
||||
id,
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 对表格上的操作进行统一处理
|
||||
* [异步]
|
||||
* @param {*} action
|
||||
* @param {*} successMessage
|
||||
*/
|
||||
async onAction(action, successMessage) {
|
||||
this.table.current.onLoading()
|
||||
try {
|
||||
if (action) {
|
||||
await action
|
||||
}
|
||||
if (successMessage) {
|
||||
Message.success(successMessage)
|
||||
}
|
||||
this.treeLayout.current.onReloadData()
|
||||
this.table.current.onReloadData()
|
||||
} catch {
|
||||
this.table.current.onLoaded()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除
|
||||
* @param {*} record
|
||||
*/
|
||||
onDelete(record) {
|
||||
this.onAction(apiAction.delete(record), '删除成功')
|
||||
}
|
||||
|
||||
//#region 自定义方法
|
||||
//#endregion
|
||||
|
||||
render() {
|
||||
return (
|
||||
<QueryTreeLayout
|
||||
ref={this.treeLayout}
|
||||
loadData={this.loadTreeData}
|
||||
defaultExpanded={true}
|
||||
onSelect={key => this.onSelectTree(key)}
|
||||
>
|
||||
<Container mode="fluid">
|
||||
<Card bordered={false}>
|
||||
<QueryTable
|
||||
ref={this.table}
|
||||
loadData={this.loadData}
|
||||
columns={this.columns}
|
||||
query={
|
||||
<Auth auth="houseZone:page">
|
||||
<Form.Item label="片区名称" name="name">
|
||||
<Input autoComplete="off" placeholder="请输入片区名称" />
|
||||
</Form.Item>
|
||||
</Auth>
|
||||
}
|
||||
operator={
|
||||
<Button
|
||||
icon={<AntIcon type="plus" />}
|
||||
onClick={() => this.onOpen(this.addForm)}
|
||||
>
|
||||
新增{name}
|
||||
</Button>
|
||||
}
|
||||
/>
|
||||
</Card>
|
||||
</Container>
|
||||
|
||||
<ModalForm
|
||||
title={`新增${name}`}
|
||||
action={apiAction.add}
|
||||
ref={this.addForm}
|
||||
onSuccess={() => this.onAction()}
|
||||
>
|
||||
<FormBody />
|
||||
</ModalForm>
|
||||
|
||||
<ModalForm
|
||||
title={`编辑${name}`}
|
||||
action={apiAction.edit}
|
||||
ref={this.editForm}
|
||||
onSuccess={() => this.onAction()}
|
||||
>
|
||||
<FormBody />
|
||||
</ModalForm>
|
||||
</QueryTreeLayout>
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
import React, { Component } from 'react'
|
||||
import { Radio, Tabs } from 'antd'
|
||||
import { ComponentDynamic } from 'components'
|
||||
import AntIcon from 'components/ant-icon'
|
||||
|
||||
const tabs = [
|
||||
{
|
||||
title: '按房屋等级',
|
||||
component: () => import('./tab1'),
|
||||
},
|
||||
{
|
||||
title: '按房屋结构',
|
||||
component: () => import('./tab2'),
|
||||
},
|
||||
]
|
||||
|
||||
export default class index extends Component {
|
||||
|
||||
state = {
|
||||
activeKey: '0',
|
||||
types: ['charts', 'charts']
|
||||
}
|
||||
|
||||
render() {
|
||||
|
||||
const { activeKey, types } = this.state
|
||||
|
||||
return (
|
||||
<div className="yo-form-page">
|
||||
<div className="yo-form-page-layout">
|
||||
<div className="yo-tab-external-mount">
|
||||
<Tabs
|
||||
tabBarExtraContent={
|
||||
<Radio.Group
|
||||
buttonStyle="solid"
|
||||
value={types[activeKey]}
|
||||
onChange={(e) => {
|
||||
const t = [...types]
|
||||
t[activeKey] = e.target.value
|
||||
this.setState({
|
||||
types: t
|
||||
})
|
||||
}}
|
||||
>
|
||||
<Radio.Button value="charts">
|
||||
<AntIcon type="bar-chart" />
|
||||
</Radio.Button>
|
||||
<Radio.Button value="table">
|
||||
<AntIcon type="table" />
|
||||
</Radio.Button>
|
||||
</Radio.Group>
|
||||
}
|
||||
onChange={(activeKey) => this.setState({ activeKey })}
|
||||
>
|
||||
{
|
||||
tabs.map((item, i) => (
|
||||
<Tabs.TabPane
|
||||
key={i}
|
||||
forceRender={true}
|
||||
tab={item.title}
|
||||
/>
|
||||
))
|
||||
}
|
||||
</Tabs>
|
||||
<div className="yo-tab-external-mount-content">
|
||||
{
|
||||
tabs.map((item, i) => (
|
||||
<div
|
||||
key={i}
|
||||
className={[
|
||||
'yo-tab-external-tabpane',
|
||||
activeKey == i ?
|
||||
'yo-tab-external-tabpane-active'
|
||||
:
|
||||
'yo-tab-external-tabpane-inactive'
|
||||
].join(' ')}
|
||||
>
|
||||
<ComponentDynamic is={item.component} type={types[i]} />
|
||||
</div>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,202 @@
|
||||
import React, { Component } from 'react'
|
||||
import { Card, Col, Row } from 'antd'
|
||||
import * as echarts from 'echarts'
|
||||
|
||||
const echartsColors = [
|
||||
{ from: '#14dbff', to: '#007dff' },
|
||||
{ from: '#45f4a6', to: '#3bb27d' },
|
||||
{ from: '#fbb456', to: '#f1961b' },
|
||||
{ from: '#fa7148', to: '#ef5932' },
|
||||
]
|
||||
|
||||
function itemColor(index) {
|
||||
return {
|
||||
type: 'linear',
|
||||
x: 0,
|
||||
y: 0,
|
||||
x2: 1,
|
||||
y2: 1,
|
||||
colorStops: [
|
||||
{
|
||||
offset: 0,
|
||||
color: echartsColors[index % echartsColors.length].from,
|
||||
},
|
||||
{
|
||||
offset: 1,
|
||||
color: echartsColors[index % echartsColors.length].to,
|
||||
},
|
||||
],
|
||||
}
|
||||
}
|
||||
|
||||
function initChart1(dom) {
|
||||
const chart = echarts.init(dom);
|
||||
chart.setOption({
|
||||
legend: {
|
||||
top: 'bottom',
|
||||
},
|
||||
tooltip: {
|
||||
formatter: '<b>{b}</b> : {c}幢 ({d}%)',
|
||||
},
|
||||
toolbox: {
|
||||
show: true,
|
||||
feature: {
|
||||
mark: { show: true },
|
||||
restore: { show: true },
|
||||
saveAsImage: { show: true },
|
||||
},
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: '面积模式',
|
||||
type: 'pie',
|
||||
radius: [50, 150],
|
||||
startAngle: 110,
|
||||
center: ['50%', '50%'],
|
||||
roseType: 'area',
|
||||
label: {
|
||||
formatter: '{d}',
|
||||
},
|
||||
data: [
|
||||
{
|
||||
value: 70,
|
||||
name: 'A级',
|
||||
itemStyle: {
|
||||
color: itemColor(0),
|
||||
},
|
||||
},
|
||||
{
|
||||
value: 38,
|
||||
name: 'B级',
|
||||
itemStyle: {
|
||||
color: itemColor(1),
|
||||
},
|
||||
},
|
||||
{
|
||||
value: 32,
|
||||
name: 'C级',
|
||||
itemStyle: {
|
||||
color: itemColor(2),
|
||||
},
|
||||
},
|
||||
{
|
||||
value: 30,
|
||||
name: 'D级',
|
||||
itemStyle: {
|
||||
color: itemColor(3),
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
});
|
||||
}
|
||||
|
||||
function initChart2(dom) {
|
||||
const chart = echarts.init(dom);
|
||||
chart.setOption({
|
||||
tooltip: {
|
||||
formatter: '<b>{b}</b> : {c}幢',
|
||||
},
|
||||
toolbox: {
|
||||
show: true,
|
||||
feature: {
|
||||
mark: { show: true },
|
||||
restore: { show: true },
|
||||
saveAsImage: { show: true },
|
||||
},
|
||||
},
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
data: ['A级', 'B级', 'C级', 'D级'],
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value',
|
||||
},
|
||||
series: [
|
||||
{
|
||||
barWidth: 20,
|
||||
itemStyle: {
|
||||
color: itemColor(0),
|
||||
borderRadius: 10,
|
||||
},
|
||||
data: [120, 200, 150, 80],
|
||||
type: 'bar',
|
||||
},
|
||||
],
|
||||
});
|
||||
}
|
||||
|
||||
function initChart3(dom) {
|
||||
const chart = echarts.init(dom);
|
||||
chart.setOption({
|
||||
tooltip: {
|
||||
formatter: '<b>{b}</b> : {c}幢',
|
||||
},
|
||||
toolbox: {
|
||||
show: true,
|
||||
feature: {
|
||||
mark: { show: true },
|
||||
restore: { show: true },
|
||||
saveAsImage: { show: true },
|
||||
},
|
||||
},
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
data: ['A级', 'B级', 'C级', 'D级'],
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value',
|
||||
},
|
||||
series: [
|
||||
{
|
||||
data: [820, 932, 800, 900],
|
||||
showSymbol: false,
|
||||
lineStyle: {
|
||||
width: 5,
|
||||
shadowBlur: 5,
|
||||
shadowOffsetY: 3,
|
||||
shadowColor: echartsColors[0].from,
|
||||
opacity: 0.5,
|
||||
cap: 'round',
|
||||
},
|
||||
itemStyle: {
|
||||
color: itemColor(0),
|
||||
},
|
||||
type: 'line',
|
||||
smooth: true,
|
||||
},
|
||||
],
|
||||
});
|
||||
}
|
||||
|
||||
export default class charts extends Component {
|
||||
|
||||
componentDidMount() {
|
||||
initChart1(this.refs['chart-1'])
|
||||
initChart2(this.refs['chart-2'])
|
||||
initChart3(this.refs['chart-3'])
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<>
|
||||
<Row gutter={16}>
|
||||
<Col span={8}>
|
||||
<Card bordered={false} type="inner">
|
||||
<div style={{ height: '400px' }} ref="chart-1"></div>
|
||||
</Card>
|
||||
</Col>
|
||||
<Col span={16}>
|
||||
<Card bordered={false} type="inner">
|
||||
<div style={{ height: '400px' }} ref="chart-2"></div>
|
||||
</Card>
|
||||
</Col>
|
||||
</Row>
|
||||
<Card bordered={false} type="inner">
|
||||
<div style={{ height: '400px' }} ref="chart-3"></div>
|
||||
</Card>
|
||||
</>
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
import React, { Component } from 'react'
|
||||
import { Button, Card, Cascader, Divider, Form, Radio } from 'antd'
|
||||
import { Container } from 'components'
|
||||
|
||||
import StatisticsCharts from './charts'
|
||||
import StatisticsTable from './table'
|
||||
|
||||
export default class index extends Component {
|
||||
|
||||
state = {
|
||||
render: 'charts'
|
||||
}
|
||||
|
||||
static getDerivedStateFromProps(props) {
|
||||
return {
|
||||
render: props.type
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Container mode="fluid">
|
||||
<Card bordered={false} type="inner">
|
||||
<Form labelCol={{ flex: '70px' }} wrapperCol={{ flex: '1' }}>
|
||||
<Form.Item className="yo-filter-item" label="房屋性质">
|
||||
<Radio.Group defaultValue={''} buttonStyle="solid" size="small">
|
||||
<Radio.Button value={''}>全部</Radio.Button>
|
||||
<Radio.Button value="1">住宅</Radio.Button>
|
||||
<Radio.Button value="2">非住宅</Radio.Button>
|
||||
</Radio.Group>
|
||||
</Form.Item>
|
||||
<Divider className="mt-sm mb-sm" dashed />
|
||||
<Form.Item className="yo-filter-item" label="审核状态">
|
||||
<Radio.Group defaultValue={''} buttonStyle="solid" size="small">
|
||||
<Radio.Button value={''}>全部</Radio.Button>
|
||||
<Radio.Button value="1">待建档</Radio.Button>
|
||||
<Radio.Button value="2">暂存</Radio.Button>
|
||||
<Radio.Button value="3">待提交</Radio.Button>
|
||||
<Radio.Button value="4">退回</Radio.Button>
|
||||
<Radio.Button value="5">待审核</Radio.Button>
|
||||
<Radio.Button value="6">审核通过</Radio.Button>
|
||||
</Radio.Group>
|
||||
</Form.Item>
|
||||
<Divider className="mt-sm mb-sm" dashed />
|
||||
<Form.Item className="yo-filter-item" label="土地性质">
|
||||
<Radio.Group defaultValue={''} buttonStyle="solid" size="small">
|
||||
<Radio.Button value={''}>全部</Radio.Button>
|
||||
<Radio.Button value="1">国有土地</Radio.Button>
|
||||
<Radio.Button value="2">集体土地</Radio.Button>
|
||||
</Radio.Group>
|
||||
</Form.Item>
|
||||
<Divider className="mt-sm mb-sm" dashed />
|
||||
<Form.Item className="yo-filter-item" label="筛选范围">
|
||||
<Cascader className="w-400" placeholder="请选择区域" />
|
||||
</Form.Item>
|
||||
<Divider className="mt-sm mb-sm" dashed />
|
||||
<div className="text-center">
|
||||
<Button className="mr-xs" type="primary">查询</Button>
|
||||
<Button>导出Excel</Button>
|
||||
</div>
|
||||
</Form>
|
||||
</Card>
|
||||
{this.state.render == 'charts' && <StatisticsCharts />}
|
||||
{this.state.render == 'table' && <StatisticsTable />}
|
||||
</Container>
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,209 @@
|
||||
import React, { Component } from 'react'
|
||||
import { Card, Table } from 'antd'
|
||||
import { isEqual } from 'lodash'
|
||||
|
||||
const columns = [
|
||||
{
|
||||
title: '区域',
|
||||
dataIndex: 'area',
|
||||
width: 150,
|
||||
fixed: true
|
||||
},
|
||||
{
|
||||
title: '总数',
|
||||
children: [
|
||||
{
|
||||
title: '幢数(占比)',
|
||||
dataIndex: 'z',
|
||||
width: 120,
|
||||
},
|
||||
{
|
||||
title: '建筑面积(占比)',
|
||||
dataIndex: 'j',
|
||||
width: 120,
|
||||
},
|
||||
{
|
||||
title: '户数',
|
||||
dataIndex: 'h',
|
||||
width: 80,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: '房屋等级',
|
||||
children: [
|
||||
{
|
||||
title: '一级',
|
||||
children: [
|
||||
{
|
||||
title: '幢数',
|
||||
dataIndex: 'z1',
|
||||
width: 80,
|
||||
},
|
||||
{
|
||||
title: '建筑面积',
|
||||
dataIndex: 'j1',
|
||||
width: 80,
|
||||
},
|
||||
{
|
||||
title: '户数',
|
||||
dataIndex: 'h1',
|
||||
width: 80,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: '二级',
|
||||
children: [
|
||||
{
|
||||
title: '幢数',
|
||||
dataIndex: 'z2',
|
||||
width: 80,
|
||||
},
|
||||
{
|
||||
title: '建筑面积',
|
||||
dataIndex: 'j2',
|
||||
width: 80,
|
||||
},
|
||||
{
|
||||
title: '户数',
|
||||
dataIndex: 'h2',
|
||||
width: 80,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: '三级',
|
||||
children: [
|
||||
{
|
||||
title: '幢数',
|
||||
dataIndex: 'z3',
|
||||
width: 80,
|
||||
},
|
||||
{
|
||||
title: '建筑面积',
|
||||
dataIndex: 'j3',
|
||||
width: 80,
|
||||
},
|
||||
{
|
||||
title: '户数',
|
||||
dataIndex: 'h3',
|
||||
width: 80,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: '四级',
|
||||
children: [
|
||||
{
|
||||
title: '幢数',
|
||||
dataIndex: 'z4',
|
||||
width: 80,
|
||||
},
|
||||
{
|
||||
title: '建筑面积',
|
||||
dataIndex: 'j4',
|
||||
width: 80,
|
||||
},
|
||||
{
|
||||
title: '户数',
|
||||
dataIndex: 'h4',
|
||||
width: 80,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: 'C级',
|
||||
children: [
|
||||
{
|
||||
title: '幢数',
|
||||
dataIndex: 'zc',
|
||||
width: 80,
|
||||
},
|
||||
{
|
||||
title: '建筑面积',
|
||||
dataIndex: 'jc',
|
||||
width: 80,
|
||||
},
|
||||
{
|
||||
title: '户数',
|
||||
dataIndex: 'hc',
|
||||
width: 80,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: 'D级',
|
||||
children: [
|
||||
{
|
||||
title: '幢数',
|
||||
dataIndex: 'zd',
|
||||
width: 80,
|
||||
},
|
||||
{
|
||||
title: '建筑面积',
|
||||
dataIndex: 'jd',
|
||||
width: 80,
|
||||
},
|
||||
{
|
||||
title: '户数',
|
||||
dataIndex: 'hd',
|
||||
width: 80,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
]
|
||||
|
||||
const data = []
|
||||
for (let i = 0; i < 30; i++) {
|
||||
data.push({
|
||||
key: i,
|
||||
area: 'John Brown',
|
||||
z: 100,
|
||||
j: 1222.33,
|
||||
h: 39,
|
||||
z1: 20,
|
||||
j1: 20,
|
||||
h1: 20,
|
||||
z2: 20,
|
||||
j2: 20,
|
||||
h2: 20,
|
||||
z3: 20,
|
||||
j3: 20,
|
||||
h3: 20,
|
||||
z4: 20,
|
||||
j4: 20,
|
||||
h4: 20,
|
||||
zc: 20,
|
||||
jc: 20,
|
||||
hc: 20,
|
||||
zd: 20,
|
||||
jd: 20,
|
||||
hd: 20,
|
||||
})
|
||||
}
|
||||
|
||||
export default class table extends Component {
|
||||
|
||||
shouldComponentUpdate(props, state) {
|
||||
return !isEqual(this.state, state)
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Card bordered={false}>
|
||||
<Table
|
||||
columns={columns}
|
||||
dataSource={data}
|
||||
pagination={false}
|
||||
scroll={{ x: 'max-content' }}
|
||||
bordered
|
||||
size="middle"
|
||||
sticky
|
||||
/>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
import React, { Component } from 'react'
|
||||
|
||||
export default class index extends Component {
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
1
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
115
framework/web-react/src/pages/home/charts.jsx
Normal file
115
framework/web-react/src/pages/home/charts.jsx
Normal file
@@ -0,0 +1,115 @@
|
||||
import React, { Component } from 'react'
|
||||
import { Card, Dropdown, Form, Menu } from 'antd'
|
||||
import { AntIcon } from 'components'
|
||||
import * as echarts from 'echarts'
|
||||
|
||||
const options = {
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
},
|
||||
legend: {
|
||||
show: false,
|
||||
},
|
||||
grid: {
|
||||
left: '3%',
|
||||
right: '4%',
|
||||
bottom: '3%',
|
||||
containLabel: true,
|
||||
},
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
boundaryGap: false,
|
||||
data: ['周一', '周二', '周三', '周四', '周五', '周六', '周日'],
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value',
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: '邮件营销',
|
||||
type: 'line',
|
||||
stack: '总量',
|
||||
data: [120, 132, 101, 134, 90, 230, 210],
|
||||
},
|
||||
{
|
||||
name: '联盟广告',
|
||||
type: 'line',
|
||||
stack: '总量',
|
||||
data: [220, 182, 191, 234, 290, 330, 310],
|
||||
},
|
||||
{
|
||||
name: '视频广告',
|
||||
type: 'line',
|
||||
stack: '总量',
|
||||
data: [150, 232, 201, 154, 190, 330, 410],
|
||||
},
|
||||
{
|
||||
name: '直接访问',
|
||||
type: 'line',
|
||||
stack: '总量',
|
||||
data: [320, 332, 301, 334, 390, 330, 320],
|
||||
},
|
||||
{
|
||||
name: '搜索引擎',
|
||||
type: 'line',
|
||||
stack: '总量',
|
||||
data: [820, 932, 901, 934, 1290, 1330, 1320],
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
export default class charts extends Component {
|
||||
|
||||
componentDidMount() {
|
||||
const chartDom = this.refs.chart
|
||||
const myChart = echarts.init(chartDom)
|
||||
myChart.setOption(options)
|
||||
window.addEventListener('resize', () => {
|
||||
myChart.resize()
|
||||
})
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Card
|
||||
title="年度项目总完成情况"
|
||||
bordered={false}
|
||||
>
|
||||
<Form layout="inline">
|
||||
<Form.Item label="区域">
|
||||
<Dropdown
|
||||
overlay={
|
||||
<Menu>
|
||||
<Menu.Item>宁波市</Menu.Item>
|
||||
<Menu.Item>鄞州区</Menu.Item>
|
||||
</Menu>
|
||||
}
|
||||
>
|
||||
<span>
|
||||
宁波市
|
||||
<AntIcon type="down" />
|
||||
</span>
|
||||
</Dropdown>
|
||||
</Form.Item>
|
||||
<Form.Item label="年份">
|
||||
<Dropdown
|
||||
overlay={
|
||||
<Menu>
|
||||
<Menu.Item>2021</Menu.Item>
|
||||
<Menu.Item>2020</Menu.Item>
|
||||
<Menu.Item>2019</Menu.Item>
|
||||
</Menu>
|
||||
}
|
||||
>
|
||||
<span>
|
||||
2021
|
||||
<AntIcon type="down" />
|
||||
</span>
|
||||
</Dropdown>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
<div ref="chart" style={{ height: '300px' }} />
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
}
|
||||
102
framework/web-react/src/pages/home/index.jsx
Normal file
102
framework/web-react/src/pages/home/index.jsx
Normal file
@@ -0,0 +1,102 @@
|
||||
import React, { Component } from 'react'
|
||||
import { Row, Col, Divider } from 'antd'
|
||||
import { isEqual } from 'lodash'
|
||||
import store from 'store'
|
||||
import { Container, Image, AntIcon } from 'components'
|
||||
import moment from 'moment'
|
||||
|
||||
import Statistics from './statistics'
|
||||
import Task from './task'
|
||||
import List from './list'
|
||||
import Notice from './notice'
|
||||
import Charts from './charts'
|
||||
|
||||
const { getState, subscribe } = store
|
||||
|
||||
const storePath = 'user'
|
||||
|
||||
export default class index extends Component {
|
||||
state = {
|
||||
[storePath]: getState(storePath),
|
||||
}
|
||||
|
||||
constructor(props) {
|
||||
super(props)
|
||||
|
||||
this.unsubscribe = subscribe(storePath, () => {
|
||||
this.setState(getState(storePath))
|
||||
})
|
||||
}
|
||||
|
||||
shouldComponentUpdate(props, state) {
|
||||
return !isEqual(this.state, state)
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this.unsubscribe()
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<>
|
||||
<div className="home-header">
|
||||
<Container mode="fluid">
|
||||
<Row align="middle" justify="space-between" type="flex">
|
||||
<Col>
|
||||
<div className="home-header-row">
|
||||
<div className="home-header-avatar">
|
||||
<Image
|
||||
id={this.state.user.avatar}
|
||||
size={64}
|
||||
icon={<AntIcon type="UserOutlined" />}
|
||||
type="avatar"
|
||||
/>
|
||||
</div>
|
||||
<div className="home-header-content">
|
||||
<h4>
|
||||
{moment().format('A')}好,
|
||||
<span>
|
||||
{this.state.user.nickName || this.state.user.name}
|
||||
</span>
|
||||
,欢迎您登录系统!
|
||||
</h4>
|
||||
<div>
|
||||
<span>上次IP:{this.state.user.lastLoginIp}</span>
|
||||
<Divider type="vertical" />
|
||||
<span>
|
||||
上次登录时间:{this.state.user.lastLoginTime}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Col>
|
||||
<Col>
|
||||
<AntIcon
|
||||
type="MailOutlined"
|
||||
style={{ fontSize: '20px', color: '#f80000' }}
|
||||
className="mr-xs"
|
||||
/>
|
||||
您有<a href="/">0</a>封未读邮件,请尽快查收!
|
||||
</Col>
|
||||
</Row>
|
||||
</Container>
|
||||
</div>
|
||||
<Container mode="fluid" className="home-container">
|
||||
<Row gutter={16}>
|
||||
<Col span={24}>
|
||||
<Statistics />
|
||||
</Col>
|
||||
<Col lg={12} md={24} xl={16}>
|
||||
<Task />
|
||||
<List />
|
||||
</Col>
|
||||
<Col lg={12} md={24} xl={8}>
|
||||
<Notice />
|
||||
<Charts />
|
||||
</Col>
|
||||
</Row>
|
||||
</Container>
|
||||
</>
|
||||
)
|
||||
}
|
||||
}
|
||||
103
framework/web-react/src/pages/home/list.jsx
Normal file
103
framework/web-react/src/pages/home/list.jsx
Normal file
@@ -0,0 +1,103 @@
|
||||
import React, { Component } from 'react'
|
||||
import { Card, Table } from 'antd'
|
||||
|
||||
const tabList = [
|
||||
{
|
||||
key: '1',
|
||||
tab: '新建项目',
|
||||
},
|
||||
{
|
||||
key: '2',
|
||||
tab: '正在签约项目',
|
||||
},
|
||||
{
|
||||
key: '3',
|
||||
tab: '完成签约项目',
|
||||
},
|
||||
{
|
||||
key: '4',
|
||||
tab: '项目进度',
|
||||
},
|
||||
]
|
||||
|
||||
const columns = [
|
||||
{
|
||||
title: '区域',
|
||||
dataIndex: 'area',
|
||||
},
|
||||
{
|
||||
title: '项目名称',
|
||||
dataIndex: 'title',
|
||||
},
|
||||
{
|
||||
title: '户数',
|
||||
dataIndex: 'count',
|
||||
},
|
||||
{
|
||||
title: '时间',
|
||||
dataIndex: 'date',
|
||||
},
|
||||
]
|
||||
|
||||
const data = [
|
||||
{
|
||||
key: '1',
|
||||
area: '海曙区',
|
||||
title: '曙光电影院地块',
|
||||
count: 13,
|
||||
date: '2021-01-01',
|
||||
},
|
||||
{
|
||||
key: '2',
|
||||
area: '江北区',
|
||||
title: '大庆新村地块旧城区改建项目',
|
||||
count: 322,
|
||||
date: '2021-01-01',
|
||||
},
|
||||
{
|
||||
key: '3',
|
||||
area: '宁海县',
|
||||
title: '桥头胡街道旧城区改造华驰文教地块',
|
||||
count: 1,
|
||||
date: '2021-01-01',
|
||||
},
|
||||
{
|
||||
key: '4',
|
||||
area: '慈溪市',
|
||||
title: '七二三南延道路工程',
|
||||
count: 1,
|
||||
date: '2021-01-01',
|
||||
},
|
||||
{
|
||||
key: '5',
|
||||
area: '北仑区',
|
||||
title: '原粮食局宿舍楼1号、2号楼(太河路北延工程)',
|
||||
count: 32,
|
||||
date: '2021-01-01',
|
||||
},
|
||||
]
|
||||
|
||||
export default class list extends Component {
|
||||
|
||||
state = {
|
||||
key: '1'
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Card
|
||||
activeTabKey={this.state.key}
|
||||
tabList={tabList}
|
||||
bordered={false}
|
||||
bodyStyle={{ padding: 0 }}
|
||||
onTabChange={key => this.setState({ key })}
|
||||
>
|
||||
<Table
|
||||
columns={columns}
|
||||
dataSource={data}
|
||||
pagination={false}
|
||||
/>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
}
|
||||
34
framework/web-react/src/pages/home/notice.jsx
Normal file
34
framework/web-react/src/pages/home/notice.jsx
Normal file
@@ -0,0 +1,34 @@
|
||||
import React, { Component } from 'react'
|
||||
import { Card, List } from 'antd'
|
||||
import { AntIcon } from 'components'
|
||||
import moment from 'moment'
|
||||
|
||||
const data = [
|
||||
{ title: '关于2020年度房屋征收评估机构信用考核情况的通报' },
|
||||
{ title: '关于2020年度房屋征收评估机构信用考核情况的通报' },
|
||||
{ title: '关于2020年度房屋征收评估机构信用考核情况的通报' },
|
||||
]
|
||||
|
||||
export default class notice extends Component {
|
||||
render() {
|
||||
return (
|
||||
<Card
|
||||
bordered={false}
|
||||
title="通知"
|
||||
extra={<a>更多</a>}
|
||||
>
|
||||
<List dataSource={data} itemLayout="horizontal" renderItem={
|
||||
(item) => (
|
||||
<List.Item>
|
||||
<List.Item.Meta
|
||||
title={item.title}
|
||||
description={moment().format('YYYY-MM-DD HH:mm:ss')}
|
||||
avatar={<AntIcon style={{ fontSize: '18px' }} type="MessageTwoTone" />}
|
||||
/>
|
||||
</List.Item>
|
||||
)
|
||||
} />
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
}
|
||||
61
framework/web-react/src/pages/home/statistics.jsx
Normal file
61
framework/web-react/src/pages/home/statistics.jsx
Normal file
@@ -0,0 +1,61 @@
|
||||
import React, { Component } from 'react'
|
||||
import { Card, Col, Dropdown, Menu, Row, Statistic } from 'antd'
|
||||
import { AntIcon } from 'components'
|
||||
|
||||
export default class statistics extends Component {
|
||||
render() {
|
||||
return (
|
||||
<Row gutter={16}>
|
||||
<Col lg={5} md={8} sm={24}>
|
||||
<Card bordered={false} hoverable={true}>
|
||||
<Statistic value={0} className="text-center" title="已完成的项目" />
|
||||
</Card>
|
||||
</Col>
|
||||
<Col lg={5} md={8} sm={24}>
|
||||
<Card bordered={false} hoverable={true}>
|
||||
<Statistic value={6} className="text-center" title="正在进行的项目" />
|
||||
</Card>
|
||||
</Col>
|
||||
<Col lg={5} md={8} sm={24}>
|
||||
<Card bordered={false} hoverable={true}>
|
||||
<Statistic value={0} className="text-center" title="还未开始的项目" />
|
||||
</Card>
|
||||
</Col>
|
||||
<Col lg={9} md={24}>
|
||||
<Card bordered={false} hoverable={true}>
|
||||
<Row>
|
||||
<Col span={12}>
|
||||
<Statistic value={8893} className="text-center" title="用户总量" />
|
||||
</Col>
|
||||
<Col span={12}>
|
||||
<Statistic
|
||||
value={1255}
|
||||
valueStyle={{ color: '#3f8600' }}
|
||||
className="text-center"
|
||||
title={
|
||||
<Dropdown
|
||||
overlay={
|
||||
<Menu>
|
||||
<Menu.Item>当月活跃用户</Menu.Item>
|
||||
<Menu.Item>当年活跃用户</Menu.Item>
|
||||
</Menu>
|
||||
}
|
||||
>
|
||||
<span>
|
||||
当月活跃用户
|
||||
<AntIcon type="down" />
|
||||
</span>
|
||||
</Dropdown>
|
||||
}
|
||||
prefix={
|
||||
<AntIcon style={{ fontSize: '13px' }} type="arrow-up" />
|
||||
}
|
||||
></Statistic>
|
||||
</Col>
|
||||
</Row>
|
||||
</Card>
|
||||
</Col>
|
||||
</Row>
|
||||
)
|
||||
}
|
||||
}
|
||||
65
framework/web-react/src/pages/home/task.jsx
Normal file
65
framework/web-react/src/pages/home/task.jsx
Normal file
@@ -0,0 +1,65 @@
|
||||
import React, { Component } from 'react'
|
||||
import { Avatar, Card, Col, Row, Tooltip } from 'antd'
|
||||
|
||||
const data = [
|
||||
{
|
||||
title: '市区雷公巷地块项目选择评估机构及上传相关材料(软件开发人员)',
|
||||
avatar: 'https://tb1.bdstatic.com/tb/steam.jpeg',
|
||||
},
|
||||
{
|
||||
title: '宁海县山河岭6号地块备案(胡靖)',
|
||||
avatar:
|
||||
'https://gss0.bdstatic.com/6LZ1dD3d1sgCo2Kml5_Y_D3/sys/portrait/item/tb.1.ac342cde.2vNGrtpPcIUN6lJpSnty3g?t=1615176031',
|
||||
},
|
||||
{ title: '宁海县盛宁线力洋至胡陈段公路工程田交朱村 地块备案(胡靖)' },
|
||||
{
|
||||
title: '慈溪市慈溪市危旧房改造一期(西门小区A1区块)项目备案(陆承)',
|
||||
avatar:
|
||||
'https://gss0.bdstatic.com/6LZ1dD3d1sgCo2Kml5_Y_D3/sys/portrait/item/tb.1.54e2faca.uBtqRshdnVUXL9XFfQMTwg?t=1604074726',
|
||||
},
|
||||
{ title: '江北区孔浦成片危旧住宅区改造项目(六号区块)备案(成薇)' },
|
||||
{ title: '镇海区宁镇路改扩建工程(庄市段)备案(董力)' },
|
||||
{ title: '鄞州区茶桃公路(同谷路—金峨路延伸段)项目备案(软件开发人员)' },
|
||||
{ title: '鄞州区咸祥大嵩湖工程备案(软件开发人员)' },
|
||||
{ title: '江北区三官堂大桥及接线工程项目备案(成薇)' },
|
||||
]
|
||||
|
||||
export default class list extends Component {
|
||||
render() {
|
||||
return (
|
||||
<Card bordered={false} title="待办" extra={<a>更多</a>}>
|
||||
{
|
||||
data.map((item, i) => {
|
||||
return (
|
||||
<Card.Grid key={i}>
|
||||
<Card.Meta
|
||||
title={
|
||||
<Tooltip placement="top" title={item.title}>
|
||||
{item.title}
|
||||
</Tooltip>
|
||||
}
|
||||
description={
|
||||
<Row align="middle">
|
||||
{
|
||||
item.avatar &&
|
||||
<Col flex="32px">
|
||||
<Avatar size={24} src={item.avatar} shape="square" />
|
||||
</Col>
|
||||
}
|
||||
<Col flex="auto">
|
||||
<Row justify="space-between" type="flex">
|
||||
<Col>软件开发人员</Col>
|
||||
<Col>2020-01-01</Col>
|
||||
</Row>
|
||||
</Col>
|
||||
</Row>
|
||||
}
|
||||
/>
|
||||
</Card.Grid>
|
||||
)
|
||||
})
|
||||
}
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
}
|
||||
193
framework/web-react/src/pages/system/account/base.jsx
Normal file
193
framework/web-react/src/pages/system/account/base.jsx
Normal file
@@ -0,0 +1,193 @@
|
||||
import React, { Component } from 'react'
|
||||
import { Button, Card, Col, Descriptions, Modal, Row, Spin, Tooltip, Upload } from 'antd'
|
||||
import { AntIcon, Image } from 'components'
|
||||
import { Cropper } from 'react-cropper'
|
||||
import 'cropperjs/dist/cropper.css'
|
||||
import { BlobToFile } from 'util/file'
|
||||
|
||||
import { api } from 'common/api'
|
||||
|
||||
export default class base extends Component {
|
||||
state = {
|
||||
img: true,
|
||||
|
||||
cropperVisible: false,
|
||||
loadingAvatar: false,
|
||||
}
|
||||
|
||||
cropper = React.createRef()
|
||||
|
||||
avatarFile = null
|
||||
|
||||
async onOpenAvatarCropper() {
|
||||
this.setState({ cropperVisible: true })
|
||||
}
|
||||
|
||||
onCloseAvatarCropper() {
|
||||
this.setState({ cropperVisible: false }, () => {
|
||||
setTimeout(() => {
|
||||
const cropper = this.cropper.current && this.cropper.current.cropper
|
||||
if (cropper) {
|
||||
cropper.destroy()
|
||||
}
|
||||
this.avatarFile = null
|
||||
this.setState({ img: true })
|
||||
}, 300)
|
||||
})
|
||||
}
|
||||
|
||||
onUploadAvatar() {
|
||||
this.setState({ loadingAvatar: true })
|
||||
const canvas = this.cropper.current.cropper.getCroppedCanvas()
|
||||
canvas.toBlob(async data => {
|
||||
try {
|
||||
const file = BlobToFile(data, this.avatarFile.name, this.avatarFile.type)
|
||||
const fd = new FormData()
|
||||
fd.append('file', file)
|
||||
const { data: avatar } = await api.sysFileInfoUpload(fd)
|
||||
await api.sysUserUpdateInfo({ avatar })
|
||||
this.onCloseAvatarCropper()
|
||||
this.props.loadData()
|
||||
} finally {
|
||||
this.setState({ loadingAvatar: false })
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
render() {
|
||||
const { user } = this.props
|
||||
|
||||
const { img, cropperVisible, loadingAvatar } = this.state
|
||||
|
||||
const cropper = this.cropper.current && this.cropper.current.cropper
|
||||
|
||||
return (
|
||||
<>
|
||||
<Card title="我的信息">
|
||||
<div className="yo-avatar-info">
|
||||
<Image id={user.avatar} type="avatar" size={128} />
|
||||
<div
|
||||
onClick={() => this.onOpenAvatarCropper()}
|
||||
className="yo-avatar-info--cover"
|
||||
>
|
||||
<AntIcon type="sync" />
|
||||
</div>
|
||||
</div>
|
||||
<br />
|
||||
<Descriptions column={1} labelStyle={{ width: '100px' }}>
|
||||
<Descriptions.Item label="用户名">{user.name}</Descriptions.Item>
|
||||
<Descriptions.Item label="昵称">{user.nickName}</Descriptions.Item>
|
||||
<Descriptions.Item label="帐号">{user.account}</Descriptions.Item>
|
||||
<Descriptions.Item label="性别">
|
||||
{user.sex === 0
|
||||
? '保密'
|
||||
: user.sex === 1
|
||||
? '男性'
|
||||
: user.sex === 2
|
||||
? '女性'
|
||||
: '保密'}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="生日">
|
||||
{user.birthday &&
|
||||
(typeof user.birthday === 'string'
|
||||
? user.birthday
|
||||
: user.birthday.format('YYYY-MM-DD'))}
|
||||
</Descriptions.Item>
|
||||
</Descriptions>
|
||||
</Card>
|
||||
<Modal
|
||||
visible={cropperVisible}
|
||||
width={800}
|
||||
footer={false}
|
||||
destroyOnClose
|
||||
onCancel={() => this.onCloseAvatarCropper()}
|
||||
>
|
||||
<Spin spinning={loadingAvatar} indicator={<AntIcon type="loading" />}>
|
||||
<Row gutter={16} align="middle">
|
||||
<Col span={12}>
|
||||
<div className="yo-avatar-cropper">
|
||||
<Cropper
|
||||
ref={this.cropper}
|
||||
src={img}
|
||||
style={{ width: '100%', height: 240 }}
|
||||
cropBoxResizable={false}
|
||||
aspectRatio={1}
|
||||
preview=".yo-avatar-preview"
|
||||
/>
|
||||
</div>
|
||||
</Col>
|
||||
<Col span={12}>
|
||||
<div className="yo-avatar-preview" />
|
||||
</Col>
|
||||
<Col span={12} className="mt-md text-center">
|
||||
<Upload
|
||||
beforeUpload={file => {
|
||||
this.avatarFile = file
|
||||
const reader = new FileReader()
|
||||
reader.readAsDataURL(file)
|
||||
reader.onload = () => {
|
||||
this.setState({ img: reader.result })
|
||||
}
|
||||
return false
|
||||
}}
|
||||
showUploadList={false}
|
||||
className="mr-xs"
|
||||
>
|
||||
<Button type="primary" icon={<AntIcon type="picture" />}>
|
||||
选择图片
|
||||
</Button>
|
||||
</Upload>
|
||||
|
||||
<Button.Group>
|
||||
<Tooltip placement="bottom" title="放大">
|
||||
<Button
|
||||
disabled={!cropper}
|
||||
type="text"
|
||||
onClick={() => cropper.zoom(0.1)}
|
||||
icon={<AntIcon type="zoom-in" />}
|
||||
/>
|
||||
</Tooltip>
|
||||
<Tooltip placement="bottom" title="缩小">
|
||||
<Button
|
||||
disabled={!cropper}
|
||||
type="text"
|
||||
onClick={() => cropper.zoom(-0.1)}
|
||||
icon={<AntIcon type="zoom-out" />}
|
||||
/>
|
||||
</Tooltip>
|
||||
<Tooltip placement="bottom" title="左旋">
|
||||
<Button
|
||||
disabled={!cropper}
|
||||
type="text"
|
||||
onClick={() => cropper.rotate(-90)}
|
||||
icon={<AntIcon type="undo" />}
|
||||
/>
|
||||
</Tooltip>
|
||||
<Tooltip placement="bottom" title="右旋">
|
||||
<Button
|
||||
disabled={!cropper}
|
||||
type="text"
|
||||
onClick={() => cropper.rotate(90)}
|
||||
icon={<AntIcon type="redo" />}
|
||||
/>
|
||||
</Tooltip>
|
||||
</Button.Group>
|
||||
</Col>
|
||||
<Col span={12} className="mt-md text-center">
|
||||
<Button
|
||||
disabled={!cropper}
|
||||
type="primary"
|
||||
className="mr-xs"
|
||||
onClick={() => this.onUploadAvatar()}
|
||||
>
|
||||
确认
|
||||
</Button>
|
||||
<Button onClick={() => this.onCloseAvatarCropper()}>取消</Button>
|
||||
</Col>
|
||||
</Row>
|
||||
</Spin>
|
||||
</Modal>
|
||||
</>
|
||||
)
|
||||
}
|
||||
}
|
||||
113
framework/web-react/src/pages/system/account/index.jsx
Normal file
113
framework/web-react/src/pages/system/account/index.jsx
Normal file
@@ -0,0 +1,113 @@
|
||||
import React, { Component } from 'react'
|
||||
import ReactDOM from 'react-dom'
|
||||
import { Anchor, Card, Col, Row, Spin } from 'antd'
|
||||
import { AntIcon, Container } from 'components'
|
||||
import moment from 'moment'
|
||||
import store from 'store'
|
||||
import { api } from 'common/api'
|
||||
|
||||
import Base from './base'
|
||||
import Info from './setting/info'
|
||||
import Safety from './setting/satety'
|
||||
|
||||
const { getState, dispatch, subscribe } = store
|
||||
|
||||
const navs = [
|
||||
{ title: '基本信息', component: Info },
|
||||
{ title: '安全设置', component: Safety },
|
||||
]
|
||||
|
||||
export default class index extends Component {
|
||||
state = {
|
||||
loading: false,
|
||||
user: getState('user'),
|
||||
}
|
||||
|
||||
container = window
|
||||
|
||||
constructor(props) {
|
||||
super(props)
|
||||
|
||||
this.unsubscribe = subscribe('user', user => {
|
||||
this.setState({ user })
|
||||
})
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.loadData()
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this.unsubscribe()
|
||||
}
|
||||
|
||||
loadData = async () => {
|
||||
this.setState({ loading: true })
|
||||
try {
|
||||
const { data } = await api.getLoginUser()
|
||||
if (data.birthday) {
|
||||
data.birthday = moment(data.birthday)
|
||||
}
|
||||
dispatch({
|
||||
type: 'SET_USER_ACCOUNT',
|
||||
user: data,
|
||||
})
|
||||
} finally {
|
||||
this.setState({ loading: false })
|
||||
}
|
||||
}
|
||||
|
||||
setContainer = container => {
|
||||
this.container = (ReactDOM.findDOMNode(container) || {}).parentNode
|
||||
}
|
||||
|
||||
render() {
|
||||
const { loadData } = this
|
||||
|
||||
const { loading, user } = this.state
|
||||
|
||||
return (
|
||||
<Spin spinning={loading} indicator={<AntIcon type="loading" />} ref={this.setContainer}>
|
||||
<Container mode="fluid">
|
||||
<Row gutter={16}>
|
||||
<Col flex="200px">
|
||||
<Anchor
|
||||
getContainer={() => this.container}
|
||||
offsetTop={24}
|
||||
targetOffset={100}
|
||||
wrapperStyle={{ backgroundColor: 'transparent' }}
|
||||
onClick={e => e.preventDefault()}
|
||||
>
|
||||
{navs.map((item, i) => (
|
||||
<Anchor.Link
|
||||
key={i}
|
||||
href={`#account-setting-${i}`}
|
||||
title={item.title}
|
||||
/>
|
||||
))}
|
||||
</Anchor>
|
||||
</Col>
|
||||
<Col flex="1">
|
||||
<Row gutter={16}>
|
||||
<Col xl={{ span: 10, order: 2 }}>
|
||||
<br />
|
||||
<Base user={user} loadData={loadData} />
|
||||
</Col>
|
||||
<Col xl={{ span: 14, order: 1 }}>
|
||||
{navs.map((item, i) => (
|
||||
<section key={i} id={`account-setting-${i}`}>
|
||||
<br />
|
||||
<Card title={item.title}>
|
||||
<item.component user={user} loadData={loadData} />
|
||||
</Card>
|
||||
</section>
|
||||
))}
|
||||
</Col>
|
||||
</Row>
|
||||
</Col>
|
||||
</Row>
|
||||
</Container>
|
||||
</Spin>
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
import React, { Component } from 'react'
|
||||
import { Button, DatePicker, Form, Input, message as Message, Radio } from 'antd'
|
||||
import { AntIcon } from 'components'
|
||||
import { api } from 'common/api'
|
||||
import moment from 'moment'
|
||||
|
||||
export default class index extends Component {
|
||||
state = {
|
||||
saving: false,
|
||||
}
|
||||
|
||||
form = React.createRef()
|
||||
|
||||
componentDidMount() {
|
||||
const { user } = this.props
|
||||
if (user.birthday) {
|
||||
user.birthday = moment(user.birthday)
|
||||
}
|
||||
this.form.current.setFieldsValue(user)
|
||||
}
|
||||
|
||||
async onSvaeInfo() {
|
||||
this.setState({ saving: true })
|
||||
try {
|
||||
await api.sysUserUpdateInfo(this.form.current.getFieldsValue())
|
||||
await this.props.loadData()
|
||||
Message.success('更新个人信息成功')
|
||||
} finally {
|
||||
this.setState({ saving: false })
|
||||
}
|
||||
}
|
||||
onAvatarStart() {}
|
||||
|
||||
render() {
|
||||
const { user } = this.props
|
||||
|
||||
const { saving } = this.state
|
||||
|
||||
return (
|
||||
<>
|
||||
<Form className="yo-form yo-form--no-border" ref={this.form}>
|
||||
<div className="yo-form-group yo-form--short">
|
||||
<Form.Item label="昵称" name="nickName">
|
||||
<Input autoComplete="off" placeholder="请输入昵称" />
|
||||
</Form.Item>
|
||||
<Form.Item label="用户名">
|
||||
<span>{user.name}</span>
|
||||
</Form.Item>
|
||||
<Form.Item label="生日" name="birthday">
|
||||
<DatePicker className="w-100-p" placeholder="请选择生日" />
|
||||
</Form.Item>
|
||||
<Form.Item label="性别" name="sex">
|
||||
<Radio.Group>
|
||||
<Radio.Button value={0}>
|
||||
<AntIcon className="mr-xxs" type="stop" />
|
||||
<span>保密</span>
|
||||
</Radio.Button>
|
||||
<Radio.Button value={1}>
|
||||
<AntIcon
|
||||
style={{ color: '#1890ff' }}
|
||||
className="mr-xxs"
|
||||
type="man"
|
||||
/>
|
||||
<span>男</span>
|
||||
</Radio.Button>
|
||||
<Radio.Button value={2}>
|
||||
<AntIcon
|
||||
style={{ color: '#eb2f96' }}
|
||||
className="mr-xxs"
|
||||
type="woman"
|
||||
/>
|
||||
<span>女</span>
|
||||
</Radio.Button>
|
||||
</Radio.Group>
|
||||
</Form.Item>
|
||||
<Form.Item label="电话" name="tel">
|
||||
<Input autoComplete="off" placeholder="请输入电话" />
|
||||
</Form.Item>
|
||||
</div>
|
||||
</Form>
|
||||
<Button loading={saving} onClick={() => this.onSvaeInfo()} block type="text">
|
||||
更新个人信息
|
||||
</Button>
|
||||
</>
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
import React, { Component } from 'react'
|
||||
import { List } from 'antd'
|
||||
import { api } from 'common/api'
|
||||
import { AntIcon, ModalForm, QueryTableActions } from 'components'
|
||||
import PasswordForm from './password'
|
||||
|
||||
import Mail from './mail'
|
||||
import Phone from './phone'
|
||||
|
||||
const apiAction = {
|
||||
updatePwd: api.sysUserUpdatePwd,
|
||||
}
|
||||
|
||||
export default class form extends Component {
|
||||
updatePwdForm = React.createRef()
|
||||
mailForm = React.createRef()
|
||||
mhoneForm = React.createRef()
|
||||
|
||||
onOpen(modal) {
|
||||
modal.current.open()
|
||||
}
|
||||
|
||||
render() {
|
||||
const { user, loadData } = this.props
|
||||
|
||||
const index = []
|
||||
//密码
|
||||
index.push({
|
||||
title: '登录密码',
|
||||
description:
|
||||
'安全性高的密码可以使帐号更安全。建议您定期更换密码,设置一个包含字母,符号或数字中至少两项且长度超过6位的密码。',
|
||||
// extra: (
|
||||
// <div>
|
||||
// 当前密码强度为:
|
||||
// {
|
||||
// [
|
||||
// <span class="text-error">弱</span>,
|
||||
// <span class="text-warning">中</span>,
|
||||
// <span class="text-success">强</span>,
|
||||
// ][user.securityLevel - 1]
|
||||
// }
|
||||
// </div>
|
||||
// ),
|
||||
done: true,
|
||||
action: () => {
|
||||
this.onOpen(this.updatePwdForm)
|
||||
},
|
||||
})
|
||||
//手机
|
||||
index.push({
|
||||
title: '手机绑定',
|
||||
description: (
|
||||
<div>
|
||||
手机号可以直接用于登录、找回密码等。
|
||||
{user.phone && (
|
||||
<>
|
||||
您已绑定了手机<b>{user.phone}</b>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
),
|
||||
done: !!user.phone,
|
||||
action: () => {
|
||||
this.onOpen(this.mhoneForm)
|
||||
},
|
||||
})
|
||||
//邮箱
|
||||
index.push({
|
||||
title: '邮箱绑定',
|
||||
description: (
|
||||
<div>
|
||||
安全邮箱可以直接用于登录、找回密码等。
|
||||
{user.email && (
|
||||
<>
|
||||
您已绑定了邮箱<b>{user.email}</b>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
),
|
||||
done: !!user.email,
|
||||
action: () => {
|
||||
this.onOpen(this.mailForm)
|
||||
},
|
||||
})
|
||||
|
||||
return (
|
||||
<>
|
||||
<List
|
||||
dataSource={index}
|
||||
itemLayout="vertical"
|
||||
renderItem={item => (
|
||||
<List.Item
|
||||
extra={item.extra}
|
||||
actions={[
|
||||
item.done == true ? (
|
||||
<QueryTableActions>
|
||||
<span className="text-success">
|
||||
<AntIcon className="mr-xxs" type="check-circle" />
|
||||
已设置
|
||||
</span>
|
||||
<a onClick={item.action}>修改</a>
|
||||
</QueryTableActions>
|
||||
) : (
|
||||
<QueryTableActions>
|
||||
<span className="text-warning">
|
||||
<AntIcon className="mr-xxs" type="exclamation-circle" />
|
||||
未设置
|
||||
</span>
|
||||
<a onClick={item.action}>设置</a>
|
||||
</QueryTableActions>
|
||||
),
|
||||
]}
|
||||
>
|
||||
<List.Item.Meta description={item.description} title={item.title} />
|
||||
</List.Item>
|
||||
)}
|
||||
/>
|
||||
{/* <ModalForm title={`更新密码`} action={apiAction.updatePwd} ref={this.updatePwdForm}> */}
|
||||
<PasswordForm ref={this.updatePwdForm} loadData={loadData} />
|
||||
{/* </ModalForm> */}
|
||||
<Phone ref={this.mhoneForm} loadData={loadData} />
|
||||
<Mail ref={this.mailForm} loadData={loadData} />
|
||||
</>
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,376 @@
|
||||
import React, { Component } from 'react'
|
||||
import { Form, Input, Modal, Spin, Steps, Button, Row, Col, message as Message, Select } from 'antd'
|
||||
import { AntIcon } from 'components'
|
||||
import { api } from 'common/api'
|
||||
import { COUNT_DWON_KEY } from 'common/storage'
|
||||
import store from 'store'
|
||||
import { cloneDeep } from 'lodash'
|
||||
|
||||
const { getState } = store
|
||||
|
||||
const steps = [
|
||||
{
|
||||
title: '验证',
|
||||
},
|
||||
{
|
||||
title: '绑定',
|
||||
},
|
||||
]
|
||||
|
||||
const reg = /^([a-zA-Z]|[0-9])(\w|)+@[a-zA-Z0-9]+\.([a-zA-Z]{2,4})$/
|
||||
|
||||
const initState = {
|
||||
visible: false,
|
||||
loading: false,
|
||||
|
||||
// 可用于验证的类型
|
||||
types: [],
|
||||
// 当前步骤
|
||||
currentStep: 0,
|
||||
|
||||
nextDisabled: true,
|
||||
// 正在发送验证码
|
||||
sendingCode: false,
|
||||
// 冷却时间
|
||||
countDown: 0,
|
||||
}
|
||||
|
||||
export default class form extends Component {
|
||||
state = cloneDeep(initState)
|
||||
|
||||
form = React.createRef()
|
||||
|
||||
orgCode = ''
|
||||
|
||||
//打开窗口
|
||||
open = () => {
|
||||
this.setState({ visible: true })
|
||||
|
||||
this.showCountDown()
|
||||
|
||||
const data = getState('user')
|
||||
const types = []
|
||||
data.phone &&
|
||||
types.push({
|
||||
title: `使用手机号${data.phone}进行验证`,
|
||||
value: 1,
|
||||
})
|
||||
data.email &&
|
||||
types.push({
|
||||
title: `使用邮箱${data.email}进行验证`,
|
||||
value: 2,
|
||||
})
|
||||
|
||||
this.setState({ types })
|
||||
}
|
||||
|
||||
close() {
|
||||
this.setState(cloneDeep(initState))
|
||||
}
|
||||
|
||||
/**
|
||||
* 将倒计时添加入到本地
|
||||
*/
|
||||
addTime() {
|
||||
const now = Date.now() + 60 * 1000
|
||||
window.localStorage.setItem(COUNT_DWON_KEY, now)
|
||||
}
|
||||
/**
|
||||
* 显示倒计时
|
||||
*/
|
||||
showCountDown() {
|
||||
const surplusTime = window.localStorage.getItem(COUNT_DWON_KEY)
|
||||
const nowTime = Date.now()
|
||||
if (surplusTime >= nowTime) {
|
||||
this.setState({
|
||||
countDown: parseInt((surplusTime - nowTime) / 1000),
|
||||
})
|
||||
setTimeout(() => {
|
||||
this.showCountDown()
|
||||
}, 1000)
|
||||
} else {
|
||||
this.setState({ countDown: 0 })
|
||||
}
|
||||
}
|
||||
|
||||
//发送验证码
|
||||
async onSendCode() {
|
||||
const form = this.form.current
|
||||
|
||||
const valid = await form.validateFields()
|
||||
if (!valid) {
|
||||
return
|
||||
}
|
||||
|
||||
this.setState({ sendingCode: true })
|
||||
|
||||
const data = form.getFieldsValue()
|
||||
try {
|
||||
await api.sysUserSendCode(data)
|
||||
const typeName = data.type ? [, '手机', '邮箱'][data.type] : '邮箱'
|
||||
Message.success(`已发送验证码到${typeName},请注意查收`)
|
||||
this.addTime()
|
||||
this.showCountDown()
|
||||
} finally {
|
||||
this.setState({ sendingCode: false })
|
||||
}
|
||||
}
|
||||
|
||||
// 下一步
|
||||
async onNext() {
|
||||
this.setState({ loading: true })
|
||||
const data = this.form.current.getFieldsValue()
|
||||
this.orgCode = data.orgCode
|
||||
|
||||
try {
|
||||
await api.sysUserCheckBindcode(data)
|
||||
window.localStorage.removeItem(COUNT_DWON_KEY)
|
||||
this.setState({
|
||||
nextDisabled: true,
|
||||
currentStep: this.state.currentStep + 1,
|
||||
})
|
||||
} finally {
|
||||
this.setState({ loading: false })
|
||||
}
|
||||
}
|
||||
|
||||
// 上一步
|
||||
onPrev() {
|
||||
window.localStorage.removeItem(COUNT_DWON_KEY)
|
||||
this.setState({
|
||||
currentStep: this.state.currentStep - 1,
|
||||
})
|
||||
}
|
||||
|
||||
//完成
|
||||
async onComplete() {
|
||||
this.setState({ loading: true })
|
||||
try {
|
||||
await api.sysUserCheckBindcode({
|
||||
...this.form.current.getFieldsValue(),
|
||||
orgCode: this.orgCode,
|
||||
})
|
||||
await this.props.loadData()
|
||||
window.localStorage.removeItem(COUNT_DWON_KEY)
|
||||
Message.success('绑定邮箱成功')
|
||||
this.close()
|
||||
} finally {
|
||||
this.setState({ loading: false })
|
||||
}
|
||||
}
|
||||
|
||||
renderForm() {
|
||||
const { nextDisabled, sendingCode, countDown } = this.state
|
||||
|
||||
return (
|
||||
<div className="yo-form-group">
|
||||
<Form
|
||||
ref={this.form}
|
||||
onValuesChange={(changedValues, allValues) => {
|
||||
this.setState({
|
||||
nextDisabled: !(allValues.target && allValues.code),
|
||||
})
|
||||
}}
|
||||
>
|
||||
<Form.Item
|
||||
label="邮箱"
|
||||
name="target"
|
||||
required={false}
|
||||
rules={[
|
||||
{ required: true, message: '请输入邮箱' },
|
||||
{ pattern: reg, message: '邮箱格式错误' },
|
||||
]}
|
||||
>
|
||||
<Input autoComplete="off" placeholder="请输入邮箱" />
|
||||
</Form.Item>
|
||||
<Form.Item label="验证码">
|
||||
<Row gutter={16} align="middle">
|
||||
<Col flex="1">
|
||||
<Form.Item name="code" noStyle>
|
||||
<Input autoComplete="off" placeholder="请输入验证码" />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col>
|
||||
{countDown ? (
|
||||
<Button disabled>{countDown}秒后重新发送</Button>
|
||||
) : (
|
||||
<Button onClick={() => this.onSendCode()} loading={sendingCode}>
|
||||
发送验证码
|
||||
</Button>
|
||||
)}
|
||||
</Col>
|
||||
</Row>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
<div className="text-center pt-md pb-md">
|
||||
<Button
|
||||
disabled={nextDisabled}
|
||||
onClick={() => this.onComplete()}
|
||||
type="primary"
|
||||
>
|
||||
绑定
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
renderStepForm() {
|
||||
const { types, currentStep, sendingCode, countDown } = this.state
|
||||
|
||||
return (
|
||||
<div className="yo-form-group">
|
||||
<Row>
|
||||
<Col span={12} offset={6} className="pt-md pb-md">
|
||||
<Steps current={currentStep}>
|
||||
{steps.map(item => (
|
||||
<Steps.Step key={item.title} title={item.title} />
|
||||
))}
|
||||
</Steps>
|
||||
</Col>
|
||||
</Row>
|
||||
<Form
|
||||
ref={this.form}
|
||||
onValuesChange={(changedValues, allValues) => {
|
||||
this.setState({
|
||||
nextDisabled: !(
|
||||
allValues.orgCode ||
|
||||
(allValues.target && allValues.code)
|
||||
),
|
||||
})
|
||||
}}
|
||||
>
|
||||
{currentStep === 0 && (
|
||||
<>
|
||||
<Form.Item
|
||||
label="选择验证方式"
|
||||
name="type"
|
||||
required={false}
|
||||
rules={[{ required: true, message: '请选择一种验证方式' }]}
|
||||
>
|
||||
<Select placeholder="请选择验证方式">
|
||||
{types.map(item => (
|
||||
<Select.Option key={item.value} value={item.value}>
|
||||
{item.title}
|
||||
</Select.Option>
|
||||
))}
|
||||
</Select>
|
||||
</Form.Item>
|
||||
<Form.Item label="验证码">
|
||||
<Row gutter={16}>
|
||||
<Col flex="1">
|
||||
<Form.Item name="orgCode" noStyle>
|
||||
<Input autoComplete="off" placeholder="请输入验证码" />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col>
|
||||
{countDown ? (
|
||||
<Button disabled>
|
||||
{countDown}
|
||||
秒后重新发送
|
||||
</Button>
|
||||
) : (
|
||||
<Button
|
||||
onClick={() => this.onSendCode()}
|
||||
loading={sendingCode}
|
||||
>
|
||||
发送验证码
|
||||
</Button>
|
||||
)}
|
||||
</Col>
|
||||
</Row>
|
||||
</Form.Item>
|
||||
</>
|
||||
)}
|
||||
{currentStep === 1 && (
|
||||
<>
|
||||
<Form.Item
|
||||
label="新邮箱"
|
||||
name="target"
|
||||
required={false}
|
||||
rules={[
|
||||
{ required: true, message: '请输入邮箱' },
|
||||
{ pattern: reg, message: '邮箱格式错误' },
|
||||
]}
|
||||
>
|
||||
<Input autoComplete="off" placeholder="请输入邮箱" />
|
||||
</Form.Item>
|
||||
<Form.Item label="验证码">
|
||||
<Row gutter={16}>
|
||||
<Col flex="1">
|
||||
<Form.Item name="code" noStyle>
|
||||
<Input autoComplete="off" placeholder="请输入验证码" />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col>
|
||||
{countDown ? (
|
||||
<Button disabled>
|
||||
{countDown}
|
||||
秒后重新发送
|
||||
</Button>
|
||||
) : (
|
||||
<Button
|
||||
onClick={() => this.onSendCode()}
|
||||
loading={sendingCode}
|
||||
>
|
||||
发送验证码
|
||||
</Button>
|
||||
)}
|
||||
</Col>
|
||||
</Row>
|
||||
</Form.Item>
|
||||
</>
|
||||
)}
|
||||
</Form>
|
||||
<div className="text-center pt-md pb-md">
|
||||
{currentStep === 0 && (
|
||||
<>
|
||||
<Button
|
||||
onClick={() => this.onNext(this.form)}
|
||||
type="primary"
|
||||
disabled={this.state.nextDisabled}
|
||||
>
|
||||
下一步,绑定
|
||||
</Button>
|
||||
</>
|
||||
)}
|
||||
{currentStep === 1 && (
|
||||
<>
|
||||
<Button onClick={() => this.onPrev()} className="mr-sm">
|
||||
上一步,验证
|
||||
</Button>
|
||||
<Button
|
||||
disabled={this.state.nextDisabled}
|
||||
onClick={() => this.onComplete()}
|
||||
type="primary"
|
||||
>
|
||||
完成
|
||||
</Button>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
render() {
|
||||
const { visible, loading, types } = this.state
|
||||
|
||||
return (
|
||||
<Modal
|
||||
destroyOnClose
|
||||
footer={false}
|
||||
onCancel={() => this.close()}
|
||||
visible={visible}
|
||||
className="yo-modal-form"
|
||||
title="绑定邮箱"
|
||||
>
|
||||
<Spin spinning={loading} indicator={<AntIcon type="loading" />}>
|
||||
<div className="yo-form">
|
||||
{types.length ? this.renderStepForm() : this.renderForm()}
|
||||
</div>
|
||||
</Spin>
|
||||
</Modal>
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,145 @@
|
||||
import React, { Component } from 'react'
|
||||
import { Form, Input, message as Message, Modal } from 'antd'
|
||||
import { api } from 'common/api'
|
||||
import { RSA_PUBLIC_KEY } from 'util/global'
|
||||
import { cloneDeep } from 'lodash'
|
||||
import { Button } from 'antd/lib/radio'
|
||||
import { encryptByRSA } from 'util/rsa'
|
||||
|
||||
const initData = {
|
||||
exist: false,
|
||||
pattern: '',
|
||||
descriptions: '',
|
||||
visible: false,
|
||||
}
|
||||
|
||||
export default class form extends Component {
|
||||
state = cloneDeep(initData)
|
||||
// 表单实例
|
||||
form = React.createRef()
|
||||
|
||||
// 初始化数据
|
||||
record = {}
|
||||
|
||||
open = () => {
|
||||
this.setState({ visible: true })
|
||||
api.getPwdRule({}).then(({ success, data, message }) => {
|
||||
if (success) {
|
||||
const { pattern, descriptions } = data
|
||||
this.setState({
|
||||
pattern,
|
||||
descriptions,
|
||||
})
|
||||
} else {
|
||||
Message.Error(message)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* mount后回调
|
||||
*/
|
||||
componentDidMount() {
|
||||
this.props.created && this.props.created(this)
|
||||
}
|
||||
|
||||
updatePwd(values) {
|
||||
let { password, newPassword } = values.current.getFieldsValue()
|
||||
password = encryptByRSA(password, RSA_PUBLIC_KEY)
|
||||
newPassword = encryptByRSA(newPassword, RSA_PUBLIC_KEY)
|
||||
const confirm = newPassword
|
||||
api.sysUserUpdatePwd({ password, newPassword, confirm }).then(
|
||||
({ success, data, message }) => {
|
||||
if (success) {
|
||||
Message.success('密码修改完成')
|
||||
this.close()
|
||||
} else {
|
||||
Message.warn(message)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
close() {
|
||||
this.setState(cloneDeep(initData))
|
||||
}
|
||||
/**
|
||||
* 填充数据
|
||||
* 可以在设置this.record之后对其作出数据结构调整
|
||||
* [异步,必要]
|
||||
* @param {*} params
|
||||
*/
|
||||
async fillData() {}
|
||||
|
||||
/**
|
||||
* 获取数据
|
||||
* 可以对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 { pattern, descriptions, visible } = this.state
|
||||
return (
|
||||
<Modal
|
||||
destroyOnClose
|
||||
onCancel={() => this.close()}
|
||||
onOk={() => this.updatePwd(this.form)}
|
||||
visible={visible}
|
||||
className="yo-modal-form"
|
||||
title="更新密码"
|
||||
>
|
||||
<Form className="yo-form" ref={this.form}>
|
||||
<div className="yo-form-group">
|
||||
<Form.Item
|
||||
label="旧密码"
|
||||
rules={[{ required: true, message: '请输入旧密码' }]}
|
||||
name="password"
|
||||
>
|
||||
<Input.Password autoComplete="off" placeholder="请输入旧密码" />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label="新密码"
|
||||
rules={[
|
||||
{ required: true, message: '请输入新密码' },
|
||||
{ pattern, message: '密码格式错误' },
|
||||
]}
|
||||
name="newPassword"
|
||||
tooltip={descriptions}
|
||||
>
|
||||
<Input.Password autoComplete="off" placeholder="请输入新密码" />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label="确认新密码"
|
||||
rules={[
|
||||
{ required: true, message: '请确认新密码' },
|
||||
({ getFieldValue }) => ({
|
||||
validator(_, value) {
|
||||
if (!value || getFieldValue('newPassword') === value) {
|
||||
return Promise.resolve()
|
||||
}
|
||||
return Promise.reject(new Error('确认新密码不匹配'))
|
||||
},
|
||||
}),
|
||||
]}
|
||||
name="confirm"
|
||||
>
|
||||
<Input.Password autoComplete="off" placeholder="请确认新密码" />
|
||||
</Form.Item>
|
||||
</div>
|
||||
</Form>
|
||||
</Modal>
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,376 @@
|
||||
import React, { Component } from 'react'
|
||||
import { Form, Input, Modal, Spin, Steps, Button, Row, Col, message as Message, Select } from 'antd'
|
||||
import { AntIcon } from 'components'
|
||||
import { api } from 'common/api'
|
||||
import { COUNT_DWON_KEY } from 'common/storage'
|
||||
import store from 'store'
|
||||
import { cloneDeep } from 'lodash'
|
||||
|
||||
const { getState } = store
|
||||
|
||||
const steps = [
|
||||
{
|
||||
title: '验证',
|
||||
},
|
||||
{
|
||||
title: '绑定',
|
||||
},
|
||||
]
|
||||
|
||||
const reg = /^((13[0-9])|(14[5,7])|(15[^4,\\D])|(17[0,1,3,6-8])|(18[0-9])|(19[8,9])|(166))[0-9]{8}$/
|
||||
|
||||
const initState = {
|
||||
visible: false,
|
||||
loading: false,
|
||||
|
||||
// 可用于验证的类型
|
||||
types: [],
|
||||
// 当前步骤
|
||||
currentStep: 0,
|
||||
|
||||
nextDisabled: true,
|
||||
// 正在发送验证码
|
||||
sendingCode: false,
|
||||
// 冷却时间
|
||||
countDown: 0,
|
||||
}
|
||||
|
||||
export default class form extends Component {
|
||||
state = cloneDeep(initState)
|
||||
|
||||
form = React.createRef()
|
||||
|
||||
orgCode = ''
|
||||
|
||||
//打开窗口
|
||||
open = () => {
|
||||
this.setState({ visible: true })
|
||||
|
||||
this.showCountDown()
|
||||
|
||||
const data = getState('user')
|
||||
const types = []
|
||||
data.phone &&
|
||||
types.push({
|
||||
title: `使用手机号${data.phone}进行验证`,
|
||||
value: 1,
|
||||
})
|
||||
data.email &&
|
||||
types.push({
|
||||
title: `使用邮箱${data.email}进行验证`,
|
||||
value: 2,
|
||||
})
|
||||
|
||||
this.setState({ types })
|
||||
}
|
||||
|
||||
close() {
|
||||
this.setState(cloneDeep(initState))
|
||||
}
|
||||
|
||||
/**
|
||||
* 将倒计时添加入到本地
|
||||
*/
|
||||
addTime() {
|
||||
const now = Date.now() + 60 * 1000
|
||||
window.localStorage.setItem(COUNT_DWON_KEY, now)
|
||||
}
|
||||
/**
|
||||
* 显示倒计时
|
||||
*/
|
||||
showCountDown() {
|
||||
const surplusTime = window.localStorage.getItem(COUNT_DWON_KEY)
|
||||
const nowTime = Date.now()
|
||||
if (surplusTime >= nowTime) {
|
||||
this.setState({
|
||||
countDown: parseInt((surplusTime - nowTime) / 1000),
|
||||
})
|
||||
setTimeout(() => {
|
||||
this.showCountDown()
|
||||
}, 1000)
|
||||
} else {
|
||||
this.setState({ countDown: 0 })
|
||||
}
|
||||
}
|
||||
|
||||
//发送验证码
|
||||
async onSendCode() {
|
||||
const form = this.form.current
|
||||
|
||||
const valid = await form.validateFields()
|
||||
if (!valid) {
|
||||
return
|
||||
}
|
||||
|
||||
this.setState({ sendingCode: true })
|
||||
|
||||
const data = form.getFieldsValue()
|
||||
try {
|
||||
await api.sysUserSendCode(data)
|
||||
const typeName = data.type ? [, '手机', '邮箱'][data.type] : '手机'
|
||||
Message.success(`已发送验证码到${typeName},请注意查收`)
|
||||
this.addTime()
|
||||
this.showCountDown()
|
||||
} finally {
|
||||
this.setState({ sendingCode: false })
|
||||
}
|
||||
}
|
||||
|
||||
// 下一步
|
||||
async onNext() {
|
||||
this.setState({ loading: true })
|
||||
const data = this.form.current.getFieldsValue()
|
||||
this.orgCode = data.orgCode
|
||||
|
||||
try {
|
||||
await api.sysUserCheckBindcode(data)
|
||||
window.localStorage.removeItem(COUNT_DWON_KEY)
|
||||
this.setState({
|
||||
nextDisabled: true,
|
||||
currentStep: this.state.currentStep + 1,
|
||||
})
|
||||
} finally {
|
||||
this.setState({ loading: false })
|
||||
}
|
||||
}
|
||||
|
||||
// 上一步
|
||||
onPrev() {
|
||||
window.localStorage.removeItem(COUNT_DWON_KEY)
|
||||
this.setState({
|
||||
currentStep: this.state.currentStep - 1,
|
||||
})
|
||||
}
|
||||
|
||||
//完成
|
||||
async onComplete() {
|
||||
this.setState({ loading: true })
|
||||
try {
|
||||
await api.sysUserCheckBindcode({
|
||||
...this.form.current.getFieldsValue(),
|
||||
orgCode: this.orgCode,
|
||||
})
|
||||
await this.props.loadData()
|
||||
window.localStorage.removeItem(COUNT_DWON_KEY)
|
||||
Message.success('绑定手机号成功')
|
||||
this.close()
|
||||
} finally {
|
||||
this.setState({ loading: false })
|
||||
}
|
||||
}
|
||||
|
||||
renderForm() {
|
||||
const { nextDisabled, sendingCode, countDown } = this.state
|
||||
|
||||
return (
|
||||
<div className="yo-form-group">
|
||||
<Form
|
||||
ref={this.form}
|
||||
onValuesChange={(changedValues, allValues) => {
|
||||
this.setState({
|
||||
nextDisabled: !(allValues.target && allValues.code),
|
||||
})
|
||||
}}
|
||||
>
|
||||
<Form.Item
|
||||
label="手机号码"
|
||||
name="target"
|
||||
required={false}
|
||||
rules={[
|
||||
{ required: true, message: '请输入手机号码' },
|
||||
{ pattern: reg, message: '手机号码格式错误' },
|
||||
]}
|
||||
>
|
||||
<Input autoComplete="off" placeholder="请输入手机号码" />
|
||||
</Form.Item>
|
||||
<Form.Item label="验证码">
|
||||
<Row gutter={16} align="middle">
|
||||
<Col flex="1">
|
||||
<Form.Item name="code" noStyle>
|
||||
<Input autoComplete="off" placeholder="请输入验证码" />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col>
|
||||
{countDown ? (
|
||||
<Button disabled>{countDown}秒后重新发送</Button>
|
||||
) : (
|
||||
<Button onClick={() => this.onSendCode()} loading={sendingCode}>
|
||||
发送验证码
|
||||
</Button>
|
||||
)}
|
||||
</Col>
|
||||
</Row>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
<div className="text-center pt-md pb-md">
|
||||
<Button
|
||||
disabled={nextDisabled}
|
||||
onClick={() => this.onComplete()}
|
||||
type="primary"
|
||||
>
|
||||
绑定
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
renderStepForm() {
|
||||
const { types, currentStep, sendingCode, countDown } = this.state
|
||||
|
||||
return (
|
||||
<div className="yo-form-group">
|
||||
<Row>
|
||||
<Col span={12} offset={6} className="pt-md pb-md">
|
||||
<Steps current={currentStep}>
|
||||
{steps.map(item => (
|
||||
<Steps.Step key={item.title} title={item.title} />
|
||||
))}
|
||||
</Steps>
|
||||
</Col>
|
||||
</Row>
|
||||
<Form
|
||||
ref={this.form}
|
||||
onValuesChange={(changedValues, allValues) => {
|
||||
this.setState({
|
||||
nextDisabled: !(
|
||||
allValues.orgCode ||
|
||||
(allValues.target && allValues.code)
|
||||
),
|
||||
})
|
||||
}}
|
||||
>
|
||||
{currentStep === 0 && (
|
||||
<>
|
||||
<Form.Item
|
||||
label="选择验证方式"
|
||||
name="type"
|
||||
required={false}
|
||||
rules={[{ required: true, message: '请选择一种验证方式' }]}
|
||||
>
|
||||
<Select placeholder="请选择验证方式">
|
||||
{types.map(item => (
|
||||
<Select.Option key={item.value} value={item.value}>
|
||||
{item.title}
|
||||
</Select.Option>
|
||||
))}
|
||||
</Select>
|
||||
</Form.Item>
|
||||
<Form.Item label="验证码">
|
||||
<Row gutter={16}>
|
||||
<Col flex="1">
|
||||
<Form.Item name="orgCode" noStyle>
|
||||
<Input autoComplete="off" placeholder="请输入验证码" />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col>
|
||||
{countDown ? (
|
||||
<Button disabled>
|
||||
{countDown}
|
||||
秒后重新发送
|
||||
</Button>
|
||||
) : (
|
||||
<Button
|
||||
onClick={() => this.onSendCode()}
|
||||
loading={sendingCode}
|
||||
>
|
||||
发送验证码
|
||||
</Button>
|
||||
)}
|
||||
</Col>
|
||||
</Row>
|
||||
</Form.Item>
|
||||
</>
|
||||
)}
|
||||
{currentStep === 1 && (
|
||||
<>
|
||||
<Form.Item
|
||||
label="新手机号码"
|
||||
name="target"
|
||||
required={false}
|
||||
rules={[
|
||||
{ required: true, message: '请输入手机号码' },
|
||||
{ pattern: reg, message: '手机号码格式错误' },
|
||||
]}
|
||||
>
|
||||
<Input autoComplete="off" placeholder="请输入手机号码" />
|
||||
</Form.Item>
|
||||
<Form.Item label="验证码">
|
||||
<Row gutter={16}>
|
||||
<Col flex="1">
|
||||
<Form.Item name="code" noStyle>
|
||||
<Input autoComplete="off" placeholder="请输入验证码" />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col>
|
||||
{countDown ? (
|
||||
<Button disabled>
|
||||
{countDown}
|
||||
秒后重新发送
|
||||
</Button>
|
||||
) : (
|
||||
<Button
|
||||
onClick={() => this.onSendCode()}
|
||||
loading={sendingCode}
|
||||
>
|
||||
发送验证码
|
||||
</Button>
|
||||
)}
|
||||
</Col>
|
||||
</Row>
|
||||
</Form.Item>
|
||||
</>
|
||||
)}
|
||||
</Form>
|
||||
<div className="text-center pt-md pb-md">
|
||||
{currentStep === 0 && (
|
||||
<>
|
||||
<Button
|
||||
onClick={() => this.onNext(this.form)}
|
||||
type="primary"
|
||||
disabled={this.state.nextDisabled}
|
||||
>
|
||||
下一步,绑定
|
||||
</Button>
|
||||
</>
|
||||
)}
|
||||
{currentStep === 1 && (
|
||||
<>
|
||||
<Button onClick={() => this.onPrev()} className="mr-sm">
|
||||
上一步,验证
|
||||
</Button>
|
||||
<Button
|
||||
disabled={this.state.nextDisabled}
|
||||
onClick={() => this.onComplete()}
|
||||
type="primary"
|
||||
>
|
||||
完成
|
||||
</Button>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
render() {
|
||||
const { visible, loading, types } = this.state
|
||||
|
||||
return (
|
||||
<Modal
|
||||
destroyOnClose
|
||||
footer={false}
|
||||
onCancel={() => this.close()}
|
||||
visible={visible}
|
||||
className="yo-modal-form"
|
||||
title="绑定手机"
|
||||
>
|
||||
<Spin spinning={loading} indicator={<AntIcon type="loading" />}>
|
||||
<div className="yo-form">
|
||||
{types.length ? this.renderStepForm() : this.renderForm()}
|
||||
</div>
|
||||
</Spin>
|
||||
</Modal>
|
||||
)
|
||||
}
|
||||
}
|
||||
133
framework/web-react/src/pages/system/app/form.jsx
Normal file
133
framework/web-react/src/pages/system/app/form.jsx
Normal file
@@ -0,0 +1,133 @@
|
||||
import React, { Component } from 'react'
|
||||
import { Form, Input, InputNumber, Spin } from 'antd'
|
||||
import { AntIcon, ColorSelector, IconSelector } from 'components'
|
||||
import { api } from 'common/api'
|
||||
|
||||
const initialValues = {
|
||||
sort: 100,
|
||||
}
|
||||
|
||||
export default class form extends Component {
|
||||
state = {
|
||||
// 加载状态
|
||||
loading: true,
|
||||
}
|
||||
|
||||
// 表单实例
|
||||
form = React.createRef()
|
||||
|
||||
iconSelector = React.createRef()
|
||||
|
||||
// 初始化数据
|
||||
record = {}
|
||||
|
||||
/**
|
||||
* mount后回调
|
||||
*/
|
||||
componentDidMount() {
|
||||
this.props.created && this.props.created(this)
|
||||
}
|
||||
|
||||
/**
|
||||
* 填充数据
|
||||
* 可以在设置this.record之后对其作出数据结构调整
|
||||
* [异步,必要]
|
||||
* @param {*} params
|
||||
*/
|
||||
async fillData(params) {
|
||||
//#region 从后端转换成前段所需格式
|
||||
if (params.id) {
|
||||
this.record = (await api.sysAppDetail({ id: params.id })).data
|
||||
}
|
||||
//#endregion
|
||||
this.form.current.setFieldsValue(this.record)
|
||||
|
||||
this.setState({
|
||||
loading: false,
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取数据
|
||||
* 可以对postData进行数据结构调整
|
||||
* [异步,必要]
|
||||
* @returns
|
||||
*/
|
||||
async getData() {
|
||||
const form = this.form.current
|
||||
|
||||
const valid = await form.validateFields()
|
||||
if (valid) {
|
||||
const postData = form.getFieldsValue()
|
||||
if (this.record) {
|
||||
postData.id = this.record.id
|
||||
}
|
||||
//#region 从前段转换后端所需格式
|
||||
//#endregion
|
||||
return postData
|
||||
}
|
||||
}
|
||||
|
||||
//#region 自定义方法
|
||||
//#endregion
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Form initialValues={initialValues} ref={this.form} className="yo-form">
|
||||
<Spin spinning={this.state.loading} indicator={<AntIcon type="loading" />}>
|
||||
<div className="yo-form-group">
|
||||
<Form.Item
|
||||
label="应用名称"
|
||||
name="name"
|
||||
rules={[{ required: true, message: '请输入应用名称' }]}
|
||||
>
|
||||
<Input autoComplete="off" placeholder="请输入应用名称" />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label="唯一编码"
|
||||
name="code"
|
||||
rules={[{ required: true, message: '请输入唯一编码' }]}
|
||||
>
|
||||
<Input autoComplete="off" placeholder="请输入唯一编码" />
|
||||
</Form.Item>
|
||||
<Form.Item label="图标" name="icon">
|
||||
<Input
|
||||
disabled
|
||||
placeholder="请选择图标"
|
||||
addonAfter={
|
||||
<AntIcon
|
||||
type="setting"
|
||||
onClick={() =>
|
||||
this.iconSelector.current.open(
|
||||
this.form.current.getFieldValue('icon')
|
||||
)
|
||||
}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form.Item label="颜色" name="color">
|
||||
<ColorSelector placeholder="请选择颜色" />
|
||||
</Form.Item>
|
||||
<Form.Item label="排序" name="sort">
|
||||
<InputNumber
|
||||
max={1000}
|
||||
min={0}
|
||||
placeholder="请输入排序"
|
||||
className="w-100-p"
|
||||
/>
|
||||
</Form.Item>
|
||||
</div>
|
||||
</Spin>
|
||||
<IconSelector
|
||||
ref={this.iconSelector}
|
||||
onSelect={icon =>
|
||||
this.form.current.setFieldsValue({
|
||||
icon,
|
||||
})
|
||||
}
|
||||
/>
|
||||
</Form>
|
||||
)
|
||||
}
|
||||
}
|
||||
323
framework/web-react/src/pages/system/app/index.jsx
Normal file
323
framework/web-react/src/pages/system/app/index.jsx
Normal file
@@ -0,0 +1,323 @@
|
||||
import React, { Component } from 'react'
|
||||
import { Button, Card, Form, Input, Popconfirm, message as Message } from 'antd'
|
||||
import { isEqual } from 'lodash'
|
||||
import { AntIcon, Auth, Container, ModalForm, QueryTable, QueryTableActions } from 'components'
|
||||
import { api } from 'common/api'
|
||||
import getDictData from 'util/dic'
|
||||
import auth from 'components/authorized/handler'
|
||||
import { toCamelCase } from 'util/format'
|
||||
import FormBody from './form'
|
||||
|
||||
// 配置页面所需接口函数
|
||||
const apiAction = {
|
||||
page: api.getAppPage,
|
||||
add: api.sysAppAdd,
|
||||
edit: api.sysAppEdit,
|
||||
delete: api.sysAppDelete,
|
||||
|
||||
setDefault: api.sysAppSetAsDefault,
|
||||
}
|
||||
|
||||
// 用于弹窗标题
|
||||
const name = '应用'
|
||||
|
||||
export default class index extends Component {
|
||||
state = {
|
||||
codes: {
|
||||
commonStatus: [],
|
||||
},
|
||||
}
|
||||
|
||||
// 表格实例
|
||||
table = React.createRef()
|
||||
|
||||
// 新增窗口实例
|
||||
addForm = React.createRef()
|
||||
// 编辑窗口实例
|
||||
editForm = React.createRef()
|
||||
|
||||
// 表格字段
|
||||
columns = [
|
||||
{
|
||||
title: '图标',
|
||||
dataIndex: 'icon',
|
||||
width: 32,
|
||||
render: (text, record) => (
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
width: '32px',
|
||||
height: '32px',
|
||||
borderRadius: '100%',
|
||||
backgroundColor: record.color,
|
||||
margin: '0 auto',
|
||||
}}
|
||||
>
|
||||
<AntIcon
|
||||
type={text}
|
||||
style={{
|
||||
fontSize: '20px',
|
||||
color: '#fff',
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
),
|
||||
},
|
||||
{
|
||||
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 && (
|
||||
<Auth auth="sysApp:setAsDefault">
|
||||
<QueryTableActions>
|
||||
<span></span>
|
||||
<Popconfirm
|
||||
placement="topRight"
|
||||
title="是否确认设置为默认应用"
|
||||
onConfirm={() => this.onSetDefault(record)}
|
||||
>
|
||||
<a>设为默认</a>
|
||||
</Popconfirm>
|
||||
</QueryTableActions>
|
||||
</Auth>
|
||||
)}
|
||||
</>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: '状态',
|
||||
dataIndex: 'status',
|
||||
width: 100,
|
||||
sorter: true,
|
||||
render: text => <>{this.bindCodeValue(text, 'common_status')}</>,
|
||||
},
|
||||
{
|
||||
title: '排序',
|
||||
dataIndex: 'sort',
|
||||
width: 100,
|
||||
sorter: true,
|
||||
defaultSortOrder: 'ascend',
|
||||
},
|
||||
]
|
||||
|
||||
/**
|
||||
* 构造函数,在渲染前动态添加操作字段等
|
||||
* @param {*} props
|
||||
*/
|
||||
constructor(props) {
|
||||
super(props)
|
||||
|
||||
const flag = auth({ sysApp: [['edit'], ['delete']] })
|
||||
|
||||
if (flag) {
|
||||
this.columns.push({
|
||||
title: '操作',
|
||||
width: 150,
|
||||
dataIndex: 'actions',
|
||||
render: (text, record) => (
|
||||
<QueryTableActions>
|
||||
<Auth auth="sysApp:edit">
|
||||
<a onClick={() => this.onOpen(this.editForm, record.id)}>编辑</a>
|
||||
</Auth>
|
||||
<Auth auth="sysApp:delete">
|
||||
<Popconfirm
|
||||
placement="topRight"
|
||||
title="是否确认删除"
|
||||
onConfirm={() => this.onDelete(record)}
|
||||
>
|
||||
<a>删除</a>
|
||||
</Popconfirm>
|
||||
</Auth>
|
||||
</QueryTableActions>
|
||||
),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 阻止外部组件引发的渲染,提升性能
|
||||
* 可自行添加渲染条件
|
||||
* [必要]
|
||||
* @param {*} props
|
||||
* @param {*} state
|
||||
* @returns
|
||||
*/
|
||||
shouldComponentUpdate(props, state) {
|
||||
return !isEqual(this.state, state)
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载字典数据,之后开始加载表格数据
|
||||
* 如果必须要加载字典数据,可直接对表格设置autoLoad=true
|
||||
*/
|
||||
componentDidMount() {
|
||||
const { onLoading, onLoadData } = this.table.current
|
||||
onLoading()
|
||||
getDictData('common_status').then(res => {
|
||||
this.setState(
|
||||
{
|
||||
codes: res,
|
||||
},
|
||||
() => {
|
||||
onLoadData()
|
||||
}
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 调用加载数据接口,可在调用前对query进行处理
|
||||
* [异步,必要]
|
||||
* @param {*} params
|
||||
* @param {*} query
|
||||
* @returns
|
||||
*/
|
||||
loadData = async (params, query) => {
|
||||
const { data } = await apiAction.page({
|
||||
...params,
|
||||
...query,
|
||||
})
|
||||
return data
|
||||
}
|
||||
|
||||
/**
|
||||
* 绑定字典数据
|
||||
* @param {*} code
|
||||
* @param {*} name
|
||||
* @returns
|
||||
*/
|
||||
bindCodeValue(code, name) {
|
||||
name = toCamelCase(name)
|
||||
const codes = this.state.codes[name]
|
||||
if (codes) {
|
||||
const c = codes.find(p => p.code == code)
|
||||
if (c) {
|
||||
return c.value
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
/**
|
||||
* 打开新增/编辑弹窗
|
||||
* @param {*} modal
|
||||
* @param {*} id
|
||||
*/
|
||||
onOpen(modal, id) {
|
||||
modal.current.open({
|
||||
id,
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 对表格上的操作进行统一处理
|
||||
* [异步]
|
||||
* @param {*} action
|
||||
* @param {*} successMessage
|
||||
*/
|
||||
async onAction(action, successMessage) {
|
||||
const { onLoading, onLoaded, onReloadData } = this.table.current
|
||||
onLoading()
|
||||
try {
|
||||
if (action) {
|
||||
await action
|
||||
}
|
||||
if (successMessage) {
|
||||
Message.success(successMessage)
|
||||
}
|
||||
onReloadData()
|
||||
} catch {
|
||||
onLoaded()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除
|
||||
* @param {*} record
|
||||
*/
|
||||
onDelete(record) {
|
||||
this.onAction(apiAction.delete(record), '删除成功')
|
||||
}
|
||||
|
||||
//#region 自定义方法
|
||||
async onSetDefault(record) {
|
||||
this.onAction(apiAction.setDefault(record), '设置成功')
|
||||
}
|
||||
//#endregion
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Container mode="fluid">
|
||||
<br />
|
||||
<Card bordered={false}>
|
||||
<QueryTable
|
||||
ref={this.table}
|
||||
autoLoad={false}
|
||||
loadData={this.loadData}
|
||||
columns={this.columns}
|
||||
query={
|
||||
<Auth auth="sysApp:page">
|
||||
<Form.Item label="应用名称" name="name">
|
||||
<Input autoComplete="off" placeholder="请输入应用名称" />
|
||||
</Form.Item>
|
||||
<Form.Item label="唯一编码" name="code">
|
||||
<Input autoComplete="off" placeholder="请输入唯一编码" />
|
||||
</Form.Item>
|
||||
</Auth>
|
||||
}
|
||||
operator={
|
||||
<Auth auth="sysApp:add">
|
||||
<Button
|
||||
icon={<AntIcon type="plus" />}
|
||||
onClick={() => this.onOpen(this.addForm)}
|
||||
>
|
||||
新增{name}
|
||||
</Button>
|
||||
</Auth>
|
||||
}
|
||||
/>
|
||||
</Card>
|
||||
|
||||
<Auth auth="sysApp:add">
|
||||
<ModalForm
|
||||
title={`新增${name}`}
|
||||
action={apiAction.add}
|
||||
ref={this.addForm}
|
||||
onSuccess={() => this.table.current.onReloadData()}
|
||||
>
|
||||
<FormBody />
|
||||
</ModalForm>
|
||||
</Auth>
|
||||
|
||||
<Auth auth="sysApp:edit">
|
||||
<ModalForm
|
||||
title={`编辑${name}`}
|
||||
action={apiAction.edit}
|
||||
ref={this.editForm}
|
||||
onSuccess={() => this.table.current.onReloadData()}
|
||||
>
|
||||
<FormBody />
|
||||
</ModalForm>
|
||||
</Auth>
|
||||
</Container>
|
||||
)
|
||||
}
|
||||
}
|
||||
143
framework/web-react/src/pages/system/area/form.jsx
Normal file
143
framework/web-react/src/pages/system/area/form.jsx
Normal file
@@ -0,0 +1,143 @@
|
||||
import React, { Component } from 'react'
|
||||
import { Form, Input, InputNumber, Select, Spin } from 'antd'
|
||||
import { AntIcon } from 'components'
|
||||
import { cloneDeep } from 'lodash'
|
||||
import getDictData from 'util/dic'
|
||||
|
||||
const initialValues = {
|
||||
sort: 100,
|
||||
}
|
||||
export default class form extends Component {
|
||||
state = {
|
||||
// 加载状态
|
||||
loading: true,
|
||||
exist: false,
|
||||
codes: {
|
||||
areacodeType: [],
|
||||
},
|
||||
}
|
||||
// 表单实例
|
||||
form = React.createRef()
|
||||
|
||||
// 初始化数据
|
||||
record = {}
|
||||
|
||||
/**
|
||||
* mount后回调
|
||||
*/
|
||||
componentDidMount() {
|
||||
this.props.created && this.props.created(this)
|
||||
}
|
||||
|
||||
/**
|
||||
* 填充数据
|
||||
* 可以在设置this.record之后对其作出数据结构调整
|
||||
* [异步,必要]
|
||||
* @param {*} params
|
||||
*/
|
||||
async fillData(params) {
|
||||
this.record = cloneDeep(params.record)
|
||||
//#region 从后端转换成前段所需格式
|
||||
|
||||
const codes = await getDictData('areacode_type')
|
||||
const exist = !!params.record
|
||||
this.setState({
|
||||
codes,
|
||||
exist,
|
||||
})
|
||||
|
||||
this.record = {
|
||||
...this.record,
|
||||
}
|
||||
//#endregion
|
||||
|
||||
this.form.current.setFieldsValue(this.record)
|
||||
|
||||
this.setState({
|
||||
loading: false,
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取数据
|
||||
* 可以对postData进行数据结构调整
|
||||
* [异步,必要]
|
||||
* @returns
|
||||
*/
|
||||
async getData() {
|
||||
const form = this.form.current
|
||||
|
||||
const valid = await form.validateFields()
|
||||
if (valid) {
|
||||
const postData = form.getFieldsValue()
|
||||
if (this.record) {
|
||||
postData.id = this.record.id
|
||||
}
|
||||
//#region 从前段转换后端所需格式
|
||||
//#endregion
|
||||
return postData
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Form initialValues={initialValues} ref={this.form} className="yo-form">
|
||||
<Spin spinning={this.state.loading} indicator={<AntIcon type="loading" />}>
|
||||
<div className="yo-form-group">
|
||||
{/* 表单控件 */}
|
||||
<Form.Item
|
||||
label="区域类型"
|
||||
name="levelType"
|
||||
rules={[{ required: true, message: '请选择区域类型' }]}
|
||||
>
|
||||
<Select
|
||||
placeholder="请选择区域类型"
|
||||
suffixIcon={<AntIcon type="lock" v-if={this.state.exist} />}
|
||||
>
|
||||
{this.state.codes.areacodeType.map(item => {
|
||||
return (
|
||||
<Select.Option key={item.code} value={+item.code}>
|
||||
{item.value}
|
||||
</Select.Option>
|
||||
)
|
||||
})}
|
||||
</Select>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label="区域名称"
|
||||
name="name"
|
||||
rules={[{ required: true, message: '请输入区域名称' }]}
|
||||
>
|
||||
<Input autoComplete="off" placeholder="请输入区域名称" />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label="区域编码"
|
||||
name="code"
|
||||
tooltip="用于系统内部使用,添加后不可更改"
|
||||
>
|
||||
<Input
|
||||
disabled={this.state.exist}
|
||||
placeholder="请输入区域编码"
|
||||
suffix={this.state.exist && <AntIcon type="lock" />}
|
||||
></Input>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label="行政编码"
|
||||
name="adCode"
|
||||
tooltip="国家规定的区划代码,可随实际情况更改而更改"
|
||||
>
|
||||
<Input placeholder="请输入区域编码"></Input>
|
||||
</Form.Item>
|
||||
<Form.Item label="排序" name="sort">
|
||||
<InputNumber min={0} placeholder="请输入排序" className="w-100-p" />
|
||||
</Form.Item>
|
||||
<Form.Item label="备注" name="note">
|
||||
<Input.TextArea placeholder="请输入备注" />
|
||||
</Form.Item>
|
||||
{/* ... */}
|
||||
</div>
|
||||
</Spin>
|
||||
</Form>
|
||||
)
|
||||
}
|
||||
}
|
||||
305
framework/web-react/src/pages/system/area/index.jsx
Normal file
305
framework/web-react/src/pages/system/area/index.jsx
Normal file
@@ -0,0 +1,305 @@
|
||||
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 { api } from 'common/api'
|
||||
import auth from 'components/authorized/handler'
|
||||
import { toCamelCase } from 'util/format'
|
||||
import { isEqual } from 'lodash'
|
||||
import getDictData from 'util/dic'
|
||||
import FormBody from './form'
|
||||
|
||||
const apiAction = {
|
||||
tree: api.getAreaTree,
|
||||
page: api.sysAreaPage,
|
||||
add: api.sysAreaAdd,
|
||||
edit: api.sysAreaEdit,
|
||||
delete: api.sysAreaDelete,
|
||||
}
|
||||
|
||||
const name = '区域'
|
||||
|
||||
export default class index extends Component {
|
||||
state = {
|
||||
codes: {
|
||||
areacodeType: [],
|
||||
},
|
||||
}
|
||||
|
||||
// 表格实例
|
||||
table = React.createRef()
|
||||
|
||||
// 新增窗口实例
|
||||
addForm = React.createRef()
|
||||
// 编辑窗口实例
|
||||
editForm = React.createRef()
|
||||
|
||||
// 树选中节点
|
||||
selectCode = undefined
|
||||
columns = [
|
||||
{
|
||||
title: '区域类型',
|
||||
dataIndex: 'levelType',
|
||||
sorter: true,
|
||||
width: 100,
|
||||
render: text => <>{this.bindCodeValue(text, 'areacode_type')}</>,
|
||||
},
|
||||
{
|
||||
title: '区域名称',
|
||||
dataIndex: 'name',
|
||||
width: 100,
|
||||
sorter: true,
|
||||
},
|
||||
{
|
||||
title: '区域编号',
|
||||
dataIndex: 'code',
|
||||
width: 100,
|
||||
sorter: true,
|
||||
},
|
||||
{
|
||||
title: '行政编号',
|
||||
dataIndex: 'adCode',
|
||||
width: 100,
|
||||
sorter: true,
|
||||
defaultSortOrder: 'ascend',
|
||||
},
|
||||
{
|
||||
title: '描述',
|
||||
dataIndex: 'note',
|
||||
width: 200,
|
||||
sorter: false,
|
||||
},
|
||||
{
|
||||
title: '排序',
|
||||
dataIndex: 'sort',
|
||||
width: 80,
|
||||
sorter: true,
|
||||
},
|
||||
]
|
||||
/**
|
||||
* 构造函数,在渲染前动态添加操作字段等
|
||||
* @param {*} props
|
||||
*/
|
||||
constructor(props) {
|
||||
super(props)
|
||||
|
||||
const flag = auth({ sysArea: [['edit'], ['delete']] })
|
||||
|
||||
if (flag) {
|
||||
this.columns.push({
|
||||
title: '操作',
|
||||
width: 150,
|
||||
dataIndex: 'actions',
|
||||
render: (text, record) => (
|
||||
<QueryTableActions>
|
||||
<Auth auth="sysArea:edit">
|
||||
<a onClick={() => this.onOpen(this.editForm, record)}>编辑</a>
|
||||
</Auth>
|
||||
<Auth auth="sysArea:delete">
|
||||
<Popconfirm
|
||||
placement="topRight"
|
||||
title="是否确认删除"
|
||||
onConfirm={() => this.onDelete(record)}
|
||||
>
|
||||
<a>删除</a>
|
||||
</Popconfirm>
|
||||
</Auth>
|
||||
</QueryTableActions>
|
||||
),
|
||||
})
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 阻止外部组件引发的渲染,提升性能
|
||||
* 可自行添加渲染条件
|
||||
* [必要]
|
||||
* @param {*} props
|
||||
* @param {*} state
|
||||
* @returns
|
||||
*/
|
||||
shouldComponentUpdate(props, state) {
|
||||
return !isEqual(this.state, state)
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载字典数据,之后开始加载表格数据
|
||||
* 如果必须要加载字典数据,可直接对表格设置autoLoad=true
|
||||
*/
|
||||
componentDidMount() {
|
||||
this.table.current.onLoading()
|
||||
getDictData('areacode_type').then(res => {
|
||||
this.setState(
|
||||
{
|
||||
codes: res,
|
||||
},
|
||||
() => {
|
||||
this.table.current.onLoadData()
|
||||
}
|
||||
)
|
||||
})
|
||||
}
|
||||
/**
|
||||
* 调用加载数据接口,可在调用前对query进行处理
|
||||
* [异步,必要]
|
||||
* @param {*} params
|
||||
* @param {*} query
|
||||
* @returns
|
||||
*/
|
||||
loadData = async (params, query) => {
|
||||
query = {
|
||||
...query,
|
||||
pcode: this.selectCode,
|
||||
}
|
||||
//首次加载根据code列升序排序
|
||||
if (!params.sortField) {
|
||||
params.sortField = 'code'
|
||||
params.sortOrder = 'ascend'
|
||||
}
|
||||
const { data } = await apiAction.page({
|
||||
...params,
|
||||
...query,
|
||||
})
|
||||
return data
|
||||
}
|
||||
|
||||
/**
|
||||
* 调用树结构数据接口
|
||||
* [异步,必要]
|
||||
* @returns
|
||||
*/
|
||||
loadTreeData = async () => {
|
||||
const { data } = await apiAction.tree()
|
||||
return data
|
||||
}
|
||||
|
||||
/**
|
||||
* 树节点选中事件
|
||||
* [必要]
|
||||
* @param {*} id
|
||||
*/
|
||||
onSelectTree(code) {
|
||||
this.selectCode = code
|
||||
this.table.current.onReloadData()
|
||||
}
|
||||
|
||||
/**
|
||||
* 绑定字典数据
|
||||
* @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({
|
||||
pcode: this.pcode,
|
||||
record,
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 对表格上的操作进行统一处理
|
||||
* [异步]
|
||||
* @param {*} action
|
||||
* @param {*} successMessage
|
||||
*/
|
||||
async onAction(action, successMessage) {
|
||||
this.table.current.onLoading()
|
||||
try {
|
||||
await action
|
||||
Message.success(successMessage)
|
||||
this.table.current.onReloadData()
|
||||
} catch {
|
||||
this.table.current.onLoaded()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除
|
||||
* @param {*} record
|
||||
*/
|
||||
onDelete(record) {
|
||||
this.onAction(apiAction.delete(record), '删除成功')
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<QueryTreeLayout
|
||||
loadData={this.loadTreeData}
|
||||
defaultExpanded={true}
|
||||
onSelect={key => this.onSelectTree(key)}
|
||||
replaceFields={{ value: 'code', title: 'name', children: 'children' }}
|
||||
>
|
||||
<Container mode="fluid">
|
||||
<Card bordered={false}>
|
||||
<QueryTable
|
||||
ref={this.table}
|
||||
autoLoad={false}
|
||||
loadData={this.loadData}
|
||||
columns={this.columns}
|
||||
query={
|
||||
<Auth auth="sysArea:page">
|
||||
<Form.Item label="区域名称" name="name">
|
||||
<Input autoComplete="off" placeholder="请输入区域名称" />
|
||||
</Form.Item>
|
||||
<Form.Item label="区域类型" name="code">
|
||||
<Input autoComplete="off" placeholder="请输入区域类型" />
|
||||
</Form.Item>
|
||||
</Auth>
|
||||
}
|
||||
operator={
|
||||
<Auth auth="sysArea:add">
|
||||
<Button
|
||||
icon={<AntIcon type="plus" />}
|
||||
onClick={() => this.onOpen(this.addForm)}
|
||||
>
|
||||
新增{name}
|
||||
</Button>
|
||||
</Auth>
|
||||
}
|
||||
></QueryTable>
|
||||
</Card>
|
||||
</Container>
|
||||
<ModalForm
|
||||
title={`新增${name}`}
|
||||
action={apiAction.add}
|
||||
ref={this.addForm}
|
||||
onSuccess={() => this.table.current.onReloadData()}
|
||||
>
|
||||
<FormBody />
|
||||
</ModalForm>
|
||||
|
||||
<ModalForm
|
||||
title={`编辑${name}`}
|
||||
action={apiAction.edit}
|
||||
ref={this.editForm}
|
||||
onSuccess={() => this.table.current.onReloadData()}
|
||||
>
|
||||
<FormBody />
|
||||
</ModalForm>
|
||||
</QueryTreeLayout>
|
||||
)
|
||||
}
|
||||
}
|
||||
134
framework/web-react/src/pages/system/config/form.jsx
Normal file
134
framework/web-react/src/pages/system/config/form.jsx
Normal file
@@ -0,0 +1,134 @@
|
||||
import React, { Component } from 'react'
|
||||
import { Form, Input, Radio, Select, Spin } from 'antd'
|
||||
import { AntIcon } from 'components'
|
||||
import { cloneDeep } from 'lodash'
|
||||
import getDictData from 'util/dic'
|
||||
|
||||
const initialValues = {}
|
||||
|
||||
export default class form extends Component {
|
||||
state = {
|
||||
// 加载状态
|
||||
loading: true,
|
||||
|
||||
codes: {
|
||||
constsType: [],
|
||||
},
|
||||
}
|
||||
|
||||
// 表单实例
|
||||
form = React.createRef()
|
||||
|
||||
// 初始化数据
|
||||
record = {}
|
||||
|
||||
/**
|
||||
* mount后回调
|
||||
*/
|
||||
componentDidMount() {
|
||||
this.props.created && this.props.created(this)
|
||||
}
|
||||
|
||||
/**
|
||||
* 填充数据
|
||||
* 可以在设置this.record之后对其作出数据结构调整
|
||||
* [异步,必要]
|
||||
* @param {*} params
|
||||
*/
|
||||
async fillData(params) {
|
||||
this.record = cloneDeep(params.record)
|
||||
//#region 从后端转换成前段所需格式,也可以在此处调用获取详细数据接口
|
||||
const codes = await getDictData('consts_type')
|
||||
this.setState({ codes })
|
||||
//#endregion
|
||||
this.form.current.setFieldsValue(this.record)
|
||||
|
||||
this.setState({
|
||||
loading: false,
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取数据
|
||||
* 可以对postData进行数据结构调整
|
||||
* [异步,必要]
|
||||
* @returns
|
||||
*/
|
||||
async getData() {
|
||||
const form = this.form.current
|
||||
|
||||
const valid = await form.validateFields()
|
||||
if (valid) {
|
||||
const postData = form.getFieldsValue()
|
||||
if (this.record) {
|
||||
postData.id = this.record.id
|
||||
}
|
||||
//#region 从前段转换后端所需格式
|
||||
//#endregion
|
||||
return postData
|
||||
}
|
||||
}
|
||||
|
||||
//#region 自定义方法
|
||||
//#endregion
|
||||
|
||||
render() {
|
||||
const { codes } = this.state
|
||||
|
||||
return (
|
||||
<Form initialValues={initialValues} ref={this.form} className="yo-form">
|
||||
<Spin spinning={this.state.loading} indicator={<AntIcon type="loading" />}>
|
||||
<div className="yo-form-group">
|
||||
<Form.Item
|
||||
label="角色名"
|
||||
name="name"
|
||||
rules={[{ required: true, message: '请输入应用名称' }]}
|
||||
>
|
||||
<Input autoComplete="off" placeholder="请输入角色名" />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label="唯一编码"
|
||||
name="code"
|
||||
rules={[{ required: true, message: '请输入唯一编码' }]}
|
||||
>
|
||||
<Input autoComplete="off" placeholder="请输入唯一编码" />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label="系统参数"
|
||||
name="sysFlag"
|
||||
rules={[{ required: true, message: '请选择参数类型' }]}
|
||||
>
|
||||
<Radio.Group disabled={this.record}>
|
||||
<Radio.Button value="Y">是</Radio.Button>
|
||||
<Radio.Button value="N">否</Radio.Button>
|
||||
</Radio.Group>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label="所属分类"
|
||||
name="groupCode"
|
||||
rules={[{ required: true, message: '请选择所属分类' }]}
|
||||
>
|
||||
<Select placeholder="请选择所属分类" disabled={this.record}>
|
||||
{codes.constsType.map(item => (
|
||||
<Select.Option key={item.code} value={item.code}>
|
||||
{item.value}
|
||||
</Select.Option>
|
||||
))}
|
||||
</Select>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label="参数值"
|
||||
name="value"
|
||||
rules={[{ required: true, message: '请输入参数值' }]}
|
||||
>
|
||||
<Input autoComplete="off" placeholder="请输入参数值" />
|
||||
</Form.Item>
|
||||
<Form.Item label="备注" name="remark">
|
||||
<Input.TextArea autoComplete="off" placeholder="请输入备注" />
|
||||
</Form.Item>
|
||||
</div>
|
||||
</Spin>
|
||||
</Form>
|
||||
)
|
||||
}
|
||||
}
|
||||
277
framework/web-react/src/pages/system/config/index.jsx
Normal file
277
framework/web-react/src/pages/system/config/index.jsx
Normal file
@@ -0,0 +1,277 @@
|
||||
import React, { Component } from 'react'
|
||||
import { Button, Card, Form, Input, message as Message, Popconfirm, Tooltip } 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'
|
||||
import FormBody from './form'
|
||||
|
||||
// 配置页面所需接口函数
|
||||
const apiAction = {
|
||||
page: api.sysConfigPage,
|
||||
add: api.sysConfigAdd,
|
||||
edit: api.sysConfigEdit,
|
||||
delete: api.sysConfigDelete,
|
||||
}
|
||||
|
||||
// 用于弹窗标题
|
||||
const name = '应用'
|
||||
|
||||
// 统一配置权限标识
|
||||
const authName = 'sysConfig'
|
||||
|
||||
export default class index extends Component {
|
||||
state = {
|
||||
codes: {
|
||||
constsType: [],
|
||||
},
|
||||
}
|
||||
|
||||
// 表格实例
|
||||
table = React.createRef()
|
||||
|
||||
// 新增窗口实例
|
||||
addForm = React.createRef()
|
||||
// 编辑窗口实例
|
||||
editForm = React.createRef()
|
||||
|
||||
columns = [
|
||||
{
|
||||
title: '参数名称',
|
||||
dataIndex: 'name',
|
||||
width: 200,
|
||||
sorter: true,
|
||||
},
|
||||
{
|
||||
title: '唯一编码',
|
||||
dataIndex: 'code',
|
||||
width: 200,
|
||||
sorter: true,
|
||||
ellipsis: {
|
||||
showTitle: false,
|
||||
},
|
||||
render: text => <Tooltip title={text}>{text}</Tooltip>,
|
||||
},
|
||||
{
|
||||
title: '参数值',
|
||||
dataIndex: 'value',
|
||||
width: 200,
|
||||
sorter: true,
|
||||
},
|
||||
{
|
||||
title: '所属分类',
|
||||
dataIndex: 'groupCode',
|
||||
width: 140,
|
||||
sorter: true,
|
||||
render: text => this.bindCodeValue(text, 'consts_type'),
|
||||
},
|
||||
{
|
||||
title: '备注',
|
||||
dataIndex: 'remark',
|
||||
width: 400,
|
||||
sorter: true,
|
||||
ellipsis: {
|
||||
showTitle: false,
|
||||
},
|
||||
render: text => <Tooltip title={text}>{text}</Tooltip>,
|
||||
},
|
||||
]
|
||||
|
||||
/**
|
||||
* 构造函数,在渲染前动态添加操作字段等
|
||||
* @param {*} props
|
||||
*/
|
||||
constructor(props) {
|
||||
super(props)
|
||||
|
||||
const flag = auth({ [authName]: [['edit'], ['delete']] })
|
||||
|
||||
if (flag) {
|
||||
this.columns.push({
|
||||
title: '操作',
|
||||
width: 150,
|
||||
dataIndex: 'actions',
|
||||
render: (text, record) => (
|
||||
<QueryTableActions>
|
||||
<Auth auth={{ [authName]: 'edit' }}>
|
||||
<a onClick={() => this.onOpen(this.editForm, record)}>编辑</a>
|
||||
</Auth>
|
||||
<Auth auth={{ [authName]: 'delete' }}>
|
||||
<Popconfirm
|
||||
placement="topRight"
|
||||
title="是否确认删除"
|
||||
onConfirm={() => this.onDelete(record)}
|
||||
>
|
||||
<a>删除</a>
|
||||
</Popconfirm>
|
||||
</Auth>
|
||||
</QueryTableActions>
|
||||
),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 阻止外部组件引发的渲染,提升性能
|
||||
* 可自行添加渲染条件
|
||||
* [必要]
|
||||
* @param {*} props
|
||||
* @param {*} state
|
||||
* @returns
|
||||
*/
|
||||
shouldComponentUpdate(props, state) {
|
||||
return !isEqual(this.state, state)
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载字典数据,之后开始加载表格数据
|
||||
* 如果必须要加载字典数据,可直接对表格设置autoLoad=true
|
||||
*/
|
||||
componentDidMount() {
|
||||
const { onLoading, onLoadData } = this.table.current
|
||||
onLoading()
|
||||
getDictData('consts_type').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 自定义方法
|
||||
//#endregion
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Container mode="fluid">
|
||||
<br />
|
||||
<Card bordered={false}>
|
||||
<QueryTable
|
||||
ref={this.table}
|
||||
autoLoad={false}
|
||||
loadData={this.loadData}
|
||||
columns={this.columns}
|
||||
scroll={{ x: 1140 }}
|
||||
query={
|
||||
<Auth auth={{ [authName]: 'page' }}>
|
||||
<Form.Item label="参数名称" name="name">
|
||||
<Input autoComplete="off" placeholder="请输入参数名称" />
|
||||
</Form.Item>
|
||||
<Form.Item label="唯一编码" name="code">
|
||||
<Input autoComplete="off" placeholder="请输入唯一编码" />
|
||||
</Form.Item>
|
||||
</Auth>
|
||||
}
|
||||
operator={
|
||||
<Auth auth={{ [authName]: 'add' }}>
|
||||
<Button
|
||||
icon={<AntIcon type="plus" />}
|
||||
onClick={() => this.onOpen(this.addForm)}
|
||||
>
|
||||
新增{name}
|
||||
</Button>
|
||||
</Auth>
|
||||
}
|
||||
/>
|
||||
</Card>
|
||||
|
||||
<Auth auth={{ [authName]: 'add' }}>
|
||||
<ModalForm
|
||||
title={`新增${name}`}
|
||||
action={apiAction.add}
|
||||
ref={this.addForm}
|
||||
onSuccess={() => this.table.current.onReloadData()}
|
||||
>
|
||||
<FormBody />
|
||||
</ModalForm>
|
||||
</Auth>
|
||||
|
||||
<Auth auth={{ [authName]: 'edit' }}>
|
||||
<ModalForm
|
||||
title={`编辑${name}`}
|
||||
action={apiAction.edit}
|
||||
ref={this.editForm}
|
||||
onSuccess={() => this.table.current.onReloadData()}
|
||||
>
|
||||
<FormBody />
|
||||
</ModalForm>
|
||||
</Auth>
|
||||
</Container>
|
||||
)
|
||||
}
|
||||
}
|
||||
110
framework/web-react/src/pages/system/dict/dictdata/form.jsx
Normal file
110
framework/web-react/src/pages/system/dict/dictdata/form.jsx
Normal file
@@ -0,0 +1,110 @@
|
||||
import React, { Component } from 'react'
|
||||
import { Form, message as Message, Spin } from 'antd'
|
||||
import { AntIcon } from 'components'
|
||||
import { cloneDeep } from 'lodash'
|
||||
import MonacoEditor from 'react-monaco-editor'
|
||||
import store from 'store'
|
||||
|
||||
const { getState } = store
|
||||
|
||||
const initialValues = {}
|
||||
|
||||
export default class form extends Component {
|
||||
state = {
|
||||
// 加载状态
|
||||
loading: true,
|
||||
...getState('layout'),
|
||||
}
|
||||
|
||||
// 表单实例
|
||||
form = React.createRef()
|
||||
|
||||
code = React.createRef()
|
||||
|
||||
// 初始化数据
|
||||
record = {}
|
||||
|
||||
/**
|
||||
* mount后回调
|
||||
*/
|
||||
componentDidMount() {
|
||||
this.props.created && this.props.created(this)
|
||||
}
|
||||
|
||||
/**
|
||||
* 填充数据
|
||||
* 可以在设置this.record之后对其作出数据结构调整
|
||||
* [异步,必要]
|
||||
* @param {*} params
|
||||
*/
|
||||
async fillData(params) {
|
||||
this.record = cloneDeep(params.record)
|
||||
//#region 从后端转换成前段所需格式
|
||||
this.code.current.editor.setValue(this.record.extCode || '')
|
||||
setTimeout(() => {
|
||||
this.code.current.editor.getAction(['editor.action.formatDocument'])._run()
|
||||
}, 100)
|
||||
//#endregion
|
||||
this.form.current.setFieldsValue(this.record)
|
||||
|
||||
this.setState({
|
||||
loading: false,
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取数据
|
||||
* 可以对postData进行数据结构调整
|
||||
* [异步,必要]
|
||||
* @returns
|
||||
*/
|
||||
async getData() {
|
||||
const form = this.form.current
|
||||
|
||||
const valid = await form.validateFields()
|
||||
if (valid) {
|
||||
const postData = form.getFieldsValue()
|
||||
if (this.record) {
|
||||
postData.id = this.record.id
|
||||
}
|
||||
//#region 从前段转换后端所需格式
|
||||
try {
|
||||
const code = JSON.parse(this.code.current.editor.getValue())
|
||||
if (code.constructor === Object) {
|
||||
postData.extCode = JSON.stringify(code)
|
||||
} else {
|
||||
throw new Error(0)
|
||||
}
|
||||
} catch {
|
||||
Message.error('错误的JSON格式')
|
||||
}
|
||||
//#endregion
|
||||
return postData
|
||||
}
|
||||
}
|
||||
|
||||
//#region 自定义方法
|
||||
//#endregion
|
||||
|
||||
render() {
|
||||
const { theme } = this.state
|
||||
|
||||
return (
|
||||
<Form initialValues={initialValues} ref={this.form} className="yo-form">
|
||||
<Spin spinning={this.state.loading} indicator={<AntIcon type="loading" />}>
|
||||
<div className="yo-form-group">
|
||||
<MonacoEditor
|
||||
height={300}
|
||||
language="json"
|
||||
theme={theme === 'dark' ? 'vs-dark' : 'default'}
|
||||
options={{
|
||||
fontSize: 12,
|
||||
}}
|
||||
ref={this.code}
|
||||
/>
|
||||
</div>
|
||||
</Spin>
|
||||
</Form>
|
||||
)
|
||||
}
|
||||
}
|
||||
436
framework/web-react/src/pages/system/dict/dictdata/index.jsx
Normal file
436
framework/web-react/src/pages/system/dict/dictdata/index.jsx
Normal file
@@ -0,0 +1,436 @@
|
||||
import React, { Component } from 'react'
|
||||
import { Button, Card, Form, Input, Popconfirm, message as Message, InputNumber } from 'antd'
|
||||
import { isEqual } from 'lodash'
|
||||
import { AntIcon, Auth, Container, ModalForm, QueryTable, QueryTableActions } from 'components'
|
||||
import { api } from 'common/api'
|
||||
import getDictData from 'util/dic'
|
||||
import auth from 'components/authorized/handler'
|
||||
import { toCamelCase } from 'util/format'
|
||||
import FormBody from './form'
|
||||
|
||||
// 配置页面所需接口函数
|
||||
const apiAction = {
|
||||
page: api.sysDictDataPage,
|
||||
add: api.sysDictDataAdd,
|
||||
edit: api.sysDictDataEdit,
|
||||
delete: api.sysDictDataDelete,
|
||||
deleteBatch: api.sysDictDataDeleteBatch,
|
||||
}
|
||||
|
||||
// 用于弹窗标题
|
||||
const name = '字典值'
|
||||
|
||||
export default class index extends Component {
|
||||
state = {
|
||||
codes: {
|
||||
commonStatus: [],
|
||||
},
|
||||
|
||||
selectedRowKeys: [],
|
||||
}
|
||||
|
||||
// 表格实例
|
||||
table = React.createRef()
|
||||
|
||||
form = React.createRef()
|
||||
|
||||
// JSON编辑窗口实例
|
||||
jsonForm = React.createRef()
|
||||
|
||||
// 表格字段
|
||||
columns = [
|
||||
{
|
||||
title: '文本',
|
||||
dataIndex: 'value',
|
||||
sorter: true,
|
||||
width: 200,
|
||||
render: (text, record, index) => (
|
||||
<Form.Item
|
||||
name={[index, 'value']}
|
||||
rules={[{ required: true, message: '请输入文本' }]}
|
||||
className="mb-none"
|
||||
>
|
||||
<Input autoComplete="off" placeholder="请输入文本" />
|
||||
</Form.Item>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: '字典值',
|
||||
dataIndex: 'code',
|
||||
sorter: true,
|
||||
width: 200,
|
||||
render: (text, record, index) => (
|
||||
<Form.Item
|
||||
name={[index, 'code']}
|
||||
rules={[{ required: true, message: '请输入文本' }]}
|
||||
className="mb-none"
|
||||
>
|
||||
<Input autoComplete="off" placeholder="请输入字典值" />
|
||||
</Form.Item>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: '扩展值',
|
||||
dataIndex: 'extCode',
|
||||
width: 80,
|
||||
align: 'center',
|
||||
render: (text, record, index) => (
|
||||
<>
|
||||
<Form.Item name={[index, 'extCode']} className="hidden">
|
||||
<Input type="hidden" />
|
||||
</Form.Item>
|
||||
{auth('sysDictData:edit') ? (
|
||||
<a
|
||||
onClick={() => this.onOpen(this.jsonForm, record)}
|
||||
style={{
|
||||
fontWeight: 'bold',
|
||||
display: 'inline-block',
|
||||
transform: 'scaleY(.85)',
|
||||
color: 'transparent',
|
||||
backgroundImage: 'linear-gradient(135deg, #007bff, #52c41a)',
|
||||
WebkitBackgroundClip: 'text',
|
||||
}}
|
||||
>
|
||||
JSON
|
||||
</a>
|
||||
) : (
|
||||
<>{text}</>
|
||||
)}
|
||||
</>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: '排序',
|
||||
dataIndex: 'sort',
|
||||
sorter: true,
|
||||
width: 100,
|
||||
render: (text, record, index) => (
|
||||
<Form.Item name={[index, 'sort']} className="mb-none">
|
||||
<InputNumber
|
||||
max={1000}
|
||||
min={0}
|
||||
step={1}
|
||||
className="w-100-p"
|
||||
autoComplete="off"
|
||||
placeholder="排序"
|
||||
/>
|
||||
</Form.Item>
|
||||
),
|
||||
defaultSortOrder: 'ascend',
|
||||
},
|
||||
{
|
||||
title: '备注',
|
||||
dataIndex: 'remark',
|
||||
sorter: true,
|
||||
render: (text, record, index) => (
|
||||
<Form.Item name={[index, 'remark']} className="mb-none">
|
||||
<Input autoComplete="off" placeholder="请输入备注" />
|
||||
</Form.Item>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: '状态',
|
||||
dataIndex: 'status',
|
||||
sorter: true,
|
||||
width: 80,
|
||||
render: text => this.bindCodeValue(text, 'common_status'),
|
||||
},
|
||||
]
|
||||
|
||||
/**
|
||||
* 构造函数,在渲染前动态添加操作字段等
|
||||
* @param {*} props
|
||||
*/
|
||||
constructor(props) {
|
||||
super(props)
|
||||
|
||||
const flag = auth({ sysDictData: [['edit'], ['delete']] })
|
||||
|
||||
if (flag) {
|
||||
this.columns.push({
|
||||
title: '操作',
|
||||
width: 150,
|
||||
dataIndex: 'actions',
|
||||
render: (text, record, index) => (
|
||||
<QueryTableActions>
|
||||
{record.id !== -1 ? (
|
||||
<Auth auth="sysDictData:edit">
|
||||
<a onClick={() => this.onEdit(index)}>保存编辑</a>
|
||||
</Auth>
|
||||
) : (
|
||||
<Auth auth="sysDictData:add">
|
||||
<a onClick={() => this.onAdd(index)}>保存新增</a>
|
||||
</Auth>
|
||||
)}
|
||||
<Auth auth="sysDictData:delete">
|
||||
<Popconfirm
|
||||
placement="topRight"
|
||||
title="是否确认删除"
|
||||
onConfirm={() => this.onDelete(record)}
|
||||
>
|
||||
<a>删除</a>
|
||||
</Popconfirm>
|
||||
</Auth>
|
||||
</QueryTableActions>
|
||||
),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 阻止外部组件引发的渲染,提升性能
|
||||
* 可自行添加渲染条件
|
||||
* [必要]
|
||||
* @param {*} props
|
||||
* @param {*} state
|
||||
* @returns
|
||||
*/
|
||||
shouldComponentUpdate(props, state) {
|
||||
return !isEqual(this.state, state)
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载字典数据,之后开始加载表格数据
|
||||
* 如果必须要加载字典数据,可直接对表格设置autoLoad=true
|
||||
*/
|
||||
componentDidMount() {
|
||||
this.table.current.onLoading()
|
||||
getDictData('common_status').then(res => {
|
||||
this.setState(
|
||||
{
|
||||
codes: res,
|
||||
},
|
||||
() => {
|
||||
this.table.current.onLoadData()
|
||||
}
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 调用加载数据接口,可在调用前对query进行处理
|
||||
* [异步,必要]
|
||||
* @param {*} params
|
||||
* @param {*} query
|
||||
* @returns
|
||||
*/
|
||||
loadData = async (params, query) => {
|
||||
query = {
|
||||
...query,
|
||||
typeId: this.props.type.id,
|
||||
}
|
||||
|
||||
const { data } = await apiAction.page({
|
||||
...params,
|
||||
...query,
|
||||
})
|
||||
|
||||
const values = {}
|
||||
data.items.forEach((item, index) => {
|
||||
values[index] = item
|
||||
})
|
||||
|
||||
this.form.current.setFieldsValue(values)
|
||||
|
||||
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, reload = true) {
|
||||
const table = this.table.current
|
||||
table.onLoading()
|
||||
try {
|
||||
await action
|
||||
Message.success(successMessage)
|
||||
if (reload) {
|
||||
table.onReloadData()
|
||||
} else {
|
||||
table.onLoaded()
|
||||
}
|
||||
} catch {
|
||||
table.onLoaded()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除
|
||||
* @param {*} record
|
||||
*/
|
||||
onDelete(record) {
|
||||
this.onAction(apiAction.delete(record), '删除成功')
|
||||
}
|
||||
|
||||
//#region 自定义方法
|
||||
onAddRow() {
|
||||
const record = {
|
||||
// 为了正常显示checkbox,默认给id赋予了-1
|
||||
id: -1,
|
||||
value: '',
|
||||
code: '',
|
||||
typeId: this.props.type.id,
|
||||
sort: 100,
|
||||
status: 0,
|
||||
remark: null,
|
||||
}
|
||||
const index = this.table.current.onAddRow(record)
|
||||
if (index !== false) {
|
||||
this.form.current.setFieldsValue({
|
||||
[index]: record,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
async onAdd(index) {
|
||||
const form = this.form.current
|
||||
try {
|
||||
await form.validateFields()
|
||||
} catch (err) {
|
||||
const e = err.errorFields.filter(item => item.name.includes(index))
|
||||
if (e.length) {
|
||||
return
|
||||
}
|
||||
}
|
||||
const record = form.getFieldsValue([index])[index]
|
||||
// 为了正常显示checkbox,默认给id赋予了-1,在这里删除id以表示新增
|
||||
record.id = undefined
|
||||
this.onAction(apiAction.add(record), '新增成功')
|
||||
}
|
||||
|
||||
async onEdit(index) {
|
||||
const form = this.form.current
|
||||
try {
|
||||
await form.validateFields()
|
||||
} catch (err) {
|
||||
const e = err.errorFields.filter(item => item.name.includes(index))
|
||||
if (e.length) {
|
||||
return
|
||||
}
|
||||
}
|
||||
const record = form.getFieldsValue([index])[index]
|
||||
this.onAction(apiAction.edit(record), '编辑成功', false)
|
||||
}
|
||||
|
||||
async onDeleteBatch() {
|
||||
await this.onAction(apiAction.deleteBatch(this.state.selectedRowKeys), '删除成功')
|
||||
|
||||
this.setState({
|
||||
selectedRowKeys: [],
|
||||
})
|
||||
}
|
||||
|
||||
onSaveExtCode = ({ id, extCode }) => {
|
||||
const table = this.table.current,
|
||||
{ dataSource } = table.state,
|
||||
data = dataSource.find(item => item.id === id),
|
||||
index = dataSource.indexOf(data)
|
||||
this.form.current.setFieldsValue({
|
||||
[index]: {
|
||||
extCode,
|
||||
},
|
||||
})
|
||||
dataSource[index].extCode = extCode
|
||||
table.setState({ dataSource })
|
||||
}
|
||||
//#endregion
|
||||
|
||||
render() {
|
||||
const { selectedRowKeys } = this.state
|
||||
|
||||
return (
|
||||
<Container mode="fluid">
|
||||
<br />
|
||||
<Card>
|
||||
<QueryTable
|
||||
ref={this.table}
|
||||
autoLoad={false}
|
||||
loadData={this.loadData}
|
||||
columns={this.columns}
|
||||
editable={true}
|
||||
form={this.form}
|
||||
sticky={false}
|
||||
rowSelection={{
|
||||
selectedRowKeys,
|
||||
onChange: selectedRowKeys => this.setState({ selectedRowKeys }),
|
||||
getCheckboxProps: record => ({
|
||||
disabled: record.id === -1,
|
||||
}),
|
||||
}}
|
||||
query={
|
||||
<Auth auth="sysDictData:page">
|
||||
<Form.Item label="文本" name="value">
|
||||
<Input autoComplete="off" placeholder="请输入文本" />
|
||||
</Form.Item>
|
||||
<Form.Item label="字典值" name="code">
|
||||
<Input autoComplete="off" placeholder="请输入字典值" />
|
||||
</Form.Item>
|
||||
</Auth>
|
||||
}
|
||||
operator={
|
||||
<Auth auth="sysDictData:delete">
|
||||
<Popconfirm
|
||||
disabled={!selectedRowKeys.length}
|
||||
placement="bottom"
|
||||
title="是否确认批量删除"
|
||||
onConfirm={() => this.onDeleteBatch()}
|
||||
>
|
||||
<Button disabled={!selectedRowKeys.length} danger>
|
||||
批量删除
|
||||
</Button>
|
||||
</Popconfirm>
|
||||
</Auth>
|
||||
}
|
||||
footer={() => (
|
||||
<Auth auth="sysDictData:add">
|
||||
<Button
|
||||
block
|
||||
icon={<AntIcon type="plus" />}
|
||||
onClick={() => this.onAddRow()}
|
||||
>
|
||||
新增{name}
|
||||
</Button>
|
||||
</Auth>
|
||||
)}
|
||||
/>
|
||||
</Card>
|
||||
|
||||
<ModalForm title="编辑" action={this.onSaveExtCode} ref={this.jsonForm}>
|
||||
<FormBody />
|
||||
</ModalForm>
|
||||
</Container>
|
||||
)
|
||||
}
|
||||
}
|
||||
166
framework/web-react/src/pages/system/dict/form.jsx
Normal file
166
framework/web-react/src/pages/system/dict/form.jsx
Normal file
@@ -0,0 +1,166 @@
|
||||
import React, { Component } from 'react'
|
||||
import { Form, Input, Radio, InputNumber, TreeSelect, Spin } from 'antd'
|
||||
import { AntIcon } from 'components'
|
||||
import { cloneDeep } from 'lodash'
|
||||
import getDictData from 'util/dic'
|
||||
import { EMPTY_ID } from 'util/global'
|
||||
import { api } from 'common/api'
|
||||
|
||||
const initialValues = {
|
||||
type: 1,
|
||||
sort: 100
|
||||
}
|
||||
export default class form extends Component {
|
||||
|
||||
state = {
|
||||
// 加载状态
|
||||
loading: true,
|
||||
options: {
|
||||
dicTreeData: []
|
||||
}
|
||||
}
|
||||
|
||||
// 表单实例
|
||||
form = React.createRef()
|
||||
|
||||
// 初始化数据
|
||||
record = {}
|
||||
|
||||
/**
|
||||
* mount后回调
|
||||
*/
|
||||
componentDidMount() {
|
||||
this.props.created && this.props.created(this)
|
||||
}
|
||||
|
||||
/**
|
||||
* 填充数据
|
||||
* 可以在设置this.record之后对其作出数据结构调整
|
||||
* [异步,必要]
|
||||
* @param {*} params
|
||||
*/
|
||||
async fillData(params) {
|
||||
this.record = cloneDeep(params.record)
|
||||
const treeData = await this.loadDicTreeData()
|
||||
this.setState({
|
||||
options: {
|
||||
dicTreeData: treeData
|
||||
}
|
||||
})
|
||||
|
||||
this.record = {
|
||||
pid: params.pid,
|
||||
...this.record
|
||||
}
|
||||
|
||||
if (this.record.code) {
|
||||
this.record.type = 2;
|
||||
this.setState({
|
||||
type: 2
|
||||
})
|
||||
}
|
||||
|
||||
this.form.current.setFieldsValue(this.record)
|
||||
|
||||
this.setState({
|
||||
loading: false
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取数据
|
||||
* 可以对postData进行数据结构调整
|
||||
* [异步,必要]
|
||||
* @returns
|
||||
*/
|
||||
async getData() {
|
||||
const form = this.form.current
|
||||
|
||||
const valid = await form.validateFields()
|
||||
if (valid) {
|
||||
const postData = form.getFieldsValue()
|
||||
if (this.record) {
|
||||
postData.id = this.record.id
|
||||
}
|
||||
//#region 从前段转换后端所需格式
|
||||
//#endregion
|
||||
return postData
|
||||
}
|
||||
}
|
||||
//#region 自定义方法
|
||||
async loadDicTreeData() {
|
||||
const { data } = await api.sysDictTypeTree()
|
||||
return [{
|
||||
id: EMPTY_ID,
|
||||
parentId: undefined,
|
||||
title: '顶级',
|
||||
value: EMPTY_ID,
|
||||
pid: undefined,
|
||||
children: data,
|
||||
}]
|
||||
}
|
||||
|
||||
//#endregion
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Form
|
||||
initialValues={initialValues}
|
||||
ref={this.form}
|
||||
className="yo-form"
|
||||
onValuesChange={(changeValues) => {
|
||||
if (changeValues.hasOwnProperty('type')) {
|
||||
this.setState({
|
||||
type: changeValues.type
|
||||
})
|
||||
}
|
||||
}}
|
||||
>
|
||||
<Spin spinning={this.state.loading} indicator={<AntIcon type="loading" />}>
|
||||
<div className="yo-form-group">
|
||||
{/* 表单控件 */}
|
||||
<Form.Item label="类型名称" name="name" rules={[{ required: true, message: '请输入类型名称' }]}>
|
||||
<Input autoComplete="off" placeholder="请输入类型名称" />
|
||||
</Form.Item>
|
||||
<Form.Item label="类型" name="type" rules={[{ required: true, message: '请选择需要添加的数据类型' }]}>
|
||||
<Radio.Group>
|
||||
<Radio.Button value={1}>
|
||||
目录
|
||||
</Radio.Button>
|
||||
<Radio.Button value={2}>
|
||||
字典类型
|
||||
</Radio.Button>
|
||||
</Radio.Group>
|
||||
</Form.Item>
|
||||
<Form.Item label="父结点" name="pid" rules={[{ required: true, message: '请选择父结点' }]}>
|
||||
<TreeSelect
|
||||
treeData={this.state.options.dicTreeData}
|
||||
dropdownStyle={{ maxHeight: '300px', overflow: 'auto' }}
|
||||
treeDefaultExpandAll
|
||||
placeholder="请选择父结点"
|
||||
/>
|
||||
</Form.Item>
|
||||
{this.state.type == 2 &&
|
||||
<>
|
||||
<Form.Item label="唯一编码" name="code" rules={[{ required: true, message: '请输入唯一编码' }]}>
|
||||
<Input autoComplete="off" placeholder="请输入唯一编码" />
|
||||
</Form.Item>
|
||||
</>
|
||||
}
|
||||
<Form.Item label="排序" name="sort">
|
||||
<InputNumber
|
||||
min={0}
|
||||
placeholder="请输入排序"
|
||||
className="w-100-p"
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form.Item label="备注" name="remark">
|
||||
<Input.TextArea placeholder="请输入备注" />
|
||||
</Form.Item>
|
||||
{/* ... */}
|
||||
</div>
|
||||
</Spin>
|
||||
</Form>
|
||||
)
|
||||
}
|
||||
}
|
||||
324
framework/web-react/src/pages/system/dict/index.jsx
Normal file
324
framework/web-react/src/pages/system/dict/index.jsx
Normal file
@@ -0,0 +1,324 @@
|
||||
import React, { Component } from 'react'
|
||||
import { Button, Card, Form, Input, message as Message, Popconfirm, Radio } from 'antd'
|
||||
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'
|
||||
import { isEqual } from 'lodash'
|
||||
import getDictData from 'util/dic'
|
||||
import FormBody from './form'
|
||||
import DictData from './dictdata'
|
||||
|
||||
const apiAction = {
|
||||
tree: api.sysDictTypeTree,
|
||||
page: api.sysDictTypePage,
|
||||
add: api.sysDictTypeAdd,
|
||||
edit: api.sysDictTypeEdit,
|
||||
delete: api.sysDictTypeDelete,
|
||||
}
|
||||
|
||||
const name = '字典'
|
||||
|
||||
export default class index extends Component {
|
||||
state = {
|
||||
codes: {
|
||||
commonStatus: [],
|
||||
},
|
||||
}
|
||||
|
||||
// 表格实例
|
||||
table = React.createRef()
|
||||
|
||||
// 新增窗口实例
|
||||
addForm = React.createRef()
|
||||
// 编辑窗口实例
|
||||
editForm = React.createRef()
|
||||
|
||||
// 树选中节点
|
||||
selectId = undefined
|
||||
|
||||
// 表格字段
|
||||
columns = [
|
||||
{
|
||||
title: '字典名称',
|
||||
dataIndex: 'name',
|
||||
width: 200,
|
||||
sorter: true,
|
||||
},
|
||||
{
|
||||
title: '类型',
|
||||
key: 'type',
|
||||
dataIndex: 'code',
|
||||
width: 120,
|
||||
sorter: true,
|
||||
render: text => (text ? '字典类型' : '目录'),
|
||||
},
|
||||
{
|
||||
title: '唯一编码',
|
||||
dataIndex: 'code',
|
||||
width: 120,
|
||||
sorter: true,
|
||||
},
|
||||
{
|
||||
title: '排序',
|
||||
dataIndex: 'sort',
|
||||
width: 80,
|
||||
sorter: true,
|
||||
defaultSortOrder: 'ascend',
|
||||
},
|
||||
{
|
||||
title: '备注',
|
||||
dataIndex: 'remark',
|
||||
width: 200,
|
||||
sorter: true,
|
||||
},
|
||||
{
|
||||
title: '状态',
|
||||
dataIndex: 'status',
|
||||
width: 80,
|
||||
sorter: true,
|
||||
render: text => this.bindCodeValue(text, 'common_status'),
|
||||
},
|
||||
]
|
||||
|
||||
/**
|
||||
* 构造函数,在渲染前动态添加操作字段等
|
||||
* @param {*} props
|
||||
*/
|
||||
constructor(props) {
|
||||
super(props)
|
||||
|
||||
const flag = auth({ sysDict: [['edit'], ['delete']] })
|
||||
|
||||
if (flag) {
|
||||
this.columns.push({
|
||||
title: '操作',
|
||||
width: 150,
|
||||
dataIndex: 'actions',
|
||||
render: (text, record) => (
|
||||
<QueryTableActions>
|
||||
<Auth auth="sysDict:edit">
|
||||
<a onClick={() => this.onOpen(this.editForm, record)}>编辑</a>
|
||||
</Auth>
|
||||
<Auth auth="sysDict:delete">
|
||||
<Popconfirm
|
||||
placement="topRight"
|
||||
title="是否确认删除"
|
||||
onConfirm={() => this.onDelete(record)}
|
||||
>
|
||||
<a>删除</a>
|
||||
</Popconfirm>
|
||||
</Auth>
|
||||
</QueryTableActions>
|
||||
),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 阻止外部组件引发的渲染,提升性能
|
||||
* 可自行添加渲染条件
|
||||
* [必要]
|
||||
* @param {*} props
|
||||
* @param {*} state
|
||||
* @returns
|
||||
*/
|
||||
shouldComponentUpdate(props, state) {
|
||||
return !isEqual(this.state, state)
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载字典数据,之后开始加载表格数据
|
||||
* 如果必须要加载字典数据,可直接对表格设置autoLoad=true
|
||||
*/
|
||||
componentDidMount() {
|
||||
this.table.current.onLoading()
|
||||
getDictData('common_status').then(res => {
|
||||
this.setState(
|
||||
{
|
||||
codes: res,
|
||||
},
|
||||
() => {
|
||||
this.table.current.onLoadData()
|
||||
}
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 调用加载数据接口,可在调用前对query进行处理
|
||||
* [异步,必要]
|
||||
* @param {*} params
|
||||
* @param {*} query
|
||||
* @returns
|
||||
*/
|
||||
loadData = async (params, query) => {
|
||||
query = {
|
||||
...query,
|
||||
pid: this.selectId,
|
||||
}
|
||||
|
||||
const { data } = await apiAction.page({
|
||||
...params,
|
||||
...query,
|
||||
})
|
||||
return data
|
||||
}
|
||||
|
||||
/**
|
||||
* 调用树结构数据接口
|
||||
* [异步,必要]
|
||||
* @returns
|
||||
*/
|
||||
loadTreeData = async () => {
|
||||
const { data } = await apiAction.tree()
|
||||
return data
|
||||
}
|
||||
|
||||
/**
|
||||
* 树节点选中事件
|
||||
* [必要]
|
||||
* @param {*} id
|
||||
*/
|
||||
onSelectTree(id) {
|
||||
this.selectId = id
|
||||
this.table.current.onReloadData()
|
||||
}
|
||||
|
||||
/**
|
||||
* 绑定字典数据
|
||||
* @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({
|
||||
pid: this.selectId,
|
||||
record,
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 对表格上的操作进行统一处理
|
||||
* [异步]
|
||||
* @param {*} action
|
||||
* @param {*} successMessage
|
||||
*/
|
||||
async onAction(action, successMessage) {
|
||||
this.table.current.onLoading()
|
||||
try {
|
||||
await action
|
||||
Message.success(successMessage)
|
||||
this.table.current.onReloadData()
|
||||
} catch {
|
||||
this.table.current.onLoaded()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除
|
||||
* @param {*} record
|
||||
*/
|
||||
onDelete(record) {
|
||||
this.onAction(apiAction.delete(record), '删除成功')
|
||||
}
|
||||
|
||||
//#region 自定义方法
|
||||
//#endregion
|
||||
|
||||
render() {
|
||||
return (
|
||||
<QueryTreeLayout
|
||||
loadData={this.loadTreeData}
|
||||
defaultExpanded={true}
|
||||
onSelect={key => this.onSelectTree(key)}
|
||||
>
|
||||
<Container mode="fluid">
|
||||
<Card bordered={false}>
|
||||
<QueryTable
|
||||
ref={this.table}
|
||||
autoLoad={false}
|
||||
loadData={this.loadData}
|
||||
columns={this.columns}
|
||||
queryInitialValues={{
|
||||
type: 2,
|
||||
}}
|
||||
query={
|
||||
<Auth auth="sysDict:page">
|
||||
<Form.Item label="类型名称" name="name">
|
||||
<Input autoComplete="off" placeholder="请输入类型名称" />
|
||||
</Form.Item>
|
||||
<Form.Item label="唯一编码" name="code">
|
||||
<Input autoComplete="off" placeholder="请输入唯一编码" />
|
||||
</Form.Item>
|
||||
<Form.Item label="类型" name="type">
|
||||
<Radio.Group>
|
||||
<Radio.Button value={1}>目录</Radio.Button>
|
||||
<Radio.Button value={2}>字典类型</Radio.Button>
|
||||
</Radio.Group>
|
||||
</Form.Item>
|
||||
</Auth>
|
||||
}
|
||||
operator={
|
||||
<Auth auth="sysDict:add">
|
||||
<Button
|
||||
icon={<AntIcon type="plus" />}
|
||||
onClick={() => this.onOpen(this.addForm)}
|
||||
>
|
||||
新增{name}
|
||||
</Button>
|
||||
</Auth>
|
||||
}
|
||||
expandable={{
|
||||
expandedRowRender: record => <DictData type={record} />,
|
||||
rowExpandable: record => !!record.code,
|
||||
}}
|
||||
/>
|
||||
</Card>
|
||||
</Container>
|
||||
<ModalForm
|
||||
title={`新增${name}`}
|
||||
action={apiAction.add}
|
||||
ref={this.addForm}
|
||||
onSuccess={() => this.table.current.onReloadData()}
|
||||
>
|
||||
<FormBody />
|
||||
</ModalForm>
|
||||
|
||||
<ModalForm
|
||||
title={`编辑${name}`}
|
||||
action={apiAction.edit}
|
||||
ref={this.editForm}
|
||||
onSuccess={() => this.table.current.onReloadData()}
|
||||
>
|
||||
<FormBody />
|
||||
</ModalForm>
|
||||
</QueryTreeLayout>
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
import React, { Component } from 'react'
|
||||
|
||||
export default class index extends Component {
|
||||
render() {
|
||||
return <div></div>
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
import React, { Component } from 'react'
|
||||
import { Breadcrumb, Space, Tag, Typography } from 'antd'
|
||||
import Highlighter from 'pages/system/doc/highlighter'
|
||||
|
||||
const { Title, Text } = Typography
|
||||
|
||||
export default class migrations extends Component {
|
||||
render() {
|
||||
return (
|
||||
<Space direction="vertical" className="w-100-p">
|
||||
<Text>在建立或修改了实体类之后,可以将实体类更新到数据库。</Text>
|
||||
<Text>
|
||||
在Visual Studio中选择
|
||||
<Breadcrumb separator=">">
|
||||
<Breadcrumb.Item>工具</Breadcrumb.Item>
|
||||
<Breadcrumb.Item>NuGet 包管理器</Breadcrumb.Item>
|
||||
<Breadcrumb.Item>程序包管理控制台</Breadcrumb.Item>
|
||||
</Breadcrumb>
|
||||
之后在打开的程序包管理控制台中,默认项目选择
|
||||
<Tag className="ml-xs" color="orange">
|
||||
Ewide.Database.Migrations
|
||||
</Tag>
|
||||
,并在控制台中输入命令。
|
||||
</Text>
|
||||
<Title level={4}>生成迁移文件</Title>
|
||||
<Highlighter code={"add-migration init -c 'DefaultDbContext'"} language={'c++'} />
|
||||
<Text>其中</Text>
|
||||
<ul>
|
||||
<li>
|
||||
<p>
|
||||
<Tag color="blue">add-migration</Tag>是固定的命令。
|
||||
</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>
|
||||
<Tag color="blue">init</Tag>是自定义的数据库版本号。
|
||||
</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>
|
||||
<Tag color="blue">-c</Tag>是固定的参数。
|
||||
</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>
|
||||
<Tag color="blue">'DefaultDbContext'</Tag>是对应的DbContext。
|
||||
</p>
|
||||
</li>
|
||||
</ul>
|
||||
<h4>更新迁移到数据库</h4>
|
||||
<Text>
|
||||
在确保
|
||||
<Tag className="ml-xs" color="orange">
|
||||
Ewide.Database.Migrations
|
||||
</Tag>
|
||||
中已经生成迁移文件之后,可以运行更新命令。
|
||||
</Text>
|
||||
<Highlighter
|
||||
code={"update-database -context 'DefaultDbContext'"}
|
||||
language={'c++'}
|
||||
/>
|
||||
<Text>其中</Text>
|
||||
<ul>
|
||||
<li>
|
||||
<p>
|
||||
<Tag color="blue">update-database</Tag>是固定的命令。
|
||||
</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>
|
||||
<Tag color="blue">-context</Tag>是固定的参数。
|
||||
</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>
|
||||
<Tag color="blue">'DefaultDbContext'</Tag>是对应的DbContext。
|
||||
</p>
|
||||
</li>
|
||||
</ul>
|
||||
</Space>
|
||||
)
|
||||
}
|
||||
}
|
||||
128
framework/web-react/src/pages/system/doc/back-end/index.jsx
Normal file
128
framework/web-react/src/pages/system/doc/back-end/index.jsx
Normal file
@@ -0,0 +1,128 @@
|
||||
import React, { Component } from 'react'
|
||||
import { Anchor, Card, Col, Row, Typography } from 'antd'
|
||||
import { Container } from 'components'
|
||||
import Database from './database'
|
||||
import DatabaseMigrations from './database/migrations'
|
||||
|
||||
const { Title, Link } = Typography
|
||||
|
||||
const docs = [
|
||||
{
|
||||
title: '数据库',
|
||||
component: Database,
|
||||
children: [
|
||||
{
|
||||
title: '实体(N)',
|
||||
},
|
||||
{
|
||||
title: '迁移',
|
||||
component: DatabaseMigrations,
|
||||
},
|
||||
],
|
||||
},
|
||||
]
|
||||
|
||||
export default class index extends Component {
|
||||
container = window
|
||||
|
||||
setContainer = container => {
|
||||
this.container = (container || { parentNode: window }).parentNode
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div ref={this.setContainer}>
|
||||
<Card bordered={false} className="mb-none">
|
||||
<Container mode="fluid">
|
||||
<Row gutter={[16, 24]} justify="center" align="middle">
|
||||
<Col span={4}>
|
||||
<Link href="https://dotnet.microsoft.com/" target="_blank">
|
||||
<img
|
||||
src="https://dotnet.microsoft.com/static/images/redesign/downloads-dot-net-core.svg?v=p6MWQNHwEtnnx0MWJ-i7vCMt-sZmoBf6h-7XmdSs5RE"
|
||||
width="128"
|
||||
alt=""
|
||||
/>
|
||||
</Link>
|
||||
</Col>
|
||||
<Col span={4}>
|
||||
<Link href="https://dotnetchina.gitee.io/furion/" target="_blank">
|
||||
<img
|
||||
src="https://dotnetchina.gitee.io/furion/img/furionlogo.png"
|
||||
width="128"
|
||||
alt=""
|
||||
/>
|
||||
</Link>
|
||||
</Col>
|
||||
</Row>
|
||||
<br />
|
||||
<Row gutter={16}>
|
||||
<Col flex="1">
|
||||
<Container mode="fluid">
|
||||
{docs.map((item, i) => (
|
||||
<React.Fragment key={i}>
|
||||
<section id={`doc-back-end-${i}`}>
|
||||
<Title level={2}>{item.title}</Title>
|
||||
{item.component && (
|
||||
<item.component codes={this.props.codes} />
|
||||
)}
|
||||
{item.children && (
|
||||
<>
|
||||
<br />
|
||||
<br />
|
||||
{item.children.map((citem, ci) => (
|
||||
<React.Fragment key={ci}>
|
||||
<section
|
||||
id={`doc-back-end-${i}-${ci}`}
|
||||
>
|
||||
<Title level={3}>
|
||||
{citem.title}
|
||||
</Title>
|
||||
{citem.component && (
|
||||
<citem.component
|
||||
codes={this.props.codes}
|
||||
/>
|
||||
)}
|
||||
</section>
|
||||
<br />
|
||||
</React.Fragment>
|
||||
))}
|
||||
</>
|
||||
)}
|
||||
</section>
|
||||
<br />
|
||||
</React.Fragment>
|
||||
))}
|
||||
</Container>
|
||||
</Col>
|
||||
<Col flex="240px">
|
||||
<Anchor
|
||||
getContainer={() => this.container}
|
||||
offsetTop={24}
|
||||
onClick={e => e.preventDefault()}
|
||||
>
|
||||
{docs.map((item, i) => (
|
||||
<Anchor.Link
|
||||
key={i}
|
||||
href={`#doc-back-end-${i}`}
|
||||
title={item.title}
|
||||
>
|
||||
{item.children &&
|
||||
item.children.map((citem, ci) => (
|
||||
<Anchor.Link
|
||||
key={ci}
|
||||
href={`#doc-back-end-${i}-${ci}`}
|
||||
title={citem.title}
|
||||
/>
|
||||
))}
|
||||
</Anchor.Link>
|
||||
))}
|
||||
</Anchor>
|
||||
</Col>
|
||||
</Row>
|
||||
{this.props.supportInfo}
|
||||
</Container>
|
||||
</Card>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
import React, { Component } from 'react'
|
||||
import { Space, Typography } from 'antd'
|
||||
|
||||
const { Text } = Typography
|
||||
|
||||
export default class index extends Component {
|
||||
render() {
|
||||
return (
|
||||
<Space direction="vertical" className="w-100-p">
|
||||
<Text>在本框架中,只需要进行简单的接口配置,就可以实现调用。</Text>
|
||||
</Space>
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
import React, { Component } from 'react'
|
||||
import { Space, Tag, Typography } from 'antd'
|
||||
import Highlighter from 'pages/system/doc/highlighter'
|
||||
|
||||
const { Text } = Typography
|
||||
|
||||
export default class setting extends Component {
|
||||
render() {
|
||||
return (
|
||||
<Space direction="vertical" className="w-100-p">
|
||||
<Text>
|
||||
维护接口地址在
|
||||
<Tag className="ml-xs" color="orange">
|
||||
/src/common/api/requests
|
||||
</Tag>
|
||||
中,通过每个子目录的
|
||||
<Tag className="ml-xs" color="orange">
|
||||
index.js
|
||||
</Tag>
|
||||
引入
|
||||
</Text>
|
||||
<Highlighter code={this.props.codes['api/setting.js']} language="javascript" />
|
||||
</Space>
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
import React, { Component } from 'react'
|
||||
import { Space, Typography } from 'antd'
|
||||
import Highlighter from 'pages/system/doc/highlighter'
|
||||
|
||||
const { Text } = Typography
|
||||
|
||||
export default class usage extends Component {
|
||||
render() {
|
||||
return (
|
||||
<Space direction="vertical" className="w-100-p">
|
||||
<Text>调用时需先引入。</Text>
|
||||
<Text>接口的函数名对应到上面配置的接口名称即可。</Text>
|
||||
<Highlighter code={this.props.codes['api/usage.js']} language="javascript" />
|
||||
</Space>
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
import React, { Component } from 'react'
|
||||
import { Space, Tag, Typography } from 'antd'
|
||||
import Highlighter from 'pages/system/doc/highlighter'
|
||||
|
||||
const { Text } = Typography
|
||||
|
||||
export default class index extends Component {
|
||||
render() {
|
||||
return (
|
||||
<Space direction="vertical" className="w-100-p">
|
||||
<Text>
|
||||
在不少业务组件中,时常需要做到权限控制一些按钮的显示隐藏。如果只使用样式来隐藏按钮,是不安全的。
|
||||
</Text>
|
||||
<Text>所以在本框架中,推荐将按钮用全局的权限组件包裹来控制是否渲染。</Text>
|
||||
<Highlighter code={this.props.codes['auth/index.txt']} language="javascript" />
|
||||
<Text>
|
||||
权限标识会读取全局用户信息中的
|
||||
<Tag className="ml-xs" color="orange">
|
||||
permissions
|
||||
</Tag>
|
||||
进行比对。
|
||||
</Text>
|
||||
<Text>权限组件内可以是任何元素,只要不符合条件,将不会渲染。</Text>
|
||||
</Space>
|
||||
)
|
||||
}
|
||||
}
|
||||
211
framework/web-react/src/pages/system/doc/front-end/index.jsx
Normal file
211
framework/web-react/src/pages/system/doc/front-end/index.jsx
Normal file
@@ -0,0 +1,211 @@
|
||||
import React, { Component } from 'react'
|
||||
import { Anchor, Card, Col, Row, Typography } from 'antd'
|
||||
import { Container } from 'components'
|
||||
|
||||
import Window from './window'
|
||||
import WindowOpen from './window/open'
|
||||
import WindowClose from './window/close'
|
||||
import WindowReload from './window/reload'
|
||||
|
||||
import Api from './api'
|
||||
import ApiSetting from './api/setting'
|
||||
import ApiUsage from './api/usage'
|
||||
|
||||
import Auth from './auth'
|
||||
|
||||
import Seed from './seed'
|
||||
|
||||
import Util from './util'
|
||||
import UtilDict from './util/dict'
|
||||
import UtilGlobal from './util/global'
|
||||
import UtilFormat from './util/format'
|
||||
import UtilFile from './util/file'
|
||||
import UtilQuery from './util/query'
|
||||
|
||||
const { Title, Link } = Typography
|
||||
|
||||
const docs = [
|
||||
{
|
||||
title: '窗口',
|
||||
component: Window,
|
||||
children: [
|
||||
{ title: '打开窗口', component: WindowOpen },
|
||||
{ title: '关闭窗口', component: WindowClose },
|
||||
{ title: '重新加载窗口', component: WindowReload },
|
||||
],
|
||||
},
|
||||
{
|
||||
title: '接口',
|
||||
component: Api,
|
||||
children: [
|
||||
{ title: '配置', component: ApiSetting },
|
||||
{ title: '调用', component: ApiUsage },
|
||||
],
|
||||
},
|
||||
{
|
||||
title: '权限渲染',
|
||||
component: Auth,
|
||||
},
|
||||
{
|
||||
title: '种子模版',
|
||||
component: Seed,
|
||||
},
|
||||
{
|
||||
title: '工具',
|
||||
component: Util,
|
||||
children: [
|
||||
{ title: '读取字典', component: UtilDict },
|
||||
{ title: '全局常量', component: UtilGlobal },
|
||||
{ title: '字符串格式转化', component: UtilFormat },
|
||||
{ title: '文件', component: UtilFile },
|
||||
{ title: '查询相关', component: UtilQuery },
|
||||
],
|
||||
},
|
||||
]
|
||||
|
||||
export default class index extends Component {
|
||||
container = window
|
||||
|
||||
setContainer = container => {
|
||||
this.container = (container || { parentNode: window }).parentNode
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div ref={this.setContainer}>
|
||||
<Card bordered={false} className="mb-none">
|
||||
<Container mode="fluid">
|
||||
<Row gutter={[16, 24]} justify="center" align="middle">
|
||||
<Col span={4}>
|
||||
<Link href="https://react.docschina.org/" target="_blank">
|
||||
<img
|
||||
src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9Ii0xMS41IC0xMC4yMzE3NCAyMyAyMC40NjM0OCI+CiAgPHRpdGxlPlJlYWN0IExvZ288L3RpdGxlPgogIDxjaXJjbGUgY3g9IjAiIGN5PSIwIiByPSIyLjA1IiBmaWxsPSIjNjFkYWZiIi8+CiAgPGcgc3Ryb2tlPSIjNjFkYWZiIiBzdHJva2Utd2lkdGg9IjEiIGZpbGw9Im5vbmUiPgogICAgPGVsbGlwc2Ugcng9IjExIiByeT0iNC4yIi8+CiAgICA8ZWxsaXBzZSByeD0iMTEiIHJ5PSI0LjIiIHRyYW5zZm9ybT0icm90YXRlKDYwKSIvPgogICAgPGVsbGlwc2Ugcng9IjExIiByeT0iNC4yIiB0cmFuc2Zvcm09InJvdGF0ZSgxMjApIi8+CiAgPC9nPgo8L3N2Zz4K"
|
||||
width="128"
|
||||
alt=""
|
||||
/>
|
||||
</Link>
|
||||
</Col>
|
||||
<Col span={4}>
|
||||
<Link
|
||||
href="https://ant-design.gitee.io/components/overview-cn/"
|
||||
target="_blank"
|
||||
>
|
||||
<img
|
||||
src="https://gw.alipayobjects.com/zos/rmsportal/KDpgvguMpGfqaHPjicRK.svg"
|
||||
width="128"
|
||||
alt=""
|
||||
/>
|
||||
</Link>
|
||||
</Col>
|
||||
<Col span={4}>
|
||||
<Link
|
||||
href="https://echarts.apache.org/zh/index.html"
|
||||
target="_blank"
|
||||
>
|
||||
<img
|
||||
src="https://cdn.jsdelivr.net/gh/apache/echarts-website@asf-site/zh/images/favicon.png"
|
||||
width="128"
|
||||
alt=""
|
||||
/>
|
||||
</Link>
|
||||
</Col>
|
||||
<Col span={4}>
|
||||
<Link href="http://lesscss.cn/" target="_blank">
|
||||
<img
|
||||
src="http://s.nodejs.cn/less/img/logo.png"
|
||||
width="128"
|
||||
alt=""
|
||||
/>
|
||||
</Link>
|
||||
</Col>
|
||||
<Col span={4}>
|
||||
<Link href="https://www.lodashjs.com/" target="_blank">
|
||||
<img
|
||||
src="https://www.lodashjs.com/img/lodash.png"
|
||||
width="128"
|
||||
alt=""
|
||||
/>
|
||||
</Link>
|
||||
</Col>
|
||||
<Col span={4}>
|
||||
<Link href="http://www.axios-js.com/zh-cn/" target="_blank">
|
||||
<img
|
||||
src="http://www.axios-js.com/logo.svg"
|
||||
width="128"
|
||||
alt=""
|
||||
/>
|
||||
</Link>
|
||||
</Col>
|
||||
</Row>
|
||||
<br />
|
||||
<Row gutter={16}>
|
||||
<Col flex="1">
|
||||
<Container mode="fluid">
|
||||
{docs.map((item, i) => (
|
||||
<React.Fragment key={i}>
|
||||
<section id={`doc-front-end-${i}`}>
|
||||
<Title level={2}>{item.title}</Title>
|
||||
{item.component && (
|
||||
<item.component codes={this.props.codes} />
|
||||
)}
|
||||
{item.children && (
|
||||
<>
|
||||
<br />
|
||||
<br />
|
||||
{item.children.map((citem, ci) => (
|
||||
<React.Fragment key={ci}>
|
||||
<section
|
||||
id={`doc-front-end-${i}-${ci}`}
|
||||
>
|
||||
<Title level={3}>
|
||||
{citem.title}
|
||||
</Title>
|
||||
{citem.component && (
|
||||
<citem.component
|
||||
codes={this.props.codes}
|
||||
/>
|
||||
)}
|
||||
</section>
|
||||
<br />
|
||||
</React.Fragment>
|
||||
))}
|
||||
</>
|
||||
)}
|
||||
</section>
|
||||
<br />
|
||||
</React.Fragment>
|
||||
))}
|
||||
</Container>
|
||||
</Col>
|
||||
<Col flex="240px">
|
||||
<Anchor
|
||||
getContainer={() => this.container}
|
||||
offsetTop={24}
|
||||
onClick={e => e.preventDefault()}
|
||||
>
|
||||
{docs.map((item, i) => (
|
||||
<Anchor.Link
|
||||
key={i}
|
||||
href={`#doc-front-end-${i}`}
|
||||
title={item.title}
|
||||
>
|
||||
{item.children &&
|
||||
item.children.map((citem, ci) => (
|
||||
<Anchor.Link
|
||||
key={ci}
|
||||
href={`#doc-front-end-${i}-${ci}`}
|
||||
title={citem.title}
|
||||
/>
|
||||
))}
|
||||
</Anchor.Link>
|
||||
))}
|
||||
</Anchor>
|
||||
</Col>
|
||||
</Row>
|
||||
{this.props.supportInfo}
|
||||
</Container>
|
||||
</Card>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
import React, { Component } from 'react'
|
||||
import { Space, Tag, Typography } from 'antd'
|
||||
|
||||
const { Title, Text } = Typography
|
||||
|
||||
export default class index extends Component {
|
||||
render() {
|
||||
return (
|
||||
<Space direction="vertical" className="w-100-p">
|
||||
<Text>
|
||||
种子模版已经提供了业务组件通用的架构,可以在{' '}
|
||||
<Tag className="ml-xs" color="orange">
|
||||
/seed
|
||||
</Tag>
|
||||
中获取架构代码。
|
||||
</Text>
|
||||
<Title level={4}>模版解释</Title>
|
||||
<ul>
|
||||
<li className="mb-md">
|
||||
<Tag color="orange">/seed/query-table/index.jsx</Tag>
|
||||
通用查询表格模版。
|
||||
</li>
|
||||
<li className="mb-md">
|
||||
<Tag color="orange">/seed/query-table-form/form.jsx</Tag>
|
||||
简单的编辑弹出框内表单,配合外部使用
|
||||
<Tag className="ml-xs" color="red">
|
||||
modal-form
|
||||
</Tag>
|
||||
。
|
||||
</li>
|
||||
<li className="mb-md">
|
||||
<Tag color="orange">/seed/form</Tag>
|
||||
<ul className="mt-md">
|
||||
<li className="mb-md">
|
||||
<Tag color="orange">/index.jsx</Tag>
|
||||
大型表单主页。可在此调用数据详情接口并分配给各个分片表单,统合分片表单。
|
||||
</li>
|
||||
<li className="mb-md">
|
||||
<Tag color="orange">/part.jsx</Tag>
|
||||
大型表单分片。主要考虑到一个文件中维护的表单字段过多,所以在此拆分。
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li className="mb-md">
|
||||
<Tag color="orange">/seed/form-tabe</Tag>
|
||||
<ul className="mt-md">
|
||||
<li className="mb-md">
|
||||
<Tag color="orange">/index.jsx</Tag>
|
||||
大型标签页-表单主页。可在此调用数据详情接口并分配给各个标签页及以下分片表单。
|
||||
</li>
|
||||
<li className="mb-md">
|
||||
<Tag color="orange">/tab</Tag>
|
||||
<ul className="mt-md">
|
||||
<li className="mb-md">
|
||||
<Tag color="orange">/index.jsx</Tag>
|
||||
单个标签页内主页,在此统合分片表单并传递数据。
|
||||
</li>
|
||||
<li className="mb-md">
|
||||
<Tag color="orange">/part.jsx</Tag>
|
||||
大型表单分片。同上。
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</Space>
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
import React, { Component } from 'react'
|
||||
import { Space, Tag, Typography } from 'antd'
|
||||
import Highlighter from 'pages/system/doc/highlighter'
|
||||
|
||||
const { Text } = Typography
|
||||
|
||||
export default class dict extends Component {
|
||||
render() {
|
||||
return (
|
||||
<Space direction="vertical" className="w-100-p">
|
||||
<Text>
|
||||
<Tag color="orange">/dic</Tag>
|
||||
<Tag color="red">getDictData</Tag>
|
||||
</Text>
|
||||
<Text>
|
||||
通过传入字典编码快速查找并返回字典数据。默认从Redux中读取,如果Redux中不存在,则会调用接口从数据库获取。需要注意的是,获取字典所需的是下划线形式的编码,而返回的字典JSON中键名称必定为驼峰形式。
|
||||
</Text>
|
||||
<Highlighter code={this.props.codes['util/dic/index.js']} language="javascript" />
|
||||
</Space>
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
import React, { Component } from 'react'
|
||||
import { Space, Tag, Typography } from 'antd'
|
||||
|
||||
const { Text } = Typography
|
||||
|
||||
export default class file extends Component {
|
||||
render() {
|
||||
return (
|
||||
<Space direction="vertical" className="w-100-p">
|
||||
<Text>
|
||||
<Tag color="orange">/file</Tag>
|
||||
</Text>
|
||||
<ul>
|
||||
<li>
|
||||
<p>
|
||||
<Tag color="red">ArrayBufferToBase64</Tag>
|
||||
<Tag color="green">(arrayBuffer: ArrayBuffer) => String</Tag>
|
||||
ArrayBuffer转成Base64。
|
||||
</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>
|
||||
<Tag color="red">ArrayBufferToBlob</Tag>
|
||||
<Tag color="green">(arrayBuffer: ArrayBuffer) => Blob</Tag>
|
||||
ArrayBuffer转成Blob。
|
||||
</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>
|
||||
<Tag color="red">Base64ToBlob</Tag>
|
||||
<Tag color="green">(base64: String) => Blob</Tag>
|
||||
Base64转成Blob。
|
||||
</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>
|
||||
<Tag color="red">BlobToBase64</Tag>
|
||||
<Tag color="green">(blob: Blob) => String</Tag>
|
||||
Blob转成Base64。
|
||||
</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>
|
||||
<Tag color="red">BlobToFile</Tag>
|
||||
<Tag color="green">
|
||||
(blob: Blob, fileName: String, fileType: String) => File
|
||||
</Tag>
|
||||
Blob转成File对象。
|
||||
</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>
|
||||
<Tag color="red">Base64ToFile</Tag>
|
||||
<Tag color="green">(base64: String, fileName: String) => File</Tag>
|
||||
Base64转成File对象。
|
||||
</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>
|
||||
<Tag color="red">PreviewFileResponse</Tag>
|
||||
<Tag color="green">(id: String) => Response</Tag>
|
||||
根据文件ID从接口获取文件信息。
|
||||
</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>
|
||||
<Tag color="red">PreviewFileArrayBuffer</Tag>
|
||||
<Tag color="green">(id: String) => ArrayBuffer</Tag>
|
||||
根据文件ID从接口获取文件ArrayBuffer。
|
||||
</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>
|
||||
<Tag color="red">PreviewFileBase64</Tag>
|
||||
<Tag color="green">(id: String) => String</Tag>
|
||||
根据文件ID从接口获取文件Base64。
|
||||
</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>
|
||||
<Tag color="red">PreviewFile</Tag>
|
||||
<Tag color="green">(id: String) => File</Tag>
|
||||
根据文件ID从接口获取文件对象。
|
||||
</p>
|
||||
</li>
|
||||
</ul>
|
||||
</Space>
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
import React, { Component } from 'react'
|
||||
import { Space, Tag, Typography } from 'antd'
|
||||
|
||||
const { Text } = Typography
|
||||
|
||||
export default class format extends Component {
|
||||
render() {
|
||||
return (
|
||||
<Space direction="vertical" className="w-100-p">
|
||||
<Text>
|
||||
<Tag color="orange">/format</Tag>
|
||||
</Text>
|
||||
<ul>
|
||||
<li>
|
||||
<p>
|
||||
<Tag color="red">numberToChinese</Tag>
|
||||
<Tag color="green">(number: [Number, String]) => String</Tag>
|
||||
将数字转换为中文数字。
|
||||
</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>
|
||||
<Tag color="red">toCamelCase</Tag>
|
||||
<Tag color="green">(str: String) => String</Tag>
|
||||
下划线转驼峰。
|
||||
</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>
|
||||
<Tag color="red">toUnderScoreCase</Tag>
|
||||
<Tag color="green">(str: String) => String</Tag>
|
||||
驼峰转下划线。
|
||||
</p>
|
||||
</li>
|
||||
</ul>
|
||||
</Space>
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
import React, { Component } from 'react'
|
||||
import { Space, Tag, Typography } from 'antd'
|
||||
|
||||
const { Text } = Typography
|
||||
|
||||
export default class global extends Component {
|
||||
render() {
|
||||
return (
|
||||
<Space direction="vertical" className="w-100-p">
|
||||
<Text>
|
||||
<Tag color="orange">/global</Tag>
|
||||
</Text>
|
||||
<ul>
|
||||
<li>
|
||||
<p>
|
||||
<Tag color="red">EMPTY_ID</Tag>
|
||||
一个空GUID字符串,一般用于判断树节点的顶层。
|
||||
</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>
|
||||
<Tag color="red">PERVIEW_URL</Tag>
|
||||
文件预览地址,只在文件预览接口开放匿名后可以直接通过src引用。
|
||||
</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>
|
||||
<Tag color="red">RSA_PUBLIC_KEY</Tag>
|
||||
前后端非对称加密的公钥。
|
||||
</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>
|
||||
<Tag color="red">AMAP_WEBAPI_KEY</Tag>
|
||||
高德地图Webapi所使用的key。
|
||||
</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>
|
||||
<Tag color="red">CITY</Tag>
|
||||
城市名称,一般用于地图定位城市。
|
||||
</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>
|
||||
<Tag color="red">SIDER_BREAK_POINT</Tag>
|
||||
响应式小屏幕响应宽度。
|
||||
</p>
|
||||
</li>
|
||||
</ul>
|
||||
</Space>
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
import React, { Component } from 'react'
|
||||
import { Space, Tag, Typography } from 'antd'
|
||||
import Highlighter from 'pages/system/doc/highlighter'
|
||||
|
||||
const { Title, Text } = Typography
|
||||
|
||||
export default class index extends Component {
|
||||
render() {
|
||||
return (
|
||||
<Space direction="vertical" className="w-100-p">
|
||||
<Text>
|
||||
在
|
||||
<Tag className="ml-xs" color="orange">
|
||||
/util
|
||||
</Tag>
|
||||
中,默认已提供了许多工具函数。也可以自行进行添加。
|
||||
</Text>
|
||||
</Space>
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
import React, { Component } from 'react'
|
||||
import { Space, Tag, Typography } from 'antd'
|
||||
import Highlighter from 'pages/system/doc/highlighter'
|
||||
|
||||
const { Text } = Typography
|
||||
|
||||
export default class query extends Component {
|
||||
render() {
|
||||
return (
|
||||
<Space direction="vertical" className="w-100-p">
|
||||
<Text>
|
||||
<Tag color="orange">/query</Tag>
|
||||
</Text>
|
||||
<ul>
|
||||
<li class="mb-md">
|
||||
<Space direction="vertical" className="w-100-p">
|
||||
<Text>
|
||||
<Tag color="red">QueryType</Tag>
|
||||
获取查询类型字符串,与后端对应。
|
||||
</Text>
|
||||
<ul>
|
||||
<li>
|
||||
<p>
|
||||
<Tag color="red">GreaterThan</Tag>:{' '}
|
||||
<Tag color="blue">></Tag>
|
||||
</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>
|
||||
<Tag color="red">GreaterThanOrEqual</Tag>:{' '}
|
||||
<Tag color="blue">>=</Tag>
|
||||
</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>
|
||||
<Tag color="red">LessThan</Tag>:{' '}
|
||||
<Tag color="blue"><</Tag>
|
||||
</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>
|
||||
<Tag color="red">LessThanOrEqual</Tag>:{' '}
|
||||
<Tag color="blue"><=</Tag>
|
||||
</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>
|
||||
<Tag color="red">Like</Tag>: <Tag color="blue">LIKE</Tag>
|
||||
</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>
|
||||
<Tag color="red">Equal</Tag>: <Tag color="blue">=</Tag>
|
||||
</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>
|
||||
<Tag color="red">StartWith</Tag>:{' '}
|
||||
<Tag color="blue">STRAT</Tag>
|
||||
</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>
|
||||
<Tag color="red">EndWith</Tag>: <Tag color="blue">END</Tag>
|
||||
</p>
|
||||
</li>
|
||||
</ul>
|
||||
</Space>
|
||||
</li>
|
||||
<li class="mb-md">
|
||||
<Space direction="vertical" className="w-100-p">
|
||||
<Text>
|
||||
<Tag color="red">getSearchDateRange</Tag>
|
||||
<Tag color="green">
|
||||
(range: Array, format: String = 'YYYY-MM-DD', unit: String =
|
||||
'days') => Array
|
||||
</Tag>
|
||||
</Text>
|
||||
<Text>获取查询用时间范围数组,在这里会自动将第二个时间增加1天。</Text>
|
||||
<Text>
|
||||
如果选择的日期范围为2021-01-01~2021-01-10,最终需要取得
|
||||
>=2021-01-01 and <2021-01-11 的结果
|
||||
</Text>
|
||||
</Space>
|
||||
</li>
|
||||
<li class="mb-md">
|
||||
<Space direction="vertical" className="w-100-p">
|
||||
<Text>
|
||||
<Tag color="red">getSearchInfo</Tag>
|
||||
<Tag color="green">
|
||||
({'{'}query: Object, queryType: Object{'}'}) => Array
|
||||
</Tag>
|
||||
</Text>
|
||||
<Text>从键值对的query类型转换成数组类型。</Text>
|
||||
<Text>键:自动作为field值</Text>
|
||||
<Text>值:得到一个数组作为value的值</Text>
|
||||
<Text>
|
||||
queryType:一个JSON类型,已query的键为键,
|
||||
<Tag className="ml-xs" color="red">
|
||||
QueryType
|
||||
</Tag>
|
||||
为值。 如果是一个
|
||||
<Tag className="ml-xs" color="red">
|
||||
QueryType
|
||||
</Tag>
|
||||
的数组,则自动对应到value中的各个值。
|
||||
</Text>
|
||||
<Text>示例:</Text>
|
||||
<Highlighter
|
||||
code={this.props.codes['util/query/index.js']}
|
||||
language="javascript"
|
||||
/>
|
||||
</Space>
|
||||
</li>
|
||||
</ul>
|
||||
</Space>
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
import React, { Component } from 'react'
|
||||
import { Space, Tag, Typography } from 'antd'
|
||||
import Highlighter from 'pages/system/doc/highlighter'
|
||||
|
||||
const { Title, Text } = Typography
|
||||
|
||||
export default class close extends Component {
|
||||
render() {
|
||||
return (
|
||||
<Space direction="vertical" className="w-100-p">
|
||||
<Text>可调用全局方法关闭窗口</Text>
|
||||
<Highlighter code={'window.closeContentWindow(key)'} language="javascript" />
|
||||
<Title level={4}>键</Title>
|
||||
<Text>
|
||||
<Tag>String | Number</Tag>
|
||||
非必传参数。指定关闭窗口的键。如果未指定键,则关闭当前选中的窗口。
|
||||
</Text>
|
||||
</Space>
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
import React, { Component } from 'react'
|
||||
import { Space, Tag, Typography } from 'antd'
|
||||
|
||||
const { Text } = Typography
|
||||
|
||||
export default class index extends Component {
|
||||
render() {
|
||||
return (
|
||||
<Space direction="vertical" className="w-100-p">
|
||||
<Text>
|
||||
本框架是以
|
||||
<b>页签</b>
|
||||
形式打开业务组件(一般意义上的页面)。当然其中也有使用到路由,但只用于登录与主页的跳转。
|
||||
</Text>
|
||||
<Text>
|
||||
所有业务组件都放置于
|
||||
<Tag className="ml-xs" color="orange">
|
||||
/src/pages
|
||||
</Tag>
|
||||
目录下,打开窗口时默认只读取该目录下的组件。
|
||||
</Text>
|
||||
<Text>
|
||||
目前已对打开和关闭窗口的方法进行了全局化处理,可以在任何组件内轻松地使用。
|
||||
</Text>
|
||||
</Space>
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
import React, { Component } from 'react'
|
||||
import { Space, Tag, Typography } from 'antd'
|
||||
import Highlighter from 'pages/system/doc/highlighter'
|
||||
|
||||
const { Title, Text } = Typography
|
||||
|
||||
export default class open extends Component {
|
||||
render() {
|
||||
return (
|
||||
<Space direction="vertical" className="w-100-p">
|
||||
<Text>可调用全局方法打开窗口</Text>
|
||||
<Highlighter code={'window.openContentWindow(settings)'} language="javascript" />
|
||||
<Title level={4}>配置</Title>
|
||||
<ul>
|
||||
<li>
|
||||
<p>
|
||||
<Tag color="red">key</Tag>
|
||||
<Tag>String | Number</Tag>
|
||||
非必要,窗口的唯一键。当下一次打开同键的窗口时,只切换到该窗口。如果未指定唯一键,将会自动生成随机键。
|
||||
</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>
|
||||
<Tag color="red">icon</Tag>
|
||||
<Tag>String</Tag>非必要,窗口页签的图标。
|
||||
</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>
|
||||
<Tag color="red">title</Tag>
|
||||
<Tag>String</Tag>
|
||||
非必要,窗口页签的标题。如果不指定标题,讲会以“新建窗口”显示。
|
||||
</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>
|
||||
<Tag color="red">subTitle</Tag>
|
||||
<Tag>String</Tag>
|
||||
非必要,窗口页签的副标题。
|
||||
</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>
|
||||
<Tag color="red">path</Tag>
|
||||
<Tag>String</Tag>必要,组件路径。
|
||||
</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>
|
||||
<Tag color="red">param</Tag>
|
||||
<Tag>Object</Tag>非必要,传递参数。在业务组件中通过
|
||||
<Tag className="ml-xs" color="orange">
|
||||
props
|
||||
</Tag>
|
||||
接收。
|
||||
</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>
|
||||
<Tag color="red">openType</Tag>
|
||||
<Tag>Number</Tag>
|
||||
打开类型:1、组件,2、iframe,3、新浏览器窗口。
|
||||
</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>
|
||||
<Tag color="red">closable</Tag>
|
||||
<Tag>Boolean</Tag>非必要,设置是否可关闭窗口。默认为可关闭。
|
||||
</p>
|
||||
</li>
|
||||
</ul>
|
||||
<Text>通过菜单名打开窗口</Text>
|
||||
<Highlighter
|
||||
code={'window.openContentWindowByMenuName(name)'}
|
||||
language="javascript"
|
||||
/>
|
||||
</Space>
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
import React, { Component } from 'react'
|
||||
import { Space, Tag, Typography } from 'antd'
|
||||
import Highlighter from 'pages/system/doc/highlighter'
|
||||
|
||||
const { Title, Text } = Typography
|
||||
|
||||
export default class reload extends Component {
|
||||
render() {
|
||||
return (
|
||||
<Space direction="vertical" className="w-100-p">
|
||||
<Text>可调用全局方法刷新窗口</Text>
|
||||
<Highlighter code={'window.reloadContentWindow(key)'} language="javascript" />
|
||||
<Title level={4}>键</Title>
|
||||
<Text>
|
||||
<Tag>String | Number</Tag>
|
||||
非必传参数。指定刷新窗口的键。如果未指定键,则刷新当前选中的窗口。
|
||||
</Text>
|
||||
</Space>
|
||||
)
|
||||
}
|
||||
}
|
||||
88
framework/web-react/src/pages/system/doc/highlighter.jsx
Normal file
88
framework/web-react/src/pages/system/doc/highlighter.jsx
Normal file
@@ -0,0 +1,88 @@
|
||||
import React, { Component } from 'react'
|
||||
import { Button, message as Message, Space } from 'antd'
|
||||
import MonacoEditor from 'react-monaco-editor'
|
||||
|
||||
const baseCopy = content => {
|
||||
try {
|
||||
const $textarea = document.createElement('textarea')
|
||||
$textarea.style = 'opacity: 0;position: fixed;top: -10000;left: -10000'
|
||||
document.body.append($textarea)
|
||||
$textarea.value = content
|
||||
$textarea.select()
|
||||
document.execCommand('copy')
|
||||
$textarea.remove()
|
||||
Message.success('已复制到剪贴板')
|
||||
} catch {
|
||||
Message.error('复制失败')
|
||||
}
|
||||
}
|
||||
|
||||
const copy = code => {
|
||||
baseCopy(code)
|
||||
}
|
||||
|
||||
const copyTemplate = code => {
|
||||
code =
|
||||
'"' +
|
||||
code
|
||||
// 转义双引号 => \"
|
||||
.replace(/"/g, '\\"')
|
||||
// 转义$ => $$
|
||||
.replace(/\$/g, '$$$$')
|
||||
// 替换行首 => "
|
||||
.replace(/\n/g, '"')
|
||||
// 替换行末 = ",
|
||||
.replace(/\r/g, '",\r') +
|
||||
'"'
|
||||
let flag = true
|
||||
while (flag) {
|
||||
const p = code.match(/\$\${.*?}/)
|
||||
if (p && p[0]) {
|
||||
code = code.replace(p[0], `$\{${p[0].slice(1)}}`)
|
||||
} else {
|
||||
flag = false
|
||||
}
|
||||
}
|
||||
baseCopy(code)
|
||||
}
|
||||
|
||||
export default class highlighter extends Component {
|
||||
onEditorDidMount(editor) {
|
||||
editor.setValue(this.props.code)
|
||||
}
|
||||
|
||||
render() {
|
||||
const { code, copyTemplate } = this.props
|
||||
|
||||
const line = code.split('\r\n').length
|
||||
const height = line > 20 ? 400 : line * 20
|
||||
|
||||
return (
|
||||
<section>
|
||||
<MonacoEditor
|
||||
height={height}
|
||||
{...this.props}
|
||||
theme="vs-dark"
|
||||
options={{
|
||||
fontSize: 12,
|
||||
readOnly: true,
|
||||
lineHeight: 20,
|
||||
}}
|
||||
editorDidMount={editor => this.onEditorDidMount(editor)}
|
||||
/>
|
||||
<div className="text-right mt-sm">
|
||||
<Space>
|
||||
{copyTemplate && (
|
||||
<Button size="small" type="dashed" onClick={() => copyTemplate(code)}>
|
||||
Copy template
|
||||
</Button>
|
||||
)}
|
||||
<Button size="small" type="dashed" onClick={() => copy(code)}>
|
||||
Copy
|
||||
</Button>
|
||||
</Space>
|
||||
</div>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
}
|
||||
78
framework/web-react/src/pages/system/doc/index.jsx
Normal file
78
framework/web-react/src/pages/system/doc/index.jsx
Normal file
@@ -0,0 +1,78 @@
|
||||
import React, { Component } from 'react'
|
||||
import { Tabs } from 'antd'
|
||||
import BackEnd from './back-end'
|
||||
import FrontEnd from './front-end'
|
||||
|
||||
const tabs = [
|
||||
{
|
||||
title: '后端',
|
||||
component: BackEnd,
|
||||
},
|
||||
{
|
||||
title: '前端',
|
||||
component: FrontEnd,
|
||||
},
|
||||
]
|
||||
|
||||
export default class index extends Component {
|
||||
state = {
|
||||
activeKey: '0',
|
||||
}
|
||||
|
||||
codes = {}
|
||||
|
||||
constructor(props) {
|
||||
super(props)
|
||||
|
||||
// 读取doc-code下所有文件内容
|
||||
const files = require.context(
|
||||
'../../../../public/doc-code',
|
||||
true,
|
||||
/\.(txt|js|jsx|html|vue|css|less|json|cs)(\?.*)?$/
|
||||
)
|
||||
const codes = {}
|
||||
files.keys().forEach(p => {
|
||||
const filepath = p.slice(2)
|
||||
const xhr = new XMLHttpRequest()
|
||||
xhr.open('GET', `./doc-code/${filepath}`, false)
|
||||
xhr.overrideMimeType('text/plain;charset=utf-8')
|
||||
xhr.send(null)
|
||||
codes[filepath] = xhr.responseText
|
||||
})
|
||||
|
||||
this.codes = codes
|
||||
}
|
||||
|
||||
render() {
|
||||
const { activeKey } = this.state
|
||||
|
||||
return (
|
||||
<div className="yo-form-page">
|
||||
<div className="yo-form-page-layout">
|
||||
<div className="yo-tab-external-mount">
|
||||
<Tabs onChange={activeKey => this.setState({ activeKey })} centered>
|
||||
{tabs.map((item, i) => (
|
||||
<Tabs.TabPane key={i} forceRender={true} tab={item.title} />
|
||||
))}
|
||||
</Tabs>
|
||||
<div className="yo-tab-external-mount-content">
|
||||
{tabs.map((item, i) => (
|
||||
<div
|
||||
key={i}
|
||||
className={[
|
||||
'yo-tab-external-tabpane',
|
||||
activeKey == i
|
||||
? 'yo-tab-external-tabpane-active'
|
||||
: 'yo-tab-external-tabpane-inactive',
|
||||
].join(' ')}
|
||||
>
|
||||
<item.component codes={this.codes} {...this.props} />
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
398
framework/web-react/src/pages/system/file/index.jsx
Normal file
398
framework/web-react/src/pages/system/file/index.jsx
Normal file
@@ -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 => <Tooltip title={text}>{text}</Tooltip>,
|
||||
},
|
||||
{
|
||||
title: '文件后缀',
|
||||
dataIndex: 'fileSuffix',
|
||||
width: 120,
|
||||
sorter: true,
|
||||
render: text => <Tag color="green">{text}</Tag>,
|
||||
},
|
||||
{
|
||||
title: '文件大小',
|
||||
dataIndex: 'fileSizeKb',
|
||||
width: 120,
|
||||
sorter: true,
|
||||
render: text => (
|
||||
<>
|
||||
{text}
|
||||
<small>KB</small>
|
||||
</>
|
||||
),
|
||||
},
|
||||
{
|
||||
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 => <Tooltip title={text}>{text}</Tooltip>,
|
||||
},
|
||||
{
|
||||
title: '唯一标识id',
|
||||
dataIndex: 'fileObjectName',
|
||||
width: 250,
|
||||
ellipsis: {
|
||||
showTitle: false,
|
||||
},
|
||||
sorter: true,
|
||||
render: text => <Tooltip title={text}>{text}</Tooltip>,
|
||||
},
|
||||
{
|
||||
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) => (
|
||||
<QueryTableActions>
|
||||
<a onClick={() => this.onFileDownload(record)}>下载</a>
|
||||
<Auth auth={{ [authName]: 'delete' }}>
|
||||
<Popconfirm
|
||||
placement="topRight"
|
||||
title="是否确认删除"
|
||||
onConfirm={() => this.onDelete(record)}
|
||||
>
|
||||
<a>删除</a>
|
||||
</Popconfirm>
|
||||
</Auth>
|
||||
{['png', 'jpeg', 'jpg', 'gif', 'tif', 'bmp'].includes(
|
||||
record.fileSuffix
|
||||
) && <a onClick={() => this.onFilePreview(record)}>预览</a>}
|
||||
</QueryTableActions>
|
||||
),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 阻止外部组件引发的渲染,提升性能
|
||||
* 可自行添加渲染条件
|
||||
* [必要]
|
||||
* @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 (
|
||||
<Container mode="fluid">
|
||||
<br />
|
||||
<Card bordered={false}>
|
||||
<QueryTable
|
||||
ref={this.table}
|
||||
autoLoad={false}
|
||||
loadData={this.loadData}
|
||||
columns={this.columns}
|
||||
query={
|
||||
<Auth auth={{ [authName]: 'page' }}>
|
||||
<Form.Item label="文件名" name="fileOriginName">
|
||||
<Input
|
||||
autoComplete="off"
|
||||
placeholder="请输入文件名"
|
||||
className="w-200"
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form.Item label="存储位置" name="fileLocation">
|
||||
<Select placeholder="请选择存储位置" className="w-200">
|
||||
{codes.fileStorageLocation.map(item => (
|
||||
<Select.Option key={item.code} value={item.code}>
|
||||
{item.value}
|
||||
</Select.Option>
|
||||
))}
|
||||
</Select>
|
||||
</Form.Item>
|
||||
<Form.Item label="文件仓库" name="fileBucket">
|
||||
<Input
|
||||
autoComplete="off"
|
||||
placeholder="请输入文件仓库"
|
||||
className="w-200"
|
||||
/>
|
||||
</Form.Item>
|
||||
</Auth>
|
||||
}
|
||||
operator={
|
||||
<Auth auth={{ [authName]: 'add' }}>
|
||||
<Upload customRequest={e => this.onFileUpload(e)} fileList={[]}>
|
||||
<Button loading={uploading} icon={<AntIcon type="upload" />}>
|
||||
上传文件
|
||||
</Button>
|
||||
</Upload>
|
||||
</Auth>
|
||||
}
|
||||
/>
|
||||
</Card>
|
||||
|
||||
<PhotoPreview ref={this.photoPreview} />
|
||||
</Container>
|
||||
)
|
||||
}
|
||||
}
|
||||
274
framework/web-react/src/pages/system/log/oplog/index.jsx
Normal file
274
framework/web-react/src/pages/system/log/oplog/index.jsx
Normal file
@@ -0,0 +1,274 @@
|
||||
import React, { Component } from 'react'
|
||||
import {
|
||||
Alert,
|
||||
Button,
|
||||
Card,
|
||||
Descriptions,
|
||||
Form,
|
||||
Popconfirm,
|
||||
Input,
|
||||
message as Message,
|
||||
Select,
|
||||
DatePicker,
|
||||
Tag,
|
||||
} from 'antd'
|
||||
import { Auth, Container, QueryTable } from 'components'
|
||||
import { api } from 'common/api'
|
||||
import { toCamelCase } from 'util/format'
|
||||
import { isEqual } from 'lodash'
|
||||
import getDictData from 'util/dic'
|
||||
import moment from 'moment'
|
||||
import ReactJson from 'react-json-view'
|
||||
|
||||
const { RangePicker } = DatePicker
|
||||
|
||||
const apiAction = {
|
||||
page: api.sysOpLogPage,
|
||||
delete: api.sysOpLogDelete,
|
||||
}
|
||||
|
||||
const methodColor = {
|
||||
POST: 'orange',
|
||||
GET: 'green',
|
||||
}
|
||||
|
||||
export default class index extends Component {
|
||||
state = {
|
||||
codes: {
|
||||
opType: [],
|
||||
},
|
||||
}
|
||||
// 表格实例
|
||||
table = React.createRef()
|
||||
// 表格字段
|
||||
columns = [
|
||||
{
|
||||
title: '日志名称',
|
||||
dataIndex: 'name',
|
||||
width: 200,
|
||||
sorter: true,
|
||||
},
|
||||
{
|
||||
title: '请求地址',
|
||||
dataIndex: 'url',
|
||||
width: 300,
|
||||
sorter: true,
|
||||
render: (text, record) => (
|
||||
<>
|
||||
<Tag color={methodColor[record.reqMethod.toUpperCase()]}>
|
||||
{record.reqMethod}
|
||||
</Tag>{' '}
|
||||
{text}
|
||||
</>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: '是否成功',
|
||||
dataIndex: 'success',
|
||||
width: 100,
|
||||
render: text => (
|
||||
<>
|
||||
{text ? (
|
||||
<span className="text-success">是</span>
|
||||
) : (
|
||||
<span className="text-error">否</span>
|
||||
)}
|
||||
</>
|
||||
),
|
||||
sorter: true,
|
||||
},
|
||||
{
|
||||
title: 'ip',
|
||||
dataIndex: 'ip',
|
||||
width: 120,
|
||||
sorter: true,
|
||||
},
|
||||
{
|
||||
title: '操作时间',
|
||||
dataIndex: 'opTime',
|
||||
width: 140,
|
||||
sorter: true,
|
||||
defaultSortOrder: 'descend',
|
||||
},
|
||||
{
|
||||
title: '操作人',
|
||||
width: 140,
|
||||
dataIndex: 'account',
|
||||
sorter: true,
|
||||
},
|
||||
]
|
||||
|
||||
/**
|
||||
* 阻止外部组件引发的渲染,提升性能
|
||||
* 可自行添加渲染条件
|
||||
* [必要]
|
||||
* @param {*} props
|
||||
* @param {*} state
|
||||
* @returns
|
||||
*/
|
||||
shouldComponentUpdate(props, state) {
|
||||
return !isEqual(this.state, state)
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载字典数据,之后开始加载表格数据
|
||||
* 如果必须要加载字典数据,可直接对表格设置autoLoad=true
|
||||
*/
|
||||
componentDidMount() {}
|
||||
|
||||
/**
|
||||
* 调用加载数据接口,可在调用前对query进行处理
|
||||
* [异步,必要]
|
||||
* @param {*} params
|
||||
* @param {*} query
|
||||
* @returns
|
||||
*/
|
||||
loadData = async (params, query) => {
|
||||
if (query.dates && query.dates.length) {
|
||||
query.searchBeginTime = moment(query.dates[0]).format('YYYY-MM-DD HH:mm:ss')
|
||||
query.searchEndTime = moment(query.dates[1]).format('YYYY-MM-DD HH:mm:ss')
|
||||
delete query.dates
|
||||
}
|
||||
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 {*} action
|
||||
* @param {*} successMessage
|
||||
*/
|
||||
async onAction(action, successMessage) {
|
||||
this.table.current.onLoading()
|
||||
try {
|
||||
await action
|
||||
Message.success(successMessage)
|
||||
this.table.current.onReloadData()
|
||||
} catch {
|
||||
this.table.current.onLoaded()
|
||||
}
|
||||
}
|
||||
|
||||
onOpLogClear() {
|
||||
this.onAction(apiAction.delete(), '清空成功')
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Container mode="fluid">
|
||||
<br />
|
||||
<Alert
|
||||
closable
|
||||
type="error"
|
||||
message={
|
||||
<>
|
||||
<div>后端bug:任何操作的操作类型都是增加</div>
|
||||
<div>没有记录请求参数.返回结果等信息</div>
|
||||
</>
|
||||
}
|
||||
/>
|
||||
<br />
|
||||
<Card bordered={false}>
|
||||
<QueryTable
|
||||
ref={this.table}
|
||||
loadData={this.loadData}
|
||||
columns={this.columns}
|
||||
query={
|
||||
<Auth auth="sysOpLog:page">
|
||||
<Form.Item label="日志名称" name="name">
|
||||
<Input autoComplete="off" placeholder="请输入日志名称" />
|
||||
</Form.Item>
|
||||
<Form.Item label="是否成功" name="success">
|
||||
<Select placeholder="请选择是否成功" style={{ width: '170px' }}>
|
||||
<Select.Option key="true">是</Select.Option>
|
||||
<Select.Option key="false">否</Select.Option>
|
||||
</Select>
|
||||
</Form.Item>
|
||||
<Form.Item label="操作时间" name="dates">
|
||||
<RangePicker
|
||||
showTime={{
|
||||
hideDisabledOptions: true,
|
||||
defaultValue: [
|
||||
moment('00:00:00', 'HH:mm:ss'),
|
||||
moment('23:59:59', 'HH:mm:ss'),
|
||||
],
|
||||
}}
|
||||
format="YYYY-MM-DD HH:mm:ss"
|
||||
></RangePicker>
|
||||
</Form.Item>
|
||||
</Auth>
|
||||
}
|
||||
operator={
|
||||
<Auth auth="sysOpLog:delete">
|
||||
<Popconfirm
|
||||
placement="bottomLeft"
|
||||
title="是否确认清空日志"
|
||||
onConfirm={() => this.onOpLogClear()}
|
||||
>
|
||||
<Button>清空日志</Button>
|
||||
</Popconfirm>
|
||||
</Auth>
|
||||
}
|
||||
expandable={{
|
||||
expandedRowRender: record => (
|
||||
<Descriptions bordered size="small" labelStyle={{ width: '150px' }}>
|
||||
<Descriptions.Item span="1" label="方法名称">
|
||||
{record.methodName}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item span="2" label="地址">
|
||||
{record.location}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="浏览器">
|
||||
{record.browser}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item span="2" label="操作系统">
|
||||
{record.os}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item span="3" label="类名称">
|
||||
{record.className}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item span="3" label="返回结果">
|
||||
<ReactJson
|
||||
src={JSON.parse(record.result || '{}')}
|
||||
collapsed
|
||||
/>
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item span="3" label="请求参数">
|
||||
<ReactJson
|
||||
src={JSON.parse(record.param || '{}')}
|
||||
collapsed
|
||||
/>
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item span="3" label="具体消息">
|
||||
{record.message}
|
||||
</Descriptions.Item>
|
||||
</Descriptions>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
</Card>
|
||||
</Container>
|
||||
)
|
||||
}
|
||||
}
|
||||
266
framework/web-react/src/pages/system/log/vislog/index.jsx
Normal file
266
framework/web-react/src/pages/system/log/vislog/index.jsx
Normal file
@@ -0,0 +1,266 @@
|
||||
import React, { Component } from 'react'
|
||||
import {
|
||||
Alert,
|
||||
Button,
|
||||
Card,
|
||||
Descriptions,
|
||||
Form,
|
||||
Popconfirm,
|
||||
Input,
|
||||
message as Message,
|
||||
Select,
|
||||
DatePicker,
|
||||
} from 'antd'
|
||||
import { Auth, Container, QueryTable } from 'components'
|
||||
import { api } from 'common/api'
|
||||
import { toCamelCase } from 'util/format'
|
||||
import { isEqual } from 'lodash'
|
||||
import getDictData from 'util/dic'
|
||||
import moment from 'moment'
|
||||
|
||||
const { RangePicker } = DatePicker
|
||||
|
||||
const apiAction = {
|
||||
page: api.sysVisLogPage,
|
||||
delete: api.sysVisLogDelete,
|
||||
}
|
||||
export default class index extends Component {
|
||||
state = {
|
||||
codes: {
|
||||
visType: [],
|
||||
},
|
||||
}
|
||||
// 表格实例
|
||||
table = React.createRef()
|
||||
// 表格字段
|
||||
columns = [
|
||||
{
|
||||
title: '日志名称',
|
||||
dataIndex: 'name',
|
||||
width: 200,
|
||||
sorter: true,
|
||||
},
|
||||
{
|
||||
title: '访问类型',
|
||||
dataIndex: 'visType',
|
||||
width: 120,
|
||||
render: text => <>{this.bindCodeValue(text, 'vis_type')}</>,
|
||||
sorter: true,
|
||||
},
|
||||
{
|
||||
title: '是否成功',
|
||||
dataIndex: 'success',
|
||||
width: 120,
|
||||
render: text => (
|
||||
<>
|
||||
{text ? (
|
||||
<span className="text-success">是</span>
|
||||
) : (
|
||||
<span className="text-error">否</span>
|
||||
)}
|
||||
</>
|
||||
),
|
||||
sorter: true,
|
||||
},
|
||||
{
|
||||
title: 'ip',
|
||||
dataIndex: 'ip',
|
||||
width: 180,
|
||||
sorter: true,
|
||||
},
|
||||
{
|
||||
title: '浏览器',
|
||||
dataIndex: 'browser',
|
||||
width: 180,
|
||||
sorter: true,
|
||||
},
|
||||
{
|
||||
title: '访问时间',
|
||||
dataIndex: 'visTime',
|
||||
width: 180,
|
||||
sorter: true,
|
||||
defaultSortOrder: 'descend',
|
||||
},
|
||||
{
|
||||
title: '访问人',
|
||||
dataIndex: 'account',
|
||||
width: 180,
|
||||
sorter: true,
|
||||
},
|
||||
]
|
||||
|
||||
/**
|
||||
* 阻止外部组件引发的渲染,提升性能
|
||||
* 可自行添加渲染条件
|
||||
* [必要]
|
||||
* @param {*} props
|
||||
* @param {*} state
|
||||
* @returns
|
||||
*/
|
||||
shouldComponentUpdate(props, state) {
|
||||
return !isEqual(this.state, state)
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载字典数据,之后开始加载表格数据
|
||||
* 如果必须要加载字典数据,可直接对表格设置autoLoad=true
|
||||
*/
|
||||
componentDidMount() {
|
||||
this.table.current.onLoading()
|
||||
getDictData('vis_type').then(res => {
|
||||
this.setState(
|
||||
{
|
||||
codes: res,
|
||||
},
|
||||
() => {
|
||||
this.table.current.onLoadData()
|
||||
}
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 调用加载数据接口,可在调用前对query进行处理
|
||||
* [异步,必要]
|
||||
* @param {*} params
|
||||
* @param {*} query
|
||||
* @returns
|
||||
*/
|
||||
loadData = async (params, query) => {
|
||||
if (query.dates && query.dates.length) {
|
||||
query.searchBeginTime = moment(query.dates[0]).format('YYYY-MM-DD HH:mm:ss')
|
||||
query.searchEndTime = moment(query.dates[1]).format('YYYY-MM-DD HH:mm:ss')
|
||||
delete query.dates
|
||||
}
|
||||
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 {*} action
|
||||
* @param {*} successMessage
|
||||
*/
|
||||
async onAction(action, successMessage) {
|
||||
this.table.current.onLoading()
|
||||
try {
|
||||
await action
|
||||
Message.success(successMessage)
|
||||
this.table.current.onReloadData()
|
||||
} catch {
|
||||
this.table.current.onLoaded()
|
||||
}
|
||||
}
|
||||
|
||||
onVisLogClear() {
|
||||
this.onAction(apiAction.delete(), '清空成功')
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Container mode="fluid">
|
||||
<br />
|
||||
<Alert
|
||||
closable
|
||||
type="error"
|
||||
message="页面刷新的时候也会有保存登录日志,但是没有ip显示"
|
||||
/>
|
||||
<br />
|
||||
<Card bordered={false}>
|
||||
<QueryTable
|
||||
ref={this.table}
|
||||
autoLoad={false}
|
||||
loadData={this.loadData}
|
||||
columns={this.columns}
|
||||
query={
|
||||
<Auth auth="sysVisLog:page">
|
||||
<Form.Item label="日志名称" name="name">
|
||||
<Input autoComplete="off" placeholder="请输入日志名称" />
|
||||
</Form.Item>
|
||||
<Form.Item label="访问类型" name="visType">
|
||||
<Select
|
||||
placeholder="请选择访问类型"
|
||||
allow-clear
|
||||
style={{ width: '170px' }}
|
||||
>
|
||||
{this.state.codes.visType.map(item => {
|
||||
return (
|
||||
<Select.Option key={item.code} value={+item.code}>
|
||||
{item.value}
|
||||
</Select.Option>
|
||||
)
|
||||
})}
|
||||
</Select>
|
||||
</Form.Item>
|
||||
<Form.Item label="是否成功" name="success">
|
||||
<Select placeholder="请选择是否成功" style={{ width: '170px' }}>
|
||||
<Select.Option key="true">是</Select.Option>
|
||||
<Select.Option key="false">否</Select.Option>
|
||||
</Select>
|
||||
</Form.Item>
|
||||
<Form.Item label="访问时间" name="dates">
|
||||
<RangePicker
|
||||
showTime={{
|
||||
hideDisabledOptions: true,
|
||||
defaultValue: [
|
||||
moment('00:00:00', 'HH:mm:ss'),
|
||||
moment('23:59:59', 'HH:mm:ss'),
|
||||
],
|
||||
}}
|
||||
format="YYYY-MM-DD HH:mm:ss"
|
||||
></RangePicker>
|
||||
</Form.Item>
|
||||
</Auth>
|
||||
}
|
||||
operator={
|
||||
<Auth auth="sysOpLog:delete">
|
||||
<Popconfirm
|
||||
placement="bottomLeft"
|
||||
title="是否确认清空日志"
|
||||
onConfirm={() => this.onVisLogClear()}
|
||||
>
|
||||
<Button>清空日志</Button>
|
||||
</Popconfirm>
|
||||
</Auth>
|
||||
}
|
||||
expandable={{
|
||||
expandedRowRender: record => (
|
||||
<Descriptions
|
||||
bordered
|
||||
size="small"
|
||||
columns={1}
|
||||
labelStyle={{ width: '150px' }}
|
||||
>
|
||||
<Descriptions.Item label="具体消息">
|
||||
{record.message}
|
||||
</Descriptions.Item>
|
||||
</Descriptions>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
</Card>
|
||||
</Container>
|
||||
)
|
||||
}
|
||||
}
|
||||
33
framework/web-react/src/pages/system/machine/base.jsx
Normal file
33
framework/web-react/src/pages/system/machine/base.jsx
Normal file
@@ -0,0 +1,33 @@
|
||||
import React, { Component } from 'react'
|
||||
import { Card, Col, Descriptions } from 'antd'
|
||||
|
||||
export default class base extends Component {
|
||||
render() {
|
||||
const { base } = this.props
|
||||
|
||||
const { hostName, systemOs, wanIp, lanIp, osArchitecture, frameworkDescription } = base
|
||||
|
||||
return (
|
||||
<>
|
||||
<Col span={24}>
|
||||
<Card bordered={false}>
|
||||
<Descriptions
|
||||
column={2}
|
||||
labelStyle={{ fontSize: '14px', opacity: 0.45 }}
|
||||
contentStyle={{ fontSize: '14px' }}
|
||||
>
|
||||
<Descriptions.Item label="主机名称">{hostName}</Descriptions.Item>
|
||||
<Descriptions.Item label="操作系统">{systemOs}</Descriptions.Item>
|
||||
<Descriptions.Item label="外网信息">{wanIp}</Descriptions.Item>
|
||||
<Descriptions.Item label="内网IP">{lanIp}</Descriptions.Item>
|
||||
<Descriptions.Item label="系统架构">{osArchitecture}</Descriptions.Item>
|
||||
<Descriptions.Item label="运行框架">
|
||||
{frameworkDescription}
|
||||
</Descriptions.Item>
|
||||
</Descriptions>
|
||||
</Card>
|
||||
</Col>
|
||||
</>
|
||||
)
|
||||
}
|
||||
}
|
||||
107
framework/web-react/src/pages/system/machine/disk-charts.jsx
Normal file
107
framework/web-react/src/pages/system/machine/disk-charts.jsx
Normal file
@@ -0,0 +1,107 @@
|
||||
import React, { Component } from 'react'
|
||||
import { Card, Col, Row } from 'antd'
|
||||
import * as echarts from 'echarts'
|
||||
|
||||
export default class diskCharts extends Component {
|
||||
diskInfo = []
|
||||
|
||||
diskChart1 = []
|
||||
diskChart2 = []
|
||||
diskChart3 = []
|
||||
diskChart4 = []
|
||||
|
||||
constructor(props) {
|
||||
super(props)
|
||||
|
||||
const { base } = props
|
||||
|
||||
this.diskInfo = base.diskInfo
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.diskInfo.forEach(({ size, freeSpace }, i) => {
|
||||
const dom = this.refs[`disk-chart-${i}`]
|
||||
this[`diskChart${i}`] = echarts.init(dom)
|
||||
|
||||
const usedSpace = size - freeSpace
|
||||
const sizeGB = (size / 1024 / 1024 / 1024).toFixed(1)
|
||||
const usedGB = (usedSpace / 1024 / 1024 / 1024).toFixed(1)
|
||||
const freeGB = (freeSpace / 1024 / 1024 / 1024).toFixed(1)
|
||||
|
||||
const option = {
|
||||
tooltip: false,
|
||||
series: [
|
||||
{
|
||||
name: '磁盘使用量',
|
||||
type: 'pie',
|
||||
radius: ['70%', '100%'],
|
||||
label: {
|
||||
show: true,
|
||||
fontSize: '14',
|
||||
position: 'center',
|
||||
formatter: `共 ${sizeGB} GB`,
|
||||
},
|
||||
emphasis: {
|
||||
label: {
|
||||
show: true,
|
||||
fontSize: '14',
|
||||
formatter: '{b}{c}GB({d}%)',
|
||||
},
|
||||
},
|
||||
labelLine: {
|
||||
show: false,
|
||||
},
|
||||
data: [
|
||||
{
|
||||
value: usedGB,
|
||||
name: '已用',
|
||||
itemStyle: {
|
||||
color: usedGB / sizeGB >= 0.85 ? '#ff4d4f' : '#007bff',
|
||||
},
|
||||
},
|
||||
{
|
||||
value: freeGB,
|
||||
name: '可用',
|
||||
itemStyle: { color: '#e0e0e0' },
|
||||
},
|
||||
],
|
||||
hoverAnimation: false,
|
||||
animation: false,
|
||||
},
|
||||
],
|
||||
}
|
||||
this[`diskChart${i}`].setOption(option)
|
||||
})
|
||||
|
||||
window.addEventListener('resize', this.onResizeCharts)
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
window.removeEventListener('resize', this.onResizeCharts)
|
||||
}
|
||||
|
||||
onResizeCharts = () => {
|
||||
this.diskInfo.forEach((item, i) => {
|
||||
this[`diskChart${i}`].resize()
|
||||
})
|
||||
}
|
||||
|
||||
render() {
|
||||
const { diskInfo } = this
|
||||
|
||||
return (
|
||||
<>
|
||||
{diskInfo.map((item, i) => (
|
||||
<Col key={i} xl={6} lg={12}>
|
||||
<Card bordered={false}>
|
||||
<div className="h4 mb-md">
|
||||
{item.description}({item.name})
|
||||
</div>
|
||||
<div className="h-200" ref={`disk-chart-${i}`}></div>
|
||||
</Card>
|
||||
</Col>
|
||||
))}
|
||||
</>
|
||||
)
|
||||
}
|
||||
}
|
||||
46
framework/web-react/src/pages/system/machine/index.jsx
Normal file
46
framework/web-react/src/pages/system/machine/index.jsx
Normal file
@@ -0,0 +1,46 @@
|
||||
import React, { Component } from 'react'
|
||||
import { Card, Col, Descriptions, Row, Statistic } from 'antd'
|
||||
import { api } from 'common/api'
|
||||
import { Container } from 'components'
|
||||
import { isEqual } from 'lodash'
|
||||
import moment from 'moment'
|
||||
|
||||
import Base from './base'
|
||||
import UseCharts from './use-charts'
|
||||
import DiskCharts from './disk-charts'
|
||||
|
||||
export default class index extends Component {
|
||||
state = {
|
||||
loading: true,
|
||||
base: {},
|
||||
network: {},
|
||||
}
|
||||
|
||||
shouldComponentUpdate(props, state) {
|
||||
return !isEqual(this.state, state) || this.props.paneActived !== props.paneActived
|
||||
}
|
||||
|
||||
async componentDidMount() {
|
||||
try {
|
||||
const { data: base } = await api.sysMachineBase()
|
||||
this.setState({ loading: false, base })
|
||||
} catch {}
|
||||
}
|
||||
|
||||
render() {
|
||||
const { paneActived } = this.props
|
||||
|
||||
const { loading } = this.state
|
||||
|
||||
return (
|
||||
<Container mode="fluid">
|
||||
<br />
|
||||
<Row gutter={16}>
|
||||
{!loading && <Base {...this.state} />}
|
||||
{!loading && <UseCharts {...this.state} actived={paneActived} />}
|
||||
{!loading && <DiskCharts {...this.state} />}
|
||||
</Row>
|
||||
</Container>
|
||||
)
|
||||
}
|
||||
}
|
||||
304
framework/web-react/src/pages/system/machine/use-charts.jsx
Normal file
304
framework/web-react/src/pages/system/machine/use-charts.jsx
Normal file
@@ -0,0 +1,304 @@
|
||||
import React, { Component } from 'react'
|
||||
import { Card, Col, Descriptions, Row, Statistic } from 'antd'
|
||||
import * as echarts from 'echarts'
|
||||
import moment from 'moment'
|
||||
import { api } from 'common/api'
|
||||
|
||||
export default class useCharts extends Component {
|
||||
state = {
|
||||
use: {},
|
||||
|
||||
nowMoment: moment(),
|
||||
}
|
||||
|
||||
timer = null
|
||||
timerMoment = null
|
||||
|
||||
actived = true
|
||||
|
||||
systemStart = moment()
|
||||
|
||||
now = Date.now()
|
||||
|
||||
cpuChart = null
|
||||
cpuData = []
|
||||
|
||||
ramChart = null
|
||||
ramData = []
|
||||
|
||||
shouldComponentUpdate(props) {
|
||||
// 当前页签未选中时停止获取状态
|
||||
if (this.props.actived !== props.actived) {
|
||||
this.actived = props.actived
|
||||
if (props.actived) {
|
||||
this.start()
|
||||
} else {
|
||||
this.stop()
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.systemStart = moment().add(-this.props.base.runTime)
|
||||
this.initCpuChart()
|
||||
this.initRamChart()
|
||||
|
||||
this.start()
|
||||
|
||||
window.addEventListener('resize', this.onResizeCharts)
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this.stop()
|
||||
|
||||
window.removeEventListener('resize', this.onResizeCharts)
|
||||
}
|
||||
|
||||
start() {
|
||||
this.actived = true
|
||||
this.refreshData()
|
||||
this.timerMoment = setInterval(() => {
|
||||
this.setState({ nowMoment: moment() })
|
||||
}, 1000)
|
||||
}
|
||||
|
||||
stop() {
|
||||
this.actived = false
|
||||
clearTimeout(this.timer)
|
||||
clearInterval(this.timerMoment)
|
||||
}
|
||||
|
||||
async refreshData() {
|
||||
const { data: use } = await api.sysMachineUse()
|
||||
|
||||
this.now = Date.now()
|
||||
|
||||
this.cpuData.shift()
|
||||
this.cpuData.push({
|
||||
name: this.now,
|
||||
value: [this.now, use.cpuRate],
|
||||
})
|
||||
this.cpuChart.setOption({
|
||||
series: [{ data: this.cpuData }],
|
||||
})
|
||||
|
||||
this.ramData.shift()
|
||||
this.ramData.push({
|
||||
name: this.now,
|
||||
value: [this.now, use.ramRate],
|
||||
})
|
||||
this.ramChart.setOption({
|
||||
series: [{ data: this.ramData }],
|
||||
})
|
||||
|
||||
this.setState({ use })
|
||||
|
||||
if (this.actived)
|
||||
this.timer = setTimeout(() => {
|
||||
this.refreshData()
|
||||
}, 3000)
|
||||
}
|
||||
|
||||
initCpuChart() {
|
||||
for (let i = 0; i < 60; i++) {
|
||||
const past = this.now - (60 - i) * 1000
|
||||
this.cpuData.push({
|
||||
name: past,
|
||||
value: [past, -1],
|
||||
})
|
||||
}
|
||||
|
||||
const dom = this.refs['cpu-chart']
|
||||
this.cpuChart = echarts.init(dom)
|
||||
const option = {
|
||||
grid: {
|
||||
show: true,
|
||||
top: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
bottom: 0,
|
||||
borderColor: 'rgba(0, 123, 255, 1)',
|
||||
borderWidth: 2,
|
||||
zlevel: 2,
|
||||
},
|
||||
tooltip: false,
|
||||
xAxis: {
|
||||
type: 'time',
|
||||
axisTick: {
|
||||
show: false,
|
||||
},
|
||||
axisLabel: {
|
||||
show: false,
|
||||
},
|
||||
axisLine: {
|
||||
show: false,
|
||||
},
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value',
|
||||
max: 100,
|
||||
min: 0,
|
||||
axisLabel: {
|
||||
show: false,
|
||||
},
|
||||
},
|
||||
series: [
|
||||
{
|
||||
type: 'line',
|
||||
showSymbol: false,
|
||||
hoverAnimation: false,
|
||||
animation: false,
|
||||
data: this.cpuData,
|
||||
lineStyle: {
|
||||
width: 1,
|
||||
color: 'rgba(0, 123, 255, .8)',
|
||||
},
|
||||
areaStyle: {
|
||||
color: 'rgba(0, 123, 255, .3)',
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
this.cpuChart.setOption(option)
|
||||
}
|
||||
|
||||
initRamChart() {
|
||||
for (let i = 0; i < 60; i++) {
|
||||
const past = this.now - (60 - i) * 1000
|
||||
this.ramData.push({
|
||||
name: past,
|
||||
value: [past, -1],
|
||||
})
|
||||
}
|
||||
|
||||
const dom = this.refs['ram-chart']
|
||||
this.ramChart = echarts.init(dom)
|
||||
const option = {
|
||||
grid: {
|
||||
show: true,
|
||||
top: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
bottom: 0,
|
||||
borderColor: 'rgba(83, 29, 171, 1)',
|
||||
borderWidth: 2,
|
||||
zlevel: 2,
|
||||
},
|
||||
tooltip: false,
|
||||
xAxis: {
|
||||
type: 'time',
|
||||
axisTick: {
|
||||
show: false,
|
||||
},
|
||||
axisLabel: {
|
||||
show: false,
|
||||
},
|
||||
axisLine: {
|
||||
show: false,
|
||||
},
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value',
|
||||
max: 100,
|
||||
min: 0,
|
||||
axisLabel: {
|
||||
show: false,
|
||||
},
|
||||
},
|
||||
series: [
|
||||
{
|
||||
type: 'line',
|
||||
showSymbol: false,
|
||||
hoverAnimation: false,
|
||||
animation: false,
|
||||
data: this.ramData,
|
||||
lineStyle: {
|
||||
width: 1,
|
||||
color: 'rgba(83, 29, 171, .8)',
|
||||
},
|
||||
areaStyle: {
|
||||
color: 'rgba(83, 29, 171, .3)',
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
this.ramChart.setOption(option)
|
||||
}
|
||||
|
||||
onResizeCharts = () => {
|
||||
this.cpuChart.resize()
|
||||
this.ramChart.resize()
|
||||
}
|
||||
|
||||
render() {
|
||||
const { base } = this.props
|
||||
const { use, nowMoment } = this.state
|
||||
const { cpuName, cpuBaseSpeed, processorCount, totalRam } = base
|
||||
const { cpuRate, ramRate } = use
|
||||
|
||||
const diffDays = nowMoment.diff(this.systemStart, 'days')
|
||||
const diff =
|
||||
diffDays + ':' + moment(nowMoment.diff(this.systemStart)).utc().format('HH:mm:ss')
|
||||
|
||||
return (
|
||||
<>
|
||||
<Col xl={12} lg={24}>
|
||||
<Card bordered={false}>
|
||||
<Row align="bottom" justify="space-between">
|
||||
<div className="h2">CPU</div>
|
||||
<div className="h5">{cpuName}</div>
|
||||
</Row>
|
||||
<div className="h-200 mt-md mb-md" ref="cpu-chart"></div>
|
||||
<Row>
|
||||
<Col flex="1">
|
||||
<Statistic title="使用率" value={(cpuRate || 0) + ' %'} />
|
||||
<Statistic title="正常运行时间" value={diff} />
|
||||
</Col>
|
||||
<Col flex="1">
|
||||
<Descriptions column={1}>
|
||||
<Descriptions.Item label="基准速度">
|
||||
<b>{((cpuBaseSpeed || 0) / 1000).toFixed(2)} GHz</b>
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="逻辑处理器">
|
||||
<b>{processorCount || 0}</b>
|
||||
</Descriptions.Item>
|
||||
</Descriptions>
|
||||
</Col>
|
||||
</Row>
|
||||
</Card>
|
||||
</Col>
|
||||
<Col xl={12} lg={24}>
|
||||
<Card bordered={false}>
|
||||
<Row align="bottom" justify="space-between">
|
||||
<div className="h2">内存</div>
|
||||
<div className="h5">{((totalRam || 0) / 1024).toFixed(1)} GB</div>
|
||||
</Row>
|
||||
<div className="h-200 mt-md mb-md" ref="ram-chart"></div>
|
||||
<Row>
|
||||
<Col flex="1">
|
||||
<Statistic
|
||||
title="使用中"
|
||||
value={
|
||||
(((totalRam || 0) / 1024) * ((ramRate || 1) / 100)).toFixed(
|
||||
1
|
||||
) + ' GB'
|
||||
}
|
||||
/>
|
||||
<Statistic
|
||||
title="可用"
|
||||
value={
|
||||
(
|
||||
((totalRam || 0) / 1024) *
|
||||
(1 - (ramRate || 1) / 100)
|
||||
).toFixed(1) + ' GB'
|
||||
}
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
</Card>
|
||||
</Col>
|
||||
</>
|
||||
)
|
||||
}
|
||||
}
|
||||
416
framework/web-react/src/pages/system/menu/form.jsx
Normal file
416
framework/web-react/src/pages/system/menu/form.jsx
Normal file
@@ -0,0 +1,416 @@
|
||||
import React, { Component } from 'react'
|
||||
import { Form, Input, InputNumber, Radio, Select, Spin, Switch, TreeSelect } from 'antd'
|
||||
import { AntIcon, IconSelector } from 'components'
|
||||
import { cloneDeep } from 'lodash'
|
||||
import getDictData from 'util/dic'
|
||||
import { api } from 'common/api'
|
||||
import { EMPTY_ID } from 'util/global'
|
||||
|
||||
const initialValues = {
|
||||
type: 1,
|
||||
openType: 1,
|
||||
weight: '2',
|
||||
visible: true,
|
||||
sort: 100,
|
||||
}
|
||||
|
||||
export default class form extends Component {
|
||||
state = {
|
||||
// 加载状态
|
||||
loading: true,
|
||||
codes: {
|
||||
menuType: [],
|
||||
openType: [],
|
||||
menuWeight: [],
|
||||
},
|
||||
options: {
|
||||
appList: [],
|
||||
parentTreeData: [],
|
||||
},
|
||||
|
||||
addType: [],
|
||||
type: initialValues.type,
|
||||
openType: initialValues.openType,
|
||||
icon: '',
|
||||
}
|
||||
|
||||
// 表单实例
|
||||
form = React.createRef()
|
||||
|
||||
iconSelector = React.createRef()
|
||||
|
||||
// 初始化数据
|
||||
record = {}
|
||||
|
||||
/**
|
||||
* mount后回调
|
||||
*/
|
||||
componentDidMount() {
|
||||
this.props.created && this.props.created(this)
|
||||
}
|
||||
|
||||
/**
|
||||
* 填充数据
|
||||
* 可以在设置this.record之后对其作出数据结构调整
|
||||
* [异步,必要]
|
||||
* @param {*} params
|
||||
*/
|
||||
async fillData(params) {
|
||||
const form = this.form.current
|
||||
this.record = cloneDeep(params.record)
|
||||
//#region 从后端转换成前段所需格式
|
||||
const codes = await getDictData('menu_type', 'open_type', 'menu_weight')
|
||||
const appList = await this.onLoadSysApplist()
|
||||
let parentTreeData = []
|
||||
if (params.isParent) {
|
||||
parentTreeData = await this.onLoadMenuTree(params.parent.application)
|
||||
}
|
||||
|
||||
if (params.record) {
|
||||
parentTreeData = await this.onLoadMenuTree(params.record.application)
|
||||
} else {
|
||||
this.setState({ addType: params.addType })
|
||||
if (params.addType.length) {
|
||||
form.setFieldsValue({
|
||||
type: params.addType[0],
|
||||
})
|
||||
}
|
||||
}
|
||||
const icon = params.record && params.record.icon
|
||||
this.setState({
|
||||
codes,
|
||||
options: {
|
||||
appList,
|
||||
parentTreeData,
|
||||
},
|
||||
icon,
|
||||
})
|
||||
//#endregion
|
||||
if (params.isParent) {
|
||||
form.setFieldsValue({
|
||||
pid: params.parent.id,
|
||||
application: params.parent.application,
|
||||
})
|
||||
} else {
|
||||
form.setFieldsValue(this.record)
|
||||
}
|
||||
|
||||
this.setState({ loading: false })
|
||||
|
||||
this.onTypeChange()
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取数据
|
||||
* 可以对postData进行数据结构调整
|
||||
* [异步,必要]
|
||||
* @returns
|
||||
*/
|
||||
async getData() {
|
||||
const form = this.form.current
|
||||
|
||||
const valid = await form.validateFields()
|
||||
if (valid) {
|
||||
const postData = form.getFieldsValue()
|
||||
if (this.record) {
|
||||
postData.id = this.record.id
|
||||
}
|
||||
//#region 从前段转换后端所需格式
|
||||
// 目录的pic必须为空
|
||||
if (!postData.type) {
|
||||
postData.pid = EMPTY_ID
|
||||
}
|
||||
//#endregion
|
||||
return postData
|
||||
}
|
||||
}
|
||||
|
||||
//#region 自定义方法
|
||||
async onLoadSysApplist() {
|
||||
const { data } = await api.getAppList()
|
||||
return data
|
||||
}
|
||||
|
||||
async onLoadMenuTree(application) {
|
||||
const { data } = await api.getMenuTree({ application })
|
||||
return [
|
||||
{
|
||||
id: EMPTY_ID,
|
||||
parentId: undefined,
|
||||
title: '顶级',
|
||||
value: EMPTY_ID,
|
||||
pid: undefined,
|
||||
children: data,
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
onTypeChange() {
|
||||
this.onTypeChangeGroup()
|
||||
// const form = this.form.current
|
||||
// const { type } = form.getFieldsValue()
|
||||
// if ([0, 2].includes(type)) {
|
||||
// form.setFieldsValue({
|
||||
// openType: 0,
|
||||
// })
|
||||
// } else {
|
||||
// form.setFieldsValue({
|
||||
// openType: 1,
|
||||
// })
|
||||
// }
|
||||
}
|
||||
|
||||
onOpenTypeChange() {
|
||||
this.onTypeChangeGroup()
|
||||
}
|
||||
|
||||
onTypeChangeGroup() {
|
||||
const form = this.form.current
|
||||
const { type, openType } = form.getFieldsValue()
|
||||
// if (type == 1 && openType == 2) {
|
||||
// form.setFieldsValue({
|
||||
// component: 'iframe'
|
||||
// })
|
||||
// } else {
|
||||
// form.setFieldsValue({
|
||||
// component: ''
|
||||
// })
|
||||
// }
|
||||
|
||||
this.setState({
|
||||
type,
|
||||
openType,
|
||||
})
|
||||
}
|
||||
|
||||
async onApplicationChange(value) {
|
||||
this.setState({
|
||||
loading: true,
|
||||
})
|
||||
const parentTreeData = await this.onLoadMenuTree(value)
|
||||
this.setState({
|
||||
loading: false,
|
||||
options: {
|
||||
...this.state.options,
|
||||
parentTreeData,
|
||||
},
|
||||
})
|
||||
this.form.current.setFieldsValue({
|
||||
pid: undefined,
|
||||
})
|
||||
}
|
||||
|
||||
onSelectIcon(icon) {
|
||||
this.form.current.setFieldsValue({
|
||||
icon,
|
||||
})
|
||||
this.setState({ icon })
|
||||
}
|
||||
//#endregion
|
||||
|
||||
render() {
|
||||
const { loading, codes, options, addType, type, openType, icon } = this.state
|
||||
|
||||
return (
|
||||
<Form initialValues={initialValues} ref={this.form} className="yo-form">
|
||||
<Spin spinning={loading} indicator={<AntIcon type="loading" />}>
|
||||
<div className="yo-form-group">
|
||||
<h3 className="h3">基本信息</h3>
|
||||
<div className="yo-form-group">
|
||||
<Form.Item
|
||||
label="菜单类型"
|
||||
name="type"
|
||||
tooltip={
|
||||
<>
|
||||
目录:一级菜单,默认添加在顶级
|
||||
<br />
|
||||
菜单:二级菜单
|
||||
<br />
|
||||
按钮:菜单中对应到接口的功能
|
||||
</>
|
||||
}
|
||||
rules={[{ required: true, message: '请选择菜单类型' }]}
|
||||
>
|
||||
<Radio.Group onChange={e => this.onTypeChange(e)}>
|
||||
{codes.menuType.map(item => {
|
||||
return (
|
||||
<Radio.Button
|
||||
key={item.code}
|
||||
value={+item.code}
|
||||
disabled={!addType.includes(+item.code)}
|
||||
>
|
||||
{item.value}
|
||||
</Radio.Button>
|
||||
)
|
||||
})}
|
||||
</Radio.Group>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label="名称"
|
||||
name="name"
|
||||
rules={[{ required: true, message: '请输入名称' }]}
|
||||
>
|
||||
<Input autoComplete="off" placeholder="请输入名称" />
|
||||
</Form.Item>
|
||||
<Form.Item label="唯一编码" name="code">
|
||||
<Input autoComplete="off" placeholder="请输入唯一编码" />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label="所属应用"
|
||||
name="application"
|
||||
rules={[{ required: true, message: '请选择所属应用' }]}
|
||||
>
|
||||
<Select
|
||||
placeholder="请选择所属应用"
|
||||
onChange={value => this.onApplicationChange(value)}
|
||||
>
|
||||
{options.appList.map(item => {
|
||||
return (
|
||||
<Select.Option key={item.code} value={item.code}>
|
||||
{item.name}
|
||||
</Select.Option>
|
||||
)
|
||||
})}
|
||||
</Select>
|
||||
</Form.Item>
|
||||
{type != 0 && (
|
||||
<Form.Item
|
||||
label="父级菜单"
|
||||
name="pid"
|
||||
rules={[{ required: true, message: '请选择父级' }]}
|
||||
>
|
||||
<TreeSelect
|
||||
dropdownStyle={{ maxHeight: '300px', overflow: 'auto' }}
|
||||
treeData={options.parentTreeData}
|
||||
placeholder="请选择父级菜单"
|
||||
/>
|
||||
</Form.Item>
|
||||
)}
|
||||
<Form.Item
|
||||
label="权重"
|
||||
name="weight"
|
||||
tooltip={
|
||||
<>
|
||||
系统权重:菜单/功能任何角色可用
|
||||
<br />
|
||||
业务权重:菜单/功能为超级管理员不可用,可防止管理员误操作
|
||||
</>
|
||||
}
|
||||
rules={[{ required: true, message: '请选择权重' }]}
|
||||
>
|
||||
<Radio.Group>
|
||||
{codes.menuWeight.map(item => {
|
||||
return (
|
||||
<Radio.Button key={item.code} value={item.code}>
|
||||
{item.value}
|
||||
</Radio.Button>
|
||||
)
|
||||
})}
|
||||
</Radio.Group>
|
||||
</Form.Item>
|
||||
</div>
|
||||
<h3 className="h3">扩展信息</h3>
|
||||
<div className="yo-form-group">
|
||||
{type == 1 && (
|
||||
<Form.Item label="打开方式" name="openType">
|
||||
<Radio.Group onChange={e => this.onOpenTypeChange(e)}>
|
||||
{codes.openType.map(item => {
|
||||
return (
|
||||
<Radio.Button key={item.code} value={+item.code}>
|
||||
{item.value}
|
||||
</Radio.Button>
|
||||
)
|
||||
})}
|
||||
</Radio.Group>
|
||||
</Form.Item>
|
||||
)}
|
||||
{type == 1 && openType == 1 && (
|
||||
<Form.Item
|
||||
label="前端组件"
|
||||
name="component"
|
||||
tooltip="打开新页签并渲染前端组件"
|
||||
rules={[{ required: true, message: '请输入前端组件' }]}
|
||||
>
|
||||
<Input autoComplete="off" placeholder="请输入前端组件" />
|
||||
</Form.Item>
|
||||
)}
|
||||
{type == 1 && openType == 2 && (
|
||||
<Form.Item
|
||||
label="内链地址"
|
||||
name="link"
|
||||
tooltip="打开新页签并使用iframe加载页面"
|
||||
rules={[{ required: true, message: '请输入内链地址' }]}
|
||||
>
|
||||
<Input autoComplete="off" placeholder="请输入内链地址" />
|
||||
</Form.Item>
|
||||
)}
|
||||
{type == 1 && openType == 3 && (
|
||||
<Form.Item
|
||||
label="外链地址"
|
||||
name="redirect"
|
||||
tooltip="打开新的浏览器窗口"
|
||||
rules={[{ required: true, message: '请输入外链地址' }]}
|
||||
>
|
||||
<Input autoComplete="off" placeholder="请输入外链地址" />
|
||||
</Form.Item>
|
||||
)}
|
||||
{type == 2 && (
|
||||
<Form.Item
|
||||
label="权限标识"
|
||||
name="permission"
|
||||
rules={[{ required: true, message: '请输入权限标识' }]}
|
||||
>
|
||||
<Input autoComplete="off" placeholder="请输入权限标识" />
|
||||
</Form.Item>
|
||||
)}
|
||||
{type == 2 && (
|
||||
<Form.Item
|
||||
label="关联上级菜单显示"
|
||||
name="visibleParent"
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
)}
|
||||
<Form.Item label="可见性" name="visible" valuePropName="checked">
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
{type != 2 && (
|
||||
<Form.Item label="图标" name="icon">
|
||||
<Input
|
||||
disabled
|
||||
placeholder="请选择图标"
|
||||
addonBefore={icon && <AntIcon type={icon} />}
|
||||
addonAfter={
|
||||
<AntIcon
|
||||
type="setting"
|
||||
onClick={() =>
|
||||
this.iconSelector.current.open(
|
||||
this.form.current.getFieldValue('icon')
|
||||
)
|
||||
}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
</Form.Item>
|
||||
)}
|
||||
<Form.Item label="排序" name="sort">
|
||||
<InputNumber
|
||||
max={1000}
|
||||
min={0}
|
||||
className="w-100-p"
|
||||
placeholder="请输入排序"
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form.Item label="备注信息" name="remark">
|
||||
<Input.TextArea placeholder="请输入备注信息" />
|
||||
</Form.Item>
|
||||
</div>
|
||||
</div>
|
||||
</Spin>
|
||||
|
||||
<IconSelector ref={this.iconSelector} onSelect={icon => this.onSelectIcon(icon)} />
|
||||
</Form>
|
||||
)
|
||||
}
|
||||
}
|
||||
415
framework/web-react/src/pages/system/menu/index.jsx
Normal file
415
framework/web-react/src/pages/system/menu/index.jsx
Normal file
@@ -0,0 +1,415 @@
|
||||
import React, { Component } from 'react'
|
||||
import { Button, Table, Card, Popconfirm, message as Message, Row, Col, Tooltip, Tag } from 'antd'
|
||||
import { isEqual } from 'lodash'
|
||||
import { AntIcon, Auth, Container, ModalForm, QueryTable, QueryTableActions } from 'components'
|
||||
import { api } from 'common/api'
|
||||
import getDictData from 'util/dic'
|
||||
import auth from 'components/authorized/handler'
|
||||
import { toCamelCase } from 'util/format'
|
||||
import FormBody from './form'
|
||||
|
||||
// 配置页面所需接口函数
|
||||
const apiAction = {
|
||||
page: api.getMenuList,
|
||||
add: api.sysMenuAdd,
|
||||
edit: api.sysMenuEdit,
|
||||
delete: api.sysMenuDelete,
|
||||
}
|
||||
|
||||
// 用于弹窗标题
|
||||
const name = '菜单'
|
||||
|
||||
export default class index extends Component {
|
||||
state = {
|
||||
codes: {
|
||||
menuType: [],
|
||||
menuWeight: [],
|
||||
openType: [],
|
||||
},
|
||||
}
|
||||
|
||||
// 表格实例
|
||||
table = React.createRef()
|
||||
|
||||
// 新增窗口实例
|
||||
addForm = React.createRef()
|
||||
// 编辑窗口实例
|
||||
editForm = React.createRef()
|
||||
|
||||
// 表格字段
|
||||
columns = [
|
||||
{
|
||||
title: '菜单名称',
|
||||
width: 220,
|
||||
dataIndex: 'name',
|
||||
},
|
||||
{
|
||||
title: '菜单类型',
|
||||
width: 100,
|
||||
dataIndex: 'type',
|
||||
render: text => this.bindCodeValue(text, 'menu_type'),
|
||||
},
|
||||
{
|
||||
title: '图标',
|
||||
width: 100,
|
||||
dataIndex: 'icon',
|
||||
render: text => text && <AntIcon type={text} />,
|
||||
},
|
||||
{
|
||||
title: '连接',
|
||||
width: 220,
|
||||
dataIndex: 'openType',
|
||||
render: (text, record) => {
|
||||
switch (text) {
|
||||
case 1:
|
||||
return (
|
||||
<>
|
||||
<Tag color="green">{this.bindCodeValue(text, 'open_type')}</Tag>{' '}
|
||||
{record.component}
|
||||
</>
|
||||
)
|
||||
case 2:
|
||||
return (
|
||||
<>
|
||||
<Tag color="orange">{this.bindCodeValue(text, 'open_type')}</Tag>{' '}
|
||||
{record.link}
|
||||
</>
|
||||
)
|
||||
case 3:
|
||||
return (
|
||||
<>
|
||||
<Tag color="red">{this.bindCodeValue(text, 'open_type')}</Tag>{' '}
|
||||
{record.redirect}
|
||||
</>
|
||||
)
|
||||
default:
|
||||
return ''
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '排序',
|
||||
width: 100,
|
||||
dataIndex: 'sort',
|
||||
},
|
||||
]
|
||||
|
||||
/**
|
||||
* 构造函数,在渲染前动态添加操作字段等
|
||||
* @param {*} props
|
||||
*/
|
||||
constructor(props) {
|
||||
super(props)
|
||||
|
||||
const flag = auth({ sysApp: [['edit'], ['delete']] })
|
||||
|
||||
if (flag) {
|
||||
this.columns.push({
|
||||
title: '操作',
|
||||
width: 220,
|
||||
dataIndex: 'actions',
|
||||
render: (text, record) => (
|
||||
<QueryTableActions>
|
||||
<Auth auth="sysMenu:edit">
|
||||
<a onClick={() => this.onOpen({ modal: this.editForm, record })}>
|
||||
编辑
|
||||
</a>
|
||||
</Auth>
|
||||
<Auth auth="sysMenu:delete">
|
||||
<Popconfirm
|
||||
placement="topRight"
|
||||
title="是否确认删除"
|
||||
onConfirm={() => this.onDelete(record)}
|
||||
>
|
||||
<a>删除</a>
|
||||
</Popconfirm>
|
||||
</Auth>
|
||||
{record.type < 2 && (
|
||||
<Auth auth="sysMenu:add">
|
||||
<a
|
||||
onClick={() =>
|
||||
this.onOpen({
|
||||
modal: this.addForm,
|
||||
record,
|
||||
isParent: true,
|
||||
addType: record.type == 0 ? [1] : [2],
|
||||
})
|
||||
}
|
||||
>
|
||||
{record.type == 0 ? '新增子菜单' : '新增功能'}
|
||||
</a>
|
||||
</Auth>
|
||||
)}
|
||||
</QueryTableActions>
|
||||
),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 阻止外部组件引发的渲染,提升性能
|
||||
* 可自行添加渲染条件
|
||||
* [必要]
|
||||
* @param {*} props
|
||||
* @param {*} state
|
||||
* @returns
|
||||
*/
|
||||
shouldComponentUpdate(props, state) {
|
||||
return !isEqual(this.state, state)
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载字典数据,之后开始加载表格数据
|
||||
* 如果必须要加载字典数据,可直接对表格设置autoLoad=true
|
||||
*/
|
||||
componentDidMount() {
|
||||
this.table.current.onLoading()
|
||||
getDictData('menu_type', 'menu_weight', 'open_type').then(res => {
|
||||
this.setState(
|
||||
{
|
||||
codes: res,
|
||||
},
|
||||
() => {
|
||||
this.table.current.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, isParent = false, addType = [] }) {
|
||||
const params = isParent
|
||||
? {
|
||||
parent: record,
|
||||
isParent,
|
||||
addType,
|
||||
}
|
||||
: {
|
||||
record,
|
||||
isParent,
|
||||
addType,
|
||||
}
|
||||
|
||||
modal.current.open(params)
|
||||
}
|
||||
|
||||
/**
|
||||
* 对表格上的操作进行统一处理
|
||||
* [异步]
|
||||
* @param {*} action
|
||||
* @param {*} successMessage
|
||||
*/
|
||||
async onAction(action, successMessage) {
|
||||
this.table.current.onLoading()
|
||||
try {
|
||||
await action
|
||||
Message.success(successMessage)
|
||||
this.table.current.onReloadData()
|
||||
} catch {
|
||||
this.table.current.onLoaded()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除
|
||||
* @param {*} record
|
||||
*/
|
||||
onDelete(record) {
|
||||
this.onAction(apiAction.delete(record), '删除成功')
|
||||
}
|
||||
|
||||
//#region 自定义方法
|
||||
/**
|
||||
* 绘制新的扩展行
|
||||
* @param {*} record
|
||||
*/
|
||||
onRowRender(record) {
|
||||
const grids = []
|
||||
let isFunction = false
|
||||
record.children.map(item => {
|
||||
if (!(item.children && item.children.length) && item.type == 2) {
|
||||
isFunction = true
|
||||
const grid = (
|
||||
<Card.Grid key={item.id}>
|
||||
<p>
|
||||
{item.visibleParent && (
|
||||
<Tooltip placement="top" title="选中此项才会显示菜单">
|
||||
<AntIcon
|
||||
type="eye"
|
||||
style={{ color: '#1890ff' }}
|
||||
className="mr-xxs"
|
||||
/>
|
||||
</Tooltip>
|
||||
)}
|
||||
<b>{item.name}</b>
|
||||
</p>
|
||||
<span className="block ellipsis text-gray">{item.permission}</span>
|
||||
<div className="text-right">
|
||||
<QueryTableActions>
|
||||
<Auth auth="sysMenu:edit">
|
||||
<Tooltip title="编辑">
|
||||
<a
|
||||
className="link-gray"
|
||||
onClick={() =>
|
||||
this.onOpen({ modal: this.editForm, record: item })
|
||||
}
|
||||
>
|
||||
<AntIcon type="edit" />
|
||||
</a>
|
||||
</Tooltip>
|
||||
</Auth>
|
||||
<Auth auth="sysMenu:delete">
|
||||
<Popconfirm
|
||||
placement="topRight"
|
||||
title="是否确认删除"
|
||||
onConfirm={() => this.onDelete(item)}
|
||||
>
|
||||
<Tooltip title="删除">
|
||||
<a className="link-gray">
|
||||
<AntIcon type="delete" />
|
||||
</a>
|
||||
</Tooltip>
|
||||
</Popconfirm>
|
||||
</Auth>
|
||||
</QueryTableActions>
|
||||
</div>
|
||||
</Card.Grid>
|
||||
)
|
||||
grids.push(grid)
|
||||
} else if (
|
||||
(item.children && item.children.length) ||
|
||||
(!(item.children && item.children.length) && item.type == 1)
|
||||
) {
|
||||
grids.push(item)
|
||||
}
|
||||
})
|
||||
if (isFunction) {
|
||||
grids.push(
|
||||
<Card.Grid
|
||||
key={0}
|
||||
style={{ padding: '18px 12px', cursor: 'pointer' }}
|
||||
className="text-center"
|
||||
onClick={() =>
|
||||
this.onOpen({ modal: this.addForm, record, isParent: true, addType: [2] })
|
||||
}
|
||||
>
|
||||
<div>
|
||||
<AntIcon type="plus" className="text-normal h2" />
|
||||
</div>
|
||||
<span className="text-gray">新增功能</span>
|
||||
</Card.Grid>
|
||||
)
|
||||
return <Card bordered={false}>{grids}</Card>
|
||||
} else {
|
||||
return (
|
||||
<Table
|
||||
columns={this.columns}
|
||||
showHeader={false}
|
||||
dataSource={grids}
|
||||
rowKey={record => record.id}
|
||||
expandable={{
|
||||
expandedRowRender: record => this.onRowRender(record),
|
||||
rowExpandable: record => record.children && record.children.length,
|
||||
}}
|
||||
childrenColumnName="none"
|
||||
bordered={true}
|
||||
pagination={false}
|
||||
/>
|
||||
)
|
||||
}
|
||||
}
|
||||
//#endregion
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Container mode="fluid">
|
||||
<br />
|
||||
<Card bordered={false}>
|
||||
<QueryTable
|
||||
className="yo-menu-table"
|
||||
childrenColumnName="none"
|
||||
ref={this.table}
|
||||
autoLoad={false}
|
||||
loadData={this.loadData}
|
||||
columns={this.columns}
|
||||
expandable={{
|
||||
expandedRowRender: record => this.onRowRender(record),
|
||||
rowExpandable: record => record.children && record.children.length,
|
||||
}}
|
||||
operator={
|
||||
<Auth auth="sysMenu:add">
|
||||
<Button
|
||||
icon={<AntIcon type="plus" />}
|
||||
onClick={() =>
|
||||
this.onOpen({ modal: this.addForm, addType: [0, 1] })
|
||||
}
|
||||
>
|
||||
新增目录/菜单
|
||||
</Button>
|
||||
</Auth>
|
||||
}
|
||||
/>
|
||||
</Card>
|
||||
|
||||
<ModalForm
|
||||
title={`新增${name}`}
|
||||
action={apiAction.add}
|
||||
ref={this.addForm}
|
||||
width={800}
|
||||
type="drawer"
|
||||
onSuccess={() => this.table.current.onReloadData()}
|
||||
>
|
||||
<FormBody />
|
||||
</ModalForm>
|
||||
|
||||
<ModalForm
|
||||
title={`编辑${name}`}
|
||||
action={apiAction.edit}
|
||||
ref={this.editForm}
|
||||
width={800}
|
||||
type="drawer"
|
||||
onSuccess={() => this.table.current.onReloadData()}
|
||||
>
|
||||
<FormBody />
|
||||
</ModalForm>
|
||||
</Container>
|
||||
)
|
||||
}
|
||||
}
|
||||
251
framework/web-react/src/pages/system/notice/form.jsx
Normal file
251
framework/web-react/src/pages/system/notice/form.jsx
Normal file
@@ -0,0 +1,251 @@
|
||||
import React, { Component } from 'react'
|
||||
import { Form, Spin, Input, Radio, Select, Upload, Button } from 'antd'
|
||||
import { api } from 'common/api'
|
||||
import { AntIcon, BraftEditor } from 'components'
|
||||
import getDictData from 'util/dic'
|
||||
import { BlobToBase64, GetFileName, PreviewFile } from 'util/file'
|
||||
import { cloneDeep } from 'lodash'
|
||||
|
||||
const initialValues = {}
|
||||
|
||||
export default class form extends Component {
|
||||
state = {
|
||||
// 加载状态
|
||||
loading: true,
|
||||
options: {
|
||||
userList: [],
|
||||
},
|
||||
codes: {
|
||||
noticeType: [],
|
||||
noticeStatus: [],
|
||||
},
|
||||
funloader: Object,
|
||||
}
|
||||
|
||||
// 表单实例
|
||||
form = React.createRef()
|
||||
|
||||
// 初始化数据
|
||||
record = {}
|
||||
|
||||
/**
|
||||
* mount后回调
|
||||
*/
|
||||
componentDidMount() {
|
||||
this.props.created && this.props.created(this)
|
||||
}
|
||||
|
||||
/**
|
||||
* 填充数据
|
||||
* 可以在设置this.record之后对其作出数据结构调整
|
||||
* [异步,必要]
|
||||
* @param {*} params
|
||||
*/
|
||||
async fillData(params) {
|
||||
this.record = cloneDeep(params.record)
|
||||
//#region 从后端转换成前段所需格式,也可以在此处调用获取详细数据接口
|
||||
if (params.id) {
|
||||
this.record = (await api.sysNoticeDetail({ id: params.id })).data
|
||||
}
|
||||
const {
|
||||
data: { items: userList },
|
||||
} = await this.onLoadUser()
|
||||
const codes = await getDictData('notice_status', 'notice_type')
|
||||
|
||||
if (this.record) {
|
||||
const { attachments } = this.record
|
||||
if (attachments) {
|
||||
const fileValue = []
|
||||
const fileList = attachments.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',
|
||||
})
|
||||
}
|
||||
}
|
||||
this.record.attachments = fileValue
|
||||
}
|
||||
}
|
||||
//#endregion
|
||||
this.form.current.setFieldsValue(this.record)
|
||||
|
||||
this.setState({
|
||||
loading: false,
|
||||
options: {
|
||||
userList,
|
||||
},
|
||||
codes,
|
||||
})
|
||||
//加载 BraftEditor 富文本插件
|
||||
this.state.funloader()
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取数据
|
||||
* 可以对postData进行数据结构调整
|
||||
* [异步,必要]
|
||||
* @returns
|
||||
*/
|
||||
async getData() {
|
||||
const form = this.form.current
|
||||
const valid = await form.validateFields()
|
||||
if (valid) {
|
||||
const postData = form.getFieldsValue()
|
||||
if (this.record) {
|
||||
postData.id = this.record.id
|
||||
postData.status = 0
|
||||
} else {
|
||||
postData.status = 1
|
||||
}
|
||||
//#region 从前段转换后端所需格式
|
||||
postData.attachments = postData.attachments.map(item =>
|
||||
item.uid.startsWith('rc-upload') ? item.response : item.uid
|
||||
)
|
||||
//#endregion
|
||||
return postData
|
||||
}
|
||||
}
|
||||
|
||||
//#region 自定义方法
|
||||
/**
|
||||
* 接受子组件传过来的方法
|
||||
* 等页面加载完毕后调用
|
||||
*/
|
||||
getChildrenMsg = funLoad => {
|
||||
this.setState({
|
||||
funloader: funLoad,
|
||||
})
|
||||
}
|
||||
async onLoadUser() {
|
||||
const data = await api.getUserPage()
|
||||
return data
|
||||
}
|
||||
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 { options, codes } = this.state
|
||||
|
||||
return (
|
||||
<Form initialValues={initialValues} ref={this.form} className="yo-form">
|
||||
<Spin spinning={this.state.loading} indicator={<AntIcon type="loading" />}>
|
||||
<div className="yo-form-group yo-form--fluid">
|
||||
<Form.Item
|
||||
label="标题"
|
||||
name="title"
|
||||
rules={[{ required: true, message: '请输入标题', trigger: 'blur' }]}
|
||||
>
|
||||
<Input autoComplete="off" placeholder="请输入标题" />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label="类型"
|
||||
name="type"
|
||||
rules={[{ required: true, message: '请选择类型' }]}
|
||||
>
|
||||
<Radio.Group buttonStyle="solid">
|
||||
{codes.noticeType.map(item => (
|
||||
<Radio.Button key={item.code} value={+item.code}>
|
||||
{item.value}
|
||||
</Radio.Button>
|
||||
))}
|
||||
</Radio.Group>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label="内容"
|
||||
name="content"
|
||||
rules={[
|
||||
{ required: true, message: '请输入内容' },
|
||||
{
|
||||
validator: async (field, value) => {
|
||||
const v = value.replace(/<\/?.+?\/?>/g, '')
|
||||
if (!v) {
|
||||
throw Error('请输入内容')
|
||||
}
|
||||
},
|
||||
},
|
||||
]}
|
||||
>
|
||||
<BraftEditor parent={this} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label="上传附件"
|
||||
name="attachments"
|
||||
valuePropName="fileList"
|
||||
getValueFromEvent={e => {
|
||||
if (Array.isArray(e)) {
|
||||
return e
|
||||
}
|
||||
return e && e.fileList
|
||||
}}
|
||||
>
|
||||
<Upload
|
||||
customRequest={e => this.onFileUpload(e)}
|
||||
showUploadList={{
|
||||
showRemoveIcon: true,
|
||||
showDownloadIcon: true,
|
||||
}}
|
||||
onPreview={() => false}
|
||||
onDownload={file => this.onFileDownload(file)}
|
||||
>
|
||||
<Button type="dashed" icon={<AntIcon type="upload" />}>
|
||||
上传附件
|
||||
</Button>
|
||||
</Upload>
|
||||
</Form.Item>
|
||||
<Form.Item label="通知到的人" name="noticeUserIdList">
|
||||
<Select
|
||||
mode="tags"
|
||||
placeholder="请选择通知到的人"
|
||||
tokenSeparators={[',']}
|
||||
>
|
||||
{options.userList.map(item => (
|
||||
<Select.Option key={item.id} value={item.id}>
|
||||
{item.name}
|
||||
</Select.Option>
|
||||
))}
|
||||
</Select>
|
||||
</Form.Item>
|
||||
</div>
|
||||
</Spin>
|
||||
</Form>
|
||||
)
|
||||
}
|
||||
}
|
||||
371
framework/web-react/src/pages/system/notice/index.jsx
Normal file
371
framework/web-react/src/pages/system/notice/index.jsx
Normal file
@@ -0,0 +1,371 @@
|
||||
import React, { Component } from 'react'
|
||||
import { Button, Card, Form, Input, message as Message, Popconfirm, Select } 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'
|
||||
import FormBody from './form'
|
||||
|
||||
/**
|
||||
* 注释段[\/**\/]为必须要改
|
||||
*/
|
||||
|
||||
/**
|
||||
* 配置页面所需接口函数
|
||||
*/
|
||||
const apiAction = {
|
||||
page: api.sysNoticePage,
|
||||
add: api.sysNoticeAdd,
|
||||
edit: api.sysNoticeEdit,
|
||||
delete: api.sysNoticeDelete,
|
||||
status: api.sysNoticeChangeStatus,
|
||||
}
|
||||
|
||||
/**
|
||||
* 用于弹窗标题
|
||||
* [必要]
|
||||
*/
|
||||
const name = '通知公告'
|
||||
|
||||
/**
|
||||
* 统一配置权限标识
|
||||
* [必要]
|
||||
*/
|
||||
const authName = 'sysNotice'
|
||||
|
||||
export default class index extends Component {
|
||||
state = {
|
||||
codes: {
|
||||
noticeStatus: [],
|
||||
noticeType: [],
|
||||
},
|
||||
}
|
||||
|
||||
// 表格实例
|
||||
table = React.createRef()
|
||||
|
||||
// 新增窗口实例
|
||||
addForm = React.createRef()
|
||||
// 编辑窗口实例
|
||||
editForm = React.createRef()
|
||||
|
||||
columns = [
|
||||
{
|
||||
title: '标题',
|
||||
dataIndex: 'title',
|
||||
width: 300,
|
||||
sorter: true,
|
||||
},
|
||||
{
|
||||
title: '发布人',
|
||||
dataIndex: 'publicUserName',
|
||||
width: 120,
|
||||
sorter: true,
|
||||
},
|
||||
{
|
||||
title: '发布时间',
|
||||
dataIndex: 'createdTime',
|
||||
width: 150,
|
||||
sorter: true,
|
||||
},
|
||||
{
|
||||
title: '发布单位',
|
||||
dataIndex: 'publicOrgName',
|
||||
width: 150,
|
||||
sorter: true,
|
||||
},
|
||||
{
|
||||
title: '类型',
|
||||
dataIndex: 'type',
|
||||
width: 120,
|
||||
sorter: true,
|
||||
render: text => this.bindCodeValue(text, 'notice_type'),
|
||||
},
|
||||
{
|
||||
title: '状态',
|
||||
dataIndex: 'status',
|
||||
width: 120,
|
||||
render: text => this.bindCodeValue(text, 'notice_status'),
|
||||
},
|
||||
]
|
||||
|
||||
/**
|
||||
* 构造函数,在渲染前动态添加操作字段等
|
||||
* @param {*} props
|
||||
*/
|
||||
constructor(props) {
|
||||
super(props)
|
||||
|
||||
const flag = auth({ [authName]: [['edit'], ['changeStatus']] })
|
||||
|
||||
if (flag) {
|
||||
this.columns.push({
|
||||
title: '操作',
|
||||
width: 200,
|
||||
dataIndex: 'actions',
|
||||
render: (text, record) => (
|
||||
<QueryTableActions>
|
||||
{record.status === 1 ? (
|
||||
<Auth
|
||||
key={this.subUniqueKey(record.id, 1)}
|
||||
auth={{ [authName]: 'changeStatus' }}
|
||||
>
|
||||
<Popconfirm
|
||||
placement="topRight"
|
||||
title="是否确认撤回"
|
||||
onConfirm={() => this.onGoBack(record.id)}
|
||||
>
|
||||
<a>撤回</a>
|
||||
</Popconfirm>
|
||||
</Auth>
|
||||
) : (
|
||||
[
|
||||
<Auth
|
||||
key={this.subUniqueKey(record.id, 2)}
|
||||
auth={{ [authName]: 'edit' }}
|
||||
>
|
||||
<a onClick={() => this.onOpen(this.editForm, record.id)}>
|
||||
编辑
|
||||
</a>
|
||||
</Auth>,
|
||||
<Auth
|
||||
key={this.subUniqueKey(record.id, 3)}
|
||||
auth={{ [authName]: 'changeStatus' }}
|
||||
>
|
||||
<Popconfirm
|
||||
placement="topRight"
|
||||
title="是否确认发布"
|
||||
onConfirm={() => this.onPublish(record.id)}
|
||||
>
|
||||
<a>发布</a>
|
||||
</Popconfirm>
|
||||
</Auth>,
|
||||
<Auth
|
||||
key={this.subUniqueKey(record.id, 4)}
|
||||
auth={{ [authName]: 'changeStatus' }}
|
||||
>
|
||||
<Popconfirm
|
||||
placement="topRight"
|
||||
title="是否确认删除"
|
||||
onConfirm={() => this.onDelete(record.id)}
|
||||
>
|
||||
<a>删除</a>
|
||||
</Popconfirm>
|
||||
</Auth>,
|
||||
]
|
||||
)}
|
||||
</QueryTableActions>
|
||||
),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 阻止外部组件引发的渲染,提升性能
|
||||
* 可自行添加渲染条件
|
||||
* [必要]
|
||||
* @param {*} props
|
||||
* @param {*} state
|
||||
* @returns
|
||||
*/
|
||||
shouldComponentUpdate(props, state) {
|
||||
return !isEqual(this.state, state)
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载字典数据,之后开始加载表格数据
|
||||
* 如果必须要加载字典数据,可直接对表格设置autoLoad=true
|
||||
*/
|
||||
componentDidMount() {
|
||||
const { onLoading, onLoadData } = this.table.current
|
||||
onLoading()
|
||||
getDictData('notice_status', 'notice_type').then(codes => {
|
||||
this.setState({ codes }, () => {
|
||||
onLoadData()
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
subUniqueKey(text, index) {
|
||||
return text.substr(index, 5)
|
||||
}
|
||||
/**
|
||||
* 调用加载数据接口,可在调用前对query进行处理
|
||||
* [异步,必要]
|
||||
* @param {*} params
|
||||
* @param {*} query
|
||||
* @returns
|
||||
*/
|
||||
loadData = async (params, query) => {
|
||||
const { data } = await apiAction.page({
|
||||
...params,
|
||||
...query,
|
||||
})
|
||||
return data
|
||||
}
|
||||
|
||||
/**
|
||||
* 绑定字典数据
|
||||
* @param {*} code
|
||||
* @param {*} name
|
||||
* @returns
|
||||
*/
|
||||
bindCodeValue(code, name) {
|
||||
name = toCamelCase(name)
|
||||
const codes = this.state.codes[name]
|
||||
if (codes) {
|
||||
const c = codes.find(p => p.code == code)
|
||||
if (c) {
|
||||
return c.value
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
/**
|
||||
* 打开新增/编辑弹窗
|
||||
* @param {*} modal
|
||||
* @param {*} id
|
||||
*/
|
||||
onOpen(modal, id) {
|
||||
modal.current.open({
|
||||
id,
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 对表格上的操作进行统一处理
|
||||
* [异步]
|
||||
* @param {*} action
|
||||
* @param {*} successMessage
|
||||
*/
|
||||
async onAction(action, successMessage) {
|
||||
const { onLoading, onLoaded, onReloadData } = this.table.current
|
||||
onLoading()
|
||||
try {
|
||||
if (action) {
|
||||
await action
|
||||
}
|
||||
if (successMessage) {
|
||||
Message.success(successMessage)
|
||||
}
|
||||
onReloadData()
|
||||
} catch {
|
||||
onLoaded()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除
|
||||
* @param {*} id
|
||||
*/
|
||||
onDelete(id) {
|
||||
this.onAction(apiAction.status({ id, status: 3 }), '删除成功')
|
||||
}
|
||||
/**
|
||||
* 发布
|
||||
* @param {*} id
|
||||
*/
|
||||
onPublish(id) {
|
||||
this.onAction(apiAction.status({ id, status: 1 }), '发布成功')
|
||||
}
|
||||
/**
|
||||
* 撤回
|
||||
* @param {*} id
|
||||
*/
|
||||
onGoBack(id) {
|
||||
this.onAction(apiAction.status({ id, status: 2 }), '撤回成功')
|
||||
} //
|
||||
|
||||
render() {
|
||||
const { codes } = this.state
|
||||
return (
|
||||
<Container mode="fluid">
|
||||
<br />
|
||||
<Card bordered={false}>
|
||||
<QueryTable
|
||||
ref={this.table}
|
||||
autoLoad={false}
|
||||
loadData={this.loadData}
|
||||
columns={this.columns}
|
||||
query={
|
||||
<Auth auth={{ [authName]: 'page' }}>
|
||||
<Form.Item label="关键字" name="searchValue">
|
||||
<Input
|
||||
autoComplete="off"
|
||||
placeholder="请输入标题、内容"
|
||||
className="w-200"
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form.Item label="类型" name="type">
|
||||
<Select placeholder="请选择类型" className="w-200" allowClear>
|
||||
{codes.noticeType.map(item => (
|
||||
<Select.Option key={item.code} value={item.code}>
|
||||
{item.value}
|
||||
</Select.Option>
|
||||
))}
|
||||
</Select>
|
||||
</Form.Item>
|
||||
</Auth>
|
||||
}
|
||||
operator={
|
||||
<Auth key={record => record.id} auth={{ [authName]: 'add' }}>
|
||||
<Button
|
||||
icon={<AntIcon type="plus" />}
|
||||
onClick={() => this.onOpen(this.addForm)}
|
||||
>
|
||||
新增{name}
|
||||
</Button>
|
||||
</Auth>
|
||||
}
|
||||
/>
|
||||
</Card>
|
||||
|
||||
<Auth auth={{ [authName]: 'add' }}>
|
||||
<ModalForm
|
||||
title={`新增${name}`}
|
||||
width={800}
|
||||
action={apiAction.add}
|
||||
buttons={[
|
||||
{
|
||||
index: 1,
|
||||
button: (getData, close) => (
|
||||
<Button
|
||||
type="primary"
|
||||
key="111"
|
||||
onClick={async () => {
|
||||
const data = await getData()
|
||||
data.status = 0 //草稿状态
|
||||
this.onAction(api.sysNoticeAdd(data))
|
||||
close()
|
||||
}}
|
||||
>
|
||||
保存草稿
|
||||
</Button>
|
||||
),
|
||||
},
|
||||
]}
|
||||
ref={this.addForm}
|
||||
onSuccess={() => this.table.current.onReloadData()}
|
||||
>
|
||||
<FormBody />
|
||||
</ModalForm>
|
||||
</Auth>
|
||||
|
||||
<Auth auth={{ [authName]: 'edit' }}>
|
||||
<ModalForm
|
||||
title={`编辑${name}`}
|
||||
width={800}
|
||||
action={apiAction.edit}
|
||||
ref={this.editForm}
|
||||
onSuccess={() => this.table.current.onReloadData()}
|
||||
>
|
||||
<FormBody />
|
||||
</ModalForm>
|
||||
</Auth>
|
||||
</Container>
|
||||
)
|
||||
}
|
||||
}
|
||||
218
framework/web-react/src/pages/system/noticeReceived/index.jsx
Normal file
218
framework/web-react/src/pages/system/noticeReceived/index.jsx
Normal file
@@ -0,0 +1,218 @@
|
||||
import React, { Component } from 'react'
|
||||
import { Card, Form, Input, Select } from 'antd'
|
||||
import { Auth, Container, NoticeDetail, QueryTable } from 'components'
|
||||
import { api } from 'common/api'
|
||||
import auth from 'components/authorized/handler'
|
||||
import { isEqual } from 'lodash'
|
||||
import getDictData from 'util/dic'
|
||||
import { toCamelCase } from 'util/format'
|
||||
import { getSearchInfo, QueryType } from 'util/query'
|
||||
|
||||
/**
|
||||
* 统一配置权限标识
|
||||
* [必要]
|
||||
*/
|
||||
const authName = 'sysNotice'
|
||||
|
||||
export default class index extends Component {
|
||||
state = {
|
||||
codes: {
|
||||
noticeStatus: [],
|
||||
noticeType: [],
|
||||
readStatus: [],
|
||||
},
|
||||
}
|
||||
|
||||
// 表格实例
|
||||
table = React.createRef()
|
||||
|
||||
detail = React.createRef()
|
||||
|
||||
columns = [
|
||||
{
|
||||
title: '标题',
|
||||
dataIndex: 'title',
|
||||
width: 300,
|
||||
sorter: true,
|
||||
},
|
||||
{
|
||||
title: '发布人',
|
||||
dataIndex: 'publicUserName',
|
||||
width: 150,
|
||||
sorter: true,
|
||||
},
|
||||
{
|
||||
title: '发布时间',
|
||||
dataIndex: 'createdTime',
|
||||
width: 200,
|
||||
sorter: true,
|
||||
},
|
||||
{
|
||||
title: '发布单位',
|
||||
dataIndex: 'publicOrgName',
|
||||
width: 200,
|
||||
sorter: true,
|
||||
},
|
||||
{
|
||||
title: '类型',
|
||||
dataIndex: 'type',
|
||||
width: 120,
|
||||
render: text => this.bindCodeValue(text, 'notice_type'),
|
||||
},
|
||||
{
|
||||
title: '已读未读',
|
||||
dataIndex: 'readStatus',
|
||||
width: 120,
|
||||
render: text => this.bindCodeValue(text, 'read_status'),
|
||||
},
|
||||
]
|
||||
|
||||
/**
|
||||
* 构造函数,在渲染前动态添加操作字段等
|
||||
* @param {*} props
|
||||
*/
|
||||
constructor(props) {
|
||||
super(props)
|
||||
const flag = auth({ [authName]: 'received' })
|
||||
if (flag) {
|
||||
this.columns.push({
|
||||
title: '操作',
|
||||
width: 150,
|
||||
dataIndex: 'actions',
|
||||
render: (text, record) => (
|
||||
<Auth auth={{ [authName]: 'received' }}>
|
||||
<a onClick={() => this.detail.current.onOpenDetail(record.id)}>查看</a>
|
||||
</Auth>
|
||||
),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 阻止外部组件引发的渲染,提升性能
|
||||
* 可自行添加渲染条件
|
||||
* [必要]
|
||||
* @param {*} props
|
||||
* @param {*} state
|
||||
* @returns
|
||||
*/
|
||||
shouldComponentUpdate(props, state) {
|
||||
return !isEqual(this.state, state)
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载字典数据,之后开始加载表格数据
|
||||
* 如果必须要加载字典数据,可直接对表格设置autoLoad=true
|
||||
*/
|
||||
componentDidMount() {
|
||||
const { onLoading, onLoadData } = this.table.current
|
||||
onLoading()
|
||||
getDictData('notice_status', 'notice_type', 'read_status').then(codes => {
|
||||
this.setState({ codes }, () => {
|
||||
onLoadData()
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 调用加载数据接口,可在调用前对query进行处理
|
||||
* [异步,必要]
|
||||
* @param {*} params
|
||||
* @param {*} query
|
||||
* @returns
|
||||
*/
|
||||
loadData = async (params, query) => {
|
||||
const searchInfo = getSearchInfo({
|
||||
query,
|
||||
queryType: {
|
||||
type: QueryType.Equal,
|
||||
title: QueryType.Like,
|
||||
readStatus: QueryType.Equal,
|
||||
},
|
||||
})
|
||||
|
||||
const { data } = await api.sysNoticeReceived({
|
||||
...params,
|
||||
searchInfo,
|
||||
})
|
||||
return data
|
||||
}
|
||||
|
||||
/**
|
||||
* 绑定字典数据
|
||||
* @param {*} code
|
||||
* @param {*} name
|
||||
* @returns
|
||||
*/
|
||||
bindCodeValue(code, name) {
|
||||
name = toCamelCase(name)
|
||||
const codes = this.state.codes[name]
|
||||
if (codes) {
|
||||
const c = codes.find(p => p.code == code)
|
||||
if (c) {
|
||||
return c.value
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
/**
|
||||
* 打开新增/编辑弹窗
|
||||
* @param {*} modal
|
||||
* @param {*} id
|
||||
*/
|
||||
onOpen(modal, id) {
|
||||
modal.current.onOpenDetail(id)
|
||||
}
|
||||
|
||||
render() {
|
||||
const { codes } = this.state
|
||||
return (
|
||||
<Container mode="fluid">
|
||||
<br />
|
||||
<Card bordered={false}>
|
||||
<QueryTable
|
||||
ref={this.table}
|
||||
autoLoad={false}
|
||||
loadData={this.loadData}
|
||||
columns={this.columns}
|
||||
query={
|
||||
<Auth auth={{ [authName]: 'received' }}>
|
||||
<Form.Item label="关键字" name="title">
|
||||
<Input
|
||||
autoComplete="off"
|
||||
placeholder="请输入标题"
|
||||
className="w-200"
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form.Item label="类型" name="type">
|
||||
<Select placeholder="请选择类型" className="w-200" allowClear>
|
||||
{codes.noticeType.map(item => (
|
||||
<Select.Option key={item.code} value={item.code}>
|
||||
{item.value}
|
||||
</Select.Option>
|
||||
))}
|
||||
</Select>
|
||||
</Form.Item>
|
||||
<Form.Item label="已读未读" name="readStatus">
|
||||
<Select
|
||||
placeholder="请选择是否已读"
|
||||
className="w-200"
|
||||
allowClear
|
||||
>
|
||||
{codes.readStatus.map(item => (
|
||||
<Select.Option key={item.code} value={item.code}>
|
||||
{item.value}
|
||||
</Select.Option>
|
||||
))}
|
||||
</Select>
|
||||
</Form.Item>
|
||||
</Auth>
|
||||
}
|
||||
/>
|
||||
</Card>
|
||||
<NoticeDetail ref={this.detail} />
|
||||
</Container>
|
||||
)
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user