消息
This commit is contained in:
122
AGENTS.md
Normal file
122
AGENTS.md
Normal file
@@ -0,0 +1,122 @@
|
|||||||
|
# AGENTS.md
|
||||||
|
|
||||||
|
Guidelines for AI agents working in this SeaTime (航海时代) codebase.
|
||||||
|
|
||||||
|
## Project Structure
|
||||||
|
|
||||||
|
Full-stack web game with two components:
|
||||||
|
- **Web/**: Nuxt 4 (Vue 3 + TypeScript) frontend
|
||||||
|
- **Service/**: .NET 10 Web API backend using Photon.Core framework
|
||||||
|
|
||||||
|
## Build Commands
|
||||||
|
|
||||||
|
### Web (Frontend)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd Web
|
||||||
|
npm install # Install dependencies
|
||||||
|
npm run dev # Start dev server on port 5068
|
||||||
|
npm run build # Production build
|
||||||
|
npm run generate # Static site generation
|
||||||
|
npm run preview # Preview production build
|
||||||
|
npm run postinstall # Nuxt prepare (runs automatically after install)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Service (Backend)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd Service
|
||||||
|
dotnet build # Build solution
|
||||||
|
dotnet run --project Application.Web # Run web API
|
||||||
|
dotnet watch --project Application.Web # Run with hot reload
|
||||||
|
```
|
||||||
|
|
||||||
|
## Testing Commands
|
||||||
|
|
||||||
|
**No test framework configured yet.** To add tests:
|
||||||
|
|
||||||
|
- **Web**: Install Vitest or Jest for unit tests
|
||||||
|
- **Service**: Use `dotnet test` after adding xUnit/NUnit test projects
|
||||||
|
|
||||||
|
When tests are added, run them with:
|
||||||
|
```bash
|
||||||
|
# Web (Vitest example)
|
||||||
|
npm run test # Run all tests
|
||||||
|
npm run test:unit # Run unit tests only
|
||||||
|
npm run test -- <file> # Run single test file
|
||||||
|
|
||||||
|
# Service
|
||||||
|
dotnet test # Run all tests
|
||||||
|
dotnet test --filter "FullyQualifiedName~ClassName" # Run specific test class
|
||||||
|
```
|
||||||
|
|
||||||
|
## Code Style Guidelines
|
||||||
|
|
||||||
|
### TypeScript / Vue (Web)
|
||||||
|
|
||||||
|
- **Imports**: Use `~/` alias for src directory imports (e.g., `import type { IUserInfo } from '~/types/user'`)
|
||||||
|
- **Vue SFCs**: Use `<script setup lang="ts">` composition API
|
||||||
|
- **Naming Conventions**:
|
||||||
|
- Stores: `useXxxStore` in `stores/` (e.g., `useUserStore`)
|
||||||
|
- Composables: `useXxx` in `composables/` (e.g., `useAuth`)
|
||||||
|
- Services: `XxxSERVICE` class in `services/` (e.g., `UserSERVICE`)
|
||||||
|
- Extends: `xxxEXTEND` class in `extends/` (e.g., `RequestEXTEND`)
|
||||||
|
- Types: `IXxx` interface in `types/` (e.g., `IUserInfo`)
|
||||||
|
- Pages: kebab-case in `pages/` (e.g., `auth/login.vue`)
|
||||||
|
- **Auto-imports**: stores, composables, extends, services are auto-imported (see nuxt.config.ts)
|
||||||
|
- **TypeScript**: Strict mode enabled, always define return types for public functions
|
||||||
|
- **Comments**: Use Chinese for business logic documentation
|
||||||
|
- **API Pattern**: Services check `response.code !== 200 && response.code !== 0` for errors
|
||||||
|
- **State Management**: Pinia stores use `state`/`getters`/`actions` pattern with `persist` for localStorage
|
||||||
|
- **Persistence**: Only persist `token` and `userInfo` to localStorage via `piniaPluginPersistedstate.localStorage()`
|
||||||
|
|
||||||
|
### C# (Service)
|
||||||
|
|
||||||
|
- Target framework: .NET 10
|
||||||
|
- Nullable reference types enabled
|
||||||
|
- Implicit usings enabled
|
||||||
|
- Standard C# naming: PascalCase for classes/methods, camelCase for locals
|
||||||
|
- Controllers use `[ApiExplorerSettings(GroupName = "...")]` for Swagger grouping
|
||||||
|
- Route pattern: `[Route("[controller]/[action]")]`
|
||||||
|
- Return `IPoAction` / `PoAction.Ok()` from controllers
|
||||||
|
- Request params in `Application.Domain/RequestParms/` folder
|
||||||
|
- Global usings defined in `Application.Web/GlobalUsings.cs`
|
||||||
|
- Uses Photon.Core framework (DI via `Inject*`, JWT, SqlSugar, Timer)
|
||||||
|
|
||||||
|
## Error Handling
|
||||||
|
|
||||||
|
### Web
|
||||||
|
|
||||||
|
- Services: Wrap API calls in try-catch, log errors with `console.error`, re-throw or return false
|
||||||
|
- Components: Use `showToast`/`showFailToast`/`showSuccessToast` from Vant for user feedback
|
||||||
|
- Always check `typeof window !== 'undefined'` for client-side only code
|
||||||
|
- Navigation: Use `navigateTo()` for programmatic navigation
|
||||||
|
|
||||||
|
### Service
|
||||||
|
|
||||||
|
- Use standard .NET exception handling middleware
|
||||||
|
- Return consistent response models with code/message/data structure
|
||||||
|
- CORS policy "all" configured for cross-origin requests
|
||||||
|
|
||||||
|
## Key Technologies
|
||||||
|
|
||||||
|
- **Frontend**: Nuxt 4, Vue 3, Pinia, Vant UI, TypeScript
|
||||||
|
- **Backend**: .NET 10 Web API, Photon.Core framework
|
||||||
|
- **State Management**: Pinia with pinia-plugin-persistedstate (localStorage)
|
||||||
|
- **HTTP Client**: Custom RequestEXTEND class based on native fetch
|
||||||
|
- **Auth**: JWT with custom JwtHandle validator
|
||||||
|
- **Timer**: AutoJob hosted service with TimerJobManager
|
||||||
|
|
||||||
|
## Project Conventions
|
||||||
|
|
||||||
|
- Source directory: `Web/src/`
|
||||||
|
- Pages use file-based routing (Nuxt convention)
|
||||||
|
- Layouts in `Web/src/layouts/`
|
||||||
|
- Layout constants in `composables/layout.ts` as `layout` object (e.g., `layout.empty`)
|
||||||
|
- Use `definePageMeta({ layout: layout.empty })` for page layout configuration
|
||||||
|
- Store persistence: Only token and userInfo are persisted to localStorage
|
||||||
|
- No ESLint/Prettier configured yet - follow existing code patterns
|
||||||
|
|
||||||
|
## Existing Rules
|
||||||
|
|
||||||
|
No Cursor rules (.cursorrules or .cursor/rules/) or Copilot instructions (.github/copilot-instructions.md) found in this repository.
|
||||||
@@ -13,9 +13,11 @@
|
|||||||
|
|
||||||
// 可以在这里进行全局初始化
|
// 可以在这里进行全局初始化
|
||||||
const appStore = useAppStore()
|
const appStore = useAppStore()
|
||||||
|
import { Loading } from 'vant'
|
||||||
// 初始化应用配置
|
// 初始化应用配置
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
|
|
||||||
|
|
||||||
// 初始化屏幕尺寸
|
// 初始化屏幕尺寸
|
||||||
if (typeof window !== 'undefined') {
|
if (typeof window !== 'undefined') {
|
||||||
appStore.updateScreenSize(window.innerWidth, window.innerHeight)
|
appStore.updateScreenSize(window.innerWidth, window.innerHeight)
|
||||||
|
|||||||
@@ -508,3 +508,126 @@ a {
|
|||||||
max-height: 150px;
|
max-height: 150px;
|
||||||
max-width: 250px;
|
max-width: 250px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ========== 自定义通知队列 ========== */
|
||||||
|
#custom-notify-container {
|
||||||
|
position: fixed;
|
||||||
|
top: 10px;
|
||||||
|
left: 50%;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
z-index: 9999;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
width: 100%;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-notify-item {
|
||||||
|
box-sizing: border-box;
|
||||||
|
max-width: 90%;
|
||||||
|
padding: 8px 16px;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
border-radius: 8px;
|
||||||
|
color: #fff;
|
||||||
|
font-size: 12px;
|
||||||
|
line-height: 1.5;
|
||||||
|
text-align: center;
|
||||||
|
word-wrap: break-word;
|
||||||
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
|
||||||
|
pointer-events: auto;
|
||||||
|
cursor: pointer;
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateY(-20px);
|
||||||
|
animation: notify-slide-in 300ms ease-out forwards;
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-notify-item.leaving {
|
||||||
|
animation: notify-fade-out 200ms ease-in forwards;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes notify-slide-in {
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes notify-fade-out {
|
||||||
|
to {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateY(-10px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ========== 顶部通告栏 ========== */
|
||||||
|
.custom-notice-bar {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 40px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 0 16px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
z-index: 9998;
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-notice-bar .notice-icon {
|
||||||
|
flex-shrink: 0;
|
||||||
|
margin-right: 8px;
|
||||||
|
font-size: 16px;
|
||||||
|
position: relative;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-notice-bar .notice-wrap {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
overflow: hidden;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 0 40px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-notice-bar .notice-text {
|
||||||
|
display: inline-block;
|
||||||
|
white-space: nowrap;
|
||||||
|
padding-right: 50px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-notice-bar .notice-wrap.scrolling {
|
||||||
|
animation: notice-scroll var(--scroll-duration, 10s) linear infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes notice-scroll {
|
||||||
|
0% {
|
||||||
|
transform: translateX(0);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
transform: translateX(-50%);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-notice-bar .notice-close {
|
||||||
|
flex-shrink: 0;
|
||||||
|
margin-left: 8px;
|
||||||
|
font-size: 18px;
|
||||||
|
cursor: pointer;
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
line-height: 1;
|
||||||
|
position: relative;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
102
Web/src/extends/MessageExtend.ts
Normal file
102
Web/src/extends/MessageExtend.ts
Normal file
@@ -0,0 +1,102 @@
|
|||||||
|
/**
|
||||||
|
* 消息提示工具
|
||||||
|
*/
|
||||||
|
export class MessageExtend {
|
||||||
|
// 消息通知
|
||||||
|
static notify(type: 'primary' | 'success' | 'danger' | 'warning', message: any) {
|
||||||
|
showNotify({ type, message })
|
||||||
|
}
|
||||||
|
|
||||||
|
// 提示弹窗
|
||||||
|
static dialog(
|
||||||
|
title: string,
|
||||||
|
message: string,
|
||||||
|
theme: 'round-button' | 'default',
|
||||||
|
onConfirm?: () => void,
|
||||||
|
onCancel?: () => void,
|
||||||
|
confirmButtonText: string = '确认',
|
||||||
|
) {
|
||||||
|
showConfirmDialog({
|
||||||
|
title: title,
|
||||||
|
message: message,
|
||||||
|
theme: theme || 'default',
|
||||||
|
confirmButtonText: confirmButtonText || '确认',
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
onConfirm?.()
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
onCancel?.()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 成功失败默认提示
|
||||||
|
static showToast(type: 'success' | 'fail' | 'default', text: any) {
|
||||||
|
if (type == 'success') {
|
||||||
|
showSuccessToast(text)
|
||||||
|
} else if (type == 'fail') {
|
||||||
|
showFailToast(text)
|
||||||
|
} else {
|
||||||
|
showToast(text)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 自定义图标提示
|
||||||
|
static showIconToast(icon: any, text: any) {
|
||||||
|
showToast({
|
||||||
|
message: text,
|
||||||
|
icon: icon,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 多条消息通知(堆叠展示)
|
||||||
|
static notifyList(type: 'primary' | 'success' | 'danger' | 'warning', messages: string[], duration: number = 3000) {
|
||||||
|
if (typeof window === 'undefined') return
|
||||||
|
if (!messages || messages.length === 0) return
|
||||||
|
|
||||||
|
const colorMap = {
|
||||||
|
primary: '#1989fa',
|
||||||
|
success: '#07c160',
|
||||||
|
danger: '#ee0a24',
|
||||||
|
warning: '#ff976a',
|
||||||
|
}
|
||||||
|
|
||||||
|
let container = document.getElementById('custom-notify-container')
|
||||||
|
if (!container) {
|
||||||
|
container = document.createElement('div')
|
||||||
|
container.id = 'custom-notify-container'
|
||||||
|
document.body.appendChild(container)
|
||||||
|
}
|
||||||
|
|
||||||
|
messages.forEach((message, index) => {
|
||||||
|
const item = document.createElement('div')
|
||||||
|
item.className = 'custom-notify-item'
|
||||||
|
item.style.backgroundColor = colorMap[type]
|
||||||
|
item.textContent = String(message)
|
||||||
|
item.style.animationDelay = `${index * 100}ms`
|
||||||
|
|
||||||
|
item.addEventListener('click', () => {
|
||||||
|
this.closeNotifyItem(item)
|
||||||
|
})
|
||||||
|
|
||||||
|
container!.appendChild(item)
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
this.closeNotifyItem(item)
|
||||||
|
}, duration)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 关闭单条通知
|
||||||
|
private static closeNotifyItem(item: HTMLElement) {
|
||||||
|
if (!item || item.classList.contains('leaving')) return
|
||||||
|
item.classList.add('leaving')
|
||||||
|
item.addEventListener('animationend', () => {
|
||||||
|
item.remove()
|
||||||
|
const container = document.getElementById('custom-notify-container')
|
||||||
|
if (container && container.children.length === 0) {
|
||||||
|
container.remove()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -48,16 +48,8 @@
|
|||||||
<label class="field-label" for="login-username">账号</label>
|
<label class="field-label" for="login-username">账号</label>
|
||||||
<div class="field-box" :class="{ 'is-error': !!errors.username }">
|
<div class="field-box" :class="{ 'is-error': !!errors.username }">
|
||||||
<span class="field-marker">A</span>
|
<span class="field-marker">A</span>
|
||||||
<input
|
<input id="login-username" v-model.trim="form.username" class="field-input" type="text" inputmode="text"
|
||||||
id="login-username"
|
autocomplete="username" placeholder="请输入账号" @input="clearFieldError('username')" />
|
||||||
v-model.trim="form.username"
|
|
||||||
class="field-input"
|
|
||||||
type="text"
|
|
||||||
inputmode="text"
|
|
||||||
autocomplete="username"
|
|
||||||
placeholder="请输入账号"
|
|
||||||
@input="clearFieldError('username')"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
<p v-if="errors.username" class="field-error">{{ errors.username }}</p>
|
<p v-if="errors.username" class="field-error">{{ errors.username }}</p>
|
||||||
</div>
|
</div>
|
||||||
@@ -66,15 +58,8 @@
|
|||||||
<label class="field-label" for="login-password">密码</label>
|
<label class="field-label" for="login-password">密码</label>
|
||||||
<div class="field-box" :class="{ 'is-error': !!errors.password }">
|
<div class="field-box" :class="{ 'is-error': !!errors.password }">
|
||||||
<span class="field-marker">P</span>
|
<span class="field-marker">P</span>
|
||||||
<input
|
<input id="login-password" v-model.trim="form.password" class="field-input" type="password"
|
||||||
id="login-password"
|
autocomplete="current-password" placeholder="请输入密码" @input="clearFieldError('password')" />
|
||||||
v-model.trim="form.password"
|
|
||||||
class="field-input"
|
|
||||||
type="password"
|
|
||||||
autocomplete="current-password"
|
|
||||||
placeholder="请输入密码"
|
|
||||||
@input="clearFieldError('password')"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
<p v-if="errors.password" class="field-error">{{ errors.password }}</p>
|
<p v-if="errors.password" class="field-error">{{ errors.password }}</p>
|
||||||
</div>
|
</div>
|
||||||
@@ -92,12 +77,8 @@
|
|||||||
|
|
||||||
<div class="login-agreement">
|
<div class="login-agreement">
|
||||||
<label class="check-row">
|
<label class="check-row">
|
||||||
<input
|
<input v-model="form.agreement" class="check-input" type="checkbox"
|
||||||
v-model="form.agreement"
|
@change="clearFieldError('agreement')" />
|
||||||
class="check-input"
|
|
||||||
type="checkbox"
|
|
||||||
@change="clearFieldError('agreement')"
|
|
||||||
/>
|
|
||||||
<span class="check-box"></span>
|
<span class="check-box"></span>
|
||||||
<span class="check-text">我已阅读并同意演示使用说明</span>
|
<span class="check-text">我已阅读并同意演示使用说明</span>
|
||||||
</label>
|
</label>
|
||||||
@@ -105,14 +86,8 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="login-actions">
|
<div class="login-actions">
|
||||||
<van-button
|
<van-button block round type="primary" native-type="submit" :loading="isSubmitting"
|
||||||
block
|
:disabled="submitDisabled">
|
||||||
round
|
|
||||||
type="primary"
|
|
||||||
native-type="submit"
|
|
||||||
:loading="isSubmitting"
|
|
||||||
:disabled="submitDisabled"
|
|
||||||
>
|
|
||||||
模拟登录
|
模拟登录
|
||||||
</van-button>
|
</van-button>
|
||||||
<van-button block round plain type="primary" native-type="button" @click="clearForm">
|
<van-button block round plain type="primary" native-type="button" @click="clearForm">
|
||||||
|
|||||||
@@ -17,7 +17,8 @@
|
|||||||
|
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
➸<a href="http://m.twbar.cn/b/1145?sid=KrWxKypJuDO0zFKrTig0bG">游戏论坛</a> ➸<a class="" href="/Login/LoginOut?sid=klxy7ADn96CBYGWQ9AG4xPqFC2Ib6Ty1Kx">退出游戏</a>
|
➸<a href="http://m.twbar.cn/b/1145?sid=KrWxKypJuDO0zFKrTig0bG">游戏论坛</a> ➸<a class=""
|
||||||
|
href="/Login/LoginOut?sid=klxy7ADn96CBYGWQ9AG4xPqFC2Ib6Ty1Kx">退出游戏</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -46,7 +47,8 @@
|
|||||||
<div>
|
<div>
|
||||||
<div class="item">
|
<div class="item">
|
||||||
1.
|
1.
|
||||||
<a class="" href="/Pallet/Notice/Detail?nt=2026041901&sid=klxy7ADn96CBYGWQ9AG4xPqFC2Ib6Ty1Kx">[招募令]航海时代2设计专员招募</a>
|
<a class=""
|
||||||
|
href="/Pallet/Notice/Detail?nt=2026041901&sid=klxy7ADn96CBYGWQ9AG4xPqFC2Ib6Ty1Kx">[招募令]航海时代2设计专员招募</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="item">
|
<div class="item">
|
||||||
2.
|
2.
|
||||||
@@ -62,14 +64,17 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="item">
|
<div class="item">
|
||||||
5.
|
5.
|
||||||
<a class="" href="/Pallet/Notice/Detail?nt=2025080002&sid=klxy7ADn96CBYGWQ9AG4xPqFC2Ib6Ty1Kx">【驰骋四海】卡片の攻略(2.24)</a>
|
<a class=""
|
||||||
|
href="/Pallet/Notice/Detail?nt=2025080002&sid=klxy7ADn96CBYGWQ9AG4xPqFC2Ib6Ty1Kx">【驰骋四海】卡片の攻略(2.24)</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="title">
|
<div class="title">
|
||||||
=====☆服务导航☆=====
|
=====☆服务导航☆=====
|
||||||
</div>
|
</div>
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<a class="" href="/Index/Kefu?sid=klxy7ADn96CBYGWQ9AG4xPqFC2Ib6Ty1Kx">客服</a>.<a class="" href="/Index/About?sid=klxy7ADn96CBYGWQ9AG4xPqFC2Ib6Ty1Kx">关于</a>.<a class="" href="/Index/Cooperation?sid=klxy7ADn96CBYGWQ9AG4xPqFC2Ib6Ty1Kx">合作</a>
|
<a class="" href="/Index/Kefu?sid=klxy7ADn96CBYGWQ9AG4xPqFC2Ib6Ty1Kx">客服</a>.<a class=""
|
||||||
|
href="/Index/About?sid=klxy7ADn96CBYGWQ9AG4xPqFC2Ib6Ty1Kx">关于</a>.<a class=""
|
||||||
|
href="/Index/Cooperation?sid=klxy7ADn96CBYGWQ9AG4xPqFC2Ib6Ty1Kx">合作</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="content">
|
<div class="content">
|
||||||
切换线路:
|
切换线路:
|
||||||
@@ -85,9 +90,12 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
|
||||||
|
// MessageExtend.showToast('success', '更新成功!')
|
||||||
|
MessageExtend.notifyList('primary', ['获取装备'])
|
||||||
|
|
||||||
definePageMeta({
|
definePageMeta({
|
||||||
layout: layout.empty
|
layout: layout.empty
|
||||||
})
|
})
|
||||||
showNotify({ message: '提示' });
|
|
||||||
// await navigateTo('/auth/login', { replace: true })
|
// await navigateTo('/auth/login', { replace: true })
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
Reference in New Issue
Block a user