生产管理系统前端 - 瓦力0.71原型图更新
This commit is contained in:
@@ -235,7 +235,7 @@ export function Navigation({ activeTab, onTabChange, onMessageClick, onProfileCl
|
||||
messages.map((msg) => (
|
||||
<div
|
||||
key={msg.id}
|
||||
className="p-4 border-b hover:bg-gray-50 cursor-pointer transition-colors"
|
||||
className="p-4 border-b hover:bg-accent cursor-pointer transition-colors"
|
||||
onClick={() => handleMessageItemClick(msg)}
|
||||
>
|
||||
<div className="flex items-start gap-3">
|
||||
|
||||
@@ -1071,21 +1071,21 @@ export function AIAlertManagement({ activePath }: AIAlertManagementProps) {
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-4 gap-4 mb-3">
|
||||
<div className="p-3 bg-gray-50 rounded">
|
||||
<div className="p-3 bg-muted rounded">
|
||||
<div className="text-xs text-muted-foreground mb-1">触发条件</div>
|
||||
<div className="text-sm font-medium">
|
||||
{rule.condition.metric} {rule.condition.operator === 'gt' ? '>' : '<'} {rule.condition.threshold}
|
||||
</div>
|
||||
</div>
|
||||
<div className="p-3 bg-gray-50 rounded">
|
||||
<div className="p-3 bg-muted rounded">
|
||||
<div className="text-xs text-muted-foreground mb-1">持续时间</div>
|
||||
<div className="text-sm font-medium">{rule.condition.duration}秒</div>
|
||||
</div>
|
||||
<div className="p-3 bg-gray-50 rounded">
|
||||
<div className="p-3 bg-muted rounded">
|
||||
<div className="text-xs text-muted-foreground mb-1">触发次数</div>
|
||||
<div className="text-sm font-medium">{rule.triggeredCount}次</div>
|
||||
</div>
|
||||
<div className="p-3 bg-gray-50 rounded">
|
||||
<div className="p-3 bg-muted rounded">
|
||||
<div className="text-xs text-muted-foreground mb-1">最后触发</div>
|
||||
<div className="text-sm font-medium">{rule.lastTriggered || '从未'}</div>
|
||||
</div>
|
||||
@@ -1214,19 +1214,19 @@ export function AIAlertManagement({ activePath }: AIAlertManagementProps) {
|
||||
<div>
|
||||
<h4 className="mb-3">基本信息</h4>
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<div className="p-3 bg-gray-50 rounded">
|
||||
<div className="p-3 bg-muted rounded">
|
||||
<div className="text-xs text-muted-foreground mb-1">告警编号</div>
|
||||
<div className="font-medium">{selectedAlert.alertNo}</div>
|
||||
</div>
|
||||
<div className="p-3 bg-gray-50 rounded">
|
||||
<div className="p-3 bg-muted rounded">
|
||||
<div className="text-xs text-muted-foreground mb-1">告警规则</div>
|
||||
<div className="font-medium">{selectedAlert.ruleName}</div>
|
||||
</div>
|
||||
<div className="p-3 bg-gray-50 rounded">
|
||||
<div className="p-3 bg-muted rounded">
|
||||
<div className="text-xs text-muted-foreground mb-1">告警级别</div>
|
||||
<div>{getLevelBadge(selectedAlert.level)}</div>
|
||||
</div>
|
||||
<div className="p-3 bg-gray-50 rounded">
|
||||
<div className="p-3 bg-muted rounded">
|
||||
<div className="text-xs text-muted-foreground mb-1">处理状态</div>
|
||||
<div>{getStatusBadge(selectedAlert.status)}</div>
|
||||
</div>
|
||||
@@ -1249,15 +1249,15 @@ export function AIAlertManagement({ activePath }: AIAlertManagementProps) {
|
||||
<div>
|
||||
<h4 className="mb-3">触发信息</h4>
|
||||
<div className="grid grid-cols-3 gap-4">
|
||||
<div className="p-3 bg-gray-50 rounded">
|
||||
<div className="p-3 bg-muted rounded">
|
||||
<div className="text-xs text-muted-foreground mb-1">触发值</div>
|
||||
<div className="font-medium">{selectedAlert.triggerValue}</div>
|
||||
</div>
|
||||
<div className="p-3 bg-gray-50 rounded">
|
||||
<div className="p-3 bg-muted rounded">
|
||||
<div className="text-xs text-muted-foreground mb-1">阈值</div>
|
||||
<div className="font-medium">{selectedAlert.threshold}</div>
|
||||
</div>
|
||||
<div className="p-3 bg-gray-50 rounded">
|
||||
<div className="p-3 bg-muted rounded">
|
||||
<div className="text-xs text-muted-foreground mb-1">触发时间</div>
|
||||
<div className="font-medium">{selectedAlert.triggerTime}</div>
|
||||
</div>
|
||||
@@ -1544,7 +1544,7 @@ export function AIAlertManagement({ activePath }: AIAlertManagementProps) {
|
||||
|
||||
{/* 根据条件类型显示不同的配置 */}
|
||||
{triggerConditionType === 'response_time' && (
|
||||
<div className="p-4 bg-gray-50 rounded-lg space-y-3">
|
||||
<div className="p-4 bg-muted rounded-lg space-y-3">
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<div>
|
||||
<Label>响应时间阈值 (ms)</Label>
|
||||
@@ -1562,7 +1562,7 @@ export function AIAlertManagement({ activePath }: AIAlertManagementProps) {
|
||||
)}
|
||||
|
||||
{triggerConditionType === 'service_exception' && (
|
||||
<div className="p-4 bg-gray-50 rounded-lg space-y-3">
|
||||
<div className="p-4 bg-muted rounded-lg space-y-3">
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<div>
|
||||
<Label>异常类型</Label>
|
||||
@@ -1590,7 +1590,7 @@ export function AIAlertManagement({ activePath }: AIAlertManagementProps) {
|
||||
)}
|
||||
|
||||
{triggerConditionType === 'decision_failure' && (
|
||||
<div className="p-4 bg-gray-50 rounded-lg space-y-3">
|
||||
<div className="p-4 bg-muted rounded-lg space-y-3">
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<div>
|
||||
<Label>失败率阈值 (%)</Label>
|
||||
@@ -1623,7 +1623,7 @@ export function AIAlertManagement({ activePath }: AIAlertManagementProps) {
|
||||
)}
|
||||
|
||||
{triggerConditionType === 'data_quality' && (
|
||||
<div className="p-4 bg-gray-50 rounded-lg space-y-3">
|
||||
<div className="p-4 bg-muted rounded-lg space-y-3">
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<div>
|
||||
<Label>数据质量评分阈值</Label>
|
||||
@@ -1658,7 +1658,7 @@ export function AIAlertManagement({ activePath }: AIAlertManagementProps) {
|
||||
)}
|
||||
|
||||
{(triggerConditionType === 'cpu_usage' || triggerConditionType === 'memory_usage') && (
|
||||
<div className="p-4 bg-gray-50 rounded-lg space-y-3">
|
||||
<div className="p-4 bg-muted rounded-lg space-y-3">
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<div>
|
||||
<Label>使用率阈值 (%)</Label>
|
||||
@@ -1676,7 +1676,7 @@ export function AIAlertManagement({ activePath }: AIAlertManagementProps) {
|
||||
)}
|
||||
|
||||
{triggerConditionType === 'error_rate' && (
|
||||
<div className="p-4 bg-gray-50 rounded-lg space-y-3">
|
||||
<div className="p-4 bg-muted rounded-lg space-y-3">
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<div>
|
||||
<Label>错误率阈值 (%)</Label>
|
||||
@@ -1694,7 +1694,7 @@ export function AIAlertManagement({ activePath }: AIAlertManagementProps) {
|
||||
)}
|
||||
|
||||
{triggerConditionType === 'request_count' && (
|
||||
<div className="p-4 bg-gray-50 rounded-lg space-y-3">
|
||||
<div className="p-4 bg-muted rounded-lg space-y-3">
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<div>
|
||||
<Label>请求数阈值</Label>
|
||||
|
||||
@@ -1019,7 +1019,7 @@ export function AIApplicationGeneration({ activePath }: AIApplicationGenerationP
|
||||
|
||||
<Card className="p-6">
|
||||
<h4 className="mb-3">数据流向图</h4>
|
||||
<div className="p-8 bg-gray-50 rounded-lg">
|
||||
<div className="p-8 bg-muted rounded-lg">
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="text-center">
|
||||
<div className="w-24 h-24 bg-blue-100 rounded-lg flex items-center justify-center mx-auto mb-2">
|
||||
|
||||
@@ -1026,27 +1026,27 @@ export function AIAuditLog({ activePath }: AIAuditLogProps) {
|
||||
<div>
|
||||
<h4 className="mb-3">基本信息</h4>
|
||||
<div className="grid grid-cols-3 gap-4">
|
||||
<div className="p-3 bg-gray-50 rounded">
|
||||
<div className="p-3 bg-muted rounded">
|
||||
<div className="text-xs text-muted-foreground mb-1">追踪ID</div>
|
||||
<div className="font-mono text-sm">{selectedLog.traceId}</div>
|
||||
</div>
|
||||
<div className="p-3 bg-gray-50 rounded">
|
||||
<div className="p-3 bg-muted rounded">
|
||||
<div className="text-xs text-muted-foreground mb-1">决策编号</div>
|
||||
<div className="font-medium">{selectedLog.decisionNo}</div>
|
||||
</div>
|
||||
<div className="p-3 bg-gray-50 rounded">
|
||||
<div className="p-3 bg-muted rounded">
|
||||
<div className="text-xs text-muted-foreground mb-1">决策类型</div>
|
||||
<div className="font-medium">{selectedLog.decisionType}</div>
|
||||
</div>
|
||||
<div className="p-3 bg-gray-50 rounded">
|
||||
<div className="p-3 bg-muted rounded">
|
||||
<div className="text-xs text-muted-foreground mb-1">地块信息</div>
|
||||
<div className="font-medium">{selectedLog.fieldName} ({selectedLog.fieldArea}亩)</div>
|
||||
</div>
|
||||
<div className="p-3 bg-gray-50 rounded">
|
||||
<div className="p-3 bg-muted rounded">
|
||||
<div className="text-xs text-muted-foreground mb-1">作物类型</div>
|
||||
<div className="font-medium">{selectedLog.cropType}</div>
|
||||
</div>
|
||||
<div className="p-3 bg-gray-50 rounded">
|
||||
<div className="p-3 bg-muted rounded">
|
||||
<div className="text-xs text-muted-foreground mb-1">执行用户</div>
|
||||
<div className="font-medium">{selectedLog.userName}</div>
|
||||
</div>
|
||||
@@ -1110,7 +1110,7 @@ export function AIAuditLog({ activePath }: AIAuditLogProps) {
|
||||
</div>
|
||||
|
||||
{step.details && (
|
||||
<div className="p-3 bg-gray-50 rounded-lg mb-2">
|
||||
<div className="p-3 bg-muted rounded-lg mb-2">
|
||||
<div className="text-xs font-medium mb-2">步骤详情:</div>
|
||||
{step.details.modelName && (
|
||||
<div className="text-xs mb-1">
|
||||
@@ -1183,7 +1183,7 @@ export function AIAuditLog({ activePath }: AIAuditLogProps) {
|
||||
<div className="space-y-4">
|
||||
<div>
|
||||
<h4 className="mb-2">步骤信息</h4>
|
||||
<div className="p-3 bg-gray-50 rounded">
|
||||
<div className="p-3 bg-muted rounded">
|
||||
<div className="grid grid-cols-2 gap-3 text-sm">
|
||||
<div>步骤名称: {selectedStep.stepName}</div>
|
||||
<div>执行时间: {selectedStep.startTime}</div>
|
||||
|
||||
@@ -710,10 +710,10 @@ export function AIDataCenter({ activePath }: AIDataCenterProps) {
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
<Card className="p-4 bg-gray-50">
|
||||
<Card className="p-4 bg-muted">
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="w-10 h-10 bg-gray-100 rounded-full flex items-center justify-center">
|
||||
<WifiOff className="w-5 h-5 text-gray-600" />
|
||||
<div className="w-10 h-10 bg-muted rounded-full flex items-center justify-center">
|
||||
<WifiOff className="w-5 h-5 text-muted-foreground" />
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-xs text-muted-foreground">离线设备</p>
|
||||
@@ -985,7 +985,7 @@ export function AIDataCenter({ activePath }: AIDataCenterProps) {
|
||||
<h4 className="text-sm font-medium mb-3">已选择的文件</h4>
|
||||
<div className="space-y-2">
|
||||
{uploadedFiles.map((file, index) => (
|
||||
<div key={index} className="flex items-center justify-between p-2 bg-gray-50 rounded">
|
||||
<div key={index} className="flex items-center justify-between p-2 bg-muted rounded">
|
||||
<div className="flex items-center gap-2 flex-1">
|
||||
<FileText className="w-4 h-4 text-blue-600" />
|
||||
<div className="flex-1">
|
||||
@@ -1086,7 +1086,7 @@ export function AIDataCenter({ activePath }: AIDataCenterProps) {
|
||||
</div>
|
||||
|
||||
{/* API认证配置 */}
|
||||
<Card className="p-4 bg-gray-50">
|
||||
<Card className="p-4 bg-muted">
|
||||
<h4 className="text-sm mb-3">API认证配置(可选)</h4>
|
||||
<div className="space-y-3">
|
||||
<div>
|
||||
@@ -1666,28 +1666,28 @@ export function AIDataCenter({ activePath }: AIDataCenterProps) {
|
||||
通用质量控制规则
|
||||
</h4>
|
||||
<div className="space-y-3">
|
||||
<div className="flex items-center justify-between p-3 bg-gray-50 rounded">
|
||||
<div className="flex items-center justify-between p-3 bg-muted rounded">
|
||||
<div>
|
||||
<p className="text-sm font-medium">数据完整性检查</p>
|
||||
<p className="text-xs text-muted-foreground">检测缺失字段和空值</p>
|
||||
</div>
|
||||
<Switch defaultChecked />
|
||||
</div>
|
||||
<div className="flex items-center justify-between p-3 bg-gray-50 rounded">
|
||||
<div className="flex items-center justify-between p-3 bg-muted rounded">
|
||||
<div>
|
||||
<p className="text-sm font-medium">时间戳连续性校验</p>
|
||||
<p className="text-xs text-muted-foreground">确保时间序列数据的连续性</p>
|
||||
</div>
|
||||
<Switch defaultChecked />
|
||||
</div>
|
||||
<div className="flex items-center justify-between p-3 bg-gray-50 rounded">
|
||||
<div className="flex items-center justify-between p-3 bg-muted rounded">
|
||||
<div>
|
||||
<p className="text-sm font-medium">异常值检测 (3σ原则)</p>
|
||||
<p className="text-xs text-muted-foreground">自动标记超出3倍标准差的数据</p>
|
||||
</div>
|
||||
<Switch defaultChecked />
|
||||
</div>
|
||||
<div className="flex items-center justify-between p-3 bg-gray-50 rounded">
|
||||
<div className="flex items-center justify-between p-3 bg-muted rounded">
|
||||
<div>
|
||||
<p className="text-sm font-medium">重复数据检测</p>
|
||||
<p className="text-xs text-muted-foreground">基于时间戳和关键字段去重</p>
|
||||
@@ -2026,7 +2026,7 @@ export function AIDataCenter({ activePath }: AIDataCenterProps) {
|
||||
</div>
|
||||
|
||||
{/* 协议配置信息 */}
|
||||
<div className="mt-4 p-3 bg-gray-50 rounded-md">
|
||||
<div className="mt-4 p-3 bg-muted rounded-md">
|
||||
<h5 className="text-xs font-medium text-muted-foreground mb-2">协议配置</h5>
|
||||
<div className="grid grid-cols-2 gap-3 text-xs">
|
||||
{selectedDevice?.protocol === 'MQTT' && (
|
||||
@@ -2064,7 +2064,7 @@ export function AIDataCenter({ activePath }: AIDataCenterProps) {
|
||||
传感器配置
|
||||
</h4>
|
||||
{selectedDevice?.sensors && selectedDevice.sensors.length > 0 && (
|
||||
<Card className="p-4 bg-gray-50">
|
||||
<Card className="p-4 bg-muted">
|
||||
<div className="grid grid-cols-4 gap-4">
|
||||
<div>
|
||||
<Label className="text-xs">传感器名称</Label>
|
||||
@@ -2307,7 +2307,7 @@ export function AIDataCenter({ activePath }: AIDataCenterProps) {
|
||||
{ time: '2024-10-23 14:28:15', status: 'success', msg: '数据采集成功,所有传感器正常' },
|
||||
{ time: '2024-10-23 14:27:45', status: 'success', msg: '数据采集成功,所有传感器正常' },
|
||||
].map((log, idx) => (
|
||||
<div key={idx} className="flex items-start gap-3 p-2 bg-gray-50 rounded text-xs">
|
||||
<div key={idx} className="flex items-start gap-3 p-2 bg-muted rounded text-xs">
|
||||
<Clock className="w-3 h-3 text-muted-foreground flex-shrink-0 mt-0.5" />
|
||||
<div className="flex-1">
|
||||
<div className="flex items-center gap-2">
|
||||
|
||||
@@ -1356,7 +1356,7 @@ export function AIDecisionDetail({ activePath }: AIDecisionDetailProps) {
|
||||
{expandedSections.has('snapshot') && (
|
||||
<div className="space-y-3">
|
||||
{autoDecisionDetail.dataSnapshot.map((data, idx) => (
|
||||
<div key={idx} className="p-3 bg-gray-50 rounded">
|
||||
<div key={idx} className="p-3 bg-muted rounded">
|
||||
<div className="flex items-center justify-between mb-2">
|
||||
<div className="flex items-center gap-2">
|
||||
<Database className="w-4 h-4 text-blue-600" />
|
||||
@@ -1566,7 +1566,7 @@ export function AIDecisionDetail({ activePath }: AIDecisionDetailProps) {
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<div>
|
||||
<div className="text-sm text-muted-foreground mb-2">执行参数</div>
|
||||
<div className="p-3 bg-gray-50 rounded space-y-1 text-sm">
|
||||
<div className="p-3 bg-muted rounded space-y-1 text-sm">
|
||||
{Object.entries(autoDecisionDetail.finalDecision.parameters).map(([key, value]) => (
|
||||
<div key={key}>
|
||||
<span className="text-muted-foreground">{key}:</span>
|
||||
@@ -1599,7 +1599,7 @@ export function AIDecisionDetail({ activePath }: AIDecisionDetailProps) {
|
||||
<div className="text-sm text-muted-foreground mb-2">备选方案</div>
|
||||
<div className="space-y-2">
|
||||
{autoDecisionDetail.finalDecision.alternatives.map((alt, i) => (
|
||||
<div key={i} className="p-3 bg-gray-50 rounded text-sm">
|
||||
<div key={i} className="p-3 bg-muted rounded text-sm">
|
||||
{alt}
|
||||
</div>
|
||||
))}
|
||||
@@ -1786,7 +1786,7 @@ export function AIDecisionDetail({ activePath }: AIDecisionDetailProps) {
|
||||
{manualDecisionDetail.manualInput.explanation && (
|
||||
<div>
|
||||
<div className="text-sm text-muted-foreground mb-2">详细说明</div>
|
||||
<div className="p-4 bg-gray-50 rounded border">
|
||||
<div className="p-4 bg-muted rounded border">
|
||||
<p className="text-sm whitespace-pre-line">{manualDecisionDetail.manualInput.explanation}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -675,7 +675,7 @@ export function AIDecisionGeneration({ activePath }: AIDecisionGenerationProps)
|
||||
</div>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<code className="text-xs bg-gray-100 px-2 py-1 rounded">
|
||||
<code className="text-xs bg-muted px-2 py-1 rounded">
|
||||
{rule.condition.substring(0, 30)}...
|
||||
</code>
|
||||
</TableCell>
|
||||
@@ -898,7 +898,7 @@ export function AIDecisionGeneration({ activePath }: AIDecisionGenerationProps)
|
||||
</div>
|
||||
<div className="divide-y">
|
||||
{decisionRecords.map((record) => (
|
||||
<div key={record.id} className="p-4 hover:bg-gray-50 transition-colors">
|
||||
<div key={record.id} className="p-4 hover:bg-accent transition-colors">
|
||||
<div className="flex items-start justify-between mb-3">
|
||||
<div className="flex-1">
|
||||
<div className="flex items-center gap-2 mb-1">
|
||||
@@ -960,7 +960,7 @@ export function AIDecisionGeneration({ activePath }: AIDecisionGenerationProps)
|
||||
<p className="text-sm">{record.finalDecision}</p>
|
||||
</div>
|
||||
|
||||
<div className="p-3 bg-gray-50 rounded">
|
||||
<div className="p-3 bg-muted rounded">
|
||||
<p className="text-xs text-muted-foreground mb-1">推理过程</p>
|
||||
<p className="text-xs">{record.reasoning}</p>
|
||||
<div className="flex gap-2 mt-2">
|
||||
@@ -1309,7 +1309,7 @@ export function AIDecisionGeneration({ activePath }: AIDecisionGenerationProps)
|
||||
|
||||
<div>
|
||||
<Label>推理过程</Label>
|
||||
<div className="field-value bg-gray-50">
|
||||
<div className="field-value">
|
||||
{selectedDecision.reasoning}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1001,7 +1001,7 @@ export function AIDecisionLog({ activePath }: AIDecisionLogProps) {
|
||||
{filteredLogs.map((log) => (
|
||||
<TableRow key={log.id}>
|
||||
<TableCell>
|
||||
<code className="text-xs bg-gray-100 px-2 py-1 rounded">{log.id}</code>
|
||||
<code className="text-xs bg-muted px-2 py-1 rounded">{log.id}</code>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<div className="text-sm">{log.timestamp}</div>
|
||||
@@ -1082,7 +1082,7 @@ export function AIDecisionLog({ activePath }: AIDecisionLogProps) {
|
||||
<div className="grid grid-cols-4 gap-4 text-sm">
|
||||
<div>
|
||||
<div className="text-xs text-muted-foreground mb-1">日志ID</div>
|
||||
<code className="text-xs bg-gray-100 px-2 py-1 rounded">{selectedLog.id}</code>
|
||||
<code className="text-xs bg-muted px-2 py-1 rounded">{selectedLog.id}</code>
|
||||
</div>
|
||||
<div>
|
||||
<div className="text-xs text-muted-foreground mb-1">生成时间</div>
|
||||
@@ -1115,7 +1115,7 @@ export function AIDecisionLog({ activePath }: AIDecisionLogProps) {
|
||||
<h4>触发信息</h4>
|
||||
</button>
|
||||
{expandedSections.has('trigger') && (
|
||||
<div className="p-4 bg-gray-50 rounded-lg space-y-2">
|
||||
<div className="p-4 bg-muted rounded-lg space-y-2">
|
||||
<div className="grid grid-cols-2 gap-4 text-sm">
|
||||
<div>
|
||||
<span className="text-muted-foreground">触发源:</span>
|
||||
@@ -1157,7 +1157,7 @@ export function AIDecisionLog({ activePath }: AIDecisionLogProps) {
|
||||
</Badge>
|
||||
</button>
|
||||
{expandedSections.has('input') && (
|
||||
<div className="p-4 bg-gray-50 rounded-lg">
|
||||
<div className="p-4 bg-muted rounded-lg">
|
||||
<div className="grid grid-cols-3 gap-3 text-sm">
|
||||
{Object.entries(selectedLog.inputData).map(([key, value]) => (
|
||||
<div key={key} className="p-3 bg-white rounded border">
|
||||
|
||||
@@ -838,7 +838,7 @@ export function AIDecisionSimulation({ activePath }: AIDecisionSimulationProps)
|
||||
{simulationResults.map((result) => (
|
||||
<TableRow key={result.id}>
|
||||
<TableCell>
|
||||
<code className="text-xs bg-gray-100 px-2 py-1 rounded">{result.id}</code>
|
||||
<code className="text-xs bg-muted px-2 py-1 rounded">{result.id}</code>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<div className="font-medium">{result.scenarioName}</div>
|
||||
@@ -1369,7 +1369,7 @@ export function AIDecisionSimulation({ activePath }: AIDecisionSimulationProps)
|
||||
<span className="font-medium">{selectedResult.evaluation.practicality}分</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="p-3 bg-gray-50 rounded-lg mt-3">
|
||||
<div className="p-3 bg-muted rounded-lg mt-3">
|
||||
<div className="text-sm text-muted-foreground">{selectedResult.evaluation.feedback}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -617,7 +617,7 @@ export function AIDecisionSupport({ activePath }: AIDecisionSupportProps) {
|
||||
decision.decisionLevel === '紧急' ? 'border-l-red-500 bg-red-50/50' :
|
||||
decision.decisionLevel === '重要' ? 'border-l-orange-500 bg-orange-50/50' :
|
||||
decision.decisionLevel === '一般' ? 'border-l-blue-500 bg-blue-50/50' :
|
||||
'border-l-gray-500 bg-gray-50/50'
|
||||
'border-l-gray-500 bg-muted'
|
||||
}`}>
|
||||
<div className="flex items-start justify-between mb-3">
|
||||
<div className="flex items-center gap-2">
|
||||
@@ -737,7 +737,7 @@ export function AIDecisionSupport({ activePath }: AIDecisionSupportProps) {
|
||||
<Card className="p-4">
|
||||
<h4 className="mb-4 flex items-center gap-2">
|
||||
<BarChart3 className="w-4 h-4 text-purple-600" />
|
||||
决策执行率
|
||||
决策<EFBFBD><EFBFBD>行率
|
||||
</h4>
|
||||
<ResponsiveContainer width="100%" height={250}>
|
||||
<BarChart data={executionRate} layout="vertical">
|
||||
@@ -942,7 +942,7 @@ export function AIDecisionSupport({ activePath }: AIDecisionSupportProps) {
|
||||
</h4>
|
||||
<div className="space-y-4">
|
||||
{selectedDecision.ruleLogic.map((rule, idx) => (
|
||||
<Card key={idx} className={`p-4 ${rule.matched ? 'bg-green-50 border-green-200' : 'bg-gray-50'}`}>
|
||||
<Card key={idx} className={`p-4 ${rule.matched ? 'bg-green-50 border-green-200' : 'bg-muted'}`}>
|
||||
<div className="flex items-start justify-between mb-3">
|
||||
<div className="flex items-center gap-2">
|
||||
<div className={`w-6 h-6 rounded-full flex items-center justify-center ${
|
||||
@@ -1002,7 +1002,7 @@ export function AIDecisionSupport({ activePath }: AIDecisionSupportProps) {
|
||||
|
||||
<div className="mb-4">
|
||||
<Label>推理过程</Label>
|
||||
<div className="field-value bg-gray-50">
|
||||
<div className="field-value">
|
||||
<p className="text-sm leading-relaxed">{selectedDecision.reasoning}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -3,13 +3,13 @@ import { Card } from '../ui/card';
|
||||
import { Button } from '../ui/button';
|
||||
import { Input } from '../ui/input';
|
||||
import { Label } from '../ui/label';
|
||||
import { Textarea } from '../ui/textarea';
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '../ui/select';
|
||||
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription, DialogFooter } from '../ui/dialog';
|
||||
import { Avatar, AvatarFallback, AvatarImage } from '../ui/avatar';
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from '../ui/tabs';
|
||||
import { Badge } from '../ui/badge';
|
||||
import { UserProfile, PasswordChange } from '../../types/profile';
|
||||
import { User, Mail, Phone, Building, Briefcase, Lock, Save, Shield } from 'lucide-react';
|
||||
import { User, Mail, Phone, Building, Briefcase, Lock, Save, Shield, CheckCircle, XCircle, Clock } from 'lucide-react';
|
||||
import { toast } from 'sonner@2.0.3';
|
||||
|
||||
export function PersonalInfo() {
|
||||
@@ -30,6 +30,8 @@ export function PersonalInfo() {
|
||||
roleNames: ['超级管理员'],
|
||||
bio: '负责系统整体架构和技术管理',
|
||||
address: '北京市海淀区中关村大街1号',
|
||||
// 账户状态:'pending'待审核(企业名称可编辑)、'approved'审核通过(企业名称只读)、'rejected'驳回(企业名称只读)
|
||||
status: 'approved', // 默认审核通过
|
||||
createdAt: '2024-01-01T00:00:00',
|
||||
lastLoginTime: '2024-10-14T09:30:00',
|
||||
lastLoginIp: '192.168.1.100',
|
||||
@@ -111,12 +113,51 @@ export function PersonalInfo() {
|
||||
setHasChanges(true);
|
||||
};
|
||||
|
||||
// 获取状态配置
|
||||
const getStatusConfig = (status?: string) => {
|
||||
switch (status) {
|
||||
case 'pending':
|
||||
return {
|
||||
label: '待审核',
|
||||
icon: Clock,
|
||||
className: 'bg-yellow-100 text-yellow-800 border-yellow-200',
|
||||
};
|
||||
case 'approved':
|
||||
return {
|
||||
label: '审核通过',
|
||||
icon: CheckCircle,
|
||||
className: 'bg-green-100 text-green-800 border-green-200',
|
||||
};
|
||||
case 'rejected':
|
||||
return {
|
||||
label: '驳回',
|
||||
icon: XCircle,
|
||||
className: 'bg-red-100 text-red-800 border-red-200',
|
||||
};
|
||||
default:
|
||||
return {
|
||||
label: '待审核',
|
||||
icon: Clock,
|
||||
className: 'bg-gray-100 text-gray-800 border-gray-200',
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
const statusConfig = getStatusConfig(profile.status);
|
||||
const StatusIcon = statusConfig.icon;
|
||||
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<h2 className="text-green-800">个人信息</h2>
|
||||
<p className="text-muted-foreground">查看和维护个人账户信息</p>
|
||||
<div className="flex items-center gap-4">
|
||||
<div>
|
||||
<h2 className="text-green-800">个人信息</h2>
|
||||
<p className="text-muted-foreground">查看和维护个人账户信息</p>
|
||||
</div>
|
||||
<div className={`flex items-center gap-2 px-3 py-1 rounded-lg border ${statusConfig.className}`}>
|
||||
<StatusIcon className="w-4 h-4" />
|
||||
<span className="text-sm">用户状态:{statusConfig.label}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex gap-2">
|
||||
<Button variant="outline" onClick={() => setShowPasswordDialog(true)}>
|
||||
@@ -231,23 +272,6 @@ export function PersonalInfo() {
|
||||
onChange={(e) => updateProfile({ birthday: e.target.value })}
|
||||
/>
|
||||
</div>
|
||||
<div className="md:col-span-2">
|
||||
<Label>个人简介</Label>
|
||||
<Textarea
|
||||
value={profile.bio}
|
||||
onChange={(e) => updateProfile({ bio: e.target.value })}
|
||||
placeholder="介绍一下自己"
|
||||
rows={3}
|
||||
/>
|
||||
</div>
|
||||
<div className="md:col-span-2">
|
||||
<Label>地址</Label>
|
||||
<Input
|
||||
value={profile.address}
|
||||
onChange={(e) => updateProfile({ address: e.target.value })}
|
||||
placeholder="请输入地址"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
@@ -263,36 +287,39 @@ export function PersonalInfo() {
|
||||
<Building className="absolute left-3 top-1/2 transform -translate-y-1/2 w-4 h-4 text-muted-foreground" />
|
||||
<Input
|
||||
value={profile.enterpriseName}
|
||||
disabled
|
||||
className="pl-10 bg-gray-50"
|
||||
onChange={(e) => updateProfile({ enterpriseName: e.target.value })}
|
||||
disabled={profile.status === 'approved'}
|
||||
className={`pl-10 ${profile.status === 'approved' ? 'bg-muted' : ''}`}
|
||||
placeholder={profile.status !== 'approved' ? '请输入企业名称' : ''}
|
||||
/>
|
||||
</div>
|
||||
{profile.status === 'pending' ? (
|
||||
<p className="text-xs text-yellow-600 dark:text-yellow-500 mt-1">待审核期间可修改企业信息</p>
|
||||
) : profile.status === 'rejected' ? (
|
||||
<p className="text-xs text-red-600 dark:text-red-500 mt-1">驳回后可修改企业信息重新提交</p>
|
||||
) : (
|
||||
<p className="text-xs text-muted-foreground mt-1">企业信息由管理员维护</p>
|
||||
)}
|
||||
</div>
|
||||
<div>
|
||||
<Label>部门</Label>
|
||||
<Input
|
||||
value={profile.department}
|
||||
onChange={(e) => updateProfile({ department: e.target.value })}
|
||||
placeholder="请输入部门"
|
||||
disabled
|
||||
className="bg-muted"
|
||||
/>
|
||||
<p className="text-xs text-muted-foreground mt-1">部门信息由管理员维护</p>
|
||||
</div>
|
||||
<div>
|
||||
<Label>职位</Label>
|
||||
<Input
|
||||
value={profile.position}
|
||||
onChange={(e) => updateProfile({ position: e.target.value })}
|
||||
placeholder="请输入职位"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<div className="md:col-span-2">
|
||||
<Label>角色</Label>
|
||||
<div className="flex flex-wrap gap-2 pt-2">
|
||||
<div className="flex flex-wrap gap-2 mt-2">
|
||||
{profile.roleNames.map((role, index) => (
|
||||
<div key={index} className="px-3 py-1 bg-green-100 text-green-700 rounded-full text-sm">
|
||||
<Badge key={index} variant="outline" className="px-3 py-1 bg-green-100 text-green-700 border-green-300 dark:bg-green-900/30 dark:text-green-400 dark:border-green-700">
|
||||
{role}
|
||||
</div>
|
||||
</Badge>
|
||||
))}
|
||||
</div>
|
||||
<p className="text-xs text-muted-foreground mt-1">角色信息由管理员分配</p>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
@@ -405,14 +432,16 @@ export function PersonalInfo() {
|
||||
</Dialog>
|
||||
|
||||
{/* 使用说明 */}
|
||||
<Card className="p-4 bg-blue-50 border-blue-200">
|
||||
<h4 className="text-blue-900 mb-2">
|
||||
<Card className="p-4 bg-blue-50 dark:bg-blue-950/30 border-blue-200 dark:border-blue-900">
|
||||
<h4 className="text-blue-900 dark:text-blue-400 mb-2">
|
||||
<User className="w-4 h-4 inline mr-2" />
|
||||
个人信息说明
|
||||
</h4>
|
||||
<ul className="space-y-1 text-sm text-blue-800">
|
||||
<ul className="space-y-1 text-sm text-blue-800 dark:text-blue-300">
|
||||
<li>• 修改个人信息后需要点击"保存修改"按钮才会生效</li>
|
||||
<li>• 用户名和企业信息由系统管理员管理,个人无法修改</li>
|
||||
<li>• 待审核和驳回状态下可以修改企业名称,审核通过后由系统管理员管理</li>
|
||||
<li>• 用户名、部门和角色由系统管理员管理,个人无法修改</li>
|
||||
<li>• 用户状态显示当前审核状态,影响系统功能使用权限</li>
|
||||
<li>• 定期修改密码可以提高账户安全性</li>
|
||||
<li>• 密码修改成功后需要重新登录</li>
|
||||
<li>• 所有个人信息修改都会记录到安全日志中</li>
|
||||
|
||||
@@ -770,7 +770,7 @@ export function OperationCalendar({ activePath }: OperationCalendarProps) {
|
||||
{/* 筛选结果提示 */}
|
||||
{(filterField !== 'all' || filterCrop !== 'all' || filterExecutor !== 'all' || filterType !== 'all' || filterStatus !== 'all') && (
|
||||
<div className="flex items-center gap-2 text-sm">
|
||||
<Badge variant="outline" className="bg-green-50">
|
||||
<Badge variant="outline">
|
||||
当前筛选: {filteredTasks.length} 个任务
|
||||
</Badge>
|
||||
{filterField !== 'all' && (
|
||||
@@ -847,7 +847,7 @@ export function OperationCalendar({ activePath }: OperationCalendarProps) {
|
||||
{/* 日历网格 */}
|
||||
<div className="border rounded-lg overflow-hidden">
|
||||
{/* 星期标题 */}
|
||||
<div className="grid grid-cols-7 bg-gray-100">
|
||||
<div className="grid grid-cols-7 bg-muted">
|
||||
{weekDays.map(day => (
|
||||
<div key={day} className="p-3 text-center text-sm font-medium border-r last:border-r-0">
|
||||
星期{day}
|
||||
@@ -869,7 +869,7 @@ export function OperationCalendar({ activePath }: OperationCalendarProps) {
|
||||
<div
|
||||
key={index}
|
||||
className={`min-h-[120px] p-2 border-r border-b last:border-r-0 ${
|
||||
!isCurrentMonth ? 'bg-gray-50' : 'bg-white'
|
||||
!isCurrentMonth ? 'bg-muted' : 'bg-card'
|
||||
} ${isToday ? 'ring-2 ring-green-500' : ''}`}
|
||||
onDrop={(e) => handleDrop(day, e)}
|
||||
onDragOver={handleDragOver}
|
||||
@@ -907,10 +907,10 @@ export function OperationCalendar({ activePath }: OperationCalendarProps) {
|
||||
</Card>
|
||||
|
||||
{/* 说明 */}
|
||||
<Card className="p-4 bg-blue-50 border-blue-200">
|
||||
<Card className="p-4">
|
||||
<div className="flex items-start gap-2">
|
||||
<Zap className="w-5 h-5 text-blue-600 flex-shrink-0 mt-0.5" />
|
||||
<div className="text-sm text-blue-900">
|
||||
<div className="text-sm">
|
||||
<p className="mb-2">可视化日历功能(当前展示 {filteredTasks.length} 个任务):</p>
|
||||
<ul className="space-y-1 text-xs">
|
||||
<li>• <strong>拖拽调整</strong>: 直接拖拽任务卡片到新日期,快速调整任务时间</li>
|
||||
@@ -1022,10 +1022,10 @@ export function OperationCalendar({ activePath }: OperationCalendarProps) {
|
||||
</Card>
|
||||
|
||||
{/* 甘特图说明 */}
|
||||
<Card className="p-4 bg-green-50 border-green-200">
|
||||
<Card className="p-4">
|
||||
<div className="flex items-start gap-2">
|
||||
<BarChart3 className="w-5 h-5 text-green-600 flex-shrink-0 mt-0.5" />
|
||||
<div className="text-sm text-green-900">
|
||||
<div className="text-sm">
|
||||
<p className="mb-2">甘特图功能(当前展示 {filteredTasks.length} 个任务的时间轴):</p>
|
||||
<ul className="space-y-1 text-xs">
|
||||
<li>• <strong>时间轴展示</strong>: 横向展示所有农事活动的时间安排和持续周期,一眼看清整月安排</li>
|
||||
@@ -1055,7 +1055,7 @@ export function OperationCalendar({ activePath }: OperationCalendarProps) {
|
||||
<Card key={field.id} className="p-6">
|
||||
<div className="flex items-center justify-between mb-4">
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="p-3 bg-green-100 rounded-lg">
|
||||
<div className="p-3 bg-green-50 rounded-lg">
|
||||
<MapPin className="w-6 h-6 text-green-600" />
|
||||
</div>
|
||||
<div>
|
||||
@@ -1088,7 +1088,7 @@ export function OperationCalendar({ activePath }: OperationCalendarProps) {
|
||||
: 0}%
|
||||
</span>
|
||||
</div>
|
||||
<div className="h-3 bg-gray-200 rounded-full overflow-hidden flex">
|
||||
<div className="h-3 bg-muted rounded-full overflow-hidden flex">
|
||||
<div
|
||||
className="bg-blue-500 transition-all"
|
||||
style={{
|
||||
@@ -1141,7 +1141,7 @@ export function OperationCalendar({ activePath }: OperationCalendarProps) {
|
||||
<span className="text-muted-foreground">完成度</span>
|
||||
<span className="font-medium">{task.progress}%</span>
|
||||
</div>
|
||||
<div className="h-2 bg-gray-200 rounded-full overflow-hidden">
|
||||
<div className="h-2 bg-muted rounded-full overflow-hidden">
|
||||
<div
|
||||
className="h-full transition-all"
|
||||
style={{
|
||||
@@ -1166,10 +1166,10 @@ export function OperationCalendar({ activePath }: OperationCalendarProps) {
|
||||
</div>
|
||||
|
||||
{/* 进度说明 */}
|
||||
<Card className="p-4 bg-purple-50 border-purple-200">
|
||||
<Card className="p-4">
|
||||
<div className="flex items-start gap-2">
|
||||
<TrendingUp className="w-5 h-5 text-purple-600 flex-shrink-0 mt-0.5" />
|
||||
<div className="text-sm text-purple-900">
|
||||
<div className="text-sm">
|
||||
<p className="mb-2">进度状态可视化:</p>
|
||||
<ul className="space-y-1 text-xs">
|
||||
<li>• <strong>色块展示</strong>: 🔵 蓝色-待开始、🟢 绿色-进行中、⚫ 灰色-已完成</li>
|
||||
|
||||
@@ -1920,7 +1920,7 @@ export function OperationPlanning({ activePath }: OperationPlanningProps) {
|
||||
|
||||
{/* 甘特图 */}
|
||||
{plan.activities.length > 0 && (
|
||||
<div className="p-4 bg-gray-50 rounded-lg">
|
||||
<div className="p-4 bg-muted rounded-lg">
|
||||
<h4 className="mb-3 flex items-center gap-2">
|
||||
<Activity className="w-4 h-4 text-blue-600" />
|
||||
农事活动时间轴
|
||||
@@ -1939,7 +1939,7 @@ export function OperationPlanning({ activePath }: OperationPlanningProps) {
|
||||
{activity.startDate} ~ {activity.endDate} ({activity.duration}天)
|
||||
</span>
|
||||
</div>
|
||||
<div className="relative h-8 bg-white rounded border">
|
||||
<div className="relative h-8 bg-background rounded border">
|
||||
<div
|
||||
className="absolute h-full rounded flex items-center px-2 text-xs text-white"
|
||||
style={{
|
||||
@@ -2545,7 +2545,7 @@ export function OperationPlanning({ activePath }: OperationPlanningProps) {
|
||||
|
||||
{/* 甘特图可视化 */}
|
||||
{editingActivities.length > 0 && editingActivities[0].startDate && (
|
||||
<Card className="p-6 bg-gray-50">
|
||||
<Card className="p-6 bg-muted">
|
||||
{/* 月份选择器 */}
|
||||
<div className="flex items-center justify-between mb-4">
|
||||
<div>
|
||||
@@ -2566,7 +2566,7 @@ export function OperationPlanning({ activePath }: OperationPlanningProps) {
|
||||
>
|
||||
<ChevronRight className="w-4 h-4 rotate-180" />
|
||||
</Button>
|
||||
<div className="px-4 py-1 bg-white rounded border min-w-[120px] text-center">
|
||||
<div className="px-4 py-1 bg-background rounded border min-w-[120px] text-center">
|
||||
<span className="text-sm font-medium">
|
||||
{selectedMonth.getFullYear()}年{selectedMonth.getMonth() + 1}月
|
||||
</span>
|
||||
@@ -2590,7 +2590,7 @@ export function OperationPlanning({ activePath }: OperationPlanningProps) {
|
||||
</div>
|
||||
|
||||
{/* 日期刻度 */}
|
||||
<div className={`mb-2 relative h-8 bg-white rounded border overflow-hidden transition-all ${
|
||||
<div className={`mb-2 relative h-8 bg-background rounded border overflow-hidden transition-all ${
|
||||
isTimelineDragging ? 'ring-2 ring-blue-500 shadow-lg' : ''
|
||||
}`}>
|
||||
<div className="flex h-full">
|
||||
@@ -2604,12 +2604,8 @@ export function OperationPlanning({ activePath }: OperationPlanningProps) {
|
||||
<div
|
||||
key={index}
|
||||
className={`flex-1 border-r last:border-r-0 flex items-center justify-center text-xs transition-colors ${
|
||||
isToday ? 'bg-blue-100' : ''
|
||||
isToday ? 'bg-blue-100' : (date.getDay() === 0 || date.getDay() === 6 ? 'bg-muted' : '')
|
||||
}`}
|
||||
style={{
|
||||
backgroundColor: isToday ? '#dbeafe' :
|
||||
(date.getDay() === 0 || date.getDay() === 6 ? '#f9fafb' : '#fff'),
|
||||
}}
|
||||
>
|
||||
<div className="text-center">
|
||||
<div className={`${isToday ? 'text-blue-600 font-bold' : 'text-muted-foreground'}`}>
|
||||
@@ -2635,7 +2631,7 @@ export function OperationPlanning({ activePath }: OperationPlanningProps) {
|
||||
{/* 甘特图活动条 */}
|
||||
<div
|
||||
ref={ganttRef}
|
||||
className="space-y-3 select-none relative"
|
||||
className="space-y-3 select-none relative p-4 rounded-lg bg-card"
|
||||
onMouseDown={handleTimelineDragStart}
|
||||
onMouseMove={(e) => {
|
||||
handleTimelineDragMove(e);
|
||||
@@ -2689,7 +2685,7 @@ export function OperationPlanning({ activePath }: OperationPlanningProps) {
|
||||
{activity.startDate} ~ {activity.endDate} ({activity.duration}天)
|
||||
</span>
|
||||
</div>
|
||||
<div className="relative h-10 bg-white rounded border">
|
||||
<div className="relative h-10 bg-background rounded border">
|
||||
<div
|
||||
className="gantt-activity-bar absolute h-full rounded flex items-center px-2 text-xs text-white transition-all group"
|
||||
style={{
|
||||
@@ -2820,7 +2816,7 @@ export function OperationPlanning({ activePath }: OperationPlanningProps) {
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<div className="p-3 bg-gray-50 rounded-lg">
|
||||
<div className="p-3 bg-muted rounded-lg">
|
||||
<p className="text-xs text-muted-foreground mb-2">包含活动:</p>
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{template.activities.map(act => (
|
||||
@@ -2858,7 +2854,7 @@ export function OperationPlanning({ activePath }: OperationPlanningProps) {
|
||||
</DialogHeader>
|
||||
{selectedWorkOrder && (
|
||||
<div className="space-y-4">
|
||||
<div className="p-4 bg-gray-50 rounded-lg">
|
||||
<div className="p-4 bg-muted rounded-lg">
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<div>
|
||||
<p className="text-xs text-muted-foreground">工单编号</p>
|
||||
@@ -3090,11 +3086,11 @@ export function OperationPlanning({ activePath }: OperationPlanningProps) {
|
||||
创建信息
|
||||
</h4>
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<div className="p-4 bg-gray-50 rounded-lg">
|
||||
<div className="p-4 bg-muted rounded-lg">
|
||||
<p className="text-xs text-muted-foreground">创建人</p>
|
||||
<p className="text-sm font-medium mt-1">{selectedPlan.createdBy}</p>
|
||||
</div>
|
||||
<div className="p-4 bg-gray-50 rounded-lg">
|
||||
<div className="p-4 bg-muted rounded-lg">
|
||||
<p className="text-xs text-muted-foreground">创建时间</p>
|
||||
<p className="text-sm font-medium mt-1">{selectedPlan.createdAt}</p>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user