|
24 | 24 | <div class="d-flex gap-2 flex-wrap mb-6"> |
25 | 25 | <v-btn |
26 | 26 | color="red" |
27 | | - href="https://github.com/ClassworksDev/Classworks/issues" |
28 | 27 | prepend-icon="mdi-bug" |
29 | | - target="_blank" |
30 | 28 | variant="tonal" |
| 29 | + @click="openReportDialog" |
31 | 30 | > |
32 | 31 | 报告问题 |
33 | 32 | </v-btn> |
|
170 | 169 | </v-card> |
171 | 170 | </v-dialog> |
172 | 171 |
|
| 172 | + <!-- 报告问题对话框 --> |
| 173 | + <v-dialog v-model="showReportDialog" max-width="640"> |
| 174 | + <v-card> |
| 175 | + <v-toolbar density="compact"> |
| 176 | + <v-btn icon="mdi-close" @click="showReportDialog = false"></v-btn> |
| 177 | + <v-toolbar-title>报告问题</v-toolbar-title> |
| 178 | + <v-spacer></v-spacer> |
| 179 | + </v-toolbar> |
| 180 | + <v-card-text> |
| 181 | + <p class="mb-4"> |
| 182 | + 调试ID与下方的浏览器环境信息将帮助我们快速定位问题,请在反馈中一并附上。 |
| 183 | + </p> |
| 184 | + <v-sheet class="mb-3 pa-3 bg-grey-lighten-4 rounded" style="max-height: 260px; overflow: auto;"> |
| 185 | + <pre class="text-body-2" style="white-space: pre-wrap; margin: 0;">{{ envBoxText }}</pre> |
| 186 | + </v-sheet> |
| 187 | + <div class="d-flex gap-2 flex-wrap mb-4"> |
| 188 | + <v-btn size="small" variant="text" prepend-icon="mdi-refresh" @click="reloadVisitorId" :loading="visitorLoading">刷新</v-btn> |
| 189 | + <v-btn size="small" variant="text" prepend-icon="mdi-content-copy" @click="copyEnvInfo">复制信息</v-btn> |
| 190 | + <v-btn size="small" variant="text" prepend-icon="mdi-open-in-new" @click="goToDebug">查看 /debug 页面</v-btn> |
| 191 | + </div> |
| 192 | + <v-alert v-if="copyOk" type="success" density="compact" class="mb-4">已复制到剪贴板</v-alert> |
| 193 | + <h4 class="text-subtitle-1 mb-2">反馈渠道</h4> |
| 194 | + <v-list lines="one" class="bg-transparent"> |
| 195 | + <v-list-item :href="qqGroupLink" target="_blank" prepend-icon="mdi-qqchat"> |
| 196 | + <v-list-item-title>QQ群 ({{ qqGroupNumber }})</v-list-item-title> |
| 197 | + <v-list-item-subtitle>964979747</v-list-item-subtitle> |
| 198 | + </v-list-item> |
| 199 | + <v-list-item :href="githubIssueUrl" target="_blank" prepend-icon="mdi-github"> |
| 200 | + <v-list-item-title>GitHub Issue</v-list-item-title> |
| 201 | + <v-list-item-subtitle>ZeroCatDev/Classworks</v-list-item-subtitle> |
| 202 | + </v-list-item> |
| 203 | + <v-list-item :href="mailtoLink" target="_blank" prepend-icon="mdi-email"> |
| 204 | + <v-list-item-title>邮件</v-list-item-title> |
| 205 | + <v-list-item-subtitle>sun@wuyuan.dev</v-list-item-subtitle> |
| 206 | + </v-list-item> |
| 207 | + </v-list> |
| 208 | + </v-card-text> |
| 209 | + <v-card-actions> |
| 210 | + <v-spacer></v-spacer> |
| 211 | + <v-btn variant="text" @click="showReportDialog = false">关闭</v-btn> |
| 212 | + </v-card-actions> |
| 213 | + </v-card> |
| 214 | + </v-dialog> |
| 215 | + |
173 | 216 | <p class="text-caption text-medium-emphasis"> |
174 | 217 | Copyright © {{ new Date().getFullYear() }} Sunwuyuan |
175 | 218 | </p> |
|
181 | 224 | </template> |
182 | 225 |
|
183 | 226 | <script> |
184 | | -import {ref, onMounted} from "vue"; |
| 227 | +import {ref, onMounted, computed} from "vue"; |
| 228 | +import { useRouter } from 'vue-router' |
| 229 | +import { getVisitorId } from '@/utils/fingerprint' |
185 | 230 | import packageJson from "../../../package.json"; |
186 | 231 |
|
187 | 232 | export default { |
188 | 233 | name: "AboutCard", |
189 | 234 | setup() { |
190 | 235 | const Dependencies = ref([]); |
191 | | - const showDeps = ref(false); // 添加对话框显示状态 |
| 236 | + const showDeps = ref(false); |
| 237 | + const showReportDialog = ref(false); |
| 238 | + const debugIdInput = ref(''); |
| 239 | + const visitorLoading = ref(false); |
| 240 | + const copyOk = ref(false); |
| 241 | + const qqGroupNumber = '964979747'; |
| 242 | + const qqGroupLink = 'https://qm.qq.com/q/T6qImKJjGi'; |
| 243 | + const router = useRouter(); |
192 | 244 |
|
193 | 245 | const loadDependencies = () => { |
194 | 246 | try { |
@@ -224,13 +276,109 @@ export default { |
224 | 276 | return descriptions[name] || ""; |
225 | 277 | }; |
226 | 278 |
|
| 279 | + const goToDebug = () => { |
| 280 | + router.push('/debug'); |
| 281 | + }; |
| 282 | +
|
| 283 | + const loadVisitorId = async () => { |
| 284 | + visitorLoading.value = true; |
| 285 | + try { |
| 286 | + const id = await getVisitorId(); |
| 287 | + debugIdInput.value = id || ''; |
| 288 | + } catch (e) { |
| 289 | + console.error('获取访客ID失败', e); |
| 290 | + } finally { |
| 291 | + visitorLoading.value = false; |
| 292 | + } |
| 293 | + }; |
| 294 | +
|
| 295 | + const reloadVisitorId = () => loadVisitorId(); |
| 296 | +
|
| 297 | + const openReportDialog = async () => { |
| 298 | + showReportDialog.value = true; |
| 299 | + if (!debugIdInput.value) await loadVisitorId(); |
| 300 | + }; |
| 301 | +
|
| 302 | + const copyEnvInfo = async () => { |
| 303 | + try { |
| 304 | + await navigator.clipboard.writeText(envBoxText.value); |
| 305 | + copyOk.value = true; |
| 306 | + setTimeout(() => (copyOk.value = false), 1800); |
| 307 | + } catch (e) { |
| 308 | + console.error('复制失败', e); |
| 309 | + } |
| 310 | + }; |
| 311 | +
|
| 312 | + const envInfo = computed(() => { |
| 313 | + const nav = navigator || {}; |
| 314 | + const intl = (typeof Intl !== 'undefined' && Intl.DateTimeFormat) ? Intl.DateTimeFormat().resolvedOptions() : {}; |
| 315 | + const tz = intl && intl.timeZone ? intl.timeZone : ''; |
| 316 | + const routePath = router.currentRoute?.value?.fullPath || location.pathname; |
| 317 | + const lines = [ |
| 318 | + `App 版本: v${packageJson?.version || 'unknown'}`, |
| 319 | + `URL: ${location.href}`, |
| 320 | + `路由: ${routePath}`, |
| 321 | + `UserAgent: ${nav.userAgent || ''}`, |
| 322 | + `语言: ${nav.language || ''}`, |
| 323 | + `时区: ${tz}`, |
| 324 | + `平台: ${nav.platform || ''}`, |
| 325 | + `在线: ${String(nav.onLine)}`, |
| 326 | + `屏幕: ${screen?.width || '-'}x${screen?.height || '-'}`, |
| 327 | + `视口: ${window.innerWidth || '-'}x${window.innerHeight || '-'}`, |
| 328 | + ]; |
| 329 | + return lines.join('\n'); |
| 330 | + }); |
| 331 | +
|
| 332 | + const envBoxText = computed(() => { |
| 333 | + return `调试ID: ${debugIdInput.value || '获取失败'}\n\n浏览器/环境信息:\n${envInfo.value}`; |
| 334 | + }); |
| 335 | +
|
| 336 | + const reportBody = computed(() => { |
| 337 | + return [ |
| 338 | + `问题描述:`, |
| 339 | + `1. 期望行为:`, |
| 340 | + `2. 实际行为:`, |
| 341 | + `3. 复现步骤:`, |
| 342 | + '', |
| 343 | + envBoxText.value, |
| 344 | + ].join('\n'); |
| 345 | + }); |
| 346 | +
|
| 347 | + const githubIssueUrl = computed(() => { |
| 348 | + const base = 'https://github.com/ZeroCatDev/Classworks/issues/new'; |
| 349 | + const title = encodeURIComponent('问题报告'); |
| 350 | + const body = encodeURIComponent(reportBody.value); |
| 351 | + return `${base}?title=${title}&body=${body}`; |
| 352 | + }); |
| 353 | +
|
| 354 | + const mailtoLink = computed(() => { |
| 355 | + const subject = encodeURIComponent('Classworks 问题报告'); |
| 356 | + const body = encodeURIComponent(reportBody.value); |
| 357 | + return `mailto:sun@wuyuan.dev?subject=${subject}&body=${body}`; |
| 358 | + }); |
| 359 | +
|
227 | 360 | onMounted(() => { |
228 | 361 | loadDependencies(); |
229 | 362 | }); |
230 | 363 |
|
231 | 364 | return { |
232 | 365 | Dependencies, |
233 | 366 | showDeps, |
| 367 | + showReportDialog, |
| 368 | + debugIdInput, |
| 369 | + visitorLoading, |
| 370 | + copyOk, |
| 371 | + qqGroupNumber, |
| 372 | + qqGroupLink, |
| 373 | + goToDebug, |
| 374 | + reloadVisitorId, |
| 375 | + openReportDialog, |
| 376 | + copyEnvInfo, |
| 377 | + envBoxText, |
| 378 | + envInfo, |
| 379 | + reportBody, |
| 380 | + githubIssueUrl, |
| 381 | + mailtoLink, |
234 | 382 | }; |
235 | 383 | }, |
236 | 384 | }; |
|
0 commit comments