Files
smart-cropx-ui/src/app/(app)/central-config/message/template/page.tsx
peng 68d9d97142 refactor: 优化认证系统和组件类型安全性
- 新增 safeLocalStorage 工具函数增强本地存储安全性
- 简化认证流程,重构用户数据结构和 token 管理
- 修复多个模块的 TypeScript 类型错误和导入问题
- 优化状态管理,重构各模块 store 结构
- 清理冗余代码,移除未使用的组件和函数
- 改进错误处理和边界情况处理
- 更新配置文件以支持最新的类型检查

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-13 14:52:52 +08:00

256 lines
8.2 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

'use client';
import { useState, useEffect } from 'react';
import { toast } from 'sonner';
import { MessageTemplate as MessageTemplateType } from './types';
import {
MessageTemplateList,
MessageTemplateSearch,
MessageTemplateHeader,
MessageTemplateDialog,
MessageTemplateTestDialog,
MessageTemplateInfo
} from './components';
export default function MessageTemplatePage() {
const [templates, setTemplates] = useState<MessageTemplateType[]>([]);
const [searchKeyword, setSearchKeyword] = useState('');
const [typeFilter, setTypeFilter] = useState<string>('all');
const [showDialog, setShowDialog] = useState(false);
const [showTestDialog, setShowTestDialog] = useState(false);
const [editingTemplate, setEditingTemplate] = useState<MessageTemplateType | null>(null);
const [testTemplate, setTestTemplate] = useState<MessageTemplateType | null>(null);
useEffect(() => {
loadTemplates();
}, []);
const loadTemplates = () => {
// 清除旧的localStorage数据确保使用最新的mock数据
localStorage.removeItem('smart_agriculture_message_templates');
const data = localStorage.getItem('smart_agriculture_message_templates');
if (data) {
try {
const parsedData = JSON.parse(data);
console.log('Loaded templates from localStorage:', parsedData);
// 确保数据是数组格式
if (Array.isArray(parsedData)) {
setTemplates(parsedData);
} else {
console.error('Loaded data is not an array:', parsedData);
setTemplates([]);
}
} catch (error) {
console.error('Error parsing templates from localStorage:', error);
setTemplates([]);
}
} else {
const mockTemplates: MessageTemplateType[] = [
{
id: 'tpl-1',
code: 'TASK_ASSIGNED',
name: '任务分配通知',
type: 'internal',
subject: '新任务分配',
content: '您好,{{username}}!您有新的作业任务:{{taskName}},计划执行时间:{{executeTime}}。请及时查看并准备。',
variables: ['username', 'taskName', 'executeTime'],
isActive: true,
description: '当任务分配给驾驶员时发送',
createdAt: '2024-01-01T00:00:00',
updatedAt: '2024-01-01T00:00:00',
createdBy: 'admin',
},
{
id: 'tpl-3',
code: 'MAINTENANCE_REMINDER',
name: '保养提醒',
type: 'email',
subject: '设备保养提醒',
content: '尊敬的用户:\n\n您的设备{{equipmentName}}(编号:{{equipmentCode}})已使用{{hours}}小时,建议进行保养维护。\n\n保养周期{{maintenanceCycle}}\n上次保养时间{{lastMaintenanceTime}}\n\n请及时安排保养确保设备正常运行。\n\n智慧农业管理系统',
variables: ['equipmentName', 'equipmentCode', 'hours', 'maintenanceCycle', 'lastMaintenanceTime'],
isActive: true,
description: '设备到达保养周期时发送邮件提醒',
createdAt: '2024-01-01T00:00:00',
updatedAt: '2024-01-01T00:00:00',
createdBy: 'admin',
},
{
id: 'tpl-4',
code: 'TASK_COMPLETED',
name: '任务完成通知',
type: 'push',
subject: '任务完成',
content: '作业任务{{taskName}}已完成,作业面积:{{area}}亩,耗时:{{duration}}。',
variables: ['taskName', 'area', 'duration'],
isActive: true,
description: '任务完成后推送通知',
createdAt: '2024-01-01T00:00:00',
updatedAt: '2024-01-01T00:00:00',
createdBy: 'admin',
},
];
console.log('Created mock templates:', mockTemplates);
localStorage.setItem('smart_agriculture_message_templates', JSON.stringify(mockTemplates));
setTemplates(mockTemplates);
}
};
const saveTemplates = (newTemplates: MessageTemplateType[]) => {
localStorage.setItem('smart_agriculture_message_templates', JSON.stringify(newTemplates));
setTemplates(newTemplates);
};
const filteredTemplates = templates.filter(tpl => {
const matchKeyword = !searchKeyword ||
tpl.name.includes(searchKeyword) ||
tpl.code.includes(searchKeyword) ||
tpl.content.includes(searchKeyword);
const matchType = typeFilter === 'all' || tpl.type === typeFilter;
return matchKeyword && matchType;
});
// 调试日志
console.log('Original templates:', templates);
console.log('Filtered templates:', filteredTemplates);
console.log('Search keyword:', searchKeyword);
console.log('Type filter:', typeFilter);
const handleAdd = () => {
setEditingTemplate(null);
setShowDialog(true);
};
const handleEdit = (template: MessageTemplateType) => {
setEditingTemplate(template);
setShowDialog(true);
};
const handleDelete = (id: string) => {
const updated = templates.filter(t => t.id !== id);
saveTemplates(updated);
toast.success('删除成功');
};
const handleSave = (formData: any) => {
if (!formData.code.trim() || !formData.name.trim() || !formData.content.trim()) {
toast.error('请填写必填项');
return;
}
const now = new Date().toISOString();
// 提取变量
const variableRegex = /\{\{(\w+)\}\}/g;
const matches = formData.content.matchAll(variableRegex);
const extractedVars = Array.from(new Set(Array.from(matches, m => m[1])));
if (editingTemplate) {
const updated = templates.map(t =>
t.id === editingTemplate.id
? {
...t,
...formData,
variables: extractedVars,
updatedAt: now,
}
: t
);
saveTemplates(updated);
toast.success('更新成功');
} else {
const newTemplate: MessageTemplateType = {
id: `tpl-${Date.now()}`,
...formData,
variables: extractedVars,
createdAt: now,
updatedAt: now,
createdBy: 'admin',
};
saveTemplates([...templates, newTemplate]);
toast.success('添加成功');
}
setShowDialog(false);
};
const handleTest = (template: MessageTemplateType) => {
setTestTemplate(template);
setShowTestDialog(true);
};
const handleSendTest = (testData: any) => {
if (!testData.recipient.trim()) {
toast.error('请输入接收人');
return;
}
// 检查变量是否都填写了
const emptyVars = Object.entries(testData.variables).filter(([k, v]) => !(v as string).trim());
if (emptyVars.length > 0) {
toast.error('请填写变量:' + emptyVars.map(([k]) => k).join(', '));
return;
}
toast.success('测试消息发送成功');
setShowTestDialog(false);
};
const handleExport = () => {
const dataStr = JSON.stringify(filteredTemplates, null, 2);
const dataBlob = new Blob([dataStr], { type: 'application/json' });
const url = URL.createObjectURL(dataBlob);
const link = document.createElement('a');
link.href = url;
link.download = `message_templates_${new Date().getTime()}.json`;
link.click();
toast.success('导出成功');
};
return (
<div className="space-y-6">
{/* 页面头部 */}
<MessageTemplateHeader
onExport={handleExport}
onAdd={handleAdd}
/>
{/* 搜索和筛选 */}
<MessageTemplateSearch
searchKeyword={searchKeyword}
onSearchChange={setSearchKeyword}
typeFilter={typeFilter}
onTypeFilterChange={setTypeFilter}
/>
{/* 模版列表 */}
<MessageTemplateList
templates={filteredTemplates}
onEdit={handleEdit}
onDelete={handleDelete}
onTest={handleTest}
/>
{/* 编辑对话框 */}
<MessageTemplateDialog
open={showDialog}
onOpenChange={setShowDialog}
editingTemplate={editingTemplate}
onSave={handleSave}
/>
{/* 测试对话框 */}
<MessageTemplateTestDialog
open={showTestDialog}
onOpenChange={setShowTestDialog}
testTemplate={testTemplate}
onSendTest={handleSendTest}
/>
{/* 使用说明 */}
<MessageTemplateInfo />
</div>
);
}