feat: 添加设备认证对话框的预配置支持,优化自动认证逻辑

This commit is contained in:
SunWuyuan
2025-11-02 15:15:46 +08:00
parent 9aae9601f6
commit 7f166ffddc
3 changed files with 157 additions and 5 deletions
+54 -2
View File
@@ -153,7 +153,9 @@
max-width="500" max-width="500"
> >
<DeviceAuthDialog <DeviceAuthDialog
ref="deviceAuthDialog"
:show-cancel="true" :show-cancel="true"
:preconfig="deviceAuthPreconfig"
@success="handleAuthSuccess" @success="handleAuthSuccess"
@cancel="showDeviceAuthDialog = false" @cancel="showDeviceAuthDialog = false"
/> />
@@ -184,13 +186,25 @@
</template> </template>
<script setup> <script setup>
import { ref, computed, onMounted } from 'vue' import { ref, computed, onMounted, watch } from 'vue'
import { getSetting, setSetting } from '@/utils/settings' import { getSetting, setSetting } from '@/utils/settings'
import DeviceAuthDialog from './auth/DeviceAuthDialog.vue' import DeviceAuthDialog from './auth/DeviceAuthDialog.vue'
import TokenInputDialog from './auth/TokenInputDialog.vue' import TokenInputDialog from './auth/TokenInputDialog.vue'
import AlternativeCodeDialog from './auth/AlternativeCodeDialog.vue' import AlternativeCodeDialog from './auth/AlternativeCodeDialog.vue'
import FirstTimeGuide from './auth/FirstTimeGuide.vue' import FirstTimeGuide from './auth/FirstTimeGuide.vue'
const props = defineProps({
preconfig: {
type: Object,
default: () => ({
namespace: null,
authCode: null,
autoOpen: false,
autoExecute: false
})
}
})
const emit = defineEmits(['done']) const emit = defineEmits(['done'])
// 控制显示:仅首页且无 kvToken(且 provider 不是 kv-local)显示 // 控制显示:仅首页且无 kvToken(且 provider 不是 kv-local)显示
@@ -202,10 +216,25 @@ const showDeviceAuthDialog = ref(false)
const showTokenDialog = ref(false) const showTokenDialog = ref(false)
const showAlternativeCodeDialog = ref(false) const showAlternativeCodeDialog = ref(false)
// 设备认证对话框引用
const deviceAuthDialog = ref(null)
const provider = computed(() => getSetting('server.provider')) const provider = computed(() => getSetting('server.provider'))
const isKvProvider = computed(() => provider.value === 'kv-server' || provider.value === 'classworkscloud') const isKvProvider = computed(() => provider.value === 'kv-server' || provider.value === 'classworkscloud')
const kvToken = computed(() => getSetting('server.kvToken')) const kvToken = computed(() => getSetting('server.kvToken'))
// 设备认证预配置数据
const deviceAuthPreconfig = computed(() => {
if (props.preconfig?.namespace) {
return {
namespace: props.preconfig.namespace,
password: props.preconfig.authCode || '',
autoExecute: props.preconfig.autoExecute || false
}
}
return null
})
const evaluateVisibility = () => { const evaluateVisibility = () => {
const path = window.location.pathname const path = window.location.pathname
const onHome = path === '/' || path === '/index' || path === '/index.html' const onHome = path === '/' || path === '/index' || path === '/index.html'
@@ -213,6 +242,21 @@ const evaluateVisibility = () => {
visible.value = onHome && need visible.value = onHome && need
} }
// 监听预配数据变化,自动打开设备认证对话框
watch(
() => props.preconfig,
(newPreconfig) => {
if (newPreconfig?.autoOpen && newPreconfig?.namespace && visible.value) {
console.log('检测到预配数据,自动打开设备认证对话框')
// 延迟一下确保组件已完全挂载
setTimeout(() => {
showDeviceAuthDialog.value = true
}, 500)
}
},
{ immediate: true, deep: true }
)
onMounted(() => { onMounted(() => {
evaluateVisibility() evaluateVisibility()
}) })
@@ -231,8 +275,16 @@ const handleAutoAuthorize = () => {
window.location.href = url window.location.href = url
} }
const handleAuthSuccess = () => { const handleAuthSuccess = (tokenData) => {
showDeviceAuthDialog.value = false showDeviceAuthDialog.value = false
console.log('认证成功:', tokenData)
// 如果是通过预配数据成功的,显示成功消息
if (props.preconfig?.namespace) {
// 可以在这里添加成功提示
console.log(`预配数据认证成功: ${props.preconfig.namespace}`)
}
evaluateVisibility() evaluateVisibility()
emit('done') emit('done')
} }
+30 -2
View File
@@ -100,14 +100,18 @@
</template> </template>
<script setup> <script setup>
import { ref } from 'vue' import { ref, watch } from 'vue'
import { getSetting, setSetting } from '@/utils/settings' import { getSetting, setSetting } from '@/utils/settings'
import axios from '@/axios/axios' import axios from '@/axios/axios'
defineProps({ const props = defineProps({
showCancel: { showCancel: {
type: Boolean, type: Boolean,
default: false default: false
},
preconfig: {
type: Object,
default: null
} }
}) })
@@ -120,6 +124,30 @@ const form = ref({
const authenticating = ref(false) const authenticating = ref(false)
const error = ref('') const error = ref('')
// 监听预配置数据变化
watch(
() => props.preconfig,
(newPreconfig) => {
if (newPreconfig) {
console.log('应用预配置数据:', newPreconfig)
form.value.namespace = newPreconfig.namespace || ''
form.value.password = newPreconfig.password || ''
// 如果启用自动执行且有命名空间,自动尝试认证
if (newPreconfig.autoExecute && newPreconfig.namespace) {
console.log('检测到自动执行标志且有命名空间,自动执行认证')
// 延迟一下确保UI已更新
setTimeout(() => {
authenticate()
}, 300)
} else if (newPreconfig.namespace) {
console.log('预配置数据已填入,等待手动认证')
}
}
},
{ immediate: true, deep: true }
)
const authenticate = async () => { const authenticate = async () => {
if (!form.value.namespace || authenticating.value) return if (!form.value.namespace || authenticating.value) return
error.value = '' error.value = ''
+73 -1
View File
@@ -43,7 +43,11 @@
</template> </template>
</v-app-bar> </v-app-bar>
<!-- 初始化选择卡片,仅在首页且需要授权时显示;不影响顶栏 --> <!-- 初始化选择卡片,仅在首页且需要授权时显示;不影响顶栏 -->
<init-service-chooser v-if="shouldShowInit" @done="settingsTick++" /> <init-service-chooser
v-if="shouldShowInit"
:preconfig="preconfigData"
@done="settingsTick++"
/>
<!-- 学生姓名管理组件 --> <!-- 学生姓名管理组件 -->
<StudentNameManager <StudentNameManager
@@ -797,6 +801,13 @@ export default {
$offKvChanged: null, $offKvChanged: null,
$offConnect: null, $offConnect: null,
debouncedRealtimeRefresh: null, debouncedRealtimeRefresh: null,
// 预配数据
preconfigData: {
namespace: null,
authCode: null,
autoOpen: false,
autoExecute: false
},
}; };
}, },
@@ -1203,6 +1214,9 @@ export default {
}, },
async initializeData() { async initializeData() {
// 解析预配数据
this.parsePreconfigData();
const configApplied = await this.parseUrlConfig(); const configApplied = await this.parseUrlConfig();
const urlParams = new URLSearchParams(window.location.search); const urlParams = new URLSearchParams(window.location.search);
@@ -2232,6 +2246,64 @@ export default {
currentDate.setDate(currentDate.getDate() + offset); currentDate.setDate(currentDate.getDate() + offset);
this.handleDateSelect(currentDate); this.handleDateSelect(currentDate);
}, },
// 解析预配数据
parsePreconfigData() {
try {
const urlParams = new URLSearchParams(window.location.search);
const namespace = urlParams.get("namespace");
const authCode = urlParams.get("authCode") || urlParams.get("auth_code");
const autoExecute = urlParams.get("autoExecute") || urlParams.get("auto_execute");
if (namespace) {
this.preconfigData.namespace = namespace;
this.preconfigData.authCode = authCode;
this.preconfigData.autoOpen = true;
// 解析自动执行参数,支持 true/false、1/0、yes/no
this.preconfigData.autoExecute = this.parseBoolean(autoExecute);
console.log("检测到预配数据:", {
namespace: this.preconfigData.namespace,
hasAuthCode: !!this.preconfigData.authCode,
autoExecute: this.preconfigData.autoExecute
});
// 清理URL参数,避免重复处理
this.cleanupUrlParams(['namespace', 'authCode', 'auth_code', 'autoExecute', 'auto_execute']);
}
} catch (error) {
console.error("解析预配数据失败:", error);
}
},
// 解析布尔值参数
parseBoolean(value) {
if (!value) return false;
const lowerValue = value.toLowerCase();
return lowerValue === 'true' || lowerValue === '1' || lowerValue === 'yes';
},
// 清理URL参数
cleanupUrlParams(params) {
try {
const url = new URL(window.location);
let hasChanged = false;
params.forEach(param => {
if (url.searchParams.has(param)) {
url.searchParams.delete(param);
hasChanged = true;
}
});
if (hasChanged) {
// 使用 replaceState 避免创建新的历史记录
window.history.replaceState({}, document.title, url.toString());
}
} catch (error) {
console.error("清理URL参数失败:", error);
}
},
}, },
}; };
</script> </script>