diff --git a/src/components/Table/EditableTable.jsx b/src/components/Table/EditableTable.jsx index 510fe21..ff2378d 100644 --- a/src/components/Table/EditableTable.jsx +++ b/src/components/Table/EditableTable.jsx @@ -1,9 +1,10 @@ -import { Form, Table, Button, Popconfirm } from 'antd' +import { memo, useCallback, useState, useEffect } from 'react' +import { Form, Button, Popconfirm } from 'antd' import { EditOutlined, SaveOutlined, PlusOutlined, CloseCircleOutlined, DeleteOutlined } from '@ant-design/icons' -import { useState, useEffect, memo, useCallback } from 'react' import { invokeWebApiWrapperAsync } from '@components/factory' +import { Table } from '.' import { EditableCell } from './EditableCell' const newRowKeyValue = 'newRow' diff --git a/src/components/Table/Table.tsx b/src/components/Table/Table.tsx new file mode 100644 index 0000000..550b3e8 --- /dev/null +++ b/src/components/Table/Table.tsx @@ -0,0 +1,57 @@ +import { memo, useCallback, useEffect, useState, ReactNode } from 'react' +import { Table as RawTable, TableProps } from 'antd' +import { ColumnGroupType, ColumnType } from 'antd/lib/table' + +import { OmitExtends } from '@utils' +import { getTableSettings, setTableSettings } from '@utils/storage' +import { applySettings, ColumnSettings, TableSettings } from '@utils/table_settings' + +import TableSettingsChanger from './TableSettingsChanger' +import { tryAddKeys } from './EditableTable' + +export type BaseTableColumn = ColumnGroupType | ColumnType +export type TableColumns = OmitExtends, ColumnSettings>[] + +export type TableContainer = TableProps & { + columns: TableColumns + dataSource: any[] + children?: ReactNode + tableName?: string + showSettingsChanger?: boolean +} + +export const Table = memo(({ columns, dataSource, children, tableName, showSettingsChanger, ...other }) => { + const [newColumns, setNewColumns] = useState([]) + const [settings, setSettings] = useState({}) + + const onSettingsChanged = useCallback((settings?: TableSettings | null) => { + if (tableName) + setTableSettings(tableName, settings) + setSettings(settings ?? {}) + }, [tableName]) + + useEffect(() => setSettings(tableName ? getTableSettings(tableName) : {}), [tableName]) + useEffect(() => setNewColumns(() => { + const newColumns = applySettings(columns, settings) + if (tableName && showSettingsChanger) { + const oldTitle = newColumns[0].title + newColumns[0].title = (props) => ( +
+ +
+ {typeof oldTitle === 'function' ? oldTitle(props) : oldTitle} +
+
+ ) + } + return newColumns + }), [settings, columns, onSettingsChanged, showSettingsChanger, tableName]) + + return ( + <> + {children} + + ) +}) + +export default Table diff --git a/src/components/Table/TableSettingsChanger.tsx b/src/components/Table/TableSettingsChanger.tsx new file mode 100644 index 0000000..bb28d87 --- /dev/null +++ b/src/components/Table/TableSettingsChanger.tsx @@ -0,0 +1,96 @@ +import { memo, useCallback, useEffect, useState } from 'react' +import { ColumnsType } from 'antd/lib/table' +import { Button, Modal, Switch, Table } from 'antd' +import { SettingOutlined } from '@ant-design/icons' + +import { ColumnSettings, makeSettings, mergeSettings, TableSettings } from '@utils/table_settings' +import { TableColumns } from './Table' +import { makeColumn } from '.' + +const parseSettings = (columns?: TableColumns, settings?: TableSettings | null): ColumnSettings[] => { + const newSettings = mergeSettings(makeSettings(columns ?? []), settings ?? {}) + return Object.values(newSettings).map((set, i) => ({ ...set, key: i })) +} + +const unparseSettings = (columns: ColumnSettings[]): TableSettings => + Object.fromEntries(columns.map((column) => [column.columnName, column])) + +export type TableSettingsChangerProps = { + title?: string + columns?: TableColumns + settings?: TableSettings | null + onChange: (settings: TableSettings | null) => void +} + +export const TableSettingsChanger = memo(({ title, columns, settings, onChange }) => { + const [visible, setVisible] = useState(false) + const [newSettings, setNewSettings] = useState(parseSettings(columns, settings)) + const [tableColumns, setTableColumns] = useState>([]) + + const onVisibilityChange = useCallback((index: number, visible: boolean) => { + setNewSettings((oldSettings) => { + const newSettings = [...oldSettings] + newSettings[index].visible = visible + return newSettings + }) + }, []) + + const toogleAll = useCallback((show: boolean) => { + setNewSettings((oldSettings) => oldSettings.map((column) => { + column.visible = show + return column + })) + }, []) + + useEffect(() => { + setTableColumns([ + makeColumn('Название', 'title'), + makeColumn(null, 'visible', { + title: () => ( + <> + Показать + + + ), + render: (visible: boolean, _?: ColumnSettings, index: number = NaN) => ( + onVisibilityChange(index, visible)} + /> + ) + }), + ]) + }, [toogleAll, onVisibilityChange]) + + useEffect(() => setNewSettings(parseSettings(columns, settings)), [columns, settings]) + + const onModalOk = useCallback(() => { + onChange(unparseSettings(newSettings)) + setVisible(false) + }, [newSettings, onChange]) + + const onModalCancel = useCallback(() => { + setNewSettings(parseSettings(columns, settings)) + setVisible(false) + }, [columns, settings]) + + return ( + <> + + + + +
+ + +
+ + } /> + + + } /> + + +
+ +
+
+
+ Отправить заявку на регистрацию
- -
- Отправить заявку на регистрацию -
- -
+ + +
) }) diff --git a/src/pages/WellOperations/index.jsx b/src/pages/WellOperations/index.jsx index cabc4d5..de57dd1 100644 --- a/src/pages/WellOperations/index.jsx +++ b/src/pages/WellOperations/index.jsx @@ -1,5 +1,5 @@ import { memo, useCallback } from 'react' -import { Layout, Menu, Popover } from 'antd' +import { Layout, Menu } from 'antd' import { Switch, useParams, useHistory, useLocation } from 'react-router-dom' import { BarChartOutlined, diff --git a/src/styles/App.less b/src/styles/App.less index cacb2d8..fb1874b 100644 --- a/src/styles/App.less +++ b/src/styles/App.less @@ -29,14 +29,15 @@ html { margin-left: 30px; } -.login_page{ +.login_page { position: absolute; - height:100%; - width:100%; + height: 100%; + width: 100%; display: flex; justify-content: center; align-items: center; padding: 24px; + background-color: #9d9d9d; } .shadow{ @@ -107,7 +108,7 @@ html { background: #fff; } -.border_small{ +.border_small{ border: 1px solid rgba(0, 0, 0, 0.05); } diff --git a/src/utils/index.ts b/src/utils/index.ts index d98a5e0..03f1ed6 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -9,3 +9,10 @@ export const mainFrameSize = () => ({ }) export const arrayOrDefault = (arr?: unknown, def: T[] = []): T[] => Array.isArray(arr) ? arr : def + +/** + * Объединить типы, исключив совпадающие поля справа + * @param T Тип, передаваемый полностью + * @param R Аддитивный тип + */ + export type OmitExtends = T & Omit diff --git a/src/utils/storage.ts b/src/utils/storage.ts index 6b6cac7..ffaa17e 100644 --- a/src/utils/storage.ts +++ b/src/utils/storage.ts @@ -1,5 +1,6 @@ import { OpenAPI, UserTokenDto } from '@api' import { Role, Permission } from './permissions' +import { normalizeColumn, optimizeColumn, TableSettings, TableSettingsStore } from './table_settings' export enum StorageNames { userId = 'userId', @@ -7,6 +8,7 @@ export enum StorageNames { login = 'login', permissions = 'permissions', roles = 'roles', + tableSettings = 'tableSettings', } export const getArrayFromLocalStorage = (name: string, sep: string | RegExp = ','): T[] | null => { @@ -39,3 +41,33 @@ export const removeUser = () => { localStorage.removeItem(StorageNames.permissions) localStorage.removeItem(StorageNames.roles) } + +export const getTableSettings = (tableName: string): TableSettings => { + const tablesJSON = localStorage.getItem(StorageNames.tableSettings) + if (!tablesJSON) return {} + try { + const tables: TableSettingsStore = JSON.parse(tablesJSON) + if (tableName in tables) { + const columns = tables[tableName] ?? {} + for (const [name, column] of Object.entries(columns)) + columns[name] = normalizeColumn(column, name) + return columns + } + } catch {} + return {} +} + +export const setTableSettings = (tableName: string, settings?: TableSettings | null): boolean => { + const currentJSON = localStorage.getItem(StorageNames.tableSettings) + try { + const currentStore: TableSettingsStore = currentJSON ? JSON.parse(currentJSON) : {} + const newSettings = settings ?? null + if (newSettings) + for (const [name, column] of Object.entries(newSettings)) + newSettings[name] = optimizeColumn(column) + currentStore[tableName] = newSettings + localStorage.setItem(StorageNames.tableSettings, JSON.stringify(currentStore)) + return true + } catch {} + return false +} diff --git a/src/utils/table_settings.ts b/src/utils/table_settings.ts new file mode 100644 index 0000000..32d9755 --- /dev/null +++ b/src/utils/table_settings.ts @@ -0,0 +1,57 @@ +import { TableColumns } from '@components/Table' + +export type ColumnSettings = { + columnName?: string + title?: string + visible?: boolean +} + +export type TableSettings = Record +export type TableSettingsStore = Record + +export const makeSettings = (columns: TableColumns): TableSettings => { + const settings: TableSettings = {} + columns.forEach((column) => { + if (!column.key) return + const key = String(column.key) + settings[key] = { + columnName: key, + title: typeof column.title === 'string' ? column.title : key, + visible: column.visible ?? true, + } + }) + return settings +} + +export const mergeSettings = (...settings: TableSettings[]): TableSettings => { + const newSettings: TableSettings = {} + for (const setting of settings) { + for (const [name, column] of Object.entries(setting)) { + newSettings[name] = { + ...newSettings[name], + ...column, + } + } + } + return newSettings +} + +export const normalizeColumn = (column: ColumnSettings, name?: string): ColumnSettings => ({ + ...column, + columnName: column.columnName ?? name, + visible: column.visible ?? true, +}) + +export const optimizeColumn = (column: ColumnSettings): ColumnSettings => ({ + ...column, + visible: column.visible ?? true, +}) + +export const applySettings = (columns: TableColumns, settings: TableSettings): TableColumns => { + let newColumns: TableColumns = columns.map((column) => ({ ...column })) + newColumns = newColumns.filter((column) => { + const name = String(column.key) + return !(name in settings) || settings[name]?.visible + }) + return newColumns +}