|
|
|
@ -770,12 +770,89 @@ function onGrantUserCleared() { |
|
|
|
grantSelectedUserId.value = '' |
|
|
|
} |
|
|
|
|
|
|
|
async function searchGrantUsers(_keyword) { |
|
|
|
// Task 3: remote user search |
|
|
|
let grantSearchSeq = 0 |
|
|
|
async function searchGrantUsers(keyword) { |
|
|
|
const q = String(keyword || '').trim() |
|
|
|
if (!q) { |
|
|
|
grantUserOptions.value = [] |
|
|
|
return |
|
|
|
} |
|
|
|
const seq = ++grantSearchSeq |
|
|
|
grantUserSearching.value = true |
|
|
|
try { |
|
|
|
const page = await request.get(urls.USERS, { |
|
|
|
params: { keyword: q, role: 'user', pageNum: 1, pageSize: 20 } |
|
|
|
}) |
|
|
|
if (seq !== grantSearchSeq) return |
|
|
|
grantUserOptions.value = (page?.records || []).map((u) => ({ |
|
|
|
label: `${u.name || '--'} · ${u.phone || '--'} · ${u.id}`, |
|
|
|
value: String(u.id) |
|
|
|
})) |
|
|
|
} catch (e) { |
|
|
|
if (seq !== grantSearchSeq) return |
|
|
|
grantUserOptions.value = [] |
|
|
|
// http 拦截器已 toast;此处不重复 |
|
|
|
} finally { |
|
|
|
if (seq === grantSearchSeq) grantUserSearching.value = false |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
function parseGrantBalanceText(data) { |
|
|
|
if (data == null) return '已发放(未返回余额字段)' |
|
|
|
if (typeof data === 'number' || (typeof data === 'string' && data.trim() !== '' && !Number.isNaN(Number(data)))) { |
|
|
|
const n = Number(data) |
|
|
|
if (!Number.isFinite(n)) return '已发放(未返回余额字段)' |
|
|
|
// 很大则按 bytes,否则按 GB |
|
|
|
return n >= GB_BYTES ? bytesToGbText(n) : `${n.toFixed(2)} GB` |
|
|
|
} |
|
|
|
if (typeof data !== 'object') return '已发放(未返回余额字段)' |
|
|
|
if (data.trafficBalanceBytes != null) return bytesToGbText(data.trafficBalanceBytes) |
|
|
|
if (data.balanceBytes != null) return bytesToGbText(data.balanceBytes) |
|
|
|
if (data.trafficBalance != null) { |
|
|
|
const n = Number(data.trafficBalance) |
|
|
|
if (!Number.isFinite(n)) return '已发放(未返回余额字段)' |
|
|
|
return n >= GB_BYTES ? bytesToGbText(n) : `${n.toFixed(2)} GB` |
|
|
|
} |
|
|
|
if (data.balance != null) { |
|
|
|
const n = Number(data.balance) |
|
|
|
if (!Number.isFinite(n)) return '已发放(未返回余额字段)' |
|
|
|
return n >= GB_BYTES ? bytesToGbText(n) : `${n.toFixed(2)} GB` |
|
|
|
} |
|
|
|
return '已发放(未返回余额字段)' |
|
|
|
} |
|
|
|
|
|
|
|
async function submitGrant() { |
|
|
|
// Task 3: validate + POST test-grants |
|
|
|
grantFormError.value = '' |
|
|
|
const userIdNum = Number(String(grantForm.userId || '').trim()) |
|
|
|
const amountGb = Number(grantForm.amountGb) |
|
|
|
const reason = String(grantForm.reason || '').trim() |
|
|
|
if (!Number.isInteger(userIdNum) || userIdNum <= 0) { |
|
|
|
grantFormError.value = '请填写有效的用户 ID' |
|
|
|
return |
|
|
|
} |
|
|
|
if (!Number.isFinite(amountGb) || amountGb < 1 || !Number.isInteger(amountGb)) { |
|
|
|
grantFormError.value = '额度须为不小于 1 的整数 GB' |
|
|
|
return |
|
|
|
} |
|
|
|
if (!reason) { |
|
|
|
grantFormError.value = '请填写原因' |
|
|
|
return |
|
|
|
} |
|
|
|
grantSubmitting.value = true |
|
|
|
try { |
|
|
|
const data = await request.post(urls.ADMIN_TRAFFIC_TEST_GRANTS, { |
|
|
|
userId: userIdNum, |
|
|
|
amountGb, |
|
|
|
reason |
|
|
|
}) |
|
|
|
grantResultBalanceText.value = parseGrantBalanceText(data) |
|
|
|
ui.toast('测试流量已发放') |
|
|
|
} catch (e) { |
|
|
|
// 拦截器已 toast;保留对话框便于改参重试 |
|
|
|
if (!e?.message) ui.toast('发放失败') |
|
|
|
} finally { |
|
|
|
grantSubmitting.value = false |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|