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 ...
随机推荐
- vue3 门户网站搭建8-字体
浏览器默认的可选字体比较少,如果没有合适的则需要额外下载并引入. 一般使用 开源字体即可,商用需要花钱~ 将下载好的 ttf 格式字体放入项目下文件夹: 样式文件中增加配置:(main.css) 即可 ...
- CNN模型踩坑记录
刚刚在跑textCNN的模型,加载了字典后,在同样的输入下模型的输出一直在变化,先发现损失函数一直在变化,不停debug之后发现是模型的输出一直在变化,在模型输入一直不变下模型输出不同,最后发现是模型 ...
- Python 使用mysql.connector、pymysql和 MYSQLdb(MysqlClient)操作MySQL数据库
MySQL是一个关系型数据库管理系统,由瑞典MySQL AB 公司开发,属于 Oracle 旗下产品.MySQL 是最流行的关系型数据库管理系统之一.本文主要介绍安装mysql.connector,. ...
- 查找大文件-清理linux磁盘
https://www.cnblogs.com/kerrycode/p/4391859.html find . -type f -size +800M -print0 | xargs -0 du - ...
- python读取xml格式数据
读取节点文本值和,属性值 # -*- coding: UTF-8 -*- from xml.dom import minidom dom=minidom.parse('F:\\python_proje ...
- Burpsuite入门之target模块攻防中利用
可以用来收集目标站点的更多资产 可以探测一些自动加载的接口.内容等,有的内容并不能被访问者直接看见,通过抓包的方式就可以一目了然. 1栏中是流量信息,其中包含着你所请求的流量 2栏中是对1栏中内容的一 ...
- java script--一些冷知识
java script--一些冷知识 目录 java script--一些冷知识 布兰登·艾奇,JavaScript 创始人 java script logo json logo json node. ...
- 蓝桥杯训练赛二-1141 问题 C: C语言训练-百钱百鸡问题
题目描述 中国古代数学家张丘建在他的<算经>中提出了著名的"百钱买百鸡问题":鸡翁一,值钱五,鸡母一,值钱三,鸡雏三,值钱一,百钱买百鸡,问翁.母.雏各几何? 输入 无 ...
- 线性斜压模式LBM学习&安装实录
本文基本参照了LBM的用户手册进行. 环境:Ubuntu 18.04LTS (Windows Subsystem Linux) 编译器:gfortran 7.5.0 安装包: lapack-3.9.0 ...
- mvc和ef如何连接
1.一般加上ef步骤:引入ef包,新建自己的context并继承自DbContext,构造函数里写上链接字符串,属性都是表集合.如何使用:程序中UserContext un = new UserCon ...