feature/核算表格双击编辑

feature/核算表格双击编辑
lys 3 months ago
parent ed8f9a056b
commit 9e3e4e6546

@ -54,6 +54,12 @@
<div class="content unicode" style="display: block;">
<ul class="icon_lists dib-box">
<li class="dib">
<span class="icon iconfont">&#xe6f0;</span>
<div class="name">双击</div>
<div class="code-name">&amp;#xe6f0;</div>
</li>
<li class="dib">
<span class="icon iconfont">&#xe61e;</span>
<div class="name">export</div>
@ -204,9 +210,9 @@
<pre><code class="language-css"
>@font-face {
font-family: 'iconfont';
src: url('iconfont.woff2?t=1741245710478') format('woff2'),
url('iconfont.woff?t=1741245710478') format('woff'),
url('iconfont.ttf?t=1741245710478') format('truetype');
src: url('iconfont.woff2?t=1742897212506') format('woff2'),
url('iconfont.woff?t=1742897212506') format('woff'),
url('iconfont.ttf?t=1742897212506') format('truetype');
}
</code></pre>
<h3 id="-iconfont-">第二步:定义使用 iconfont 的样式</h3>
@ -232,6 +238,15 @@
<div class="content font-class">
<ul class="icon_lists dib-box">
<li class="dib">
<span class="icon iconfont icon-shuangji"></span>
<div class="name">
双击
</div>
<div class="code-name">.icon-shuangji
</div>
</li>
<li class="dib">
<span class="icon iconfont icon-export"></span>
<div class="name">
@ -457,6 +472,14 @@
<div class="content symbol">
<ul class="icon_lists dib-box">
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#icon-shuangji"></use>
</svg>
<div class="name">双击</div>
<div class="code-name">#icon-shuangji</div>
</li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#icon-export"></use>

@ -1,8 +1,8 @@
@font-face {
font-family: "iconfont"; /* Project id 4257468 */
src: url('iconfont.woff2?t=1741245710478') format('woff2'),
url('iconfont.woff?t=1741245710478') format('woff'),
url('iconfont.ttf?t=1741245710478') format('truetype');
src: url('iconfont.woff2?t=1742897212506') format('woff2'),
url('iconfont.woff?t=1742897212506') format('woff'),
url('iconfont.ttf?t=1742897212506') format('truetype');
}
.iconfont {
@ -13,6 +13,10 @@
-moz-osx-font-smoothing: grayscale;
}
.icon-shuangji:before {
content: "\e6f0";
}
.icon-export:before {
content: "\e61e";
}

File diff suppressed because one or more lines are too long

