diff --git a/web/src/components/RealtimeTable/Cell/Cell/Cell.jsx b/web/src/components/RealtimeTable/Cell/Cell/Cell.jsx
index 472c89c..7f93b6c 100644
--- a/web/src/components/RealtimeTable/Cell/Cell/Cell.jsx
+++ b/web/src/components/RealtimeTable/Cell/Cell/Cell.jsx
@@ -134,24 +134,34 @@ const CellComponent = ({
isUpdating,
onCellNumberClick,
isInvalid,
+ vspOptions,
}) => {
const { lockedCells } = useRealtime();
const cellKey = `${row.id}_${column.id}`;
const isLocked = lockedCells.includes(cellKey);
+ const isVspDropdown = column?.columnDef?.editType === 'vsp_dropdown';
+
// Мемоизация значения
const { rawValue, displayValue, isNumeric } = useMemo(() => {
const value = cell.getValue();
const isNum = !isNaN(Number(value)) && value !== null && value !== undefined && value !== '';
let display = String(value || '');
+ if (isVspDropdown && value != null) {
+ const vsp = vspOptions?.find(v => v.id === Number(value));
+ if (vsp) {
+ return { rawValue: value, displayValue: vsp.registration_number, isNumeric: false };
+ }
+ }
+
if (isNum) {
const asInteger = INTEGER_COLUMN_IDS.has(column.id);
display = formatNumber(value, asInteger);
}
return { rawValue: value, displayValue: display, isNumeric: isNum };
- }, [cell, column.id]);
+ }, [cell, column.id, isVspDropdown, vspOptions]);
const highlightedContent = useMemo(() => {
const searchQueries = [globalFilter, columnFilter];
@@ -207,7 +217,8 @@ const Cell = React.memo(CellComponent, (prevProps, nextProps) => {
prevProps.isInvalid === nextProps.isInvalid &&
prevProps.isEditable === nextProps.isEditable &&
prevProps.isUpdating === nextProps.isUpdating &&
- prevProps.onCellNumberClick === nextProps.onCellNumberClick
+ prevProps.onCellNumberClick === nextProps.onCellNumberClick &&
+ prevProps.vspOptions === nextProps.vspOptions
);
});
diff --git a/web/src/components/RealtimeTable/Cell/EditCell/VspDropdownEditCell.jsx b/web/src/components/RealtimeTable/Cell/EditCell/VspDropdownEditCell.jsx
new file mode 100644
index 0000000..9c9e914
--- /dev/null
+++ b/web/src/components/RealtimeTable/Cell/EditCell/VspDropdownEditCell.jsx
@@ -0,0 +1,113 @@
+import React, { memo, useEffect, useRef, useLayoutEffect, useState, useCallback, useMemo } from 'react';
+import { FormControl, Select, MenuItem } from '@mui/material';
+import { useRealtime } from '../../contexts/RealtimeContext';
+
+const EDITOR_STYLES = {
+ position: 'absolute',
+ zIndex: 12,
+ top: 0,
+};
+
+const VspDropdownEditCell = memo(({ refCell, onChange, disabled, value, table }) => {
+ const { endEditing: contextEndEditing, vspOptions } = useRealtime();
+
+ const [selectedValue, setSelectedValue] = useState(() => value ?? '');
+ const [position, setPosition] = useState();
+ const [open, setOpen] = useState(false);
+
+ const refFinished = useRef(false);
+
+ useEffect(() => {
+ const timer = setTimeout(() => {
+ setOpen(true);
+ }, 80);
+ return () => clearTimeout(timer);
+ }, []);
+
+ useLayoutEffect(() => {
+ if (!refCell?.current) return;
+
+ const td = refCell.current.offsetParent;
+ const tr = td?.offsetParent;
+
+ if (td && tr) {
+ setPosition({
+ left: td.offsetLeft,
+ width: td.offsetWidth,
+ translateY: tr.style.transform || '',
+ });
+ }
+ }, [refCell]);
+
+ const finishEditing = useCallback(() => {
+ if (refFinished.current) return;
+ refFinished.current = true;
+ table.setEditingCell(null);
+ const editingCell = table.getState().editingCell;
+ if (editingCell) {
+ contextEndEditing?.(editingCell.row, editingCell.column);
+ }
+ }, [table, contextEndEditing]);
+
+ const handleSelectChange = useCallback((e) => {
+ const val = e.target.value;
+ if (val == null || val === '') return;
+ refFinished.current = true;
+ setSelectedValue(val);
+ onChange?.(val);
+ const editingCell = table.getState().editingCell;
+ if (editingCell) {
+ contextEndEditing?.(editingCell.row, editingCell.column);
+ }
+ }, [onChange, table, contextEndEditing]);
+
+ const handleSelectClose = useCallback((_event, reason) => {
+ if (reason === 'selectOption') return;
+ finishEditing();
+ }, [finishEditing]);
+
+ const editorStyles = useMemo(() => ({
+ ...EDITOR_STYLES,
+ left: position?.left || 0,
+ width: position?.width || 0,
+ transform: position?.translateY || 0,
+ }), [position]);
+
+ return (
+
+ |
+ {position &&
+
+
+
+
+
+ }
+ |
+
+ );
+});
+
+VspDropdownEditCell.displayName = 'VspDropdownEditCell';
+
+export default VspDropdownEditCell;
diff --git a/web/src/components/RealtimeTable/RealtimeTable.jsx b/web/src/components/RealtimeTable/RealtimeTable.jsx
index 2711f3f..27213eb 100644
--- a/web/src/components/RealtimeTable/RealtimeTable.jsx
+++ b/web/src/components/RealtimeTable/RealtimeTable.jsx
@@ -32,6 +32,7 @@ import { getRowId } from './utils/rowUtils';
import { FORM_TYPE_OPTIONS } from '../../constants/constants';
import { debounce } from '@mui/material';
import { CreateProgramModal } from './Modals/CreateProgramProjectModal';
+import { DictVspApi } from '../../api/dict-vsp';
const RealtimeTable = ({ formType, formId, sheetName, direction, year }) => {
const { data, setData, isLoading: isTableLoading, editingCells: dataEditingCells } = useRealtimeData(formId, sheetName, direction, formType, year);
@@ -87,6 +88,7 @@ const RealtimeTable = ({ formType, formId, sheetName, direction, year }) => {
startEditing: contextStartEditing,
addProgram: contextAddProgram,
addProject: contextAddProject,
+ setVspOptions,
} = useRealtime();
const {
@@ -136,6 +138,26 @@ const RealtimeTable = ({ formType, formId, sheetName, direction, year }) => {
loadConfig();
}, [formType, sheetName]);
+ useEffect(() => {
+ if (!formId || !columnsConfig?.columns) return;
+
+ const hasVspDropdown = (cols) => {
+ for (const col of cols) {
+ if (col.editType === 'vsp_dropdown') return true;
+ if (col.columns && hasVspDropdown(col.columns)) return true;
+ }
+ return false;
+ };
+
+ if (!hasVspDropdown(columnsConfig.columns)) return;
+
+ DictVspApi.getDropdownVsp({ form_id: formId }).then(data => {
+ if (data.success) {
+ setVspOptions(data.result);
+ }
+ });
+ }, [formId, columnsConfig, setVspOptions]);
+
const handleUpdateCell = useCallback(async (row, column, value) => {
return await contextUpdateCell(row, column, value);
}, [contextUpdateCell]);
diff --git a/web/src/components/RealtimeTable/constants/FORM_2/AHR.js b/web/src/components/RealtimeTable/constants/FORM_2/AHR.js
index a6cf4b5..57e5be7 100644
--- a/web/src/components/RealtimeTable/constants/FORM_2/AHR.js
+++ b/web/src/components/RealtimeTable/constants/FORM_2/AHR.js
@@ -29,6 +29,10 @@ export const config = {
color_type: greenColumn,
accessorKey: "data.header.vsp_id",
},
+ "data.header.vsp_address": {
+ color_type: greenColumn,
+ accessorKey: "data.header.vsp_address",
+ },
"data.plan.q1": {
color_type: greenColumn,
accessorKey: "data.plan.q1",
@@ -725,6 +729,15 @@ export const config = {
columnLetter: "E",
size: 150,
filterFn: "contains",
+ editType: "vsp_dropdown",
+ },
+ {
+ header: "Адрес ВСП",
+ accessorKey: "data.header.vsp_address",
+ columnLetter: "E1",
+ size: 220,
+ filterFn: "contains",
+ editType: "vsp_dropdown",
},
],
muiTableHeadCellProps: {
diff --git a/web/src/components/RealtimeTable/constants/FORM_2/CAP.js b/web/src/components/RealtimeTable/constants/FORM_2/CAP.js
index 0eaffdc..cadf2cd 100644
--- a/web/src/components/RealtimeTable/constants/FORM_2/CAP.js
+++ b/web/src/components/RealtimeTable/constants/FORM_2/CAP.js
@@ -25,6 +25,14 @@ export const config = {
color_type: orangeColumn,
accessorKey: "data.header.name",
},
+ "data.header.vsp_id": {
+ color_type: orangeColumn,
+ accessorKey: "data.header.vsp_id",
+ },
+ "data.header.vsp_address": {
+ color_type: orangeColumn,
+ accessorKey: "data.header.vsp_address",
+ },
"data.header.internal_order": {
color_type: orangeColumn,
accessorKey: "data.header.internal_order",
@@ -830,6 +838,33 @@ export const config = {
},
],
},
+ {
+ header: "ВСП РФ",
+ accessorKey: "field_vsp_rf",
+ columns: [
+ {
+ header: "",
+ accessorKey: "data.header.vsp_id",
+ columnLetter: "G",
+ size: 150,
+ filterFn: "contains",
+ editType: "vsp_dropdown",
+ },
+ ],
+ },
+ {
+ header: "Адрес ВСП",
+ accessorKey: "field_adres_vsp",
+ columns: [
+ {
+ header: "",
+ accessorKey: "data.header.vsp_address",
+ columnLetter: "G1",
+ size: 220,
+ filterFn: "contains",
+ },
+ ],
+ },
{
header: "Внутренний заказ",
accessorKey: "field_vnutrenniy_zakaz",
@@ -837,7 +872,7 @@ export const config = {
{
header: "",
accessorKey: "data.header.internal_order",
- columnLetter: "G",
+ columnLetter: "H",
size: 150,
filterFn: "contains",
},
diff --git a/web/src/components/RealtimeTable/constants/FORM_2/OPER.js b/web/src/components/RealtimeTable/constants/FORM_2/OPER.js
index 894bb85..bc7a9d4 100644
--- a/web/src/components/RealtimeTable/constants/FORM_2/OPER.js
+++ b/web/src/components/RealtimeTable/constants/FORM_2/OPER.js
@@ -25,6 +25,14 @@ export const config = {
color_type: blueColumn,
accessorKey: "data.header.name",
},
+ "data.header.vsp_id": {
+ color_type: blueColumn,
+ accessorKey: "data.header.vsp_id",
+ },
+ "data.header.vsp_address": {
+ color_type: blueColumn,
+ accessorKey: "data.header.vsp_address",
+ },
"data.header.internal_order": {
color_type: blueColumn,
accessorKey: "data.header.internal_order",
@@ -830,6 +838,33 @@ export const config = {
},
],
},
+ {
+ header: "ВСП РФ",
+ accessorKey: "field_vsp_rf",
+ columns: [
+ {
+ header: "",
+ accessorKey: "data.header.vsp_id",
+ columnLetter: "G",
+ size: 150,
+ filterFn: "contains",
+ editType: "vsp_dropdown",
+ },
+ ],
+ },
+ {
+ header: "Адрес ВСП",
+ accessorKey: "field_adres_vsp",
+ columns: [
+ {
+ header: "",
+ accessorKey: "data.header.vsp_address",
+ columnLetter: "G1",
+ size: 220,
+ filterFn: "contains",
+ },
+ ],
+ },
{
header: "Внутренний заказ",
accessorKey: "field_vnutrenniy_zakaz",
@@ -837,7 +872,7 @@ export const config = {
{
header: "",
accessorKey: "data.header.internal_order",
- columnLetter: "G",
+ columnLetter: "H",
size: 150,
filterFn: "contains",
},
diff --git a/web/src/components/RealtimeTable/contexts/RealtimeContext.js b/web/src/components/RealtimeTable/contexts/RealtimeContext.js
index 4028366..ea0312c 100644
--- a/web/src/components/RealtimeTable/contexts/RealtimeContext.js
+++ b/web/src/components/RealtimeTable/contexts/RealtimeContext.js
@@ -43,6 +43,7 @@ export const RealtimeProvider = ({
//для ячеек которые редактируются другими пользователями
const [lockedCells, setLockedCells] = useState([]);
+ const [vspOptions, setVspOptions] = useState([]);
const handleMessage = useCallback((data) => {
// Обработка ошибок
if (data.error) {
@@ -445,6 +446,8 @@ export const RealtimeProvider = ({
retryLogin,
lockedCells,
setLockedCells,
+ vspOptions,
+ setVspOptions,
releaseAllLocks: () => releaseAllLocks(userId),
}}
>
diff --git a/web/src/components/RealtimeTable/tableColumns.jsx b/web/src/components/RealtimeTable/tableColumns.jsx
index 43fb911..68d854f 100644
--- a/web/src/components/RealtimeTable/tableColumns.jsx
+++ b/web/src/components/RealtimeTable/tableColumns.jsx
@@ -2,6 +2,7 @@ import React, { useEffect, useRef, useState, useMemo, useCallback } from 'react'
import { createPortal } from 'react-dom';
import { useParams } from 'react-router';
import { useRealtime } from './contexts/RealtimeContext';
+import VspDropdownEditCell from './Cell/EditCell/VspDropdownEditCell';
const EditCellPortal = React.memo(({
cell,
@@ -60,13 +61,23 @@ const EditCellPortal = React.memo(({
cellId: `${cell.row.id}_${cell.column.id}`,
}), [cell, isEditable, handleChange, tableKey, table]);
+ const editType = cell.column.columnDef?.editType;
+
const portalContent = useMemo(() => {
if (!refTbody || isSaving) return null;
+
+ if (editType === 'vsp_dropdown') {
+ return createPortal(
+ ,
+ refTbody
+ );
+ }
+
return createPortal(
,
refTbody
);
- }, [refTbody, isSaving, editCellProps, EditCell]);
+ }, [refTbody, isSaving, editCellProps, EditCell, editType]);
return (
<>
@@ -152,18 +163,19 @@ export const getTableColumns = ({
return props;
};
- // Оптимизированная функция processColumns
const processColumns = (columns) => {
columns.forEach((col) => {
col.Cell = ({ cell, table, column, row }) => {
+ const { vspOptions } = useRealtime();
const props = getCachedCellProps(row, column, table);
const cellProps = useMemo(() => ({
row,
column,
cell,
+ vspOptions,
...props,
- }), [row.id, column.id, cell.getValue(), props._hash]);
+ }), [row.id, column.id, cell.getValue(), props._hash, vspOptions]);
return | ;
};
@@ -184,6 +196,10 @@ export const getTableColumns = ({
return editComponent;
};
+ if (col.editType === 'vsp_dropdown') {
+ col.enableEditing = (row) => row?.original?.row_type === 'INPUT';
+ }
+
if (col.columns?.length) {
processColumns(col.columns);
}