@@ -1,186 +0,0 @@
<!DOCTYPE html>
< html lang = "zh-CN" >
< head >
< meta charset = "UTF-8" >
< title > 注册码申请管理< / title >
< style >
body { margin : 0 ; font-family : system-ui , - apple-system , Segoe UI , Roboto , sans-serif ; background : #f5f6fa ; }
. sidebar { position : fixed ; top : 0 ; left : 0 ; width : 180 px ; height : 100 vh ; background : #1e1e2e ; color : white ; padding : 20 px ; box-shadow : 2 px 0 5 px rgba ( 0 , 0 , 0 , 0.1 ) ; z-index : 1000 ; display : flex ; flex-direction : column ; align-items : center ; }
. sidebar h3 { margin-top : 0 ; font-size : 18 px ; color : #add8e6 ; text-align : center ; margin-bottom : 20 px ; }
. navigation-links { width : 100 % ; margin-top : 60 px ; }
. sidebar a { display : block ; color : #8be9fd ; text-decoration : none ; margin : 10 px 0 ; font-size : 16 px ; padding : 15 px ; border-radius : 4 px ; transition : all 0.2 s ease ; }
. sidebar a : hover { color : #ff79c6 ; background-color : rgba ( 139 , 233 , 253 , 0.2 ) ; }
. main-content { margin-left : 220 px ; padding : 40 px ; }
. card { background : #fff ; border-radius : 14 px ; box-shadow : 0 10 px 24 px rgba ( 31 , 35 , 40 , 0.08 ) ; padding : 24 px ; }
. header { display : flex ; align-items : center ; justify-content : space-between ; margin-bottom : 14 px ; }
. btn { padding : 8 px 12 px ; border : none ; border-radius : 10 px ; cursor : pointer ; }
. btn-primary { background : #4f46e5 ; color : #fff ; }
. btn-secondary { background : #64748b ; color : #fff ; }
. btn-danger { background : #ff4d4f ; color : #fff ; }
. muted { color : #6b7280 ; font-size : 12 px ; }
table { width : 100 % ; border-collapse : collapse ; margin-top : 12 px ; }
th , td { text-align : left ; border-bottom : 1 px solid #e5e7eb ; padding : 10 px 8 px ; vertical-align : top ; font-size : 13 px ; }
tr : hover { background : #f8fafc ; }
. tag { display : inline-block ; padding : 2 px 8 px ; border-radius : 999 px ; font-size : 12 px ; background : #eef2ff ; color : #3730a3 ; }
. tag . pending { background : #fff7ed ; color : #9a3412 ; }
. tag . approved { background : #dcfce7 ; color : #166534 ; }
. tag . rejected { background : #fee2e2 ; color : #991b1b ; }
< / style >
{% csrf_token %}
< / head >
< body >
< div class = "sidebar" >
< h3 > 你好,{{ username|default:"管理员" }}< / h3 >
< div class = "navigation-links" >
< a href = "{% url 'main:home' %}" > 返回主页< / a >
< a id = "logoutBtn" style = "cursor:pointer;" > 退出登录< / a >
< div id = "logoutMsg" class = "muted" style = "margin-top:6px;" > < / div >
{% csrf_token %}
< / div >
< / div >
< div class = "main-content" >
< div class = "card" >
< div class = "header" >
< h2 style = "margin:0;" > 注册码申请管理< / h2 >
< div style = "display:flex; gap:10px; align-items:center;" >
< select id = "statusFilter" style = "padding:8px 10px; border:1px solid #d1d5db; border-radius:10px;" >
< option value = "pending" > 待审核< / option >
< option value = "" > 全部< / option >
< option value = "approved" > 已同意< / option >
< option value = "rejected" > 已拒绝< / option >
< / select >
< button id = "refreshBtn" class = "btn btn-secondary" type = "button" > 刷新< / button >
< / div >
< / div >
< div class = "muted" > 同意后,用户会获得“注册码管理”入口,且仅能使用自己新增的 key。< / div >
< table >
< thead >
< tr >
< th style = "width:120px;" > 用户< / th >
< th > 申请理由< / th >
< th style = "width:170px;" > 时间< / th >
< th style = "width:110px;" > 状态< / th >
< th style = "width:220px;" > 操作< / th >
< / tr >
< / thead >
< tbody id = "reqBody" > < / tbody >
< / table >
< div id = "pageMsg" class = "muted" style = "margin-top:12px;" > < / div >
< / div >
< / div >
< script >
function getCookie ( name ) { const v = ` ; ${ document . cookie } ` ; const p = v . split ( ` ; ${ name } = ` ) ; if ( p . length === 2 ) return p . pop ( ) . split ( ';' ) . shift ( ) ; }
document . getElementById ( 'logoutBtn' ) . addEventListener ( 'click' , async ( ) => {
const msg = document . getElementById ( 'logoutMsg' ) ;
msg . textContent = '' ;
const csrftoken = getCookie ( 'csrftoken' ) ;
try {
const resp = await fetch ( '/accounts/logout/' , {
method : 'POST' ,
credentials : 'same-origin' ,
headers : { 'Content-Type' : 'application/json' , 'X-CSRFToken' : csrftoken || '' } ,
body : JSON . stringify ( { } )
} ) ;
const data = await resp . json ( ) ;
if ( data . ok ) window . location . href = data . redirect _url ;
} catch ( e ) { msg . textContent = '登出失败' ; }
} ) ;
function fmtTime ( t ) {
try {
const d = new Date ( t ) ;
if ( String ( d ) !== 'Invalid Date' ) {
const pad = n => String ( n ) . padStart ( 2 , '0' ) ;
return ` ${ d . getFullYear ( ) } - ${ pad ( d . getMonth ( ) + 1 ) } - ${ pad ( d . getDate ( ) ) } ${ pad ( d . getHours ( ) ) } : ${ pad ( d . getMinutes ( ) ) } ` ;
}
} catch ( e ) { }
return t || '' ;
}
function renderStatus ( s ) {
const v = String ( s || 'pending' ) ;
const cls = ( v === 'approved' || v === 'rejected' ) ? v : 'pending' ;
const text = v === 'approved' ? '已同意' : ( v === 'rejected' ? '已拒绝' : '待审核' ) ;
return ` <span class="tag ${ cls } "> ${ text } </span> ` ;
}
async function loadRequests ( ) {
const status = document . getElementById ( 'statusFilter' ) . value ;
const msg = document . getElementById ( 'pageMsg' ) ;
msg . textContent = '加载中...' ;
const url = status ? ` /accounts/registration-code/requests/list/?status= ${ encodeURIComponent ( status ) } ` : '/accounts/registration-code/requests/list/' ;
try {
const resp = await fetch ( url , { credentials : 'same-origin' } ) ;
const data = await resp . json ( ) ;
if ( ! ( resp . ok && data && data . ok ) ) {
msg . textContent = ( data && data . message ) ? data . message : '加载失败' ;
return ;
}
const body = document . getElementById ( 'reqBody' ) ;
body . innerHTML = '' ;
const rows = data . data || [ ] ;
if ( ! rows . length ) {
msg . textContent = '暂无数据' ;
return ;
}
msg . textContent = '' ;
rows . forEach ( r => {
const tr = document . createElement ( 'tr' ) ;
const uname = ( r . username || '' ) + ( r . user _id !== undefined ? ` ( ${ r . user _id } ) ` : '' ) ;
const reason = String ( r . reason || '' ) . replace ( /</g , '<' ) . replace ( />/g , '>' ) ;
const created = fmtTime ( r . created _at ) ;
const statusHtml = renderStatus ( r . status ) ;
const id = r . request _id || r . _id || '' ;
const ops = ( String ( r . status || 'pending' ) === 'pending' )
? ` <button class="btn btn-primary" data-act="approve" data-id=" ${ id } ">同意</button>
<button class="btn btn-danger" data-act="reject" data-id=" ${ id } ">拒绝</button> `
: ` <button class="btn btn-secondary" data-act="view" data-id=" ${ id } ">查看</button> ` ;
tr . innerHTML = ` <td> ${ uname } </td><td style="white-space:pre-wrap;"> ${ reason } </td><td> ${ created } </td><td> ${ statusHtml } </td><td> ${ ops } </td> ` ;
body . appendChild ( tr ) ;
} ) ;
} catch ( e ) {
msg . textContent = '加载失败' ;
}
}
async function decide ( id , action ) {
const csrftoken = getCookie ( 'csrftoken' ) ;
const note = '' ;
const resp = await fetch ( '/accounts/registration-code/requests/decide/' , {
method : 'POST' ,
credentials : 'same-origin' ,
headers : { 'Content-Type' : 'application/json' , 'X-CSRFToken' : csrftoken || '' } ,
body : JSON . stringify ( { request _id : id , action , note } )
} ) ;
const data = await resp . json ( ) ;
if ( ! ( resp . ok && data && data . ok ) ) {
alert ( ( data && data . message ) ? data . message : '操作失败' ) ;
return ;
}
loadRequests ( ) ;
}
document . getElementById ( 'refreshBtn' ) . addEventListener ( 'click' , loadRequests ) ;
document . getElementById ( 'statusFilter' ) . addEventListener ( 'change' , loadRequests ) ;
document . addEventListener ( 'click' , ( e ) => {
const t = e . target ;
if ( ! ( t && t . dataset && t . dataset . id && t . dataset . act ) ) return ;
const id = t . dataset . id ;
const act = t . dataset . act ;
if ( act === 'approve' ) {
if ( confirm ( '确定同意该申请吗?' ) ) decide ( id , 'approve' ) ;
} else if ( act === 'reject' ) {
if ( confirm ( '确定拒绝该申请吗?' ) ) decide ( id , 'reject' ) ;
} else if ( act === 'view' ) {
return ;
}
} ) ;
loadRequests ( ) ;
< / script >
< / body >
< / html >