collect-system/apps/web/src/app/main/devicepage/page.tsx

583 lines
18 KiB
TypeScript
Raw Normal View History

2025-05-20 10:29:13 +08:00
import { MagnifyingGlassIcon } from "@heroicons/react/24/outline";
import {
Button,
DatePicker,
Form,
Input,
Modal,
Select,
AutoComplete,
Tag,
Dropdown,
Space,
2025-06-02 19:35:10 +08:00
Card,
Row,
Col,
Typography,
} from "antd";
2025-05-20 10:29:13 +08:00
import { useCallback, useEffect, useState, useRef } from "react";
import _ from "lodash";
import { useMainContext } from "../layout/MainProvider";
import DeviceTable from "./devicetable/page";
import DeviceModal from "./devicemodal/page";
import React from "react";
import {
SearchOutlined,
ReloadOutlined,
PlusOutlined,
ImportOutlined,
ExportOutlined,
UpOutlined,
DownOutlined,
HistoryOutlined,
CloseOutlined,
2025-06-02 19:35:10 +08:00
FilterOutlined,
DatabaseOutlined,
2025-05-20 10:29:13 +08:00
} from "@ant-design/icons";
import DepartmentSelect from "@web/src/components/models/department/department-select";
import SystemTypeSelect from "@web/src/app/main/devicepage/select/System-select";
import DeviceTypeSelect from "@web/src/app/main/devicepage/select/Device-select";
import dayjs from "dayjs";
2025-05-23 08:18:31 +08:00
import FixTypeSelect from "./select/Fix-select";
import { api } from "@nice/client";
const { RangePicker } = DatePicker;
2025-06-02 19:35:10 +08:00
const { Title } = Typography;
2025-05-20 10:29:13 +08:00
// 添加筛选条件类型
type SearchCondition = {
deletedAt: null;
systemType?: string;
deviceType?: string;
deptId?: string;
responsiblePerson?: { contains: string };
createdAt?: {
gte: string;
lte: string;
2025-05-26 10:13:21 +08:00
};
2025-05-20 10:29:13 +08:00
};
// 搜索历史类型
type SearchHistory = {
id: string;
keyword: string;
conditions: SearchCondition;
timestamp: number;
label: string; // 用于显示的标签
};
2025-05-20 10:29:13 +08:00
export default function DeviceMessage() {
const {
form,
formValue,
setFormValue,
setVisible,
setSearchValue,
editingRecord,
} = useMainContext();
2025-05-20 10:29:13 +08:00
// 控制展开/收起状态
const [expanded, setExpanded] = useState(false);
2025-05-20 10:29:13 +08:00
// 添加所有筛选条件的状态
const [selectedSystem, setSelectedSystem] = useState<string | null>(null);
2025-05-26 10:13:21 +08:00
const [selectedDeviceType, setSelectedDeviceType] = useState<string | null>(
null
);
const [selectedDept, setSelectedDept] = useState<string | null>(null);
// 修改为日期范围
const [dateRange, setDateRange] = useState<
[dayjs.Dayjs | null, dayjs.Dayjs | null] | null
>(null);
2025-05-20 10:29:13 +08:00
const [ipAddress, setIpAddress] = useState<string>("");
const [macAddress, setMacAddress] = useState<string>("");
const [serialNumber, setSerialNumber] = useState<string>("");
const [assetNumber, setAssetNumber] = useState<string>("");
const [manufacturer, setManufacturer] = useState<string>("");
const [model, setModel] = useState<string>("");
const [location, setLocation] = useState<string>("");
const [status, setStatus] = useState<string | null>(null);
const [selectedSystemTypeId, setSelectedSystemTypeId] = useState<string>("");
2025-05-23 08:18:31 +08:00
const [selectedFixType, setSelectedFixType] = useState<string | null>(null);
2025-05-20 10:29:13 +08:00
// 搜索历史相关状态
const [searchHistory, setSearchHistory] = useState<SearchHistory[]>([]);
const [searchKeyword, setSearchKeyword] = useState<string>("");
const [showHistory, setShowHistory] = useState(false);
2025-05-20 10:29:13 +08:00
// 创建ref以访问DeviceTable内部方法
const tableRef = useRef(null);
// 存储选中行的状态
const [selectedKeys, setSelectedKeys] = useState<React.Key[]>([]);
const [selectedData, setSelectedData] = useState<any[]>([]);
// 添加数据查询以获取名称信息
const { data: systemTypeTerms } = api.term.findMany.useQuery({
where: { taxonomy: { slug: "system_type" }, deletedAt: null },
orderBy: { order: "asc" },
});
const { data: deviceTypeTerms } = api.term.findMany.useQuery({
where: { taxonomy: { slug: "device_type" }, deletedAt: null },
orderBy: { order: "asc" },
});
const { data: departments } = api.department.findMany.useQuery({
where: { deletedAt: null },
});
// 根据ID获取名称的辅助函数
const getSystemTypeName = (id: string) => {
const term = systemTypeTerms?.find((t) => t.id === id);
return term?.name || id;
};
const getDeviceTypeName = (id: string) => {
const term = deviceTypeTerms?.find((t) => t.id === id);
return term?.name || id;
};
const getDepartmentName = (id: string) => {
const dept = departments?.find((d) => d.id === id);
return dept?.name || id;
};
// 初始化时加载搜索历史
useEffect(() => {
const savedHistory = localStorage.getItem("device-search-history");
if (savedHistory) {
try {
setSearchHistory(JSON.parse(savedHistory));
} catch (error) {
console.error("加载搜索历史失败:", error);
}
}
}, []);
// 保存搜索历史到localStorage
const saveSearchHistory = (history: SearchHistory[]) => {
localStorage.setItem("device-search-history", JSON.stringify(history));
setSearchHistory(history);
};
// 生成搜索标签 - 修改为显示名称而不是ID并支持日期范围
const generateSearchLabel = (
conditions: SearchCondition,
keyword: string
) => {
const parts = [];
if (keyword) parts.push(`关键词: ${keyword}`);
if (conditions.systemType)
parts.push(`网系: ${getSystemTypeName(conditions.systemType)}`);
if (conditions.deviceType)
parts.push(`故障类型: ${getDeviceTypeName(conditions.deviceType)}`);
if (conditions.deptId)
parts.push(`单位: ${getDepartmentName(conditions.deptId)}`);
if (conditions.createdAt) {
const startDate = dayjs(conditions.createdAt.gte).format("YYYY-MM-DD");
const endDate = dayjs(conditions.createdAt.lte).format("YYYY-MM-DD");
if (startDate === endDate) {
parts.push(`日期: ${startDate}`);
} else {
parts.push(`日期: ${startDate} ~ ${endDate}`);
}
}
return parts.length > 0 ? parts.join(" | ") : "全部故障";
};
// 添加搜索历史
const addSearchHistory = (
conditions: SearchCondition,
keyword: string = ""
) => {
const newHistory: SearchHistory = {
id: Date.now().toString(),
keyword,
conditions,
timestamp: Date.now(),
label: generateSearchLabel(conditions, keyword),
};
const updatedHistory = [
newHistory,
...searchHistory.filter((h) => h.label !== newHistory.label),
].slice(0, 10); // 只保留最近10条
saveSearchHistory(updatedHistory);
};
// 应用历史搜索 - 修改为支持日期范围
const applyHistorySearch = (history: SearchHistory) => {
const { conditions, keyword } = history;
// 恢复搜索条件
setSelectedSystem(conditions.systemType || null);
setSelectedDeviceType(conditions.deviceType || null);
setSelectedDept(conditions.deptId || null);
setSelectedFixType(conditions.responsiblePerson?.contains || null);
setSearchKeyword(keyword);
if (conditions.createdAt) {
const startDate = dayjs(conditions.createdAt.gte);
const endDate = dayjs(conditions.createdAt.lte);
setDateRange([startDate, endDate]);
} else {
setDateRange(null);
}
// 应用搜索条件
setSearchValue(conditions as any);
setShowHistory(false);
};
// 删除搜索历史
const removeSearchHistory = (historyId: string, event: React.MouseEvent) => {
event.stopPropagation();
const updatedHistory = searchHistory.filter((h) => h.id !== historyId);
saveSearchHistory(updatedHistory);
};
// 清空所有搜索历史
const clearAllHistory = () => {
saveSearchHistory([]);
};
2025-05-20 10:29:13 +08:00
const handleNew = () => {
form.setFieldsValue(formValue);
console.log(editingRecord);
setVisible(true);
};
// 查询按钮点击处理 - 修改为支持日期范围
2025-05-20 10:29:13 +08:00
const handleSearch = () => {
// 构建查询条件
const whereCondition: SearchCondition = {
deletedAt: null,
...(selectedSystem && { systemType: selectedSystem }),
...(selectedDeviceType && { deviceType: selectedDeviceType }),
2025-05-23 08:18:31 +08:00
...(selectedFixType && { deviceStatus: selectedFixType }),
2025-05-20 10:29:13 +08:00
...(selectedDept && { deptId: selectedDept }),
...(dateRange &&
dateRange[0] &&
dateRange[1] && {
createdAt: {
gte: dateRange[0].startOf("day").toISOString(),
lte: dateRange[1].endOf("day").toISOString(),
},
}),
...(searchKeyword && {
OR: [
{ description: { contains: searchKeyword } },
{ location: { contains: searchKeyword } },
{ responsiblePerson: { contains: searchKeyword } },
],
2025-05-20 10:29:13 +08:00
}),
};
// 添加到搜索历史
addSearchHistory(whereCondition, searchKeyword);
2025-05-20 10:29:13 +08:00
// 更新查询条件到全局上下文
setSearchValue(whereCondition as any);
};
// 重置按钮处理 - 修改为重置日期范围
2025-05-20 10:29:13 +08:00
const handleReset = () => {
setSelectedSystem(null);
setSelectedDeviceType(null);
setSelectedDept(null);
setDateRange(null); // 重置日期范围
setSearchKeyword("");
2025-05-20 10:29:13 +08:00
setIpAddress("");
setMacAddress("");
setSerialNumber("");
setAssetNumber("");
setManufacturer("");
setModel("");
setLocation("");
saveSearchHistory([]);
2025-05-20 10:29:13 +08:00
// 重置为只查询未删除的记录
setSearchValue({ deletedAt: null } as any);
};
// 修复DepartmentSelect的onChange类型
const handleDeptChange = (value: string | string[]) => {
setSelectedDept(value as string);
};
// 处理选择变更的回调
2025-05-26 10:13:21 +08:00
const handleSelectedChange = (keys: React.Key[], data: any[]) => {
2025-05-20 10:29:13 +08:00
console.log("选中状态变化:", keys.length);
setSelectedKeys(keys);
setSelectedData(data);
};
// 处理导出按钮点击
const handleExport = () => {
if (tableRef.current) {
tableRef.current.handleExportSelected();
}
};
// 系统类别变化处理
const handleSystemTypeChange = (value: string) => {
setSelectedSystemTypeId(value);
2025-05-26 10:13:21 +08:00
form.setFieldValue("deviceType", undefined); // 清空已选故障类型
2025-05-20 10:29:13 +08:00
};
// 搜索历史下拉菜单
const historyMenuItems = [
{
key: "header",
label: (
<div className="flex justify-between items-center p-2 border-b">
<span className="font-medium"></span>
{searchHistory.length > 0 && (
<Button
type="link"
size="small"
onClick={clearAllHistory}
className="text-red-500"
>
</Button>
)}
</div>
),
disabled: true,
},
...searchHistory.map((history) => ({
key: history.id,
label: (
<div
className="flex justify-between items-center p-2 hover:bg-gray-50 cursor-pointer"
onClick={() => applyHistorySearch(history)}
>
<div className="flex-1">
<div className="text-sm font-medium truncate">{history.label}</div>
<div className="text-xs text-gray-500">
{dayjs(history.timestamp).format("MM-DD HH:mm")}
</div>
</div>
<Button
type="text"
size="small"
icon={<CloseOutlined />}
onClick={(e) => removeSearchHistory(history.id, e)}
className="ml-2 opacity-60 hover:opacity-100"
/>
</div>
),
})),
...(searchHistory.length === 0
? [
{
key: "empty",
label: (
<div className="text-center text-gray-500 p-4"></div>
),
disabled: true,
},
]
: []),
];
2025-05-20 10:29:13 +08:00
return (
2025-06-02 19:35:10 +08:00
<div className="min-h-screen bg-gray-50 p-4">
{/* 页面标题区域 */}
<div className="mb-6">
<Card className="shadow-sm border border-gray-200">
<div className="flex justify-between items-center">
<div className="flex items-center gap-3">
<div className="w-10 h-10 bg-blue-600 rounded-lg flex items-center justify-center">
<DatabaseOutlined className="text-lg text-white" />
</div>
<div>
<Title level={3} className="!mb-0 text-gray-800">
</Title>
<p className="text-gray-500 text-sm mt-1">
</p>
</div>
</div>
<div className="flex items-center gap-3">
<Dropdown
menu={{ items: historyMenuItems }}
trigger={["click"]}
open={showHistory}
onOpenChange={setShowHistory}
placement="bottomRight"
>
<Button
icon={<HistoryOutlined />}
className="border-gray-300 hover:border-blue-500 hover:text-blue-600"
>
</Button>
</Dropdown>
<Button
type="primary"
icon={<PlusOutlined />}
onClick={handleNew}
className="bg-blue-600 hover:bg-blue-700 border-blue-600"
>
</Button>
</div>
</div>
</Card>
2025-05-20 15:37:08 +08:00
</div>
2025-06-02 19:35:10 +08:00
{/* 搜索筛选区域 */}
<Card className="mb-6 shadow-sm border border-gray-200">
<div className="mb-4">
<div className="flex items-center gap-2 mb-4">
<FilterOutlined className="text-gray-600" />
<span className="font-medium text-gray-700"></span>
</div>
<Row gutter={[16, 16]}>
<Col xs={24} sm={12} md={8} lg={6}>
<div className="space-y-2">
<label className="text-sm font-medium text-gray-600">
</label>
<SystemTypeSelect
value={selectedSystem}
onChange={(value) => {
setSelectedSystem(value);
setSelectedSystemTypeId(value || ""); // 同步更新
setSelectedDeviceType(null); // 清空故障类型选择
}}
placeholder="选择网系类别"
className="w-full"
/>
</div>
</Col>
<Col xs={24} sm={12} md={8} lg={6}>
<div className="space-y-2">
<label className="text-sm font-medium text-gray-600">
</label>
<DeviceTypeSelect
value={selectedDeviceType}
onChange={setSelectedDeviceType}
placeholder="选择故障类型"
className="w-full"
systemTypeId={selectedSystemTypeId}
/>
</div>
</Col>
<Col xs={24} sm={12} md={8} lg={6}>
<div className="space-y-2">
<label className="text-sm font-medium text-gray-600">
</label>
<FixTypeSelect
value={selectedFixType}
onChange={setSelectedFixType}
placeholder="选择故障状态"
className="w-full"
/>
</div>
</Col>
<Col xs={24} sm={12} md={8} lg={6}>
<div className="space-y-2">
<label className="text-sm font-medium text-gray-600">
</label>
<DepartmentSelect
placeholder="选择单位"
className="w-full"
value={selectedDept}
onChange={handleDeptChange}
/>
</div>
</Col>
<Col xs={24} sm={12} md={16} lg={12}>
<div className="space-y-2">
<label className="text-sm font-medium text-gray-600">
</label>
<RangePicker
placeholder={["开始日期", "结束日期"]}
className="w-full"
value={dateRange}
onChange={(dates) => setDateRange(dates)}
format="YYYY-MM-DD"
allowClear
/>
</div>
</Col>
<Col xs={24} sm={12} md={8} lg={6}>
<div className="space-y-2">
<label className="text-sm font-medium text-gray-600 opacity-0">
</label>
<div className="flex gap-2">
<Button
type="primary"
icon={<SearchOutlined />}
onClick={handleSearch}
className="bg-blue-600 hover:bg-blue-700 border-blue-600"
block
>
</Button>
<Button
icon={<ReloadOutlined />}
onClick={handleReset}
className="border-gray-300 hover:border-gray-400"
>
</Button>
</div>
</div>
</Col>
</Row>
2025-05-20 10:29:13 +08:00
</div>
2025-06-02 19:35:10 +08:00
{/* 搜索历史标签 */}
{searchHistory.length > 0 && (
<div className="mt-4 p-4 bg-gray-50 rounded-lg border border-gray-200">
<div className="flex items-center gap-3 mb-3">
<HistoryOutlined className="text-gray-600" />
<span className="text-sm font-medium text-gray-700">
</span>
</div>
<div className="flex flex-wrap gap-2">
{searchHistory.slice(0, 5).map((history) => (
<Tag
key={history.id}
className="cursor-pointer bg-white border-gray-300 text-gray-700 hover:bg-gray-50 hover:border-blue-400 hover:text-blue-600 transition-colors duration-200"
onClick={() => applyHistorySearch(history)}
closable
onClose={(e) => {
e.preventDefault();
removeSearchHistory(history.id, e as any);
}}
>
<span className="text-xs">
{history.label.length > 25
? `${history.label.substring(0, 25)}...`
: history.label}
</span>
</Tag>
))}
</div>
</div>
2025-06-02 19:35:10 +08:00
)}
</Card>
2025-06-02 19:35:10 +08:00
{/* 表格区域 */}
<Card className="shadow-sm border border-gray-200">
2025-05-26 10:13:21 +08:00
<DeviceTable ref={tableRef} onSelectedChange={handleSelectedChange} />
2025-05-20 15:37:08 +08:00
<DeviceModal />
2025-06-02 19:35:10 +08:00
</Card>
2025-05-20 10:29:13 +08:00
</div>
);
}