一、弹框Modal表单

  1. 使用Form.create()包装得到一个含有this.props.form属性的CreatForm自组件
  2. 从页面主(父)组件获得props数据和propsMethod方法
  3. return渲染一个Modal中嵌入Form包裹着多个FormItem的弹框表单
const CreateForm = Form.create()(props => {
const { form, current, detail, imgList, introImgList, visible,
previewVisible, previewImage, handleImgChange, handleImgRemove,
handleImgPreview, handleImgCancel, beforeUpload, initImgList, handleFileThumb,
introPreviewVisible, introPreviewImage, handleIntroImgChange, handleIntroImgRemove,
handleIntroImgPreview, handleIntroImgCancel, beforeIntroUpload, handleIntroFileThumb,
handleSubmit, handleCancel,
handleContentChange, handleEditPreview } = props; const previewArr = previewImage.split('/');
const previewType = previewArr[previewArr.length-2];
const controls = [ 'undo', 'redo', 'separator',
'font-size', 'line-height', 'letter-spacing', 'separator',
'text-color', 'bold', 'italic', 'underline', 'strike-through', 'separator',
'superscript', 'subscript', 'remove-styles', 'emoji', 'separator', 'text-indent', 'text-align', 'separator',
'headings', 'list-ul', 'list-ol', 'separator',
'link', 'separator', 'hr', 'separator',
'media', 'separator',
'clear'
]; const { getFieldDecorator, getFieldValue, setFieldsValue } = form;
const formLayout = {
labelCol: { span: 5 },
wrapperCol: { span: 16 },
};
const formLayoutWithOutLabel = {
wrapperCol: { span: 16, offset: 5 }
} const extendControls = [
{
key: 'custom-button',
type: 'button',
text: '预览',
onClick: handleEditPreview
}
]; const initKeys = (detail) => {
let defaultKeys = [];
detail.param.forEach((val, index) => defaultKeys.push(index))
return defaultKeys;
}
getFieldDecorator('keys', { initialValue: current && detail && detail.param.length ? initKeys(detail) : [0] });
const keys = getFieldValue('keys'); const ImgUpButton = (
<div>
<Icon type="plus" />
<div className="ant-upload-text">Upload</div>
</div>
); const okHandle = () => {
form.validateFields((err, fieldsValue) => {
if (err) return;
form.resetFields();
handleSubmit(fieldsValue);
});
} const add = () => {
const keys = getFieldValue('keys'); if (keys.length === 5) {
message.info('产品参数最多5个')
return;
}
let nextKeys = keys;
let nextKeyValue = keys[keys.length-1]+1;
nextKeys = nextKeys.concat(nextKeyValue); setFieldsValue({
keys: nextKeys,
});
}; const remove = index => {
const keys = getFieldValue('keys');
let param = getFieldValue('param');
if (keys.length === 1) {
return;
}
if(param[index]){
param.splice(index, 1)
}
setFieldsValue({
keys: keys.filter((keyItem, i) => i !== index),
param
});
}; const handleUploadFn = (param) => {
const { file } = param; handleImageUpload(file, 'tutorial').then(res => {
param.success({
url: `${setFileHost()+res}`,
meta: {
id: new Date().getTime(),
loop: false,
autoPlay: false,
controls: true
}
})
})
} const handleValidateFn = (file) => {
return file.size < 1024 * 1024 * 100
} const defaultContent = (content) => {
let contentObj = JSON.parse(BraftEditor.createEditorState(content).toRAW());
let urlArr;
Object.keys(contentObj.entityMap).forEach((key) => {
if(contentObj.entityMap[key].data.url){
urlArr = contentObj.entityMap[key].data.url.split('/')
if(urlArr.length == 3){
urlArr.splice(0,1);
contentObj.entityMap[key].data.url = `${setFileHost()}`+ 'sys/' + urlArr.join('/');
}
}
});
let contentRaw = JSON.stringify(contentObj);
return contentRaw;
} const initParamValue = (detail, index) => {
let defaultParamValue = {};
detail.param.forEach((p, i) => {
if(i === index){
defaultParamValue = p
}
})
return defaultParamValue;
} const handleParamChange = (newValue, index) => {
let param = getFieldValue('param');
setFieldsValue({
param: param.map((p, i) => i == index ? newValue : p)
});
} const paramsFormItems = keys.map((k, index) => (
<FormItem
{...(index === 0 ? formLayout : formLayoutWithOutLabel)}
label={index === 0 ? '参数信息' : ''}
key={k}
>
{getFieldDecorator(`param[${index}]`, {
validateTrigger: ['onChange', 'onBlur'],
rules: [
{
type: 'object',
required: true,
validator: (_, value, callback) => {
if (!value.key || !value.value || (value.key && value.key.length > 10) || (value.value && value.value.length > 20)) {
callback('请输入1-10字参数及1-20参数信息或删除此输入框')
} else {
callback()
}
}
},
],
initialValue: current && detail && detail.param.length ? initParamValue(detail, index) : {}
})(
<ParamsInputArray keys={keys} index={index} add={add} remove={remove} onChange={handleParamChange}/>
)}
</FormItem>
)); return (
<Modal
destroyOnClose
width={1200}
bodyStyle={{height: 750, overflow: 'auto'}}
style={{ top: 0 }}
title={`${Object.keys(current).length ? '编辑' : '添加'}产品`}
visible={visible}
keyboard={false}
maskClosable={false}
okText="确定"
cancelText="取消"
onOk={okHandle}
onCancel={handleCancel}
>
<Form onSubmit={handleSubmit}>
<FormItem label="产品名称" {...formLayout}>
{getFieldDecorator('name', {
rules: [{ required: true, message: '请输入5-20字产品名称', min: 5, max: 20}],
initialValue: current && detail ? detail.name : ''
})(<Input placeholder="请输入" />)}
</FormItem>
<FormItem label="产品价格" {...formLayout}>
{getFieldDecorator('price', {
rules: [
{ type: 'number', required: true, message: '请输入1-100000元整数产品价格'},
{ pattern: /^[0-9]*[1-9][0-9]*$/, message: '请输入1-100000元整数产品价格'}
],
initialValue: current && detail ? detail.price/100 : ''
})(<InputNumber
min={1}
max={100000}
style={{width: 120}}
placeholder="请输入"
/>)}
</FormItem>
<FormItem label="产品库存" {...formLayout}>
{getFieldDecorator('stock', {
rules: [{ type: 'number', required: true, message: '请输入5-100000产品库存', min:5, max:100000}],
initialValue: current && detail ? detail.stock : ''
})(<InputNumber
min={5}
max={100000}
style={{width: 120}}
placeholder="请输入"
formatter={value => `${value}`.replace(/\B(?=(\d{3})+(?!\d))/g, ',')}
parser={value => value.replace(/\$\s?|(,*)/g, '')}
/>)}
</FormItem>
<FormItem label="是否设为系统产品" {...formLayout}>
{getFieldDecorator('fromSystem', {
rules: [{ required: true, message: '请选择是否设为系统产品'}],
initialValue: current && detail ? detail.fromSystem : false
})(<Switch defaultChecked={current && detail ? detail.fromSystem : false} />)}
</FormItem>
<FormItem label="发布状态" {...formLayout}>
{getFieldDecorator('publishStatus', {
rules: [{ type: 'number', required: true, message: '请选择发布状态'}],
initialValue: current && detail ? Number(detail.publishStatus) : ''
})(<Select placeholder="请选择">
<SelectOption value={0}>未发布</SelectOption>
<SelectOption value={1}>已发布</SelectOption>
</Select>)}
</FormItem>
<FormItem label="人物介绍图片" {...formLayout}>
{getFieldDecorator('introPic', {
initialValue: current && detail && detail.introPic
? [{
uid: '-1',
status: 'done',
name: detail.introPic,
url: `${setFileHost()+detail.introPic}`,
thumbUrl: `${setFileHost()+detail.introPic}`
}] : ''
})(
<div>
<Upload
accept="image/*"
// action={(file) => handleImageUpload(file, 'image').then(res => {
// handleIntroFileThumb(res)
// })}
listType="picture-card"
fileList={introImgList}
onPreview={handleIntroImgPreview}
onRemove={handleIntroImgRemove}
beforeUpload={beforeIntroUpload}
// onChange={handleIntroImgChange}
>
{introImgList.length >= 1 ? null : ImgUpButton}
</Upload>
<Modal visible={introPreviewVisible} footer={null} onCancel={handleIntroImgCancel} style={{textAlign: 'center'}}>
<img alt="人物介绍图片" style={{ width: '100%' }} src={introPreviewImage} />
</Modal>
</div>
)}
</FormItem>
<FormItem label="产品图片" {...formLayout}>
{getFieldDecorator('rotationChart', {
rules: [{ required: true, message: '请上传1-7张图片'}],
initialValue: current && detail && detail.rotationChart && detail.rotationChart.length
? initImgList(detail) : []
})(
<div>
<Upload
accept="image/*"
// action={(file) => handleImageUpload(file, 'image').then(res => {
// handleFileThumb(res, file, imgList)
// })}
listType="picture-card"
fileList={imgList}
onPreview={handleImgPreview}
onRemove={handleImgRemove}
beforeUpload={beforeUpload}
// onChange={handleImgChange}
>
{imgList.length >= 7 ? null : ImgUpButton}
</Upload>
<Modal visible={previewVisible} footer={null} onCancel={handleImgCancel} style={{textAlign: 'center'}}>
{previewType == 'liveWallPaper' ?
<video src={previewImage} style={{ width: '50%' }} controls="controls" autoPlay="autoplay">
您的浏览器不支持 video 标签。
</video>
: <img alt="产品图片" style={{ width: '100%' }} src={previewImage} />}
</Modal>
</div>
)}
</FormItem>
{paramsFormItems}
<FormItem label="产品详情" {...formLayout}>
{getFieldDecorator('content', {
validateTrigger: 'onBlur',
rules: [{
required: true,
validator: (_, value, callback) => {
if (value.toHTML().length < 50 || value.toHTML().length > 15000000) {
callback('请输入50-15000000字产品详情')
} else {
callback()
}
}
}],
initialValue: current && detail ? BraftEditor.createEditorState(defaultContent(detail.content)) : ''
})(
<BraftEditor // 富文本插件组件
className="my-editor"
controls={controls}
extendControls={extendControls}
placeholder="请输入50-15000000字产品详情"
media={{
uploadFn: handleUploadFn,
validateFn: handleValidateFn,
accepts: {
image: 'image/png, image/jpeg, image/jpg, image/gif, image/webp, image/apng, image/svg',
video: 'video/mp4, video/ogg, video/webm',
audio: 'audio/mp3, audio/mp4, audio/ogg, audio/mpeg'
}
}}
onChange={handleContentChange}
/>
)}
</FormItem>
</Form>
</Modal>
);
});  

