Commit 8c144095 authored by wuhao's avatar wuhao 🎯

asder

parent 76866941
...@@ -28,12 +28,21 @@ ...@@ -28,12 +28,21 @@
} }
.anticon { .anticon {
font-size: 16px !important; font-size: 14px !important;
color: #637381 !important; color: #637381 !important;
} }
.ant-pro-card{ .ant-select {
.anticon-close-circle {
font-size: 14px !important;
margin-top: -2px;
margin-left: -2px;
}
}
.ant-pro-card {
border-radius: 12px !important; border-radius: 12px !important;
} }
.ant-pro-card-body { .ant-pro-card-body {
padding-inline: 0 !important; padding-inline: 0 !important;
} }
......
import * as React from "react";
import Button from "@mui/material/Button";
import Dialog from "@mui/material/Dialog";
import DialogActions from "@mui/material/DialogActions";
import DialogContent from "@mui/material/DialogContent";
import DialogContentText from "@mui/material/DialogContentText";
import DialogTitle from "@mui/material/DialogTitle";
import Paper from "@mui/material/Paper";
import Draggable from "react-draggable";
function PaperComponent(props) {
return (
<Draggable
handle="#draggable-dialog-title"
cancel={'[class*="MuiDialogContent-root"]'}
>
<Paper {...props} />
</Draggable>
);
}
export default function DraggableDialog({ children }) {
const [open, setOpen] = React.useState(false);
const formRef = React.useRef();
const handleClickOpen = () => {
setOpen(true);
};
const handleClose = () => {
setOpen(false);
};
return (
<div>
<Button variant="outlined" onClick={handleClickOpen}>
Open draggable dialog
</Button>
<Dialog
maxWidth="md"
open={open}
onClose={handleClose}
PaperComponent={PaperComponent}
aria-labelledby="draggable-dialog-title"
>
<DialogTitle style={{ cursor: "move" }} id="draggable-dialog-title">
Subscribe
</DialogTitle>
<DialogContent>
{React.cloneElement(children, { submitter: false, formRef })}
</DialogContent>
<DialogActions>
<Button
type="reset"
key="rest"
onClick={() => {
formRef?.current?.resetFields();
}}
>
重置
</Button>
<Button
type="submit"
key="submit"
variant="contained"
onClick={() => () => {
formRef?.current?.submit();
}}
>
提交
</Button>
</DialogActions>
</Dialog>
</div>
);
}
/* eslint-disable react-hooks/exhaustive-deps */
/* eslint-disable react-hooks/rules-of-hooks */
import React, { useEffect, useRef, useState, memo, useMemo } from 'react';
import { EditableProTable } from '@ant-design/pro-components';
import { Tooltip } from '@mui/material';
import { doFetch } from '@/utils/doFetch';
const EditTable = (props) => {
const {
actionRef, //表格动作
formRef, //表单Ref
rowKey, // key
columns = [], //columns
style, //style
path, //接口地址
extraparams, //额外参数
pageSize, //修改默认pageSize
pagination, //分页设置
x, //横向滚动
refreshDep, //依赖刷新 (已废弃)
} = props;
const actionRefs = actionRef ?? useRef(),
formRefs = formRef ?? useRef(),
ifspagination = pagination == 'false' || pagination === false,
[size, setsize] = useState('small');
//调用接口
const request = async (params, sort, filter) => {
if (!path) return;
let newparams = {
...params,
...extraparams, //父组件传参
pageIndex: params.current,
pageSize: params.pageSize || pageSize,
};
delete newparams.current;
if (ifspagination) {
delete newparams.pageIndex;
delete newparams.pageSize;
}
const result = await doFetch({ url: path, params: newparams });
//分页结果
let data = result?.data?.page?.list,
success = true,
total = result?.data?.page?.total;
//不带分页获取结果
if (ifspagination || !data) {
data = result?.data?.dataList;
total = result?.data?.dataList?.length;
}
//存在默认选中向上返回选中值
return {
data,
success,
total,
};
};
let columncs = useMemo(() => {
return columns.map((item, index) => {
let it = { ...item };
let itemwidth = it.width ? it.width : 'auto';
let options = {};
if (it.valueType == 'select' || it.valueType == 'checkbox') {
if (Array.isArray(it.options)) {
options = {
fieldProps: {
options: [...it.options],
},
};
} else if (it.options) {
options = {
request: async (params) => {
let list = await doFetch({ url: it?.options?.path, params: it?.options?.params });
return list.data.dataList;
},
};
}
}
if (it.valueType == 'option') {
options = {
key: 'option',
dataIndex: 'option',
fixed: 'right',
};
}
if (!it.render) {
options = {
...options,
render: (text, row) => {
return (
<Tooltip title={row[it.dataIndex]} placement="bottom-start">
<span className="table-cell">{row[it.dataIndex] ?? '-'}</span>
</Tooltip>
);
},
};
}
options = {
...options,
width: itemwidth,
};
delete it.formItemProps;
return {
...it,
...options,
};
});
}, [columns]);
return (
<EditableProTable
{...props}
recordCreatorProps={false}
size={size}
onSubmit={(params) => {
console.log(params, 'onSubmit');
}}
onSizeChange={(size) => {
localStorage.setItem('size', size); //设置全局表格规格缓存
setsize(size);
}}
columns={columncs ?? []}
style={style || {}}
actionRef={actionRefs}
formRef={formRefs}
rowKey={rowKey ?? 'id'} //表格每行数据的key
dateFormatter="string"
request={request}
scroll={
x
? {
x: x,
}
: {}
}
pagination={
ifspagination
? false
: {
showTotal: (total, range) => <span>{total}</span>,
showQuickJumper: true,
showSizeChanger: true,
pageSizeOptions: [5, 10, 15, 30, 50, 100, 200],
defaultPageSize: pageSize || 15,
}
}
editable={{
type: 'multiple',
editableKeys: props?.rowSelection?.selectedRowKeys ?? [],
...props?.editable,
}}
search={{
filterType: 'light', //轻量模式
}}
/>
);
};
export default memo(EditTable);
/* eslint-disable react-hooks/exhaustive-deps */
import 'braft-editor/dist/index.css';
import BraftEditor from 'braft-editor';
import React, { useState, useMemo, useEffect } from 'react';
import dayjs from 'dayjs';
const prefix = import.meta.env.VITE_APP_URL;
export default function EditorItem({
value,
onChange,
height,
serverURL,
style,
bordered,
formRef,
curkey,
}) {
let UploadFn = (param) => {
const xhr = new XMLHttpRequest();
const fd = new FormData();
const successFn = (response) => {
// 假设服务端直接返回文件上传后的地址
// 上传成功后调用param.success并传入上传后的文件地址
param.success({
url: xhr.responseText ? JSON.parse(xhr.responseText).data?.dataList[0].url : null,
meta: {
id: dayjs().valueOf(),
title: param.file.name,
alt: param.file.name,
loop: true, // 指定音视频是否循环播放
autoPlay: true, // 指定音视频是否自动播放
controls: true, // 指定音视频是否显示控制栏
poster: 'http://xxx/xx.png', // 指定视频播放器的封面
},
});
};
const progressFn = (event) => {
// 上传进度发生变化时调用param.progress
param.progress((event.loaded / event.total) * 100);
};
const errorFn = (response) => {
// 上传发生错误时调用param.error
param.error({
msg: '上传失败',
});
};
xhr.upload.addEventListener('progress', progressFn, false);
xhr.addEventListener('load', successFn, false);
xhr.addEventListener('error', errorFn, false);
xhr.addEventListener('abort', errorFn, false);
fd.append('file', param.file);
xhr.open(
'POST',
serverURL
? serverURL
: prefix + '/file/upload',
true,
);
xhr.send(fd);
};
return (
<div
style={{
...style,
border: bordered === false ? '#f9f9f9 solid 1px' : '#ddd solid 1px',
height: height ? height : 400,
overflow: 'hidden',
borderRadius:8,
backgroundColor:"#fff"
}}
>
<BraftEditor
media={{ uploadFn: UploadFn }}
value={BraftEditor.createEditorState(value)}
onChange={onChange}
/>
</div>
);
}
import React, { useState, useRef, memo, createElement, useEffect } from "react";
import {
ProForm,
ProFormDependency,
ProFormSelect,
ProFormText,
ProFormMoney,
ProFormTextArea,
ProFormDigit,
ProFormDigitRange,
ProFormDatePicker,
ProFormDateTimePicker,
ProFormDateRangePicker,
ProFormDateTimeRangePicker,
ProFormTimePicker,
ProFormTreeSelect,
ProFormCheckbox,
ProFormRadio,
ProFormCascader,
ProFormSwitch,
ProFormRate,
ProFormSlider,
ProFormUploadDragger,
ProFormUploadButton,
ProFormList,
} from "@ant-design/pro-components";
import ImgCrop from "antd-img-crop";
import { doFetch } from "@/utils/doFetch";
import dayjs from "dayjs";
import * as Antd from "antd";
import {
PlusOutlined,
DownOutlined,
CloseOutlined,
RedoOutlined,
} from "@ant-design/icons";
import EditTable from "./EditTable";
import EditorItem from "./EditorItem";
const prefix = import.meta.env.VITE_APP_URL;
const { Image, Form, Upload, Col, Dropdown, Menu, Tabs } = Antd;
function upperCase(str) {
const newStr = str.slice(0, 1).toUpperCase() + str.slice(1);
return newStr;
}
// tree遍历
function treeForeach(tree, func) {
tree.forEach((data) => {
func(data);
data.children && treeForeach(data.children, func); // 遍历子树
});
}
// colProps 默认删格
function Input({ item, colProps }) {
let keys = item.key ?? item.dataIndex ?? "";
keys = keys ?? "";
const defaultrule =
keys.indexOf("phone") != -1
? {
pattern: /^(((\d{3,4}-)?[0-9]{7,8})|(1(3|4|5|6|7|8|9)\d{9}))$/,
message: item.title + "格式不正确",
}
: keys.indexOf("mail") != -1
? {
pattern:
/^[a-z0-9A-Z]+[- | a-z0-9A-Z . _]+@([a-z0-9A-Z]+(-[a-z0-9A-Z]+)?\.)+[a-z]{2,}$/,
message: "邮箱格式不正确",
}
: {};
return (
<>
<ProFormText
fieldProps={item?.fieldProps}
formItemProps={{
...item.formItemProps,
rules: [defaultrule, ...(item?.formItemProps?.rules ?? [])],
}} //手机号邮箱自带验证
name={keys}
colProps={item.colProps ?? colProps}
label={item.title}
placeholder={`请输入${item.title}`}
/>
</>
);
}
//pwd
function Password({ item, colProps }) {
return (
<>
<ProFormText.Password
fieldProps={item?.fieldProps}
formItemProps={item.formItemProps}
name={item.key ?? item.dataIndex}
colProps={item.colProps ?? colProps}
label={item.title}
placeholder={`请输入${item.title}`}
/>
</>
);
}
//money
function Money({ item, colProps }) {
return (
<>
<ProFormMoney
locale="zh-CN"
fieldProps={item?.fieldProps}
formItemProps={item.formItemProps}
name={item.key ?? item.dataIndex}
colProps={item.colProps ?? colProps}
label={item.title}
placeholder={`请输入${item.title}`}
min={item.min}
max={item.max}
/>
</>
);
}
//textarea
function Textarea({ item }) {
return (
<>
<ProFormTextArea
fieldProps={item?.fieldProps}
formItemProps={item.formItemProps}
name={item.key ?? item.dataIndex}
colProps={item.colProps ?? { span: 24 }}
label={item.title}
placeholder={`请输入${item.title}`}
/>
</>
);
}
//digit
function Digit({ item, colProps }) {
return (
<>
<ProFormDigit
formItemProps={item.formItemProps}
name={item.key ?? item.dataIndex}
colProps={item.colProps ?? colProps}
label={item.title}
placeholder={`请输入${item.title}`}
min={item.min}
max={item.max}
fieldProps={{
precision: item.precision ?? 0,
...(item?.fieldProps ?? {}),
}}
/>
</>
);
}
//digitrange
function DigitRange({ item, colProps }) {
return (
<>
<ProFormDigitRange
formItemProps={item.formItemProps}
name={item.key ?? item.dataIndex}
colProps={item.colProps ?? colProps}
label={item.title}
placeholder={["请输入最小值", "请输入最大值"]}
min={item.min}
max={item.max}
fieldProps={{
precision: item.precision ?? 0,
...(item?.fieldProps ?? {}),
}}
/>
</>
);
}
//Date
function Date({ item, colProps }) {
return (
<>
<ProFormDatePicker
fieldProps={item?.fieldProps}
formItemProps={item.formItemProps}
name={item.key ?? item.dataIndex}
colProps={item.colProps ?? colProps}
label={item.title}
placeholder={`请选择${item.title}`}
width="100%"
/>
</>
);
}
function DateWeek({ item, colProps }) {
const weekFormat = "YYYY-MM-DD";
const customWeekStartEndFormat = (value) =>
`${dayjs(value).startOf("week").format(weekFormat)} ~ ${dayjs(value)
.endOf("week")
.format(weekFormat)}`;
return (
<>
<ProFormDatePicker
fieldProps={{
...item?.fieldProps,
picker: "week",
format: customWeekStartEndFormat,
}}
formItemProps={item.formItemProps}
name={item.key ?? item.dataIndex}
colProps={item.colProps ?? colProps}
label={item.title}
placeholder={`请选择${item.title}`}
width="100%"
/>
</>
);
}
//DateMonth
function DateMonth({ item, colProps }) {
return (
<>
<ProFormDatePicker
fieldProps={{ ...item?.fieldProps, picker: "month", format: "YYYY-MM" }}
formItemProps={item.formItemProps}
name={item.key ?? item.dataIndex}
colProps={item.colProps ?? colProps}
label={item.title}
placeholder={`请选择${item.title}`}
width="100%"
/>
</>
);
}
//DateQuarter
function DateQuarter({ item, colProps }) {
const quarterFormat = "YYYY-MM-DD";
const customWeekStartEndFormat = (value) =>
`${dayjs(value).startOf("quarter").format(quarterFormat)} ~ ${dayjs(value)
.endOf("quarter")
.format(quarterFormat)}`;
return (
<>
<ProFormDatePicker
fieldProps={{
...item?.fieldProps,
picker: "quarter",
format: customWeekStartEndFormat,
}}
formItemProps={item.formItemProps}
name={item.key ?? item.dataIndex}
colProps={item.colProps ?? colProps}
label={item.title}
placeholder={`请选择${item.title}`}
width="100%"
/>
</>
);
}
//DateYear
function DateYear({ item, colProps }) {
return (
<>
<ProFormDatePicker
fieldProps={{ ...item?.fieldProps, picker: "year", format: "YYYY" }}
formItemProps={item.formItemProps}
name={item.key ?? item.dataIndex}
colProps={item.colProps ?? colProps}
label={item.title}
placeholder={`请选择${item.title}`}
width="100%"
/>
</>
);
}
//dateTime
function DateTime({ item, colProps }) {
return (
<>
<ProFormDateTimePicker
fieldProps={item?.fieldProps}
formItemProps={item.formItemProps}
name={item.key ?? item.dataIndex}
colProps={item.colProps ?? colProps}
label={item.title}
placeholder={`请选择${item.title}`}
width="100%"
/>
</>
);
}
//DateRange
function DateRange({ item, colProps }) {
return (
<>
<ProFormDateRangePicker
fieldProps={item?.fieldProps}
formItemProps={item.formItemProps}
name={item.key ?? item.dataIndex}
colProps={item.colProps ?? colProps}
label={item.title}
placeholder={["请选择开始日期", "请选择结束日期"]}
width="100%"
/>
</>
);
}
//dateTimeRange
function DateTimeRange({ item, colProps }) {
return (
<>
<ProFormDateTimeRangePicker
fieldProps={item?.fieldProps}
formItemProps={item.formItemProps}
name={item.key ?? item.dataIndex}
colProps={item.colProps ?? colProps}
label={item.title}
placeholder={["请选择开始时间", "请选择结束时间"]}
width="100%"
/>
</>
);
}
//Time
function Time({ item, colProps }) {
return (
<>
<ProFormTimePicker
fieldProps={item?.fieldProps}
formItemProps={item.formItemProps}
name={item.key ?? item.dataIndex}
colProps={item.colProps ?? colProps}
label={item.title}
placeholder={`请选择${item.title}`}
width="100%"
/>
</>
);
}
//TimeRange
function TimeRange({ item, colProps }) {
return (
<>
<ProFormTimePicker.RangePicker
fieldProps={item?.fieldProps}
formItemProps={item.formItemProps}
name={item.key ?? item.dataIndex}
colProps={item.colProps ?? colProps}
label={item.title}
placeholder={["请选择开始时间", "请选择结束时间"]}
width="100%"
/>
</>
);
}
function LinkSelect({ item, colProps, formRef, name, curindex }) {
let curoption = item.options ?? null,
curlinkparams = curoption?.linkParams ?? {}; //获取linkParams下声明的key
return (
<>
<ProFormDependency name={Object.keys(curlinkparams)}>
{(params) => {
const curkey = item.key ?? item.dataIndex;
return (
<ProFormSelect
convertValue={(value) => {
return item?.fieldProps?.mode == "multiple"
? !value
? []
: null
: null;
}}
fieldProps={item?.fieldProps}
formItemProps={item.formItemProps}
name={curkey}
colProps={item.colProps ?? colProps}
label={item.title}
placeholder={`请选择${item.title}`}
params={params}
mode={item?.mode}
request={async (parse) => {
let result = {};
for (let key in curlinkparams) {
let reversekey = !curlinkparams[key]
? key
: curlinkparams[key];
result[reversekey] = parse[key];
}
let res = await doFetch({
url: curoption?.path,
params: result,
});
if (name) {
let curvals = formRef?.current?.getFieldValue(name);
curvals = curvals.map((it, i) => {
if (i == curindex) {
it[curkey] = null;
}
return it;
});
formRef?.current?.setFieldsValue({ [name]: curvals });
} else {
let curval = formRef?.current?.getFieldValue(curkey),
ifclean;
if (Array.isArray(curval)) {
ifclean = res?.data?.dataList
?.map((it) => it.value)
.filter?.((it) => {
return curval?.includes(it);
});
} else {
ifclean = res?.data?.dataList.filter(
(it) => it.value == curval
)?.[0]?.value;
}
formRef?.current?.setFieldsValue({ [curkey]: ifclean });
}
return res?.data?.dataList ?? [];
}}
showSearch
/>
);
}}
</ProFormDependency>
</>
);
}
function NolinkSelect({ item, colProps }) {
let options = {
options: [],
},
curoption = item.options ?? null;
if (Array.isArray(curoption)) {
options = {
options: [...curoption],
};
} else if (curoption) {
options = {
request: async () => {
let list = await doFetch({
url: curoption?.path,
params: curoption?.params,
});
return list.data.dataList;
},
};
}
return (
<>
<ProFormSelect
fieldProps={item.fieldProps}
formItemProps={item.formItemProps}
name={item.key ?? item.dataIndex}
colProps={item.colProps ?? colProps}
label={item.title}
placeholder={`请选择${item.title}`}
showSearch
mode={item?.mode}
{...options}
/>
</>
);
}
//Select 高阶组建
function Select(props) {
let ifs = props?.item?.options?.linkParams;
if (ifs) {
return <LinkSelect {...props} />;
} else {
return <NolinkSelect {...props} />;
}
}
function LinkTreeSelect({ item, colProps, formRef, name, curindex }) {
let prevparse = useRef();
let curoption = item.options ?? null,
curlinkparams = curoption?.linkParams ?? {}; //获取linkParams下声明的key
return (
<>
<ProFormDependency name={Object.keys(curlinkparams)}>
{(params) => {
const curkey = item.key ?? item.dataIndex;
return (
<ProFormTreeSelect
fieldProps={{
...item?.fieldProps,
fieldNames: {
label: "title",
value: "key",
children: "children",
},
showSearch: false,
multiple: item?.mode === "multiple",
}}
formItemProps={item.formItemProps}
name={curkey}
colProps={item.colProps ?? colProps}
label={item.title}
placeholder={`请选择${item.title}`}
params={params}
request={async (parse) => {
delete parse.keyWords;
let result = {};
for (let key in curlinkparams) {
let reversekey = !curlinkparams[key]
? key
: curlinkparams[key];
result[reversekey] = parse[key];
}
let res = await doFetch({
url: curoption?.path,
params: result,
});
if (prevparse.current !== JSON.stringify(parse)) {
if (name) {
let curvals = formRef?.current?.getFieldValue(name);
curvals = curvals.map((it, i) => {
if (i == curindex) {
it[curkey] = null;
}
return it;
});
formRef?.current?.setFieldsValue({ [name]: curvals });
} else {
let curval = formRef?.current?.getFieldValue(curkey),
ifclean;
//树结构所有value提取到数组
let allvalue = [];
treeForeach(res?.data?.dataList, (node) => {
allvalue.push(node.key);
});
//过滤存在的value
if (Array.isArray(curval)) {
ifclean = allvalue?.filter?.((it) => {
return curval?.includes(it);
});
} else {
ifclean = allvalue?.filter?.((it) => it == curval)?.[0];
}
formRef?.current?.setFieldsValue({ [curkey]: ifclean });
}
}
prevparse.current = JSON.stringify(parse);
return res?.data?.dataList ?? [];
}}
/>
);
}}
</ProFormDependency>
</>
);
}
function NolinkTreeSelect({ item, colProps }) {
let options = {
options: [],
},
curoption = item.options ?? null;
if (Array.isArray(curoption)) {
options = {
options: [...curoption],
};
} else if (curoption) {
options = {
request: async () => {
let list = await doFetch({
url: curoption?.path,
params: curoption?.params,
});
return list.data.dataList;
},
};
}
return (
<>
<ProFormTreeSelect
fieldProps={{
...item?.fieldProps,
fieldNames: { label: "title", value: "key", children: "children" },
showSearch: true,
multiple: item?.mode === "multiple",
}}
formItemProps={item.formItemProps}
name={item.key ?? item.dataIndex}
colProps={item.colProps ?? colProps}
label={item.title}
placeholder={`请选择${item.title}`}
multiple
{...options}
/>
</>
);
}
//TreeSelect 高阶组建
function TreeSelect(props) {
let ifs = props?.item?.options?.linkParams;
if (ifs) {
return <LinkTreeSelect {...props} />;
} else {
return <NolinkTreeSelect {...props} />;
}
}
function CheckboxItem({ item, colProps }) {
return (
<>
<ProFormCheckbox
fieldProps={item.fieldProps}
formItemProps={item.formItemProps}
name={item.key ?? item.dataIndex}
colProps={item.colProps ?? colProps}
label={item.title}
placeholder={`请选择${item.title}`}
/>
</>
);
}
function LinkCheckbox({ item, colProps, formRef, name, curindex }) {
let curoption = item.options ?? null,
curlinkparams = curoption?.linkParams ?? {}; //获取linkParams下声明的key
return (
<>
<ProFormDependency name={Object.keys(curlinkparams)}>
{(params) => {
const curkey = item.key ?? item.dataIndex;
return (
<ProFormCheckbox.Group
fieldProps={item?.fieldProps}
formItemProps={item.formItemProps}
name={curkey}
colProps={item.colProps ?? colProps}
label={item.title}
placeholder={`请选择${item.title}`}
params={params}
request={async (parse) => {
let result = {};
for (let key in curlinkparams) {
let reversekey = !curlinkparams[key]
? key
: curlinkparams[key];
result[reversekey] = parse[key];
}
let res = await doFetch({
url: curoption?.path,
params: result,
});
if (name) {
let curvals = formRef?.current?.getFieldValue(name);
curvals = curvals.map((it, i) => {
if (i == curindex) {
it[curkey] = null;
}
return it;
});
formRef?.current?.setFieldsValue({ [name]: curvals });
} else {
let curval = formRef?.current?.getFieldValue(curkey),
ifclean;
if (Array.isArray(curval)) {
ifclean = res?.data?.dataList
?.map((it) => it.value)
.filter?.((it) => {
return curval?.includes(it);
});
} else {
ifclean = res?.data?.dataList.filter(
(it) => it.value == curval
)?.[0]?.value;
}
formRef?.current?.setFieldsValue({ [curkey]: ifclean });
}
return res?.data?.dataList ?? [];
}}
/>
);
}}
</ProFormDependency>
</>
);
}
function NolinkCheckbox({ item, colProps }) {
let options = {
options: [],
},
curoption = item.options ?? null;
if (Array.isArray(curoption)) {
options = {
options: [...curoption],
};
} else if (curoption) {
options = {
request: async () => {
let list = await doFetch({
url: curoption?.path,
params: curoption?.params,
});
return list.data.dataList;
},
};
}
return (
<>
<ProFormCheckbox.Group
fieldProps={item.fieldProps}
formItemProps={item.formItemProps}
name={item.key ?? item.dataIndex}
colProps={item.colProps ?? colProps}
label={item.title}
placeholder={`请选择${item.title}`}
{...options}
/>
</>
);
}
//Checkbox 高阶组建
function Checkbox(props) {
let ifs = props?.item?.options?.linkParams;
if (ifs) {
return <LinkCheckbox {...props} />;
} else {
return <NolinkCheckbox {...props} />;
}
}
function RadioItem({ item, colProps }) {
return (
<>
<ProFormRadio
fieldProps={item.fieldProps}
formItemProps={item.formItemProps}
name={item.key ?? item.dataIndex}
colProps={item.colProps ?? colProps}
label={item.title}
placeholder={`请选择${item.title}`}
/>
</>
);
}
function LinkRadio({ item, colProps, formRef, name, curindex }) {
let curoption = item.options ?? null,
curlinkparams = curoption?.linkParams ?? {}; //获取linkParams下声明的key
return (
<>
<ProFormDependency name={Object.keys(curlinkparams)}>
{(params) => {
const curkey = item.key ?? item.dataIndex;
return (
<ProFormRadio.Group
fieldProps={item?.fieldProps}
formItemProps={item.formItemProps}
name={curkey}
colProps={item.colProps ?? colProps}
label={item.title}
placeholder={`请选择${item.title}`}
params={params}
request={async (parse) => {
let result = {};
for (let key in curlinkparams) {
let reversekey = !curlinkparams[key]
? key
: curlinkparams[key];
result[reversekey] = parse[key];
}
let res = await doFetch({
url: curoption?.path,
params: result,
});
if (name) {
let curvals = formRef?.current?.getFieldValue(name);
curvals = curvals.map((it, i) => {
if (i == curindex) {
it[curkey] = null;
}
return it;
});
formRef?.current?.setFieldsValue({ [name]: curvals });
} else {
let curval = formRef?.current?.getFieldValue(curkey),
ifclean;
if (Array.isArray(curval)) {
ifclean = res?.data?.dataList
?.map((it) => it.value)
.filter?.((it) => {
return curval?.includes(it);
});
} else {
ifclean = res?.data?.dataList.filter(
(it) => it.value == curval
)?.[0]?.value;
}
formRef?.current?.setFieldsValue({ [curkey]: ifclean });
}
return res?.data?.dataList ?? [];
}}
/>
);
}}
</ProFormDependency>
</>
);
}
function NolinkRadio({ item, colProps }) {
let options = {
options: [],
},
curoption = item.options ?? null;
if (Array.isArray(curoption)) {
options = {
options: [...curoption],
};
} else if (curoption) {
options = {
request: async () => {
let list = await doFetch({
url: curoption?.path,
params: curoption?.params,
});
return list.data.dataList;
},
};
}
return (
<>
<ProFormRadio.Group
fieldProps={item.fieldProps}
formItemProps={item.formItemProps}
name={item.key ?? item.dataIndex}
colProps={item.colProps ?? colProps}
label={item.title}
placeholder={`请选择${item.title}`}
{...options}
/>
</>
);
}
//Radio 高阶组建
function Radio(props) {
let ifs = props?.item?.options?.linkParams;
if (ifs) {
return <LinkRadio {...props} />;
} else {
return <NolinkRadio {...props} />;
}
}
function LinkCascader({ item, colProps, formRef, name, curindex }) {
let prevparse = useRef();
let curoption = item.options ?? null,
curlinkparams = curoption?.linkParams ?? {}; //获取linkParams下声明的key
return (
<>
<ProFormDependency name={Object.keys(curlinkparams)}>
{(params) => {
const curkey = item.key ?? item.dataIndex;
return (
<ProFormCascader
fieldProps={{
...item?.fieldProps,
fieldNames: {
label: "title",
value: "key",
children: "children",
},
showSearch: true,
multiple: item?.mode === "multiple",
}}
formItemProps={item.formItemProps}
name={curkey}
colProps={item.colProps ?? colProps}
label={item.title}
placeholder={`请选择${item.title}`}
params={params}
request={async (parse) => {
delete parse.keyWords;
let result = {};
for (let key in curlinkparams) {
let reversekey = !curlinkparams[key]
? key
: curlinkparams[key];
result[reversekey] = parse[key];
}
let res = await doFetch({
url: curoption?.path,
params: result,
});
if (prevparse.current !== JSON.stringify(parse)) {
if (name) {
let curvals = formRef?.current?.getFieldValue(name);
curvals = curvals.map((it, i) => {
if (i == curindex) {
it[curkey] = null;
}
return it;
});
formRef?.current?.setFieldsValue({ [name]: curvals });
} else {
let curval = formRef?.current?.getFieldValue(curkey),
ifclean;
if (Array.isArray(curval)) {
ifclean = res?.data?.dataList
?.map((it) => it.value)
.filter?.((it) => {
return curval?.includes(it);
});
} else {
ifclean = res?.data?.dataList.filter(
(it) => it.value == curval
)?.[0]?.value;
}
formRef?.current?.setFieldsValue({ [curkey]: ifclean });
}
}
prevparse.current = JSON.stringify(parse);
return res?.data?.dataList ?? [];
}}
/>
);
}}
</ProFormDependency>
</>
);
}
function NolinkCascader({ item, colProps }) {
let options = {
options: [],
},
curoption = item.options ?? null;
if (Array.isArray(curoption)) {
options = {
options: [...curoption],
};
} else if (curoption) {
options = {
request: async () => {
let list = await doFetch({
url: curoption?.path,
params: curoption?.params,
});
return list.data.dataList;
},
};
}
return (
<>
<ProFormCascader
fieldProps={{
...item?.fieldProps,
fieldNames: { label: "title", value: "key", children: "children" },
showSearch: true,
multiple: item?.mode === "multiple",
}}
formItemProps={item.formItemProps}
name={item.key ?? item.dataIndex}
colProps={item.colProps ?? colProps}
label={item.title}
placeholder={`请选择${item.title}`}
{...options}
/>
</>
);
}
//Cascader 高阶组建
function Cascader(props) {
let ifs = props?.item?.options?.linkParams;
if (ifs) {
return <LinkCascader {...props} />;
} else {
return <NolinkCascader {...props} />;
}
}
//switch
function Switch({ item, colProps }) {
return (
<>
<ProFormSwitch
fieldProps={item?.fieldProps}
formItemProps={item.formItemProps}
name={item.key ?? item.dataIndex}
colProps={item.colProps ?? colProps}
label={item.title}
placeholder={`请输入${item.title}`}
/>
</>
);
}
//Rate
function Rate({ item, colProps }) {
return (
<>
<ProFormRate
fieldProps={item?.fieldProps}
formItemProps={item.formItemProps}
name={item.key ?? item.dataIndex}
colProps={item.colProps ?? colProps}
label={item.title}
/>
</>
);
}
//Slider
function Slider({ item, colProps }) {
return (
<>
<ProFormSlider
{...item?.fieldProps}
fieldProps={item?.fieldProps}
formItemProps={item.formItemProps}
name={item.key ?? item.dataIndex}
colProps={item.colProps ?? colProps}
label={item.title}
/>
</>
);
}
//uploadbtn
function UploadBtn({ item, colProps }) {
return (
<>
<ProFormUploadButton
fieldProps={{
...item?.fieldProps,
action: prefix + "/file/upload",
onPreview: (file) => {
let url = "";
if (file.response) {
url = file.response.data.dataList[0].url;
} else if (file.url) {
url = file.url;
} else {
url = file.thumbUrl;
}
window.open(url);
},
}}
transform={(value) => {
const key = item.key ?? item.dataIndex;
const transvalue = value?.map((it) => {
if (it.response) {
return it?.response?.data?.dataList[0];
} else {
return it;
}
});
return {
[key]: transvalue,
};
}}
formItemProps={item.formItemProps}
name={item.key ?? item.dataIndex}
colProps={item.colProps ?? colProps}
label={item.title}
title={`上传${item.title}`}
/>
</>
);
}
function UploadImg({ value, onChange, fieldProps }) {
const [image, setImage] = useState({});
let token = "18e1081d54f57af2fdeac1964cc981e7";
function beforeUpload(file) {
const isJpgOrPng =
file.type === "image/jpg" ||
file.type === "image/jpeg" ||
file.type === "image/png";
if (!isJpgOrPng) {
message.error("只能上传.jpg/.jpeg/.png图片!");
return;
}
return true;
}
// maxCount 最大数量
const defaultconfig = {
name: "file",
action: prefix + "/file/upload",
accept: ".jpg,.png,.jpeg",
listType: "picture-card",
beforeUpload: beforeUpload,
defaultFileList: value,
headers: { token },
onChange(info) {
let {
file: { status },
fileList,
} = info;
if (status == "error") {
message.error(`${info.file.name} 上传失败`);
} else if (status === "done") {
const transfile = fileList.map((it) => {
return it?.response ? it?.response.data.dataList[0] : it;
});
onChange(transfile);
}
},
onRemove(file) {
let uid = file?.response?.data?.dataList[0]?.uid ?? file?.uid;
let newvalue = value.filter((it) => it.uid != uid);
onChange(newvalue);
},
onPreview(file) {
let url = "";
if (file.response) {
url = file.response.data.dataList[0].url;
} else if (file.url) {
url = file.url;
} else {
url = file.thumbUrl;
}
setImage({
url,
visible: true,
});
},
};
const uploadButton = (
<div>
<PlusOutlined />
<div style={{ marginTop: 8 }}>上传图片</div>
</div>
);
return (
<>
<Image
src={image.url}
width={0}
height={0}
preview={{
visible: image?.visible,
onVisibleChange: () => {
if (image?.visible) {
setImage((s) => ({
...s,
visible: false,
}));
}
},
}}
/>
{fieldProps?.crop ? (
<ImgCrop
rotate
grid
quality={1}
shape={fieldProps?.crop?.shape ?? "rect"} //裁切区域形状,'rect' 或 'round'
aspect={fieldProps?.crop?.aspect ?? 1 / 1} //裁切区域宽高比,width / height
>
<Upload {...defaultconfig}>
{!value
? uploadButton
: value?.length < fieldProps.limit
? uploadButton
: null}
</Upload>
</ImgCrop>
) : (
<Upload {...defaultconfig}>
{!value
? uploadButton
: value?.length < fieldProps.limit
? uploadButton
: null}
</Upload>
)}
</>
);
}
//upload Image
function UploadImage({ item, colProps }) {
let col = item.colProps ?? colProps;
return (
<Col {...col}>
<Form.Item
name={item.key ?? item.dataIndex}
label={item.title}
{...item.formItemProps}
>
<UploadImg fieldProps={{ ...item?.fieldProps }} />
</Form.Item>
</Col>
);
}
// uploadDragger
function UploadDragger({ item, colProps }) {
return (
<>
<ProFormUploadDragger
fieldProps={{
...item?.fieldProps,
action: prefix + "/file/upload",
onPreview: (file) => {
let url = "";
if (file.response) {
url = file.response.data.dataList[0].url;
} else if (file.url) {
url = file.url;
} else {
url = file.thumbUrl;
}
window.open(url);
},
}}
transform={(value) => {
const key = item.key ?? item.dataIndex;
const transvalue = value?.map((it) => {
if (it.response) {
return it?.response?.data?.dataList[0];
} else {
return it;
}
});
return {
[key]: transvalue,
};
}}
formItemProps={item.formItemProps}
name={item.key ?? item.dataIndex}
colProps={item.colProps ?? colProps}
label={item.title}
/>
</>
);
}
// editor
function Editor({ item, colProps, formRef }) {
let col = item.colProps ?? colProps;
let curkey = item.key ?? item.dataIndex;
return (
<Col {...col}>
<ProForm.Item
// convertValue={(value) => {
// return BraftEditor.createEditorState(value);
// }}
transform={(value) => {
return {
[curkey]: value.toHTML(),
};
}}
name={curkey}
label={item.title}
{...item.formItemProps}
>
<EditorItem
item={item}
params={item.params}
formRef={formRef}
curkey={curkey}
/>
</ProForm.Item>
</Col>
);
}
function FormList({ item, colProps, formRef }) {
let col = item.colProps ?? colProps;
let fields = item.columns;
return (
<Col {...col}>
<ProFormList
name={item.key ?? item.dataIndex}
label={item.title}
min={item.min ?? 1}
max={item.max ?? 100}
itemContainerRender={(doms) => {
return <ProForm.Group>{doms}</ProForm.Group>;
}}
alwaysShowItemLabel={false}
>
{(f, index, action) => {
return (
<FormRender
fields={fields}
action={action}
curindex={index}
formRef={formRef}
name={item.key ?? item.dataIndex}
/>
);
}}
</ProFormList>
</Col>
);
}
function TableSelect({ item, value, onChange, params = {} }) {
const rowKey = item?.rowKey ?? "id";
const [chooses, setchooses] = useState([]); //mark 标记
const [activetab, setactivetab] = useState(1);
const actionRef = useRef();
const menu = (selectedRows) => (
<Menu
style={{ width: 160 }}
items={
selectedRows.length > 0
? selectedRows.map((it) => ({
key: it[rowKey],
label: (
<div
className="spread"
onClick={(e) => {
e.stopPropagation();
let key = it[rowKey];
setchooses((s) => {
let news = [...s];
if (s.includes(key)) {
news = news.filter((it) => {
return it != key;
});
} else {
news.push(key);
}
return news;
});
}}
>
<span
style={{
color: chooses.includes(it[rowKey])
? "#1890ff"
: "#333333",
transition: "all 0.4s",
userSelect: "none",
}}
>
{it[item.rowName]}
</span>
<CloseOutlined
onClick={(e) => {
e.stopPropagation();
let newvalue = value.filter(
(its) => its[rowKey] != it[rowKey]
);
onChange(newvalue);
setchooses((s) => {
let news = [...s];
news = news.filter((its) => {
return its != it[rowKey];
});
return news;
});
}}
/>
</div>
),
}))
: [
{
key: -1,
label: "请先选择",
},
]
}
/>
);
useEffect(() => {
onChange([]);
actionRef?.current?.reload?.();
}, [params]);
const Todo = (
<EditTable
actionRef={actionRef}
defaultValue={value} //调用接口合并初始值
path={item.path}
extraparams={params ?? {}}
rowKey={rowKey}
columns={item.columns}
resizeable={false}
alwaysShowAlert={false}
tableAlertRender={false}
tableAlertOptionRender={false}
rowClassName={(record) => {
if (chooses.includes(record[rowKey])) {
return "lightblue";
} else {
return "";
}
}}
rowSelection={{
...item.rowSelection,
columnWidth: 44,
preserveSelectedRowKeys: true,
selectedRowKeys: value && value?.map((it) => it[rowKey]),
onChange: (selectedKeys, selectedRows) => {
const rowkeylist = value ? value?.map((it) => it[rowKey]) : [];
const newValue = selectedRows?.map((its) => {
if (rowkeylist.includes(its[rowKey])) {
return value.filter((it) => it[rowKey] == its[rowKey])[0];
} else {
return its;
}
});
onChange(newValue);
},
}}
editable={{
onValuesChange: (record) => {
const newValue = value?.map((its) => {
if (its[rowKey] == record[rowKey]) {
return record;
} else {
return its;
}
});
onChange(newValue);
},
}}
/>
);
const Done = (
<EditTable
value={value}
rowKey={rowKey}
columns={item.columns}
resizeable={false}
alwaysShowAlert={false}
tableAlertRender={false}
tableAlertOptionRender={false}
rowClassName={(record) => {
if (chooses.includes(record[rowKey])) {
return "lightblue";
} else {
return "";
}
}}
rowSelection={{
...item.rowSelection,
columnWidth: 44,
preserveSelectedRowKeys: true,
selectedRowKeys: value && value?.map((it) => it[rowKey]),
onChange: (selectedKeys, selectedRows) => {
const rowkeylist = value ? value?.map((it) => it[rowKey]) : [];
const newValue = selectedRows?.map((its) => {
if (rowkeylist.includes(its[rowKey])) {
return value.filter((it) => it[rowKey] == its[rowKey])[0];
} else {
return its;
}
});
onChange(newValue);
},
}}
editable={{
onValuesChange: (record) => {
const newValue = value?.map((its) => {
if (its[rowKey] == record[rowKey]) {
return record;
} else {
return its;
}
});
onChange(newValue);
},
}}
/>
);
return (
<div className="selecttable">
<Tabs
tabBarExtraContent={
<div className="center">
<Dropdown overlay={menu(value ?? [])}>
<a>
已选择{value?.length ?? 0}<DownOutlined />
</a>
</Dropdown>
<div
className="center"
style={{
color: "red",
cursor: "pointer",
margin: "0 6px 0 16px",
}}
onClick={() => {
onChange([]);
setchooses([]);
}}
>
<RedoOutlined rotate={-90} />
清空
</div>
</div>
}
onChange={setactivetab}
items={[
{ label: "数据选择", key: 1, children: activetab == 1 && Todo },
{
label: `选择结果${value?.length ?? 0}项`,
key: 2,
children: activetab == 2 && Done,
},
]}
/>
</div>
);
}
function LinkSelectList({ item, colProps }) {
let col = item.colProps ?? colProps;
let curlinkparams = item?.linkParams ?? {}; //获取linkParams下声明的key
return (
<Col {...col}>
<ProFormDependency name={Object.keys(curlinkparams)}>
{(params) => {
const curkey = item.key ?? item.dataIndex;
let result = {};
for (let key in curlinkparams) {
let reversekey = !curlinkparams[key] ? key : curlinkparams[key];
result[reversekey] = params[key];
}
return (
<Form.Item name={curkey} label={item.title} {...item.formItemProps}>
<TableSelect item={item} params={result} />
</Form.Item>
);
}}
</ProFormDependency>
</Col>
);
}
function NolinkSelectList({ item, colProps }) {
let col = item.colProps ?? colProps;
let curkey = item.key ?? item.dataIndex; //获取key
return (
<Col {...col}>
<Form.Item name={curkey} label={item.title} {...item.formItemProps}>
<TableSelect item={item} params={item.params} />
</Form.Item>
</Col>
);
}
function FormSelectList(props) {
let ifs = props?.item?.linkParams;
if (ifs) {
return <LinkSelectList {...props} />;
} else {
return <NolinkSelectList {...props} />;
}
}
const FormItems = {
Input,
Password,
Money,
Textarea,
Digit,
DigitRange,
Date,
Time,
DateTime,
DateWeek,
DateMonth,
DateQuarter,
DateYear,
DateRange,
TimeRange,
DateTimeRange,
Select,
TreeSelect,
Checkbox,
Radio,
Switch,
Cascader,
Rate,
Slider,
UploadBtn,
UploadImage,
UploadDragger,
Editor,
FormList,
FormSelectList,
CheckboxItem,
RadioItem,
};
let FormRender = memo(({ fields = [], name, curindex, formRef }) => {
return (
<>
{fields
.filter((it) => it.hideInForm !== true)
.map((item) => {
let key = item?.valueType ? upperCase(item?.valueType) : "Input";
let { hideInForm } = item;
if (hideInForm && Object.keys(hideInForm)) {
return (
<ProFormDependency name={Object.keys(hideInForm)}>
{(params) => {
let ifs = true;
let res = Object.keys(hideInForm).map((its) => {
if (Array.isArray(hideInForm[its])) {
return !hideInForm[its].includes(params[its]);
} else {
let vals = hideInForm[its].reverse; //取反 即不存在当前数组中的
return vals.indexOf(params[its]) != -1;
}
});
ifs = res.includes(false);
if (ifs) {
return (
<Col {...item.colProps}>
{curindex == 0 ? (
<p>
<label htmlFor="">{item.title}</label>
<p style={{ padding: "6px 0 0 0", margin: 0 }}>
<b style={{ color: "red" }}>!</b>{" "}
需满足条件才可以填写{item.title}
</p>
</p>
) : (
<p style={{ padding: "4px 0 0 0", margin: 0 }}>
<b style={{ color: "red" }}>!</b>{" "}
需满足条件才可以填写{item.title}
</p>
)}
</Col>
);
} else {
return (
<>
{createElement(FormItems[key], {
item: item,
colProps: item.colProps,
key: item.dataIndex,
name: name,
formRef,
curindex,
})}
</>
);
}
}}
</ProFormDependency>
);
} else {
return (
<>
{createElement(FormItems[key], {
item: item,
colProps: item.colProps,
key: item.dataIndex,
name: name,
formRef,
curindex,
})}
</>
);
}
})}
</>
);
});
export default {
Input,
Password,
Money,
Textarea,
Digit,
DigitRange,
Date,
Time,
DateTime,
DateWeek,
DateMonth,
DateQuarter,
DateYear,
DateRange,
TimeRange,
DateTimeRange,
Select,
TreeSelect,
Checkbox,
Radio,
Switch,
Cascader,
Rate,
Slider,
UploadBtn,
UploadImage,
UploadDragger,
Editor,
FormList,
FormSelectList,
CheckboxItem,
RadioItem,
};
import React, { useEffect, memo, useState } from 'react'; import React, { createElement, memo, useRef } from "react";
import { View, TextField, Button, Text, Picker, Colors, DateTimePicker } from 'react-native-ui-lib' import { ProForm, ProFormDependency } from "@ant-design/pro-components";
import { useForm, Controller } from "react-hook-form"; import { doFetch } from "@/utils/doFetch";
import _ from 'lodash'; import "./index.less";
import { useAsyncEffect } from "ahooks"; import FormItems from "./FormItems";
import AntdIcons from 'react-native-vector-icons/AntDesign'; import { Button } from "@mui/material";
import FontAwesome5 from 'react-native-vector-icons/FontAwesome5';
import moment from 'moment'; function upperCase(str) {
import ImagePicker from '../ImagePicker'; const newStr = str.slice(0, 1).toUpperCase() + str.slice(1);
import ListPicker from '../ListPicker'; return newStr;
import Buttonloading from "../Buttonloading"; }
let ControlDom = ({ children, it, value, title, control, required, errors, column }) => (
<View bg-white paddingH-12>
<View row spread centerHkey={it} style={{ overflow: "hidden", height: 50, borderBottomColor: "#f0f0f0", borderBottomWidth: 1 }}>
<View row center paddingR-12>
{(errors[it]) && <AntdIcons name={'exclamationcircle'} size={12} style={{ marginRight: 4, color: "red" }}></AntdIcons>}
{(required) && <FontAwesome5 name={'star-of-life'} size={6} style={{ marginRight: 4, color: "red" }}></FontAwesome5>}
<Text subbody>
{title}
</Text>
</View>
<View right bg-grey90 flex-1 >
{
column ?
column() :
<Controller
control={control}
rules={{
required,
}}
render={({ field: { onChange, onBlur, value } }) => React.cloneElement(children, { onChange, onBlur, value })}
name={it}
/>
}
</View>
</View>
{
column ? <Controller
control={control}
rules={{
required,
}}
render={({ field: { onChange, onBlur, value } }) => React.cloneElement(children, { onChange, onBlur, value })}
name={it}
/> : null
}
</View>
), RenderInput = ({ onChange, onBlur, value, item }) => (
<TextField
style={{ textAlign: "right", height: 30, marginTop: 10 }}
placeholder="请输入"
onBlur={onBlur}
secureTextEntry={item?.type == "password"}
onChangeText={onChange}
value={value}
/>
), RenderSelect = ({ onChange, onBlur, value, item, setValue, setigger, option }) => (
<Picker
placeholder="请选择"
value={value}
enableModalBlur={false}
onChange={(val) => {
if (item?.linked) {
item?.linked.map(name => {
setValue(name, null, {
shouldValidate: true,
shouldDirty: true
})
});
setigger(new Date().getTime())
}
onChange(val)
}}
topBarProps={{ title: item?.title ?? "标题", doneLabel: "保存" }}
style={{ marginTop: 10 }}
showSearch
searchPlaceholder={'请选择'}
mode={item?.multiple ? Picker.modes.MULTI : null}
searchStyle={{ color: Colors.blue30, placeholderTextColor: Colors.grey50 }}
// onSearchChange={value => console.warn('value', value)}
>
{_.map(option[item?.name[0]], cur => (
<Picker.Item key={cur?.value} value={cur} label={cur?.label} disabled={cur?.disabled} />
))}
</Picker>
), RenderDatePicker = ({ onChange, onBlur, value, item, setValue, setigger, option }) => {
let format = item?.showTime ? {
timeFormat: item?.format,
} : {
dateFormat: item?.format,
}
let FormRender = memo(({ fields = [], colProps, proformRef }) => {
return ( return (
<DateTimePicker <>
onChange={onChange} {fields
value={value} .filter((it) => it.hideInForm !== true)
style={{ marginTop: 10 }} .map((item, index) => {
placeholder="请选择" let key = item?.valueType ? upperCase(item?.valueType) : "Input";
mode={item?.showTime ? "time" : "date"} let { hideInForm } = item;
{...format} item.formItemProps = item.formItemProps ?? { rules: [] };
> if (item.valueType == "split") {
</DateTimePicker> return (
) <div
}, RenderImagePicker = ({ onChange, onBlur, value, item, setValue, setigger, option }) => ( className="title"
<ImagePicker style={{ borderWidth: index == 0 ? 0 : 1 }}
onChange={onChange}
value={value}
item={item}
> >
</ImagePicker> {item.title}
), RenderList = memo(({ onChange, onBlur, value, item, setValue, setigger, option }) => { </div>
let alldata = item?.dataSource ? item?.dataSource.map(it => ({ );
id: it.id
})) : []
let newColumns = item?.columns.map(it => {
if (it?.submittype) {
return {
...it,
render: (cur, row) => {
let judge = it.judge ?? [], //判断规则
ifs = row[it?.submittype],//判断条件
curtype = judge.filter(it => it?.val == ifs)[0];//当前规则
let { key, type } = curtype ?? {};//结构结果
let newvalue = value ?? alldata,//初始化idlist
curval = newvalue.filter(it => it.id == row.id)[0];//当前行val
curval = curval ?? {};
// console.log(newvalue,444);
// console.log(item?.dataSource,222);
// console.log(alldata,333);
if (type == "select") {
return <View height={20}>
<Picker
placeholder="请选择"
topBarProps={{ title: curval[key] ?? "请选择", doneLabel: "保存" }}
style={{ textAlign: "right", height: 20, marginTop: 0 }}
value={typeof (curval[key]) == "object" ? curval[key] : {
value: curval[key],
label: it?.options?.filter(it => it.value == curval[key]).length > 0 ? it?.options?.filter(it => it.value == curval[key])[0]?.label : ""
}}
onChange={(val) => {
let changeval = newvalue.map(it => {
// console.log(it);
if (it.id == row.id) {
return {
...it,
[key]: val
} }
if (hideInForm && Object.keys(hideInForm)) {
return (
<ProFormDependency name={Object.keys(hideInForm)}>
{(params) => {
let ifs = true;
let res = Object.keys(hideInForm).map((its) => {
if (Array.isArray(hideInForm[its])) {
return !hideInForm[its].includes(params[its]);
} else { } else {
return { let vals = hideInForm[its].reverse; //取反 即不存在当前数组中的
...it return vals.indexOf(params[its]) != -1;
}
} }
}); });
// console.log(changeval,111); ifs = res.includes(false);
if (ifs) {
onChange(changeval) return;
}}
>
{_.map(it?.options ?? [], item => (
<Picker.Item key={item?.value} value={item} label={item?.label} disabled={item?.disabled} />
))}
</Picker>
</View>
} else if (type == "input") {
return <View height={20}>
<TextField
keyboardType='numeric'
style={{ textAlign: "right", height: 20, marginTop: 0 }}
placeholder="请输入"
value={curval[key]}
onChangeText={(val) => {
let changeval = newvalue.map(it => {
if (it.id == row.id) {
return {
...it,
[key]: val
}
} else { } else {
return { return (
...it <>
} {createElement(FormItems[key], {
item: item,
colProps: colProps,
key: item.dataIndex,
formRef: proformRef,
})}
</>
);
} }
})
onChange(changeval)
}} }}
/> </ProFormDependency>
</View> );
}
}
}
} else { } else {
return {
...it
}
}
})
return ( return (
<ListPicker <>
onChange={onChange} {createElement(FormItems[key], {
value={value} item: {
item={item} ...item,
columns={newColumns} fieldProps: {
> ...item.fieldProps,
</ListPicker> size: "large",
) },
}) },
colProps: colProps,
key: item.dataIndex,
formRef: proformRef,
})}
let InitForm = ({ </>
);
}
})}
</>
);
});
function InitForm({
formRef,
onFinish = (vals) => {
console.log(vals);
},
formKey,
params = {},
detailpath = "",
defaultFormValue = {},
submitter,
fields, fields,
submit, colProps = { xs: 24, sm: 24, md: 12, lg: 12, xl: 12, xxl: 12 },
submitLabel, onValuesChange = (changedValues, allValues) => {
loading console.log(changedValues, allValues);
}) => { },
let { }) {
control, let proformRef = useRef();
handleSubmit, proformRef = formRef ?? proformRef;
formState: { errors },
watch,
setValue,
getValues,
reset
} = useForm({
defaultValues: {
}
}),
[option, cop] = useState({}),
[tigger, setigger] = useState();
const onSubmit = (data) => { return (
let resdata = {}; <ProForm
for (let i in fields) { style={{ overflow: "hidden" }}
if (fields[i].type == "datepicker") { formRef={proformRef}
resdata[i] = data[i] ? moment(data[i]).format(fields[i].format) : null; onFinish={onFinish}
} else if (fields[i].type == "select") { formKey={formKey ?? parseInt(Math.random() * 1000000)}
resdata[i] = data[i] ? data[i].value : null; params={params}
} else { submitter={
resdata[i] = data[i]; submitter || submitter === false
? submitter
: {
render: (props, doms) => {
return [
<Button
type="reset"
key="rest"
onClick={() => props.form?.resetFields()}
>
重置
</Button>,
<Button
type="submit"
key="submit"
variant="contained"
onClick={() => props.form?.submit?.()}
>
提交
</Button>,
];
},
} }
} }
submit && submit(resdata, reset) grid={true}
rowProps={{
gutter: 12,
}}
request={async (params) => {
if (detailpath) {
let res = await doFetch({ url: detailpath, params });
return {
...defaultFormValue,
...(res?.data?.data ?? {}),
}; };
async function setoptions(item) {
let { options, belinked } = item;
if (!options && !belinked) {
return
}
if (Array.isArray(options)) {
return options
}
if (options?.database) {
let { database, params } = options,
res = await database(params)
return res?.data?.dataList;
}
if (belinked?.options) {
let { database, params } = belinked.options, newparams = {};
for (let i in params) {
if (params[i] == 'linked') {
newparams[i] = getValues(i)?.value;
} else { } else {
newparams[i] = params[i]; return {
} ...defaultFormValue,
} };
let res = await database({ ...newparams, key: item?.name[0] })
return res?.data?.dataList;
}
}
useAsyncEffect(async () => {
for (let i in fields) {
let resop = await setoptions(fields[i]);
cop((cur) => ({
...cur,
[i]: resop
}))
fields[i].value && setValue(i, fields[i].value);
}
}, [tigger])
return <View style={{ borderRadius: 8, overflow: "hidden" }} bg-white>
{
Object.keys(fields).map((it) => {
let item = fields[it];
let { title, required, type, value, hides } = item,
controlprops = {
key: it,
it,
value,
title,
control,
required,
errors
}
if (type == "input" || type == "password") {
if (hides) {
return
}
return <ControlDom {...controlprops}>
<RenderInput item={item}></RenderInput>
</ControlDom>
} else if (type == "select") {
if (hides) {
return
}
return <ControlDom {...controlprops}>
<RenderSelect item={item} setValue={setValue} setigger={setigger} option={option}></RenderSelect>
</ControlDom>
} else if (type == "datepicker") {
if (hides) {
return
}
return <ControlDom {...controlprops}>
<RenderDatePicker item={item}></RenderDatePicker>
</ControlDom>
} else if (type == "upload") {
if (hides) {
return
}
return <ControlDom {...controlprops} column={() => {
let num = watch(it) ? watch(it).length : null
return <View center height={50}><Text
body
style={{ color: "#707070", fontSize: 16, paddingRight: 2 }}
>{num ? `已选择(${num + "/" + item?.limit})` : "请选择"}</Text></View>
}}>
<RenderImagePicker item={item}></RenderImagePicker>
</ControlDom>
} else if (type == "list") {
if (hides) {
return
}
return <ControlDom {...controlprops} column={() => {
return <View center height={50}><Text
body
style={{ color: "#ccc", fontSize: 16, paddingRight: 2 }}
></Text></View>
}}>
<RenderList item={item}></RenderList>
</ControlDom>
}
})
}
{
!loading && <Button disabled={loading} backgroundColor={Colors.secondaryColor} borderRadius={8} marginT-23 title="Submit" onPress={handleSubmit(onSubmit)} label={submitLabel ?? "提交"} />
}
{
loading && <Buttonloading style={{ marginTop: 23 }} />
} }
}}
autoFocusFirstInput
</View> onValuesChange={(changedValues, allValues) => {
onValuesChange?.(changedValues, allValues);
}}
>
<FormRender
fields={fields.filter((it) => it.valueType != "option")}
colProps={colProps}
proformRef={proformRef}
/>
</ProForm>
);
} }
export default InitForm export default InitForm;
.title {
position: relative;
width: 100%;
margin-bottom: 8px;
padding-left: 16px;
color: #000000;
font-weight: bolder;
font-size: 16px;
&::before {
position: absolute;
top: 4px;
left: 7px;
width: 3px;
height: 16px;
background-color: #13c2c2;
border-radius: 4px;
content: '';
}
&::after {
position: absolute;
top: 14px;
right: 0px;
width: calc(100% - 160px);
height: 1px;
border-bottom: 1px dotted rgba(0, 0, 0, 0.1);
border-radius: 4px;
content: '';
}
}
import React, { useState, useEffect } from "react"; import React from 'react';
function Home() { class Home extends React.Component {
return <span> home </span>; handleClick = (e) => {
if (e.target.tagName === 'IMG') {
console.log('Image clicked!', e.target.src);
}
}
render() {
return (
<div onClick={this.handleClick}>
<img src="image1.jpg" alt="Image 1" />
<img src="image2.jpg" alt="Image 2" />
<img src="image3.jpg" alt="Image 3" />
</div>
);
}
} }
export default Home; export default Home;
import React, { useState, useEffect } from "react"; import React, { useState, useEffect } from "react";
import AutoTable from "@/components/AutoTable"; import AutoTable from "@/components/AutoTable";
import InitForm from "@/components/InitForm";
import { Container, Box, Typography } from "@mui/material"; import { Container, Box, Typography } from "@mui/material";
import DraggableDialog from "@/components/DraggableDialog";
function Organization() { function Organization() {
return ( return (
<Container maxWidth="xxl"> <Container maxWidth="xxl">
<Box sx={{ mb: 3 }} mt={0}> <Box sx={{ mb: 3 }} mt={0}>
<Typography variant="h5"> <Typography variant="h5">组织管理</Typography>
组织管理
</Typography>
</Box> </Box>
<DraggableDialog
>
<InitForm
fields={[
{
title: "姓名",
dataIndex: "userName",
key: "userId",
sorter: (a, b) => a.containers - b.containers,
hideInForm: {
accountName: { reverse: ["1", "2", "5"] },
remark: ["3"],
},
search: false,
},
{
title: "用户名123",
dataIndex: "accountName",
formItemProps: {
rules: [{ required: false, message: "此项为必填项" }],
},
hideInForm: false,
key: "accountName",
valueType: "select",
mode: "tags",
options: [{ label: "123", value: "123" }],
},
{ title: "基础信息", valueType: "split", key: "基础信息" },
{ title: "额外信息", valueType: "split", key: "额外信息" },
{
title: "联系电话",
dataIndex: "telephone",
formItemProps: {
rules: [{ required: false, message: "此项为必填项" }],
},
search: false,
key: "telephone",
},
{
title: "邮箱",
dataIndex: "mailNo",
formItemProps: {
rules: [{ required: false, message: "此项为必填项" }],
},
search: false,
key: "mailNo",
},
{
title: "备注",
dataIndex: "remark",
valueType: "editor",
search: false,
colProps: { span: 24 },
initialValue: "<p>Hello<b>World!</b></p>",
key: "remark",
},
{
title: "上传样式-图片",
dataIndex: "uploadImage",
key: "uploadImage",
valueType: "uploadImage",
fieldProps: { limit: 2 },
formItemProps: {
rules: [{ required: false, message: "此项为必填项" }],
},
},
{ title: "操作", valueType: "option", width: 150, key: "操作" },
]}
></InitForm>
</DraggableDialog>
<Box boxShadow={"0 0 18px #f0f0f0"} borderRadius={2}> <Box boxShadow={"0 0 18px #f0f0f0"} borderRadius={2}>
<AutoTable <AutoTable
columns={[ columns={[
......
...@@ -48,4 +48,10 @@ export default defineConfig({ ...@@ -48,4 +48,10 @@ export default defineConfig({
], ],
}, },
}, },
define: {
global: {
},
getSelection:{}
},
}); });
...@@ -1350,7 +1350,7 @@ ...@@ -1350,7 +1350,7 @@
resolved "https://registry.npmmirror.com/@babel/regjsgen/-/regjsgen-0.8.0.tgz#f0ba69b075e1f05fb2825b7fad991e7adbb18310" resolved "https://registry.npmmirror.com/@babel/regjsgen/-/regjsgen-0.8.0.tgz#f0ba69b075e1f05fb2825b7fad991e7adbb18310"
integrity sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA== integrity sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA==
"@babel/runtime@^7.10.1", "@babel/runtime@^7.10.4", "@babel/runtime@^7.11.1", "@babel/runtime@^7.11.2", "@babel/runtime@^7.12.1", "@babel/runtime@^7.12.5", "@babel/runtime@^7.16.3", "@babel/runtime@^7.16.7", "@babel/runtime@^7.18.0", "@babel/runtime@^7.18.3", "@babel/runtime@^7.2.0", "@babel/runtime@^7.20.0", "@babel/runtime@^7.20.7", "@babel/runtime@^7.21.0", "@babel/runtime@^7.5.5", "@babel/runtime@^7.7.6", "@babel/runtime@^7.7.7", "@babel/runtime@^7.8.4", "@babel/runtime@^7.8.7", "@babel/runtime@^7.9.2": "@babel/runtime@^7.0.0", "@babel/runtime@^7.10.1", "@babel/runtime@^7.10.4", "@babel/runtime@^7.11.1", "@babel/runtime@^7.11.2", "@babel/runtime@^7.12.1", "@babel/runtime@^7.12.5", "@babel/runtime@^7.16.3", "@babel/runtime@^7.16.7", "@babel/runtime@^7.18.0", "@babel/runtime@^7.18.3", "@babel/runtime@^7.2.0", "@babel/runtime@^7.20.0", "@babel/runtime@^7.20.7", "@babel/runtime@^7.21.0", "@babel/runtime@^7.5.5", "@babel/runtime@^7.7.6", "@babel/runtime@^7.7.7", "@babel/runtime@^7.8.4", "@babel/runtime@^7.8.7", "@babel/runtime@^7.9.2":
version "7.21.0" version "7.21.0"
resolved "https://registry.npmmirror.com/@babel/runtime/-/runtime-7.21.0.tgz#5b55c9d394e5fcf304909a8b00c07dc217b56673" resolved "https://registry.npmmirror.com/@babel/runtime/-/runtime-7.21.0.tgz#5b55c9d394e5fcf304909a8b00c07dc217b56673"
integrity sha512-xwII0//EObnq89Ji5AKYQaRYiW/nZ3llSv29d49IuxPhKbtJoLP+9QUUZ4nVragQVtaVGeZrpB+ZtG/Pdy/POw== integrity sha512-xwII0//EObnq89Ji5AKYQaRYiW/nZ3llSv29d49IuxPhKbtJoLP+9QUUZ4nVragQVtaVGeZrpB+ZtG/Pdy/POw==
...@@ -3337,6 +3337,15 @@ ansi-styles@^5.0.0: ...@@ -3337,6 +3337,15 @@ ansi-styles@^5.0.0:
resolved "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-5.2.0.tgz#07449690ad45777d1924ac2abb2fc8895dba836b" resolved "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-5.2.0.tgz#07449690ad45777d1924ac2abb2fc8895dba836b"
integrity sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA== integrity sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==
antd-img-crop@^4.12.2:
version "4.12.2"
resolved "https://registry.npmmirror.com/antd-img-crop/-/antd-img-crop-4.12.2.tgz#28d3fe752d0b0b6d6bae1bb8303f3aefe04e0470"
integrity sha512-xgxR3x2sg+tCBHMfx1gejEfhhvnIL2mpZ4OIPfQDJZTTfm9YpMqaqLF9qWbF9Nf83bXCdaQywYihcVGyw3niDg==
dependencies:
compare-versions "6.0.0-rc.1"
react-easy-crop "^4.7.4"
tslib "^2.5.0"
antd@^5.4.2: antd@^5.4.2:
version "5.4.2" version "5.4.2"
resolved "https://registry.npmmirror.com/antd/-/antd-5.4.2.tgz#3923b96da76fc7276992e9fc0286ebb3a638e016" resolved "https://registry.npmmirror.com/antd/-/antd-5.4.2.tgz#3923b96da76fc7276992e9fc0286ebb3a638e016"
...@@ -3521,7 +3530,7 @@ array.prototype.tosorted@^1.1.1: ...@@ -3521,7 +3530,7 @@ array.prototype.tosorted@^1.1.1:
es-shim-unscopables "^1.0.0" es-shim-unscopables "^1.0.0"
get-intrinsic "^1.1.3" get-intrinsic "^1.1.3"
asap@~2.0.6: asap@~2.0.3, asap@~2.0.6:
version "2.0.6" version "2.0.6"
resolved "https://registry.npmmirror.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46" resolved "https://registry.npmmirror.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46"
integrity sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA== integrity sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==
...@@ -3822,6 +3831,39 @@ braces@^3.0.2, braces@~3.0.2: ...@@ -3822,6 +3831,39 @@ braces@^3.0.2, braces@~3.0.2:
dependencies: dependencies:
fill-range "^7.0.1" fill-range "^7.0.1"
braft-convert@^2.3.0:
version "2.3.0"
resolved "https://registry.npmmirror.com/braft-convert/-/braft-convert-2.3.0.tgz#27d5905136c334903d083b7a2352a72045627888"
integrity sha512-5km+dLHk8iYDv2iEYDrDQ2ld/ZoUx66QLql0qdm5PqZEcNXc8dBHGLORfzeu3iMw1jLeAiHxtdY5+ypuIhczVg==
dependencies:
draft-convert "^2.0.0"
draft-js "^0.10.3"
braft-editor@^2.3.9:
version "2.3.9"
resolved "https://registry.npmmirror.com/braft-editor/-/braft-editor-2.3.9.tgz#fd2b8e23ea71191016579a1ed8231d16ad8f5b4a"
integrity sha512-mqdPk/zI2dhFK8tW/A4Qj/AkkARLh5L/niNw+iif5wFqb6zh15rMlrShgz1nWO/QXyAKr8XtDgxiBbR0zWwtRg==
dependencies:
"@babel/runtime" "^7.0.0"
braft-convert "^2.3.0"
braft-finder "^0.0.19"
braft-utils "^3.0.8"
draft-convert "^2.0.0"
draft-js "^0.10.3"
draft-js-multidecorators "^1.0.0"
draftjs-utils "^0.9.4"
immutable "~3.7.4"
braft-finder@^0.0.19:
version "0.0.19"
resolved "https://registry.npmmirror.com/braft-finder/-/braft-finder-0.0.19.tgz#c324d82526ed3476a93de86cc9b407f4e188bc8d"
integrity sha512-0kzI6/KbomJJhYX1hpjn4edCKhblyUyWdUrsgBmOrwy0vrj+pPkm69+Uf8Uj6KGAULM6LF0ooC++p7fqUGgFHw==
braft-utils@^3.0.8:
version "3.0.12"
resolved "https://registry.npmmirror.com/braft-utils/-/braft-utils-3.0.12.tgz#2b755ce1d8397d96b627b6767f74d07f25729d85"
integrity sha512-O2cKysURNC4HSEMKgNmQ2RluwcrxvYrztlEmyPN5SzktiNX3vaLFQoo0Ez3PlIhvjaGrIBSIT2Oyh2N6mn6TFg==
browser-process-hrtime@^1.0.0: browser-process-hrtime@^1.0.0:
version "1.0.0" version "1.0.0"
resolved "https://registry.npmmirror.com/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz#3c9b4b7d782c8121e56f10106d84c0d0ffc94626" resolved "https://registry.npmmirror.com/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz#3c9b4b7d782c8121e56f10106d84c0d0ffc94626"
...@@ -4145,6 +4187,11 @@ commondir@^1.0.1: ...@@ -4145,6 +4187,11 @@ commondir@^1.0.1:
resolved "https://registry.npmmirror.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b" resolved "https://registry.npmmirror.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b"
integrity sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg== integrity sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==
compare-versions@6.0.0-rc.1:
version "6.0.0-rc.1"
resolved "https://registry.npmmirror.com/compare-versions/-/compare-versions-6.0.0-rc.1.tgz#93e6beb8767c2375333ee168fa64c28b75ace2c6"
integrity sha512-cFhkjbGY1jLFWIV7KegECbfuyYPxSGvgGkdkfM+ibboQDoPwg2FRHm5BSNTOApiauRBzJIQH7qvOJs2sW5ueKQ==
compressible@~2.0.16: compressible@~2.0.16:
version "2.0.18" version "2.0.18"
resolved "https://registry.npmmirror.com/compressible/-/compressible-2.0.18.tgz#af53cca6b070d4c3c0750fbd77286a6d7cc46fba" resolved "https://registry.npmmirror.com/compressible/-/compressible-2.0.18.tgz#af53cca6b070d4c3c0750fbd77286a6d7cc46fba"
...@@ -4257,6 +4304,11 @@ core-js-pure@^3.23.3: ...@@ -4257,6 +4304,11 @@ core-js-pure@^3.23.3:
resolved "https://registry.npmmirror.com/core-js-pure/-/core-js-pure-3.30.0.tgz#41b6c42e5f363bd53d79999bd35093b17e42e1bf" resolved "https://registry.npmmirror.com/core-js-pure/-/core-js-pure-3.30.0.tgz#41b6c42e5f363bd53d79999bd35093b17e42e1bf"
integrity sha512-+2KbMFGeBU0ln/csoPqTe0i/yfHbrd2EUhNMObsGtXMKS/RTtlkYyi+/3twLcevbgNR0yM/r0Psa3TEoQRpFMQ== integrity sha512-+2KbMFGeBU0ln/csoPqTe0i/yfHbrd2EUhNMObsGtXMKS/RTtlkYyi+/3twLcevbgNR0yM/r0Psa3TEoQRpFMQ==
core-js@^1.0.0:
version "1.2.7"
resolved "https://registry.npmmirror.com/core-js/-/core-js-1.2.7.tgz#652294c14651db28fa93bd2d5ff2983a4f08c636"
integrity sha512-ZiPp9pZlgxpWRu0M+YWbm6+aQ84XEfH1JRXvfOc/fILWI0VKhLC2LX13X1NYq4fULzLMq7Hfh43CSo2/aIaUPA==
core-js@^3.19.2: core-js@^3.19.2:
version "3.30.0" version "3.30.0"
resolved "https://registry.npmmirror.com/core-js/-/core-js-3.30.0.tgz#64ac6f83bc7a49fd42807327051701d4b1478dea" resolved "https://registry.npmmirror.com/core-js/-/core-js-3.30.0.tgz#64ac6f83bc7a49fd42807327051701d4b1478dea"
...@@ -4531,7 +4583,7 @@ date-fns@^2.29.3: ...@@ -4531,7 +4583,7 @@ date-fns@^2.29.3:
resolved "https://registry.npmmirror.com/date-fns/-/date-fns-2.29.3.tgz#27402d2fc67eb442b511b70bbdf98e6411cd68a8" resolved "https://registry.npmmirror.com/date-fns/-/date-fns-2.29.3.tgz#27402d2fc67eb442b511b70bbdf98e6411cd68a8"
integrity sha512-dDCnyH2WnnKusqvZZ6+jA1O51Ibt8ZMRNkDZdyAyK4YfbDwa/cEmuztzG5pk6hqlp9aSBPYcjOlktquahGwGeA== integrity sha512-dDCnyH2WnnKusqvZZ6+jA1O51Ibt8ZMRNkDZdyAyK4YfbDwa/cEmuztzG5pk6hqlp9aSBPYcjOlktquahGwGeA==
dayjs@^1.11.1, dayjs@^1.11.4, dayjs@^1.9.1: dayjs@^1.11.1, dayjs@^1.11.4, dayjs@^1.11.7, dayjs@^1.9.1:
version "1.11.7" version "1.11.7"
resolved "https://registry.npmmirror.com/dayjs/-/dayjs-1.11.7.tgz#4b296922642f70999544d1144a2c25730fce63e2" resolved "https://registry.npmmirror.com/dayjs/-/dayjs-1.11.7.tgz#4b296922642f70999544d1144a2c25730fce63e2"
integrity sha512-+Yw9U6YO5TQohxLcIkrXBeY73WP3ejHWVvx8XCk3gxvQDCTEmS48ZrSZCKciI7Bhl/uCMyxYtE9UqRILmFphkQ== integrity sha512-+Yw9U6YO5TQohxLcIkrXBeY73WP3ejHWVvx8XCk3gxvQDCTEmS48ZrSZCKciI7Bhl/uCMyxYtE9UqRILmFphkQ==
...@@ -4821,6 +4873,36 @@ dotenv@^16.0.0: ...@@ -4821,6 +4873,36 @@ dotenv@^16.0.0:
resolved "https://registry.npmmirror.com/dotenv/-/dotenv-16.0.3.tgz#115aec42bac5053db3c456db30cc243a5a836a07" resolved "https://registry.npmmirror.com/dotenv/-/dotenv-16.0.3.tgz#115aec42bac5053db3c456db30cc243a5a836a07"
integrity sha512-7GO6HghkA5fYG9TYnNxi14/7K9f5occMlp3zXAuSxn7CKCxt9xbNWG7yF8hTCSUchlfWSe3uLmlPfigevRItzQ== integrity sha512-7GO6HghkA5fYG9TYnNxi14/7K9f5occMlp3zXAuSxn7CKCxt9xbNWG7yF8hTCSUchlfWSe3uLmlPfigevRItzQ==
draft-convert@^2.0.0:
version "2.1.13"
resolved "https://registry.npmmirror.com/draft-convert/-/draft-convert-2.1.13.tgz#ffa1878feee88e0911c5728cd1445f6e5d861a41"
integrity sha512-/h/n4JCfyO8aWby7wKBkccHdsuVbbDyHWXi/B3Zf2pN++lN1lDOIVt5ulXCcbH2Y5YJEFzMJw/YGfN+R0axxxg==
dependencies:
"@babel/runtime" "^7.5.5"
immutable "~3.7.4"
invariant "^2.2.1"
draft-js-multidecorators@^1.0.0:
version "1.0.0"
resolved "https://registry.npmmirror.com/draft-js-multidecorators/-/draft-js-multidecorators-1.0.0.tgz#6c4be8d7b78dd2b966ee51ee6cc179b9b535e612"
integrity sha512-7qdy+YQol5iq38AoEerhgSJWhCzxvZLn1x5ODfUlGfWlg0SrZ9AXJbaxHVIjdSIZNrbVIm+WANujNxMqCmDSZQ==
dependencies:
immutable "*"
draft-js@^0.10.3:
version "0.10.5"
resolved "https://registry.npmmirror.com/draft-js/-/draft-js-0.10.5.tgz#bfa9beb018fe0533dbb08d6675c371a6b08fa742"
integrity sha512-LE6jSCV9nkPhfVX2ggcRLA4FKs6zWq9ceuO/88BpXdNCS7mjRTgs0NsV6piUCJX9YxMsB9An33wnkMmU2sD2Zg==
dependencies:
fbjs "^0.8.15"
immutable "~3.7.4"
object-assign "^4.1.0"
draftjs-utils@^0.9.4:
version "0.9.4"
resolved "https://registry.npmmirror.com/draftjs-utils/-/draftjs-utils-0.9.4.tgz#976c61aa133dbbbfedd65ae1dd6627d7b98c6f08"
integrity sha512-KYjABSbGpJrwrwmxVj5UhfV37MF/p0QRxKIyL+/+QOaJ8J9z1FBKxkblThbpR0nJi9lxPQWGg+gh+v0dAsSCCg==
duplexer@^0.1.2: duplexer@^0.1.2:
version "0.1.2" version "0.1.2"
resolved "https://registry.npmmirror.com/duplexer/-/duplexer-0.1.2.tgz#3abe43aef3835f8ae077d136ddce0f276b0400e6" resolved "https://registry.npmmirror.com/duplexer/-/duplexer-0.1.2.tgz#3abe43aef3835f8ae077d136ddce0f276b0400e6"
...@@ -5461,6 +5543,19 @@ fb-watchman@^2.0.0: ...@@ -5461,6 +5543,19 @@ fb-watchman@^2.0.0:
dependencies: dependencies:
bser "2.1.1" bser "2.1.1"
fbjs@^0.8.15:
version "0.8.18"
resolved "https://registry.npmmirror.com/fbjs/-/fbjs-0.8.18.tgz#9835e0addb9aca2eff53295cd79ca1cfc7c9662a"
integrity sha512-EQaWFK+fEPSoibjNy8IxUtaFOMXcWsY0JaVrQoZR9zC8N2Ygf9iDITPWjUTVIax95b6I742JFLqASHfsag/vKA==
dependencies:
core-js "^1.0.0"
isomorphic-fetch "^2.1.1"
loose-envify "^1.0.0"
object-assign "^4.1.0"
promise "^7.1.1"
setimmediate "^1.0.5"
ua-parser-js "^0.7.30"
file-entry-cache@^6.0.1: file-entry-cache@^6.0.1:
version "6.0.1" version "6.0.1"
resolved "https://registry.npmmirror.com/file-entry-cache/-/file-entry-cache-6.0.1.tgz#211b2dd9659cb0394b073e7323ac3c933d522027" resolved "https://registry.npmmirror.com/file-entry-cache/-/file-entry-cache-6.0.1.tgz#211b2dd9659cb0394b073e7323ac3c933d522027"
...@@ -6091,6 +6186,16 @@ immer@^9.0.16, immer@^9.0.7: ...@@ -6091,6 +6186,16 @@ immer@^9.0.16, immer@^9.0.7:
resolved "https://registry.npmmirror.com/immer/-/immer-9.0.21.tgz#1e025ea31a40f24fb064f1fef23e931496330176" resolved "https://registry.npmmirror.com/immer/-/immer-9.0.21.tgz#1e025ea31a40f24fb064f1fef23e931496330176"
integrity sha512-bc4NBHqOqSfRW7POMkHd51LvClaeMXpm8dx0e8oE2GORbq5aRK7Bxl4FyzVLdGtLmvLKL7BTDBG5ACQm4HWjTA== integrity sha512-bc4NBHqOqSfRW7POMkHd51LvClaeMXpm8dx0e8oE2GORbq5aRK7Bxl4FyzVLdGtLmvLKL7BTDBG5ACQm4HWjTA==
immutable@*:
version "4.3.0"
resolved "https://registry.npmmirror.com/immutable/-/immutable-4.3.0.tgz#eb1738f14ffb39fd068b1dbe1296117484dd34be"
integrity sha512-0AOCmOip+xgJwEVTQj1EfiDDOkPmuyllDuTuEX+DDXUgapLAsBIfkg3sxCYyCEA8mQqZrrxPUGjcOQ2JS3WLkg==
immutable@~3.7.4:
version "3.7.6"
resolved "https://registry.npmmirror.com/immutable/-/immutable-3.7.6.tgz#13b4d3cb12befa15482a26fe1b2ebae640071e4b"
integrity sha512-AizQPcaofEtO11RZhPPHBOJRdo/20MKQF9mBLnVkBoyHi1/zXK8fzVdnEpSV9gxqtnh6Qomfp3F0xT5qP/vThw==
import-fresh@^3.0.0, import-fresh@^3.1.0, import-fresh@^3.2.1: import-fresh@^3.0.0, import-fresh@^3.1.0, import-fresh@^3.2.1:
version "3.3.0" version "3.3.0"
resolved "https://registry.npmmirror.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b" resolved "https://registry.npmmirror.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b"
...@@ -6149,7 +6254,7 @@ intersection-observer@^0.12.0: ...@@ -6149,7 +6254,7 @@ intersection-observer@^0.12.0:
resolved "https://registry.npmmirror.com/intersection-observer/-/intersection-observer-0.12.2.tgz#4a45349cc0cd91916682b1f44c28d7ec737dc375" resolved "https://registry.npmmirror.com/intersection-observer/-/intersection-observer-0.12.2.tgz#4a45349cc0cd91916682b1f44c28d7ec737dc375"
integrity sha512-7m1vEcPCxXYI8HqnL8CKI6siDyD+eIWSwgB3DZA+ZTogxk9I4CDnj4wilt9x/+/QbHI4YG5YZNmC6458/e9Ktg== integrity sha512-7m1vEcPCxXYI8HqnL8CKI6siDyD+eIWSwgB3DZA+ZTogxk9I4CDnj4wilt9x/+/QbHI4YG5YZNmC6458/e9Ktg==
invariant@^2.2.4: invariant@^2.2.1, invariant@^2.2.4:
version "2.2.4" version "2.2.4"
resolved "https://registry.npmmirror.com/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6" resolved "https://registry.npmmirror.com/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6"
integrity sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA== integrity sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==
...@@ -6420,7 +6525,7 @@ isexe@^2.0.0: ...@@ -6420,7 +6525,7 @@ isexe@^2.0.0:
resolved "https://registry.npmmirror.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" resolved "https://registry.npmmirror.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10"
integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==
isomorphic-fetch@^2.2.1: isomorphic-fetch@^2.1.1, isomorphic-fetch@^2.2.1:
version "2.2.1" version "2.2.1"
resolved "https://registry.npmmirror.com/isomorphic-fetch/-/isomorphic-fetch-2.2.1.tgz#611ae1acf14f5e81f729507472819fe9733558a9" resolved "https://registry.npmmirror.com/isomorphic-fetch/-/isomorphic-fetch-2.2.1.tgz#611ae1acf14f5e81f729507472819fe9733558a9"
integrity sha512-9c4TNAKYXM5PRyVcwUZrF3W09nQ+sO7+jydgs4ZGW9dhsLG2VOlISJABombdQqQRXCwuYG3sYV/puGf5rp0qmA== integrity sha512-9c4TNAKYXM5PRyVcwUZrF3W09nQ+sO7+jydgs4ZGW9dhsLG2VOlISJABombdQqQRXCwuYG3sYV/puGf5rp0qmA==
...@@ -7571,6 +7676,11 @@ normalize-url@^6.0.1: ...@@ -7571,6 +7676,11 @@ normalize-url@^6.0.1:
resolved "https://registry.npmmirror.com/normalize-url/-/normalize-url-6.1.0.tgz#40d0885b535deffe3f3147bec877d05fe4c5668a" resolved "https://registry.npmmirror.com/normalize-url/-/normalize-url-6.1.0.tgz#40d0885b535deffe3f3147bec877d05fe4c5668a"
integrity sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A== integrity sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==
normalize-wheel@^1.0.1:
version "1.0.1"
resolved "https://registry.npmmirror.com/normalize-wheel/-/normalize-wheel-1.0.1.tgz#aec886affdb045070d856447df62ecf86146ec45"
integrity sha512-1OnlAPZ3zgrk8B91HyRj+eVv+kS5u+Z0SCsak6Xil/kmgEia50ga7zfkumayonZrImffAxPU/5WcyGhzetHNPA==
npm-run-path@^4.0.1: npm-run-path@^4.0.1:
version "4.0.1" version "4.0.1"
resolved "https://registry.npmmirror.com/npm-run-path/-/npm-run-path-4.0.1.tgz#b7ecd1e5ed53da8e37a55e1c2269e0b97ed748ea" resolved "https://registry.npmmirror.com/npm-run-path/-/npm-run-path-4.0.1.tgz#b7ecd1e5ed53da8e37a55e1c2269e0b97ed748ea"
...@@ -7602,7 +7712,7 @@ nwsapi@^2.2.0: ...@@ -7602,7 +7712,7 @@ nwsapi@^2.2.0:
resolved "https://registry.npmmirror.com/nwsapi/-/nwsapi-2.2.2.tgz#e5418863e7905df67d51ec95938d67bf801f0bb0" resolved "https://registry.npmmirror.com/nwsapi/-/nwsapi-2.2.2.tgz#e5418863e7905df67d51ec95938d67bf801f0bb0"
integrity sha512-90yv+6538zuvUMnN+zCr8LuV6bPFdq50304114vJYJ8RDyK8D5O9Phpbd6SZWgI7PwzmmfN1upeOJlvybDSgCw== integrity sha512-90yv+6538zuvUMnN+zCr8LuV6bPFdq50304114vJYJ8RDyK8D5O9Phpbd6SZWgI7PwzmmfN1upeOJlvybDSgCw==
object-assign@4.x, object-assign@^4.0.1, object-assign@^4.1.1: object-assign@4.x, object-assign@^4.0.1, object-assign@^4.1.0, object-assign@^4.1.1:
version "4.1.1" version "4.1.1"
resolved "https://registry.npmmirror.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" resolved "https://registry.npmmirror.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg== integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==
...@@ -8556,6 +8666,13 @@ process-nextick-args@~2.0.0: ...@@ -8556,6 +8666,13 @@ process-nextick-args@~2.0.0:
resolved "https://registry.npmmirror.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" resolved "https://registry.npmmirror.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2"
integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==
promise@^7.1.1:
version "7.3.1"
resolved "https://registry.npmmirror.com/promise/-/promise-7.3.1.tgz#064b72602b18f90f29192b8b1bc418ffd1ebd3bf"
integrity sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==
dependencies:
asap "~2.0.3"
promise@^8.1.0: promise@^8.1.0:
version "8.3.0" version "8.3.0"
resolved "https://registry.npmmirror.com/promise/-/promise-8.3.0.tgz#8cb333d1edeb61ef23869fbb8a4ea0279ab60e0a" resolved "https://registry.npmmirror.com/promise/-/promise-8.3.0.tgz#8cb333d1edeb61ef23869fbb8a4ea0279ab60e0a"
...@@ -9132,6 +9249,14 @@ react-draggable@^4.0.3: ...@@ -9132,6 +9249,14 @@ react-draggable@^4.0.3:
clsx "^1.1.1" clsx "^1.1.1"
prop-types "^15.8.1" prop-types "^15.8.1"
react-easy-crop@^4.7.4:
version "4.7.4"
resolved "https://registry.npmmirror.com/react-easy-crop/-/react-easy-crop-4.7.4.tgz#3106bfb3de371d9393aedc0b9ae1fb97c2b9f722"
integrity sha512-oDi1375Jo/zuPUvo3oauxnNbfy8L4wsbmHD1KB2vT55fdgu+q8/K0w/rDWzy9jz4jfQ94Q9+3Yu366sDDFVmiA==
dependencies:
normalize-wheel "^1.0.1"
tslib "2.0.1"
react-error-overlay@^6.0.11: react-error-overlay@^6.0.11:
version "6.0.11" version "6.0.11"
resolved "https://registry.npmmirror.com/react-error-overlay/-/react-error-overlay-6.0.11.tgz#92835de5841c5cf08ba00ddd2d677b6d17ff9adb" resolved "https://registry.npmmirror.com/react-error-overlay/-/react-error-overlay-6.0.11.tgz#92835de5841c5cf08ba00ddd2d677b6d17ff9adb"
...@@ -9771,6 +9896,11 @@ serve-static@1.15.0: ...@@ -9771,6 +9896,11 @@ serve-static@1.15.0:
parseurl "~1.3.3" parseurl "~1.3.3"
send "0.18.0" send "0.18.0"
setimmediate@^1.0.5:
version "1.0.5"
resolved "https://registry.npmmirror.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285"
integrity sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==
setprototypeof@1.1.0: setprototypeof@1.1.0:
version "1.1.0" version "1.1.0"
resolved "https://registry.npmmirror.com/setprototypeof/-/setprototypeof-1.1.0.tgz#d0bd85536887b6fe7c0d818cb962d9d91c54e656" resolved "https://registry.npmmirror.com/setprototypeof/-/setprototypeof-1.1.0.tgz#d0bd85536887b6fe7c0d818cb962d9d91c54e656"
...@@ -10541,12 +10671,17 @@ tsconfig-paths@^3.14.1: ...@@ -10541,12 +10671,17 @@ tsconfig-paths@^3.14.1:
minimist "^1.2.6" minimist "^1.2.6"
strip-bom "^3.0.0" strip-bom "^3.0.0"
tslib@2.0.1:
version "2.0.1"
resolved "https://registry.npmmirror.com/tslib/-/tslib-2.0.1.tgz#410eb0d113e5b6356490eec749603725b021b43e"
integrity sha512-SgIkNheinmEBgx1IUNirK0TUD4X9yjjBRTqqjggWCU3pUEqIk3/Uwl3yRixYKT6WjQuGiwDv4NomL3wqRCj+CQ==
tslib@^1.8.1: tslib@^1.8.1:
version "1.14.1" version "1.14.1"
resolved "https://registry.npmmirror.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" resolved "https://registry.npmmirror.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00"
integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==
tslib@^2.0.3, tslib@^2.3.0, tslib@^2.4.1: tslib@^2.0.3, tslib@^2.3.0, tslib@^2.4.1, tslib@^2.5.0:
version "2.5.0" version "2.5.0"
resolved "https://registry.npmmirror.com/tslib/-/tslib-2.5.0.tgz#42bfed86f5787aeb41d031866c8f402429e0fddf" resolved "https://registry.npmmirror.com/tslib/-/tslib-2.5.0.tgz#42bfed86f5787aeb41d031866c8f402429e0fddf"
integrity sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg== integrity sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==
...@@ -10616,6 +10751,11 @@ typedarray-to-buffer@^3.1.5: ...@@ -10616,6 +10751,11 @@ typedarray-to-buffer@^3.1.5:
dependencies: dependencies:
is-typedarray "^1.0.0" is-typedarray "^1.0.0"
ua-parser-js@^0.7.30:
version "0.7.35"
resolved "https://registry.npmmirror.com/ua-parser-js/-/ua-parser-js-0.7.35.tgz#8bda4827be4f0b1dda91699a29499575a1f1d307"
integrity sha512-veRf7dawaj9xaWEu9HoTVn5Pggtc/qj+kqTOFvNiN1l0YdxwC1kvel57UCjThjGa3BHBihE8/UJAHI+uQHmd/g==
umi-request@^1.4.0: umi-request@^1.4.0:
version "1.4.0" version "1.4.0"
resolved "https://registry.npmmirror.com/umi-request/-/umi-request-1.4.0.tgz#ed0e54e47f043d2be06e691477f0890383f9dd8a" resolved "https://registry.npmmirror.com/umi-request/-/umi-request-1.4.0.tgz#ed0e54e47f043d2be06e691477f0890383f9dd8a"
......
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