import React, { useState, useEffect, useCallback, useMemo } from "react";
import { JsonForms } from "@jsonforms/react";
import { materialCells, materialRenderers } from '@jsonforms/material-renderers';
import EnumSelectRenderer, { EnumSelectRendererTester } from '../../renderer/EnumSelectRenderer';
import BaseRenderer, { BaseRendererTester } from '../../renderer/BaseRenderer';
import {
vanillaCells,
vanillaRenderers,
vanillaStyles,
JsonFormsStyleContext,
} from "@jsonforms/vanilla-renderers";
import { useTranslation } from "react-i18next";
import formSchema from "./form-schema.json";
import formUischema from "./form-ui-schema.json";
import liststyle from "../../../style/design-system-form.json";
import '../style/global.css';
import '../style/S-ECSCM.css';
import "tabulator-tables/dist/css/tabulator.min.css";
import { ReactTabulator, ReactTabulatorOptions } from 'react-tabulator';
import { useUnifiedForm } from '../../../yupCommon/useUnifiedForm';
import { useYupI18n } from '../../../yupCommon/useYupI18n';
import { createValidationSchema } from './validationSchemas';
import initdata from './table-data.json';
const tableData = initdata["table"];
const styleContextValue = { styles: [...vanillaStyles, ...liststyle] };
import { rankWith, scopeEndsWith, and, or, schemaTypeIs } from '@jsonforms/core';
import useProtectedNavigate from "../../../services/useProtectedNavigate";
import * as Yup from "yup";
import $ from 'jquery';
import { useHttpClient } from '../../httpClient';
import { useUserData } from '../../../UserContext';
const sampleData = {
"number": "111",
"parts": "222",
"partsid": "",
"products": "",
"productsid": "",
"shop": "",
"shopid": "",
"status-category": "",
"survey-category": ["test1", "test2"],
"request-category": "",
"request-dept": "",
"request-person": "",
"requestdate_min": "",
"requestdate_max": "",
"expected_response_min": "",
"expected_response_max": "",
"response_date_min": "",
"response_date_max": ""
};
const SECSCMTEST = () => {
const { t: translation, i18n: i18nInstance } = useTranslation();
const locale = i18nInstance.language;
const protectedNavigate = useProtectedNavigate();
const [statusSelected, setStatusSelected] = useState(false);
const tableRef = React.useRef<{table?: Tabulator;}>(null);
const [tableData, setTableData] = useState([]);
const [totalRecords, setTotalRecords] = useState(0);
const [currentPage, setCurrentPage] = useState(1);
const [totalPages, setTotalPages] = useState(0);
// 使用多语言 Yup 配置
const { createLabeledSchema, currentLang } = useYupI18n();
// 创建多语言验证 schema
const validationSchema = useMemo(() => {
const baseSchema = createValidationSchema(currentLang);
return createLabeledSchema(baseSchema);
}, [createLabeledSchema, currentLang]);
// 使用改进后的统一表单管理(现在支持输入法兼容和多语言)
const {
formData,
validationErrors,
isValid,
isSubmitting,
updateFormData,
updateField,
registerField,
getFieldValue,
getFieldError,
resetForm,
submitForm
} = useUnifiedForm({
schema: validationSchema,
initialData: sampleData,
debounceMs: 500, // 设置防抖延迟为500ms
onSubmit: (data) => {
console.log("表单提交数据:", data);
alert("表单校验通过,数据提交成功!");
}
});
// 添加画面变更时触发的方法,将错误消息放到 validation input-description 中
const updateValidationMessages = useCallback(() => {
const validationElements = document.getElementsByClassName('validation');
Array.from(validationElements).forEach(element => {
element.innerHTML = '';
});
Object.entries(validationErrors).forEach(([field, error]) => {
// 修正:使用正确的模板字符串语法和原生 DOM API
const controlDiv = document.querySelector(`div[id*="${field}"]`);
if (controlDiv) {
// 在控制容器中查找验证消息容器
const validationDiv = controlDiv.querySelector('.validation');
if (validationDiv) {
// 更新错误消息内容和样式
validationDiv.textContent = error || '';
const htmlElement = validationDiv as HTMLElement;
if (error) {
htmlElement.style.color = '#ff4444';
htmlElement.style.fontSize = '12px';
htmlElement.style.marginTop = '4px';
htmlElement.style.fontWeight = '400';
htmlElement.style.display = 'block';
} else {
htmlElement.style.color = '';
htmlElement.style.fontSize = '';
htmlElement.style.marginTop = '';
htmlElement.style.fontWeight = '';
htmlElement.style.display = 'none';
}
}
}
});
}, [getFieldError]);
const httpClient = useHttpClient();
const getTableData = (page) => {
var obj: any = {};
const numbers = $("select[id^='#/properties/number']");
if(numbers.length != 0){
obj['number'] = $("select[id^='#/properties/number']")[0].value;
}else{
obj['number'] = null;
}
obj['page'] = page;
obj['size'] = 10;
httpClient.post('/SECSCMTEST/getTableData', obj)
.then(response => {
console.log('success:', response.data);
setTableData(response.data.table || []);
setTotalRecords(response.data.total || 0); // 保存总记录数
setCurrentPage(page);
// 在获取数据后更新最大页数
if (tableRef.current) {
const maxPage = Math.ceil((response.data.total || 0) / 10);
try {
// 尝试不同的方法设置最大页数
if (tableRef.current.setMaxPage) {
tableRef.current.setMaxPage(maxPage);
} else if (tableRef.current.modules && tableRef.current.modules.page) {
tableRef.current.modules.page.setMaxPage(maxPage);
}
console.log('设置最大页数成功:', maxPage);
} catch (error) {
console.error('设置最大页数失败:', error);
}
}
})
.catch(error => {
console.error('Save failed:', error);
});
};
const { userData } = useUserData();
useEffect(() => {
if(userData && userData.role){
getTableData(1);
}
}, []);
// 监听 validationErrors 变化,自动更新错误消息显示
useEffect(() => {
// 使用 setTimeout 确保 DOM 已经渲染完成
const timer = setTimeout(() => {
updateValidationMessages();
}, 100);
return () => clearTimeout(timer);
}, [validationErrors, updateValidationMessages]);
useEffect(() => {
if (tableRef.current && tableRef.current.table) {
const tabulator = tableRef.current.table;
setTimeout(() => {
try {
tabulator.setColumns(statusSelected ? Columns1 : Columns2);
} catch (error) {
console.error("Failed to refresh table:", error);
}
}, 100);
}
}, [statusSelected]);
const cellClickFunc = (_e: any, cell: any) => {
const _rowdata = cell.getRow().getData();
const _investigation_no = _rowdata.investigation_no;
const _investigation_status = _rowdata.investigation_status;
const _representative_supplier_name = _rowdata.representative_supplier_name;
if (_investigation_no && _investigation_status && _representative_supplier_name) {
protectedNavigate(`/SECSCM005?no=${_investigation_no}&status=${_investigation_status}&date=${_representative_supplier_name}`)
} else {
console.error("Row data is missing required fields for navigation:", _rowdata);
}
}
const baseColumnConfig = {
headerHozAlign: 'center' as const,
hozAlign: 'left' as const,
vertAlign: 'center' as const,
cellClick: (_e: any, cell: any) => { cellClickFunc(_e, cell) }
};
const Columns1 = [
{
title: 'ID',
formatter: "rownum",
hozAlign: "center" as const,
headerHozAlign: "center" as const,
width: 40,
headerSort: false
},
{
title: "",
formatter: "rowSelection",
titleFormatter: "rowSelection",
hozAlign: "center" as const,
headerHozAlign: "center" as const,
headerSort: false,
width: 40
},
{ ...baseColumnConfig, title: '調査依頼ID', field: 'investigation_no'},
{ ...baseColumnConfig, title: '調査対象', field: 'representative_part_name'},
{ ...baseColumnConfig, title: '依頼区分', field: 'representative_part_id'},
{ ...baseColumnConfig, title: '依頼部門', field: 'purchased_item_name'},
{ ...baseColumnConfig, title: '回答希望日', field: 'representative_supplier_name'},
{
title: "个人信息",
headerHozAlign: 'center' as const,
columns: [
{ ...baseColumnConfig, title: '最短回答予定日', field: 'representative_supplier_id'},
{ ...baseColumnConfig, title: '最長回答予定日', field: 'investigation_status'},
]
},
{ ...baseColumnConfig, title: '依頼担当者', field: 'purchased_item_id'},
];
const Columns2 = [
{
title: "",
formatter: "rowSelection",
titleFormatter: "rowSelection",
hozAlign: "center" as const,
headerHozAlign: "center" as const,
headerSort: false,
width: 40
},
{ ...baseColumnConfig, title: '調査依頼ID', field: 'investigation_no'},
{ ...baseColumnConfig, title: '調査対象', field: 'representative_part_name'},
{ ...baseColumnConfig, title: '依頼区分', field: 'representative_part_id'},
{ ...baseColumnConfig, title: '依頼部門', field: 'purchased_item_name'},
{ ...baseColumnConfig, title: '回答希望日', field: 'representative_supplier_name'},
{ ...baseColumnConfig, title: '最短回答予定日', field: 'representative_supplier_id'},
{ ...baseColumnConfig, title: '最長回答予定日', field: 'investigation_status'},
{ ...baseColumnConfig, title: '依頼担当者', field: 'purchased_item_id'},
];
const handleFormChange = useCallback((data: any) => {
updateFormData(data);
setStatusSelected(data["status-category"] && data["status-category"] !== "");
// 表单数据变化时也更新验证消息
setTimeout(() => {
updateValidationMessages();
}, 100);
}, [updateFormData, updateValidationMessages]);
const handleClearForm = () => {
resetForm();
};
const ErrorMessage = ({ fieldName }: { fieldName: keyof typeof sampleData }) => {
const error = getFieldError(fieldName);
return error ? <div style={{ color: "red", fontSize: "12px", marginTop: "2px" }}>{error}</div> : null;
};
// 远程数据加载函数
const fetchRemoteData = (url, config, params) => {
return new Promise((resolve, reject) => {
// 模拟 API 参数(实际项目替换为真实 API)
const queryParams = new URLSearchParams({
page: params.page,
size: params.size,
sort: params.sorters.map(s => `${s.field}:${s.dir}`).join(',')
});
// 实际项目使用 fetch/axios 替换此部分
setTimeout(() => {
const mockData = {
last_page: 5,
data: Array.from({ length: params.size }, (_, i) => ({
representative_part_id: i + (params.page - 1) * params.size,
investigation_no: `用户 ${i + 1}`,
representative_part_name: `user${i + 1}@example.com`,
representative_supplier_name: `职位 ${Math.floor(Math.random() * 10)}`
}))
};
resolve(mockData);
}, 500);
});
};
const options: ReactTabulatorOptions = {
renderHorizontal: "virtual",
layout: "fitDataTable",
selectable: true,
pagination: "remote",
paginationSize: 10,
// 远程分页控制属性
page: 1, // 当前页码
paginationSizeSelector: [10, 25, 50, 100], // 可选的每页条数
// 分页回调函数
//pageLoaded: (pageno: number) => {
// if (pageno !== currentPage) {
// getTableData(pageno);
// }
//},
ajaxRequestFunc: fetchRemoteData,
ajaxResponse: function(url, params, response) {
return {
data: response.data,
last_page: response.last_page
};
}
};
const renderers = [
...vanillaRenderers,
{ tester: BaseRendererTester, renderer: BaseRenderer }
//{ tester: EnumSelectRendererTester, renderer: EnumSelectRenderer }
];
// 修改测试函数,尝试多种方法
const checkTableRef = () => {
console.log('手动检查 tableRef:');
console.log('tableRef.current:', tableRef.current);
if (tableRef.current) {
console.log('找到 Tabulator 实例');
console.log('可用方法:', Object.getOwnPropertyNames(tableRef.current.current));
console.log('modules:', tableRef.current.modules);
const maxPage = Math.ceil(totalRecords / 10);
console.log('计算的最大页数:', maxPage);
// 尝试多种设置方法
try {
if (tableRef.current.current.setMaxPage) {
tableRef.current.current.setMaxPage(maxPage);
console.log('方法1成功: setMaxPage');
} else if (tableRef.current.modules && tableRef.current.modules.page) {
if (tableRef.current.modules.page.setMaxPage) {
tableRef.current.modules.page.setMaxPage(maxPage);
console.log('方法2成功: modules.page.setMaxPage');
}
} else {
// 尝试重新设置选项
const newOptions = { ...options, maxPage };
console.log('尝试重新设置选项:', newOptions);
}
} catch (error) {
console.error('所有方法都失败:', error);
}
}
};
return (
<div id="content">
<br />
<details className="details">
<summary className="details-summary">{translation('common.button.show-select-menu.label','no label')}</summary>
<form onSubmit={submitForm}>
{/* JsonForms 部分 */}
<div style={{ marginBottom: "20px" }}>
<JsonFormsStyleContext.Provider value={styleContextValue}>
<JsonForms
i18n={{locale: locale, translate: (key: string, defaultMessage?: string) => translation(key, defaultMessage || '')}}
schema={formSchema}
uischema={formUischema}
data={formData}
renderers={renderers}
cells={vanillaCells}
onChange={({ data }) => handleFormChange(data)}
validationMode="NoValidation"
/>
</JsonFormsStyleContext.Provider>
</div>
{/* 原生HTML表单部分 - 现在支持输入法兼容 */}
<div style={{ border: "1px solid #fff"}}>
<div className="control" id="#/properties/tel">
<label htmlFor="tel-input" className="label">电话:</label>
<input
className="validate valid input text-field"
id="tel-input"
name="tel"
type="text"
ref={(ref) => registerField('tel', ref)}
defaultValue={getFieldValue('tel')}
/>
<ErrorMessage fieldName="tel" />
</div>
</div>
<div id="button-right">
<input
type="button"
className="button"
style={{ marginRight: "20px" }}
value={translation('common.button.clear.label','no label')}
onClick={handleClearForm}
/>
<input
type="submit"
className="button"
value={translation('common.button.select.label','no label')}
disabled={!isValid || isSubmitting}
style={{ opacity: (isValid && !isSubmitting) ? 1 : 0.5 }}
/>
{/* 添加测试按钮 */}
<input
type="button"
className="button"
style={{ marginLeft: "20px", backgroundColor: "#ff6600" }}
value="测试tableRef"
onClick={checkTableRef}
/>
</div>
</form>
</details>
<br />
<div>
<ReactTabulator
onRef={(instance) => { tableRef.current = instance; }}
columns={Columns2 as any}
data={[]}
options={options}
/>
</div>
<div style={{ marginTop: "10px" }}>
<input className="button" type="button" style={{ marginRight: "30px" }} value={translation('common.button.select-all.label','no label')} />
<input type="button" className="button" value={translation('common.button.CSV-download.label','no label')} />
</div>
</div>
);
};
export default SECSCMTEST;
找出我这个文件的错误
最新发布