!105 czh-20260717-优化-测试页面增加linkTitle和data支持

* czh-20260717-优化-测试页面增加linkTitle和data支持
* czh-20260717-修正测试页面默认Mock端口为19010
* czh-20260717-新增即时通推送测试页面
This commit is contained in:
陈志浩
2026-07-22 01:51:19 +00:00
parent ac9064b363
commit 11916d7da1
+301
View File
@@ -0,0 +1,301 @@
<template>
<div class="ksim-test-container">
<a-card title="即时通消息推送测试" style="margin-bottom: 16px">
<a-form :label-col="{ span: 4 }" :wrapper-col="{ span: 18 }">
<a-divider orientation="left">即时通连接配置</a-divider>
<a-form-item label="API 地址(host">
<a-input v-model:value="formData.host" placeholder="http://localhost:17072" />
<span style="color: #8c8c8c; font-size: 12px; margin-left: 8px">即时通服务器地址含端口号</span>
</a-form-item>
<a-form-item label="应用IDappId">
<a-input v-model:value="formData.appId" placeholder="test-app" />
</a-form-item>
<a-form-item label="应用密钥(appSecret">
<a-input-password v-model:value="formData.appSecret" placeholder="test-secret" />
</a-form-item>
<a-divider orientation="left">消息内容</a-divider>
<a-form-item label="用户标识类型">
<a-input v-model:value="formData.toUserKeyType" placeholder="Email" />
<span style="color: #8c8c8c; font-size: 12px; margin-left: 8px"> EmailPhone 对应即时通用户关联类型</span>
</a-form-item>
<a-form-item label="用户标识值">
<a-input v-model:value="formData.toUserValue" placeholder="user@example.com" />
<span style="color: #8c8c8c; font-size: 12px; margin-left: 8px">接收人的具体标识值</span>
</a-form-item>
<a-form-item label="消息标题">
<a-input v-model:value="formData.title" placeholder="测试消息标题" />
</a-form-item>
<a-form-item label="消息内容">
<a-textarea v-model:value="formData.content" placeholder="测试消息正文" :rows="3" />
</a-form-item>
<a-form-item label="链接地址(可选)">
<a-input v-model:value="formData.linkUrl" placeholder="http://localhost:3100/semri/task" />
<span style="color: #8c8c8c; font-size: 12px; margin-left: 8px">点击消息后跳转的链接</span>
</a-form-item>
<a-form-item label="链接标题(可选)">
<a-input v-model:value="formData.linkTitle" placeholder="查看详情" />
<span style="color: #8c8c8c; font-size: 12px; margin-left: 8px">链接显示的文字</span>
</a-form-item>
<a-form-item label="业务数据 data(可选)">
<a-textarea v-model:value="formData.dataJson" placeholder='{"id":"123","name":"zhangsan"}' :rows="3" />
<span style="color: #8c8c8c; font-size: 12px; margin-left: 8px">JSON 格式发送给即时通的业务数据</span>
</a-form-item>
<a-form-item :wrapper-col="{ offset: 4, span: 18 }">
<a-space>
<a-button type="primary" @click="handleSend" :loading="sending">
{{ sending ? '发送中...' : '发送消息' }}
</a-button>
<a-button @click="handleReset">重置</a-button>
<a-button @click="handleFillMock">填充 Mock 默认值</a-button>
</a-space>
</a-form-item>
</a-form>
</a-card>
<a-card title="完整请求(Request" style="margin-bottom: 16px">
<a-descriptions :column="1" size="small" bordered>
<a-descriptions-item label="Token 请求 URL">
<code>{{ requestInfo.tokenRequestUrl || '(未发送)' }}</code>
</a-descriptions-item>
<a-descriptions-item label="Token 请求体">
<pre class="json-block">{{ formatJson(requestInfo.tokenRequestBody) }}</pre>
</a-descriptions-item>
<a-descriptions-item label="消息请求 URL">
<code>{{ requestInfo.msgRequestUrl || '(未发送)' }}</code>
</a-descriptions-item>
<a-descriptions-item label="消息请求体">
<pre class="json-block">{{ formatJson(requestInfo.msgRequestBody) }}</pre>
</a-descriptions-item>
</a-descriptions>
</a-card>
<a-card title="完整响应(Response" style="margin-bottom: 16px">
<a-space direction="vertical" style="width: 100%">
<div v-if="responseInfo.success === true" style="margin-bottom: 8px">
<a-tag color="green">发送成功</a-tag>
<span v-if="responseInfo.messageId"> messageId: {{ responseInfo.messageId }}</span>
</div>
<div v-else-if="responseInfo.success === false" style="margin-bottom: 8px">
<a-tag color="red">发送失败</a-tag>
<span v-if="responseInfo.errorMessage"> {{ responseInfo.errorMessage }}</span>
</div>
<a-descriptions :column="1" size="small" bordered>
<a-descriptions-item label="Token 响应">
<pre class="json-block">{{ formatJson(responseInfo.tokenResponse) }}</pre>
</a-descriptions-item>
<a-descriptions-item label="消息响应">
<pre class="json-block">{{ formatJson(responseInfo.msgResponse) }}</pre>
</a-descriptions-item>
<a-descriptions-item label="状态码">
<a-tag :color="responseInfo.responseCode === 0 || responseInfo.responseCode === 200 ? 'green' : 'red'">
{{ responseInfo.responseCode !== undefined ? responseInfo.responseCode : '-' }}
</a-tag>
</a-descriptions-item>
</a-descriptions>
</a-space>
</a-card>
<a-card title="事件日志">
<div class="event-log-wrapper">
<a-button size="small" @click="eventLogs = []" style="margin-bottom: 8px">清空日志</a-button>
<div class="event-log-list">
<div v-for="(log, idx) in eventLogs" :key="idx" class="event-log-item">
<a-tag :color="log.type === 'success' ? 'green' : log.type === 'error' ? 'red' : 'blue'" size="small">
{{ log.type }}
</a-tag>
<span class="log-time">{{ log.time }}</span>
<span class="log-msg">{{ log.message }}</span>
</div>
<div v-if="eventLogs.length === 0" class="empty-log">暂无日志</div>
</div>
</div>
</a-card>
</div>
</template>
<script lang="ts" setup>
import { ref, reactive } from 'vue';
import { defHttp } from '/@/utils/http/axios';
const sending = ref(false);
const eventLogs = ref<any[]>([]);
const formData = reactive({
host: 'http://localhost:19010',
appId: 'test-app',
appSecret: 'test-secret',
toUserKeyType: 'Email',
toUserValue: 'test@example.com',
title: '测试消息',
content: '这是一条来自督办系统的测试消息',
linkUrl: '',
linkTitle: '',
dataJson: '',
});
const requestInfo = reactive<Record<string, any>>({});
const responseInfo = reactive<Record<string, any>>({});
function addLog(type: string, message: string) {
const now = new Date();
const time = `${now.getHours().toString().padStart(2, '0')}:${now.getMinutes().toString().padStart(2, '0')}:${now.getSeconds().toString().padStart(2, '0')}`;
eventLogs.value.unshift({ type, message, time });
if (eventLogs.value.length > 100) eventLogs.value.pop();
}
function formatJson(obj: any): string {
if (!obj) return '(空)';
try {
return JSON.stringify(obj, null, 2);
} catch {
return String(obj);
}
}
function handleFillMock() {
formData.host = 'http://localhost:19010';
formData.appId = 'test-app';
formData.appSecret = 'test-secret';
formData.toUserKeyType = 'Email';
formData.toUserValue = 'test@example.com';
formData.title = '测试消息';
formData.content = '这是一条来自督办系统的测试消息';
formData.linkUrl = '';
formData.linkTitle = '';
formData.dataJson = '';
addLog('info', '已填充 Mock 默认值');
}
async function handleSend() {
if (!formData.host) {
addLog('error', '请填写 API 地址(host');
return;
}
if (!formData.toUserKeyType || !formData.toUserValue) {
addLog('error', '请填写用户标识类型和值');
return;
}
sending.value = true;
addLog('info', `开始发送消息 → ${formData.host}`);
// 清空上次结果
Object.keys(requestInfo).forEach(k => delete requestInfo[k]);
Object.keys(responseInfo).forEach(k => delete responseInfo[k]);
try {
const res = await defHttp.post({
url: '/ksim/test/send',
data: {
...formData,
data: formData.dataJson ? JSON.parse(formData.dataJson) : null,
},
});
if (res?.request) {
Object.assign(requestInfo, res.request);
}
if (res?.tokenResponse) {
responseInfo.tokenResponse = res.tokenResponse;
}
if (res?.msgResponse) {
responseInfo.msgResponse = res.msgResponse;
}
responseInfo.success = res?.success;
responseInfo.messageId = res?.messageId;
responseInfo.errorMessage = res?.errorMessage;
responseInfo.responseCode = res?.responseCode;
if (res?.success) {
addLog('success', `发送成功!messageId: ${res?.messageId || '-'}`);
} else {
addLog('error', `发送失败: ${res?.errorMessage || '未知错误'}`);
}
} catch (e: any) {
addLog('error', `请求异常: ${e?.message || e}`);
responseInfo.success = false;
responseInfo.errorMessage = e?.message || '网络请求异常';
} finally {
sending.value = false;
}
}
function handleReset() {
formData.host = 'http://localhost:19010';
formData.appId = 'test-app';
formData.appSecret = 'test-secret';
formData.toUserKeyType = 'Email';
formData.toUserValue = 'test@example.com';
formData.title = '测试消息';
formData.content = '这是一条来自督办系统的测试消息';
formData.linkUrl = '';
formData.linkTitle = '';
formData.dataJson = '';
Object.keys(requestInfo).forEach(k => delete requestInfo[k]);
Object.keys(responseInfo).forEach(k => delete responseInfo[k]);
eventLogs.value = [];
addLog('info', '表单已重置');
}
</script>
<style lang="less" scoped>
.ksim-test-container {
padding: 16px;
}
.json-block {
background: #f5f5f5;
border: 1px solid #e8e8e8;
border-radius: 4px;
padding: 12px;
margin: 0;
font-size: 12px;
line-height: 1.6;
max-height: 400px;
overflow-y: auto;
white-space: pre-wrap;
word-break: break-all;
}
.event-log-wrapper {
.event-log-list {
max-height: 300px;
overflow-y: auto;
background: #fafafa;
border-radius: 4px;
padding: 8px;
}
.event-log-item {
display: flex;
align-items: center;
gap: 8px;
padding: 4px 0;
border-bottom: 1px solid #f0f0f0;
&:last-child {
border-bottom: none;
}
.log-time {
font-size: 12px;
color: #8c8c8c;
flex-shrink: 0;
}
.log-msg {
font-size: 13px;
color: #262626;
}
}
.empty-log {
text-align: center;
color: #8c8c8c;
font-size: 13px;
padding: 16px 0;
}
}
</style>