优化: df-miniapp 跨端 AI Chat 更新(会话前基线收尾)
- ws.ts 心跳 pong 闭环 + useAiChat 看门狗治半连接挂死 - pages 新增 settings/ + chat/conversations 更新 + types events/relay + mdRenderer + tsconfig
This commit is contained in:
@@ -0,0 +1,200 @@
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue'
|
||||
import { getConfig } from '@/config'
|
||||
import { useAiChat } from '@/composables/useAiChat'
|
||||
|
||||
/**
|
||||
* 设置页(只读查看,不编辑)。
|
||||
*
|
||||
* 展示当前连接配置 + 连接状态,用户据此核对/排查问题。
|
||||
* 编辑功能后续做(relayHost/deviceId/token 手填)或走二维码配对(分发阶段)。
|
||||
*
|
||||
* 数据源:
|
||||
* - 连接配置:config.ts getConfig()(storage 持久化,含默认值合并)
|
||||
* - 连接状态:useAiChat() 单例(wsStatus + deviceOnline,实时)
|
||||
* - 版本号:manifest.json versionName(手动维护此处常量,对齐 0.1.0)
|
||||
*/
|
||||
|
||||
// 连接配置(只读快照,页面 onShow 时刷新,切 tab 回来看最新值)
|
||||
const cfg = computed(() => getConfig())
|
||||
|
||||
// 连接状态(实时,useAiChat 单例 ref 直接响应)
|
||||
const ai = useAiChat()
|
||||
const wsStatus = computed(() => ai.wsStatus.value)
|
||||
const deviceOnline = computed(() => ai.deviceOnline.value)
|
||||
const isWsConnected = computed(() => ai.isWsConnected.value)
|
||||
|
||||
/** 连接状态中文文案 + 颜色 class(对齐 chat 页 STATUS_TEXT 口径) */
|
||||
const STATUS_META: Record<string, { label: string; cls: string }> = {
|
||||
connected: { label: '已连接', cls: 'st-ok' },
|
||||
connecting: { label: '连接中…', cls: 'st-wait' },
|
||||
handshaking: { label: '握手中…', cls: 'st-wait' },
|
||||
reconnecting: { label: '重连中…', cls: 'st-warn' },
|
||||
disconnected: { label: '已断开', cls: 'st-err' },
|
||||
}
|
||||
const statusText = computed(() => {
|
||||
if (isWsConnected.value) {
|
||||
return deviceOnline.value ? '已连接桌面端' : '已连接中继'
|
||||
}
|
||||
return STATUS_META[wsStatus.value]?.label || wsStatus.value
|
||||
})
|
||||
const statusCls = computed(() => {
|
||||
if (isWsConnected.value) return deviceOnline.value ? 'st-ok' : 'st-warn'
|
||||
return STATUS_META[wsStatus.value]?.cls || 'st-err'
|
||||
})
|
||||
|
||||
/** token 脱敏(只显前 4 位 + 星号,查看页不暴露全量凭据) */
|
||||
function maskToken(token: string): string {
|
||||
if (!token) return '(未设置)'
|
||||
if (token.length <= 8) return '*'.repeat(token.length)
|
||||
return `${token.slice(0, 4)}${'*'.repeat(Math.min(token.length - 4, 12))}`
|
||||
}
|
||||
|
||||
/** 毫秒转可读(30000 → 30s) */
|
||||
function fmtMs(ms: number): string {
|
||||
return ms >= 1000 ? `${ms / 1000}s` : `${ms}ms`
|
||||
}
|
||||
|
||||
/** 版本号(对齐 manifest.json versionName,改版时同步) */
|
||||
const APP_VERSION = '0.1.0'
|
||||
|
||||
/** 手动重连(断开/重连中可点,复用 ai.resumeIfDisconnected) */
|
||||
function onReconnect(): void {
|
||||
if (wsStatus.value === 'disconnected' || wsStatus.value === 'reconnecting') {
|
||||
ai.resumeIfDisconnected()
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<view class="settings-page">
|
||||
<!-- 连接状态卡片(实时,断开可点重连) -->
|
||||
<view class="card">
|
||||
<view class="card-head">
|
||||
<text class="card-title">连接状态</text>
|
||||
</view>
|
||||
<view class="row">
|
||||
<text class="row-label">当前状态</text>
|
||||
<text class="row-value status" :class="statusCls" @tap="onReconnect">{{ statusText }}</text>
|
||||
</view>
|
||||
<view class="row">
|
||||
<text class="row-label">中继地址</text>
|
||||
<text class="row-value mono">{{ cfg.relayHost }}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 连接配置(只读展示,编辑后续做) -->
|
||||
<view class="card">
|
||||
<view class="card-head">
|
||||
<text class="card-title">连接配置</text>
|
||||
<text class="card-sub">只读 · 编辑后续开放</text>
|
||||
</view>
|
||||
<view class="row">
|
||||
<text class="row-label">设备 ID</text>
|
||||
<text class="row-value mono">{{ cfg.deviceId }}</text>
|
||||
</view>
|
||||
<view class="row">
|
||||
<text class="row-label">配对 Token</text>
|
||||
<text class="row-value mono">{{ maskToken(cfg.token) }}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 高级参数 -->
|
||||
<view class="card">
|
||||
<view class="card-head">
|
||||
<text class="card-title">高级参数</text>
|
||||
</view>
|
||||
<view class="row">
|
||||
<text class="row-label">心跳间隔</text>
|
||||
<text class="row-value">{{ fmtMs(cfg.heartbeatInterval) }}</text>
|
||||
</view>
|
||||
<view class="row">
|
||||
<text class="row-label">重连初始间隔</text>
|
||||
<text class="row-value">{{ fmtMs(cfg.reconnectBaseDelay) }}</text>
|
||||
</view>
|
||||
<view class="row">
|
||||
<text class="row-label">重连最大间隔</text>
|
||||
<text class="row-value">{{ fmtMs(cfg.reconnectMaxDelay) }}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 关于 -->
|
||||
<view class="card">
|
||||
<view class="card-head">
|
||||
<text class="card-title">关于</text>
|
||||
</view>
|
||||
<view class="row">
|
||||
<text class="row-label">版本</text>
|
||||
<text class="row-value">DevFlow Mini v{{ APP_VERSION }}</text>
|
||||
</view>
|
||||
<view class="row">
|
||||
<text class="row-label">说明</text>
|
||||
<text class="row-value">远程 AI Chat · 跨端操作桌面端开发助手</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.settings-page {
|
||||
min-height: 100vh;
|
||||
padding: 24rpx;
|
||||
box-sizing: border-box;
|
||||
background-color: $uni-bg-color;
|
||||
}
|
||||
.card {
|
||||
margin-bottom: 24rpx;
|
||||
padding: 24rpx;
|
||||
background-color: $uni-bg-color-light;
|
||||
border-radius: 16rpx;
|
||||
}
|
||||
.card-head {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: baseline;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 16rpx;
|
||||
}
|
||||
.card-title {
|
||||
font-size: $uni-font-size-base;
|
||||
font-weight: bold;
|
||||
color: $uni-text-color;
|
||||
}
|
||||
.card-sub {
|
||||
font-size: $uni-font-size-sm;
|
||||
color: $uni-text-color-disable;
|
||||
}
|
||||
.row {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 16rpx 0;
|
||||
border-bottom: 1rpx solid $uni-border-color;
|
||||
}
|
||||
.row:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
.row-label {
|
||||
font-size: $uni-font-size-sm;
|
||||
color: $uni-text-color-grey;
|
||||
}
|
||||
.row-value {
|
||||
flex: 1;
|
||||
margin-left: 24rpx;
|
||||
font-size: $uni-font-size-sm;
|
||||
color: $uni-text-color;
|
||||
text-align: right;
|
||||
word-break: break-all;
|
||||
}
|
||||
.row-value.mono {
|
||||
font-family: monospace;
|
||||
}
|
||||
.row-value.status {
|
||||
font-weight: bold;
|
||||
}
|
||||
.st-ok { color: #6bd99a; }
|
||||
.st-wait { color: $uni-color-primary; }
|
||||
.st-warn { color: #f0c75e; }
|
||||
.st-err { color: #e85a4f; }
|
||||
</style>
|
||||
Reference in New Issue
Block a user