add
This commit is contained in:
parent
ee320de2ca
commit
754900d0b8
|
@ -21,20 +21,22 @@ module.exports = {
|
|||
'@typescript-eslint/explicit-function-return-type': 'off',
|
||||
'@typescript-eslint/explicit-module-boundary-types': 'off',
|
||||
'@typescript-eslint/no-explicit-any': 'off',
|
||||
// 允许使用短路表达式
|
||||
'no-unused-expressions': 'off',
|
||||
// 允许使用 let 声明后不重新赋值的变量
|
||||
'prefer-const': 'off',
|
||||
// 允许使用 any 类型
|
||||
'@typescript-eslint/no-explicit-any': 'off',
|
||||
|
||||
// 允许声明但未使用的变量
|
||||
'@typescript-eslint/no-unused-vars': [
|
||||
'warn',
|
||||
{
|
||||
vars: 'all', // 检查所有变量
|
||||
args: 'none', // 不检查函数参数
|
||||
vars: 'all',
|
||||
args: 'none',
|
||||
ignoreRestSiblings: true,
|
||||
},
|
||||
],
|
||||
|
||||
// 禁止使用未声明的变量
|
||||
'no-undef': 'error',
|
||||
// 可选:关闭未定义变量检查
|
||||
'no-undef': 'off',
|
||||
},
|
||||
};
|
||||
|
|
|
@ -23,8 +23,16 @@ export default tseslint.config(
|
|||
"warn",
|
||||
{ allowConstantExport: true },
|
||||
],
|
||||
"@typescript-eslint/interface-name-prefix": "off",
|
||||
"@typescript-eslint/explicit-function-return-type": "off",
|
||||
"@typescript-eslint/explicit-module-boundary-types": "off",
|
||||
"@typescript-eslint/no-explicit-any": "off",
|
||||
// 允许使用 any 类型
|
||||
"@typescript-eslint/no-explicit-any": "off",
|
||||
// 允许使用 let 声明后不重新赋值的变量
|
||||
"no-unused-expressions": "off",
|
||||
// 允许使用 let 声明后不重新赋值的变量
|
||||
"prefer-const": "off",
|
||||
|
||||
// 允许声明但未使用的变量
|
||||
"@typescript-eslint/no-unused-vars": [
|
||||
|
|
|
@ -1,23 +1,28 @@
|
|||
import { useState, useEffect, useMemo, useCallback } from 'react';
|
||||
import { AgGridReact } from 'ag-grid-react';
|
||||
/* eslint-disable no-undef */
|
||||
/* eslint-disable @typescript-eslint/no-unused-expressions */
|
||||
/* eslint-disable no-unused-expressions */
|
||||
import { useState, useEffect, useMemo, useCallback } from "react";
|
||||
import { AgGridReact } from "ag-grid-react";
|
||||
import { api, useStaff } from "@nice/client";
|
||||
import { Button, CascaderProps, message, Modal, Input, Upload } from 'antd';
|
||||
import { areaOptions } from '@web/src/app/main/staffinfo_write/area-options';
|
||||
import StaffInfoWrite from '@web/src/app/main/staffinfo_write/staffinfo_write.page';
|
||||
import { utils, writeFile, read } from 'xlsx';
|
||||
import { UploadOutlined } from '@ant-design/icons';
|
||||
import { Button, CascaderProps, message, Modal, Input, Upload } from "antd";
|
||||
import { areaOptions } from "@web/src/app/main/staffinfo_write/area-options";
|
||||
import StaffInfoWrite from "@web/src/app/main/staffinfo_write/staffinfo_write.page";
|
||||
import { utils, writeFile, read } from "xlsx";
|
||||
import { UploadOutlined } from "@ant-design/icons";
|
||||
|
||||
function getAreaName(codes: string[], level?: number): string {
|
||||
const result: string[] = [];
|
||||
let currentLevel: CascaderProps['options'] = areaOptions;
|
||||
let currentLevel: CascaderProps["options"] = areaOptions;
|
||||
for (const code of codes) {
|
||||
const found = currentLevel?.find(opt => opt.value === code);
|
||||
const found = currentLevel?.find((opt) => opt.value === code);
|
||||
if (!found) break;
|
||||
result.push(String(found.label));
|
||||
currentLevel = found.children || [];
|
||||
if (level && result.length >= level) break; // 添加层级控制
|
||||
}
|
||||
return level ? result[level - 1] || '' : result.join(' / ') || codes.join('/');
|
||||
return level
|
||||
? result[level - 1] || ""
|
||||
: result.join(" / ") || codes.join("/");
|
||||
}
|
||||
|
||||
// 添加表头提取工具函数
|
||||
|
@ -25,8 +30,8 @@ function extractHeaders(columns: any[]): string[] {
|
|||
const result: string[] = [];
|
||||
|
||||
const extractHeadersRecursive = (cols: any[]) => {
|
||||
cols.forEach(col => {
|
||||
if ('children' in col && col.children) {
|
||||
cols.forEach((col) => {
|
||||
if ("children" in col && col.children) {
|
||||
extractHeadersRecursive(col.children);
|
||||
} else if (col.headerName) {
|
||||
result.push(col.headerName);
|
||||
|
@ -48,39 +53,43 @@ export default function StaffMessage() {
|
|||
const [currentEditStaff, setCurrentEditStaff] = useState<any>(null);
|
||||
const [gridApi, setGridApi] = useState<any>(null);
|
||||
const [fileNameVisible, setFileNameVisible] = useState(false);
|
||||
const [fileName, setFileName] = useState('');
|
||||
const [defaultFileName] = useState(`员工数据_${new Date().toISOString().slice(0, 10)}`);
|
||||
const [fileName, setFileName] = useState("");
|
||||
const [defaultFileName] = useState(
|
||||
`员工数据_${new Date().toISOString().slice(0, 10)}`
|
||||
);
|
||||
const [importVisible, setImportVisible] = useState(false);
|
||||
|
||||
// 获取数据
|
||||
const { data: staffData } = api.staff.findMany.useQuery({
|
||||
where: {
|
||||
deletedAt: null
|
||||
deletedAt: null,
|
||||
},
|
||||
include: {
|
||||
fieldValues: {
|
||||
include: {
|
||||
// 添加这两个关联字段
|
||||
staff: { select: { id: true } }, // 关联员工ID
|
||||
field: { select: { id: true } } // 关联字段ID
|
||||
}
|
||||
field: { select: { id: true } }, // 关联字段ID
|
||||
},
|
||||
},
|
||||
department: true,
|
||||
},
|
||||
department: true
|
||||
}
|
||||
} as any);
|
||||
// console.log(staffData);
|
||||
const actionColumns = [{
|
||||
const actionColumns = [
|
||||
{
|
||||
field: "action",
|
||||
width: 50,
|
||||
checkboxSelection: true,
|
||||
headerCheckboxSelection: true,
|
||||
pinned: 'left'
|
||||
}]
|
||||
pinned: "left",
|
||||
},
|
||||
];
|
||||
// 新增编辑处理函数
|
||||
const handleEdit = useCallback(async () => {
|
||||
if (selectedRows.length === 0) return;
|
||||
if (selectedRows.length > 1) {
|
||||
message.error('只能选择一个员工进行编辑');
|
||||
message.error("只能选择一个员工进行编辑");
|
||||
return;
|
||||
}
|
||||
setCurrentEditStaff(selectedRows[0]);
|
||||
|
@ -97,47 +106,51 @@ export default function StaffMessage() {
|
|||
// 新增删除处理函数
|
||||
const handleDelete = useCallback(async () => {
|
||||
if (selectedRows.length === 0) return;
|
||||
console.log('待删除的选中行数据:', selectedRows); // 新增调试语句
|
||||
console.log("待删除的选中行数据:", selectedRows); // 新增调试语句
|
||||
try {
|
||||
await softDeleteByIds.mutateAsync({
|
||||
ids: selectedRows?.map(row => {
|
||||
console.log('当前行ID:', row.id); // 检查每个ID
|
||||
return row.id
|
||||
})
|
||||
ids: selectedRows?.map((row) => {
|
||||
console.log("当前行ID:", row.id); // 检查每个ID
|
||||
return row.id;
|
||||
}),
|
||||
});
|
||||
message.success('删除成功');
|
||||
message.success("删除成功");
|
||||
} catch (error) {
|
||||
message.error('删除失败');
|
||||
console.error('详细错误信息:', error); // 输出完整错误堆栈
|
||||
message.error("删除失败");
|
||||
console.error("详细错误信息:", error); // 输出完整错误堆栈
|
||||
}
|
||||
// 重新获取数据或本地过滤已删除项
|
||||
}, [selectedRows]);
|
||||
|
||||
// 缓存基础列定义
|
||||
const baseColumns = useMemo(() => [
|
||||
const baseColumns = useMemo(
|
||||
() => [
|
||||
{
|
||||
field: 'showname',
|
||||
headerName: '姓名',
|
||||
filter: 'agSetColumnFilter',
|
||||
pinned: 'left'
|
||||
field: "showname",
|
||||
headerName: "姓名",
|
||||
filter: "agSetColumnFilter",
|
||||
pinned: "left",
|
||||
},
|
||||
{
|
||||
field: 'deptId',
|
||||
headerName: '所属部门',
|
||||
valueGetter: params => params.data.department?.name,
|
||||
filter: 'agSetColumnFilter'
|
||||
}
|
||||
], []);
|
||||
field: "deptId",
|
||||
headerName: "所属部门",
|
||||
valueGetter: (params) => params.data.department?.name,
|
||||
filter: "agSetColumnFilter",
|
||||
},
|
||||
],
|
||||
[]
|
||||
);
|
||||
|
||||
// 缓存动态列定义
|
||||
const dynamicColumns = useMemo(() =>
|
||||
(fields.data || [] as any).map(field => ({
|
||||
const dynamicColumns = useMemo(
|
||||
() =>
|
||||
(fields.data || ([] as any)).map((field) => ({
|
||||
field: field.name,
|
||||
headerName: field.label || field.name,
|
||||
filter: 'agSetColumnFilter',
|
||||
cellStyle: { whiteSpace: 'pre-line' },
|
||||
filter: "agSetColumnFilter",
|
||||
cellStyle: { whiteSpace: "pre-line" },
|
||||
autoHeight: true,
|
||||
valueGetter: params => {
|
||||
valueGetter: (params) => {
|
||||
// 获取原始值
|
||||
const rawValue = params.data.fieldValues?.find(
|
||||
(fv: any) => fv.fieldId === field.id
|
||||
|
@ -145,22 +158,22 @@ export default function StaffMessage() {
|
|||
|
||||
// 根据字段类型格式化
|
||||
switch (field.type) {
|
||||
case 'cascader':
|
||||
return rawValue ? getAreaName(rawValue.split('/')) : '';
|
||||
case "cascader":
|
||||
return rawValue ? getAreaName(rawValue.split("/")) : "";
|
||||
|
||||
case 'date':
|
||||
case "date":
|
||||
// 格式化日期(假设存储的是ISO字符串)
|
||||
return rawValue ? new Date(rawValue).toLocaleDateString() : '';
|
||||
return rawValue ? new Date(rawValue).toLocaleDateString() : "";
|
||||
|
||||
case 'textarea':
|
||||
case "textarea":
|
||||
// 换行处理
|
||||
|
||||
return rawValue?.replace(/,/g, '\n');
|
||||
return rawValue?.replace(/,/g, "\n");
|
||||
|
||||
default:
|
||||
return rawValue;
|
||||
}
|
||||
}
|
||||
},
|
||||
})),
|
||||
[fields.data]
|
||||
);
|
||||
|
@ -179,7 +192,9 @@ export default function StaffMessage() {
|
|||
const headerNames = extractHeaders(columnDefs);
|
||||
|
||||
// 创建示例数据行
|
||||
let exampleRow: Record<string, string> = {};
|
||||
const exampleRow: Record<string, string> = {};
|
||||
// 定义 fieldsList(移到这里)
|
||||
const fieldsList = Array.isArray(fields?.data) ? fields.data : [];
|
||||
|
||||
// 检查是否有选中行
|
||||
if (selectedRows.length > 0) {
|
||||
|
@ -187,11 +202,10 @@ export default function StaffMessage() {
|
|||
const templateData = selectedRows[0];
|
||||
|
||||
// 基础字段
|
||||
exampleRow['姓名'] = templateData.showname || '';
|
||||
exampleRow['所属部门'] = templateData.department?.name || '';
|
||||
exampleRow["姓名"] = templateData.showname || "";
|
||||
exampleRow["所属部门"] = templateData.department?.name || "";
|
||||
|
||||
// 处理自定义字段
|
||||
const fieldsList = Array.isArray(fields?.data) ? fields.data : [];
|
||||
fieldsList.forEach((field: any) => {
|
||||
const fieldValue = templateData.fieldValues?.find(
|
||||
(fv: any) => fv.fieldId === field.id
|
||||
|
@ -200,32 +214,35 @@ export default function StaffMessage() {
|
|||
let displayValue = fieldValue;
|
||||
|
||||
// 根据字段类型处理值
|
||||
if (field.type === 'cascader' && fieldValue) {
|
||||
displayValue = getAreaName(fieldValue.split('/'));
|
||||
} else if (field.type === 'date' && fieldValue) {
|
||||
if (field.type === "cascader" && fieldValue) {
|
||||
displayValue = getAreaName(fieldValue.split("/"));
|
||||
} else if (field.type === "date" && fieldValue) {
|
||||
displayValue = new Date(fieldValue).toLocaleDateString();
|
||||
} else if (field.type === 'textarea' && fieldValue) {
|
||||
displayValue = fieldValue.replace(/,/g, '\n');
|
||||
} else if (field.type === "textarea" && fieldValue) {
|
||||
displayValue = fieldValue.replace(/,/g, "\n");
|
||||
}
|
||||
|
||||
exampleRow[field.label || field.name] = displayValue || '';
|
||||
exampleRow[field.label || field.name] = displayValue || "";
|
||||
});
|
||||
} else {
|
||||
// 如果没有选中行,使用默认示例数据
|
||||
exampleRow['姓名'] = '张三';
|
||||
exampleRow['所属部门'] = '技术部';
|
||||
exampleRow["姓名"] = "张三";
|
||||
exampleRow["所属部门"] = "技术部";
|
||||
|
||||
// 添加所有自定义字段的空值
|
||||
fieldsList.forEach((field: any) => {
|
||||
exampleRow[field.label || field.name] = '';
|
||||
exampleRow[field.label || field.name] = "";
|
||||
});
|
||||
}
|
||||
|
||||
// 创建空白行供用户填写
|
||||
const emptyRow = headerNames.reduce((obj, header) => {
|
||||
obj[header] = '';
|
||||
const emptyRow = headerNames.reduce(
|
||||
(obj, header) => {
|
||||
obj[header] = "";
|
||||
return obj;
|
||||
}, {} as Record<string, string>);
|
||||
},
|
||||
{} as Record<string, string>
|
||||
);
|
||||
|
||||
// 创建工作簿和工作表
|
||||
const wb = utils.book_new();
|
||||
|
@ -233,26 +250,30 @@ export default function StaffMessage() {
|
|||
|
||||
// 设置列宽
|
||||
const colWidth = headerNames.map(() => ({ wch: 20 }));
|
||||
ws['!cols'] = colWidth;
|
||||
ws["!cols"] = colWidth;
|
||||
|
||||
// 在第二行添加提示文字
|
||||
const rowIdx = 2; // 第二行索引
|
||||
const cellRef = utils.encode_cell({ r: rowIdx, c: 0 }); // A3单元格
|
||||
|
||||
const tipText = selectedRows.length > 0
|
||||
? '以上为选中人员数据,请在下方行填写实际数据'
|
||||
: '以上为示例数据,请在下方行填写实际数据';
|
||||
const tipText =
|
||||
selectedRows.length > 0
|
||||
? "以上为选中人员数据,请在下方行填写实际数据"
|
||||
: "以上为示例数据,请在下方行填写实际数据";
|
||||
|
||||
ws[cellRef] = { t: 's', v: tipText };
|
||||
ws[cellRef] = { t: "s", v: tipText };
|
||||
|
||||
// 手动添加空白行
|
||||
utils.sheet_add_json(ws, [emptyRow], { skipHeader: true, origin: rowIdx + 1 });
|
||||
utils.sheet_add_json(ws, [emptyRow], {
|
||||
skipHeader: true,
|
||||
origin: rowIdx + 1,
|
||||
});
|
||||
|
||||
// 合并提示文字单元格
|
||||
if (!ws['!merges']) ws['!merges'] = [];
|
||||
ws['!merges'].push({
|
||||
if (!ws["!merges"]) ws["!merges"] = [];
|
||||
ws["!merges"].push({
|
||||
s: { r: rowIdx, c: 0 }, // 起始单元格 A3
|
||||
e: { r: rowIdx, c: Math.min(5, headerNames.length - 1) } // 结束单元格,跨越多列
|
||||
e: { r: rowIdx, c: Math.min(5, headerNames.length - 1) }, // 结束单元格,跨越多列
|
||||
});
|
||||
|
||||
utils.book_append_sheet(wb, ws, "员工模板");
|
||||
|
@ -268,7 +289,7 @@ export default function StaffMessage() {
|
|||
const handleFileNameConfirm = useCallback(() => {
|
||||
setFileNameVisible(false);
|
||||
if (!gridApi) {
|
||||
console.error('Grid API 未正确初始化');
|
||||
console.error("Grid API 未正确初始化");
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -277,29 +298,31 @@ export default function StaffMessage() {
|
|||
const allData = selectedRows.length > 0 ? selectedRows : rowData;
|
||||
|
||||
// 格式化数据
|
||||
const exportData = allData.map(row => {
|
||||
const exportData = allData.map((row) => {
|
||||
const formattedRow: Record<string, any> = {};
|
||||
|
||||
// 基础字段
|
||||
formattedRow['姓名'] = row.showname || '';
|
||||
formattedRow['所属部门'] = row.department?.name || '';
|
||||
formattedRow["姓名"] = row.showname || "";
|
||||
formattedRow["所属部门"] = row.department?.name || "";
|
||||
|
||||
// 动态字段
|
||||
const fieldsList = Array.isArray(fields?.data) ? fields.data : [];
|
||||
fieldsList.forEach((field: any) => {
|
||||
const fieldValue = row.fieldValues?.find((fv: any) => fv.fieldId === field.id)?.value;
|
||||
const fieldValue = row.fieldValues?.find(
|
||||
(fv: any) => fv.fieldId === field.id
|
||||
)?.value;
|
||||
let displayValue = fieldValue;
|
||||
|
||||
// 根据字段类型处理值
|
||||
if (field.type === 'cascader' && fieldValue) {
|
||||
displayValue = getAreaName(fieldValue.split('/'));
|
||||
} else if (field.type === 'date' && fieldValue) {
|
||||
if (field.type === "cascader" && fieldValue) {
|
||||
displayValue = getAreaName(fieldValue.split("/"));
|
||||
} else if (field.type === "date" && fieldValue) {
|
||||
displayValue = new Date(fieldValue).toLocaleDateString();
|
||||
} else if (field.type === 'textarea' && fieldValue) {
|
||||
displayValue = fieldValue.replace(/,/g, '\n');
|
||||
} else if (field.type === "textarea" && fieldValue) {
|
||||
displayValue = fieldValue.replace(/,/g, "\n");
|
||||
}
|
||||
|
||||
formattedRow[field.label || field.name] = displayValue || '';
|
||||
formattedRow[field.label || field.name] = displayValue || "";
|
||||
});
|
||||
|
||||
return formattedRow;
|
||||
|
@ -314,43 +337,44 @@ export default function StaffMessage() {
|
|||
const finalFileName = fileName || defaultFileName;
|
||||
writeFile(wb, `${finalFileName}.xlsx`);
|
||||
} catch (error) {
|
||||
console.error('导出失败:', error);
|
||||
message.error('导出失败,请稍后重试');
|
||||
console.error("导出失败:", error);
|
||||
message.error("导出失败,请稍后重试");
|
||||
}
|
||||
}, [fileName, defaultFileName, rowData, selectedRows, fields.data, gridApi]);
|
||||
|
||||
// 处理导入数据
|
||||
const createMany = api.staff.create.useMutation({
|
||||
onSuccess: () => {
|
||||
message.success('员工数据导入成功');
|
||||
message.success("员工数据导入成功");
|
||||
// 刷新数据
|
||||
api.staff.findMany.useQuery();
|
||||
setImportVisible(false);
|
||||
},
|
||||
onError: (error) => {
|
||||
message.error(`导入失败: ${error.message}`);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
// 处理Excel导入数据
|
||||
const handleImportData = useCallback((excelData: any[]) => {
|
||||
const handleImportData = useCallback(
|
||||
(excelData: any[]) => {
|
||||
if (excelData.length === 0) {
|
||||
message.warning('没有可导入的数据');
|
||||
message.warning("没有可导入的数据");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// 将Excel数据转换为API需要的格式
|
||||
const staffData = excelData.map(row => {
|
||||
const staffData = excelData.map((row) => {
|
||||
const staff: any = { fieldValues: [] };
|
||||
|
||||
// 处理基础字段
|
||||
if (row['姓名']) staff.showname = row['姓名'];
|
||||
if (row["姓名"]) staff.showname = row["姓名"];
|
||||
|
||||
// 处理部门
|
||||
if (row['所属部门']) {
|
||||
if (row["所属部门"]) {
|
||||
// 简单存储部门名称,后续可能需要查询匹配
|
||||
staff.departmentName = row['所属部门'];
|
||||
staff.departmentName = row["所属部门"];
|
||||
}
|
||||
|
||||
// 处理自定义字段
|
||||
|
@ -359,16 +383,16 @@ export default function StaffMessage() {
|
|||
let value = row[field.label || field.name];
|
||||
|
||||
// 跳过空值
|
||||
if (value === undefined || value === '') return;
|
||||
if (value === undefined || value === "") return;
|
||||
|
||||
// 根据字段类型处理输入值
|
||||
switch (field.type) {
|
||||
case 'cascader':
|
||||
case "cascader":
|
||||
// 级联选择器可能需要将显示名称转回代码
|
||||
// 这里简单保留原值,实际可能需要查询转换
|
||||
break;
|
||||
|
||||
case 'date':
|
||||
case "date":
|
||||
// 尝试将日期字符串转换为ISO格式
|
||||
try {
|
||||
const dateObj = new Date(value);
|
||||
|
@ -380,10 +404,10 @@ export default function StaffMessage() {
|
|||
}
|
||||
break;
|
||||
|
||||
case 'textarea':
|
||||
case "textarea":
|
||||
// 将换行符替换回逗号进行存储
|
||||
if (typeof value === 'string') {
|
||||
value = value.replace(/\n/g, ',');
|
||||
if (typeof value === "string") {
|
||||
value = value.replace(/\n/g, ",");
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -393,7 +417,7 @@ export default function StaffMessage() {
|
|||
// 添加到fieldValues数组
|
||||
staff.fieldValues.push({
|
||||
fieldId: field.id,
|
||||
value: String(value)
|
||||
value: String(value),
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -401,35 +425,63 @@ export default function StaffMessage() {
|
|||
});
|
||||
|
||||
// 提交数据
|
||||
createMany.mutate({ data: staffData });
|
||||
createMany.mutate({
|
||||
data: staffData[0], // 由于类型限制,这里只能一条一条导入
|
||||
});
|
||||
// 如果有多条数据,需要循环处理
|
||||
for (let i = 1; i < staffData.length; i++) {
|
||||
createMany.mutate({ data: staffData[i] });
|
||||
}
|
||||
message.info(`正在导入${staffData.length}条员工数据...`);
|
||||
} catch (error) {
|
||||
console.error('处理导入数据失败:', error);
|
||||
message.error('数据格式错误,导入失败');
|
||||
console.error("处理导入数据失败:", error);
|
||||
message.error("数据格式错误,导入失败");
|
||||
}
|
||||
}, [fields.data, createMany]);
|
||||
},
|
||||
[fields.data, createMany]
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="flex justify-between items-center mb-4">
|
||||
<div className="space-x-2">
|
||||
<Button className="bg-red-500 hover:bg-red-600 border-red-500 text-white rounded-md px-4 py-2"
|
||||
onClick={handleDelete}>批量删除</Button>
|
||||
<Button className="bg-blue-500 hover:bg-blue-600 border-blue-500 text-white rounded-md px-4 py-2"
|
||||
onClick={handleEdit}>编辑</Button>
|
||||
<Button onClick={() => setImportVisible(true)} className="bg-orange-500 text-white px-4 py-2 rounded hover:bg-orange-600">
|
||||
<Button
|
||||
className="bg-red-500 hover:bg-red-600 border-red-500 text-white rounded-md px-4 py-2"
|
||||
onClick={handleDelete}
|
||||
>
|
||||
批量删除
|
||||
</Button>
|
||||
<Button
|
||||
className="bg-blue-500 hover:bg-blue-600 border-blue-500 text-white rounded-md px-4 py-2"
|
||||
onClick={handleEdit}
|
||||
>
|
||||
编辑
|
||||
</Button>
|
||||
<Button
|
||||
onClick={() => setImportVisible(true)}
|
||||
className="bg-orange-500 text-white px-4 py-2 rounded hover:bg-orange-600"
|
||||
>
|
||||
导入Excel
|
||||
</Button>
|
||||
<Button onClick={handleExportTemplate} className="bg-blue-500 text-white px-4 py-2 rounded hover:bg-blue-600">
|
||||
<Button
|
||||
onClick={handleExportTemplate}
|
||||
className="bg-blue-500 text-white px-4 py-2 rounded hover:bg-blue-600"
|
||||
>
|
||||
导出模板
|
||||
</Button>
|
||||
<Button onClick={handleConfirm} className="bg-green-500 text-white px-4 py-2 rounded hover:bg-green-600">
|
||||
<Button
|
||||
onClick={handleConfirm}
|
||||
className="bg-green-500 text-white px-4 py-2 rounded hover:bg-green-600"
|
||||
>
|
||||
导出所有数据
|
||||
</Button>
|
||||
</div>
|
||||
<Button className="bg-gray-100 hover:bg-gray-200 border-gray-300 text-gray-700 rounded-md px-4 py-2"
|
||||
onClick={() => {
|
||||
}}>调试数据</Button>
|
||||
<Button
|
||||
className="bg-gray-100 hover:bg-gray-200 border-gray-300 text-gray-700 rounded-md px-4 py-2"
|
||||
onClick={() => {}}
|
||||
>
|
||||
调试数据
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<div className="bg-white rounded-lg shadow-md p-6 mb-4">
|
||||
|
@ -442,7 +494,7 @@ export default function StaffMessage() {
|
|||
pagination={true}
|
||||
paginationPageSize={10}
|
||||
paginationPageSizeSelector={[10, 20, 50, 100]}
|
||||
onSelectionChanged={e => setSelectedRows(e.api.getSelectedRows())}
|
||||
onSelectionChanged={(e) => setSelectedRows(e.api.getSelectedRows())}
|
||||
rowSelection="multiple"
|
||||
className="rounded border border-gray-200"
|
||||
headerHeight={40}
|
||||
|
@ -498,7 +550,7 @@ export default function StaffMessage() {
|
|||
>
|
||||
<Upload
|
||||
accept=".xlsx,.xls"
|
||||
beforeUpload={file => {
|
||||
beforeUpload={(file) => {
|
||||
const reader = new FileReader();
|
||||
reader.onload = (e) => {
|
||||
try {
|
||||
|
@ -506,15 +558,15 @@ export default function StaffMessage() {
|
|||
const data = utils.sheet_to_json(wb.Sheets[wb.SheetNames[0]]);
|
||||
|
||||
if (data.length === 0) {
|
||||
message.warning('Excel文件中没有数据');
|
||||
message.warning("Excel文件中没有数据");
|
||||
return;
|
||||
}
|
||||
|
||||
message.info(`读取到${data.length}条数据,正在处理...`);
|
||||
handleImportData(data);
|
||||
} catch (error) {
|
||||
console.error('解析Excel文件失败:', error);
|
||||
message.error('Excel文件格式错误,请确保使用正确的模板');
|
||||
console.error("解析Excel文件失败:", error);
|
||||
message.error("Excel文件格式错误,请确保使用正确的模板");
|
||||
}
|
||||
};
|
||||
reader.readAsArrayBuffer(file);
|
||||
|
|
|
@ -466,7 +466,8 @@ model Staff {
|
|||
enabled Boolean? @default(true)
|
||||
officerId String? @map("officer_id")
|
||||
phoneNumber String? @map("phone_number")
|
||||
|
||||
age Int?@map("age")
|
||||
sex String?@map("sex")
|
||||
// 部门关系
|
||||
domainId String? @map("domain_id")
|
||||
deptId String? @map("dept_id")
|
||||
|
|
Loading…
Reference in New Issue