主(父)组件中<CreatForm />的使用,props数据和propsMethods方法的传递

const parentMethods = {
initImgList: this.initImgList,
handleFileThumb: this.handleFileThumb,
handleImgChange: this.handleImgChange,
handleImgRemove: this.handleImgRemove,
handleImgPreview: this.handleImgPreview,
handleImgCancel: this.handleImgCancel,
handleContentChange: this.handleContentChange,
handleEditPreview: this.handleEditPreview,
beforeUpload: this.beforeUpload,
handleIntroFileThumb: this.handleIntroFileThumb,
handleIntroImgChange: this.handleIntroImgChange,
handleIntroImgRemove: this.handleIntroImgRemove,
handleIntroImgPreview: this.handleIntroImgPreview,
handleIntroImgCancel: this.handleIntroImgCancel,
beforeIntroUpload: this.beforeIntroUpload,
handleSubmit: this.handleSubmit,
handleCancel: this.handleCancel
} // state数据、model层的props数据
const parentProps = {
current,
detail,
imgList,
introImgList,
visible,
previewVisible,
previewImage,
introPreviewVisible,
introPreviewImage,
contentVisible
} return (
<PageHeaderWrapper title="产品列表">
<div className={styles.standardList}>
// 页面展示的列表 、表格
</div>
<CreateForm {...parentMethods} {...parentProps}></CreateForm>
// 其它简单Modal 直接在此处使用<Modal />
</PageHeaderWrapper>
);
}
}

  

