Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
ni5
form-active表单可视化拖拽低代码平台
提交
d658d620
F
form-active表单可视化拖拽低代码平台
项目概览
ni5
/
form-active表单可视化拖拽低代码平台
与 Fork 源项目一致
Fork自
youjia727 / form-active表单可视化拖拽低代码平台
通知
1
Star
0
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
DevOps
流水线
流水线任务
计划
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
F
form-active表单可视化拖拽低代码平台
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
DevOps
DevOps
流水线
流水线任务
计划
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
流水线任务
提交
Issue看板
体验新版 GitCode,发现更多精彩内容 >>
提交
d658d620
编写于
5月 09, 2023
作者:
youjia727
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
增加层级联动逻辑关系设置
上级
c9b271f0
变更
20
隐藏空白更改
内联
并排
Showing
20 changed file
with
350 addition
and
166 deletion
+350
-166
web/.env
web/.env
+1
-1
web/.env.development
web/.env.development
+1
-1
web/.env.production
web/.env.production
+2
-2
web/.env.test
web/.env.test
+2
-2
web/.nvmrc
web/.nvmrc
+0
-1
web/package-lock.json
web/package-lock.json
+0
-2
web/package.json
web/package.json
+0
-2
web/src/App.tsx
web/src/App.tsx
+3
-2
web/src/assets/style/editorContext.less
web/src/assets/style/editorContext.less
+25
-3
web/src/assets/style/modal.less
web/src/assets/style/modal.less
+11
-6
web/src/assets/utils/formConfig/editorConfig.ts
web/src/assets/utils/formConfig/editorConfig.ts
+12
-12
web/src/assets/utils/tree.ts
web/src/assets/utils/tree.ts
+4
-4
web/src/components/CascaderList.tsx
web/src/components/CascaderList.tsx
+8
-5
web/src/components/Preview.tsx
web/src/components/Preview.tsx
+8
-5
web/src/components/formConfig/Address.tsx
web/src/components/formConfig/Address.tsx
+13
-22
web/src/components/formConfig/Cascader.tsx
web/src/components/formConfig/Cascader.tsx
+21
-30
web/src/components/formConfig/Input.tsx
web/src/components/formConfig/Input.tsx
+1
-1
web/src/components/modal/CascaderConfig.tsx
web/src/components/modal/CascaderConfig.tsx
+235
-62
web/src/components/modal/RuleInput.tsx
web/src/components/modal/RuleInput.tsx
+1
-1
web/src/hooks/useUpdate.ts
web/src/hooks/useUpdate.ts
+2
-2
未找到文件。
web/.env
浏览文件 @
d658d620
VITE_BASE_API="public_variable"
VITE_RANDOM = "
tInterview19a6e8cf8aa68bdf39bec4315f284e5dvv=
"
VITE_RANDOM = "
sdfsdfsdffdfgffgfghfhfghfghfghfgfghfghf
"
NODE_ENV = 'development'
\ No newline at end of file
web/.env.development
浏览文件 @
d658d620
VITE_BASE_API="http://192.168.110.72:8000"
VITE_RANDOM = "
tInterview19a6e8cf8aa68bdf39bec4315f284e5dvv=
"
VITE_RANDOM = "
asdasasdasasdfsfddgdfdfgfgdfgdf
"
NODE_ENV = 'development'
\ No newline at end of file
web/.env.production
浏览文件 @
d658d620
VITE_BASE_API="https://
workapi.dajishizhijia.com
"
VITE_RANDOM = "
tInterview19a6e8cf8aa68bdf39bec4315f284e5dvv=
"
VITE_BASE_API="https://
xxxxxxxxxxxxxxxxxxxxx
"
VITE_RANDOM = "
dsdsdfsdfsdfsdfsdfsdfsdfsdfsdfsdfs
"
NODE_ENV = 'production'
\ No newline at end of file
web/.env.test
浏览文件 @
d658d620
VITE_BASE_API
=
"https://
workapi-t.dajishizhijia.com
"
VITE_RANDOM
=
"
tInterview19a6e8cf8aa68bdf39bec4315f284e5dvv=
"
VITE_BASE_API
=
"https://
xxxxxxxxxxxxxxxxxxxxxxxxxxxx
"
VITE_RANDOM
=
"
sdfsdfsdfsdfsdfsdfddfgdfgdfdfgdfdfg
"
NODE_ENV
=
'production'
\ No newline at end of file
web/.nvmrc
已删除
100644 → 0
浏览文件 @
c9b271f0
v18.14.2
web/package-lock.json
浏览文件 @
d658d620
...
...
@@ -8,12 +8,10 @@
"name"
:
"vite-react-ts-admin"
,
"version"
:
"0.0.0"
,
"dependencies"
:
{
"@ant-design/cssinjs"
:
"^1.9.1"
,
"@ant-design/icons"
:
"^5.0.1"
,
"@dnd-kit/core"
:
"^6.0.8"
,
"@dnd-kit/modifiers"
:
"^6.0.1"
,
"@dnd-kit/sortable"
:
"^7.0.2"
,
"@dnd-kit/utilities"
:
"^3.2.1"
,
"antd"
:
"^5.4.6"
,
"dayjs"
:
"^1.11.7"
,
"less"
:
"^4.1.3"
,
...
...
web/package.json
浏览文件 @
d658d620
...
...
@@ -10,12 +10,10 @@
"preview"
:
"vite preview"
},
"dependencies"
:
{
"@ant-design/cssinjs"
:
"^1.9.1"
,
"@ant-design/icons"
:
"^5.0.1"
,
"@dnd-kit/core"
:
"^6.0.8"
,
"@dnd-kit/modifiers"
:
"^6.0.1"
,
"@dnd-kit/sortable"
:
"^7.0.2"
,
"@dnd-kit/utilities"
:
"^3.2.1"
,
"antd"
:
"^5.4.6"
,
"dayjs"
:
"^1.11.7"
,
"less"
:
"^4.1.3"
,
...
...
web/src/App.tsx
浏览文件 @
d658d620
...
...
@@ -2,10 +2,11 @@ import { App } from 'antd';
import
{
useLocation
,
useRoutes
}
from
'
react-router-dom
'
;
import
router
from
'
@/router
'
;
import
LoginLayout
from
'
@/components/layout/LoginLayout
'
;
import
{
ReactElement
}
from
'
react
'
;
function
View
()
{
// 解析路由配置表对象
const
outlet
=
useRoutes
(
router
);
const
outlet
=
useRoutes
(
router
)
as
ReactElement
;
// 初始化location对象实例
const
location
=
useLocation
();
...
...
@@ -13,7 +14,7 @@ function View() {
<
App
>
{
/* outlet 占位符,类似于窗口, 有点像vue中的router-view */
}
{
location
.
pathname
===
'
/login
'
?
<
LoginLayout
>
{
outlet
as
React
.
ReactElement
<
any
,
any
>
}
</
LoginLayout
>
:
<
LoginLayout
>
{
outlet
}
</
LoginLayout
>
:
<>
{
outlet
}
</>
}
</
App
>
...
...
web/src/assets/style/editorContext.less
浏览文件 @
d658d620
...
...
@@ -258,7 +258,7 @@
cursor: default;
}
.multipline {
.multipl
e-l
ine {
height: 58px;
padding-left: 10px;
border: 1px solid #e2e6ed;
...
...
@@ -476,10 +476,15 @@
margin-right: 0;
}
.delete-area-item {
.delete-area-item,
.delete-area-btn {
display: flex;
align-items: center;
}
.delete-area-btn {
display: none;
}
}
}
...
...
@@ -492,7 +497,7 @@
padding-bottom: 0;
.local-wrapper {
display:
flex
;
display:
none
;
align-items: center;
.local-text {
...
...
@@ -543,4 +548,21 @@
}
}
}
.cascader-info {
.local-wrapper {
display: flex;
}
}
.cascader-wrapper {
.cascader-item {
.delete-area-btn {
display: flex;
}
}
}
}
\ No newline at end of file
web/src/assets/style/modal.less
浏览文件 @
d658d620
.rule-wrapper {
padding
: 16px 0
;
padding
-top: 16px
;
}
.rule-boxs {
...
...
@@ -14,7 +14,7 @@
input {
flex: 1;
margin-left: 1
4
px;
margin-left: 1
2
px;
&:first-of-type {
margin-left: 0;
...
...
@@ -312,11 +312,15 @@
}
.cascader-select-wrapper {
max-height: 31
4
px;
max-height: 31
0
px;
padding: 8px 10px 0;
overflow: auto;
}
.last-cascader-select {
padding-right: 12px;
}
.cascader-title {
font-size: 15px;
font-weight: 600;
...
...
@@ -371,13 +375,14 @@
.cascader-add-wrapper {
font-size: 12px;
height: 32px;
line-height: 32px;
padding: 0 10px;
height: 36px;
line-height: 36px;
color: @primary-color;
text-align: center;
>div {
display: inline-block;
//
display: inline-block;
.add-cascader-item-icon {
margin-right: 6px;
...
...
web/src/assets/utils/formConfig/editorConfig.ts
浏览文件 @
d658d620
...
...
@@ -34,7 +34,7 @@ export interface baseProps {
export
interface
cascaderModeTypes
{
label
:
string
,
placeholder
:
string
text
:
string
}
/* 基本信息的配置 */
...
...
@@ -104,18 +104,18 @@ const cascaderInit = {
levelCount
:
3
,
//层级数
cascaderMode
:
[{
label
:
'
levelOne
'
,
placeholder
:
''
text
:
''
},
{
label
:
'
levelTwo
'
,
placeholder
:
''
text
:
''
},
{
label
:
'
levelThree
'
,
placeholder
:
''
text
:
''
}],
setDetail
:
tru
e
,
setDetail
:
fals
e
,
details
:
{
label
:
'
details
'
,
placeholder
:
'
详细内容
输入区
'
text
:
'
填写者
输入区
'
},
// 配置option的属性
fieldNames
:
{
...
...
@@ -222,7 +222,7 @@ export const baseCompList: Array<baseCompProps> = [{
icon
:
'
a-ziyuan22
'
,
tag
:
'
date
'
},
{
label
:
'
层级联动
'
,
label
:
'
级联选择
'
,
icon
:
'
guanlian
'
,
tag
:
'
cascader
'
}];
...
...
@@ -290,19 +290,19 @@ export const templateCompList = [{
title
:
'
地址
'
,
cascaderMode
:
[{
label
:
'
province
'
,
placeholder
:
'
省/自治区/直辖市
'
text
:
'
省/自治区/直辖市
'
},
{
label
:
'
city
'
,
placeholder
:
'
市
'
text
:
'
市
'
},
{
label
:
'
district
'
,
placeholder
:
'
区/县
'
text
:
'
区/县
'
}],
options
:
[],
//配置数据
setDetail
:
tru
e
,
//是否设置其他输入
setDetail
:
fals
e
,
//是否设置其他输入
details
:
{
label
:
'
address
'
,
placeholder
:
'
详细地址
'
text
:
'
详细地址
'
}
}
}];
\ No newline at end of file
web/src/assets/utils/tree.ts
浏览文件 @
d658d620
export
const
options
=
[{
value
:
'
1
'
,
value
:
''
,
text
:
''
,
children
:
[{
value
:
'
2
'
,
value
:
''
,
text
:
''
,
children
:
[{
value
:
'
3
'
,
value
:
''
,
text
:
''
,
children
:
[{
value
:
'
4
'
,
value
:
''
,
text
:
''
,
},
{
value
:
''
,
...
...
web/src/components/CascaderList.tsx
浏览文件 @
d658d620
import
{
useState
,
memo
,
useMemo
,
useEffect
}
from
'
react
'
;
import
{
Popover
,
Input
,
Cascader
}
from
'
antd
'
;
import
{
Popover
,
Cascader
}
from
'
antd
'
;
import
{
CheckOutlined
,
CaretDownOutlined
,
CloseCircleFilled
}
from
'
@ant-design/icons
'
;
import
'
@/assets/style/cascaderList.less
'
;
...
...
@@ -77,7 +77,6 @@ function CascaderList(props: propTypes) {
return
treeToList
(
options
);
},
[])
// 控制弹框显示
const
[
open
,
setOpen
]
=
useState
(
false
);
// 当前选中数据
...
...
@@ -130,6 +129,10 @@ function CascaderList(props: propTypes) {
if
(
!
checkedList
.
length
||
tabKey
===
0
)
return
options
;
return
allCascaderList
.
find
(
item
=>
item
.
text
===
checkedList
[
tabKey
-
1
])?.
children
;
};
/* 当前选中信息 */
const
checkSelectOption
=
(
text
:
string
,
key
:
number
)
=>
{
return
checkedList
[
key
]
===
text
;
};
/* 完成全部选择 */
const
onSelectCascaderFinish
=
(
selectValues
:
Array
<
string
>
)
=>
{
setOpen
(
false
);
...
...
@@ -170,9 +173,9 @@ function CascaderList(props: propTypes) {
<
ul
className
=
'cascader-list-wrapper'
>
{
renderCallback
(
checkedList
,
activeKey
)?.
map
((
item
:
objTypes
)
=>
(
<
li
key
=
{
item
.
value
}
onClick
=
{
(
e
)
=>
handleSelectCascader
(
e
,
item
.
text
)
}
className
=
{
`
${
check
edList
.
includes
(
item
.
text
)
?
'
checked-cascader
'
:
''
}
cursor`
}
className
=
{
`
${
check
SelectOption
(
item
.
text
,
activeKey
)
?
'
checked-cascader
'
:
''
}
cursor`
}
>
{
check
edList
.
includes
(
item
.
text
)
?
<
CheckOutlined
className
=
'checked-cascader-icon primary-color'
/>
:
null
}
{
check
SelectOption
(
item
.
text
,
activeKey
)
?
<
CheckOutlined
className
=
'checked-cascader-icon primary-color'
/>
:
null
}
<
span
>
{
item
.
text
}
</
span
>
</
li
>
))
}
...
...
@@ -197,7 +200,7 @@ function CascaderList(props: propTypes) {
allowClear
open
=
{
false
}
value
=
{
renderCheckedText
}
placeholder
=
{
placeholder
??
'
请选择
'
}
placeholder
=
{
placeholder
||
'
请选择
'
}
displayRender
=
{
()
=>
checkedList
.
join
(
separator
)
}
suffixIcon
=
{
<
CaretDownOutlined
/>
}
clearIcon
=
{
<
CloseCircleFilled
onClick
=
{
handleClear
}
/>
}
...
...
web/src/components/Preview.tsx
浏览文件 @
d658d620
...
...
@@ -10,7 +10,7 @@ import CascaderList from '@/components/CascaderList';
import
utils
from
'
@/assets/utils
'
;
import
dayjs
from
'
dayjs
'
;
import
{
useUpdate
}
from
'
@/hooks/useUpdate
'
;
import
{
baseProps
,
optionProps
,
objProps
}
from
'
@/assets/utils/formConfig/editorConfig
'
;
import
{
baseProps
,
optionProps
,
objProps
,
cascaderModeTypes
}
from
'
@/assets/utils/formConfig/editorConfig
'
;
import
'
@/assets/style/preview.less
'
;
const
{
TextArea
}
=
Input
;
...
...
@@ -484,8 +484,11 @@ function Preview(props: propTypes) {
validateFormItem
(
attr
,
arr
);
};
/* 层级联动的文本占位符提示 */
const
cascaderPlaceholderCallback
=
(
cascaderMode
:
Array
<
{
label
:
string
,
placeholder
:
string
}
>
)
=>
{
const
placeholderList
=
cascaderMode
.
map
(
item
=>
item
.
placeholder
);
const
cascaderPlaceholderCallback
=
(
cascaderMode
:
Array
<
cascaderModeTypes
>
)
=>
{
let
placeholderList
:
Array
<
string
>
=
[];
cascaderMode
.
forEach
(
item
=>
{
item
.
text
.
trim
().
length
?
placeholderList
.
push
(
item
.
text
)
:
null
;
})
return
placeholderList
.
join
(
'
/
'
);
};
/* 点击提交数据 */
...
...
@@ -731,8 +734,8 @@ function Preview(props: propTypes) {
{
item
.
type
===
'
cascader
'
?
<
div
className
=
'space-wrapper'
>
<
CascaderList
column
=
{
item
.
cascaderMode
.
length
}
options
=
{
options
}
column
=
{
item
.
levelCount
}
options
=
{
item
.
tag
===
'
address
'
?
options
:
item
.
options
}
checkedValue
=
{
submitValues
[
item
.
id
]?.
value
}
onFinish
=
{
(
checkedList
:
Array
<
string
>
)
=>
validateFormItem
(
item
.
id
,
checkedList
)
}
placeholder
=
{
cascaderPlaceholderCallback
(
item
.
cascaderMode
)
}
...
...
web/src/components/formConfig/Address.tsx
浏览文件 @
d658d620
import
{
memo
}
from
'
react
'
;
import
{
useUpdate
}
from
'
@/hooks/useUpdate
'
;
import
{
PlusCircleOutlined
,
EnvironmentOutlined
,
CloseOutlined
,
CaretDownOutlined
}
from
'
@ant-design/icons
'
;
import
{
baseProps
}
from
'
@/assets/utils/formConfig/editorConfig
'
;
import
{
PlusCircleOutlined
,
EnvironmentOutlined
,
CloseOutlined
,
CaretDownOutlined
}
from
'
@ant-design/icons
'
;
import
{
baseProps
,
cascaderModeTypes
}
from
'
@/assets/utils/formConfig/editorConfig
'
;
type
propsType
=
{
item
:
baseProps
}
type
cascaderObj
=
{
label
:
string
,
placeholder
:
string
}
const
cascaderList
=
[{
label
:
'
province
'
,
placeholder
:
'
省/自治区/直辖市
'
text
:
'
省/自治区/直辖市
'
},
{
label
:
'
city
'
,
placeholder
:
'
市
'
text
:
'
市
'
},
{
label
:
'
district
'
,
placeholder
:
'
区/县
'
text
:
'
区/县
'
}];
/* 层级联动 */
...
...
@@ -49,13 +40,13 @@ function AddressCascader(props: propsType) {
})
};
/* 添加层级的文字提示 */
const
addressTextCallback
=
(
cascaderItem
:
{
label
:
string
,
placeholder
:
string
}
)
=>
{
const
addressTextCallback
=
(
cascaderItem
:
cascaderModeTypes
)
=>
{
const
idx
=
cascaderList
.
findIndex
(
el
=>
el
.
label
===
cascaderItem
.
label
);
return
cascaderList
[
idx
+
1
].
placeholder
;
return
cascaderList
[
idx
+
1
].
text
;
};
/* 添加层级 */
const
handleAddCascader
=
()
=>
{
const
cascaderItem
=
item
.
cascaderMode
[
item
.
cascaderMode
.
length
-
1
];
const
cascaderItem
:
cascaderModeTypes
=
item
.
cascaderMode
[
item
.
cascaderMode
.
length
-
1
];
const
idx
=
cascaderList
.
findIndex
(
el
=>
el
.
label
===
cascaderItem
.
label
);
update
(()
=>
{
item
.
cascaderMode
.
push
(
cascaderList
[
idx
+
1
]);
...
...
@@ -65,16 +56,16 @@ function AddressCascader(props: propsType) {
return
(
<>
<
div
className
=
'cascader-wrapper'
>
{
item
.
cascaderMode
.
map
((
cascader
:
cascader
Obj
,
idx
:
number
)
=>
(
{
item
.
cascaderMode
.
map
((
cascader
:
cascader
ModeTypes
,
idx
:
number
)
=>
(
<
div
className
=
'cascader-item'
key
=
{
cascader
.
label
}
>
<
span
>
{
cascader
.
placeholder
}
</
span
>
<
span
>
{
cascader
.
text
}
</
span
>
<
div
className
=
'delete-area-item'
>
<
CaretDownOutlined
/>
{
idx
+
1
===
item
.
cascaderMode
.
length
&&
idx
!==
0
?
<>
<
div
className
=
'delete-area-btn'
>
<
div
className
=
'line cascader-line'
></
div
>
<
CloseOutlined
onClick
=
{
()
=>
handleDeleteCascader
(
idx
)
}
className
=
'hover-color'
title
=
'删除'
/>
</>
</
div
>
:
null
}
</
div
>
...
...
@@ -83,7 +74,7 @@ function AddressCascader(props: propsType) {
</
div
>
{
item
.
setDetail
?
<
div
className
=
"placeholder-info cascader-info"
>
<
span
>
{
item
.
details
.
placeholder
}
</
span
>
<
span
>
{
item
.
details
.
text
}
</
span
>
<
div
className
=
'local-wrapper'
>
{
item
.
tag
===
'
address
'
?
<>
...
...
web/src/components/formConfig/Cascader.tsx
浏览文件 @
d658d620
import
{
useState
,
memo
}
from
'
react
'
;
import
{
useUpdate
}
from
'
@/hooks/useUpdate
'
;
import
{
PlusCircleOutlined
,
CloseOutlined
,
CaretDownOutlined
,
EditOutlined
}
from
'
@ant-design/icons
'
;
import
{
baseProps
,
objProps
}
from
'
@/assets/utils/formConfig/editorConfig
'
;
import
{
PlusCircleOutlined
,
CloseOutlined
,
CaretDownOutlined
,
EditOutlined
}
from
'
@ant-design/icons
'
;
import
{
baseProps
,
cascaderModeTypes
,
objProps
}
from
'
@/assets/utils/formConfig/editorConfig
'
;
import
CascaderConfig
from
'
../modal/CascaderConfig
'
;
type
propsType
=
{
item
:
baseProps
}
type
cascaderObj
=
{
label
:
string
,
placeholder
:
string
}
/* 下拉选择的内容 */
const
options
=
[{
value
:
1
,
label
:
'
一级联动
'
},
{
value
:
2
,
label
:
'
二级联动
'
},
{
value
:
3
,
label
:
'
三级联动
'
}]
/* 层级联动 */
function
Cascader
(
props
:
propsType
)
{
...
...
@@ -44,17 +23,30 @@ function Cascader(props: propsType) {
item
.
setDetail
=
visible
;
})
};
/* placeholder 占位提示语 */
const
placeholderCallback
=
(
tipList
:
Array
<
cascaderModeTypes
>
)
=>
{
const
tipsText
:
Array
<
string
>
=
[];
tipList
.
forEach
(
tips
=>
{
tips
.
text
.
trim
().
length
?
tipsText
.
push
(
tips
.
text
)
:
null
;
})
return
tipsText
.
length
?
'
(
'
+
tipsText
.
join
(
'
/
'
)
+
'
)
'
:
''
;
};
/* 编辑层级联动配置的回调函数 */
const
cascaderCallback
=
(
visible
:
boolean
,
cascaderRule
?:
objProps
)
=>
{
cascaderRule
?
Object
.
assign
(
item
,
cascaderRule
)
:
null
;
setOpen
(
false
)
const
cascaderCallback
=
(
visible
:
boolean
,
config
?:
objProps
)
=>
{
console
.
log
(
config
)
if
(
config
)
{
item
.
options
=
config
.
options
;
item
.
cascaderMode
=
config
.
title
;
item
.
levelCount
=
config
.
count
;
}
setOpen
(
visible
);
};
return
(
<>
<
div
className
=
'cascader-wrapper'
>
<
div
className
=
'cascader-item'
>
<
span
>
填写者选择区
</
span
>
<
span
>
填写者选择区
{
placeholderCallback
(
item
.
cascaderMode
)
}
</
span
>
<
div
className
=
'delete-area-item'
>
<
CaretDownOutlined
/>
</
div
>
...
...
@@ -62,7 +54,7 @@ function Cascader(props: propsType) {
</
div
>
{
item
.
setDetail
?
<
div
className
=
"placeholder-info cascader-info"
>
<
span
>
{
item
.
details
.
placeholder
}
</
span
>
<
span
>
{
item
.
details
.
text
}
</
span
>
<
div
className
=
'local-wrapper'
>
<
CloseOutlined
onClick
=
{
()
=>
handleSetDetail
(
false
)
}
className
=
'hover-color'
title
=
'删除'
/>
</
div
>
...
...
@@ -73,13 +65,12 @@ function Cascader(props: propsType) {
<
EditOutlined
className
=
'icon-block'
/>
<
span
>
编辑选项
</
span
>
</
div
>
{
!
(
item
.
cascaderMode
.
length
>=
3
)
&&
!
item
.
setDetail
?
<
div
className
=
'split-add'
></
div
>
:
null
}
{
!
item
.
setDetail
?
<>
<
div
className
=
'split-add'
></
div
>
<
div
onClick
=
{
()
=>
handleSetDetail
(
true
)
}
className
=
'setting-block opacity'
>
<
PlusCircleOutlined
className
=
'icon-block'
/>
<
span
>
添加详细
</
span
>
<
span
>
添加详细
输入
</
span
>
</
div
>
</>
:
null
}
...
...
web/src/components/formConfig/Input.tsx
浏览文件 @
d658d620
...
...
@@ -39,7 +39,7 @@ function View(props: propsTypes) {
return
(
<>
{
/* multiline 代表多行输入 */
}
<
div
className
=
{
`placeholder-info
${
tag
===
'
textarea
'
?
'
multiline
'
:
''
}
`
}
>
填写者回答区
</
div
>
<
div
className
=
{
`placeholder-info
${
tag
===
'
textarea
'
?
'
multi
ple-
line
'
:
''
}
`
}
>
填写者回答区
</
div
>
<
div
className
=
"form-item-setting"
>
<
div
className
=
'setting-block opacity'
onClick
=
{
()
=>
setOpen
(
true
)
}
>
<
PlusCircleOutlined
className
=
'icon-block'
/>
...
...
web/src/components/modal/CascaderConfig.tsx
浏览文件 @
d658d620
import
{
useState
}
from
'
react
'
;
import
{
useState
,
useRef
}
from
'
react
'
;
import
{
PlusCircleOutlined
,
MinusCircleOutlined
,
RightOutlined
,
CaretDownOutlined
}
from
'
@ant-design/icons
'
;
import
{
Modal
,
Input
,
Select
}
from
'
antd
'
;
import
utils
from
'
@/assets/utils
'
;
import
{
useUpdate
}
from
'
@/hooks/useUpdate
'
;
import
{
options
}
from
'
@/assets/utils/tree
'
;
import
useMessage
from
'
@/hooks/useMessage
'
;
import
{
baseProps
,
cascaderModeTypes
}
from
'
@/assets/utils/formConfig/editorConfig
'
;
import
'
@/assets/style/modal.less
'
;
...
...
@@ -20,28 +20,24 @@ type propTypes = {
type
cascaderOptionTypes
=
{
value
:
string
,
text
:
string
,
children
?:
Array
<
cascaderOptionTypes
>
children
?:
Array
<
cascaderOptionTypes
>
,
[
key
:
string
]:
any
}
// 层级联动配置
const
cascaderMode
=
[{
const
cascaderMode
List
=
[{
label
:
'
levelOne
'
,
placeholder
:
''
text
:
''
},
{
label
:
'
levelTwo
'
,
placeholder
:
''
text
:
''
},
{
label
:
'
levelThree
'
,
placeholder
:
''
text
:
''
},
{
label
:
'
levelFour
'
,
placeholder
:
''
}]
// 将字符串转换成js执行
function
stringToJs
(
str
:
string
)
{
return
new
Function
(
'
return
'
+
str
)();
};
text
:
''
}];
// 截取树的节点
function
treeDataSlice
<
T
,
>
(
treeList
:
Array
<
T
>
,
level
:
number
)
{
...
...
@@ -55,110 +51,281 @@ function treeDataSlice<T,>(treeList: Array<T>, level: number) {
level
&&
treeDataSlice
(
ele
.
children
,
level
);
}
};
// 返回列表显示
function
treeListCallback
<
T
,
>
(
treeList
:
Array
<
T
>
=
[],
idx
:
number
):
any
{
if
(
!
idx
)
return
treeList
;
idx
--
;
return
treeListCallback
(((
treeList
[
idx
])
as
cascaderOptionTypes
)?.
children
||
[],
idx
)
};
// 修改层级选项的数据(input输入)
function
editCascaderOptionValue
(
treeList
:
Array
<
cascaderOptionTypes
>
=
[],
value
:
string
,
idx
:
number
,
optIdx
:
number
):
Array
<
cascaderOptionTypes
>
{
const
newList
=
treeListCallback
(
treeList
,
idx
);
newList
[
optIdx
].
text
=
value
;
newList
[
optIdx
].
value
=
value
;
return
treeList
;
};
// 生成树形结构数据
function
initTreeData
<
T
,
>
(
treeList
:
Array
<
T
>
,
level
:
number
)
{
if
(
level
===
4
)
return
options
;
// 深拷贝一份
const
cloneTreeList
=
utils
.
deepClone
(
treeList
);
treeDataSlice
(
cloneTreeList
,
level
);
console
.
log
(
cloneTreeList
)
return
cloneTreeList
;
};
// 树结构的节点
// function addTreeData(nodeCount: number, level: number) {
// // nodeCount 节点数, level 层级数,从0开始
// const treeNode: cascaderOptionTypes = {
// value: '',
// text: '',
// level
// }
// if (nodeCount === 1) return treeNode;
// nodeCount--;
// treeNode['children'] = [addTreeData(nodeCount, level), addTreeData(nodeCount, level)];
// return treeNode;
// };
function
addTreeData
(
nodeCount
:
number
)
{
// nodeCount 节点数
const
treeNode
:
cascaderOptionTypes
=
{
value
:
''
,
text
:
''
}
if
(
nodeCount
===
1
)
return
treeNode
;
nodeCount
--
;
treeNode
[
'
children
'
]
=
[
addTreeData
(
nodeCount
),
addTreeData
(
nodeCount
)];
return
treeNode
;
};
// 将树形结构转换成数组
function
treeToList
<
T
>
(
tree
:
Array
<
T
>
)
{
let
res
:
Array
<
T
>
=
[];
let
id
=
0
;
formateData
(
tree
,
0
);
function
formateData
(
tree
:
Array
<
T
>
,
level
:
number
,
pid
?:
number
)
{
for
(
let
i
=
0
;
i
<
tree
.
length
;
i
++
)
{
let
count
=
level
||
0
;
const
element
=
tree
[
i
]
as
cascaderOptionTypes
;
element
[
'
level
'
]
=
count
;
element
[
'
id
'
]
=
id
;
element
[
'
pid
'
]
=
pid
;
res
.
push
(
element
as
T
);
id
++
;
if
(
element
.
children
)
{
count
++
;
formateData
(
element
.
children
as
Array
<
T
>
,
count
,
element
.
id
);
}
}
}
return
res
;
};
/* 返回增加节点的新树结构 */
function
addTreeNodeCallback
<
T
>
(
treeList
:
Array
<
T
>
,
level
:
number
):
Array
<
T
>
{
let
newList
=
[...
treeList
];
for
(
let
i
=
0
;
i
<
newList
.
length
;
i
++
)
{
const
element
=
newList
[
i
]
as
cascaderOptionTypes
;
if
(
!
element
.
children
)
{
// 增加新节点到树上结构
const
treeNode
=
addTreeData
(
level
);
element
[
'
children
'
]
=
[
treeNode
,
treeNode
];
}
else
{
addTreeNodeCallback
(
element
.
children
,
level
);
}
}
return
newList
;
};
const
numArrIndex
=
[
'
一
'
,
'
二
'
,
'
三
'
,
'
四
'
];
/* 层级联动数据配置 */
function
CascaderConfig
(
props
:
propTypes
)
{
const
{
open
,
cancel
,
item
}
=
props
;
// console.log('item===========', item)
const
update
=
useUpdate
();
const
selectRef
=
useRef
(
null
);
const
message
=
useMessage
();
// 控制弹框显示
const
[
show
,
setShow
]
=
useState
(
open
);
// 是否点击确认按钮
const
[
clickBtn
,
setClickBtn
]
=
useState
(
false
);
// 选择层级联动次数
const
[
visible
,
setVisible
]
=
useState
(
false
);
// 当前编辑的列索引
const
[
idxObj
,
setIdxObj
]
=
useState
({
idx0
:
0
,
idx1
:
0
,
idx2
:
0
,
idx3
:
0
,
idx4
:
0
idx3
:
0
})
// 层级联动级数
const
[
count
,
setCount
]
=
useState
<
number
>
(
item
.
levelCount
||
3
);
// 层级联动显示数据
const
[
cascaderMode
,
setCascaderMode
]
=
useState
<
Array
<
cascaderModeTypes
>>
(
item
.
cascaderMode
);
// 层级联动列表
const
[
cascaderOption
,
setCascaderOption
]
=
useState
(()
=>
{
// 层级联动
树型
列表
const
[
cascaderOption
,
setCascaderOption
]
=
useState
<
Array
<
cascaderOptionTypes
>>
(()
=>
{
return
item
.
options
.
length
?
item
.
options
:
initTreeData
(
options
,
count
);
});
// 层级联动数组列表
// const [cascaderList, setCascaderList] = useState<Array<cascaderOptionTypes>>(treeToList(cascaderOption));
/* 标题输入框内容发生变化 */
const
titleInputChange
=
(
value
:
string
,
idx
:
number
)
=>
{
setCascaderMode
(
preList
=>
{
const
newList
=
utils
.
deepClone
(
preList
);
newList
[
idx
].
placeholder
=
value
;
newList
[
idx
].
text
=
value
;
return
newList
;
})
};
/* 筛选出需要修改的层级下标索引 */
const
levelNumberCallback
=
(
idx
:
number
)
=>
{
const
arr
=
[
0
,
1
,
2
,
3
];
return
arr
.
filter
(
el
=>
el
>
idx
);
};
/* 当前选项选中 */
const
handleSelectActive
=
(
idx
:
number
,
optIdx
:
number
)
=>
{
const
newObj
=
{
...
idxObj
};
const
arr
=
[
0
,
1
,
2
,
3
];
const
newArr
=
arr
.
filter
(
el
=>
el
>
idx
);
const
newArr
=
levelNumberCallback
(
idx
);
// 修改当前选中项,后面层级选中第一个
for
(
let
i
=
0
;
i
<
newArr
.
length
;
i
++
)
{
newObj
[(
'
idx
'
+
(
newArr
[
i
]
+
1
)
)
as
keyof
typeof
idxObj
]
=
0
;
newObj
[(
'
idx
'
+
newArr
[
i
]
)
as
keyof
typeof
idxObj
]
=
0
;
}
// 设置当前选中状态
newObj
[(
'
idx
'
+
(
idx
+
1
)
)
as
keyof
typeof
idxObj
]
=
optIdx
;
newObj
[(
'
idx
'
+
idx
)
as
keyof
typeof
idxObj
]
=
optIdx
;
setIdxObj
(
newObj
);
};
/* 生成当前显示选项列表 */
const
treeListCallback
=
(
treeList
:
Array
<
cascaderOptionTypes
>
=
[],
idx
:
number
):
Array
<
cascaderOptionTypes
>
=>
{
if
(
!
idx
)
return
treeList
;
let
renderCascaderOptions
:
Array
<
cascaderOptionTypes
>
=
[];
for
(
let
i
=
0
;
i
<
idx
;
i
++
)
{
// 上一层级选中的索引值
const
preEditIdx
=
idxObj
[(
'
idx
'
+
i
)
as
keyof
typeof
idxObj
];
// 上一层级的孩子列表
renderCascaderOptions
=
(
renderCascaderOptions
.
length
?
renderCascaderOptions
[
preEditIdx
].
children
:
treeList
[
preEditIdx
].
children
)
??
[];
}
return
renderCascaderOptions
;
};
/* 级联列表显示 */
const
renderListCallback
=
(
idx
:
number
,
treeList
?:
Array
<
cascaderOptionTypes
>
)
=>
{
return
treeListCallback
(
treeList
??
utils
.
deepClone
(
cascaderOption
),
idx
);
};
/* 修改层级选项的数据(input输入) */
const
editCascaderOptionValue
=
(
treeList
:
Array
<
cascaderOptionTypes
>
=
[],
value
:
string
,
idx
:
number
,
optIdx
:
number
)
=>
{
const
newList
=
renderListCallback
(
idx
,
treeList
);
newList
[
optIdx
].
text
=
value
;
newList
[
optIdx
].
value
=
value
;
return
treeList
;
};
/* 选项输入框内容变化 */
const
cascaderInputChange
=
(
value
:
string
,
idx
:
number
,
optIdx
:
number
)
=>
{
const
newData
=
editCascaderOptionValue
(
utils
.
deepClone
(
cascaderOption
),
value
,
idx
,
optIdx
);
setCascaderOption
(
newData
);
};
/* 点击选择层级联动变化的图标 */
const
handleSelect
=
()
=>
{
const
{
current
}
=
selectRef
;
current
&&
(
current
as
objProps
).
focus
();
};
/* 增加节点标题 */
const
addTreeOptionTitle
=
<
T
,
>
(
modeList
:
Array
<
T
>
,
level
:
number
):
Array
<
T
>
=>
{
const
sliceList
=
cascaderModeList
.
slice
(
modeList
.
length
);
const
newModeList
=
[...
modeList
];
for
(
let
i
=
0
;
i
<
level
;
i
++
)
{
const
element
=
sliceList
[
i
]
as
T
;
newModeList
.
push
(
element
)
}
return
newModeList
;
};
/* 选择层级联动级数变化 */
const
handleChange
=
(
value
:
number
)
=>
{
let
treeData
:
Array
<
cascaderOptionTypes
>
=
[];
let
newCascaderMode
:
Array
<
cascaderModeTypes
>
=
[];
if
(
value
>
count
)
{
// 增加节点
treeData
=
addTreeNodeCallback
(
cascaderOption
,
value
-
count
);
//增加节点树
newCascaderMode
=
addTreeOptionTitle
(
cascaderMode
,
value
-
count
);
// 增加节点标题
}
else
{
// 删除节点
treeData
=
initTreeData
(
cascaderOption
,
value
);
//删除节点树
newCascaderMode
=
[...
cascaderMode
].
slice
(
0
,
value
);
// 删除节点标题
}
handleSelectActive
(
0
,
0
);
setCascaderOption
(
treeData
);
setCascaderMode
(
newCascaderMode
);
setCount
(
value
);
};
/* 级联列表显示 */
const
renderListCallback
=
(
idx
:
number
)
=>
{
console
.
log
(
'
idx=======
'
,
idx
)
return
treeListCallback
(
cascaderOption
,
idx
);
/* 增加选项的回调函数 */
const
editCascaderOptionCallback
=
(
treeList
:
Array
<
cascaderOptionTypes
>
,
level
:
number
,
type
:
string
,
treeNode
?:
cascaderOptionTypes
,
optIdx
?:
number
)
=>
{
const
cloneTreeList
=
utils
.
deepClone
(
treeList
);
let
childrenList
:
Array
<
cascaderOptionTypes
>
=
[];
for
(
let
i
=
0
;
i
<
level
;
i
++
)
{
// 上一层级选中的索引值
const
preEditIdx
=
idxObj
[(
'
idx
'
+
i
)
as
keyof
typeof
idxObj
];
childrenList
=
(
childrenList
.
length
?
childrenList
[
preEditIdx
].
children
:
cloneTreeList
[
preEditIdx
].
children
)
||
[];
}
// 限制至少设置一个选项
if
(
type
===
'
delete
'
&&
childrenList
.
length
<=
1
)
{
message
.
info
(
'
至少设置一个选项
'
);
return
false
;
}
type
===
'
add
'
?
childrenList
.
push
(
treeNode
as
cascaderOptionTypes
)
:
childrenList
.
splice
(
optIdx
as
number
,
1
);
setCascaderOption
(
cloneTreeList
);
};
/* 添加层级的选项 */
const
handleAddCascaderOption
=
(
idx
:
number
)
=>
{
const
levelList
=
cascaderMode
.
map
((
el
,
i
)
=>
i
+
1
).
reverse
();
const
treeNode
=
addTreeData
(
levelList
[
idx
]);
//当前选项节点
// 如果是第一级就直接添加,其它级需要插入到上一级选中的children中
idx
?
editCascaderOptionCallback
(
cascaderOption
,
idx
,
'
add
'
,
treeNode
)
:
setCascaderOption
([...
cascaderOption
,
treeNode
]);
};
/* 删除层级选项 */
const
handleDeleteCascaderOption
=
(
level
:
number
,
optIdx
:
number
)
=>
{
const
cloneCascaderOption
=
[...
cascaderOption
];
if
(
!
level
)
{
// 限制至少设置一个选项
if
(
cloneCascaderOption
.
length
<=
1
)
{
message
.
info
(
'
至少设置一个选项
'
);
return
false
;
}
// 如果是第一级就直接删除选项
cloneCascaderOption
.
splice
(
optIdx
,
1
);
setCascaderOption
(
cloneCascaderOption
);
}
else
{
// 如果是其它层级就需要找到上一节点children的列表,再删除当前选项
editCascaderOptionCallback
(
cascaderOption
,
level
,
'
delete
'
,
undefined
,
optIdx
);
};
handleSelectActive
(
level
,
optIdx
-
1
);
};
/* 判断是否有相同的值 */
const
uniqueValueCallback
=
(
treeList
:
Array
<
cascaderOptionTypes
>
):
boolean
=>
{
const
values
:
Array
<
string
>
=
[];
//数据数组,判断是否重复
return
treeList
.
some
(
el
=>
{
// 判断是否是空字符串
if
(
!
el
.
value
.
trim
().
length
)
{
el
.
value
.
length
>
0
?
message
.
info
(
'
选项内容不能为空
'
)
:
message
.
info
(
'
请输入选项内容
'
);
return
true
;
};
// 判断是否内容重复
if
(
values
.
includes
(
el
.
value
))
{
message
.
info
(
'
选项内容重复,请修改
'
)
return
true
;
}
else
{
values
.
push
(
el
.
value
);
};
// 如果有children就递归判断内容
if
(
el
.
children
)
{
return
uniqueValueCallback
(
el
.
children
);
};
});
};
/* 点击确认操作 */
const
handleOk
=
()
=>
{
setShow
(
false
);
if
(
!
uniqueValueCallback
(
cascaderOption
))
{
setShow
(
false
);
setClickBtn
(
true
);
}
};
/* modal框完全关闭之后 */
const
afterClose
=
()
=>
{
if
(
!
clickBtn
)
return
cancel
(
false
);
// 关闭弹框,并且传递数据
// let ruleObj = {
// options: clone.options,
// fieldNames: clone.fieldNames,
// cascaderPlaceholder: clone.cascaderPlaceholder,
// mode: clone.mode
// }
cancel
(
false
);
const
configData
=
{
options
:
cascaderOption
,
title
:
cascaderMode
,
count
}
cancel
(
false
,
configData
);
};
return
(
...
...
@@ -176,15 +343,16 @@ function CascaderConfig(props: propTypes) {
<
div
className
=
'cascader-config-wrapper'
>
{
cascaderMode
.
map
((
ele
,
idx
)
=>
(
<
div
className
=
'cascader-config-item'
key
=
{
ele
.
label
}
>
<
div
className
=
'cascader-select-wrapper'
>
<
div
className
=
{
`cascader-select-wrapper
${
cascaderMode
.
length
===
idx
+
1
?
'
last-cascader-select
'
:
''
}
`
}
>
<
Input
className
=
'form-item-input cascader-title'
value
=
{
ele
.
placeholder
}
onChange
=
{
e
=>
titleInputChange
(
e
.
target
.
value
,
idx
)
}
value
=
{
ele
.
text
}
onChange
=
{
e
=>
titleInputChange
(
e
.
target
.
value
,
idx
)
}
placeholder
=
{
numArrIndex
[
idx
]
+
'
级标题名称
'
}
/>
{
renderListCallback
(
idx
).
map
((
opt
:
cascaderOptionTypes
,
i
:
number
)
=>
(
<
div
className
=
'cascader-select-item'
key
=
{
'
cascader-select-
'
+
idx
+
'
-
'
+
i
}
>
<
MinusCircleOutlined
className
=
'cascader-option-icon hover-color'
title
=
'删除'
/>
<
div
className
=
{
`input-wrapper
${
idxObj
[(
'
idx
'
+
(
idx
+
1
))
as
keyof
typeof
idxObj
]
===
i
?
'
cascader-option-active
'
:
''
}
`
}
onClick
=
{
()
=>
handleSelectActive
(
idx
,
i
)
}
>
<
MinusCircleOutlined
className
=
'cascader-option-icon hover-color'
title
=
'删除'
onClick
=
{
()
=>
handleDeleteCascaderOption
(
idx
,
i
)
}
/>
<
div
className
=
{
`input-wrapper
${
idxObj
[(
'
idx
'
+
idx
)
as
keyof
typeof
idxObj
]
===
i
?
'
cascader-option-active
'
:
''
}
`
}
onClick
=
{
()
=>
handleSelectActive
(
idx
,
i
)
}
>
<
Input
value
=
{
opt
.
text
}
onChange
=
{
e
=>
cascaderInputChange
(
e
.
target
.
value
,
idx
,
i
)
}
placeholder
=
{
numArrIndex
[
idx
]
+
'
级选项名称
'
}
/>
{
cascaderMode
.
length
===
idx
+
1
?
null
:
<
RightOutlined
className
=
'cascader-right-icon'
/>
}
</
div
>
...
...
@@ -192,7 +360,7 @@ function CascaderConfig(props: propTypes) {
))
}
</
div
>
<
div
className
=
'cascader-add-wrapper'
>
<
div
className
=
'
opacity'
>
<
div
className
=
'
cursor'
onClick
=
{
()
=>
handleAddCascaderOption
(
idx
)
}
>
<
PlusCircleOutlined
className
=
'add-cascader-item-icon'
/>
<
span
>
添加选项
</
span
>
</
div
>
...
...
@@ -204,10 +372,14 @@ function CascaderConfig(props: propTypes) {
<
div
className
=
'select-cascader-level'
>
<
span
>
下拉框级数
</
span
>
<
Select
ref
=
{
selectRef
}
value
=
{
count
}
size
=
'small'
open
=
{
visible
}
onClick
=
{
()
=>
setVisible
(
!
visible
)
}
onBlur
=
{
()
=>
setVisible
(
false
)
}
popupClassName
=
'select-cascader-level-popover'
suffixIcon
=
{
<
CaretDownOutlined
style
=
{
{
fontSize
:
10
}
}
/>
}
suffixIcon
=
{
<
CaretDownOutlined
onClick
=
{
handleSelect
}
style
=
{
{
fontSize
:
10
}
}
/>
}
style
=
{
{
width
:
64
}
}
dropdownMatchSelectWidth
=
{
100
}
onChange
=
{
handleChange
}
...
...
@@ -223,4 +395,5 @@ function CascaderConfig(props: propTypes) {
</
Modal
>
)
}
export
default
CascaderConfig
;
\ No newline at end of file
web/src/components/modal/RuleInput.tsx
浏览文件 @
d658d620
...
...
@@ -132,7 +132,7 @@ function RuleInput(props: propTypes) {
))
}
</
div
>
{
newRules
.
length
>=
6
?
null
:
<
div
style
=
{
{
textAlign
:
'
center
'
,
padding
Top
:
10
}
}
>
<
div
style
=
{
{
textAlign
:
'
center
'
,
padding
Bottom
:
10
}
}
>
<
Button
onClick
=
{
handleAddRule
}
icon
=
{
<
PlusOutlined
/>
}
>
添加验证规则
</
Button
>
</
div
>
}
...
...
web/src/hooks/useUpdate.ts
浏览文件 @
d658d620
...
...
@@ -66,11 +66,11 @@ export function useReactive(data: objProps) {
/* 普通更新数据 */
export
function
useUpdate
()
{
const
[
_
,
update
]
=
useState
<
number
>
(
0
);
const
[
_
,
update
]
=
useState
(
0
);
function
updateCallback
(
callback
:
Function
)
{
callback
();
update
(
Date
.
now
());
update
(
+
new
Date
());
};
return
updateCallback
;
};
\ No newline at end of file
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录