741 lines
23 KiB
Vue
741 lines
23 KiB
Vue
<template>
|
||
<div class="salesforce-login-container">
|
||
<div class="login-header">
|
||
<h2>Salesforce 认证中心</h2>
|
||
<p class="login-description">选择适合您的登录方式连接到 Salesforce</p>
|
||
</div>
|
||
|
||
<!-- 登录表单 -->
|
||
<div class="login-section" v-if="!isLoggedIn">
|
||
<el-card class="login-card" shadow="hover">
|
||
<el-tabs v-model="activeTab" @tab-change="handleTabChange" class="login-tabs">
|
||
<!-- OAuth 2.0 登录 -->
|
||
<el-tab-pane label="OAuth 2.0" name="oauth2">
|
||
<div class="tab-content">
|
||
<div class="tab-description">
|
||
<h3>OAuth 2.0 认证</h3>
|
||
<p>使用标准的 OAuth 2.0 协议进行安全认证</p>
|
||
</div>
|
||
<el-form
|
||
ref="oauth2FormRef"
|
||
:model="oauth2Form"
|
||
:rules="oauth2Rules"
|
||
label-width="120px"
|
||
class="login-form"
|
||
>
|
||
<el-form-item label="授权类型" prop="grantType">
|
||
<el-select v-model="oauth2Form.grantType" placeholder="请选择授权类型" style="width: 100%">
|
||
<el-option label="密码模式" value="password"></el-option>
|
||
<el-option label="客户端凭证模式" value="client_credentials"></el-option>
|
||
<el-option label="授权码模式" value="authorization_code"></el-option>
|
||
</el-select>
|
||
</el-form-item>
|
||
|
||
<el-form-item label="客户端ID" prop="clientId">
|
||
<el-input v-model="oauth2Form.clientId" placeholder="请输入客户端ID"></el-input>
|
||
</el-form-item>
|
||
|
||
<el-form-item label="客户端密钥" prop="clientSecret">
|
||
<el-input v-model="oauth2Form.clientSecret" type="password" placeholder="请输入客户端密钥" show-password></el-input>
|
||
</el-form-item>
|
||
|
||
<el-form-item label="用户名" prop="username" v-show="oauth2Form.grantType === 'password'">
|
||
<el-input v-model="oauth2Form.username" placeholder="请输入用户名"></el-input>
|
||
</el-form-item>
|
||
|
||
<el-form-item label="密码" prop="password" v-show="oauth2Form.grantType === 'password'">
|
||
<el-input v-model="oauth2Form.password" type="password" placeholder="请输入密码" show-password></el-input>
|
||
</el-form-item>
|
||
|
||
<el-form-item label="安全令牌" prop="securityToken" v-show="oauth2Form.grantType === 'password'">
|
||
<el-input v-model="oauth2Form.securityToken" placeholder="请输入安全令牌(可选)"></el-input>
|
||
</el-form-item>
|
||
|
||
<el-form-item label="授权码" prop="code" v-show="oauth2Form.grantType === 'authorization_code'">
|
||
<el-input v-model="oauth2Form.code" placeholder="请输入授权码"></el-input>
|
||
</el-form-item>
|
||
|
||
<el-form-item label="State" prop="state" v-show="oauth2Form.grantType === 'authorization_code'">
|
||
<el-input v-model="oauth2Form.state" placeholder="请输入state参数"></el-input>
|
||
</el-form-item>
|
||
|
||
<el-form-item class="form-actions">
|
||
<el-button type="primary" @click="handleOAuth2Login" :loading="loading" size="large">
|
||
<el-icon><Connection /></el-icon>
|
||
登录
|
||
</el-button>
|
||
</el-form-item>
|
||
</el-form>
|
||
</div>
|
||
</el-tab-pane>
|
||
|
||
<!-- 传统凭据登录 -->
|
||
<el-tab-pane label="传统凭据" name="legacy_credential">
|
||
<div class="tab-content">
|
||
<div class="tab-description">
|
||
<h3>传统凭据认证</h3>
|
||
<p>使用用户名和密码进行传统登录</p>
|
||
</div>
|
||
<el-form
|
||
ref="legacyFormRef"
|
||
:model="legacyForm"
|
||
:rules="legacyRules"
|
||
label-width="120px"
|
||
class="login-form"
|
||
>
|
||
<el-form-item label="用户名" prop="username">
|
||
<el-input v-model="legacyForm.username" placeholder="请输入用户名"></el-input>
|
||
</el-form-item>
|
||
|
||
<el-form-item label="密码" prop="password">
|
||
<el-input v-model="legacyForm.password" type="password" placeholder="请输入密码" show-password></el-input>
|
||
</el-form-item>
|
||
|
||
<el-form-item label="安全令牌" prop="securityToken">
|
||
<el-input v-model="legacyForm.securityToken" placeholder="请输入安全令牌(可选)"></el-input>
|
||
</el-form-item>
|
||
|
||
<el-form-item class="form-actions">
|
||
<el-button type="primary" @click="handleLegacyLogin" :loading="loading" size="large">
|
||
<el-icon><User /></el-icon>
|
||
登录
|
||
</el-button>
|
||
</el-form-item>
|
||
</el-form>
|
||
</div>
|
||
</el-tab-pane>
|
||
|
||
<!-- Session ID 登录 -->
|
||
<el-tab-pane label="Session ID" name="session_id">
|
||
<div class="tab-content">
|
||
<div class="tab-description">
|
||
<h3>Session ID 认证</h3>
|
||
<p>使用现有的 Session ID 直接登录</p>
|
||
</div>
|
||
<el-form
|
||
ref="sessionIdFormRef"
|
||
:model="sessionIdForm"
|
||
:rules="sessionIdRules"
|
||
label-width="120px"
|
||
class="login-form"
|
||
>
|
||
<el-form-item label="Session ID" prop="sessionId">
|
||
<el-input v-model="sessionIdForm.sessionId" placeholder="请输入Session ID" type="textarea" :rows="3"></el-input>
|
||
</el-form-item>
|
||
|
||
<el-form-item class="form-actions">
|
||
<el-button type="primary" @click="handleSessionIdLogin" :loading="loading" size="large">
|
||
<el-icon><Key /></el-icon>
|
||
登录
|
||
</el-button>
|
||
</el-form-item>
|
||
</el-form>
|
||
</div>
|
||
</el-tab-pane>
|
||
|
||
<!-- Salesforce CLI 登录 -->
|
||
<el-tab-pane label="Salesforce CLI" name="salesforce_cli">
|
||
<div class="tab-content">
|
||
<div class="tab-description">
|
||
<h3>Salesforce CLI 认证</h3>
|
||
<p>使用已配置的 Salesforce CLI 凭据登录</p>
|
||
</div>
|
||
<el-form
|
||
ref="cliFormRef"
|
||
:model="cliForm"
|
||
label-width="120px"
|
||
class="login-form"
|
||
>
|
||
<el-form-item label="组织别名" prop="orgAlias">
|
||
<el-input v-model="cliForm.orgAlias" placeholder="请输入组织别名(可选)"></el-input>
|
||
</el-form-item>
|
||
|
||
<el-form-item class="form-actions">
|
||
<el-button type="primary" @click="handleCliLogin" :loading="loading" size="large">
|
||
<el-icon><Monitor /></el-icon>
|
||
登录
|
||
</el-button>
|
||
</el-form-item>
|
||
</el-form>
|
||
</div>
|
||
</el-tab-pane>
|
||
</el-tabs>
|
||
</el-card>
|
||
</div>
|
||
|
||
<!-- 登录成功后的会话信息展示 -->
|
||
<div v-if="isLoggedIn && sessionInfo" class="session-info-section">
|
||
<el-card class="session-card" shadow="hover">
|
||
<template #header>
|
||
<div class="card-header">
|
||
<h3>会话信息</h3>
|
||
<el-tag :type="sessionInfo.status === 'active' ? 'success' : 'danger'" size="large">
|
||
{{ sessionInfo.status === 'active' ? '活跃' : '非活跃' }}
|
||
</el-tag>
|
||
</div>
|
||
</template>
|
||
|
||
<el-descriptions :column="2" border class="session-descriptions">
|
||
<el-descriptions-item label="会话ID" span="2">
|
||
<el-text class="session-id" copyable>{{ sessionInfo.sessionId }}</el-text>
|
||
</el-descriptions-item>
|
||
<el-descriptions-item label="租户编号">{{ sessionInfo.tenantId }}</el-descriptions-item>
|
||
<el-descriptions-item label="用户名">{{ sessionInfo.username }}</el-descriptions-item>
|
||
<el-descriptions-item label="登录类型">{{ sessionInfo.loginType }}</el-descriptions-item>
|
||
<el-descriptions-item label="会话状态">
|
||
<el-tag :type="sessionInfo.status === 'active' ? 'success' : 'danger'">
|
||
{{ sessionInfo.status }}
|
||
</el-tag>
|
||
</el-descriptions-item>
|
||
<el-descriptions-item label="登录时间">{{ sessionInfo.loginTime }}</el-descriptions-item>
|
||
<el-descriptions-item label="过期时间">{{ sessionInfo.expireTime }}</el-descriptions-item>
|
||
<el-descriptions-item label="最后活动时间">{{ sessionInfo.lastActivityTime }}</el-descriptions-item>
|
||
<el-descriptions-item label="登录IP" span="2">{{ sessionInfo.loginIp }}</el-descriptions-item>
|
||
<el-descriptions-item label="设备信息" span="2">{{ sessionInfo.deviceInfo }}</el-descriptions-item>
|
||
<el-descriptions-item label="浏览器信息" span="2">{{ sessionInfo.browserInfo }}</el-descriptions-item>
|
||
<el-descriptions-item label="令牌ID" span="2">
|
||
<el-text class="token-id" copyable>{{ sessionInfo.tokenId }}</el-text>
|
||
</el-descriptions-item>
|
||
</el-descriptions>
|
||
|
||
<div class="session-actions">
|
||
<el-button type="primary" @click="fetchSessionInfo" :loading="loading">
|
||
<el-icon><Refresh /></el-icon>
|
||
刷新会话信息
|
||
</el-button>
|
||
<el-button type="danger" @click="logout">
|
||
<el-icon><SwitchButton /></el-icon>
|
||
退出登录
|
||
</el-button>
|
||
</div>
|
||
</el-card>
|
||
</div>
|
||
</div>
|
||
</template>
|
||
|
||
<script setup lang="ts">
|
||
import { ref, reactive, onMounted } from 'vue'
|
||
import { executeLogin as salesforceLogin, getLoginSession as getCurrentSession } from '@/api/auth/register'
|
||
import { ElMessage, ElMessageBox, FormInstance, FormRules } from 'element-plus'
|
||
import { Connection, User, Key, Monitor, Refresh, SwitchButton } from '@element-plus/icons-vue'
|
||
|
||
// 表单引用
|
||
const oauth2FormRef = ref<FormInstance>()
|
||
const legacyFormRef = ref<FormInstance>()
|
||
const sessionIdFormRef = ref<FormInstance>()
|
||
const cliFormRef = ref<FormInstance>()
|
||
|
||
// 当前激活的标签页
|
||
const activeTab = ref('oauth2')
|
||
|
||
// 加载状态
|
||
const loading = ref(false)
|
||
|
||
// 登录状态
|
||
const isLoggedIn = ref(false)
|
||
|
||
// 会话信息
|
||
const sessionInfo = ref<any>(null)
|
||
|
||
// OAuth2 表单数据
|
||
const oauth2Form = reactive({
|
||
loginType: 'oauth2',
|
||
grantType: 'password',
|
||
username: '',
|
||
password: '',
|
||
securityToken: '',
|
||
clientId: '',
|
||
clientSecret: '',
|
||
code: '',
|
||
state: ''
|
||
})
|
||
|
||
// 传统凭据表单数据
|
||
const legacyForm = reactive({
|
||
loginType: 'legacy_credential',
|
||
username: '',
|
||
password: '',
|
||
securityToken: ''
|
||
})
|
||
|
||
// Session ID 表单数据
|
||
const sessionIdForm = reactive({
|
||
loginType: 'session_id',
|
||
sessionId: ''
|
||
})
|
||
|
||
// CLI 表单数据
|
||
const cliForm = reactive({
|
||
loginType: 'salesforce_cli',
|
||
orgAlias: ''
|
||
})
|
||
|
||
// OAuth2 表单校验规则
|
||
const oauth2Rules = reactive<FormRules>({
|
||
grantType: [{ required: true, message: '请选择授权类型', trigger: 'change' }],
|
||
clientId: [{ required: true, message: '请输入客户端ID', trigger: 'blur' }],
|
||
clientSecret: [{ required: true, message: '请输入客户端密钥', trigger: 'blur' }],
|
||
username: [{
|
||
required: false,
|
||
message: '请输入用户名',
|
||
trigger: 'blur',
|
||
validator: (rule, value, callback) => {
|
||
if (oauth2Form.grantType === 'password' && !value) {
|
||
callback(new Error('密码模式下用户名为必填项'))
|
||
} else {
|
||
callback()
|
||
}
|
||
}
|
||
}],
|
||
password: [{
|
||
required: false,
|
||
message: '请输入密码',
|
||
trigger: 'blur',
|
||
validator: (rule, value, callback) => {
|
||
if (oauth2Form.grantType === 'password' && !value) {
|
||
callback(new Error('密码模式下密码为必填项'))
|
||
} else {
|
||
callback()
|
||
}
|
||
}
|
||
}],
|
||
code: [{
|
||
required: false,
|
||
message: '请输入授权码',
|
||
trigger: 'blur',
|
||
validator: (rule, value, callback) => {
|
||
if (oauth2Form.grantType === 'authorization_code' && !value) {
|
||
callback(new Error('授权码模式下授权码为必填项'))
|
||
} else {
|
||
callback()
|
||
}
|
||
}
|
||
}],
|
||
state: [{
|
||
required: false,
|
||
message: '请输入state参数',
|
||
trigger: 'blur',
|
||
validator: (rule, value, callback) => {
|
||
if (oauth2Form.grantType === 'authorization_code' && !value) {
|
||
callback(new Error('授权码模式下state参数为必填项'))
|
||
} else {
|
||
callback()
|
||
}
|
||
}
|
||
}]
|
||
})
|
||
|
||
// 传统凭据表单校验规则
|
||
const legacyRules = reactive<FormRules>({
|
||
username: [{ required: true, message: '请输入用户名', trigger: 'blur' }],
|
||
password: [{ required: true, message: '请输入密码', trigger: 'blur' }]
|
||
})
|
||
|
||
// Session ID 表单校验规则
|
||
const sessionIdRules = reactive<FormRules>({
|
||
sessionId: [{ required: true, message: '请输入Session ID', trigger: 'blur' }]
|
||
})
|
||
|
||
// 标签页切换处理
|
||
const handleTabChange = () => {
|
||
// 清空表单校验状态
|
||
oauth2FormRef.value?.clearValidate()
|
||
legacyFormRef.value?.clearValidate()
|
||
sessionIdFormRef.value?.clearValidate()
|
||
cliFormRef.value?.clearValidate()
|
||
}
|
||
|
||
// 通用登录处理函数
|
||
const handleLogin = async (formRef: FormInstance | undefined, formData: any, loginType: string) => {
|
||
if (!formRef) return
|
||
|
||
await formRef.validate(async (valid) => {
|
||
if (valid) {
|
||
loading.value = true
|
||
try {
|
||
// 处理CLI登录的特殊情况
|
||
let loginParams = { ...formData }
|
||
if (loginType === 'salesforce_cli' && !loginParams.orgAlias) {
|
||
delete loginParams.orgAlias
|
||
}
|
||
|
||
await salesforceLogin(loginParams)
|
||
ElMessage.success('登录成功')
|
||
await fetchSessionInfo()
|
||
} catch (error: any) {
|
||
console.error(`${loginType}登录失败:`, error)
|
||
const errorMessage = error.response?.data?.message || error.message || '登录失败,请检查您的凭据'
|
||
ElMessage.error(errorMessage)
|
||
} finally {
|
||
loading.value = false
|
||
}
|
||
}
|
||
})
|
||
}
|
||
|
||
// OAuth2 登录处理
|
||
const handleOAuth2Login = async () => {
|
||
if (!oauth2FormRef.value) return
|
||
|
||
await oauth2FormRef.value.validate(async (valid) => {
|
||
if (valid) {
|
||
loading.value = true
|
||
try {
|
||
// 构造登录参数 - 所有OAuth2模式都需要客户端ID和密钥
|
||
const loginParams: any = {
|
||
loginType: oauth2Form.loginType,
|
||
grantType: oauth2Form.grantType,
|
||
clientId: oauth2Form.clientId,
|
||
clientSecret: oauth2Form.clientSecret
|
||
}
|
||
|
||
// 根据不同授权类型添加特定参数
|
||
if (oauth2Form.grantType === 'password') {
|
||
loginParams.username = oauth2Form.username
|
||
loginParams.password = oauth2Form.password
|
||
// 仅在有值时添加安全令牌
|
||
if (oauth2Form.securityToken) {
|
||
loginParams.securityToken = oauth2Form.securityToken
|
||
}
|
||
} else if (oauth2Form.grantType === 'authorization_code') {
|
||
loginParams.code = oauth2Form.code
|
||
loginParams.state = oauth2Form.state
|
||
}
|
||
// client_credentials 类型需要 clientId 和 clientSecret,已在基础参数中
|
||
|
||
await salesforceLogin(loginParams)
|
||
ElMessage.success('登录成功')
|
||
await fetchSessionInfo()
|
||
} catch (error: any) {
|
||
console.error('OAuth2登录失败:', error)
|
||
const errorMessage = error.response?.data?.message || error.message || '登录失败,请检查您的凭据'
|
||
ElMessage.error(errorMessage)
|
||
} finally {
|
||
loading.value = false
|
||
}
|
||
}
|
||
})
|
||
}
|
||
|
||
// 传统凭据登录处理
|
||
const handleLegacyLogin = () => handleLogin(legacyFormRef.value, legacyForm, '传统凭据')
|
||
|
||
// Session ID 登录处理
|
||
const handleSessionIdLogin = () => handleLogin(sessionIdFormRef.value, sessionIdForm, 'Session ID')
|
||
|
||
// CLI 登录处理
|
||
const handleCliLogin = () => {
|
||
if (!cliFormRef.value) return
|
||
|
||
loading.value = true
|
||
try {
|
||
// 如果没有填写组织别名,则删除该字段
|
||
const loginParams = { ...cliForm }
|
||
if (!loginParams.orgAlias) {
|
||
delete loginParams.orgAlias
|
||
}
|
||
|
||
salesforceLogin(loginParams).then(() => {
|
||
ElMessage.success('登录成功')
|
||
fetchSessionInfo()
|
||
}).catch((error: any) => {
|
||
console.error('CLI登录失败:', error)
|
||
const errorMessage = error.response?.data?.message || error.message || '登录失败,请检查您的CLI配置'
|
||
ElMessage.error(errorMessage)
|
||
}).finally(() => {
|
||
loading.value = false
|
||
})
|
||
} catch (error: any) {
|
||
console.error('CLI登录失败:', error)
|
||
const errorMessage = error.response?.data?.message || error.message || '登录失败,请检查您的CLI配置'
|
||
ElMessage.error(errorMessage)
|
||
loading.value = false
|
||
}
|
||
}
|
||
|
||
// 获取会话信息
|
||
const fetchSessionInfo = async () => {
|
||
try {
|
||
const response = await getCurrentSession()
|
||
if (response.code === 200) {
|
||
sessionInfo.value = response.data
|
||
isLoggedIn.value = true
|
||
} else {
|
||
ElMessage.error(response.msg || '获取会话信息失败')
|
||
isLoggedIn.value = false
|
||
sessionInfo.value = null
|
||
}
|
||
} catch (error: any) {
|
||
console.error('获取会话信息失败:', error)
|
||
const errorMessage = error.response?.data?.message || error.message || '获取会话信息失败'
|
||
ElMessage.error(errorMessage)
|
||
isLoggedIn.value = false
|
||
sessionInfo.value = null
|
||
}
|
||
}
|
||
|
||
// 页面加载时尝试获取会话信息
|
||
onMounted(() => {
|
||
fetchSessionInfo()
|
||
})
|
||
|
||
// 退出登录
|
||
const logout = () => {
|
||
ElMessageBox.confirm('确定要退出登录吗?', '提示', {
|
||
confirmButtonText: '确定',
|
||
cancelButtonText: '取消',
|
||
type: 'warning'
|
||
}).then(() => {
|
||
isLoggedIn.value = false
|
||
sessionInfo.value = null
|
||
ElMessage.success('已退出登录')
|
||
}).catch(() => {
|
||
// 取消退出
|
||
})
|
||
}
|
||
</script>
|
||
|
||
<style scoped lang="scss">
|
||
.salesforce-login-container {
|
||
padding: 24px;
|
||
max-width: 900px;
|
||
margin: 0 auto;
|
||
min-height: 100vh;
|
||
display: flex;
|
||
flex-direction: column;
|
||
|
||
.login-header {
|
||
text-align: center;
|
||
margin-bottom: 32px;
|
||
|
||
h2 {
|
||
font-size: 28px;
|
||
font-weight: 600;
|
||
color: #303133;
|
||
margin-bottom: 12px;
|
||
}
|
||
|
||
.login-description {
|
||
font-size: 16px;
|
||
color: #606266;
|
||
max-width: 600px;
|
||
margin: 0 auto;
|
||
}
|
||
}
|
||
|
||
.login-section {
|
||
flex: 1;
|
||
display: flex;
|
||
flex-direction: column;
|
||
justify-content: center;
|
||
|
||
.login-card {
|
||
border-radius: 12px;
|
||
overflow: hidden;
|
||
|
||
.login-tabs {
|
||
:deep(.el-tabs__header) {
|
||
margin-bottom: 24px;
|
||
}
|
||
|
||
:deep(.el-tabs__nav-wrap) {
|
||
&::after {
|
||
height: 1px;
|
||
}
|
||
}
|
||
|
||
:deep(.el-tabs__item) {
|
||
font-size: 16px;
|
||
font-weight: 500;
|
||
padding: 0 24px;
|
||
}
|
||
}
|
||
|
||
.tab-content {
|
||
padding: 0 8px;
|
||
|
||
.tab-description {
|
||
margin-bottom: 24px;
|
||
text-align: center;
|
||
|
||
h3 {
|
||
font-size: 20px;
|
||
font-weight: 600;
|
||
color: #303133;
|
||
margin-bottom: 8px;
|
||
}
|
||
|
||
p {
|
||
font-size: 14px;
|
||
color: #909399;
|
||
}
|
||
}
|
||
|
||
.login-form {
|
||
max-width: 500px;
|
||
margin: 0 auto;
|
||
|
||
:deep(.el-form-item__label) {
|
||
font-weight: 500;
|
||
color: #606266;
|
||
}
|
||
|
||
:deep(.el-input__wrapper) {
|
||
border-radius: 6px;
|
||
}
|
||
|
||
:deep(.el-select .el-input .el-input__wrapper) {
|
||
border-radius: 6px;
|
||
}
|
||
|
||
.form-actions {
|
||
margin-top: 32px;
|
||
text-align: center;
|
||
|
||
.el-button {
|
||
padding: 12px 32px;
|
||
font-size: 16px;
|
||
border-radius: 6px;
|
||
|
||
.el-icon {
|
||
margin-right: 8px;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
.session-info-section {
|
||
margin-top: 32px;
|
||
|
||
.session-card {
|
||
border-radius: 12px;
|
||
overflow: hidden;
|
||
|
||
.card-header {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
|
||
h3 {
|
||
font-size: 20px;
|
||
font-weight: 600;
|
||
color: #303133;
|
||
margin: 0;
|
||
}
|
||
}
|
||
|
||
.session-descriptions {
|
||
margin: 24px 0;
|
||
|
||
:deep(.el-descriptions__label) {
|
||
font-weight: 500;
|
||
color: #606266;
|
||
}
|
||
|
||
:deep(.el-descriptions__content) {
|
||
color: #303133;
|
||
}
|
||
|
||
.session-id, .token-id {
|
||
font-family: monospace;
|
||
font-size: 13px;
|
||
word-break: break-all;
|
||
}
|
||
}
|
||
|
||
.session-actions {
|
||
display: flex;
|
||
justify-content: center;
|
||
gap: 16px;
|
||
margin-top: 24px;
|
||
|
||
.el-button {
|
||
padding: 10px 24px;
|
||
border-radius: 6px;
|
||
|
||
.el-icon {
|
||
margin-right: 8px;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
@media (max-width: 768px) {
|
||
.salesforce-login-container {
|
||
padding: 16px;
|
||
|
||
.login-header {
|
||
h2 {
|
||
font-size: 24px;
|
||
}
|
||
|
||
.login-description {
|
||
font-size: 14px;
|
||
}
|
||
}
|
||
|
||
.login-section {
|
||
.login-card {
|
||
.login-tabs {
|
||
:deep(.el-tabs__item) {
|
||
font-size: 14px;
|
||
padding: 0 16px;
|
||
}
|
||
}
|
||
|
||
.tab-content {
|
||
.tab-description {
|
||
h3 {
|
||
font-size: 18px;
|
||
}
|
||
|
||
p {
|
||
font-size: 13px;
|
||
}
|
||
}
|
||
|
||
.login-form {
|
||
max-width: 100%;
|
||
|
||
.form-actions {
|
||
.el-button {
|
||
padding: 10px 24px;
|
||
font-size: 14px;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
.session-info-section {
|
||
.session-card {
|
||
.session-descriptions {
|
||
:deep(.el-descriptions__label) {
|
||
font-size: 13px;
|
||
}
|
||
|
||
:deep(.el-descriptions__content) {
|
||
font-size: 13px;
|
||
}
|
||
}
|
||
|
||
.session-actions {
|
||
flex-direction: column;
|
||
align-items: center;
|
||
|
||
.el-button {
|
||
width: 100%;
|
||
max-width: 200px;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
</style> |