通过组件内定义方法,生成Modal弹框内容Content,直接使用<Modal />

const getModalContent = () => {
const controls = [ 'undo', 'redo', 'separator',
'font-size', 'line-height', 'letter-spacing', 'separator',
'text-color', 'bold', 'italic', 'underline', 'strike-through', 'separator',
'superscript', 'subscript', 'remove-styles', 'emoji', 'separator', 'text-indent', 'text-align', 'separator',
'headings', 'list-ul', 'list-ol', 'separator',
'link', 'separator', 'hr', 'separator',
'media', 'separator',
'clear'
];
const formLayout = {
labelCol: { span: 5 },
wrapperCol: { span: 16 },
}; const handleUploadFn = (param) => {
const { file } = param; const fileTypeArr = file.type.split('/');
const fileType = fileTypeArr[0]; if(fileType == 'video'){
handleImageUpload(file, 'tutorialVideo').then(res => {
param.success({
url: `${setFileHost()+res}`,
meta: {
id: new Date().getTime(),
loop: false,
autoPlay: false,
controls: true
}
})
})
}else{
handleImageUpload(file, 'tutorial').then(res => {
param.success({
url: `${setFileHost()+res}`,
meta: {
id: new Date().getTime(),
loop: false,
autoPlay: false,
controls: true
}
})
})
}
} const handleValidateFn = (file) => {
return file.size < 1024 * 1024 * 100
} const defaultContent = (content) => {
let contentObj = JSON.parse(BraftEditor.createEditorState(content).toRAW());
let urlArr;
Object.keys(contentObj.entityMap).forEach((key) => {
if(contentObj.entityMap[key].data.url){
urlArr = contentObj.entityMap[key].data.url.split('/')
console.log('默认内容', urlArr);
if(urlArr.length == 2){ //ios视频前缀yihezo
urlArr.splice(0,1);
contentObj.entityMap[key].data.url = `${setFileHost()}`+ 'yihezo/' + urlArr.join('/');
} if(urlArr.length == 3){ //其它媒体文件前缀sys/tutorail
urlArr.splice(0,1);
contentObj.entityMap[key].data.url = `${setFileHost()}`+ 'sys/' + urlArr.join('/');
}
}
});
let contentRaw = JSON.stringify(contentObj);
return contentRaw;
} const extendControls = [
{
key: 'custom-button',
type: 'button',
text: '预览',
onClick: this.handleEditPreview
}
]; return (
<Form onSubmit={this.handleSubmit}>
<FormItem label="教程标题" {...formLayout}>
{getFieldDecorator('title', {
rules: [{ required: true, message: '请输入至多10字标题', max: 10 }],
initialValue: current && detail ? detail.title : '',
})(<Input placeholder="请输入" />)}
</FormItem>
<FormItem label="教程类型" {...formLayout}>
{getFieldDecorator('kindId', {
rules: [{ required: true, message: '请选择类型' }],
initialValue: current && detail ? detail.kindId : undefined
})(
<TreeSelect
dropdownStyle={{ maxHeight: 400, overflow: 'auto' }}
treeData={typeData}
placeholder="请选择"
onChange={this.handleTypeChange}
/>
)}
</FormItem>
<FormItem label="教程正文" {...formLayout}>
{getFieldDecorator('content', {
validateTrigger: 'onBlur',
rules: [{
required: true,
validator: (_, value, callback) => {
if (value.isEmpty()) {
callback('请输入正文内容')
} else {
callback()
}
}
}],
initialValue: current && detail ? BraftEditor.createEditorState(defaultContent(detail.content)) : ''
})(
<BraftEditor
className="my-editor"
controls={controls}
extendControls={extendControls}
placeholder="请输入正文内容"
media={{
uploadFn: handleUploadFn,
validateFn: handleValidateFn,
accepts: {
image: 'image/png, image/jpeg, image/jpg, image/gif, image/webp, image/apng, image/svg',
video: 'video/mp4',
audio: 'audio/mp3, audio/mp4, audio/ogg, audio/mpeg'
}
}}
onChange={this.handleContentChange}
/>
)}
</FormItem>
</Form>
);
};
const modalFooter = done
? { footer: null, onCancel: this.handleDone }
: { okText: '保存', onOk: this.handleSubmit, onCancel: this.handleCancel }; return (
<PageHeaderWrapper title="产品列表">
<div className={styles.standardList}>
// 页面展示的列表 、表格
</div>
<Modal
title={done ? null : `教程${current.id ? '编辑' : '添加'}`}
className={styles.standardListForm}
width={1200}
style={{ top: 0 }}
bodyStyle={done ? { padding: '56px 0' } : { padding: '28px 0 0' }}
destroyOnClose
visible={visible}
keyboard={false}
maskClosable={false}
{...modalFooter}
>
{getModalContent()}
</Modal>
</PageHeaderWrapper> ); } }

  

