|
|
@@ -112,8 +112,63 @@
|
|
|
>
|
|
|
{{ loading ? '登录中...' : '登录' }}
|
|
|
</button>
|
|
|
+ <button
|
|
|
+ type="button"
|
|
|
+ class="btn-token"
|
|
|
+ :disabled="loading"
|
|
|
+ @click="openTokenModal"
|
|
|
+ title="使用 Token 登录"
|
|
|
+ >
|
|
|
+ <svg
|
|
|
+ viewBox="0 0 24 24"
|
|
|
+ aria-hidden="true"
|
|
|
+ class="btn-token-icon"
|
|
|
+ >
|
|
|
+ <path
|
|
|
+ d="M3 7a4 4 0 1 1 7.8 1.3l9.2 3.54a3.5 3.5 0 1 1-1.1 2.74l-9.2-3.54A4 4 0 0 1 3 7zm4 2a2 2 0 1 0 0-4 2 2 0 0 0 0 4zm13 9.5a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3z"
|
|
|
+ fill="currentColor"
|
|
|
+ />
|
|
|
+ </svg>
|
|
|
+ </button>
|
|
|
</div>
|
|
|
</form>
|
|
|
+ <div
|
|
|
+ v-if="tokenModalVisible"
|
|
|
+ class="token-modal-overlay"
|
|
|
+ @click.self="closeTokenModal"
|
|
|
+ >
|
|
|
+ <div class="token-modal">
|
|
|
+ <div class="token-modal-header">Token 登录</div>
|
|
|
+ <div
|
|
|
+ v-if="tokenError"
|
|
|
+ class="error-message"
|
|
|
+ >
|
|
|
+ {{ tokenError }}
|
|
|
+ </div>
|
|
|
+ <input
|
|
|
+ v-model="tokenInput"
|
|
|
+ class="token-input"
|
|
|
+ type="text"
|
|
|
+ placeholder="请输入 Token"
|
|
|
+ >
|
|
|
+ <div class="token-modal-actions">
|
|
|
+ <button
|
|
|
+ type="button"
|
|
|
+ class="btn-secondary"
|
|
|
+ @click="closeTokenModal"
|
|
|
+ >
|
|
|
+ 取消
|
|
|
+ </button>
|
|
|
+ <button
|
|
|
+ type="button"
|
|
|
+ class="btn-primary"
|
|
|
+ @click="confirmToken"
|
|
|
+ >
|
|
|
+ 确认
|
|
|
+ </button>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
@@ -148,6 +203,9 @@ const loading = ref(false)
|
|
|
const error = ref('')
|
|
|
const showPassword = ref(false)
|
|
|
const selectedEnvironment = ref(getApiEnvironment())
|
|
|
+const tokenModalVisible = ref(false)
|
|
|
+const tokenInput = ref('')
|
|
|
+const tokenError = ref('')
|
|
|
|
|
|
watch(
|
|
|
selectedEnvironment,
|
|
|
@@ -188,6 +246,29 @@ const handleSubmit = async () => {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+const openTokenModal = () => {
|
|
|
+ tokenInput.value = ''
|
|
|
+ tokenError.value = ''
|
|
|
+ tokenModalVisible.value = true
|
|
|
+}
|
|
|
+
|
|
|
+const closeTokenModal = () => {
|
|
|
+ tokenModalVisible.value = false
|
|
|
+ tokenError.value = ''
|
|
|
+}
|
|
|
+
|
|
|
+const confirmToken = () => {
|
|
|
+ const token = tokenInput.value.trim()
|
|
|
+ if (!token) {
|
|
|
+ tokenError.value = '请输入 Token'
|
|
|
+ return
|
|
|
+ }
|
|
|
+ setApiEnvironment(selectedEnvironment.value)
|
|
|
+ setToken(token)
|
|
|
+ tokenModalVisible.value = false
|
|
|
+ emit('success')
|
|
|
+}
|
|
|
+
|
|
|
const handleCancel = () => {
|
|
|
if (!loading.value) {
|
|
|
emit('cancel')
|
|
|
@@ -216,6 +297,7 @@ const handleCancel = () => {
|
|
|
width: 90%;
|
|
|
max-width: 400px;
|
|
|
box-shadow: 0 18px 48px rgba(0, 0, 0, 0.46);
|
|
|
+ position: relative;
|
|
|
}
|
|
|
|
|
|
.modal-header {
|
|
|
@@ -348,10 +430,12 @@ const handleCancel = () => {
|
|
|
|
|
|
.form-actions {
|
|
|
margin-top: 30px;
|
|
|
+ display: flex;
|
|
|
+ gap: 10px;
|
|
|
}
|
|
|
|
|
|
.btn-primary {
|
|
|
- width: 100%;
|
|
|
+ flex: 1;
|
|
|
padding: 12px;
|
|
|
background: linear-gradient(180deg, #2f5fd7 0%, #244cb0 100%);
|
|
|
border: 1px solid #325bbf;
|
|
|
@@ -372,4 +456,95 @@ const handleCancel = () => {
|
|
|
opacity: 0.6;
|
|
|
cursor: not-allowed;
|
|
|
}
|
|
|
+
|
|
|
+.btn-secondary {
|
|
|
+ background: transparent;
|
|
|
+ border: 1px solid #2a2a2a;
|
|
|
+ color: #cfcfcf;
|
|
|
+ border-radius: 6px;
|
|
|
+ padding: 10px 16px;
|
|
|
+ font-size: 14px;
|
|
|
+ cursor: pointer;
|
|
|
+}
|
|
|
+
|
|
|
+.btn-secondary:hover {
|
|
|
+ border-color: #3a3a3a;
|
|
|
+ color: #f2f2f2;
|
|
|
+}
|
|
|
+
|
|
|
+.btn-token {
|
|
|
+ width: 44px;
|
|
|
+ height: 44px;
|
|
|
+ display: inline-flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+ border-radius: 6px;
|
|
|
+ border: 1px solid #2a2a2a;
|
|
|
+ background: rgba(255, 255, 255, 0.02);
|
|
|
+ color: #cfcfcf;
|
|
|
+ cursor: pointer;
|
|
|
+}
|
|
|
+
|
|
|
+.btn-token:hover {
|
|
|
+ border-color: #3a3a3a;
|
|
|
+ color: #ffffff;
|
|
|
+}
|
|
|
+
|
|
|
+.btn-token:disabled {
|
|
|
+ opacity: 0.6;
|
|
|
+ cursor: not-allowed;
|
|
|
+}
|
|
|
+
|
|
|
+.btn-token-icon {
|
|
|
+ width: 18px;
|
|
|
+ height: 18px;
|
|
|
+}
|
|
|
+
|
|
|
+.token-modal-overlay {
|
|
|
+ position: absolute;
|
|
|
+ inset: 0;
|
|
|
+ background: rgba(0, 0, 0, 0.5);
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+ border-radius: 10px;
|
|
|
+}
|
|
|
+
|
|
|
+.token-modal {
|
|
|
+ width: 86%;
|
|
|
+ max-width: 320px;
|
|
|
+ background: #0d0d0d;
|
|
|
+ border: 1px solid #2a2a2a;
|
|
|
+ border-radius: 8px;
|
|
|
+ padding: 14px;
|
|
|
+ box-shadow: 0 16px 36px rgba(0, 0, 0, 0.4);
|
|
|
+}
|
|
|
+
|
|
|
+.token-modal-header {
|
|
|
+ font-size: 14px;
|
|
|
+ color: #f2f2f2;
|
|
|
+ margin-bottom: 10px;
|
|
|
+}
|
|
|
+
|
|
|
+.token-input {
|
|
|
+ width: 100%;
|
|
|
+ padding: 10px 12px;
|
|
|
+ background: #121212;
|
|
|
+ border: 1px solid #2a2a2a;
|
|
|
+ color: #f2f2f2;
|
|
|
+ border-radius: 6px;
|
|
|
+ outline: none;
|
|
|
+ font-size: 13px;
|
|
|
+}
|
|
|
+
|
|
|
+.token-input:focus {
|
|
|
+ border-color: #2f8cff;
|
|
|
+}
|
|
|
+
|
|
|
+.token-modal-actions {
|
|
|
+ display: flex;
|
|
|
+ justify-content: flex-end;
|
|
|
+ gap: 10px;
|
|
|
+ margin-top: 12px;
|
|
|
+}
|
|
|
</style>
|