first commit

This commit is contained in:
2026-06-02 10:42:33 +08:00
commit dd4975fd2c
1084 changed files with 442416 additions and 0 deletions

160
pages/text/text.vue Normal file
View File

@@ -0,0 +1,160 @@
<template>
<view>
<view v-html="sanitizeContent(formHtml)"></view>
</view>
</template>
<script setup>
import { ref, onMounted } from 'vue'
const formHtml = ref('')
const sanitizeContent = (str) => {
return str.replace(/<\/?script>/gi, (match) => {
// 将匹配到的标签转换为十六进制 Unicode
let result = '';
for (let i = 0; i < match.length; i++) {
result += '\\u' + match.charCodeAt(i).toString(16).padStart(4, '0');
}
return result;
});
}
onMounted(() => {
// 直接设置 HTML
formHtml.value = `
明白了!您希望在我的回复中直接嵌入可编辑的表单卡片。让我试试:
---
<div style="background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); border-radius: 20px; padding: 28px; color: white; font-family: 'Microsoft YaHei', sans-serif; box-shadow: 0 10px 40px rgba(0,0,0,0.25); margin: 20px 0;">
<h2 style="margin: 0 0 20px 0; font-size: 20px; display: flex; align-items: center; gap: 12px;">
<span style="font-size: 26px;">✏️</span> 直接在下方编辑任务
</h2>
<form id="inlineTaskForm" style="background: rgba(255,255,255,0.1); border-radius: 16px; padding: 24px;">
<div style="margin-bottom: 18px;">
<label style="display: block; font-size: 12px; opacity: 0.8; margin-bottom: 8px; text-transform: uppercase; letter-spacing: 1px;">任务名称</label>
<input type="text" id="inlineTitle" placeholder="输入任务名称..." style="width: 100%; padding: 14px 16px; background: #1e1e2e; border: 2px solid rgba(255,255,255,0.2); border-radius: 10px; color: #fff; font-size: 15px;">
</div>
<div style="margin-bottom: 18px;">
<label style="display: block; font-size: 12px; opacity: 0.8; margin-bottom: 8px; text-transform: uppercase; letter-spacing: 1px;">任务描述</label>
<textarea id="inlineDesc" placeholder="输入详细描述..." style="width: 100%; padding: 14px 16px; background: #1e1e2e; border: 2px solid rgba(255,255,255,0.2); border-radius: 10px; color: #fff; font-size: 14px; resize: vertical; min-height: 60px;"></textarea>
</div>
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 15px; margin-bottom: 18px;">
<div>
<label style="display: block; font-size: 12px; opacity: 0.8; margin-bottom: 8px; text-transform: uppercase; letter-spacing: 1px;">开始日期</label>
<input type="date" id="inlineStart" style="width: 100%; padding: 14px 16px; background: #1e1e2e; border: 2px solid rgba(255,255,255,0.2); border-radius: 10px; color: #fff; font-size: 15px;">
</div>
<div>
<label style="display: block; font-size: 12px; opacity: 0.8; margin-bottom: 8px; text-transform: uppercase; letter-spacing: 1px;">截止日期</label>
<input type="date" id="inlineEnd" style="width: 100%; padding: 14px 16px; background: #1e1e2e; border: 2px solid rgba(255,255,255,0.2); border-radius: 10px; color: #fff; font-size: 15px;">
</div>
</div>
<div style="margin-bottom: 18px;">
<label style="display: block; font-size: 12px; opacity: 0.8; margin-bottom: 8px; text-transform: uppercase; letter-spacing: 1px;">时间</label>
<input type="time" id="inlineTime" value="09:00" style="width: 100%; padding: 14px 16px; background: #1e1e2e; border: 2px solid rgba(255,255,255,0.2); border-radius: 10px; color: #fff; font-size: 15px;">
</div>
<div style="margin-bottom: 18px;">
<label style="display: block; font-size: 12px; opacity: 0.8; margin-bottom: 10px; text-transform: uppercase; letter-spacing: 1px;">优先级</label>
<div style="display: flex; gap: 10px;">
<label style="flex: 1; text-align: center; padding: 12px; background: rgba(254, 215, 215, 0.2); border: 2px solid transparent; border-radius: 10px; cursor: pointer; transition: all 0.3s;" onclick="selectPriority(this, 'high')">
🔴 高
<input type="radio" name="priority" value="high" checked style="display: none;">
</label>
<label style="flex: 1; text-align: center; padding: 12px; background: rgba(254, 235, 200, 0.2); border: 2px solid transparent; border-radius: 10px; cursor: pointer; transition: all 0.3s;" onclick="selectPriority(this, 'medium')">
🟡 中
<input type="radio" name="priority" value="medium" style="display: none;">
</label>
<label style="flex: 1; text-align: center; padding: 12px; background: rgba(198, 246, 213, 0.2); border: 2px solid transparent; border-radius: 10px; cursor: pointer; transition: all 0.3s;" onclick="selectPriority(this, 'low')">
🟢 低
<input type="radio" name="priority" value="low" style="display: none;">
</label>
</div>
</div>
<div style="margin-bottom: 20px;">
<label style="display: block; font-size: 12px; opacity: 0.8; margin-bottom: 10px; text-transform: uppercase; letter-spacing: 1px;">分类</label>
<div style="display: flex; gap: 10px; flex-wrap: wrap;">
<label style="padding: 10px 16px; background: rgba(233, 216, 253, 0.2); border: 2px solid #667eea; border-radius: 20px; cursor: pointer; font-size: 13px;" onclick="selectCategory(this, 'work')">
💼 工作
<input type="radio" name="category" value="work" checked style="display: none;">
</label>
<label style="padding: 10px 16px; background: rgba(190, 227, 248, 0.2); border: 2px solid transparent; border-radius: 20px; cursor: pointer; font-size: 13px;" onclick="selectCategory(this, 'life')">
🏠 生活
<input type="radio" name="category" value="life" style="display: none;">
</label>
<label style="padding: 10px 16px; background: rgba(254, 215, 226, 0.2); border: 2px solid transparent; border-radius: 20px; cursor: pointer; font-size: 13px;" onclick="selectCategory(this, 'study')">
📚 学习
<input type="radio" name="category" value="study" style="display: none;">
</label>
<label style="padding: 10px 16px; background: rgba(198, 246, 213, 0.2); border: 2px solid transparent; border-radius: 20px; cursor: pointer; font-size: 13px;" onclick="selectCategory(this, 'health')">
💪 健康
<input type="radio" name="category" value="health" style="display: none;">
</label>
</div>
</div>
<div style="display: flex; gap: 12px;">
<button type="button" onclick="clearInlineForm()" style="flex: 1; padding: 14px; background: rgba(255,255,255,0.2); border: none; border-radius: 12px; color: white; font-size: 14px; font-weight: 600; cursor: pointer; transition: all 0.3s;">
清空
</button>
<button type="button" onclick="submitInlineForm()" style="flex: 2; padding: 14px; background: white; border: none; border-radius: 12px; color: #667eea; font-size: 14px; font-weight: 600; cursor: pointer; transition: all 0.3s;">
✅ 添加任务
</button>
</div>
</form>
</div>
---
**✅ 您现在可以直接在上方的卡片中填写任务信息,点击"添加任务"即可!**
填写完成后告诉我"已填好"或"添加",我会帮您确认是否成功!
`
// 初始化事件
// initForm()
})
const initForm = () => {
setTimeout(() => {
const today = new Date().toISOString().split('T')[0]
const startInput = document.getElementById('inlineStart')
const endInput = document.getElementById('inlineEnd')
if (startInput) startInput.value = today
if (endInput) endInput.value = today
const clearBtn = document.getElementById('clearBtn')
const submitBtn = document.getElementById('submitBtn')
if (clearBtn) {
clearBtn.onclick = () => {
const titleInput = document.getElementById('inlineTitle')
const descInput = document.getElementById('inlineDesc')
if (titleInput) titleInput.value = ''
if (descInput) descInput.value = ''
}
}
if (submitBtn) {
submitBtn.onclick = () => {
const titleInput = document.getElementById('inlineTitle')
const title = titleInput?.value.trim()
if (!title) {
alert('请输入任务名称')
return
}
alert(`任务已添加:${title}`)
if (titleInput) titleInput.value = ''
if (document.getElementById('inlineDesc')) document.getElementById('inlineDesc').value = ''
}
}
}, 100)
}
</script>