二、新页Card表单

1.布局就是简单的Card分栏,Form表单包裹FormItem表单项

2.需要注意的是提交方法handleSubmit,提交成功后router.push跳转返回到列表展示页

import router from 'umi/router';

router.push(`/newmall/goodsList?kw=${keyword}&&cp=${currentPage}`);

三、Card表单与表格Model表单结合

  • Card表单部分

     <Card title="基本信息" bordered={false}>
    <Row gutter={24} style={{marginTop: 5}}> // Input输入框
    <Col xl={12} lg={12} md={24} sm={24} xs={24}>
    <Form.Item label='分类名称'>
    {getFieldDecorator('categoryName', {
    rules: [
    { required: true, message: '请输入1-5字分类名称' },
    { min: 1, max: 5, message: '请输入1-5字分类名称' }
    ],
    initialValue: info && info.categoryName ? info.categoryName : ''
    })(<Input placeholder="请输入分类名称" style={{maxWidth: 300}} />)}
    </Form.Item>
    </Col> // Upload上传图片
    <Col xl={12} lg={12} md={24} sm={24} xs={24}>
    <Form.Item label='分类Logo'>
    {getFieldDecorator('categoryLogo', {
    rules: [{ required: true, message: '请选择分类Logo' }],
    initialValue: info && info.categoryLogo ? [{
    uid: '-1',
    status: 'done',
    name: info.categoryLogo,
    url: `${setFileHost()+info.categoryLogo}`,
    thumbUrl: `${setFileHost()+info.categoryLogo}`
    }] : ''
    })(
    <div>
    <Upload
    accept="image/*"
    action={(file) => handleImageUpload(file, 'img').then(res => {
    this.handleFileThumb(res)
    })}
    listType="picture-card"
    fileList={imgList}
    beforeUpload={this.beforeImgUpload}
    onRemove={this.handleImgRemove}
    onPreview={this.handleImgPreview}
    onChange={this.handleImgChange}
    >
    {imgList.length >= 1 ? null : ImgUpButton}
    </Upload>
    <Modal visible={previewImgVisible} footer={null} onCancel={this.handleImgCancel} style={{textAlign: 'center'}}>
    <img alt="分类Logo" style={{ width: '100%' }} src={previewImage} />
    </Modal>
    </div>
    )}
    </Form.Item>
    </Col>
    </Row>
    </Card>
  • Table结合Modal表单

    // 表格展示一   ----   可添加/编辑
    <Card title="轮播图商品管理" bordered={false} style=
    {{marginTop: 30}}>
    <Button
    style={{ width: '100%', marginBottom: 16 }}
    type="dashed"
    onClick={() => this.addRotation()}
    icon="plus"
    >
    新增轮播图商品
    </Button>
    <Table
    pagination={false} //关闭分页功能
    loading={rotationLoading}
    rowKey={record => record.id}
    dataSource={rotation}
    columns={rotationColumns}
    onChange={this.handleRotationTableChange}
    />
    </Card> <RotationModal {...rotationModalMethods} {...rotationModalProps} /> // Modal表单 // 表格展示二 ---- 可添加/删除
    <Card title="关联商品管理" bordered={false} style={{marginTop: 30}}>
    <Button
    style={{ width: '100%', marginBottom: 16 }}
    type="dashed"
    onClick={() => this.addProduct()}
    icon="plus"
    >
    关联商品
    </Button>
    <Table
    pagination={productPage} //分页展示
    loading={productLoading}
    rowKey={record => record.productId}
    dataSource={productList}
    columns={productColumns}
    onChange={this.handleProductTableChange}
    />
    </Card> <ProductModal {...productModalMethods} {...productModalProps} /> // Modal表单

