mirror of
https://github.com/ZeroCatDev/Classworks.git
synced 2025-12-07 13:03:59 +00:00
feat: 添加 FingerprintJS 依赖,集成访客 ID 和指纹数据功能
This commit is contained in:
parent
ba96069a9b
commit
5f363aba38
@ -12,6 +12,7 @@
|
||||
"dependencies": {
|
||||
"@examaware-cs/core": "^1.0.0",
|
||||
"@examaware-cs/player": "^1.0.2",
|
||||
"@fingerprintjs/fingerprintjs": "^5.0.1",
|
||||
"@mdi/font": "7.4.47",
|
||||
"@microsoft/clarity": "^1.0.2",
|
||||
"@vueuse/core": "^14.1.0",
|
||||
|
||||
8
pnpm-lock.yaml
generated
8
pnpm-lock.yaml
generated
@ -14,6 +14,9 @@ importers:
|
||||
'@examaware-cs/player':
|
||||
specifier: ^1.0.2
|
||||
version: 1.0.2(tdesign-vue-next@1.17.5(vue@3.5.25(typescript@5.9.3)))(vue@3.5.25(typescript@5.9.3))
|
||||
'@fingerprintjs/fingerprintjs':
|
||||
specifier: ^5.0.1
|
||||
version: 5.0.1
|
||||
'@mdi/font':
|
||||
specifier: 7.4.47
|
||||
version: 7.4.47
|
||||
@ -871,6 +874,9 @@ packages:
|
||||
tdesign-vue-next: ^1.15.5
|
||||
vue: ^3.0.0
|
||||
|
||||
'@fingerprintjs/fingerprintjs@5.0.1':
|
||||
resolution: {integrity: sha512-KbaeE/rk2WL8MfpRP6jTI4lSr42SJPjvkyrjP3QU6uUDkOMWWYC2Ts1sNSYcegHC8avzOoYTHBj+2fTqvZWQBA==}
|
||||
|
||||
'@humanfs/core@0.19.1':
|
||||
resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==}
|
||||
engines: {node: '>=18.18.0'}
|
||||
@ -4559,6 +4565,8 @@ snapshots:
|
||||
tdesign-vue-next: 1.17.5(vue@3.5.25(typescript@5.9.3))
|
||||
vue: 3.5.25(typescript@5.9.3)
|
||||
|
||||
'@fingerprintjs/fingerprintjs@5.0.1': {}
|
||||
|
||||
'@humanfs/core@0.19.1': {}
|
||||
|
||||
'@humanfs/node@0.16.7':
|
||||
|
||||
36
src/App.vue
36
src/App.vue
@ -2,50 +2,42 @@
|
||||
<v-app>
|
||||
<!-- 正常路由 -->
|
||||
<router-view v-slot="{ Component, route }">
|
||||
<transition
|
||||
mode="out-in"
|
||||
name="md3"
|
||||
>
|
||||
<component
|
||||
:is="Component"
|
||||
:key="route.path"
|
||||
/>
|
||||
<transition mode="out-in" name="md3">
|
||||
<component :is="Component" :key="route.path" />
|
||||
</transition>
|
||||
</router-view>
|
||||
<global-message/>
|
||||
<rate-limit-modal/>
|
||||
<global-message />
|
||||
<rate-limit-modal />
|
||||
</v-app>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import {onMounted} from "vue";
|
||||
import {useTheme} from "vuetify";
|
||||
import {getSetting} from "@/utils/settings";
|
||||
import { onMounted } from "vue";
|
||||
import { useTheme } from "vuetify";
|
||||
import { getSetting } from "@/utils/settings";
|
||||
import RateLimitModal from "@/components/RateLimitModal.vue";
|
||||
import Clarity from "@microsoft/clarity";
|
||||
import { getVisitorId } from "@/utils/fingerprint";
|
||||
|
||||
const theme = useTheme();
|
||||
|
||||
onMounted(() => {
|
||||
onMounted(async () => {
|
||||
// 应用保存的主题设置
|
||||
const savedTheme = getSetting("theme.mode");
|
||||
theme.global.name.value = savedTheme;
|
||||
|
||||
const visitorId = await getVisitorId();
|
||||
console.log("Visitor ID:", visitorId);
|
||||
// Clarity 标识(保留在 App 层)
|
||||
Clarity.identify(
|
||||
getSetting("device.uuid"),
|
||||
getSetting("server.domain"),
|
||||
getSetting("server.provider"),
|
||||
getSetting("server.classNumber")
|
||||
);
|
||||
Clarity.identify(visitorId);
|
||||
Clarity.setTag("fingerprintjs", visitorId);
|
||||
});
|
||||
|
||||
</script>
|
||||
<style>
|
||||
.md3-enter-active,
|
||||
.md3-leave-active {
|
||||
transition: opacity 0.3s cubic-bezier(0.4, 0, 0.2, 1),
|
||||
transform 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
transform 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
}
|
||||
|
||||
.md3-enter-from {
|
||||
|
||||
46
src/pages/debug.vue
Normal file
46
src/pages/debug.vue
Normal file
@ -0,0 +1,46 @@
|
||||
<template>
|
||||
<v-container>
|
||||
<v-card class="mb-4">
|
||||
<v-card-title>调试信息</v-card-title>
|
||||
<v-card-text>
|
||||
<div class="text-h6 mb-2">访客 ID</div>
|
||||
<v-code class="d-block pa-2 bg-grey-lighten-4 rounded mb-4">
|
||||
{{ visitorId || '加载中...' }}
|
||||
</v-code>
|
||||
|
||||
|
||||
</v-card-text>
|
||||
<v-card-actions>
|
||||
<v-btn color="primary" @click="loadData" :loading="loading">
|
||||
Refresh
|
||||
</v-btn>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
</v-container>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted } from 'vue'
|
||||
import { getVisitorId, getFingerprintData } from '@/utils/fingerprint'
|
||||
|
||||
const visitorId = ref('')
|
||||
const fingerprintData = ref({})
|
||||
const loading = ref(false)
|
||||
|
||||
const loadData = async () => {
|
||||
loading.value = true
|
||||
try {
|
||||
visitorId.value = await getVisitorId()
|
||||
fingerprintData.value = await getFingerprintData()
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
visitorId.value = 'Error loading visitor ID'
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
loadData()
|
||||
})
|
||||
</script>
|
||||
22
src/utils/fingerprint.js
Normal file
22
src/utils/fingerprint.js
Normal file
@ -0,0 +1,22 @@
|
||||
import FingerprintJS from '@fingerprintjs/fingerprintjs'
|
||||
|
||||
let fpPromise
|
||||
|
||||
export const loadFingerprint = () => {
|
||||
if (!fpPromise) {
|
||||
fpPromise = FingerprintJS.load()
|
||||
}
|
||||
return fpPromise
|
||||
}
|
||||
|
||||
export const getVisitorId = async () => {
|
||||
const fp = await loadFingerprint()
|
||||
const result = await fp.get()
|
||||
return result.visitorId
|
||||
}
|
||||
|
||||
export const getFingerprintData = async () => {
|
||||
const fp = await loadFingerprint()
|
||||
const result = await fp.get()
|
||||
return result
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user