feat: add web application

This commit is contained in:
richjohnrambo
2026-10-10 20:55:59 +08:00
parent 3eb5d78875
commit 50fcfd2d3d
571 changed files with 73992 additions and 135 deletions
@@ -0,0 +1,47 @@
<template>
<n-form class="py-4">
<n-form-item label="测试入口" path="index">
<n-input-group>
<n-input placeholder="请输入" :default-value="url" :disabled="true" />
<n-button v-copy="url" type="primary" @click="copy"> 复制链接 </n-button>
</n-input-group>
</n-form-item>
<n-form-item label="二维码" path="index" v-if="showQrcode">
<div class="text-center">
<qrcode-vue :value="url" :size="220" class="canvas" style="margin: 0 auto" />
</div>
</n-form-item>
</n-form>
</template>
<script lang="ts" setup>
import { useUserStoreWidthOut } from '@/store/modules/user';
import QrcodeVue from 'qrcode.vue';
import { useMessage } from 'naive-ui';
const message = useMessage();
interface Props {
path: string;
showQrcode?: boolean;
}
const props = withDefaults(defineProps<Props>(), {
path: '',
showQrcode: true,
});
const copy = () => {
message.success('复制成功');
};
const useUserStore = useUserStoreWidthOut();
const url = useUserStore.config?.domain + props.path;
</script>
<style scoped>
::v-deep(.card-tabs .n-tabs-nav--bar-type) {
padding-left: 4px;
}
</style>
@@ -0,0 +1,35 @@
<template>
<div>
<n-card title="应用入口" class="proCard">
<n-tabs type="line" animated>
<n-tab-pane name="admin" tab="后台API">
<Form path="/admin/hgexample/index/test?name=HotGo" />
</n-tab-pane>
<n-tab-pane name="api" tab="前台API">
<Form path="/api/hgexample/index/test?name=HotGo" />
</n-tab-pane>
<n-tab-pane name="home" tab="前台页面">
<Form path="/home/hgexample/index/test?name=HotGo" />
</n-tab-pane>
<n-tab-pane name="static" tab="静态目录">
<Form path="/addons/hgexample/default" />
</n-tab-pane>
<n-tab-pane name="websocket" tab="Websocket">
<Form path="/socket/hgexample/index/test?name=HotGo" :showQrcode="false" />
<WebsocketTest />
</n-tab-pane>
</n-tabs>
</n-card>
</div>
</template>
<script lang="ts" setup>
import Form from './form.vue';
import WebsocketTest from './websocketTest.vue';
</script>
<style scoped>
::v-deep(.card-tabs .n-tabs-nav--bar-type) {
padding-left: 4px;
}
</style>
@@ -0,0 +1,134 @@
<template>
<div>
<div class="n-layout-page-header">
<n-card :bordered="false" title="测试websocket">
尝试在下方输入框中输入任意文字消息内容,发送后websocket服务器收到会原样返回
</n-card>
</div>
<n-card :bordered="false" class="proCard">
<n-space vertical>
<n-input-group style="width: 520px">
<n-input
@keyup.enter="sendMessage"
:style="{ width: '78%' }"
placeholder="请输入消息内容"
:on-focus="onFocus"
:on-blur="onBlur"
v-model:value="inputMessage"
/>
<n-button type="primary" @click="sendMessage"> 发送消息</n-button>
</n-input-group>
<div class="mt-5"></div>
<n-timeline :icon-size="20">
<n-timeline-item color="grey" content="输入中.." v-if="isInput">
<template #icon>
<n-icon>
<MessageOutlined />
</n-icon>
</template>
</n-timeline-item>
<n-timeline-item
v-for="item in messages"
:key="item"
:type="item.type == Enum.SendType ? 'success' : 'info'"
:title="item.type == Enum.SendType ? '发送消息' : '收到消息'"
:content="item.content"
:time="item.time"
>
<template #icon>
<n-icon>
<SendOutlined v-if="item.type == Enum.SendType" />
<SoundOutlined v-if="item.type == Enum.ReceiveType" />
</n-icon>
</template>
</n-timeline-item>
</n-timeline>
</n-space>
</n-card>
</div>
</template>
<script lang="ts" setup>
import { onBeforeUnmount, onMounted, ref } from 'vue';
import { MessageOutlined, SendOutlined, SoundOutlined } from '@vicons/antd';
import { format } from 'date-fns';
import { addOnMessage, removeOnMessage, sendMsg, WebSocketMessage } from '@/utils/websocket';
import { useMessage } from 'naive-ui';
interface Message {
type: Enum;
content: string;
time: string;
}
const message = useMessage();
const messages = ref<Message[]>([]);
const inputMessage = ref('你好,HotGo');
const isInput = ref(false);
const testMessageEvent = 'admin/addons/hgexample/testMessage';
enum Enum {
SendType = 1, // 发送类型
ReceiveType = 2, // 接受类型
}
function onFocus() {
isInput.value = true;
}
function onBlur() {
isInput.value = false;
}
function sendMessage() {
if (inputMessage.value == '') {
message.error('消息内容不能为空');
return;
}
// 发送消息
sendMsg(testMessageEvent, {
message: inputMessage.value,
});
const msg: Message = {
type: Enum.SendType,
content: inputMessage.value,
time: format(new Date(), 'yyyy-MM-dd HH:mm:ss'),
};
insertMessage(msg);
inputMessage.value = '';
}
// 存入本地记录
function insertMessage(msg: Message): void {
messages.value.unshift(msg); // 在头部插入消息
if (messages.value.length > 10) {
messages.value = messages.value.slice(0, 10); // 如果超过10个,则只保留最前面10个
}
}
const onMessage = (res: WebSocketMessage) => {
const msg: Message = {
type: Enum.ReceiveType,
content: res.data.message,
time: format(new Date(), 'yyyy-MM-dd HH:mm:ss'),
};
insertMessage(msg);
};
onMounted(() => {
// 在当前页面注册消息监听
addOnMessage(testMessageEvent, onMessage);
});
onBeforeUnmount(() => {
// 移除消息监听
removeOnMessage(testMessageEvent);
});
</script>
<style scoped></style>