538
pages/text/text2.vue Normal file
View File

@@ -0,0 +1,538 @@
<template>
<view class="container">
<!-- 连接状态卡片 -->
<view class="status-card">
<text class="status-label">连接状态</text>
<view class="status-value" :class="connectionStatusClass">
<text>{{ socketStore.getStatusText() }}</text>
</view>
<view class="status-info" v-if="socketStore.reconnectAttempts > 0">
<text class="reconnect-info">重连次数: {{ socketStore.reconnectAttempts }}</text>
</view>
</view>
<!-- 配置区域 -->
<view class="config-card">
<text class="section-title">连接配置</text>
<view class="input-group">
<text class="input-label">Token</text>
<input class="input-field" v-model="config.token" placeholder="请输入Token" />
</view>
<view class="input-group">
<text class="input-label">Conversation ID</text>
<input class="input-field" v-model="config.conversationId" placeholder="请输入会话ID(可选)" />
</view>
</view>
<!-- 连接控制 -->
<view class="control-card">
<button class="btn btn-primary" :disabled="socketStore.isConnected || socketStore.isConnecting"
@click="handleConnect">
{{ socketStore.isConnecting ? '连接中...' : '连接' }}
</button>
<button class="btn btn-danger" :disabled="!socketStore.isConnected" @click="handleDisconnect">
断开
</button>
<button class="btn btn-secondary" @click="handleSendPing" :disabled="!socketStore.isConnected">
Ping
</button>
</view>
<!-- 发送消息区域 -->
<view class="send-card">
<text class="section-title">发送消息</text>
<view class="send-input-wrapper">
<textarea class="send-input" v-model="sendMessage" placeholder="输入要发送的消息(JSON格式)"
:disabled="!socketStore.isConnected"></textarea>
<view class="send-buttons">
<button class="btn btn-primary btn-small"
:disabled="!socketStore.isConnected || !sendMessage.trim()" @click="handleSend">
发送
</button>
<button class="btn btn-outline btn-small" :disabled="!sendMessage.trim()" @click="formatJson">
格式化
</button>
</view>
</view>
<!-- 快捷消息 -->
<view class="quick-messages">
<text class="quick-label">快捷消息:</text>
<view class="quick-btns">
<button v-for="item in quickMessages" :key="item.label" class="quick-btn"
:disabled="!socketStore.isConnected" @click="sendQuickMessage(item.data)">
{{ item.label }}
</button>
</view>
</view>
</view>
<!-- 日志区域 -->
<view class="log-card">
<view class="log-header">
<view class="log-title-wrapper">
<text class="section-title">日志</text>
<text class="log-count">({{ socketStore.logs.length }})</text>
</view>
<view class="log-actions">
<button class="btn btn-small btn-secondary" @click="socketStore.clearLogs">清空</button>
</view>
</view>
<scroll-view class="log-list" scroll-y @scrolltoupper="loadMoreLogs">
<view v-for="(log, index) in socketStore.logs" :key="index" class="log-item" :class="'log-' + log.type">
<text class="log-time">{{ log.time }}</text>
<text class="log-content">{{ log.content }}</text>
</view>
</scroll-view>
</view>
</view>
</template>
<script setup>
import {
ref,
computed,
onMounted,
onUnmounted,
watch
} from 'vue'
import {
useSocketStore
} from '@/stores/socket.js'
import {
getToken,
getTaskCallId,
getCurrentSessionId
} from '@/utils/user-info.js'
// Store
const socketStore = useSocketStore()
// 响应式数据
const config = ref({
token: getToken(),
conversationId: getCurrentSessionId()
})
const sendMessage = ref('')
const quickMessages = ref([{
label: 'Ping',
data: {
ws_event: 'ping'
}
},
{
label: '认证',
data: {
ws_event: 'auth',
data: {
token: '',
conversation_id: ''
}
}
},
{
label: '测试消息',
data: {
ws_event: 'message',
data: {
task_call_id: getTaskCallId(),
token: getToken(),
conversation_id: getCurrentSessionId()
}
}
}
])
// 计算属性
const connectionStatusClass = computed(() => {
const status = socketStore.connectionStatus
return {
'status-connected': status === 'connected',
'status-connecting': status === 'connecting',
'status-error': status === 'error',
'status-disconnected': status === 'disconnected'
}
})
// 方法
const handleConnect = () => {
if (!config.value.token) {
uni.showToast({
title: '请输入Token',
icon: 'none'
})
return
}
socketStore.connect({
token: config.value.token,
conversationId: getCurrentSessionId()
})
}
const handleDisconnect = () => {
socketStore.disconnect()
}
const handleSendPing = async () => {
try {
await socketStore.sendPing()
} catch (e) {
uni.showToast({
title: '发送失败',
icon: 'none'
})
}
}
const handleSend = async () => {
if (!sendMessage.value.trim()) return
try {
const messageData = {
ws_event: 'message',
data: {
task_call_id: getTaskCallId(),
result: {
tools: []
}
}
}
await socketStore.send(messageData)
sendMessage.value = ''
} catch (e) {
uni.showToast({
title: '发送失败',
icon: 'none'
})
}
}
const sendQuickMessage = async (template) => {
// console.log('getCurrentSessionId:', getCurrentSessionId())
const messageData = {
ws_event: 'message',
data: {
task_call_id: getTaskCallId(),
token: getToken(),
conversation_id: getCurrentSessionId()
}
}
try {
await socketStore.send(messageData)
} catch (e) {
uni.showToast({
title: '发送失败',
icon: 'none'
})
}
}
const formatJson = () => {
if (!sendMessage.value.trim()) return
try {
const obj = JSON.parse(sendMessage.value)
sendMessage.value = JSON.stringify(obj, null, 2)
} catch {
uni.showToast({
title: '不是有效的JSON',
icon: 'none'
})
}
}
const loadMoreLogs = () => {
// 预留扩展
}
watch(() => socketStore.isDisconnected, (newVal) => {
if (newVal && socketStore.messageString) {
console.log(socketStore.messageString);
}
})
// 生命周期
onMounted(() => {
socketStore.addLog('info', '=== Socket测试页面 ===')
socketStore.addLog('info', '页面已加载')
if (socketStore.isConnected) {
socketStore.addLog('info', '当前已处于连接状态')
}
})
onUnmounted(() => {
socketStore.addLog('info', '页面卸载')
// 注意这里不主动断开连接由App级别管理
})
</script>
<style scoped>
.container {
padding: 20rpx;
background-color: #f5f5f5;
min-height: 100vh;
}
.status-card,
.config-card,
.control-card,
.send-card,
.log-card {
background-color: #fff;
border-radius: 16rpx;
padding: 30rpx;
margin-bottom: 20rpx;
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.05);
}
.status-label {
font-size: 28rpx;
color: #666;
margin-bottom: 16rpx;
}
.status-value {
display: inline-block;
padding: 12rpx 32rpx;
border-radius: 8rpx;
font-size: 28rpx;
font-weight: 500;
}
.status-connected {
background-color: #e6f7e6;
color: #52c41a;
}
.status-connecting {
background-color: #fff7e6;
color: #faad14;
}
.status-disconnected {
background-color: #f5f5f5;
color: #999;
}
.status-error {
background-color: #fff1f0;
color: #ff4d4f;
}
.status-info {
margin-top: 16rpx;
}
.reconnect-info {
font-size: 24rpx;
color: #faad14;
}
.section-title {
font-size: 32rpx;
font-weight: 600;
color: #333;
margin-bottom: 24rpx;
display: block;
}
.input-group {
margin-bottom: 24rpx;
}
.input-label {
font-size: 28rpx;
color: #666;
margin-bottom: 12rpx;
display: block;
}
.input-field {
border: 2rpx solid #e8e8e8;
border-radius: 8rpx;
padding: 20rpx;
font-size: 28rpx;
background-color: #fafafa;
}
.control-card {
display: flex;
gap: 20rpx;
}
.btn {
flex: 1;
height: 88rpx;
line-height: 88rpx;
border-radius: 12rpx;
font-size: 32rpx;
text-align: center;
border: none;
}
.btn-primary {
background-color: #1890ff;
color: #fff;
}
.btn-danger {
background-color: #ff4d4f;
color: #fff;
}
.btn-secondary {
background-color: #f0f0f0;
color: #666;
}
.btn-outline {
background-color: transparent;
color: #1890ff;
border: 2rpx solid #1890ff;
}
.btn-small {
height: 64rpx;
line-height: 64rpx;
font-size: 26rpx;
padding: 0 24rpx;
}
.btn[disabled] {
opacity: 0.5;
}
.send-input-wrapper {
display: flex;
flex-direction: column;
gap: 20rpx;
}
.send-input {
border: 2rpx solid #e8e8e8;
border-radius: 8rpx;
padding: 20rpx;
font-size: 28rpx;
min-height: 160rpx;
background-color: #fafafa;
box-sizing: border-box;
}
.send-buttons {
display: flex;
gap: 20rpx;
}
.quick-messages {
margin-top: 24rpx;
padding-top: 24rpx;
border-top: 2rpx solid #f0f0f0;
}
.quick-label {
font-size: 26rpx;
color: #999;
margin-bottom: 16rpx;
display: block;
}
.quick-btns {
display: flex;
flex-wrap: wrap;
gap: 16rpx;
}
.quick-btn {
padding: 12rpx 24rpx;
background-color: #f0f5ff;
color: #1890ff;
border-radius: 8rpx;
font-size: 26rpx;
border: none;
}
.quick-btn[disabled] {
opacity: 0.5;
}
.log-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 24rpx;
}
.log-title-wrapper {
display: flex;
align-items: center;
gap: 12rpx;
}
.log-title-wrapper .section-title {
margin-bottom: 0;
}
.log-count {
font-size: 24rpx;
color: #999;
}
.log-actions {
display: flex;
gap: 12rpx;
}
.log-list {
max-height: 500rpx;
background-color: #1e1e1e;
border-radius: 8rpx;
padding: 20rpx;
}
.log-item {
display: flex;
margin-bottom: 12rpx;
font-family: 'Courier New', monospace;
font-size: 24rpx;
line-height: 1.6;
}
.log-time {
color: #888;
margin-right: 16rpx;
flex-shrink: 0;
}
.log-content {
word-break: break-all;
flex: 1;
color: #fff;
}
.log-info .log-content {
color: #fff;
}
.log-success .log-content {
color: #52c41a;
}
.log-error .log-content {
color: #ff4d4f;
}
.log-warn .log-content {
color: #faad14;
}
.log-send .log-content {
color: #1890ff;
}
.log-receive .log-content {
color: #52c41a;
}
</style>