Commit 3b85970e authored by 田翔's avatar 田翔

增加表单渲染组件

parent d91680b2
import React, { useState, useEffect, useRef } from 'react'; import React, { useState, useEffect, useRef } from 'react';
import { FormRender, FormDesigner } from '../../src/index'; import { FormRender, FormDesigner } from '../../src/index';
import { options1, options2 } from './data'; import { options1, options2 } from './data';
import { Button } from 'antd'; import { Button, Tabs } from 'antd';
window.globalConfig = { window.globalConfig = {
"env": "daily", "env": "daily",
...@@ -3506,36 +3506,27 @@ window.globalConfig = { ...@@ -3506,36 +3506,27 @@ window.globalConfig = {
"apiGatewayDomain": "http://127.0.0.1:8080" "apiGatewayDomain": "http://127.0.0.1:8080"
} }
export default (props) => { const Test = (props) => {
const [editState, setEditState] = useState('edit');
const [trigger, setTrigger] = useState(false); const [schema, setSchema] = useState({})
const [readOnly, setReadOnly] = useState(false)
const testRef = useRef(); const changeSchema = (value) => {
setSchema(value)
}
return ( return (
<div> <div style={{ width: '100%' }}>
<FormDesigner <Tabs>
style={{ margin: '0' }} <Tabs.TabPane tab="编辑" key="item-1">
loading={props.loading} <FormDesigner setSchema={changeSchema} />
key={editState} </Tabs.TabPane>
handleType={editState} <Tabs.TabPane tab="渲染" key="item-2">
// isReadOnly={1} <FormRender schema={schema} />
tablesSchema={options1} </Tabs.TabPane>
onSubmit={(formData, formInstance) => { </Tabs>
console.log('formData', formData)
// setEditState('scan')
// setReadOnly(true)
}}
// codes={{ '工单编号': 'RYWX00000269', '事件编号': 'RYWXTB220000320' }}
// ref={testRef}
customerSubmitTrigger={{
trigger: trigger,
}}
onBack={() => {
// setEditState('edit')
// setReadOnly(false)
}}
/>
</div> </div>
); )
};
}
export default Test
...@@ -29,7 +29,7 @@ app.use(webpackHotMiddleware(compiler)); ...@@ -29,7 +29,7 @@ app.use(webpackHotMiddleware(compiler));
app.use(express.static(__dirname)); app.use(express.static(__dirname));
// app.use('/', createProxyMiddleware('/', { target: 'http://127.0.0.1:8880', changeOrigin: true })); // app.use('/', createProxyMiddleware('/', { target: 'http://127.0.0.1:8880', changeOrigin: true }));
app.use('/', createProxyMiddleware('/', { target: 'http://192.168.10.167:8088', changeOrigin: true })); app.use('/', createProxyMiddleware('/', { target: 'http://192.168.10.167:8088', changeOrigin: true }));
const port = process.env.PORT || 8383; const port = process.env.PORT || 8888;
module.exports = app.listen(port, () => { module.exports = app.listen(port, () => {
console.log(`Server listening on http://localhost:${port}, Ctrl+C to stop`); console.log(`Server listening on http://localhost:${port}, Ctrl+C to stop`);
}); });
...@@ -64,13 +64,37 @@ module.exports = { ...@@ -64,13 +64,37 @@ module.exports = {
}, },
{ {
test: /\.less$/, test: /\.less$/,
use: [ use: [
{ {
loader: 'style-loader', loader: 'style-loader',
}, },
{ {
loader: 'css-loader', loader: 'css-loader',
options: {
modules: {
getLocalIdent: (context, _, localName) => {
if (
context.resourcePath.includes('node_modules') ||
context.resourcePath.includes('ant.design.pro.less') ||
context.resourcePath.includes('main.less')
) {
return localName;
}
const match = context.resourcePath.match(/src(.*)/);
const matchAntd = /^ant/.test(localName);
if (match && match[1] && !matchAntd) {
const antdProPath = match[1].replace('.less', '');
const arr = slash(antdProPath)
.split('/')
.map(a => a.replace(/([A-Z])/g, '-$1'))
.map(a => a.toLowerCase());
return `parse-form-${arr.join('-')}-${localName}`.replace(/--/g, '-');
}
return localName;
},
},
},
}, },
{ {
loader: 'less-loader', loader: 'less-loader',
......
{ {
"name": "panda-xform", "name": "panda-xform",
"version": "1.1.0", "version": "1.2.0",
"description": "1.1.0: 项目初始化", "description": "1.2.0: 增加渲染表单组件",
"keywords": [ "keywords": [
"panda-xform" "panda-xform"
], ],
...@@ -37,6 +37,7 @@ ...@@ -37,6 +37,7 @@
"moment": "^2.29.1", "moment": "^2.29.1",
"qrcode.react": "^1.0.1", "qrcode.react": "^1.0.1",
"react": "17.0.2", "react": "17.0.2",
"react-file-viewer": "^1.2.1",
"react-svg": "15.1.9", "react-svg": "15.1.9",
"yarn": "^1.22.17" "yarn": "^1.22.17"
}, },
......
import settings from './settings'
import {
baseSettings,
commonSettings,
switchSettings,
elementSettings,
globalSettings,
} from './otherSettings'
export {
settings,
baseSettings,
commonSettings,
switchSettings,
elementSettings,
globalSettings,
}
\ No newline at end of file
const baseSettings = {
title: {
title: '标题',
type: 'string',
// widget: 'htmlInput',
displayType: 'row',
labelWidth: 80,
},
}
const commonSettings = {
$id: {
title: '字段名称',
type: 'string',
widget: 'FieldNames',
required: true,
displayType: 'row',
labelWidth: 80,
// width: '70%',
},
title: {
title: '标题',
required: true,
type: 'string',
widget: 'htmlInput',
displayType: 'row',
labelWidth: 80,
},
placeholder: {
title: '提示语',
type: 'string',
displayType: 'row',
labelWidth: 80,
},
// description: {
// title: '说明',
// type: 'string',
// },
presetValue: {
title: '默认值',
type: 'string',
displayType: 'row',
labelWidth: 80,
// default: ''
},
}
const switchSettings = {
disabled: {
title: '只读',
type: 'boolean',
widget: 'BooleanSwitch',
displayType: 'row',
labelWidth: 80,
default: false
},
required: {
title: '必填',
type: 'boolean',
widget: 'BooleanSwitch',
displayType: 'row',
labelWidth: 80,
default: false
},
hidden: {
title: '隐藏',
type: 'boolean',
widget: 'BooleanSwitch',
default: false,
displayType: 'row',
labelWidth: 80,
},
}
const elementSettings = {
width: {
title: '元素宽度',
type: 'string',
widget: 'percentSlider',
// default: '33%',
},
labelWidth: {
title: '标签宽度',
description: '默认值110',
default: 110,
type: 'number',
widget: 'slider',
max: 400,
props: {
hideNumber: true,
},
},
}
const globalSettings = {
type: 'object',
properties: {
column: {
title: '整体布局',
type: 'number',
enum: [1],
default: 1,
enumNames: ['一行一列'],
widget: 'select',
props: {
placeholder: '默认一行一列',
},
displayType: 'row',
labelWidth: 100,
},
displayType: {
title: '标签展示模式',
type: 'string',
default: 'row',
enum: ['row', 'column'],
enumNames: ['同行', '单独一行'],
widget: 'select',
displayType: 'row',
labelWidth: 100,
},
labelWidth: {
title: '标签宽度',
type: 'number',
widget: 'slider',
max: 300,
default: 120,
props: {
hideNumber: true,
},
},
},
}
export {
baseSettings,
commonSettings,
switchSettings,
elementSettings,
globalSettings
}
\ No newline at end of file
const baseSettings = { import { commonSettings, switchSettings, elementSettings } from './otherSettings'
title: {
title: '标题',
type: 'string',
// widget: 'htmlInput',
displayType: 'row',
labelWidth: 80,
},
}
const commonSettings = {
$id: {
title: '字段名称',
type: 'string',
widget: 'FieldNames',
required: true,
displayType: 'row',
labelWidth: 80,
// width: '70%',
},
title: {
title: '标题',
required: true,
type: 'string',
widget: 'htmlInput',
displayType: 'row',
labelWidth: 80,
},
placeholder: {
title: '提示语',
type: 'string',
displayType: 'row',
labelWidth: 80,
},
// description: {
// title: '说明',
// type: 'string',
// },
presetValue: {
title: '默认值',
type: 'string',
displayType: 'row',
labelWidth: 80,
// default: ''
},
}
const switchSettings = {
disabled: {
title: '只读',
type: 'boolean',
widget: 'BooleanSwitch',
displayType: 'row',
labelWidth: 80,
default: false
},
required: {
title: '必填',
type: 'boolean',
widget: 'BooleanSwitch',
displayType: 'row',
labelWidth: 80,
default: false
},
hidden: {
title: '隐藏',
type: 'boolean',
widget: 'BooleanSwitch',
default: false,
displayType: 'row',
labelWidth: 80,
},
}
const elementSettings = {
width: {
title: '元素宽度',
type: 'string',
widget: 'percentSlider',
// default: '33%',
},
labelWidth: {
title: '标签宽度',
description: '默认值110',
default: 110,
type: 'number',
widget: 'slider',
max: 400,
props: {
hideNumber: true,
},
},
}
const allSetting = { const allSetting = {
...commonSettings, ...commonSettings,
...@@ -110,16 +18,7 @@ const settings = [ ...@@ -110,16 +18,7 @@ const settings = [
type: 'object', type: 'object',
properties: {} properties: {}
}, },
setting: { setting: {},
hidden: {
title: '隐藏',
type: 'boolean',
widget: 'BooleanSwitch',
default: false,
displayType: 'row',
labelWidth: 80,
},
},
}, },
], ],
}, },
...@@ -132,7 +31,6 @@ const settings = [ ...@@ -132,7 +31,6 @@ const settings = [
schema: { schema: {
title: '文本', title: '文本',
type: 'string', type: 'string',
widget: 'TextInput',
}, },
setting: { setting: {
...allSetting ...allSetting
...@@ -168,7 +66,7 @@ const settings = [ ...@@ -168,7 +66,7 @@ const settings = [
schema: { schema: {
title: '数值', title: '数值',
type: 'number', type: 'number',
widget: 'NumberInput' widget: 'number'
}, },
setting: { setting: {
...allSetting ...allSetting
...@@ -243,10 +141,10 @@ const settings = [ ...@@ -243,10 +141,10 @@ const settings = [
}, },
setting: { setting: {
...commonSettings, ...commonSettings,
options: { dataSource: {
title: '可选值', title: '数据来源',
type: 'string', type: 'string',
widget: 'EnumOptions', widget: 'DataSource',
displayType: 'row', displayType: 'row',
labelWidth: 80, labelWidth: 80,
}, },
...@@ -365,32 +263,20 @@ const settings = [ ...@@ -365,32 +263,20 @@ const settings = [
}, },
setting: { setting: {
...commonSettings, ...commonSettings,
interaction: { // dependencies: {
title: '交互方式', // title: '关联字段',
type: 'string', // type: 'array',
enum: ['下拉框', '分组模态', '树形模态'], // widget: 'CascadeField',
default: '下拉框', // displayType: 'row',
enumNames: ['下拉框', '分组模态', '树形模态'], // labelWidth: 80,
displayType: 'row', // },
labelWidth: 80, // isMultiple: {
}, // title: '是否多选',
role: { // type: 'boolean',
title: '机构角色', // widget: 'BooleanSwitch',
type: 'string', // displayType: 'row',
required: true, // labelWidth: 80,
// enum: ['下拉框', '分组模态', '树形模态'], // },
// default: '下拉框',
// enumNames: ['下拉框', '分组模态', '树形模态'],
displayType: 'row',
labelWidth: 80,
},
isMultiple: {
title: '是否多选',
type: 'boolean',
widget: 'BooleanSwitch',
displayType: 'row',
labelWidth: 80,
},
...switchSettings, ...switchSettings,
...elementSettings, ...elementSettings,
} }
...@@ -417,14 +303,16 @@ const settings = [ ...@@ -417,14 +303,16 @@ const settings = [
type: 'string', type: 'string',
displayType: 'row', displayType: 'row',
labelWidth: 80, labelWidth: 80,
widget: 'date', widget: 'DateSelect',
dependencies: ['format']
}, },
format: { format: {
title: '日期格式', title: '日期格式',
type: 'string', type: 'string',
enum: ['date', 'dateTime', 'year', 'month', 'quarter', 'week'], // enum: ['date', 'dateTime', 'year', 'month', 'quarter', 'week'],
enum: ['date', 'dateTime', 'year', 'month'],
default: 'date', default: 'date',
enumNames: ['日期', '日期时间', '日期年份', '日期月份', '日期季度', '日期周'], enumNames: ['日期', '日期时间', '日期年份', '日期月份'],
displayType: 'row', displayType: 'row',
labelWidth: 80, labelWidth: 80,
}, },
...@@ -470,32 +358,31 @@ const settings = [ ...@@ -470,32 +358,31 @@ const settings = [
type: 'string', type: 'string',
widget: 'FileUpload', widget: 'FileUpload',
}, },
}, setting: {
{ ...commonSettings,
text: '图片', presetValue: {},
name: '图片', fileType: {
schema: { title: '文件类型',
title: '图片',
type: 'string', type: 'string',
widget: 'FileUpload', required: true,
}, widget: 'select',
enum: ['图片', '文档', '音频', '视频'],
enumNames: ['图片', '文档', '音频', '视频'],
default: '图片',
displayType: 'row',
labelWidth: 80,
}, },
{ listType: {
text: '录音', title: '显示样式',
name: '录音',
schema: {
title: '录音',
type: 'string', type: 'string',
widget: 'FileUpload', default: 'picture',
}, enum: ['picture', 'text', 'picture-card'],
enumNames: ['列表', '文本', '卡片'],
displayType: 'row',
labelWidth: 80,
}, },
{ ...switchSettings,
text: '视频', ...elementSettings,
name: '视频',
schema: {
title: '视频',
type: 'string',
widget: 'FileUpload',
}, },
}, },
], ],
...@@ -511,55 +398,11 @@ const settings = [ ...@@ -511,55 +398,11 @@ const settings = [
schema: { schema: {
title: '坐标控件', title: '坐标控件',
type: 'string', type: 'string',
widget: 'DateSelect', widget: 'Coordinate',
}, },
}, },
], ],
}, },
] ]
const globalSettings = { export default settings
type: 'object', \ No newline at end of file
properties: {
column: {
title: '整体布局',
type: 'number',
enum: [1],
default: 1,
enumNames: ['一行一列'],
widget: 'select',
props: {
placeholder: '默认一行一列',
},
displayType: 'row',
labelWidth: 100,
},
displayType: {
title: '标签展示模式',
type: 'string',
default: 'row',
enum: ['row', 'column'],
enumNames: ['同行', '单独一行'],
widget: 'select',
displayType: 'row',
labelWidth: 100,
},
labelWidth: {
title: '标签宽度',
type: 'number',
widget: 'slider',
max: 300,
default: 120,
props: {
hideNumber: true,
},
},
},
}
export {
settings,
baseSettings,
commonSettings,
globalSettings
}
\ No newline at end of file
...@@ -19,6 +19,7 @@ const FormDesigner = (props) => { ...@@ -19,6 +19,7 @@ const FormDesigner = (props) => {
if (errors.length) { if (errors.length) {
return message.error('请按照提示完善表单内容') return message.error('请按照提示完善表单内容')
} }
props.setSchema && props.setSchema(ref.current.getValue())
} }
} }
] ]
...@@ -46,7 +47,6 @@ const FormDesigner = (props) => { ...@@ -46,7 +47,6 @@ const FormDesigner = (props) => {
<div className={prefixCls}> <div className={prefixCls}>
<Generator <Generator
configProvider={{ prefixCls: prefixClsWithoutParseForm }} configProvider={{ prefixCls: prefixClsWithoutParseForm }}
// getId={name => name ? `${name}_${count++}` : null}
mapping={{ mapping={{
object: 'Header', object: 'Header',
}} }}
......
const schema = {
"type": "object",
"properties": {
"formGroup_1": {
"type": "object",
"title": "文本类",
"properties": {
"多行文本": {
"title": "多行文本",
"type": "string",
"format": "textarea",
"default": "",
"min": 0,
"max": 254,
"className": "",
"showDescIcon": true,
"column": 1,
"required": false,
"widget": "textArea",
"rules": [
{
"patterns": false,
"message": ""
}
],
"disabled": false,
"placeholder": "请输入多行文本",
"width": "100%",
"pathKey": "formGroup_1"
},
"富文本": {
"title": "富文本",
"widget": "RichText",
"type": "string",
"config": "",
"format": "text",
"className": "",
"default": "富文本默认值",
"content": "富文本默认值",
"min": 0,
"max": 254,
"disabled": false,
"showDescIcon": true,
"required": false,
"widget": "RichText",
"rules": [
{},
{
"pattern": false,
"message": ""
}
],
"width": "100%",
"pathKey": "formGroup_1"
},
},
"required": [],
"className": "ant-parse-form-group"
},
},
"required": [],
"column": 4,
"displayType": "row",
"showDescIcon": true,
"labelWidth": 120
}
export default schema
\ No newline at end of file
import React, { useState, useEffect, useRef, useContext, createContext, forwardRef, useImperativeHandle, useMemo } from 'react'
import FormRender, { useForm } from 'form-render'
import { ConfigProvider } from 'antd'
import widgets from '../widgets'
// import schema from './data'
// console.log('FormRender', widgets)
export const GlobalStore = createContext(null)
const XRender = (props, ref) => {
// useImperativeHandle(ref, () => ({
// form,
// getFormValues,
// setListData
// }))
const { schema } = props
const { getPrefixCls } = useContext(ConfigProvider.ConfigContext)
const prefixCls = getPrefixCls('parse-form')
const prefixClsWithoutParseForm = getPrefixCls()
const [extraData, setExtraData] = useState({})
const form = useForm()
return (
<GlobalStore.Provider
value={{
extraData,
setExtraData
}}>
<div className={prefixCls}>
<FormRender
configProvider={{ prefixCls: prefixClsWithoutParseForm }}
form={form}
schema={schema}
// onChange={form.setValues}
mapping={{
object: 'Header',
}}
widgets={widgets}
/>
</div>
</GlobalStore.Provider>
);
}
export default forwardRef(XRender)
/* import React, { useState, useEffect, useContext, useRef } from 'react'
* 地图中,显示的是墨卡托坐标,使用在地图上来做交互是用的经纬度坐标 import { Input, Modal, Spin, Button } from 'antd'
* */ import { CompassOutlined } from '@ant-design/icons'
import React, { useState, useEffect, useContext, useRef } from 'react'; import { AMapScene, AMapDrawTool, VectorLayer, PointLayer } from '@wisdom-map/amap'
import { Input, Modal, Spin, Button } from 'antd'; import { ArcGISSceneMap, AutoCompleteSearch, Drawtool as drawTool, Graphic, GraphicsLayer, Point } from '@wisdom-map/arcgismap'
import { CompassOutlined } from '@ant-design/icons'; import { mktlng } from '@wisdom-map/basemap/es/utils/coordTransformation'
import { AMapScene, AMapDrawTool, VectorLayer, PointLayer } from '@wisdom-map/amap'; import { GlobalStore } from '../../../FormRender'
import {
ArcGISSceneMap,
AutoCompleteSearch,
Drawtool as drawTool,
Graphic,
GraphicsLayer,
Point
} from '@wisdom-map/arcgismap';
import { mktlng } from '@wisdom-map/basemap/es/utils/coordTransformation';
import { GlobalStore } from '../../../../FormRender'
const CoordinatePicker = ({ value, onChange, name, schema }) => { const CoordinatePicker = ({ value, onChange, name, schema }) => {
......
/*
* 地图中,显示的是墨卡托坐标,使用在地图上来做交互是用的经纬度坐标
* */
import React, {useState, useEffect, useContext} from 'react';
const CoordinatePicker = ({value}) => {
return <div>{value || '地图组件暂不可用'}</div>
};
export default CoordinatePicker;
import React, { useState, useEffect, useContext } from 'react' import React, { useState, useEffect, useContext } from 'react'
import { Input, message } from 'antd'; import { Input, message } from 'antd';
import { GlobalStore } from '../../../../FormRender' import { GlobalStore } from '../../../FormRender'
import { AMap } from '@wisdom-map/amap' import { AMap } from '@wisdom-map/amap'
const SearchLocation = ({ value, onChange, name, schema }) => { const SearchLocation = ({ value, onChange, name, schema }) => {
......
// 自定义时间组件 // 自定义时间组件
import React, { useState, useEffect, useMemo } from 'react'; import React, { useState, useEffect, useMemo } from 'react'
import { DatePicker, Input } from 'antd'; import { DatePicker, Input } from 'antd'
import { dateType, dateFormat } from '../../../../constant/constant'; import style from '../../style'
import moment from 'moment'; import moment from 'moment'
import locale from 'antd/lib/date-picker/locale/zh_CN'; import locale from 'antd/lib/date-picker/locale/zh_CN'
const DateTime = ({ value, onChange, schema }) => { const DateTime = (props) => {
console.log('schema', schema) const { value, onChange, schema } = props
const { disabled, format, presetValue, placeholder } = schema const { disabled, format, presetValue, placeholder } = schema
// console.log('schema', schema)
// const dateTime = value || schema.default;
// const format = schema.shape != '日期周' ? dateFormat[schema.shape] : 'YYYY-wo';
const valueShow = useMemo(() => { const valueShow = useMemo(() => {
return moment(value || presetValue) console.log('value+presetValue', value, presetValue)
return (value || presetValue) ? moment(value || presetValue) : null
}, [presetValue, value]) }, [presetValue, value])
const disabledDate = (current) => { const disabledDate = (current) => {
...@@ -24,45 +22,30 @@ const DateTime = ({ value, onChange, schema }) => { ...@@ -24,45 +22,30 @@ const DateTime = ({ value, onChange, schema }) => {
} else { } else {
return false return false
} }
};
// 不超过当前时间这个配置没有意义,在moment的工具中,已经囊括了未到时间这步
const disabledTime = (current) => {
if (schema.config === '不超过当前时间') {
return {
disabledHours: () => [],
disabledMinutes: () => [],
disabledSeconds: () => [55, 56],
};
} else {
return {}
} }
};
const dateChange = (date, dateStr) => { const dateChange = (date, dateStr) => {
// console.log(val, dateVal) onChange(dateStr)
// onChange(schema.shape != '日期周' ? dateVal : (val ? moment(val).format('YYYY-MM-DD') : null)) }
// onChange(moment(date._d).format('YYYY-MM-DD HH:mm:ss'))
console.log(date, dateStr) const onOk = (value) => {
onChange(date) console.log('value', value)
} }
// useEffect(() => { // console.log('valueShow', valueShow)
// onChange(dateTime);
// }, []);
return ( return (
<DatePicker <DatePicker
mode={format} picker={format}
disabled={disabled} disabled={disabled}
placeholder={placeholder} placeholder={placeholder}
value={valueShow} value={valueShow}
onChange={dateChange} onChange={dateChange}
picker={dateType[schema.shape]} showNow
showTime={format === 'dateTime'} showTime={format === 'dateTime'}
style={{ width: '100%' }} style={style}
locale={locale} locale={locale}
disabledDate={disabledDate} onOk={onOk}
/> />
); );
......
import React, { useMemo } from 'react' import React, { useMemo } from 'react'
import { TimePicker } from 'antd' import { TimePicker } from 'antd'
import moment from 'moment' import moment from 'moment'
import style from '../../style'
const format = 'HH:mm:ss'
const Time = ({ value, onChange, schema }) => { const Time = ({ value, onChange, schema }) => {
// console.log(value, onChange, schema)
const { presetValue } = schema const { presetValue } = schema
const valueShow = useMemo(() => { const valueShow = useMemo(() => {
// console.log('时间改变', moment(value || presetValue)) return (value || presetValue) ? moment(value || presetValue, 'HH:mm:ss') : null
// return (value || presetValue) ? moment(value || presetValue) : null
return (value || presetValue) ? moment(value || presetValue) : null
}, [presetValue, value]) }, [presetValue, value])
const timeChange = (time, timeStr) => { const timeChange = (time, timeStr) => {
console.log(time, timeStr) onChange(timeStr)
} }
console.log('valueShow', valueShow) console.log('valueShow', valueShow)
return ( return (
<TimePicker value={valueShow} onChange={timeChange} format={format} /> <TimePicker style={style} value={valueShow} onChange={timeChange} />
) )
} }
......
import React, { useEffect, useMemo, useState } from 'react'
import styles from './index.less'
import { Upload, Button, message, Modal } from 'antd'
import { UploadOutlined, FileOutlined } from '@ant-design/icons'
import FileViewer from 'react-file-viewer'
import { uploadFileUrl, downloadFileUrl, downloadFile } from '../../../../apis/process'
import { downloadFunc, filenameVerification } from '../../../../utils/utils'
const accepts = {
'图片': ['.bmp', '.gif', '.jpeg', 'tiff', '.png', '.svg', '.jpg'],
'文档': ['.docx', '.xlsx', '.pdf'],
'视频': ['.mp4'],
'音频': ['.mp3'],
}
const FileUpload = (props) => {
const { value, schema, onChange } = props
const { disabled, fileType, listType, presetValue, placeholder } = schema
const [showList, setShowList] = useState('')
const [visible, setVisible] = useState(false)
const [showFile, setShowFile] = useState({ fileType: '', filePath: '' })
const option = {
name: 'file',
action: `${window.location.origin}${uploadFileUrl}`,
listType: listType,
withCredentials: true,
beforeUpload(file, fileList) {
/** @Tips: 解决提交文件中存在特殊字符的问题 */
let _continueUpload = true;
let _msg = {
type: 'success',
content: '上传成功!',
};
fileList.forEach(item => {
let _msgObject = filenameVerification(item);
if (_msgObject.type === 'error') {
_continueUpload = false;
_msg = {
type: 'error',
content: '上传失败!文件名不符合规则!',
};
}
});
_msg.type === 'error' ? message[_msg.type](_msg.content) : ''
return _continueUpload;
},
onChange: ({ file, fileList, event }) => {
// 检验名字,名字不通过不允许显示
if (filenameVerification(file).type === 'error') return false;
// 返回的链接在file.response内;不设置url,预览图表不可点击
if (file.status === 'done' && file.response.code === 0) {
file.url = `${downloadFileUrl}?filePath=${file.response.data}`
file.sourcePath = file.response.data;
message.success('上传成功!')
} else if (file.status === 'done' && file.response.code !== 0) {
file.status = 'error';
message.error('上传失败!')
}
if (Array.isArray(fileList)) {
let list = fileList.map(v => v.sourcePath || v.value)
onChange(list.join(','))
setShowList(JSON.stringify(fileList))
} else {
onChange('')
setShowList()
}
},
onPreview: (file) => {
let fileType = null
if (file.name.includes('docx')) {
fileType = 'docx'
} else if (file.name.includes('xlsx')) {
fileType = 'xlsx'
} else if (file.name.includes('jpg')) {
fileType = 'jpg'
}
if (fileType) {
setShowFile({ name: file.name, fileType: fileType, filePath: file.url })
setVisible(true)
}
}
}
const iconRender = (file, listType) => {
if (listType !== 'text') {
if (file.name.includes('docx')) {
return <div className={styles.iconImg} type='docx'></div>
} else if (file.name.includes('xlsx')) {
return <div className={styles.iconImg} type='xlsx'></div>
}
}
return <FileOutlined />
}
useEffect(() => {
if (value || presetValue) {
let fileList = [];
let list = (value || presetValue) && !(value || presetValue).includes('拍照相册') ? (value || presetValue).split(',') : []
list.forEach((item, index) => {
if (item) { // @Tips: 直接过滤掉名字中有异常字符的文件
let uid = index + '_' + Math.random()
let _obj = {
uid: uid,
value: item,
name: item.split('\\').reverse()[0],
type: fileType === '图片' ? 'image/jpeg' : 'file',
status: 'done',
url: `${downloadFileUrl}?filePath=${item}`,
sourcePath: item.url,
originFileObj: { "uid": uid },
"response": { "code": 0, "msg": "Ok", "data": item, "stackTrace": null },
"xhr": {},
};
if (schema.renderTo === 'Image') _obj.thumbUrl = `${downloadFileUrl}?filePath=${item}`;
fileList.push(_obj);
}
})
setShowList(JSON.stringify(fileList))
}
}, [value, presetValue])
return (
<div className={styles.uploadBox}>
<Upload
disabled={disabled}
accept={accepts[fileType]}
fileList={showList ? JSON.parse(showList) : []}
className="upload-list-inline"
iconRender={iconRender}
{...option}
>
{listType !== 'picture-card' ? <Button icon={<UploadOutlined />}>{placeholder || '上传'}</Button> : (disabled ? null : (placeholder || '+ 上传'))}
</Upload>
<Modal
width={'80%'}
title={showFile.name}
visible={visible}
onCancel={() => setVisible(false)}
bodyStyle={{ height: 650, overflowY: showFile.fileType.includes('xlsx') ? 'none' : "auto" }}
>
<FileViewer
title='123'
fileType={showFile.fileType}
filePath={window.location.origin + showFile.filePath}
/>
</Modal>
</div>
)
}
export default FileUpload
\ No newline at end of file
@imgSrc: '../../../../assets/images';
.uploadBox {
.iconImg {
width:45px;
height: 45px;
margin: 0 auto;
&[type='docx'] {
background: url('@{imgSrc}/Word.png');
background-size: 100% 100%;
}
&[type='xlsx'] {
background: url('@{imgSrc}/Excel.png');
background-size: 100% 100%;
}
}
}
\ No newline at end of file
import FileUpload from './FileUpload'
const file = {
FileUpload,
}
export default file
\ No newline at end of file
...@@ -3,15 +3,17 @@ import setting from './settings' ...@@ -3,15 +3,17 @@ import setting from './settings'
import text from './text' import text from './text'
import date from './date' import date from './date'
import select from './select' import select from './select'
// import file from './file' import file from './file'
// import coord from './coord' import coord from './coord'
export default { const widgets = {
Header, Header,
...setting, ...setting,
...text, ...text,
...date, ...date,
...select, ...select,
// ...file, ...file,
// ...coord, ...coord,
} }
export default widgets
\ No newline at end of file
// 选择器,值选择器 // 选择器,值选择器
import React, { useState, useMemo } from 'react' import React, { useState, useMemo } from 'react'
import { Select } from 'antd' import { Select } from 'antd'
import style from '../../style'
const { Option } = Select const { Option } = Select
...@@ -33,7 +34,7 @@ const ValueSelect = ({ value, onChange, schema }) => { ...@@ -33,7 +34,7 @@ const ValueSelect = ({ value, onChange, schema }) => {
return ( return (
<Select <Select
style={{ minWidth: 150 }} style={style}
mode={mode} mode={mode}
disabled={disabled} disabled={disabled}
showArrow={!disabled} showArrow={!disabled}
......
import React, { useState } from 'react'
import { Button, Form, Input, Select, Space, Modal } from 'antd'
import style from '../../style'
const options = [
{ label: '手动输入', value: '手动输入' },
{ label: '数据字典', value: '数据字典' },
]
const DataSource = (props) => {
console.log('props', props)
const { formData } = props.addons
const [visible, setVisible] = useState(false)
const [form] = Form.useForm()
const onFocus = () => {
setVisible(true)
}
const inputChange = (e) => {
console.log('e', e)
}
return (
<>
<Modal
title={`【${formData.$id}】数据来源`}
width='500px'
visible={visible}
onOk={() => setVisible(false)}
onCancel={() => setVisible(false)}
>
<Form
form={form}
>
<Form.Item
name="area"
label="数据来源"
rules={[
{
required: true,
message: 'Missing area',
},
]}
>
<Select options={options} />
</Form.Item>
</Form>
</Modal>
<Input value={valueShow} onChange={inputChange} style={style} onClick={onFocus} />
</>
)
}
export default DataSource
\ No newline at end of file
import React, { useState, useEffect, useMemo } from 'react' import React, { useState, useEffect, useMemo } from 'react'
import { DatePicker, Input } from 'antd' import { DatePicker, TimePicker, Input } from 'antd'
import moment from 'moment' import moment from 'moment'
import locale from 'antd/lib/date-picker/locale/zh_CN' import locale from 'antd/lib/date-picker/locale/zh_CN'
...@@ -13,12 +13,33 @@ const DateSelect = (props) => { ...@@ -13,12 +13,33 @@ const DateSelect = (props) => {
onChange(dateStr) onChange(dateStr)
} }
const onOk = (value) => {
console.log('value', value)
}
console.log('format', format)
if (format === 'dateTime') {
return (
<DatePicker
picker={format}
// value={valueShow}
onChange={dateChange}
onOk={onOk}
showNow
showTime
style={{ width: '100%' }}
locale={locale}
/>
)
}
return ( return (
<DatePicker <DatePicker
picker={format} picker={format}
// value={valueShow} // value={valueShow}
onChange={dateChange} onChange={dateChange}
showTime={format === 'dateTime'} onOk={onOk}
style={{ width: '100%' }} style={{ width: '100%' }}
locale={locale} locale={locale}
/> />
......
import React, { useMemo } from 'react' import React, { useMemo } from 'react'
import { Input, Select } from 'antd' import { Input, Select } from 'antd'
import style from '../../style'
const field = [ const field = [
"处理站点", "处理站点",
...@@ -90,7 +91,7 @@ const FieldNames = (props) => { ...@@ -90,7 +91,7 @@ const FieldNames = (props) => {
} }
return ( return (
<Select onChange={change} style={{ minWidth: '143px' }} value={valueShow}> <Select onChange={change} style={style} value={valueShow}>
{ {
field.map(v => <Select.Option key={v} value={v}>{v}</Select.Option>) field.map(v => <Select.Option key={v} value={v}>{v}</Select.Option>)
} }
......
...@@ -3,6 +3,7 @@ import EnumOptions from './EnumOptions' ...@@ -3,6 +3,7 @@ import EnumOptions from './EnumOptions'
import FieldNames from './FieldNames' import FieldNames from './FieldNames'
import CascadeField from './CascadeField' import CascadeField from './CascadeField'
import DateSelect from './DateSelect' import DateSelect from './DateSelect'
import DataSource from './DataSource'
const settings = { const settings = {
BooleanSwitch, BooleanSwitch,
...@@ -10,6 +11,7 @@ const settings = { ...@@ -10,6 +11,7 @@ const settings = {
FieldNames, FieldNames,
CascadeField, CascadeField,
DateSelect, DateSelect,
DataSource,
} }
export default settings export default settings
\ No newline at end of file
const style = {
width: '100%'
}
export default style
\ No newline at end of file
import React, { useMemo } from 'react' import React, { useMemo } from 'react'
import { InputNumber } from 'antd' import { InputNumber } from 'antd'
import style from '../../style'
const style = {
width: '100%'
}
const NumberInput = ({ value, onChange, schema }) => { const NumberInput = ({ value, onChange, schema }) => {
console.log('数字输入框', schema)
const { disabled, presetValue } = schema const { disabled, presetValue } = schema
const valueShow = useMemo(() => { const valueShow = useMemo(() => {
return presetValue || value return presetValue || value
}, [presetValue, value]) }, [presetValue, value])
return ( return (
<InputNumber <InputNumber
disabled={disabled} disabled={disabled}
......
This source diff could not be displayed because it is too large. You can view the blob instead.
...@@ -15,9 +15,6 @@ const TextArea = ({ value, onChange, schema }) => { ...@@ -15,9 +15,6 @@ const TextArea = ({ value, onChange, schema }) => {
onChange(e.target.value) onChange(e.target.value)
} }
// useEffect(() => {
// onChange(val)
// }, [])
return ( return (
<Input.TextArea <Input.TextArea
......
...@@ -3,6 +3,8 @@ import { Input } from 'antd' ...@@ -3,6 +3,8 @@ import { Input } from 'antd'
const TextInput = ({ value, onChange, schema }) => { const TextInput = ({ value, onChange, schema }) => {
console.log('文本输入框', schema)
const { disabled, placeholder, presetValue } = schema const { disabled, placeholder, presetValue } = schema
const valueShow = useMemo(() => { const valueShow = useMemo(() => {
...@@ -13,10 +15,6 @@ const TextInput = ({ value, onChange, schema }) => { ...@@ -13,10 +15,6 @@ const TextInput = ({ value, onChange, schema }) => {
onChange(e.target.value) onChange(e.target.value)
} }
// useEffect(() => {
// onChange(val)
// }, [])
return ( return (
<Input <Input
disabled={disabled} disabled={disabled}
......
import FormRender from './core/FormRender'
import FormDesigner from './core/FormDesigner' import FormDesigner from './core/FormDesigner'
import './main.less' import './main.less'
export { export {
FormDesigner FormRender,
FormDesigner,
} }
\ No newline at end of file
...@@ -6,6 +6,11 @@ ...@@ -6,6 +6,11 @@
//@parse-form-prefix-cls: ~'ant-parse-form'; //@parse-form-prefix-cls: ~'ant-parse-form';
.@{parse-form-prefix-cls} { .@{parse-form-prefix-cls} {
.dnd-container {
height: 700px;
}
.fr-generator-container .right-layout { .fr-generator-container .right-layout {
width: 20rem; width: 20rem;
} }
...@@ -132,10 +137,13 @@ ...@@ -132,10 +137,13 @@
} }
// 上传组件的图片类型按钮的disabled样式 // 上传组件的图片类型按钮的disabled样式
.@{ant-prefix}-upload.@{ant-prefix}-upload-disabled { .@{ant-prefix}-upload-disabled {
color: @disabledColor; color: @disabledColor;
cursor: default; cursor: default;
border: none; border: none;
.@{ant-prefix}-btn-default {
display: none;
}
} }
// 多选下的tag的disabled样式 // 多选下的tag的disabled样式
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment