设备基本信息设置

This commit is contained in:
柳清爽
2025-03-31 17:33:05 +08:00
parent dbf20fd870
commit 1cb8e9f397
5 changed files with 242 additions and 10 deletions

View File

@@ -33,6 +33,22 @@ export const fetchDeviceDetail = async (id: string | number): Promise<ApiRespons
return api.get<ApiResponse<any>>(`/v1/devices/${id}`);
};
// 更新设备任务配置
export const updateDeviceTaskConfig = async (
id: string | number,
config: {
autoAddFriend?: boolean;
autoReply?: boolean;
momentsSync?: boolean;
aiChat?: boolean;
}
): Promise<ApiResponse<any>> => {
return api.post<ApiResponse<any>>(`/v1/devices/task-config`, {
id,
...config
});
};
// 删除设备
export const deleteDevice = async (id: number): Promise<ApiResponse<any>> => {
return api.delete<ApiResponse<any>>(`/v1/devices/${id}`);

View File

@@ -10,7 +10,7 @@ import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"
import { Switch } from "@/components/ui/switch"
import { Label } from "@/components/ui/label"
import { ScrollArea } from "@/components/ui/scroll-area"
import { fetchDeviceDetail } from "@/api/devices"
import { fetchDeviceDetail, updateDeviceTaskConfig } from "@/api/devices"
import { toast } from "sonner"
interface WechatAccount {
@@ -37,7 +37,7 @@ interface Device {
features: {
autoAddFriend: boolean
autoReply: boolean
contentSync: boolean
momentsSync: boolean
aiChat: boolean
}
history: {
@@ -68,6 +68,12 @@ export default function DeviceDetailPage() {
const [device, setDevice] = useState<Device | null>(null)
const [activeTab, setActiveTab] = useState("info")
const [loading, setLoading] = useState(true)
const [savingFeatures, setSavingFeatures] = useState({
autoAddFriend: false,
autoReply: false,
momentsSync: false,
aiChat: false
})
useEffect(() => {
if (!params.id) return
@@ -91,16 +97,47 @@ export default function DeviceDetailPage() {
historicalIds: [], // 服务端暂无此数据
wechatAccounts: [], // 默认空数组
history: [], // 服务端暂无此数据
features: serverData.features || {
features: {
autoAddFriend: false,
autoReply: false,
contentSync: false,
momentsSync: false,
aiChat: false
},
totalFriend: serverData.totalFriend || 0,
thirtyDayMsgCount: serverData.thirtyDayMsgCount || 0
}
// 解析features
if (serverData.features) {
// 如果后端直接返回了features对象使用它
formattedDevice.features = {
autoAddFriend: Boolean(serverData.features.autoAddFriend),
autoReply: Boolean(serverData.features.autoReply),
momentsSync: Boolean(serverData.features.momentsSync || serverData.features.contentSync),
aiChat: Boolean(serverData.features.aiChat)
}
} else if (serverData.taskConfig) {
try {
// 解析taskConfig字段
let taskConfig = serverData.taskConfig
if (typeof taskConfig === 'string') {
taskConfig = JSON.parse(taskConfig)
}
if (taskConfig) {
console.log('解析的taskConfig:', taskConfig);
formattedDevice.features = {
autoAddFriend: Boolean(taskConfig.autoAddFriend),
autoReply: Boolean(taskConfig.autoReply),
momentsSync: Boolean(taskConfig.momentsSync),
aiChat: Boolean(taskConfig.aiChat)
}
}
} catch (err) {
console.error('解析taskConfig失败:', err)
}
}
// 如果有微信账号信息,构建微信账号对象
if (serverData.wechatId) {
formattedDevice.wechatAccounts = [
@@ -169,7 +206,7 @@ export default function DeviceDetailPage() {
features: {
autoAddFriend: true,
autoReply: true,
contentSync: false,
momentsSync: false,
aiChat: true,
},
history: [
@@ -193,6 +230,74 @@ export default function DeviceDetailPage() {
fetchDevice()
}, [params.id])
// 处理功能开关状态变化
const handleFeatureChange = async (feature: keyof Device['features'], checked: boolean) => {
if (!device) return
// 避免已经在处理中的功能被重复触发
if (savingFeatures[feature]) {
return
}
setSavingFeatures(prev => ({ ...prev, [feature]: true }))
try {
// 准备更新后的功能状态
const updatedFeatures = { ...device.features, [feature]: checked }
// 创建API请求参数
const configUpdate = { [feature]: checked }
// 立即更新UI状态提供即时反馈
setDevice(prev => prev ? {
...prev,
features: updatedFeatures
} : null)
// 调用API更新服务器配置
const response = await updateDeviceTaskConfig(device.id, configUpdate)
if (response && response.code === 200) {
toast.success(`${getFeatureName(feature)}${checked ? '已启用' : '已禁用'}`)
} else {
// 如果请求失败回滚UI变更
setDevice(prev => prev ? {
...prev,
features: { ...prev.features, [feature]: !checked }
} : null)
// 处理错误信息,使用类型断言解决字段不一致问题
const anyResponse = response as any;
const errorMsg = anyResponse ? (anyResponse.message || anyResponse.msg || '未知错误') : '未知错误';
toast.error(`更新失败: ${errorMsg}`)
}
} catch (error) {
console.error(`更新${getFeatureName(feature)}失败:`, error)
// 异常情况下也回滚UI变更
setDevice(prev => prev ? {
...prev,
features: { ...prev.features, [feature]: !checked }
} : null)
toast.error('更新失败,请稍后重试')
} finally {
setSavingFeatures(prev => ({ ...prev, [feature]: false }))
}
}
// 获取功能中文名称
const getFeatureName = (feature: string): string => {
const nameMap: Record<string, string> = {
autoAddFriend: '自动加好友',
autoReply: '自动回复',
momentsSync: '朋友圈同步',
aiChat: 'AI会话'
}
return nameMap[feature] || feature
}
if (loading || !device) {
return <div>...</div>
}
@@ -261,28 +366,68 @@ export default function DeviceDetailPage() {
<Label></Label>
<div className="text-sm text-gray-500"></div>
</div>
<Switch checked={device.features.autoAddFriend} />
<div className="flex items-center">
{savingFeatures.autoAddFriend && (
<div className="w-4 h-4 mr-2 rounded-full border-2 border-blue-500 border-t-transparent animate-spin"></div>
)}
<Switch
checked={Boolean(device.features.autoAddFriend)}
onCheckedChange={(checked) => handleFeatureChange('autoAddFriend', checked)}
disabled={savingFeatures.autoAddFriend}
className="data-[state=checked]:bg-blue-500 transition-all duration-200"
/>
</div>
</div>
<div className="flex items-center justify-between">
<div className="space-y-0.5">
<Label></Label>
<div className="text-sm text-gray-500"></div>
</div>
<Switch checked={device.features.autoReply} />
<div className="flex items-center">
{savingFeatures.autoReply && (
<div className="w-4 h-4 mr-2 rounded-full border-2 border-blue-500 border-t-transparent animate-spin"></div>
)}
<Switch
checked={Boolean(device.features.autoReply)}
onCheckedChange={(checked) => handleFeatureChange('autoReply', checked)}
disabled={savingFeatures.autoReply}
className="data-[state=checked]:bg-blue-500 transition-all duration-200"
/>
</div>
</div>
<div className="flex items-center justify-between">
<div className="space-y-0.5">
<Label></Label>
<div className="text-sm text-gray-500"></div>
</div>
<Switch checked={device.features.contentSync} />
<div className="flex items-center">
{savingFeatures.momentsSync && (
<div className="w-4 h-4 mr-2 rounded-full border-2 border-blue-500 border-t-transparent animate-spin"></div>
)}
<Switch
checked={Boolean(device.features.momentsSync)}
onCheckedChange={(checked) => handleFeatureChange('momentsSync', checked)}
disabled={savingFeatures.momentsSync}
className="data-[state=checked]:bg-blue-500 transition-all duration-200"
/>
</div>
</div>
<div className="flex items-center justify-between">
<div className="space-y-0.5">
<Label>AI会话</Label>
<div className="text-sm text-gray-500">AI智能对话</div>
</div>
<Switch checked={device.features.aiChat} />
<div className="flex items-center">
{savingFeatures.aiChat && (
<div className="w-4 h-4 mr-2 rounded-full border-2 border-blue-500 border-t-transparent animate-spin"></div>
)}
<Switch
checked={Boolean(device.features.aiChat)}
onCheckedChange={(checked) => handleFeatureChange('aiChat', checked)}
disabled={savingFeatures.aiChat}
className="data-[state=checked]:bg-blue-500 transition-all duration-200"
/>
</div>
</div>
</div>
</Card>

View File

@@ -16,6 +16,7 @@ Route::group('v1/', function () {
Route::post('', 'app\\devices\\controller\\Device@save'); // 添加设备
Route::put('refresh', 'app\\devices\\controller\\Device@refresh'); // 刷新设备状态
Route::delete(':id', 'app\\devices\\controller\\Device@delete'); // 删除设备
Route::post('task-config', 'app\\devices\\controller\\Device@updateTaskConfig'); // 更新设备任务配置
});
// 设备微信相关

View File

@@ -396,4 +396,73 @@ class Device extends Controller
]);
}
}
/**
* 更新设备任务配置
* @return \think\response\Json
*/
public function updateTaskConfig()
{
// 获取请求参数
$data = $this->request->post();
// 验证参数
if (empty($data['id'])) {
return json(['code' => 400, 'msg' => '设备ID不能为空']);
}
// 转换为整型确保ID格式正确
$deviceId = intval($data['id']);
// 先获取设备信息,确认设备存在且未删除
$device = \app\devices\model\Device::where('id', $deviceId)
->where('isDeleted', 0)
->find();
if (!$device) {
return json(['code' => 404, 'msg' => '设备不存在或已删除']);
}
// 读取原taskConfig如果存在则解析
$taskConfig = [];
if (!empty($device['taskConfig'])) {
$taskConfig = json_decode($device['taskConfig'], true) ?: [];
}
// 更新需要修改的配置项
$updateFields = ['autoAddFriend', 'autoReply', 'momentsSync', 'aiChat'];
$hasUpdate = false;
foreach ($updateFields as $field) {
if (isset($data[$field])) {
// 将值转换为布尔类型存储
$taskConfig[$field] = (bool)$data[$field];
$hasUpdate = true;
}
}
// 如果没有需要更新的字段,直接返回成功
if (!$hasUpdate) {
return json(['code' => 200, 'msg' => '更新成功', 'data' => ['taskConfig' => $taskConfig]]);
}
// 更新设备taskConfig字段
$result = \app\devices\model\Device::where('id', $deviceId)
->update([
'taskConfig' => json_encode($taskConfig),
'updateTime' => time()
]);
if ($result) {
return json([
'code' => 200,
'msg' => '更新任务配置成功',
'data' => [
'taskConfig' => $taskConfig
]
]);
} else {
return json(['code' => 500, 'msg' => '更新任务配置失败']);
}
}
}

View File

@@ -38,7 +38,8 @@ class WechatAccount extends Model
'gender' => 'integer',
'currentDeviceId' => 'integer',
'isDeleted' => 'integer',
'groupId' => 'integer'
'groupId' => 'integer',
'status' => 'integer'
];
/**