react+antd pro实现【列表可实时行内编辑】的弹窗表单组件
纯列表版效果展示:
① 初始无值,展示为唤醒按钮+文案外链
②点击按钮唤醒弹窗(简易版示意图)
③配置后
可编辑表格组件文档:
https://procomponents.ant.design/components/editable-table
纯列表组件代码:
表单引用:
// antd-pro的高级表单组件
import SchemaForm from '@/components/SchemaForm';
import AssetsTable from './AssetsTable'; --------------------------------------- <SchemaForm<API.AssetsProps>
formRef={formRef}
dataSource={dataSource.map((item: any) => {
if (item.id === 'event_asset') {
return {
...item,
required: '请配置事件资产',
fieldProps: { ...item.fieldProps, fetchListApi },
renderFormItem: (schema: any) => <AssetsTable {...schema.fieldProps} />,
formItemProps: {
rules: [
{
required: true,
validator(rule: any, value: any[]) {
if (value && value.length) {
if (value.length !== accounts?.length) {
return Promise.reject(new Error('请为全部账户配置资产事件'));
}
if (value.find((itemDeep: any) => !itemDeep.deep_bid_type)) {
return Promise.reject(new Error('请为全部账户配置深度出价方式'));
}
return Promise.resolve();
}
return Promise.resolve();
},
},
],
},
};
}
return item;
})}
submitter={false}
autoFocusFirstInput={false}
onValuesChange={(changedFields: any, allFields: any) => {
// 表单值改变时的处理逻辑
}}
/>
组件代码:
import { useEffect, useState } from 'react';
import { Modal, Button, Input, message } from 'antd';
import { isEmpty } from 'lodash';
import type { ProColumns } from '@ant-design/pro-table';
import { EditableProTable } from '@ant-design/pro-table';
import { fetchAssetsList } from './services'; const { Search } = Input; type AssetsProps = {
value?: any[];
onChange?: (value?: any[]) => void;
/** 列表接口地址 */
fetchListApi?: string;
}; /** 弹窗 */
const AssetsTable: React.FC<AssetsProps> = (props) => {
const { value, onChange, fetchListApi } = props;
const [data, setData] = useState<API.AssetsProps[]>(value || []); // 存储选中的数据
const [visible, setVisible] = useState<boolean>(false);
const [dataSource, setDataSource] = useState<API.AssetsProps[]>([]); // 列表数据
const [tableColumns, setTableColumns] = useState<ProColumns[]>([]);
const [tableParams, setTableParams] = useState<any>();
const [tableLoading, setTableLoading] = useState<boolean>(false);
const [editableKeys, setEditableRowKeys] = useState<React.Key[]>([]);
const [keys, setKeys] = useState<(string | number)[]>([]); const handleOk = () => {
if (onChange) { onChange(data); setVisible(false);}
}; const fetchList = async () => {
setTableLoading(true);
if (fetchListApi) {
// 根据资产类型+投放账号调用头条已有的资产作为选择值
try {
const res: any = await fetchAssetsList(fetchListApi, tableParams);
if (res.result) {
setTableColumns([]);
const { tableHeaderList = [], tableList = [] } = res.result;
// 当有存储值时,覆盖掉接口返回的对应数据
if (data && data.length) {
const initObj = data.find((item: any) => item.account_id === id);
const newList = tableList.map((item: any) => {
if (initObj && initObj.id && item.id === initObj.id) {
return initObj;
}
return item;
});
setDataSource(newList);
setEditableRowKeys(newList?.map((item: any) => item.id));
} else {
setDataSource(tableList);
setEditableRowKeys(tableList?.map((item: any) => item.id));
}
// 深度优化目标下拉单选
setTableColumns([
...tableHeaderList.map((item: any) => {
if (item.dataIndex === 'deep_external_action') {
return {
...item,
width: 200,
valueType: 'select',
valueEnum: (row: any) => {
const optionList = row?.deep_goals
?.filter((itemF: any) => itemF.optimization_name)
.map((itemO: any) => {
return {
label: itemO.optimization_name,
value: itemO.deep_external_action,
};
});
const valueEnumObj = {};
optionList?.forEach((items: any) => {
valueEnumObj[items.value] = { text: items.label };
});
return valueEnumObj;
},
};
}
// 深度出价方式需要动态获取
if (item.dataIndex === 'deep_bid_type') {
return {
...item,
width: 200,
valueType: 'select',
valueEnum: (row: any) => {
let deepList: any[] = [];
const hasChoose = row?.deep_goals?.find(
(el: any) => el.deep_external_action === row.deep_external_action,
);
if (hasChoose) {
deepList =
row?.deep_goals.find(
(el: any) => el.deep_external_action === row.deep_external_action,
)?.deep_bid_type || [];
} else {
deepList = row?.default_deep_bid_type || [];
}
const optionList = deepList?.map((itemO: any) => {
return {
label: itemO.name,
value: itemO.id,
};
});
const valueEnumObj = {};
optionList?.forEach((items: any) => {
valueEnumObj[items.value] = { text: items.label };
});
return valueEnumObj;
},
};
}
return { ...item, editable: false };
}),
]);
}
setTableLoading(false);
} catch (error) {
//
}
}
}; // 关键词搜索
useEffect(() => {
if (!isEmpty(tableParams)) fetchList();
}, [tableParams]); useEffect(() => {
setData(dataSource || []);
}, [dataSource]); useEffect(() => {
if (visible) {
fetchList();
// 更新data
if (value && Array.isArray(value) && value.length) {
setData(value);
}
}
}, [visible]); useEffect(() => {
// 更新data
if (value && Array.isArray(value) && value.length) {
setData(value);
}
}, [value]); return (
<>
<div>
<Button onClick={() => setVisible(true)}>
{data && data.length ? '已' : ''}选择事件资产{data && data.length ? ',点击配置' : ''}
</Button>
</div>
<Modal width={1500} visible={visible} onCancel={() => setVisible(false)} onOk={handleOk}>
<div>
<Search
addonBefore="资产名称"
placeholder="请输入关键词搜索"
allowClear
onSearch={async (valueSearch: string) =>
setTableParams({ ...tableParams, asset_name: valueSearch })
}
style={{ width: 300, marginBottom: '8px' }}
/>
<EditableProTable
rowKey="id"
loading={tableLoading}
bordered
request={async () => ({
data: dataSource,
total: dataSource.length,
success: true,
})}
tableAlertRender={false}
value={dataSource}
onChange={(valueNew: any[]) => {
const newTableData = valueNew.map((item: any, index: number) => {
// 深度优化目标的值改变时,清空本条数据的深度出价方式的值
if (item.id === dataSource[index].id) {
if (item.deep_external_action !== dataSource[index].deep_external_action) {
return { ...item, deep_bid_type: undefined };
}
return item;
}
return item;
});
setDataSource(newTableData);
}}
controlled
recordCreatorProps={false}
editable={{
type: 'multiple',
editableKeys,
actionRender: undefined,
onChange: setEditableRowKeys,
}}
scroll={{ y: 320, x: 'max-content' }}
columns={tableColumns}
tableRender={undefined}
pagination={false}
rowSelection={{
type: 'radio',
selectedRowKeys: keys,
onChange: (selectedRowKeys: any, selectedRows: any) => {
// 存储选中数据逻辑
},
}}
/>
{dataSource && dataSource.length ? <p>共 {dataSource.length} 条</p> : null}
</div>
</Modal>
</>
);
}; export default AssetsTable;
--------------------------------------以上代码可满足按钮唤醒弹窗并对列表数据进行行内修改的需求-------------------------------------
完整版需求效果展示:
其实就是多了左侧账号栏,要求给左侧每个账号进行配置,右侧展示可选列表,单项选择,可配置内容,其中选中项必须配置出价方式;
点击确定时校验是否满足要求,否则弹出提示;
重新点开弹窗时也要保留上次选择与配置的内容。
具体代码:
import { useEffect, useState, useRef, useContext } from 'react';
import { Modal, Button, Input, message } from 'antd';
import { isEmpty } from 'lodash';
import ScrollList from '@/components/ScrollList';
import type { ProColumns } from '@ant-design/pro-table';
import { EditableProTable } from '@ant-design/pro-table';
import { fetchAssetsList } from './services';
import SmartCreationContext from '../../context';
import styles from './Assets.less'; const { Search } = Input; type AssetsProps = {
value?: any[];
onChange?: (value?: any[]) => void;
/** 事件资产列表接口地址 */
fetchListApi?: string;
}; /** 事件资产弹窗 */
const AssetsTable: React.FC<AssetsProps> = (props) => {
const { value, onChange, fetchListApi } = props;
const { dependentData, values } = useContext(SmartCreationContext);
const { accounts = [] } = dependentData || {};
const { create_rules } = values || {};
const adsRef = useRef<any>();
const [data, setData] = useState<API.AssetsProps[]>(value || []); // 存储选中的数据
const [visible, setVisible] = useState<boolean>(false);
const [dataSource, setDataSource] = useState<API.AssetsProps[]>([]); // 列表数据
const [tableColumns, setTableColumns] = useState<ProColumns[]>([]);
const [tableParams, setTableParams] = useState<any>();
const [tableLoading, setTableLoading] = useState<boolean>(false);
const [targetAccount, setTargetAccount] = useState<any>('');
const [editableKeys, setEditableRowKeys] = useState<React.Key[]>([]);
const [keys, setKeys] = useState<(string | number)[]>([]); const handleOk = () => {
if (onChange) {
if (data.length === accounts.length) {
if (data?.find((itemDeep: any) => !itemDeep.deep_bid_type)) {
message.error('请为全部账户配置深度出价方式');
return;
}
onChange(data);
setVisible(false);
} else {
message.error('请为全部账户配置资产事件');
}
}
}; const fetchList = async (id: string) => {
setTargetAccount(id);
setTableLoading(true);
const trace_events = JSON.parse(sessionStorage.getItem('ASSETS_FORM') || '');
if (fetchListApi) {
// 根据账号调用已有的资产作为选择值
try {
const res: any = await fetchAssetsList(fetchListApi, {
account_id: id, // 账号
...tableParams,
});
if (res.result) {
setTableColumns([]);
const { tableHeaderList = [], tableList = [] } = res.result;
const dataTable = tableList.map((item: any) => {
return { ...item, account_id: id };
});
// 当有存储值时,覆盖掉接口返回的对应数据
if (data && data.length) {
const initObj = data.find((item: any) => item.account_id === id);
const newList = dataTable.map((item: any) => {
if (initObj && initObj.id && item.id === initObj.id) {
return initObj;
}
return item;
});
setDataSource(newList);
setEditableRowKeys(newList?.map((item: any) => item.id));
} else {
setDataSource(dataTable);
setEditableRowKeys(dataTable?.map((item: any) => item.id));
}
// 深度优化目标下拉单选
setTableColumns([
...tableHeaderList.map((item: any) => {
if (item.dataIndex === 'deep_external_action') {
return {
...item,
width: 200,
valueType: 'select',
valueEnum: (row: any) => {
const optionList = row?.deep_goals
?.filter((itemF: any) => itemF.optimization_name)
.map((itemO: any) => {
return {
label: itemO.optimization_name,
value: itemO.deep_external_action,
};
});
const valueEnumObj = {};
optionList?.forEach((items: any) => {
valueEnumObj[items.value] = { text: items.label };
});
return valueEnumObj;
},
};
}
// 深度出价方式需要动态获取
if (item.dataIndex === 'deep_bid_type') {
return {
...item,
width: 200,
valueType: 'select',
valueEnum: (row: any) => {
let deepList: any[] = [];
const hasChoose = row?.deep_goals?.find(
(el: any) => el.deep_external_action === row.deep_external_action,
);
if (hasChoose) {
deepList =
row?.deep_goals.find(
(el: any) => el.deep_external_action === row.deep_external_action,
)?.deep_bid_type || [];
} else {
deepList = row?.default_deep_bid_type || [];
}
const optionList = deepList?.map((itemO: any) => {
return {
label: itemO.name,
value: itemO.id,
};
});
const valueEnumObj = {};
optionList?.forEach((items: any) => {
valueEnumObj[items.value] = { text: items.label };
});
return valueEnumObj;
},
};
}
return {
...item,
width: 200,
editable: false,
};
}),
]);
}
setTableLoading(false);
} catch (error) {
//
}
}
}; // 关键词搜索
useEffect(() => {
if (!isEmpty(tableParams)) fetchList(targetAccount);
}, [tableParams]); useEffect(() => {
if (dataSource.length) {
const newList = data?.map((item: any) => {
if (item.account_id === targetAccount) {
const newObj = dataSource.find((itemList: any) => itemList.id === item.id);
return newObj;
}
return item;
});
setData(newList);
} else {
setData([]);
}
}, [dataSource]); // 当前选中key
useEffect(() => {
if (targetAccount) {
if (data && data.length) {
const idList = data
.filter((item) => item.account_id === targetAccount)
.map((item: API.AssetsProps) => {
return item.id;
});
setKeys(idList);
} else {
setKeys([]);
}
}
}, [targetAccount, data]); // 打开弹窗时,默认左侧广告投放列表选中第一项,并进行列表请求
useEffect(() => {
if (accounts && accounts[0]) {
adsRef?.current?.selectItem(accounts[0]);
setTargetAccount(accounts[0].id);
if (visible) {
fetchList(accounts[0].id || '');
// 更新data
if (value && Array.isArray(value) && value.length) {
setData(value);
}
}
}
}, [visible]); useEffect(() => {
// 更新data
if (value && Array.isArray(value) && value.length) {
setData(value);
}
}, [value]); return (
<>
<div>
<Button
onClick={() => {
const trace_events = JSON.parse(sessionStorage.getItem('ASSETS_FORM') || '');
if (!trace_events?.app_type) {
message.error('请选择资产类型');
return;
}
setVisible(true);
}}
>
{data && data.length ? '已' : ''}选择事件资产{data && data.length ? ',点击配置' : ''}
</Button>
{data && data.length ? null : (
<span style={{ marginLeft: '8px' }}>
找不到适用的资产,去
<a
href="/"
key="createAssets"
target="_blank"
rel="noreferrer"
>
创建资产
</a>
!
</span>
)}
</div>
<Modal width={1500} visible={visible} onCancel={() => setVisible(false)} onOk={handleOk}>
<div className={styles['existring-ads-list']}>
<div style={{ width: '16%', marginRight: '16px' }}>
<ScrollList cRef={adsRef} list={accounts} title="账号名称" onChange={fetchList} />
</div>
<div style={{ width: '84%' }}>
<Search
addonBefore="资产名称"
placeholder="请输入关键词搜索"
allowClear
onSearch={async (valueSearch: string) =>
setTableParams({ ...tableParams, asset_name: valueSearch })
}
style={{ width: 300, marginBottom: '8px' }}
/>
<EditableProTable
rowKey="id"
loading={tableLoading}
bordered
request={async () => ({
data: dataSource,
total: dataSource.length,
success: true,
})}
tableAlertRender={false}
className={styles['exist-table-edit']}
value={dataSource}
onChange={(valueNew: any[]) => {
const newTableData = valueNew.map((item: any, index: number) => {
// 深度优化目标的值改变时,清空本条数据的深度出价方式的值
if (item.id === dataSource[index].id) {
if (item.deep_external_action !== dataSource[index].deep_external_action) {
return { ...item, deep_bid_type: undefined };
}
return item;
}
return item;
});
setDataSource(newTableData);
}}
controlled
recordCreatorProps={false}
editable={{
type: 'multiple',
editableKeys,
actionRender: undefined,
onChange: setEditableRowKeys,
}}
scroll={{ y: 320, x: 'max-content' }}
columns={tableColumns}
tableRender={undefined}
pagination={false}
rowSelection={{
type: 'radio',
selectedRowKeys: keys,
onChange: (selectedRowKeys: any, selectedRows: any) => {
if (data && data.length) {
// hasAccount = 当前账号是否存在
const hasAccount = data.filter((item) => item.account_id === targetAccount);
if (hasAccount && hasAccount.length) {
const newList = data.map((item: API.AssetsProps) => {
if (item.account_id === targetAccount) {
return selectedRows[0];
}
return item;
});
setData(newList);
} else {
setData([...data, ...selectedRows]);
}
} else {
setData(selectedRows);
}
},
}}
/>
{dataSource && dataSource.length ? <p>共 {dataSource.length} 条</p> : null}
</div>
</div>
</Modal>
</>
);
}; export default AssetsTable;
react+antd pro实现【列表可实时行内编辑】的弹窗表单组件的更多相关文章
- ASP.NET Aries 入门开发教程6:列表数据表格的格式化处理及行内编辑
前言: 为了赶进度,周末也写文了! 前几篇讲完查询框和工具栏,这节讲表格数据相关的操作. 先看一下列表: 接下来我们有很多事情可以做. 1:格式化 - 键值的翻译 对于“启用”列,已经配置了格式化 # ...
- 封装react antd的form表单组件
form表单在我们日常的开发过程中被使用到的概率还是很大的,比如包含了登录.注册.修改个人信息.新增修改业务数据等的公司内部管理系统.而在使用时这些表单的样式如高度.上下边距.边框.圆角.阴影.高亮等 ...
- Dynamics 365新功能:可编辑的网格(行内编辑)
关注本人微信和易信公众号: 微软动态CRM专家罗勇 ,回复238或者20161127可方便获取本文,同时可以在第一间得到我发布的最新的博文信息,follow me!我的网站是 www.luoyong. ...
- Django项目:CRM(客户关系管理系统)--69--59PerfectCRM实现king_admin行内编辑
#base_admin.py # ————————24PerfectCRM实现King_admin自定义操作数据———————— from django.shortcuts import render ...
- JS组件系列——BootstrapTable 行内编辑解决方案:x-editable
前言:之前介绍bootstrapTable组件的时候有提到它的行内编辑功能,只不过为了展示功能,将此一笔带过了,罪过罪过!最近项目里面还是打算将行内编辑用起来,于是再次研究了下x-editable组件 ...
- jQuery EasyUI 数据网格 - 启用行内编辑(转自http://www.runoob.com/jeasyui/jeasyui-datagrid-datagrid12.html)
可编辑的功能是最近添加到数据网格(datagrid)的.它可以使用户添加一个新行到数据网格(datagrid).用户也可以更新一个或多个行.本教程向您展示如何创建一个数据网格(datagrid)和内联 ...
- ASP.NET MVC5+EF6+EasyUI 后台管理系统(83)-Easyui Datagrid 行内编辑扩展
这次我们要从复杂的交互入手来说明一些用法,这才能让系统做出更加复杂的业务,上一节讲述了Datagird的批量编辑和提交本节主要演示扩展Datagrid行内编辑的属性,下面来看一个例子,我开启编辑行的时 ...
- bootstrap-editable实现bootstrap-table行内编辑
bootstrap-editable行内编辑效果如下: 需要引入插件 列初始化代码,为可编辑的列添加editable属性: columns = [ { title: '文件名', field: 'Na ...
- 雷林鹏分享:jQuery EasyUI 数据网格 - 启用行内编辑
jQuery EasyUI 数据网格 - 启用行内编辑 可编辑的功能是最近添加到数据网格(datagrid)的.它可以使用户添加一个新行到数据网格(datagrid).用户也可以更新一个或多个行. 本 ...
- BootStrap行内编辑
Bootstrap行内编辑,这里下载了一个X-Editable的插件,在Nuget里面就可以搜到. 引用的js和css大致如下: @*.Jquery组件引用*@ <script src=&quo ...
随机推荐
- java开发常用软件下载
1.idea工具下载:https://www.jetbrains.com/idea/download/#section=windows 2.dbeaver下载:https://dbeaver.io ...
- oracle 索引操作
1 查询表中所有的索引 -- 固定写法"tb_user"(注意大小写)为表名 select * from user_indexes where table_name='tb_use ...
- 处理uniapp激励广告
使用uniapp查看广告 激励视频广告组件.激励视频广告组件是一个原生组件,并且是一个全局单例.层级比上屏 Canvas 高,会覆盖在上屏 Canvas 上.激励视频 广告组件默认是隐藏的,需要调用 ...
- AWT+Swing区别
AWT 是Abstract Window ToolKit (抽象窗口工具包)的缩写,这个工具包提供了一套与本地图形界面进行交互的接口.AWT 中的图形函数与操作系统所提供的图形函数之间有着一一对应的关 ...
- debian11下载软件包及依赖(本地使用)
记录下实践情况,原文: https://blog.csdn.net/zgp210317/article/details/120586189?spm=1001.2101.3001.6650.2& ...
- 【OBS Studio】使用 VLC 视频源播放视频报错:Unhandled exception: c0000005
使用 OBS Studio 和 VLC media player 可以实现视频播放列表的推流,参考『OBS如何添加播放列表?』. 但是使用过程中发现使用 VLC 视频源播放视频时,一个视频播放完切换下 ...
- Docker 使用阿里云加速拉取官方镜像
首先登陆阿里云容器镜像服务控制台,在左侧导航栏选择镜像工具 > 镜像加速器,在镜像加速器页面获取镜像加速地址. 例如: 加速器地址:[系统分配前缀].mirror.aliyuncs.com 配置 ...
- 不安全的权限 0644,建议使用 0600 虚拟机无法分配内存 virtual memory exhausted: Cannot allocate memory
我都不知道我写了啥,自己都很混乱 aoteman@aoteman-virtual-machine:/tmp$ sudo -s #进入root用户模式 [sudo] aoteman 的密码: 12对不起 ...
- 做文件上传功能时,dubbo对MultipartFile文件传输时,一个bug:Fail to decode request due to: RpcInvocation
三月 22, 2019 2:37:27 下午 org.apache.catalina.core.StandardWrapperValve invoke 严重: Servlet.service() fo ...
- js给ul下面所有的li追加类名