11
This commit is contained in:
@@ -3,7 +3,6 @@ using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace Application.Web.Controllers.Pub
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// 公共接口
|
||||
/// </summary>
|
||||
@@ -12,20 +11,31 @@ namespace Application.Web.Controllers.Pub
|
||||
public class PubController : ControllerBase
|
||||
{
|
||||
private readonly IAreaService _areaService;
|
||||
public PubController(IAreaService areaService)
|
||||
{
|
||||
private readonly INoticeService _noticeService;
|
||||
public PubController(IAreaService areaService,INoticeService noticeService)
|
||||
{
|
||||
_areaService = areaService;
|
||||
_noticeService = noticeService;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取首页信息
|
||||
/// </summary>
|
||||
/// <param name="sid"></param>
|
||||
/// <returns></returns>
|
||||
[HttpGet]
|
||||
public async Task<IPoAction> GetMain()
|
||||
public async Task<IPoAction> GetMain(string sid)
|
||||
{
|
||||
bool isOnline = false;
|
||||
if (!string.IsNullOrEmpty(sid))
|
||||
{
|
||||
isOnline = true;
|
||||
}
|
||||
|
||||
var areaData = await _areaService.GetAreaData();
|
||||
return PoAction.Ok(new{area=areaData});
|
||||
var notice = await _noticeService.GetNoticeDataByTake(5);
|
||||
|
||||
return PoAction.Ok(new { area = areaData ,notice,isOnline});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,592 +0,0 @@
|
||||
<template>
|
||||
<div class="page-login">
|
||||
<div class="login-orb login-orb-a"></div>
|
||||
<div class="login-orb login-orb-b"></div>
|
||||
|
||||
<div class="login-shell">
|
||||
<section class="login-hero">
|
||||
<p class="hero-kicker">SEA TIME</p>
|
||||
<h1>欢迎回到航海时代</h1>
|
||||
<p class="hero-copy">
|
||||
这是一套移动端登录页示例,保留 Vant 的按钮与标签组件,表单主体改为 SSR 稳定写法。
|
||||
</p>
|
||||
|
||||
<div class="hero-pills">
|
||||
<span>移动端优先</span>
|
||||
<span>SSR 安全</span>
|
||||
<span>模拟登录</span>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="login-card">
|
||||
<div class="card-top">
|
||||
<div>
|
||||
<p class="card-eyebrow">账号登录</p>
|
||||
<h2>继续你的航程</h2>
|
||||
</div>
|
||||
<van-tag round plain type="primary">Sample</van-tag>
|
||||
</div>
|
||||
|
||||
<div class="card-banner">
|
||||
<span class="banner-dot"></span>
|
||||
默认可直接使用演示账号,也可以手动输入任意符合规则的内容。
|
||||
</div>
|
||||
|
||||
<div class="demo-account">
|
||||
<div>
|
||||
<strong>演示账号</strong>
|
||||
<span>captain</span>
|
||||
</div>
|
||||
<div>
|
||||
<strong>演示密码</strong>
|
||||
<span>Voyage123</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<form class="login-form" novalidate @submit.prevent="handleSubmit">
|
||||
<div class="field-group">
|
||||
<label class="field-label" for="login-username">账号</label>
|
||||
<div class="field-box" :class="{ 'is-error': !!errors.username }">
|
||||
<span class="field-marker">A</span>
|
||||
<input id="login-username" v-model.trim="form.username" class="field-input" type="text" inputmode="text"
|
||||
autocomplete="username" placeholder="请输入账号" @input="clearFieldError('username')" />
|
||||
</div>
|
||||
<p v-if="errors.username" class="field-error">{{ errors.username }}</p>
|
||||
</div>
|
||||
|
||||
<div class="field-group">
|
||||
<label class="field-label" for="login-password">密码</label>
|
||||
<div class="field-box" :class="{ 'is-error': !!errors.password }">
|
||||
<span class="field-marker">P</span>
|
||||
<input id="login-password" v-model.trim="form.password" class="field-input" type="password"
|
||||
autocomplete="current-password" placeholder="请输入密码" @input="clearFieldError('password')" />
|
||||
</div>
|
||||
<p v-if="errors.password" class="field-error">{{ errors.password }}</p>
|
||||
</div>
|
||||
|
||||
<div class="login-options">
|
||||
<label class="check-row">
|
||||
<input v-model="form.remember" class="check-input" type="checkbox" />
|
||||
<span class="check-box"></span>
|
||||
<span class="check-text">记住演示账号</span>
|
||||
</label>
|
||||
|
||||
<button class="ghost-link" type="button" @click="fillDemoAccount">一键填充</button>
|
||||
<van-button type="primary" to="/home" size="mini" :loading="false">路由跳转</van-button>
|
||||
</div>
|
||||
|
||||
<div class="login-agreement">
|
||||
<label class="check-row">
|
||||
<input v-model="form.agreement" class="check-input" type="checkbox"
|
||||
@change="clearFieldError('agreement')" />
|
||||
<span class="check-box"></span>
|
||||
<span class="check-text">我已阅读并同意演示使用说明</span>
|
||||
</label>
|
||||
<p v-if="errors.agreement" class="field-error agreement-error">{{ errors.agreement }}</p>
|
||||
</div>
|
||||
|
||||
<div class="login-actions">
|
||||
<van-button block round type="primary" native-type="submit" :loading="isSubmitting"
|
||||
:disabled="submitDisabled">
|
||||
模拟登录
|
||||
</van-button>
|
||||
<van-button block round plain type="primary" native-type="button" @click="clearForm">
|
||||
清空输入
|
||||
</van-button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<div class="login-tips">
|
||||
<div class="tip-item">
|
||||
<span>01</span>
|
||||
去掉 ClientOnly,刷新时不再有表单占位替换。
|
||||
</div>
|
||||
<div class="tip-item">
|
||||
<span>02</span>
|
||||
提交后延迟模拟请求,并跳转到主页。
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { IUserInfo } from '~/types/user'
|
||||
|
||||
definePageMeta({
|
||||
layout: layout.empty
|
||||
})
|
||||
|
||||
type ErrorField = 'username' | 'password' | 'agreement'
|
||||
|
||||
const isSubmitting = ref(false)
|
||||
const userStore = useUserStore()
|
||||
const { emit } = useEventBus()
|
||||
|
||||
const form = reactive({
|
||||
username: '',
|
||||
password: '',
|
||||
remember: true,
|
||||
agreement: true
|
||||
})
|
||||
|
||||
const errors = reactive<Record<ErrorField, string>>({
|
||||
username: '',
|
||||
password: '',
|
||||
agreement: ''
|
||||
})
|
||||
|
||||
const submitDisabled = computed(() => {
|
||||
return !form.username.trim() || !form.password.trim() || !form.agreement
|
||||
})
|
||||
|
||||
const clearFieldError = (field: ErrorField) => {
|
||||
errors[field] = ''
|
||||
}
|
||||
|
||||
const validateForm = () => {
|
||||
errors.username = ''
|
||||
errors.password = ''
|
||||
errors.agreement = ''
|
||||
|
||||
if (!form.username.trim()) {
|
||||
errors.username = '请输入账号'
|
||||
} else if (form.username.trim().length < 3) {
|
||||
errors.username = '账号至少 3 位'
|
||||
}
|
||||
|
||||
if (!form.password.trim()) {
|
||||
errors.password = '请输入密码'
|
||||
} else if (form.password.trim().length < 6) {
|
||||
errors.password = '密码至少 6 位'
|
||||
}
|
||||
|
||||
if (!form.agreement) {
|
||||
errors.agreement = '请先勾选使用说明'
|
||||
}
|
||||
|
||||
return !errors.username && !errors.password && !errors.agreement
|
||||
}
|
||||
|
||||
const fillDemoAccount = () => {
|
||||
form.username = 'captain'
|
||||
form.password = 'Voyage123'
|
||||
errors.username = ''
|
||||
errors.password = ''
|
||||
showToast('已填充演示账号')
|
||||
}
|
||||
|
||||
const clearForm = () => {
|
||||
form.username = ''
|
||||
form.password = ''
|
||||
form.agreement = true
|
||||
errors.username = ''
|
||||
errors.password = ''
|
||||
errors.agreement = ''
|
||||
showToast('已清空输入')
|
||||
}
|
||||
|
||||
const handleSubmit = async () => {
|
||||
if (!validateForm()) {
|
||||
showFailToast(Object.values(errors).find(Boolean) || '请检查表单内容')
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
isSubmitting.value = true
|
||||
await new Promise((resolve) => setTimeout(resolve, 900))
|
||||
|
||||
const loginAt = new Date().toISOString()
|
||||
const mockUserInfo: IUserInfo = {
|
||||
id: Date.now(),
|
||||
username: form.username.trim(),
|
||||
nickname: form.username.trim(),
|
||||
role: 'user'
|
||||
}
|
||||
|
||||
userStore.setUserInfo(mockUserInfo, `demo-token-${mockUserInfo.id}`)
|
||||
emit('auth:login', {
|
||||
userId: mockUserInfo.id || 0,
|
||||
username: mockUserInfo.username || '',
|
||||
nickname: mockUserInfo.nickname || '',
|
||||
loginAt
|
||||
})
|
||||
|
||||
showSuccessToast('登录成功')
|
||||
await navigateTo('/home')
|
||||
} finally {
|
||||
isSubmitting.value = false
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.page-login {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
min-height: 100vh;
|
||||
padding: 28px 16px 40px;
|
||||
background:
|
||||
linear-gradient(180deg, #0b3558 0%, #0f2943 46%, #f4f8fc 46%, #f4f8fc 100%);
|
||||
}
|
||||
|
||||
.login-orb {
|
||||
position: absolute;
|
||||
border-radius: 999px;
|
||||
filter: blur(4px);
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.login-orb-a {
|
||||
top: -36px;
|
||||
right: -22px;
|
||||
width: 148px;
|
||||
height: 148px;
|
||||
background: radial-gradient(circle, rgba(103, 232, 249, 0.56), rgba(103, 232, 249, 0));
|
||||
}
|
||||
|
||||
.login-orb-b {
|
||||
top: 168px;
|
||||
left: -52px;
|
||||
width: 180px;
|
||||
height: 180px;
|
||||
background: radial-gradient(circle, rgba(96, 165, 250, 0.24), rgba(96, 165, 250, 0));
|
||||
}
|
||||
|
||||
.login-shell {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
max-width: 420px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.login-hero {
|
||||
padding: 8px 6px 24px;
|
||||
color: #f8fbff;
|
||||
}
|
||||
|
||||
.hero-kicker {
|
||||
margin: 0 0 10px;
|
||||
font-size: 12px;
|
||||
font-weight: 700;
|
||||
letter-spacing: 0.28em;
|
||||
color: rgba(186, 230, 253, 0.92);
|
||||
}
|
||||
|
||||
.login-hero h1 {
|
||||
margin: 0;
|
||||
font-size: 34px;
|
||||
line-height: 1.08;
|
||||
letter-spacing: 0.01em;
|
||||
}
|
||||
|
||||
.hero-copy {
|
||||
margin: 14px 0 0;
|
||||
font-size: 14px;
|
||||
line-height: 1.7;
|
||||
color: rgba(226, 232, 240, 0.92);
|
||||
}
|
||||
|
||||
.hero-pills {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 8px;
|
||||
margin-top: 18px;
|
||||
}
|
||||
|
||||
.hero-pills span {
|
||||
padding: 6px 10px;
|
||||
border: 1px solid rgba(186, 230, 253, 0.18);
|
||||
border-radius: 999px;
|
||||
font-size: 12px;
|
||||
color: #dbeafe;
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
.login-card {
|
||||
margin-top: 8px;
|
||||
padding: 22px 16px 18px;
|
||||
border-radius: 28px;
|
||||
background: #ffffff;
|
||||
box-shadow: 0 28px 60px rgba(15, 23, 42, 0.14);
|
||||
}
|
||||
|
||||
.card-top {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
justify-content: space-between;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.card-eyebrow {
|
||||
margin: 0 0 6px;
|
||||
font-size: 12px;
|
||||
color: #64748b;
|
||||
}
|
||||
|
||||
.card-top h2 {
|
||||
margin: 0;
|
||||
font-size: 24px;
|
||||
color: #0f172a;
|
||||
}
|
||||
|
||||
.card-banner {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
margin-top: 16px;
|
||||
padding: 12px 14px;
|
||||
border-radius: 16px;
|
||||
font-size: 13px;
|
||||
line-height: 1.6;
|
||||
color: #0f3d62;
|
||||
background: linear-gradient(135deg, #e0f2fe, #eff6ff);
|
||||
}
|
||||
|
||||
.banner-dot {
|
||||
flex: 0 0 auto;
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
border-radius: 50%;
|
||||
background: linear-gradient(135deg, #0ea5e9, #2563eb);
|
||||
box-shadow: 0 0 0 4px rgba(14, 165, 233, 0.12);
|
||||
}
|
||||
|
||||
.demo-account {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
gap: 12px;
|
||||
margin-top: 14px;
|
||||
}
|
||||
|
||||
.demo-account div {
|
||||
padding: 12px 14px;
|
||||
border-radius: 18px;
|
||||
background: #f8fafc;
|
||||
}
|
||||
|
||||
.demo-account strong,
|
||||
.demo-account span {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.demo-account strong {
|
||||
margin-bottom: 6px;
|
||||
font-size: 12px;
|
||||
color: #64748b;
|
||||
}
|
||||
|
||||
.demo-account span {
|
||||
font-size: 15px;
|
||||
font-weight: 700;
|
||||
color: #0f172a;
|
||||
}
|
||||
|
||||
.login-form {
|
||||
margin-top: 18px;
|
||||
}
|
||||
|
||||
.field-group+.field-group {
|
||||
margin-top: 14px;
|
||||
}
|
||||
|
||||
.field-label {
|
||||
display: block;
|
||||
margin-bottom: 8px;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
color: #334155;
|
||||
}
|
||||
|
||||
.field-box {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
min-height: 52px;
|
||||
padding: 0 14px;
|
||||
border: 1px solid #dbe4f0;
|
||||
border-radius: 18px;
|
||||
background: #f8fafc;
|
||||
transition: border-color 0.2s ease, box-shadow 0.2s ease, background-color 0.2s ease;
|
||||
}
|
||||
|
||||
.field-box:focus-within {
|
||||
border-color: #3b82f6;
|
||||
background: #ffffff;
|
||||
box-shadow: 0 0 0 4px rgba(59, 130, 246, 0.1);
|
||||
}
|
||||
|
||||
.field-box.is-error {
|
||||
border-color: #f87171;
|
||||
}
|
||||
|
||||
.field-marker {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
border-radius: 8px;
|
||||
font-size: 12px;
|
||||
font-weight: 700;
|
||||
color: #2563eb;
|
||||
background: #dbeafe;
|
||||
}
|
||||
|
||||
.field-input {
|
||||
flex: 1;
|
||||
width: 100%;
|
||||
min-width: 0;
|
||||
border: 0;
|
||||
background: transparent;
|
||||
color: #0f172a;
|
||||
}
|
||||
|
||||
.field-input::placeholder {
|
||||
color: #94a3b8;
|
||||
}
|
||||
|
||||
.field-input:-webkit-autofill,
|
||||
.field-input:-webkit-autofill:hover,
|
||||
.field-input:-webkit-autofill:focus {
|
||||
-webkit-text-fill-color: #0f172a;
|
||||
box-shadow: 0 0 0 1000px #f8fafc inset;
|
||||
}
|
||||
|
||||
.field-error {
|
||||
margin: 6px 0 0;
|
||||
font-size: 12px;
|
||||
color: #dc2626;
|
||||
}
|
||||
|
||||
.login-options,
|
||||
.login-agreement {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 12px;
|
||||
padding: 16px 2px 0;
|
||||
}
|
||||
|
||||
.login-agreement {
|
||||
display: block;
|
||||
padding-top: 12px;
|
||||
}
|
||||
|
||||
.agreement-error {
|
||||
margin-left: 32px;
|
||||
}
|
||||
|
||||
.check-row {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
color: #475569;
|
||||
}
|
||||
|
||||
.check-input {
|
||||
position: absolute;
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.check-box {
|
||||
position: relative;
|
||||
flex: 0 0 auto;
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
border: 1.5px solid #cbd5e1;
|
||||
border-radius: 6px;
|
||||
background: #ffffff;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.check-box::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 1px;
|
||||
left: 5px;
|
||||
width: 4px;
|
||||
height: 9px;
|
||||
border-right: 2px solid #ffffff;
|
||||
border-bottom: 2px solid #ffffff;
|
||||
opacity: 0;
|
||||
transform: rotate(45deg);
|
||||
}
|
||||
|
||||
.check-input:checked+.check-box {
|
||||
border-color: #2563eb;
|
||||
background: #2563eb;
|
||||
}
|
||||
|
||||
.check-input:checked+.check-box::after {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.check-text {
|
||||
font-size: 13px;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.ghost-link {
|
||||
padding: 0;
|
||||
border: 0;
|
||||
color: #2563eb;
|
||||
background: transparent;
|
||||
appearance: none;
|
||||
-webkit-appearance: none;
|
||||
}
|
||||
|
||||
.login-actions {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
padding-top: 18px;
|
||||
}
|
||||
|
||||
.login-tips {
|
||||
display: grid;
|
||||
gap: 10px;
|
||||
margin-top: 18px;
|
||||
}
|
||||
|
||||
.tip-item {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
padding: 12px 14px;
|
||||
border-radius: 16px;
|
||||
font-size: 13px;
|
||||
line-height: 1.6;
|
||||
color: #334155;
|
||||
background: #f8fafc;
|
||||
}
|
||||
|
||||
.tip-item span {
|
||||
flex: 0 0 auto;
|
||||
width: 26px;
|
||||
height: 26px;
|
||||
border-radius: 10px;
|
||||
font-size: 12px;
|
||||
font-weight: 700;
|
||||
line-height: 26px;
|
||||
text-align: center;
|
||||
color: #2563eb;
|
||||
background: #dbeafe;
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.page-login {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 36px 20px;
|
||||
}
|
||||
|
||||
.login-shell {
|
||||
max-width: 460px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,598 +0,0 @@
|
||||
<template>
|
||||
<div class="im-page">
|
||||
<section class="hero-card">
|
||||
<div>
|
||||
<p class="eyebrow">SignalR IM Demo</p>
|
||||
<h1>即时通讯测试页</h1>
|
||||
<p class="hero-text">
|
||||
当前页面会连接服务端 `ChatHub`,默认调用 `SendMessage(user, message)`,并监听 `ReceiveMessage`、`UserConnected`、`UserDisconnected`。收到聊天消息时会同步发出全局事件 `chat:received`。
|
||||
</p>
|
||||
</div>
|
||||
<div class="status-box">
|
||||
<span class="status-label">连接状态</span>
|
||||
<strong :class="['status-pill', `is-${status}`]">{{ statusText }}</strong>
|
||||
<span v-if="connectionId" class="connection-id">连接 ID:{{ connectionId }}</span>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="panel">
|
||||
<div class="panel-title">连接配置</div>
|
||||
<div class="field-grid">
|
||||
<label class="field">
|
||||
<span>Hub 地址</span>
|
||||
<input v-model.trim="hubUrl" type="text" placeholder="https://localhost:7198/chatHub">
|
||||
</label>
|
||||
<label class="field">
|
||||
<span>当前昵称</span>
|
||||
<input v-model.trim="nickname" type="text" placeholder="请输入发送昵称">
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<label class="field">
|
||||
<span>Bearer Token</span>
|
||||
<textarea
|
||||
v-model.trim="accessToken"
|
||||
rows="3"
|
||||
placeholder="如果服务端开启鉴权,可以在这里粘贴 token;为空则匿名连接。"
|
||||
/>
|
||||
</label>
|
||||
|
||||
<div class="action-row">
|
||||
<button class="primary-btn" :disabled="isConnecting || isConnected" @click="connectHub">连接</button>
|
||||
<button class="ghost-btn" :disabled="!isConnected" @click="disconnectHub">断开</button>
|
||||
<button class="ghost-btn" @click="resetToken">读取本地 Token</button>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="panel">
|
||||
<div class="panel-title">消息面板</div>
|
||||
<div ref="messageListRef" class="message-list">
|
||||
<div v-if="!messages.length" class="empty-state">还没有消息,先连接再发一条试试。</div>
|
||||
<article
|
||||
v-for="item in messages"
|
||||
:key="item.id"
|
||||
:class="['message-item', item.type === 'system' ? 'is-system' : item.user === nickname ? 'is-self' : '']"
|
||||
>
|
||||
<div class="message-meta">
|
||||
<strong>{{ item.user }}</strong>
|
||||
<span>{{ item.time }}</span>
|
||||
</div>
|
||||
<p>{{ item.message }}</p>
|
||||
</article>
|
||||
</div>
|
||||
|
||||
<label class="field">
|
||||
<span>发送内容</span>
|
||||
<textarea
|
||||
v-model.trim="draftMessage"
|
||||
rows="3"
|
||||
placeholder="输入消息后点击发送"
|
||||
@keydown.ctrl.enter.exact.prevent="sendMessage"
|
||||
/>
|
||||
</label>
|
||||
|
||||
<div class="action-row">
|
||||
<button class="primary-btn" :disabled="!isConnected || !draftMessage" @click="sendMessage">发送消息</button>
|
||||
<button class="ghost-btn" @click="clearMessages">清空消息</button>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="panel">
|
||||
<div class="panel-title">运行日志</div>
|
||||
<div class="log-list">
|
||||
<p v-if="!logs.length" class="empty-state">暂无日志。</p>
|
||||
<p v-for="item in logs" :key="item.id" class="log-item">{{ item.text }}</p>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { HubConnection } from '@microsoft/signalr'
|
||||
import { BaseConfig } from '@/config/BaseConfig'
|
||||
|
||||
definePageMeta({
|
||||
layout: layout.default
|
||||
})
|
||||
|
||||
type ConnectionStatus = 'disconnected' | 'connecting' | 'connected' | 'error'
|
||||
type MessageType = 'chat' | 'system'
|
||||
|
||||
interface ChatMessage {
|
||||
id: string
|
||||
user: string
|
||||
message: string
|
||||
time: string
|
||||
type: MessageType
|
||||
}
|
||||
|
||||
interface LogItem {
|
||||
id: string
|
||||
text: string
|
||||
}
|
||||
|
||||
const userStore = useUserStore()
|
||||
const { emit } = useEventBus()
|
||||
|
||||
const defaultHubUrl = `${BaseConfig.BaseUrl.replace(/\/$/, '')}/chatHub`
|
||||
|
||||
const hubUrl = ref(defaultHubUrl)
|
||||
const nickname = ref('')
|
||||
const accessToken = ref('')
|
||||
const draftMessage = ref('')
|
||||
const status = ref<ConnectionStatus>('disconnected')
|
||||
const connectionId = ref('')
|
||||
const messages = ref<ChatMessage[]>([])
|
||||
const logs = ref<LogItem[]>([])
|
||||
const connection = shallowRef<HubConnection | null>(null)
|
||||
const messageListRef = useTemplateRef<HTMLDivElement>('messageListRef')
|
||||
|
||||
const isConnected = computed(() => status.value === 'connected')
|
||||
const isConnecting = computed(() => status.value === 'connecting')
|
||||
const statusText = computed(() => {
|
||||
switch (status.value) {
|
||||
case 'connecting':
|
||||
return '连接中'
|
||||
case 'connected':
|
||||
return '已连接'
|
||||
case 'error':
|
||||
return '连接异常'
|
||||
default:
|
||||
return '未连接'
|
||||
}
|
||||
})
|
||||
|
||||
const buildId = () => `${Date.now()}-${Math.random().toString(16).slice(2)}`
|
||||
|
||||
const getNowText = () =>
|
||||
new Date().toLocaleTimeString('zh-CN', {
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
second: '2-digit'
|
||||
})
|
||||
|
||||
const addLog = (text: string) => {
|
||||
logs.value.unshift({
|
||||
id: buildId(),
|
||||
text: `[${getNowText()}] ${text}`
|
||||
})
|
||||
|
||||
logs.value = logs.value.slice(0, 60)
|
||||
}
|
||||
|
||||
const addMessage = (user: string, message: string, type: MessageType = 'chat') => {
|
||||
messages.value.push({
|
||||
id: buildId(),
|
||||
user,
|
||||
message,
|
||||
time: getNowText(),
|
||||
type
|
||||
})
|
||||
|
||||
void nextTick(() => {
|
||||
const target = messageListRef.value
|
||||
if (target) {
|
||||
target.scrollTop = target.scrollHeight
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const resolveHubUrl = (value: string) => {
|
||||
const finalValue = value.trim() || defaultHubUrl
|
||||
|
||||
if (/^https?:\/\//i.test(finalValue)) {
|
||||
return finalValue
|
||||
}
|
||||
|
||||
if (import.meta.client && finalValue.startsWith('/')) {
|
||||
return `${window.location.origin}${finalValue}`
|
||||
}
|
||||
|
||||
return finalValue
|
||||
}
|
||||
|
||||
const registerConnectionEvents = (hub: HubConnection) => {
|
||||
hub.on('ReceiveMessage', (user: string, message: string) => {
|
||||
const sender = user || '匿名用户'
|
||||
const text = message || ''
|
||||
|
||||
addMessage(sender, text)
|
||||
emit('chat:received', {
|
||||
user: sender,
|
||||
message: text,
|
||||
receivedAt: new Date().toISOString(),
|
||||
type: 'chat'
|
||||
})
|
||||
})
|
||||
|
||||
hub.on('UserConnected', (id: string) => {
|
||||
addMessage('系统', `用户已连接:${id}`, 'system')
|
||||
addLog(`收到 UserConnected 事件:${id}`)
|
||||
})
|
||||
|
||||
hub.on('UserDisconnected', (id: string) => {
|
||||
addMessage('系统', `用户已断开:${id}`, 'system')
|
||||
addLog(`收到 UserDisconnected 事件:${id}`)
|
||||
})
|
||||
|
||||
hub.onreconnecting((error) => {
|
||||
status.value = 'connecting'
|
||||
addLog(`连接重试中:${error?.message || '网络波动'}`)
|
||||
})
|
||||
|
||||
hub.onreconnected((id) => {
|
||||
status.value = 'connected'
|
||||
connectionId.value = id || hub.connectionId || ''
|
||||
addLog(`重连成功:${connectionId.value || '无连接 ID'}`)
|
||||
})
|
||||
|
||||
hub.onclose((error) => {
|
||||
status.value = error ? 'error' : 'disconnected'
|
||||
connectionId.value = ''
|
||||
addLog(`连接关闭:${error?.message || '已主动断开'}`)
|
||||
})
|
||||
}
|
||||
|
||||
const createConnection = async () => {
|
||||
const signalR = await import('@microsoft/signalr')
|
||||
|
||||
const hub = new signalR.HubConnectionBuilder()
|
||||
.withUrl(resolveHubUrl(hubUrl.value), {
|
||||
withCredentials: false,
|
||||
accessTokenFactory: () => accessToken.value.trim()
|
||||
})
|
||||
.withAutomaticReconnect()
|
||||
.configureLogging(signalR.LogLevel.Information)
|
||||
.build()
|
||||
|
||||
registerConnectionEvents(hub)
|
||||
return hub
|
||||
}
|
||||
|
||||
const connectHub = async () => {
|
||||
if (isConnecting.value || isConnected.value) {
|
||||
return
|
||||
}
|
||||
|
||||
if (!nickname.value.trim()) {
|
||||
nickname.value = `游客${Math.floor(Math.random() * 1000)}`
|
||||
}
|
||||
|
||||
try {
|
||||
status.value = 'connecting'
|
||||
addLog(`开始连接:${resolveHubUrl(hubUrl.value)}`)
|
||||
|
||||
if (connection.value) {
|
||||
await connection.value.stop()
|
||||
connection.value = null
|
||||
}
|
||||
|
||||
const hub = await createConnection()
|
||||
await hub.start()
|
||||
|
||||
connection.value = hub
|
||||
connectionId.value = hub.connectionId || ''
|
||||
status.value = 'connected'
|
||||
|
||||
addLog(`连接成功:${connectionId.value || '未返回连接 ID'}`)
|
||||
addMessage('系统', 'SignalR 已连接,可以开始发送消息。', 'system')
|
||||
} catch (error) {
|
||||
status.value = 'error'
|
||||
connectionId.value = ''
|
||||
connection.value = null
|
||||
|
||||
const message = error instanceof Error ? error.message : '未知错误'
|
||||
addLog(`连接失败:${message}`)
|
||||
addMessage('系统', `连接失败:${message}`, 'system')
|
||||
}
|
||||
}
|
||||
|
||||
const disconnectHub = async () => {
|
||||
if (!connection.value) {
|
||||
status.value = 'disconnected'
|
||||
return
|
||||
}
|
||||
|
||||
await connection.value.stop()
|
||||
connection.value = null
|
||||
status.value = 'disconnected'
|
||||
connectionId.value = ''
|
||||
addMessage('系统', '已主动断开连接。', 'system')
|
||||
}
|
||||
|
||||
const sendMessage = async () => {
|
||||
const message = draftMessage.value.trim()
|
||||
if (!connection.value || !isConnected.value || !message) {
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
await connection.value.invoke('SendMessage', nickname.value.trim(), message)
|
||||
addLog(`发送成功:${message}`)
|
||||
draftMessage.value = ''
|
||||
} catch (error) {
|
||||
const text = error instanceof Error ? error.message : '未知错误'
|
||||
addLog(`发送失败:${text}`)
|
||||
addMessage('系统', `发送失败:${text}`, 'system')
|
||||
}
|
||||
}
|
||||
|
||||
const clearMessages = () => {
|
||||
messages.value = []
|
||||
}
|
||||
|
||||
const resetToken = () => {
|
||||
if (!import.meta.client) {
|
||||
return
|
||||
}
|
||||
|
||||
accessToken.value = userStore.token || localStorage.getItem('token') || ''
|
||||
addLog(accessToken.value ? '已读取本地 token。' : '未读取到本地 token。')
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
nickname.value = userStore.userNickname || `游客${Math.floor(Math.random() * 1000)}`
|
||||
resetToken()
|
||||
})
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
if (connection.value) {
|
||||
void connection.value.stop()
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.im-page,
|
||||
.im-page div,
|
||||
.im-page section,
|
||||
.im-page article,
|
||||
.im-page p,
|
||||
.im-page h1,
|
||||
.im-page span,
|
||||
.im-page strong {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.im-page {
|
||||
min-height: 100vh;
|
||||
padding: 20px 16px 40px;
|
||||
background:
|
||||
radial-gradient(circle at top right, rgba(217, 83, 79, 0.14), transparent 32%),
|
||||
linear-gradient(180deg, #f7efe4 0%, #f4f0ea 100%);
|
||||
color: #2a221d;
|
||||
}
|
||||
|
||||
.hero-card,
|
||||
.panel {
|
||||
width: min(960px, 100%);
|
||||
margin: 0 auto 16px;
|
||||
border: 1px solid rgba(71, 50, 38, 0.14);
|
||||
border-radius: 20px;
|
||||
background: rgba(255, 250, 244, 0.92);
|
||||
box-shadow: 0 16px 36px rgba(54, 38, 29, 0.08);
|
||||
}
|
||||
|
||||
.hero-card {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 1fr) auto;
|
||||
gap: 16px;
|
||||
padding: 22px;
|
||||
}
|
||||
|
||||
.eyebrow {
|
||||
font-size: 12px;
|
||||
letter-spacing: 0.18em;
|
||||
text-transform: uppercase;
|
||||
color: #8d5d48;
|
||||
}
|
||||
|
||||
h1 {
|
||||
margin-top: 8px;
|
||||
font-size: 28px;
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
.hero-text {
|
||||
margin-top: 10px;
|
||||
max-width: 640px;
|
||||
color: #665246;
|
||||
font-size: 14px;
|
||||
line-height: 1.7;
|
||||
}
|
||||
|
||||
.status-box {
|
||||
display: flex;
|
||||
min-width: 180px;
|
||||
flex-direction: column;
|
||||
align-items: flex-end;
|
||||
justify-content: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.status-label,
|
||||
.connection-id,
|
||||
.field span,
|
||||
.panel-title {
|
||||
color: #7d6557;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.status-pill {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-width: 84px;
|
||||
padding: 8px 14px;
|
||||
border-radius: 999px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.status-pill.is-disconnected,
|
||||
.status-pill.is-error {
|
||||
background: rgba(217, 83, 79, 0.14);
|
||||
color: #b53d38;
|
||||
}
|
||||
|
||||
.status-pill.is-connecting {
|
||||
background: rgba(254, 94, 8, 0.14);
|
||||
color: #d46514;
|
||||
}
|
||||
|
||||
.status-pill.is-connected {
|
||||
background: rgba(25, 135, 84, 0.14);
|
||||
color: #198754;
|
||||
}
|
||||
|
||||
.panel {
|
||||
padding: 18px;
|
||||
}
|
||||
|
||||
.panel-title {
|
||||
margin-bottom: 14px;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.field-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.field {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
margin-bottom: 14px;
|
||||
}
|
||||
|
||||
.field input,
|
||||
.field textarea {
|
||||
width: 100%;
|
||||
border: 1px solid rgba(98, 77, 65, 0.18);
|
||||
border-radius: 14px;
|
||||
padding: 12px 14px;
|
||||
background: #fffdfa;
|
||||
color: #2a221d;
|
||||
font-size: 14px;
|
||||
outline: none;
|
||||
transition: border-color 0.2s ease, box-shadow 0.2s ease;
|
||||
}
|
||||
|
||||
.field input:focus,
|
||||
.field textarea:focus {
|
||||
border-color: #d46514;
|
||||
box-shadow: 0 0 0 3px rgba(254, 94, 8, 0.14);
|
||||
}
|
||||
|
||||
.field textarea {
|
||||
resize: vertical;
|
||||
min-height: 88px;
|
||||
}
|
||||
|
||||
.action-row {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.primary-btn,
|
||||
.ghost-btn {
|
||||
border: 0;
|
||||
border-radius: 12px;
|
||||
padding: 10px 16px;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
transition: transform 0.2s ease, opacity 0.2s ease, background 0.2s ease;
|
||||
}
|
||||
|
||||
.primary-btn {
|
||||
background: linear-gradient(135deg, #fe5e08 0%, #d9534f 100%);
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.ghost-btn {
|
||||
background: rgba(89, 70, 59, 0.08);
|
||||
color: #4a3a31;
|
||||
}
|
||||
|
||||
.primary-btn:hover,
|
||||
.ghost-btn:hover {
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
.primary-btn:disabled,
|
||||
.ghost-btn:disabled {
|
||||
cursor: not-allowed;
|
||||
opacity: 0.45;
|
||||
transform: none;
|
||||
}
|
||||
|
||||
.message-list,
|
||||
.log-list {
|
||||
max-height: 360px;
|
||||
overflow-y: auto;
|
||||
border: 1px solid rgba(98, 77, 65, 0.14);
|
||||
border-radius: 16px;
|
||||
background: rgba(255, 255, 255, 0.76);
|
||||
padding: 12px;
|
||||
margin-bottom: 14px;
|
||||
}
|
||||
|
||||
.message-item {
|
||||
padding: 12px;
|
||||
border-radius: 14px;
|
||||
background: #fff;
|
||||
border: 1px solid rgba(98, 77, 65, 0.1);
|
||||
}
|
||||
|
||||
.message-item + .message-item {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.message-item.is-self {
|
||||
border-color: rgba(25, 135, 84, 0.22);
|
||||
background: rgba(25, 135, 84, 0.06);
|
||||
}
|
||||
|
||||
.message-item.is-system {
|
||||
border-style: dashed;
|
||||
background: rgba(254, 94, 8, 0.06);
|
||||
}
|
||||
|
||||
.message-meta {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
gap: 12px;
|
||||
margin-bottom: 6px;
|
||||
font-size: 13px;
|
||||
color: #7d6557;
|
||||
}
|
||||
|
||||
.message-item p,
|
||||
.log-item,
|
||||
.empty-state {
|
||||
font-size: 14px;
|
||||
color: #45352d;
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
.log-item + .log-item {
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.hero-card,
|
||||
.field-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.status-box {
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 24px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,201 +0,0 @@
|
||||
<template>
|
||||
<div class="page-home">
|
||||
<section class="home-card">
|
||||
<p class="page-tag">Home</p>
|
||||
<h1>页面事件总线</h1>
|
||||
<p class="page-desc">
|
||||
这一版只保留最小能力:页面自己定义事件名,自己决定 payload 结构,通过 `on / emit / off / clear` 完成页面间监听和订阅。
|
||||
</p>
|
||||
|
||||
<div class="feature-list">
|
||||
<div class="feature-item">
|
||||
<strong>自定义事件名</strong>
|
||||
<span>例如 `auth:login`、`chat:received`、`user:refresh`</span>
|
||||
</div>
|
||||
<div class="feature-item">
|
||||
<strong>自定义数据</strong>
|
||||
<span>payload 不做预设,页面按业务自己约定</span>
|
||||
</div>
|
||||
<div class="feature-item">
|
||||
<strong>自动解绑</strong>
|
||||
<span>`useEventBus().on()` 在页面销毁时会自动取消监听</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="code-card">
|
||||
<pre><code>const { on, emit } = useEventBus()
|
||||
|
||||
on('user:refresh', (payload) => {
|
||||
console.log(payload)
|
||||
})
|
||||
|
||||
emit('user:refresh', { id: 1 })</code></pre>
|
||||
</div>
|
||||
|
||||
<div class="entry-row">
|
||||
<NuxtLink class="entry-link" to="/auth/login">
|
||||
前往登录页
|
||||
</NuxtLink>
|
||||
<NuxtLink class="entry-link secondary" to="/home/im">
|
||||
进入 IM Demo
|
||||
</NuxtLink>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
definePageMeta({
|
||||
layout: layout.default
|
||||
})
|
||||
|
||||
interface IUserRefreshPayload {
|
||||
id: number
|
||||
name: string
|
||||
}
|
||||
|
||||
const { on, emit } = useEventBus()
|
||||
|
||||
// EventBus 使用示例:订阅自定义事件
|
||||
on<IUserRefreshPayload>('user:refresh', (payload) => {
|
||||
console.log('收到 user:refresh 事件', payload)
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
// EventBus 使用示例:派发自定义事件
|
||||
emit<IUserRefreshPayload>('user:refresh', {
|
||||
id: 1,
|
||||
name: 'captain'
|
||||
})
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.page-home,
|
||||
.page-home div,
|
||||
.page-home section,
|
||||
.page-home p,
|
||||
.page-home h1,
|
||||
.page-home strong,
|
||||
.page-home span,
|
||||
.page-home pre,
|
||||
.page-home code {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.page-home {
|
||||
min-height: 100vh;
|
||||
padding: 24px 16px;
|
||||
background:
|
||||
linear-gradient(135deg, rgba(254, 94, 8, 0.1), transparent 38%),
|
||||
linear-gradient(180deg, #f5f1ea 0%, #f8f6f2 100%);
|
||||
}
|
||||
|
||||
.home-card {
|
||||
width: min(760px, 100%);
|
||||
margin: 0 auto;
|
||||
padding: 24px;
|
||||
border-radius: 24px;
|
||||
background: rgba(255, 252, 247, 0.95);
|
||||
border: 1px solid rgba(70, 54, 45, 0.12);
|
||||
box-shadow: 0 16px 40px rgba(48, 40, 40, 0.08);
|
||||
}
|
||||
|
||||
.page-tag {
|
||||
font-size: 12px;
|
||||
letter-spacing: 0.2em;
|
||||
text-transform: uppercase;
|
||||
color: #8d5d48;
|
||||
}
|
||||
|
||||
h1 {
|
||||
margin-top: 8px;
|
||||
font-size: 30px;
|
||||
color: #2a221d;
|
||||
}
|
||||
|
||||
.page-desc {
|
||||
margin-top: 12px;
|
||||
font-size: 15px;
|
||||
line-height: 1.7;
|
||||
color: #665246;
|
||||
}
|
||||
|
||||
.feature-list {
|
||||
display: grid;
|
||||
gap: 12px;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.feature-item,
|
||||
.code-card {
|
||||
padding: 14px 16px;
|
||||
border-radius: 18px;
|
||||
background: rgba(255, 255, 255, 0.88);
|
||||
border: 1px solid rgba(98, 77, 65, 0.12);
|
||||
}
|
||||
|
||||
.feature-item strong {
|
||||
display: block;
|
||||
font-size: 15px;
|
||||
color: #2a221d;
|
||||
}
|
||||
|
||||
.feature-item span {
|
||||
display: block;
|
||||
margin-top: 6px;
|
||||
font-size: 14px;
|
||||
color: #665246;
|
||||
}
|
||||
|
||||
.code-card {
|
||||
margin-top: 16px;
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
.code-card code {
|
||||
font-size: 14px;
|
||||
line-height: 1.7;
|
||||
color: #3f3129;
|
||||
}
|
||||
|
||||
.entry-row {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 12px;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.entry-link {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-width: 140px;
|
||||
padding: 12px 18px;
|
||||
border-radius: 14px;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.entry-link {
|
||||
background: linear-gradient(135deg, #fe5e08 0%, #d9534f 100%);
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.entry-link.secondary {
|
||||
background: rgba(89, 70, 59, 0.08);
|
||||
color: #4a3a31;
|
||||
}
|
||||
|
||||
.entry-link:hover,
|
||||
.entry-link:focus,
|
||||
.entry-link:active {
|
||||
color: #fff;
|
||||
background: linear-gradient(135deg, #db5208 0%, #bf4744 100%);
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
h1 {
|
||||
font-size: 24px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,3 +0,0 @@
|
||||
<template>
|
||||
ddd
|
||||
</template>
|
||||
@@ -1,3 +0,0 @@
|
||||
<template>
|
||||
index
|
||||
</template>
|
||||
@@ -1,3 +0,0 @@
|
||||
<template>
|
||||
test
|
||||
</template>
|
||||
@@ -1,10 +1,9 @@
|
||||
<template>
|
||||
<div class="head">
|
||||
<img src="http://gree.pccsh.com/images/site/logo.png" class="logo" /><br />
|
||||
<img src="/assets/images/logo.png" class="logo" /><br />
|
||||
【驰骋四海·社区版】
|
||||
</div>
|
||||
<div>
|
||||
|
||||
✧当前在线<strong> 139 </strong>位玩家在驰骋四海✧
|
||||
</div>
|
||||
<div class="content" style="font-size:17px">
|
||||
@@ -93,23 +92,21 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
|
||||
//MessageExtend.ShowToast('success', '更新成功!')
|
||||
//MessageExtend.NotifyList("success", ['获取装备',"获取物品"])
|
||||
definePageMeta({
|
||||
layout: layout.empty
|
||||
})
|
||||
|
||||
const areaData = ref<any>([]);
|
||||
const isOnline = ref(false);
|
||||
|
||||
|
||||
|
||||
const Initialize = async (): Promise<void> => {
|
||||
var result = await PubService.GetMain();
|
||||
var result = await PubService.GetMain(StateHelper.Sid);
|
||||
if (result.code == 0) {
|
||||
areaData.value = result.data?.area;
|
||||
|
||||
|
||||
isOnline.value = result.data.isOnline;
|
||||
console.log(isOnline.value);
|
||||
}
|
||||
else {
|
||||
MessageExtend.ShowToast("fail", result.msg);
|
||||
@@ -117,10 +114,10 @@ const Initialize = async (): Promise<void> => {
|
||||
};
|
||||
|
||||
|
||||
// await navigateTo('/auth/login', { replace: true })
|
||||
onMounted(async () => {
|
||||
//默认设置身份,正式环境删除
|
||||
StateHelper.SetSid("kUVjj2cBUemcdokUEIBEKh0qhKkkSkui0x");
|
||||
|
||||
//EventBusExtend.emit("connect","11111");
|
||||
|
||||
const id = PageExtend.QueryString("id");
|
||||
|
||||
@@ -128,7 +125,7 @@ onMounted(async () => {
|
||||
|
||||
await Initialize();
|
||||
|
||||
|
||||
|
||||
|
||||
//alert(1);
|
||||
})
|
||||
|
||||
@@ -3,7 +3,7 @@ export class PubService {
|
||||
* 获取首页信息
|
||||
* GET /Pub/GetMain
|
||||
*/
|
||||
static async GetMain() {
|
||||
return await ApiService.Request("get", "/Pub/GetMain");
|
||||
static async GetMain(sid: string) {
|
||||
return await ApiService.Request("get", "/Pub/GetMain", { sid });
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user