@@ -128,6 +128,7 @@ async function refreshMe() {
128128 me .value = j .user
129129 fillForm (j .user )
130130 loadMyApps ().catch (() => {})
131+ loadMyEntry ().catch (() => {})
131132 }
132133 } catch {
133134 /* 忽略 */
@@ -266,6 +267,92 @@ function loadImage(file: File): Promise<HTMLImageElement> {
266267 })
267268}
268269
270+ // ---------- MC 正版 ID 自助管理 ----------
271+ // 条目数据:mc_id 为名称缓存,mc_uuid 为解析所得 UUID(改名不影响白名单准入)
272+ interface MyEntry {
273+ id: string
274+ mc_id: string
275+ mc_uuid: string
276+ }
277+ const myEntry = ref <MyEntry | null >(null )
278+ const mcBusy = ref (false )
279+ const mcMsg = ref (' ' )
280+ const mcErr = ref (' ' )
281+ const mcInput = ref (' ' )
282+
283+ async function loadMyEntry() {
284+ if (! token .value || ! me .value ) return
285+ try {
286+ const r = await fetch (apiURL (` /contributors?user_id=${me .value .id } ` ), {
287+ headers: {Authorization: ` Bearer ${token .value } ` },
288+ })
289+ if (r .ok ) {
290+ const j = await r .json ()
291+ const list: MyEntry [] = j .entries ?? []
292+ myEntry .value = list .length ? list [list .length - 1 ] : null
293+ mcInput .value = myEntry .value ?.mc_id ?? ' '
294+ }
295+ } catch { /* 忽略 */ }
296+ }
297+
298+ // 从 UUID 重新获取当前玩家名(改名后刷新缓存)
299+ async function refreshMCID() {
300+ if (! token .value || mcBusy .value ) return
301+ mcBusy .value = true
302+ mcMsg .value = ' '
303+ mcErr .value = ' '
304+ try {
305+ const r = await fetch (apiURL (' /users/me/mc-id/refresh' ), {
306+ method: ' POST' , headers: {Authorization: ` Bearer ${token .value } ` },
307+ })
308+ const j = await r .json ()
309+ if (! r .ok ) {
310+ mcErr .value = j .error ?? ' 刷新失败'
311+ return
312+ }
313+ myEntry .value = j .entry
314+ mcInput .value = j .entry .mc_id
315+ mcMsg .value = ` 已刷新为 ${j .entry .mc_id } `
316+ } catch {
317+ mcErr .value = ' 刷新失败:网络错误,请重试'
318+ } finally {
319+ mcBusy .value = false
320+ }
321+ }
322+
323+ // 重新设置 MC ID(服务端重新解析 UUID 并校验正版账号)
324+ async function resetMCID() {
325+ if (! token .value || mcBusy .value ) return
326+ const id = mcInput .value .trim ()
327+ if (! / ^ [0-9A-Za-z _] {3,16} $ / .test (id )) {
328+ mcErr .value = ' MC ID 须为 3-16 位,只能包含数字、大小写字母和下划线'
329+ mcMsg .value = ' '
330+ return
331+ }
332+ mcBusy .value = true
333+ mcMsg .value = ' '
334+ mcErr .value = ' '
335+ try {
336+ const r = await fetch (apiURL (' /users/me/mc-id' ), {
337+ method: ' PUT' ,
338+ headers: {' Content-Type' : ' application/json' , Authorization: ` Bearer ${token .value } ` },
339+ body: JSON .stringify ({mc_id: id }),
340+ })
341+ const j = await r .json ()
342+ if (! r .ok ) {
343+ mcErr .value = j .error ?? ' 设置失败'
344+ return
345+ }
346+ myEntry .value = j .entry
347+ mcInput .value = j .entry .mc_id
348+ mcMsg .value = ` 已设置为 ${j .entry .mc_id } `
349+ } catch {
350+ mcErr .value = ' 设置失败:网络错误,请重试'
351+ } finally {
352+ mcBusy .value = false
353+ }
354+ }
355+
269356// ---------- 邀请码注册为管理员 ----------
270357const inviteCode = ref (' ' )
271358const regBusy = ref (false )
@@ -416,6 +503,28 @@ onMounted(async () => {
416503 <button class =" btn primary" :disabled =" saving" @click =" saveProfile" >{{ saving ? '保存中…' : '保存资料' }}</button >
417504 <p v-if =" msg" class =" ok" >{{ msg }}</p >
418505
506+ <h4 >Minecraft 正版 ID</h4 >
507+ <template v-if =" myEntry " >
508+ <div class =" field" >
509+ <span >当前 ID(白名单以 UUID 为准,改名不影响准入)</span >
510+ <div class =" mc-row" >
511+ <button
512+ class =" btn icon-btn"
513+ :disabled =" mcBusy || !myEntry.mc_uuid"
514+ :title =" myEntry.mc_uuid ? '从 Mojang 重新获取当前玩家名' : '尚未解析 UUID,请使用重新设置'"
515+ @click =" refreshMCID"
516+ >↻</button >
517+ <code class =" mc-id" >{{ myEntry.mc_id || '未设置' }}</code >
518+ </div >
519+ <span v-if =" myEntry.mc_uuid" class =" hint small-tip" >UUID:{{ myEntry.mc_uuid }}</span >
520+ </div >
521+ <label class =" field" >重新设置<input v-model =" mcInput" maxlength =" 16" placeholder =" 3-16 位数字、大小写字母或下划线,设置时重新解析 UUID" /></label >
522+ <button class =" btn primary" :disabled =" mcBusy" @click =" resetMCID" >{{ mcBusy ? '处理中…' : '保存 MC ID' }}</button >
523+ <p v-if =" mcMsg" class =" ok" >{{ mcMsg }}</p >
524+ <p v-if =" mcErr" class =" error" >{{ mcErr }}</p >
525+ </template >
526+ <p v-else class =" hint" >暂无绑定的贡献者条目(申请审核通过后自动绑定,届时可在此管理 MC ID)。</p >
527+
419528 <h4 >邮箱 TOTP 绑定(管理员注册需要)</h4 >
420529 <div v-if =" totpStep === 'done'" >
421530 <p class =" hint" >已绑定邮箱:{{ totpEmail || me.email || '—' }}</p >
@@ -586,6 +695,25 @@ onMounted(async () => {
586695 font-size : 12px ;
587696 margin-top : 4px ;
588697}
698+ .mc-row {
699+ display : flex ;
700+ align-items : center ;
701+ gap : 8px ;
702+ margin-top : 4px ;
703+ }
704+ .mc-id {
705+ font-size : 15px ;
706+ font-weight : 600 ;
707+ }
708+ .btn.icon-btn {
709+ padding : 4px 10px ;
710+ font-size : 15px ;
711+ line-height : 1 ;
712+ }
713+ .btn.icon-btn :disabled {
714+ opacity : 0.5 ;
715+ cursor : not-allowed ;
716+ }
589717.my-apps {
590718 list-style : none ;
591719 margin : 8px 0 ;
0 commit comments