转载请注明出处

最新文章

  1. MD5加密的Java实现
  2. MyEclipse — Maven+Spring+Struts+Hibernate 整合 [学习笔记-2]
  3. Linux驱动的两种加载方式过程分析
  4. 柔性数组-读《深度探索C++对象模型》有感 (转载)
  5. FZU 2243 Daxia like uber
  6. Netty2:粘包/拆包问题与使用LineBasedFrameDecoder的解决方案
  7. python浅拷贝和深拷贝
  8. Tomcat源码分析 -- Tomcat整体架构
  9. bzoj2007 NOI2010 海拔(对偶图)
  10. UTC时间戳转为时间
  11. day3.python字符串格式化
  12. ORM常用字段介绍
  13. BZOJ1131[POI2008]Sta——树形DP
  14. hadoop old API CombineFileInputFormat
  15. Linux中的邮件发送
  16. Qt架构图及模块分析介绍
  17. automake安装出错
  18. 排查在 Azure 中新建 Windows 虚拟机时遇到的经典部署问题
  19. 用django实现redirect的几种方法总结
  20. 使用node.js做一个自用的天气插件

热门文章

  1. RocketMQ吐血总结
  2. NSIS打包软件使用
  3. sql server 平方根函数SQRT(x)
  4. git 版本回退方法
  5. MVVM框架简单实现
  6. 雪花算法生成ID
  7. Spring MVC使用AOP实现审计日志
  8. 【洛谷P1490】买蛋糕
  9. QT程序在发布的时候应注意的地方
  10. Hadoop中配置环境后重启失效解决方法