@ -5,6 +5,13 @@
"css_prefix_text": "icon-",
"description": "",
"glyphs": [
{
"icon_id": "39084114",
"name": "双击",
"font_class": "shuangji",
"unicode": "e6f0",
"unicode_decimal": 59120
},
{
"icon_id": "10124054",
"name": "export",

Binary file not shown.

Binary file not shown.

@ -268,16 +268,16 @@
background-color: #f7fbfe;
}
th, td {
font-size: var(--data-size);
.ant-form-item {
margin-bottom: 0;
}
.ant-form-item {
margin-bottom: 0;
.ant-input-number {
width: 100% !important;
}
.ant-input-number {
width: 100%;
}
}
th, td {
font-size: var(--data-size);
}
.ant-typography {

@ -11,8 +11,7 @@ type Props = OwnProps;
const CalcExplainFooter: FunctionComponent<Props> = (props) => {
const { i18n } = props;
return (
<div className={styles["explain-icon-area"]}>
return (<div className={styles["explain-icon-area"]}>
<div className={styles["icon-item"]}>
<Icon type="icon-piliangsuoding" />
<span>{i18n["批量锁定"]}</span>
@ -29,18 +28,19 @@ const CalcExplainFooter: FunctionComponent<Props> = (props) => {
<UnlockOutlined />
<span>{i18n["当前状态未锁定,右击锁定"]}</span>
</div>
{(i18n["反馈信息,右击删除反馈信息"] || i18n["反馈信息,右击设置反馈信息"]) && (
<div className={styles["icon-item"]}>
{(i18n["反馈信息,右击删除反馈信息"] || i18n["反馈信息,右击设置反馈信息"]) && (<div className={styles["icon-item"]}>
<QuestionCircleOutlined />
<span>{i18n["反馈信息,右击删除反馈信息"] || i18n["反馈信息,右击设置反馈信息"]}</span>
</div>
)}
</div>)}
<div className={styles["icon-item"]}>
<Icon type="icon-pilianggengxin" />
<span>{i18n["批量更新"]}</span>
</div>
</div>
);
<div className={styles["icon-item"]}>
<Icon type="icon-shuangji" />
<span>{i18n["双击单元格更新"]}</span>
</div>
</div>);
};
export default CalcExplainFooter;

@ -4,26 +4,39 @@
* Description:
* Date: 2023/9/14
*/
import React, { FunctionComponent, useEffect, useState } from "react";
import type { MenuProps } from "antd";
import { Button, Dropdown, Space, Table, Tooltip, Typography } from "antd";
import React, { FunctionComponent, useContext, useEffect, useMemo, useRef, useState } from "react";
import { Button, Dropdown, Form, Input, InputNumber, MenuProps, Space, Table, Tooltip, Typography } from "antd";
import { DeleteOutlined, LockOutlined, QuestionCircleOutlined, SettingOutlined, UnlockOutlined } from "@ant-design/icons";
import type { FormInstance } from "antd/es/form";
import CustomTableTitle from "@/pages/calcTable/customTableTitle";
import CalcExplainFooter from "@/pages/calcTable/calcExplainFooter";
import CaclFixedTotal from "./calcFixedTotal";
import type { ColumnType } from "antd/lib/table";
import type { PaginationData } from "rc-pagination";
import { exceptStr, paginationFun } from "@/utils/common";
import { exceptStr, paginationFun, toDecimal_n } from "@/utils/common";
import { IPage } from "@/common/types";
import styles from "@/pages/atdTable/components/index.less";
interface OwnProps {}
interface EditableRowProps {}
interface EditableCellProps {
editCell?: any;
children?: React.ReactNode;
record?: any;
pattern?: number;
rowIndex?: number;
dataIndex?: any;
handleSave?: (type: string, record: any) => void;
}
type Props = OwnProps;
type fixedProps = boolean | "top" | "bottom";
const { Text } = Typography;
const index: FunctionComponent<Props> = (props) => {
const EditableContext = React.createContext<FormInstance<any> | null>(null);
const Index: FunctionComponent<Props> = (props) => {
const [columns, setColumns] = useState<ColumnType<any>[]>([]);
const [dataSource, setDataSource] = useState<any[]>([]);
const [selectedRowKeys, setSelectedRowKeys] = useState<React.Key[]>([]);
@ -36,7 +49,10 @@ const index: FunctionComponent<Props> = (props) => {
const [tableScrollHeight, setTableScrollHeight] = useState<Number>(0);
const [payload, setPayload] = useState<string>("");
const [fixed, setFixed] = useState<fixedProps>(true);
const [optWidth, setOptWidth] = useState<number>(120); //操作列宽度
const [sumRow, setSumRow] = useState<Partial<{}>>({}); //总计行数据
const [editCell, setEditCell] = useState<any>({}); //编辑行key
const [form] = Form.useForm();
useEffect(() => {
window.parent.postMessage({ type: "init" }, "*");
@ -74,51 +90,11 @@ const index: FunctionComponent<Props> = (props) => {
setSumRow(sumRow);
setPageInfo(pageInfo);
setDataSource(dataSource);
setOptWidth(optWidth);
setSelectedRowKeys([...selectedRowKeys, ...rowKeys]);
setTableScrollHeight(tableScrollHeight);
setColumns([
...convertColumns(_.map(columns, (o) => ({ ...o, i18n: i18nRes }))),
{
title: i18nRes["操作"],
dataIndex: "operate",
fixed: "right",
width: optWidth || 120,
render: (__, record) => (
<Space>
<Button type="link" onClick={() => handleEdit(record?.id, showSee)}>
{showSee ? i18nRes["查看"] : i18nRes["编辑"]}
</Button>
{optWidth && (
<>
<Button
type="link"
onClick={() =>
handleLockEmp({
lockStatus: "LOCK",
acctEmpIds: [record?.id]
})
}
>
{i18nRes["锁定"]}
</Button>
<Button
type="link"
onClick={() =>
handleLockEmp({
lockStatus: "UNLOCK",
acctEmpIds: [record?.id]
})
}
>
{i18nRes["解锁"]}
</Button>
</>
)}
{record?.lockTime && <Text>{record?.lockTime}</Text>}
</Space>
)
}
]);
setColumns(_.map(columns, (o) => ({ ...o, i18n: i18nRes })));
setEditCell({});
}
};
const convertColumns: any = (cols: any[]) => {
@ -132,11 +108,21 @@ const index: FunctionComponent<Props> = (props) => {
children: convertColumns(_.map(item.children, (o) => ({ ...o, i18n: item.i18n }))),
className: styles["td_odd"],
i18n: item.i18n,
onCell: (record: any) => ({
onCell: (record: any, rowIndex: number) => ({
className: !_.isEmpty(record[`${item.dataIndex}_feedback`]) && styles["feedbackBg"],
editCell,
record,
rowIndex,
pattern: item.pattern,
dataIndex: item.dataIndex,
onContextMenu: (e: any) => {
(!item.calcDetail || item.rightClickType) && e.preventDefault();
}
},
onDoubleClick: (e: any) => {
e.preventDefault();
!item.calcDetail || item.rightClickType ? setEditCell({ dataIndex: item?.dataIndex, rowIndex }) : null;
},
handleSave
}),
render: (text: string, record: any) => {
let items: MenuProps["items"] = !item.calcDetail
@ -215,6 +201,10 @@ const index: FunctionComponent<Props> = (props) => {
}
});
};
const handleSave = (salaryItemId: string, row: any) => {
const params = { salaryItemId, idList: [row?.id], value: row[salaryItemId] };
window.parent.postMessage({ type: "turn", payload: { id: "EDITCELL", params } }, "*");
};
const handleFormulaTd = (dataIndex: string) => {
window.parent.postMessage({ type: "turn", payload: { id: "FORMULA", params: { dataIndex } } }, "*");
};
@ -240,7 +230,7 @@ const index: FunctionComponent<Props> = (props) => {
};
const rowSelection = {
columnWidth: 50,
columnTitle: isDetailTable ? "序号" : "",
columnTitle: isDetailTable ? "序号" : "",
renderCell: (value: boolean, record: any, index: number, originNode: React.ReactNode) => (isDetailTable ? <span>{index + 1}</span> : originNode),
selectedRowKeys,
preserveSelectedRowKeys: true,
@ -255,44 +245,149 @@ const index: FunctionComponent<Props> = (props) => {
);
}
};
return (
<Table
rowKey="id"
size="small"
bordered
className={styles.tableWrapper}
dataSource={dataSource}
rowSelection={rowSelection}
scroll={{
x: 1200,
y: `calc(100vh - ${tableScrollHeight || (!showTotalCell ? 165 : 200)}px)`
}}
columns={!isDetailTable || showSee ? columns : _.filter(columns, (o) => o.dataIndex !== "operate")}
footer={() => (!isDetailTable ? <CalcExplainFooter i18n={i18n} /> : null)}
pagination={
!_.isNil(pageInfo)
? {
...paginationFun(pageInfo, sizeChange, onChange, i18n),
size: "default"
}
: false
}
summary={() =>
!showTotalCell ? (
<></>
) : (
<Table.Summary fixed={fixed}>
<Table.Summary.Row>
<Table.Summary.Cell index={0} align="center">
<Text type="danger">{i18n["总计"]}</Text>
</Table.Summary.Cell>
<CaclFixedTotal columns={columns} dataSourceUrl={sumRowlistUrl} payload={payload} sumRow={sumRow} />
</Table.Summary.Row>
</Table.Summary>
const cols = useMemo(() => {
return [
...convertColumns(columns),
{
title: i18n["操作"],
dataIndex: "operate",
fixed: "right",
width: optWidth || 120,
render: (__: any, record: any) => (
<Space>
<Button type="link" onClick={() => handleEdit(record?.id, showSee)}>
{showSee ? i18n["查看"] : i18n["编辑"]}
</Button>
{optWidth && (
<>
<Button
type="link"
onClick={() =>
handleLockEmp({
lockStatus: "LOCK",
acctEmpIds: [record?.id]
})
}
>
{i18n["锁定"]}
</Button>
<Button
type="link"
onClick={() =>
handleLockEmp({
lockStatus: "UNLOCK",
acctEmpIds: [record?.id]
})
}
>
{i18n["解锁"]}
</Button>
</>
)}
{record?.lockTime && <Text>{record?.lockTime}</Text>}
</Space>
)
}
/>
];
}, [columns, editCell, i18n]);
const components = {
body: {
row: EditableRow,
cell: EditableCell
}
};
return (
<Form form={form} component={false}>
<Table
rowKey="id"
size="small"
bordered
className={styles.tableWrapper}
dataSource={dataSource}
rowSelection={rowSelection}
components={components}
scroll={{
x: 1200,
y: `calc(100vh - ${tableScrollHeight || (!showTotalCell ? 165 : 200)}px)`
}}
columns={!isDetailTable || showSee ? cols : _.filter(cols, (o) => o.dataIndex !== "operate")}
footer={() => (!isDetailTable ? <CalcExplainFooter i18n={i18n} /> : null)}
pagination={
!_.isNil(pageInfo)
? {
...paginationFun(pageInfo, sizeChange, onChange, i18n),
size: "default"
}
: false
}
summary={() =>
!showTotalCell ? (
<></>
) : (
<Table.Summary fixed={fixed}>
<Table.Summary.Row>
<Table.Summary.Cell index={0} align="center">
<Text type="danger">{i18n["总计"]}</Text>
</Table.Summary.Cell>
<CaclFixedTotal columns={cols} dataSourceUrl={sumRowlistUrl} payload={payload} sumRow={sumRow} />
</Table.Summary.Row>
</Table.Summary>
)
}
/>
</Form>
);
};
export default index;
export default Index;
const EditableRow: React.FC<EditableRowProps> = (props) => {
const [form] = Form.useForm();
return (
<Form form={form} component={false}>
<EditableContext.Provider value={form}>
<tr {...props} />
</EditableContext.Provider>
</Form>
);
};
const EditableCell: React.FC<EditableCellProps> = (props) => {
const { children, editCell, record, dataIndex, rowIndex, handleSave, ...restProps } = props;
const form = useContext(EditableContext)!;
const inputRef = useRef<any>(null);
useEffect(() => {
if (dataIndex && editCell && editCell.dataIndex === dataIndex && editCell.rowIndex === rowIndex) {
toggleEdit();
inputRef.current!.focus();
}
}, [editCell, dataIndex, rowIndex]);
const toggleEdit = () => {
form.setFieldsValue({ [dataIndex]: record[dataIndex] });
};
const onSave = _.debounce(async (e) => {
try {
let values = await form.validateFields();
if (record[`${_.keys(values)[0]}_type`] === "number") {
values = {
...values,
[_.keys(values)[0]]: !!_.get(values, _.keys(values)[0]) ? toDecimal_n(_.get(values, _.keys(values)[0]), restProps?.pattern) : ""
};
}
handleSave?.(_.keys(values)[0], { ...record, ...values });
} catch (errInfo) {
console.log("Save failed:", errInfo);
}
}, 200);
let childNode =
dataIndex && editCell && editCell.dataIndex === dataIndex && editCell.rowIndex === rowIndex ? (
<Form.Item style={{ margin: 0 }} name={dataIndex}>
{record[`${dataIndex}_type`] === "number" ? (
<InputNumber ref={inputRef} onPressEnter={onSave} onBlur={onSave} precision={restProps?.pattern} />
) : (
<Input value={record[dataIndex]} onPressEnter={onSave} onBlur={onSave} ref={inputRef} />
)}
</Form.Item>
) : (
<>{children}</>
);
return <td {...restProps}>{childNode}</td>;
};

Loading…
Cancel
Save