优化前端
This commit is contained in:
@@ -0,0 +1,445 @@
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
import { useSettings } from '@/composables/useSettings'
|
||||
|
||||
const { settings, loadSettings, saveSettings } = useSettings()
|
||||
|
||||
const visible = ref(false)
|
||||
const formData = ref({
|
||||
timeApiUrl: '',
|
||||
traceEnabled: false,
|
||||
traceMaxBody: 0,
|
||||
logLevel: 'info',
|
||||
concurrency: 80,
|
||||
attempts: 1
|
||||
})
|
||||
|
||||
const open = async () => {
|
||||
await loadSettings()
|
||||
formData.value = { ...settings.value }
|
||||
visible.value = true
|
||||
}
|
||||
|
||||
const close = () => {
|
||||
visible.value = false
|
||||
}
|
||||
|
||||
const handleSave = async () => {
|
||||
const success = await saveSettings(formData.value)
|
||||
if (success) {
|
||||
close()
|
||||
}
|
||||
}
|
||||
|
||||
// 暴露给父组件
|
||||
defineExpose({ open, close })
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Teleport to="body">
|
||||
<Transition name="modal">
|
||||
<div v-if="visible" class="modal-overlay" @click.self="close">
|
||||
<div class="modal">
|
||||
<div class="modal-header">
|
||||
<h3>⚙ 设置</h3>
|
||||
<button class="modal-close" @click="close">×</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<!-- 时间 API -->
|
||||
<div class="form-group">
|
||||
<label class="form-label">时间 API 地址</label>
|
||||
<input
|
||||
v-model="formData.timeApiUrl"
|
||||
class="form-input"
|
||||
type="text"
|
||||
placeholder="留空使用默认地址"
|
||||
>
|
||||
<div class="form-hint">用于获取斗鱼服务端时间,一般不需要修改</div>
|
||||
</div>
|
||||
|
||||
<!-- 日志设置 -->
|
||||
<div class="form-section">
|
||||
<div class="form-section-title">日志设置</div>
|
||||
|
||||
<div class="form-group">
|
||||
<div class="toggle-row">
|
||||
<div>
|
||||
<span class="toggle-label">Trace 日志</span>
|
||||
<div class="form-hint">开启后会在 logs/ 目录生成 douyu_trace_*.log</div>
|
||||
</div>
|
||||
<label class="toggle">
|
||||
<input v-model="formData.traceEnabled" type="checkbox">
|
||||
<span class="toggle-slider"></span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-row">
|
||||
<div class="form-group">
|
||||
<label class="form-label">Trace 正文上限</label>
|
||||
<div class="input-with-unit">
|
||||
<input
|
||||
v-model.number="formData.traceMaxBody"
|
||||
class="form-input"
|
||||
type="number"
|
||||
min="0"
|
||||
step="1024"
|
||||
placeholder="0"
|
||||
>
|
||||
<span class="input-unit">字节</span>
|
||||
</div>
|
||||
<div class="form-hint">0 表示不限制</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label">日志显示等级</label>
|
||||
<select v-model="formData.logLevel" class="form-select">
|
||||
<option value="debug">Debug</option>
|
||||
<option value="info">Info</option>
|
||||
<option value="warn">Warn</option>
|
||||
<option value="error">Error</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 抢购设置 -->
|
||||
<div class="form-section">
|
||||
<div class="form-section-title">抢购设置</div>
|
||||
|
||||
<div class="form-row">
|
||||
<div class="form-group">
|
||||
<label class="form-label">并发数</label>
|
||||
<input
|
||||
v-model.number="formData.concurrency"
|
||||
class="form-input"
|
||||
type="number"
|
||||
min="1"
|
||||
max="500"
|
||||
step="10"
|
||||
>
|
||||
<div class="form-hint">同时发起的请求数量,建议 50-200</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label">尝试包数</label>
|
||||
<input
|
||||
v-model.number="formData.attempts"
|
||||
class="form-input"
|
||||
type="number"
|
||||
min="1"
|
||||
max="10"
|
||||
step="1"
|
||||
>
|
||||
<div class="form-hint">每个账号生成几个抢购包,轮询使用</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="info-card">
|
||||
<div class="info-title">💡 抢购策略说明</div>
|
||||
<ul class="info-list">
|
||||
<li>抢购窗口:<strong>75 秒</strong>,自动循环重试</li>
|
||||
<li>前 8 秒高频(180ms),之后逐渐降速</li>
|
||||
<li>尝试包数 > 1 时,每轮使用不同的包</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="info-card info-card-warn">
|
||||
<div class="info-title">⚠️ 自动停止条件</div>
|
||||
<ul class="info-list">
|
||||
<li><strong>成功</strong> - 抢到商品</li>
|
||||
<li><strong>库存耗尽</strong> - 商品本次活动库存已耗完</li>
|
||||
<li><strong>兑换上限</strong> - 该账号已达本次活动兑换上限</li>
|
||||
<li><strong>未绑定</strong> - 账号未绑定角色</li>
|
||||
<li><strong>资格不足</strong> - 需要开通精英手册</li>
|
||||
<li><strong>超时</strong> - 75秒抢购窗口结束</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button class="btn btn-ghost" @click="close">取消</button>
|
||||
<button class="btn btn-primary" @click="handleSave">保存</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Transition>
|
||||
</Teleport>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.modal-overlay {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
background: rgba(0, 0, 0, .5);
|
||||
z-index: 9000;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
backdrop-filter: blur(4px);
|
||||
}
|
||||
|
||||
.modal {
|
||||
background: var(--surface);
|
||||
border: 1px solid var(--line);
|
||||
border-radius: var(--radius);
|
||||
width: 520px;
|
||||
max-width: 92vw;
|
||||
max-height: 85vh;
|
||||
overflow-y: auto;
|
||||
box-shadow: var(--shadow);
|
||||
}
|
||||
|
||||
.modal-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 16px 20px;
|
||||
border-bottom: 1px solid var(--line);
|
||||
}
|
||||
|
||||
.modal-header h3 {
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
color: var(--text-strong);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.modal-close {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border: 0;
|
||||
border-radius: var(--radius-sm);
|
||||
background: transparent;
|
||||
color: var(--muted);
|
||||
cursor: pointer;
|
||||
font-size: 20px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
transition: .15s;
|
||||
}
|
||||
|
||||
.modal-close:hover {
|
||||
background: var(--surface-2);
|
||||
color: var(--text-strong);
|
||||
}
|
||||
|
||||
.modal-body {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.form-section {
|
||||
margin-top: 20px;
|
||||
padding-top: 16px;
|
||||
border-top: 1px solid var(--line);
|
||||
}
|
||||
|
||||
.form-section-title {
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
color: var(--text-strong);
|
||||
margin-bottom: 14px;
|
||||
}
|
||||
|
||||
.form-group {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.form-group:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.form-label {
|
||||
display: block;
|
||||
font-size: 12.5px;
|
||||
font-weight: 500;
|
||||
color: var(--text);
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
|
||||
.form-hint {
|
||||
font-size: 11px;
|
||||
color: var(--muted);
|
||||
margin-top: 4px;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.form-row {
|
||||
display: flex;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.form-row .form-group {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.form-input, .form-select {
|
||||
width: 100%;
|
||||
height: 36px;
|
||||
padding: 0 12px;
|
||||
background: var(--field);
|
||||
color: var(--text-strong);
|
||||
border: 1px solid var(--line);
|
||||
border-radius: var(--radius-sm);
|
||||
font-size: 13px;
|
||||
transition: .15s;
|
||||
}
|
||||
|
||||
.form-input:focus, .form-select:focus {
|
||||
outline: 0;
|
||||
border-color: var(--accent);
|
||||
box-shadow: var(--focus);
|
||||
}
|
||||
|
||||
.input-with-unit {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.input-with-unit .form-input {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.input-unit {
|
||||
font-size: 12px;
|
||||
color: var(--muted);
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.toggle-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.toggle-label {
|
||||
font-size: 13px;
|
||||
color: var(--text-strong);
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.toggle {
|
||||
position: relative;
|
||||
width: 44px;
|
||||
height: 24px;
|
||||
cursor: pointer;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.toggle input {
|
||||
opacity: 0;
|
||||
width: 0;
|
||||
height: 0;
|
||||
}
|
||||
|
||||
.toggle-slider {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background: var(--line-strong);
|
||||
border-radius: 999px;
|
||||
transition: .2s;
|
||||
}
|
||||
|
||||
.toggle-slider::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
left: 2px;
|
||||
top: 2px;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
background: #fff;
|
||||
border-radius: 50%;
|
||||
transition: .2s;
|
||||
}
|
||||
|
||||
.toggle input:checked + .toggle-slider {
|
||||
background: var(--accent);
|
||||
}
|
||||
|
||||
.toggle input:checked + .toggle-slider::before {
|
||||
transform: translateX(20px);
|
||||
}
|
||||
|
||||
.modal-footer {
|
||||
padding: 16px 20px;
|
||||
border-top: 1px solid var(--line);
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.info-card {
|
||||
margin-top: 16px;
|
||||
padding: 14px;
|
||||
background: var(--accent-soft);
|
||||
border: 1px solid var(--accent-border);
|
||||
border-radius: var(--radius-sm);
|
||||
}
|
||||
|
||||
.info-card-warn {
|
||||
background: var(--warning-soft);
|
||||
border-color: var(--warning);
|
||||
}
|
||||
|
||||
.info-card-warn .info-title {
|
||||
color: var(--warning);
|
||||
}
|
||||
|
||||
.info-title {
|
||||
font-size: 12.5px;
|
||||
font-weight: 600;
|
||||
color: var(--accent);
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.info-list {
|
||||
margin: 0;
|
||||
padding-left: 18px;
|
||||
font-size: 12px;
|
||||
color: var(--text);
|
||||
line-height: 1.8;
|
||||
}
|
||||
|
||||
.info-list li {
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.info-list li:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.info-list strong {
|
||||
color: var(--text-strong);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
/* 动画 */
|
||||
.modal-enter-active {
|
||||
transition: all .2s ease;
|
||||
}
|
||||
|
||||
.modal-leave-active {
|
||||
transition: all .2s ease;
|
||||
}
|
||||
|
||||
.modal-enter-from {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.modal-leave-to {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.modal-enter-from .modal {
|
||||
transform: scale(.95);
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.modal-leave-to .modal {
|
||||
transform: scale(.95);
|
||||
opacity: 0;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user