Skip to content

Instantly share code, notes, and snippets.

@wisetc
Created September 17, 2019 06:16
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save wisetc/324a5af68bcd3e20ba914c530775d877 to your computer and use it in GitHub Desktop.
Save wisetc/324a5af68bcd3e20ba914c530775d877 to your computer and use it in GitHub Desktop.
Complex interaction with ajax. 纠结的判断。
import React, { Component, Fragment } from 'react';
import { Dialog } from 'saltui';
import { observer } from 'mobx-react';
import { toJS } from 'mobx';
import {
Viewport,
InputField,
DataList,
NoContext,
PickerField,
DatetimeField,
ToggleForm,
WhichField,
} from 'components/';
import { Link } from 'react-router-dom';
import { withLastLocation } from 'react-router-last-location';
import './ContractForm.scss';
import {
house,
houseBelongs,
workflow,
form as formState,
dingtalk,
houseEarnest,
} from 'src/store';
import { enum2Options } from 'src/lib/utils';
import {
certificateType as certificateTypeEnum,
cycle as cycleEnum,
source as sourceEnum,
renterType as renterTypeEnum,
} from '../enums';
import {
feedback,
utils,
debug, // eslint-disable-line
getUserId,
getUsername,
getPhone,
form as formApi,
} from 'src/lib';
import API, { form as formAPI } from 'src/xhr';
import CommentSection from '../components/CommentSection';
import MetaFeeSection from './components/MetaFeeSection';
import ReservationPicker from './components/ReservationPicker';
import CheckIn from './components/CheckIn';
import Terms from './components/Terms';
import BookingEarnest from 'src/pages/contract/BookingEarnest/BookingEarnest';
import ErrorWrapper from './ErrorWrapper';
import { getOtherFeeList } from 'src/pages/common/TaskDetail/fields-renderer';
import formDef from 'src/xhr/form-definition/rent/form-new-rent-request-def.json';
const isBlank = val => {
return val === null || typeof val === 'undefined' || val === '';
};
const timeRangeMonths = [
'ONE_MONTH',
'TWO_MONTH',
'THREE_MONTH',
'FOUR_MONTH',
'FIVE_MONTH',
'HALF_YEAR',
'SEVEN_MONTH',
'EIGHT_MONTH',
'NINE_MONTH',
'TEN_MONTH',
'ELEVEN_MONTH',
'ONE_YEAR',
];
const contractRelatedEnum = [
'FD_END',
'FD_BEFORE_ONE',
'FD_AFTER_ONE',
'FD_AFTER_TWO',
'FD_AFTER_THREE',
];
@observer
@withLastLocation
class ContractForm extends Component {
postMethod = API.processInstance.create;
actionName = '合同新建';
redirectMethod = () => this.props.history.replace('/');
state = {
form: {
renterType: 'COMMON',
houseSpaceId: house.houseSpaceId,
houseSpaceName: house.keywordAddress,
unitId: house.zoneId,
contractType: 'RENT',
signType: 'NEW',
loginId: getUserId(),
housekeeperId: getUserId(),
enterId: getUserId(),
housekeeperName: getUsername(),
housekeeperPhone: getPhone(),
},
isPreviewing: false, // 处于预览状态
needNation: false, // 当证件类型为护照时需要上传国籍
keepTime: null, // 房源有预定时,预定的有效期截止时间
isHousePriceLoading: false,
canCreateContract: true,
userInfoLocked: false, // 用户基本信息是否不可编辑
reservationLocked: false, // 预定信息是否不可编辑
otherFeeList: [], // 物业费、网管费等其他费用
reservationOptions: [],
timeRangeOptions: [],
step: 0, // 初始第 0 步,填写合同表单
openDays: 0, // 合同开始时间宽松天数
isDepositRatioNil: false, // 是否押金比率自定义
};
reservationId = null;
formDefinitionId = null;
priceAdjusted = false;
depositRatio = 1; // 编辑价格中押金为相对租金的倍率
firstCutDiscount = null; // 首月减免上限倍率表示实际首月减免的额度不能大于租金与改值之积,可为null
hasEarnestHandleWorkflow = false; // 当前房源如果有预定时,该预定是否有预定定金正在处理
constructor(props) {
super(props);
// preserve it in case value changed.
this.isFromBookingEarnest = utils.isFromPath.call(
this,
/^\/booking-detail\/\w+\/earnest-deal$/
);
}
handleReservationPick = item => {
const form = this.state.form;
const { reservationId, earnest, userName, userPhone } = item;
this.reservationId = reservationId;
form.userName = userName;
form.userPhone = userPhone;
form.earnest = earnest / 1000;
debug.log(form);
this.setState({ reservationOptions: [], form, keepTime: item.keepTime });
};
componentDidMount() {
this.loadInitialState();
house._received && this.loadContractTimeRangeOptions();
this.getOpenDays();
}
componentWillUnmount() {
houseBelongs.clear();
houseEarnest.destroyHouseEarnest();
formState.destroy();
}
getOpenDays() {
if (house.houseSpaceManagerId) {
formAPI
.getCompany(house.houseSpaceManagerId)
.then(companyName => {
let openDays = 0;
if (companyName.includes('顾寓')) {
openDays = 30;
}
this.setState({ openDays });
})
.catch(_ => _);
}
}
loadInitialState() {
// 当房源状态为已预定(RESERVED)时,根据房源id获取并保存预定信息
if (utils.isFromHouseDetail.call(this) && house._received) {
if (house.houseSpaceStatus === 'RESERVED') {
formAPI
.getReservation(house)
.then(reservation => {
console.log({ reservation });
this.setRevervationDetail(reservation);
})
.catch(e => {
console.error(e.message);
});
}
} else if (this.isFromBookingEarnest && houseEarnest._received) {
this.setRevervationDetail(houseEarnest);
} else if (utils.isFromWorkflow.call(this) && workflow._received) {
// 如果来自流程,则从流程中获取并设置相关数据
this.setEssentialData();
}
}
loadContractTimeRangeOptions() {
API.utils
.getContractTimeRangeOptions({ houseSpaceId: house.houseSpaceId })
.then(res => {
if (res.data.code !== 200) {
feedback.error(`获取租期时长选项失败。${res.data.message}`);
return;
}
const getlist = res.data.data.newCrtTimeSoltList;
if (Array.isArray(getlist)) {
const order = {
ONE_YEAR: 1,
HALF_YEAR: 2,
};
this.setState({
timeRangeOptions: getlist
.map(item => ({
text: item.value,
value: item.name,
order:
typeof order[item.name] === 'undefined'
? 99
: order[item.name],
}))
.sort((a, b) => a.order - b.order),
});
}
});
}
setRevervationDetail(reservation, callback = utils.noop) {
const {
reservationId,
reservationStatus,
userName,
userPhone,
earnest,
keepTime,
} = reservation;
this.reservationId = reservationId;
const { form } = this.state;
form.userName = userName;
form.userPhone = userPhone;
form.earnest = earnest ? earnest / 1000 : undefined;
if (reservationStatus !== 'HANDLER' && reservationStatus !== 'ALPAY') {
feedback.error('该定金有流程正在进行中,请先处理完流程再创建合同');
this.props.history.go(-1);
return;
} else if (
reservationStatus === 'HANDLER' &&
workflow.formKey !== 'form-earnest-deal-request'
) {
// 如果预定状态是系统发起的流程定金在处理中,但是进入本页面没有经过定金处理流程页面
feedback.error('请从定金处理流程 - 定金处理任务详情进入');
this.props.history.go(-1);
return;
}
// 保存定金处理 - 创建合同表单信息
// 不要与修改合同中保存的表单信息冲突,先取修改合同的表单信息,再存定金处理
formState.init({
bkAddress: reservation.houseSpaceName,
bkEarnest: earnest ? earnest / 1000 : undefined,
bkFailureTime: keepTime,
bkHouseId: reservation.houseSpaceId,
bkReservationId: reservationId,
bkSaleId: reservation.enterId,
bkSaleName: reservation.enterName,
bkSalePhone: reservation.enterPhone,
bkType: JSON.stringify({ name: '创建合同', id: 'BUCON' }),
bkUserName: userName,
bkUserPhone: userPhone,
});
this.hasEarnestHandleWorkflow = reservationStatus === 'HANDLER';
this.setState({ keepTime, form, reservationLocked: true }, callback);
}
setEssentialData() {
const variables = toJS(formState.variables);
const form = utils.reMapObj(
formApi.parseDropdownFields(formDef, variables),
'nr',
true
);
form.renterType = form.renterType.value;
// 其他的参数解析
const otherFeeList = getOtherFeeList(form.reqChargInputLogBeanList); // eslint-disable-line
this.setState({ otherFeeList });
this.setState({ form, userInfoLocked: true });
const articlesLists = utils.tryParseArrayT(form.articlesLists);
const energyConsumption = utils.tryParseArrayT(form.energyConsumption);
const idcardImages = utils.tryParseArrayT(form.idcardImages);
houseBelongs.setArticlesLists(articlesLists);
houseBelongs.setEnergyConsumption(energyConsumption);
houseBelongs.setIdcardImages(idcardImages);
formApi.getFormDef(workflow).then(({ id }) => {
this.formDefinitionId = id;
});
API.houseSpace.retrieve(form.houseSpaceId).then(res => {
if (res.data.code !== 200) {
feedback.error(`获取房源详情失败,${res.data.message}`);
return;
}
house.setHouse({ ...res.data.data });
this.forceUpdate();
});
}
handleChange(field, value) {
this.setState({
form: {
...this.state.form,
[field]: value,
},
});
}
handlePick(field, [item]) {
this.handleChange(field, item);
}
mergeForm(partialForm) {
this.setState({
form: {
...this.state.form,
...partialForm,
},
});
}
// 如果用户编辑价格,要求价格完整
validatePriceForm(form) {
const { rental, deposit, firstCreditAmount } = form;
if (!this.canAdjustPrice) return null;
if (
this.priceAdjusted &&
!((!!rental || rental === '0') && (!!deposit || Number(deposit) === 0))
)
return new Error('调整房租价格填写不完整');
else if (
this.priceAdjusted &&
(firstCreditAmount === null || firstCreditAmount === '')
) {
return new Error('请填写实际首期减免额');
}
return null;
}
validate(form) {
const priceFormError = this.validatePriceForm(this.state.form);
if (priceFormError) {
return priceFormError;
}
const required = {
idcardImages: '请上传证件照片',
userName: '请输入姓名',
sex: '请选择性别',
userPhone: '请输入手机号',
certificateType: '请选择证件类型',
certificateNum: '请输入证件号码',
urgentPhone: '请输入紧急联系人电话',
urgentRelationship: '请输入紧急联系人关系',
startTime: '请选择租期开始时间',
endTime: '请获取计算所得退租时间',
cycle: '请选择支付周期',
rental: '请获取租金',
deposit: '请获取押金',
source: '请选择租客来源',
allowNumber: '请选择最多可居住人数',
livesNumber: '请选择实际居住人数',
articlesLists: '请填写物品清单',
};
for (let k in required) {
if (utils.isEmpty(form[k])) {
if (k === 'rental' && form[k] === 0) return null;
else if (k === 'deposit' && form[k] === 0) return null;
return new Error(`${required[k]}。`);
}
}
// 手机号格式校验
if (!/^1\d{10}$/.test(form.userPhone)) {
return new Error(
`您输入的手机号为[${form.userPhone}],手机号格式不正确。`
);
}
// 押金不可低于租金,合同无法提交。
const { rental, deposit } = this.state.form;
if (
!this.state.isDepositRatioNil &&
!!rental &&
!!deposit &&
deposit < rental
)
return new Error('押金不可低于租金,合同无法提交。');
const otherFeeList = form.reqChargInputLogBeanList.filter(
item => item.chargType !== 'REN'
);
if (!utils.isEmpty(otherFeeList)) {
const err = MetaFeeSection.validate(otherFeeList);
if (err) return err;
}
return null;
}
getForm() {
const {
ruleRental,
ruleDeposit,
ruleFirstCreditAmount,
userPhone,
} = this.state.form;
let { renterType, rental, deposit, firstCreditAmount } = this.state.form;
// 租客类型
renterType = {
value: renterType,
text: renterTypeEnum[renterType],
};
rental = rental || ruleRental;
deposit = deposit === 0 ? 0 : deposit || ruleDeposit;
firstCreditAmount =
firstCreditAmount === 0 ? 0 : firstCreditAmount || ruleFirstCreditAmount;
const rentalDiff = ruleRental - rental;
const firstCreditAmountDiff = firstCreditAmount - ruleFirstCreditAmount;
const idcardImages = toJS(houseBelongs.idcardImages).filter(Boolean);
return {
...this.state.form,
signType: '{"id":"NEW","name":"新签合同"}',
contractType: '{"id":"RENT","name":"出租合同"}',
renterType,
idcardImages,
rental,
deposit,
firstCreditAmount,
rentalDiff,
firstCreditAmountDiff,
userPhone: typeof userPhone === 'string' ? userPhone.trim() : userPhone,
reservationId: this.reservationId || undefined,
...toJS(this.props.houseBelongs.form),
...this.getReqChargInputLogBeanList(),
};
}
dealWithMoney(variables) {
const ret = {};
// 金额后台要求1000倍
const moneyFields = [
'earnest',
'rental',
'deposit',
'firstCreditAmount',
'ruleRental',
'ruleDeposit',
'ruleFirstCreditAmount',
'rentalDiff',
'firstCreditAmountDiff',
];
for (let k of Object.getOwnPropertyNames(variables)) {
const value = variables[k];
if (moneyFields.includes(k)) {
ret[k] = Number(value); // * 1000;
} else if (k === 'reqChargInputLogBeanList' && value instanceof Array) {
ret[k] = value.map(fee => ({
...fee,
initialAmount: Number(fee.initialAmount), // * 1000,
}));
} else {
ret[k] = value;
}
}
return ret;
}
async beforePost(variables) {
const _variables = formApi.stringifyDropdownFields(
formDef,
formApi.stringifyArray(
utils.reMapObj(this.dealWithMoney(variables), 'nr')
)
);
let ret = {
initiator: getUserId(),
processBusinessKey: 'process-new-rent-v5',
processInstanceName: 'form-new-rent-request',
processScopeId: house.zoneId, // zoneId
variables: _variables,
};
// 更新出租流程
if (workflow._received && !this.isFromBookingEarnest) {
let taskId = workflow.taskId;
let formDefinitionId = this.formDefinitionId;
if (workflow.formKey !== 'form-new-rent-request') {
const variables = {
nrChooseType: '{"id":"UPDATE","name":"修改合同"}',
nrEnterId: getUserId(),
nrMemo: '',
};
await formAPI.updatePendingTask(variables);
taskId = await formAPI.getNextTaskIdByInstance(workflow);
const res = await API.pendingTask.retrieve(taskId);
const task = res.data.data;
workflow.setWorkflow(task);
const newFormDef = await formApi.getFormDef(workflow);
formDefinitionId = newFormDef.id;
}
ret = {
...utils.getFormByDef(formDef),
formDefinitionId,
taskId,
variables: _variables,
};
this.postMethod = API.pendingTask.update;
this.actionName = '合同修改';
}
return ret;
}
// 上一步
previous(e) {
e.preventDefault();
let { step } = this.state;
step--;
this.setState({ step });
}
// 下一步
next(e) {
e.preventDefault();
let { step } = this.state;
step++;
this.setState({ step });
}
submit(e) {
e.preventDefault();
const form = this.getForm();
const error = this.validate(form);
if (error) {
feedback.warning(error.message);
return;
}
this._submit(form);
}
async _submit(form) {
const _form = await this.beforePost(form);
const loading = feedback.loading();
this.postMethod(_form)
.then(res => {
if (res.data.code !== 200) {
feedback.error(`${this.actionName}失败。${res.data.message}。`);
return;
}
feedback.success(`${this.actionName}成功`, () => {
if (typeof this.successCallback === 'function') {
this.successCallback();
} else {
this.redirectMethod();
}
});
})
.finally(loading);
}
resetReservation(callback = utils.noop) {
const { form } = this.state;
form.earnest = null;
this.reservationId = null;
this.setState({ form }, callback);
}
handlePhoneInput(field, phone) {
this.resetReservation(() => {
this.handleChange(field, phone);
});
const phoneNum = 11;
if (house.houseSpaceStatus !== 'RESERVED' && phone.length === phoneNum) {
API.reservation.list({ phone }, true).then(res => {
if (res.data.code !== 200) {
debug.log(`根据手机号获取定金失败,${res.data.message}`);
return;
}
if (res.data.data && res.data.data.length) {
this.phoneInputEl.blur();
Dialog.confirm({
title: '提示',
content: '是否确定将其他房间的定金结转过来',
onConfirm: () => {
this.setState({
reservationOptions: res.data.data,
});
},
});
debug.log(res.data);
}
});
}
}
handleUpdate(otherFeeList) {
this.setState({ otherFeeList });
}
// 计算费用
getReqChargInputLogBeanList() {
let { startTime, endTime, cycle, rental, ruleRental } = this.state.form;
rental = rental || ruleRental;
let reqChargInputLogBeanList = [
{
chargType: 'REN',
businessType: 'RENT',
financeType: 'INC',
startChargTime: startTime,
endChargTime: endTime,
initialAmount: rental,
cycle: cycle ? cycle.value : undefined,
addCycle: 'NORISE',
addType: 'NOTRISE',
},
];
// 其他费用(选填)
const { otherFeeList } = this.state;
if (!utils.isEmpty(otherFeeList)) {
reqChargInputLogBeanList = reqChargInputLogBeanList.concat(otherFeeList);
}
return { reqChargInputLogBeanList };
}
loadHousePrice(field, value) {
const { cycle, endTime, startTime, renterType } = this.state.form;
const params = { cycle, endTime, startTime, renterType };
params[field] = value;
const paramsValid = Object.values(params).every(
item => !!item || item === false
);
if (paramsValid) {
this.setState({ isHousePriceLoading: true });
const { timeRange } = this.state.form;
const index = timeRange && timeRange.value ? timeRange.value : null;
const { type } = this.getTimeRange(index);
API.housePrice
.retrieve({
contractSignType: 'NEW',
enumPayCycle: params.cycle.value,
houseId: house.houseSpaceId,
contractEndEnum: type === 'MONTH' ? undefined : type,
payStartTime: params.startTime,
payEndTime: params.endTime,
renterRoleEnum: params.renterType,
})
.then(res => {
if (res.data.data === null || res.data.code !== 200) {
feedback.error(`营销规则获取失败。${res.data.message}。`);
const form = {
...this.state.form,
ruleDeposit: null,
ruleRental: null,
ruleFirstCreditAmount: null,
};
this.setState({ form });
return;
}
const {
depositByMonth: ruleDeposit,
monthPrice: ruleRental,
firstCutPrice: ruleFirstCreditAmount,
firstCutDiscount,
} = res.data.data.priceByPaymentResponse;
const { activitysId: saleContentId, description } = res.data.data;
const form = {
...this.state.form,
ruleDeposit: ruleDeposit / 1000,
ruleRental: ruleRental / 1000,
ruleFirstCreditAmount: ruleFirstCreditAmount
? ruleFirstCreditAmount / 1000
: 0,
saleContentId,
description, // 营销规则ID
};
this.firstCutDiscount = firstCutDiscount;
this.setState({ form });
})
.finally(() => {
this.resetAdjustPriceForm();
this.setState({ isHousePriceLoading: false });
});
} else {
const form = {
...this.state.form,
ruleDeposit: null,
ruleRental: null,
ruleFirstCreditAmount: null,
};
this.setState({ form });
}
}
// 租期时长
getTimeRange(index) {
let type = 'MONTH',
value = String(index);
if (contractRelatedEnum.includes(index)) {
type = index;
value = house.houseSpaceId;
} else {
const i = timeRangeMonths.indexOf(index);
if (i > -1) {
type = 'MONTH';
value = String(i + 1);
} else {
console.warn('租期时长,查无此月');
}
}
return {
type,
value,
};
}
loadEndTime(nowTime, index, callback = utils.noop) {
if (!nowTime || !index) {
callback.bind(this);
return;
}
const { type: contractEndTimeType, value } = this.getTimeRange(index);
API.utils
.getContractEndTime({
contractEndTimeType,
nowTime,
value,
})
.then(res => {
let endTime = null;
if (res.data.data !== null) {
endTime = res.data.data;
} else {
feedback.error(`计算退租时间失败。${res.data.message}`);
}
this.setState(
{
form: {
...this.state.form,
endTime,
},
},
callback.bind(this, endTime)
);
});
}
handleTimeRangePick(field, value) {
// 重置支付周期字段,保存租期时长字段
this.setState({
form: {
...this.state.form,
cycle: null,
[field]: value[0],
},
});
// 获取租期时长的值
const index = value[0].value;
// 从合同服务加载退租时间
this.loadEndTime(this.state.form.startTime, index, endTime => {
this.loadHousePrice('endTime', endTime);
});
}
handleStartTimePick(field, value, callback = utils.noop) {
this.handleChange(field, value);
const { timeRange } = this.state.form;
const index = timeRange && timeRange.value ? timeRange.value : null;
this.loadEndTime(value, index, callback);
}
preview(e) {
e.preventDefault();
this.setState({ isPreviewing: !this.state.isPreviewing });
}
// 证件类型切换
handleCertTypePick(field, [item]) {
// 护照
const needNation = item.value === 'P';
this.setState({ needNation });
// 非护照时不提交国籍字段
if (!needNation) {
const { form } = this.state;
form.nationality = undefined;
this.setState(form);
}
this.handlePick(field, [item]);
}
get successCallback() {
return this.hasEarnestHandleWorkflow
? BookingEarnest.terminateWorkflow.bind(
{},
() => {
// feedback.success('已结束定金对应定金处理流程');
window.location.href = '#/unhandled';
},
data => {
feedback.error(`定金处理流程结束失败。${data.message}`);
}
)
: null;
}
// 根据字段租期时长过滤支付周期选项
get filteredCycleEnum() {
let optionsAll = enum2Options(cycleEnum);
if (!this.state.form.timeRange) return optionsAll;
let cycleTextArr = null;
switch (this.state.form.timeRange.value) {
case 'ONE_MONTH':
case 'TWO_MONTH':
cycleTextArr = ['月付'];
break;
case 'THREE_MONTH':
case 'FOUR_MONTH':
case 'FIVE_MONTH':
cycleTextArr = ['月付', '季付'];
break;
case 'HALF_YEAR':
case 'SEVEN_MONTH':
case 'EIGHT_MONTH':
case 'NINE_MONTH':
case 'TEN_MONTH':
case 'ELEVEN_MONTH':
cycleTextArr = ['月付', '季付', '半年付'];
break;
case 'ONE_YEAR':
cycleTextArr = ['月付', '季付', '半年付', '年付'];
break;
default:
cycleTextArr = ['月付', '季付', '半年付', '年付'];
break;
}
return optionsAll.filter(option =>
cycleTextArr.some(text => option.text === text)
);
}
// 获取起租时间的日期的选择范围
get startTimeBoundary() {
if (this.state.isDepositRatioNil) return {};
const now = new Date().getTime();
const oneDay = 24 * 60 * 60 * 1000;
const minDate = now - (this.state.openDays + 1) * oneDay;
// 直接建合同,起租时间只能是从今天起的7天时间
// 基于有预定的情形建合同,keepTime为日期时间,故不考虑组件跨天
const maxDate = !this.reservationId
? now + 6 * oneDay
: this.state.keepTime;
return {
minDate,
maxDate,
};
}
get canAdjustPrice() {
// 租客类别为公司员工/集团员工/因公住宿员工,没有调整房租按钮
const { renterType } = this.state.form;
if (renterType === 'COMMON') return true;
else if (
renterType === 'SUIYU_WORKER' ||
renterType === 'DEXIN_WORKER' ||
renterType === 'OFFICIAL_WORKER'
)
return false;
else return false;
}
get hasFirstCreditAmount() {
// 公司员工/集团员工,不存在首期减免问题,直接按照不含服务费的房源价格来
const { renterType } = this.state.form;
if (renterType === 'SUIYU_WORKER' || renterType === 'DEXIN_WORKER')
return false;
else return true;
}
resetAdjustPriceForm() {
const form = {
...this.state.form,
deposit: null,
rental: null,
firstCreditAmount: null,
};
this.setState({ form });
}
resetFirstCreditAmountAndError(errorMessage) {
feedback.error(errorMessage);
this.setState({
form: {
...this.state.form,
firstCreditAmount: 0,
},
});
}
prepare = e => {
e.preventDefault();
const form = this.getForm();
const error = this.validate(form);
if (error) {
feedback.warning(error.message);
return;
}
this.next(e);
};
renderForm() {
const { keywordAddress } = house;
let {
form: {
renterType,
userName,
startTime,
endTime,
ruleRental,
ruleDeposit,
ruleFirstCreditAmount,
rental,
deposit,
firstCreditAmount,
sex,
userPhone,
certificateType,
certificateNum,
earnest,
nationality,
urgentPhone,
urgentRelationship,
timeRange,
cycle,
source,
allowNumber,
livesNumber,
memo,
},
isPreviewing,
needNation,
isHousePriceLoading,
otherFeeList,
timeRangeOptions,
} = this.state;
const {
reservationLocked,
userInfoLocked,
reservationOptions,
} = this.state;
const imagesLen = toJS(houseBelongs.idcardImages).length;
const articlesLen = toJS(houseBelongs.articlesLists).length;
const energyConsumptionLen = toJS(houseBelongs.energyConsumption).length;
return (
<div className="contract-form">
{reservationOptions && reservationOptions.length > 0 && (
<ReservationPicker
data={reservationOptions}
onPick={this.handleReservationPick}
onClose={() => {
this.setState({ reservationOptions: [] });
}}
/>
)}
<InputField label="房源名称" value={keywordAddress} readOnly />
<DataList title="租客信息">
<PickerField
label="租客类型"
placeholder="请选择租客类型"
data={enum2Options(renterTypeEnum)}
value={renterType}
onPick={([{ value }]) => {
this.loadHousePrice('renterType', value);
this.handlePick('renterType', [value]);
}}
readOnly={isPreviewing}
/>
<PickerField
label="证件照片"
placeholder={imagesLen > 0 ? '已上传' : '请上传证件照片'}
data={[]}
onNavigate={() => {
this.props.history.push('/contract-form/uploader-form');
}}
/>
<InputField
label="姓名"
placeholder="请输入姓名"
maxLength="250"
defaultValue={userName}
onChange={this.handleChange.bind(this, 'userName')}
readOnly={reservationLocked || userInfoLocked || isPreviewing}
/>
<PickerField
label="性别"
placeholder="请选择性别"
data={[{ text: '男', value: 'M' }, { text: '女', value: 'F' }]}
value={sex}
onPick={this.handlePick.bind(this, 'sex')}
readOnly={userInfoLocked || isPreviewing}
/>
<InputField
label="手机号"
placeholder="请输入手机号"
defaultValue={userPhone}
maxLength="12"
ref={el => (this.phoneInputEl = el)}
onChange={this.handlePhoneInput.bind(this, 'userPhone')}
readOnly={reservationLocked || userInfoLocked || isPreviewing}
/>
<PickerField
label="证件类型"
placeholder="请选择证件类型"
value={certificateType}
data={enum2Options(certificateTypeEnum)}
onPick={this.handleCertTypePick.bind(this, 'certificateType')}
readOnly={userInfoLocked || isPreviewing}
/>
<InputField
label="证件号"
placeholder="请输入证件号"
defaultValue={certificateNum}
maxLength="25"
onChange={this.handleChange.bind(this, 'certificateNum')}
readOnly={userInfoLocked || isPreviewing}
/>
{!!earnest && (
<InputField label="定金抵扣押金" value={earnest} readOnly />
)}
{needNation && (
<InputField
label="国籍"
placeholder="请输入国籍"
defaultValue={nationality}
maxLength="25"
onChange={this.handleChange.bind(this, 'nationality')}
readOnly={isPreviewing}
/>
)}
<InputField
label="紧急联系人电话"
placeholder="请输入紧急联系人电话"
defaultValue={urgentPhone}
maxLength="11"
onChange={this.handleChange.bind(this, 'urgentPhone')}
readOnly={isPreviewing}
/>
<InputField
label="紧急联系人关系"
placeholder="请输入紧急联系人关系"
defaultValue={urgentRelationship}
maxLength="10"
onChange={this.handleChange.bind(this, 'urgentRelationship')}
readOnly={isPreviewing}
/>
</DataList>
<DataList title="租期信息">
<DatetimeField
label="起租时间"
placeholder="请选择起租时间"
dateOnly
value={startTime}
{...this.startTimeBoundary}
onChange={_value => {
this.handleStartTimePick('startTime', _value, () => {
this.loadHousePrice('startTime', _value);
});
}}
readOnly={isPreviewing}
/>
<PickerField
label="租期时长"
placeholder="请选择租期时长"
value={timeRange}
data={timeRangeOptions}
onPick={this.handleTimeRangePick.bind(this, 'timeRange')}
readOnly={isPreviewing}
/>
<DatetimeField
label="退租时间"
dateOnly
readOnly
value={endTime}
onChange={value => {
this.loadHousePrice('endTime', value);
this.setState({
form: {
...this.state.form,
endTime: value,
timeRange: null,
},
});
}}
/>
<PickerField
label="支付周期"
placeholder="请选择支付周期"
data={this.filteredCycleEnum}
value={cycle}
onPick={_value => {
this.loadHousePrice('cycle', _value[0]);
this.handlePick('cycle', _value);
}}
readOnly={isPreviewing}
/>
{!isHousePriceLoading && (
<Fragment>
{!isBlank(ruleRental) && (
<InputField
label="规则租金"
value={ruleRental}
suffix="元/月"
readOnly
maxLength="10"
/>
)}
{!isBlank(ruleDeposit) && (
<InputField
label="规则押金"
value={ruleDeposit}
suffix="元"
readOnly
maxLength="10"
/>
)}
{this.hasFirstCreditAmount && !isBlank(ruleFirstCreditAmount) && (
<InputField
label="规则首期减免额"
value={ruleFirstCreditAmount}
suffix="元"
readOnly
maxLength="10"
/>
)}
</Fragment>
)}
</DataList>
{this.canAdjustPrice && (
<ToggleForm
title="调整房租(调低租金需经过审批租客方能签订合同)"
expanded={!!rental || !!firstCreditAmount}
onToggle={(expanded, closeMethod) => {
if (isBlank(ruleRental)) {
feedback.warning('请先获取房源规则租金');
closeMethod();
return;
}
this.priceAdjusted = expanded;
// 如果关闭,重置数据
if (!expanded) {
this.resetAdjustPriceForm();
} else {
const { form } = this.state;
form.firstCreditAmount = null;
this.setState({ form });
}
}}
readOnly={isPreviewing}
>
<InputField
label="租金"
placeholder="请输入实际出租价格"
value={rental}
suffix="元/月"
pattern={/^\d+(\.\d{0,2})?$/}
maxLength="25"
onChange={value => {
const { form, isDepositRatioNil } = this.state;
form.rental = value;
if (!isDepositRatioNil) {
form.deposit = value * this.depositRatio;
}
// 重置被依赖项之实际首期减免
form.firstCreditAmount = null;
this.setState({
form,
});
}}
readOnly={isPreviewing}
/>
<InputField
label="租金价差"
placeholder=""
value={!!rental || rental === 0 ? ruleRental - rental : null}
suffix="元/月"
readOnly={true}
/>
<WhichField
label={`押金${
typeof deposit === 'number' ? ' = ' + deposit + '元' : ''
}`}
data={[
{ text: '1倍租金', value: 1 },
{ text: '1.5倍租金', value: 1.5 },
{ text: '自定义', value: null },
]}
onChange={item => {
const value = item.value;
let { form, isDepositRatioNil } = this.state;
if (value !== null) {
this.depositRatio = value;
form.deposit = form.rental * value;
isDepositRatioNil = false;
} else {
isDepositRatioNil = true;
}
this.setState({
form,
isDepositRatioNil,
});
}}
/>
{this.state.isDepositRatioNil && (
<InputField
label="自定义押金"
suffix="元"
placeholder="请输入自定义押金"
pattern={/^\d+(\.\d{0,2})?$/}
maxLength="25"
onChange={value => {
const { form } = this.state;
form.deposit = Number(value);
this.setState({ form });
}}
/>
)}
<InputField
label="实际首期减免额"
placeholder="请输入首期减免额"
pattern={/^\d+(\.\d{0,2})?$/}
value={firstCreditAmount}
suffix="元"
maxLength="25"
onChange={value => {
const { firstCutDiscount } = this;
const { rental } = this.state.form;
if (
(firstCutDiscount || firstCutDiscount === 0) &&
!isNaN(firstCreditAmount) &&
rental &&
value > (Number(firstCutDiscount) * rental) / 100
) {
this.resetFirstCreditAmountAndError(
'实际首期减免额大于实际租金下允许的减免,无法提交合同'
);
return;
} else if (
firstCutDiscount === null &&
Number(value) > Number(ruleFirstCreditAmount)
) {
this.resetFirstCreditAmountAndError(
'实际首期减免额大于规则首期减免额,无法提交合同'
);
return;
}
this.handleChange('firstCreditAmount', value);
}}
readOnly={isPreviewing}
/>
</ToggleForm>
)}
<DataList title="其他费用(选填)">
<MetaFeeSection
value={otherFeeList}
startChargTime={startTime || 0}
endChargTime={endTime || 0}
cycleOptions={this.filteredCycleEnum}
onUpdate={this.handleUpdate.bind(this)}
/>
</DataList>
<DataList title="其他信息">
<PickerField
label="租客来源"
placeholder="请选择租客来源"
value={source}
data={enum2Options(sourceEnum)}
onPick={this.handlePick.bind(this, 'source')}
readOnly={isPreviewing}
/>
<PickerField
label="最多可居住人数"
placeholder="请选择最多可居住人数"
value={allowNumber}
data={Array(10)
.fill(null)
.map((a, i) => ({ text: String(i + 1), value: String(i + 1) }))}
onPick={this.handlePick.bind(this, 'allowNumber')}
readOnly={isPreviewing}
/>
<PickerField
label="实际居住人数"
placeholder="请选择实际居住人数"
value={livesNumber}
data={Array(10)
.fill(null)
.map((a, i) => ({ text: String(i + 1), value: String(i + 1) }))}
onPick={this.handlePick.bind(this, 'livesNumber')}
readOnly={isPreviewing}
/>
</DataList>
<DataList title="物业交割">
<PickerField
label="能耗情况"
placeholder={energyConsumptionLen > 0 ? '已填写' : ''}
data={[]}
onNavigate={() => {
this.props.history.push('/contract-form/energy-form');
}}
/>
<PickerField
label="物品清单"
placeholder={articlesLen > 0 ? '已选择' : ''}
data={[]}
onNavigate={() => {
this.props.history.push('/contract-form/materials-form');
}}
/>
</DataList>
<CommentSection
title="备注信息"
placeholder="请输入备注"
name="memo"
defaultValue={memo}
onChange={this.mergeForm.bind(this)}
readOnly={isPreviewing}
/>
</div>
);
}
render() {
dingtalk.setTitle('新建合同');
if (!house._received) {
return <NoContext />;
}
const { step } = this.state;
if (!step) {
return (
<Fragment>
<Viewport
title="新建合同"
style={{ WebkitOverflowScrolling: 'touch' }}
>
{this.renderForm()}
</Viewport>
<div className="bt-action-container bt-action-container--full">
<Link
className="_action _action--chief"
style={{ flex: 1 }}
to="/"
onClick={this.prepare.bind(this)}
>
下一步
</Link>
</div>
</Fragment>
);
} else if (step === 1) {
return (
<CheckIn
onPrevious={this.previous.bind(this)}
onNext={this.next.bind(this)}
form={this.getForm()}
/>
);
} else if (step === 2) {
return (
<Terms
onPrevious={this.previous.bind(this)}
onSubmit={this.submit.bind(this)}
/>
);
} else {
return <Viewport title="新建合同">No Content</Viewport>;
}
}
}
export default props => (
<ErrorWrapper pageTitle="新建合同">
<ContractForm {...props} houseBelongs={houseBelongs} />
</ErrorWrapper>
);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment