Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in / Register
Toggle navigation
Menu
Open sidebar
陈曦
sub2api
Commits
3f05ef2a
Unverified
Commit
3f05ef2a
authored
Apr 26, 2026
by
Oliver Li
Committed by
GitHub
Apr 26, 2026
Browse files
Merge branch 'Wei-Shaw:main' into vertex
parents
6d11f9ed
c056db74
Changes
47
Show whitespace changes
Inline
Side-by-side
frontend/src/views/auth/RegisterView.vue
View file @
3f05ef2a
...
@@ -15,17 +15,20 @@
...
@@ -15,17 +15,20 @@
<
LinuxDoOAuthSection
<
LinuxDoOAuthSection
v
-
if
=
"
linuxdoOAuthEnabled
"
v
-
if
=
"
linuxdoOAuthEnabled
"
:
disabled
=
"
isLoading
"
:
disabled
=
"
isLoading
"
:
aff
-
code
=
"
formData.aff_code
"
:
show
-
divider
=
"
false
"
:
show
-
divider
=
"
false
"
/>
/>
<
WechatOAuthSection
<
WechatOAuthSection
v
-
if
=
"
wechatOAuthEnabled
"
v
-
if
=
"
wechatOAuthEnabled
"
:
disabled
=
"
isLoading
"
:
disabled
=
"
isLoading
"
:
aff
-
code
=
"
formData.aff_code
"
:
show
-
divider
=
"
false
"
:
show
-
divider
=
"
false
"
/>
/>
<
OidcOAuthSection
<
OidcOAuthSection
v
-
if
=
"
oidcOAuthEnabled
"
v
-
if
=
"
oidcOAuthEnabled
"
:
disabled
=
"
isLoading
"
:
disabled
=
"
isLoading
"
:
provider
-
name
=
"
oidcOAuthProviderName
"
:
provider
-
name
=
"
oidcOAuthProviderName
"
:
aff
-
code
=
"
formData.aff_code
"
:
show
-
divider
=
"
false
"
:
show
-
divider
=
"
false
"
/>
/>
<
div
class
=
"
flex items-center gap-3
"
>
<
div
class
=
"
flex items-center gap-3
"
>
...
@@ -293,6 +296,11 @@ import {
...
@@ -293,6 +296,11 @@ import {
isRegistrationEmailSuffixAllowed
,
isRegistrationEmailSuffixAllowed
,
normalizeRegistrationEmailSuffixWhitelist
normalizeRegistrationEmailSuffixWhitelist
}
from
'
@/utils/registrationEmailPolicy
'
}
from
'
@/utils/registrationEmailPolicy
'
import
{
clearAffiliateReferralCode
,
loadAffiliateReferralCode
,
resolveAffiliateReferralCode
}
from
'
@/utils/oauthAffiliate
'
const
{
t
,
locale
}
=
useI18n
()
const
{
t
,
locale
}
=
useI18n
()
...
@@ -378,9 +386,19 @@ watch(validationToastMessage, (value, previousValue) => {
...
@@ -378,9 +386,19 @@ watch(validationToastMessage, (value, previousValue) => {
}
}
}
)
}
)
function
syncAffiliateReferralCode
():
string
{
const
code
=
resolveAffiliateReferralCode
(
route
.
query
.
aff
,
route
.
query
.
aff_code
)
if
(
code
)
{
formData
.
aff_code
=
code
}
return
code
}
// ==================== Lifecycle ====================
// ==================== Lifecycle ====================
onMounted
(
async
()
=>
{
onMounted
(
async
()
=>
{
syncAffiliateReferralCode
()
try
{
try
{
const
settings
=
await
getPublicSettings
()
const
settings
=
await
getPublicSettings
()
registrationEnabled
.
value
=
settings
.
registration_enabled
registrationEnabled
.
value
=
settings
.
registration_enabled
...
@@ -407,10 +425,7 @@ onMounted(async () => {
...
@@ -407,10 +425,7 @@ onMounted(async () => {
await
validatePromoCodeDebounced
(
promoParam
)
await
validatePromoCodeDebounced
(
promoParam
)
}
}
}
}
const
affParam
=
(
route
.
query
.
aff
as
string
)
||
(
route
.
query
.
aff_code
as
string
)
syncAffiliateReferralCode
()
if
(
affParam
)
{
formData
.
aff_code
=
affParam
.
trim
()
}
}
catch
(
error
)
{
}
catch
(
error
)
{
console
.
error
(
'
Failed to load public settings:
'
,
error
)
console
.
error
(
'
Failed to load public settings:
'
,
error
)
}
finally
{
}
finally
{
...
@@ -418,6 +433,13 @@ onMounted(async () => {
...
@@ -418,6 +433,13 @@ onMounted(async () => {
}
}
}
)
}
)
watch
(
()
=>
[
route
.
query
.
aff
,
route
.
query
.
aff_code
],
()
=>
{
syncAffiliateReferralCode
()
}
)
onUnmounted
(()
=>
{
onUnmounted
(()
=>
{
if
(
promoValidateTimeout
)
{
if
(
promoValidateTimeout
)
{
clearTimeout
(
promoValidateTimeout
)
clearTimeout
(
promoValidateTimeout
)
...
@@ -702,6 +724,11 @@ async function handleRegister(): Promise<void> {
...
@@ -702,6 +724,11 @@ async function handleRegister(): Promise<void> {
isLoading
.
value
=
true
isLoading
.
value
=
true
try
{
try
{
const
affCode
=
formData
.
aff_code
.
trim
()
||
loadAffiliateReferralCode
()
if
(
affCode
)
{
formData
.
aff_code
=
affCode
}
// If email verification is enabled, redirect to verification page
// If email verification is enabled, redirect to verification page
if
(
emailVerifyEnabled
.
value
)
{
if
(
emailVerifyEnabled
.
value
)
{
// Store registration data in sessionStorage
// Store registration data in sessionStorage
...
@@ -713,7 +740,7 @@ async function handleRegister(): Promise<void> {
...
@@ -713,7 +740,7 @@ async function handleRegister(): Promise<void> {
turnstile_token
:
turnstileToken
.
value
,
turnstile_token
:
turnstileToken
.
value
,
promo_code
:
formData
.
promo_code
||
undefined
,
promo_code
:
formData
.
promo_code
||
undefined
,
invitation_code
:
formData
.
invitation_code
||
undefined
,
invitation_code
:
formData
.
invitation_code
||
undefined
,
...(
formData
.
aff
_c
ode
?
{
aff_code
:
formData
.
aff
_c
ode
}
:
{
}
)
...(
aff
C
ode
?
{
aff_code
:
aff
C
ode
}
:
{
}
)
}
)
}
)
)
)
...
@@ -729,8 +756,9 @@ async function handleRegister(): Promise<void> {
...
@@ -729,8 +756,9 @@ async function handleRegister(): Promise<void> {
turnstile_token
:
turnstileEnabled
.
value
?
turnstileToken
.
value
:
undefined
,
turnstile_token
:
turnstileEnabled
.
value
?
turnstileToken
.
value
:
undefined
,
promo_code
:
formData
.
promo_code
||
undefined
,
promo_code
:
formData
.
promo_code
||
undefined
,
invitation_code
:
formData
.
invitation_code
||
undefined
,
invitation_code
:
formData
.
invitation_code
||
undefined
,
...(
formData
.
aff
_c
ode
?
{
aff_code
:
formData
.
aff
_c
ode
}
:
{
}
)
...(
aff
C
ode
?
{
aff_code
:
aff
C
ode
}
:
{
}
)
}
)
}
)
clearAffiliateReferralCode
()
// Show success toast
// Show success toast
appStore
.
showSuccess
(
t
(
'
auth.accountCreatedSuccess
'
,
{
siteName
:
siteName
.
value
}
))
appStore
.
showSuccess
(
t
(
'
auth.accountCreatedSuccess
'
,
{
siteName
:
siteName
.
value
}
))
...
...
frontend/src/views/auth/WechatCallbackView.vue
View file @
3f05ef2a
...
@@ -340,6 +340,11 @@ import {
...
@@ -340,6 +340,11 @@ import {
type
OAuthTokenResponse
,
type
OAuthTokenResponse
,
type
PendingOAuthExchangeResponse
type
PendingOAuthExchangeResponse
}
from
'
@/api/auth
'
}
from
'
@/api/auth
'
import
{
clearAllAffiliateReferralCodes
,
loadOAuthAffiliateCode
,
oauthAffiliatePayload
}
from
'
@/utils/oauthAffiliate
'
const
route
=
useRoute
()
const
route
=
useRoute
()
const
router
=
useRouter
()
const
router
=
useRouter
()
...
@@ -802,6 +807,7 @@ async function finalizeCompletion(completion: PendingOAuthExchangeResponse, redi
...
@@ -802,6 +807,7 @@ async function finalizeCompletion(completion: PendingOAuthExchangeResponse, redi
if
(
getOAuthCompletionKind
(
completion
)
===
'
bind
'
)
{
if
(
getOAuthCompletionKind
(
completion
)
===
'
bind
'
)
{
const
bindRedirect
=
sanitizeRedirectPath
(
completion
.
redirect
||
'
/profile
'
)
const
bindRedirect
=
sanitizeRedirectPath
(
completion
.
redirect
||
'
/profile
'
)
clearPendingAuthSession
()
clearPendingAuthSession
()
clearAllAffiliateReferralCodes
()
appStore
.
showSuccess
(
bindSuccessMessage
)
appStore
.
showSuccess
(
bindSuccessMessage
)
await
router
.
replace
(
bindRedirect
)
await
router
.
replace
(
bindRedirect
)
return
return
...
@@ -813,6 +819,7 @@ async function finalizeCompletion(completion: PendingOAuthExchangeResponse, redi
...
@@ -813,6 +819,7 @@ async function finalizeCompletion(completion: PendingOAuthExchangeResponse, redi
persistOAuthTokenContext
(
completion
)
persistOAuthTokenContext
(
completion
)
await
authStore
.
setToken
(
completion
.
access_token
)
await
authStore
.
setToken
(
completion
.
access_token
)
clearAllAffiliateReferralCodes
()
appStore
.
showSuccess
(
t
(
'
auth.loginSuccess
'
))
appStore
.
showSuccess
(
t
(
'
auth.loginSuccess
'
))
await
router
.
replace
(
redirect
)
await
router
.
replace
(
redirect
)
}
}
...
@@ -861,18 +868,20 @@ async function handleSubmitInvitation() {
...
@@ -861,18 +868,20 @@ async function handleSubmitInvitation() {
isSubmitting
.
value
=
true
isSubmitting
.
value
=
true
try
{
try
{
const
affCode
=
loadOAuthAffiliateCode
()
const
decision
=
currentAdoptionDecision
()
const
completion
:
PendingWeChatCompletion
=
legacyPendingOAuthToken
.
value
const
completion
:
PendingWeChatCompletion
=
legacyPendingOAuthToken
.
value
?
(
?
(
await
apiClient
.
post
<
PendingWeChatCompletion
>
(
'
/auth/oauth/wechat/complete-registration
'
,
{
await
apiClient
.
post
<
PendingWeChatCompletion
>
(
'
/auth/oauth/wechat/complete-registration
'
,
{
pending_oauth_token
:
legacyPendingOAuthToken
.
value
,
pending_oauth_token
:
legacyPendingOAuthToken
.
value
,
invitation_code
:
invitationCode
.
value
.
trim
(),
invitation_code
:
invitationCode
.
value
.
trim
(),
...
serializeAdoptionDecision
(
currentAdoptionDecision
())
...
oauthAffiliatePayload
(
affCode
),
...
serializeAdoptionDecision
(
decision
)
}
)
}
)
).
data
).
data
:
await
completeWeChatOAuthRegistration
(
:
affCode
invitationCode
.
value
.
trim
(),
?
await
completeWeChatOAuthRegistration
(
invitationCode
.
value
.
trim
(),
decision
,
affCode
)
currentAdoptionDecision
()
:
await
completeWeChatOAuthRegistration
(
invitationCode
.
value
.
trim
(),
decision
)
)
await
finalizePendingAccountResponse
(
completion
)
await
finalizePendingAccountResponse
(
completion
)
}
catch
(
e
:
unknown
)
{
}
catch
(
e
:
unknown
)
{
const
err
=
e
as
{
message
?:
string
;
response
?:
{
data
?:
{
message
?:
string
}
}
}
const
err
=
e
as
{
message
?:
string
;
response
?:
{
data
?:
{
message
?:
string
}
}
}
...
@@ -907,6 +916,7 @@ async function handleCreateAccount(payload: PendingOAuthCreateAccountPayload) {
...
@@ -907,6 +916,7 @@ async function handleCreateAccount(payload: PendingOAuthCreateAccountPayload) {
password
:
payload
.
password
,
password
:
payload
.
password
,
verify_code
:
payload
.
verifyCode
||
undefined
,
verify_code
:
payload
.
verifyCode
||
undefined
,
invitation_code
:
payload
.
invitationCode
||
undefined
,
invitation_code
:
payload
.
invitationCode
||
undefined
,
...
oauthAffiliatePayload
(
loadOAuthAffiliateCode
()),
...
serializeAdoptionDecision
(
currentAdoptionDecision
())
...
serializeAdoptionDecision
(
currentAdoptionDecision
())
}
)
}
)
await
finalizePendingAccountResponse
(
data
)
await
finalizePendingAccountResponse
(
data
)
...
@@ -955,6 +965,7 @@ async function handleSubmitTotpChallenge() {
...
@@ -955,6 +965,7 @@ async function handleSubmitTotpChallenge() {
}
)
}
)
persistOAuthTokenContext
(
completion
)
persistOAuthTokenContext
(
completion
)
await
authStore
.
setToken
(
completion
.
access_token
)
await
authStore
.
setToken
(
completion
.
access_token
)
clearAllAffiliateReferralCodes
()
appStore
.
showSuccess
(
t
(
'
auth.loginSuccess
'
))
appStore
.
showSuccess
(
t
(
'
auth.loginSuccess
'
))
await
router
.
replace
(
redirectTo
.
value
)
await
router
.
replace
(
redirectTo
.
value
)
}
catch
(
e
:
unknown
)
{
}
catch
(
e
:
unknown
)
{
...
@@ -1015,6 +1026,7 @@ onMounted(async () => {
...
@@ -1015,6 +1026,7 @@ onMounted(async () => {
if
(
legacyLogin
)
{
if
(
legacyLogin
)
{
persistOAuthTokenContext
(
legacyLogin
)
persistOAuthTokenContext
(
legacyLogin
)
await
authStore
.
setToken
(
legacyLogin
.
access_token
)
await
authStore
.
setToken
(
legacyLogin
.
access_token
)
clearAllAffiliateReferralCodes
()
appStore
.
showSuccess
(
t
(
'
auth.loginSuccess
'
))
appStore
.
showSuccess
(
t
(
'
auth.loginSuccess
'
))
await
router
.
replace
(
redirect
)
await
router
.
replace
(
redirect
)
return
return
...
...
frontend/src/views/auth/__tests__/EmailVerifyView.spec.ts
View file @
3f05ef2a
...
@@ -112,6 +112,7 @@ describe('EmailVerifyView', () => {
...
@@ -112,6 +112,7 @@ describe('EmailVerifyView', () => {
apiClientPostMock
.
mockReset
()
apiClientPostMock
.
mockReset
()
authStoreState
.
pendingAuthSession
=
null
authStoreState
.
pendingAuthSession
=
null
sessionStorage
.
clear
()
sessionStorage
.
clear
()
localStorage
.
clear
()
getPublicSettingsMock
.
mockResolvedValue
({
getPublicSettingsMock
.
mockResolvedValue
({
turnstile_enabled
:
false
,
turnstile_enabled
:
false
,
...
@@ -136,6 +137,7 @@ describe('EmailVerifyView', () => {
...
@@ -136,6 +137,7 @@ describe('EmailVerifyView', () => {
JSON
.
stringify
({
JSON
.
stringify
({
email
:
'
fresh@example.com
'
,
email
:
'
fresh@example.com
'
,
password
:
'
secret-123
'
,
password
:
'
secret-123
'
,
aff_code
:
'
AFF123
'
,
})
})
)
)
...
@@ -334,6 +336,7 @@ describe('EmailVerifyView', () => {
...
@@ -334,6 +336,7 @@ describe('EmailVerifyView', () => {
email
:
'
fresh@example.com
'
,
email
:
'
fresh@example.com
'
,
password
:
'
secret-123
'
,
password
:
'
secret-123
'
,
verify_code
:
'
123456
'
,
verify_code
:
'
123456
'
,
aff_code
:
'
AFF123
'
,
})
})
expect
(
persistOAuthTokenContextMock
).
toHaveBeenCalledWith
({
expect
(
persistOAuthTokenContextMock
).
toHaveBeenCalledWith
({
access_token
:
'
oauth-access-token
'
,
access_token
:
'
oauth-access-token
'
,
...
...
frontend/src/views/auth/__tests__/LinuxDoCallbackView.spec.ts
View file @
3f05ef2a
...
@@ -93,6 +93,7 @@ describe('LinuxDoCallbackView', () => {
...
@@ -93,6 +93,7 @@ describe('LinuxDoCallbackView', () => {
})
})
window
.
location
.
hash
=
''
window
.
location
.
hash
=
''
localStorage
.
clear
()
localStorage
.
clear
()
sessionStorage
.
clear
()
})
})
it
(
'
accepts the legacy fragment token success callback without pending-session exchange
'
,
async
()
=>
{
it
(
'
accepts the legacy fragment token success callback without pending-session exchange
'
,
async
()
=>
{
...
...
frontend/src/views/auth/__tests__/OidcCallbackView.spec.ts
View file @
3f05ef2a
...
@@ -97,6 +97,7 @@ describe('OidcCallbackView', () => {
...
@@ -97,6 +97,7 @@ describe('OidcCallbackView', () => {
})
})
window
.
location
.
hash
=
''
window
.
location
.
hash
=
''
localStorage
.
clear
()
localStorage
.
clear
()
sessionStorage
.
clear
()
})
})
it
(
'
accepts the legacy fragment token success callback without pending-session exchange
'
,
async
()
=>
{
it
(
'
accepts the legacy fragment token success callback without pending-session exchange
'
,
async
()
=>
{
...
...
frontend/src/views/auth/__tests__/WechatCallbackView.spec.ts
View file @
3f05ef2a
...
@@ -172,6 +172,7 @@ describe('WechatCallbackView', () => {
...
@@ -172,6 +172,7 @@ describe('WechatCallbackView', () => {
appStoreState
.
cachedPublicSettings
=
null
appStoreState
.
cachedPublicSettings
=
null
appStoreState
.
publicSettingsLoaded
=
false
appStoreState
.
publicSettingsLoaded
=
false
localStorage
.
clear
()
localStorage
.
clear
()
sessionStorage
.
clear
()
locationState
.
current
=
{
locationState
.
current
=
{
href
:
'
http://localhost/auth/wechat/callback
'
,
href
:
'
http://localhost/auth/wechat/callback
'
,
hash
:
''
,
hash
:
''
,
...
...
frontend/src/views/user/AffiliateView.vue
View file @
3f05ef2a
...
@@ -9,10 +9,7 @@
...
@@ -9,10 +9,7 @@
<template
v-else-if=
"detail"
>
<template
v-else-if=
"detail"
>
<div
class=
"grid gap-4 sm:grid-cols-2 lg:grid-cols-4"
>
<div
class=
"grid gap-4 sm:grid-cols-2 lg:grid-cols-4"
>
<!-- 返利比例:用主色突出,让用户一眼看到「能拿多少」 -->
<div
class=
"card p-5"
>
<div
class=
"card relative overflow-hidden p-5"
>
<div
class=
"absolute -right-6 -top-6 h-24 w-24 rounded-full bg-primary-500/10"
></div>
<div
class=
"relative"
>
<p
class=
"flex items-center gap-1.5 text-sm text-gray-500 dark:text-dark-400"
>
<p
class=
"flex items-center gap-1.5 text-sm text-gray-500 dark:text-dark-400"
>
<Icon
name=
"dollar"
size=
"sm"
class=
"text-primary-500"
/>
<Icon
name=
"dollar"
size=
"sm"
class=
"text-primary-500"
/>
{{
t
(
'
affiliate.stats.rebateRate
'
)
}}
{{
t
(
'
affiliate.stats.rebateRate
'
)
}}
...
@@ -24,7 +21,6 @@
...
@@ -24,7 +21,6 @@
{{
t
(
'
affiliate.stats.rebateRateHint
'
)
}}
{{
t
(
'
affiliate.stats.rebateRateHint
'
)
}}
</p>
</p>
</div>
</div>
</div>
<div
class=
"card p-5"
>
<div
class=
"card p-5"
>
<p
class=
"text-sm text-gray-500 dark:text-dark-400"
>
{{
t
(
'
affiliate.stats.invitedUsers
'
)
}}
</p>
<p
class=
"text-sm text-gray-500 dark:text-dark-400"
>
{{
t
(
'
affiliate.stats.invitedUsers
'
)
}}
</p>
<p
class=
"mt-2 text-2xl font-semibold text-gray-900 dark:text-white"
>
<p
class=
"mt-2 text-2xl font-semibold text-gray-900 dark:text-white"
>
...
@@ -42,6 +38,9 @@
...
@@ -42,6 +38,9 @@
<p
class=
"mt-2 text-2xl font-semibold text-gray-900 dark:text-white"
>
<p
class=
"mt-2 text-2xl font-semibold text-gray-900 dark:text-white"
>
{{
formatCurrency
(
detail
.
aff_history_quota
)
}}
{{
formatCurrency
(
detail
.
aff_history_quota
)
}}
</p>
</p>
<p
v-if=
"detail.aff_frozen_quota > 0"
class=
"mt-1 text-xs text-amber-600 dark:text-amber-400"
>
{{
t
(
'
affiliate.stats.frozenQuota
'
)
}}
:
{{
formatCurrency
(
detail
.
aff_frozen_quota
)
}}
</p>
</div>
</div>
</div>
</div>
...
@@ -79,6 +78,7 @@
...
@@ -79,6 +78,7 @@
<li>
1.
{{
t
(
'
affiliate.tips.line1
'
)
}}
</li>
<li>
1.
{{
t
(
'
affiliate.tips.line1
'
)
}}
</li>
<li>
2.
{{
t
(
'
affiliate.tips.line2
'
,
{
rate
:
`${formattedRebateRate
}
%`
}
)
}}
<
/li
>
<li>
2.
{{
t
(
'
affiliate.tips.line2
'
,
{
rate
:
`${formattedRebateRate
}
%`
}
)
}}
<
/li
>
<
li
>
3
.
{{
t
(
'
affiliate.tips.line3
'
)
}}
<
/li
>
<
li
>
3
.
{{
t
(
'
affiliate.tips.line3
'
)
}}
<
/li
>
<
li
v
-
if
=
"
detail.aff_frozen_quota > 0
"
>
4
.
{{
t
(
'
affiliate.tips.line4
'
)
}}
<
/li
>
<
/ul
>
<
/ul
>
<
/div
>
<
/div
>
<
/div
>
<
/div
>
...
@@ -115,6 +115,7 @@
...
@@ -115,6 +115,7 @@
<
tr
class
=
"
border-b border-gray-200 text-gray-500 dark:border-dark-700 dark:text-dark-400
"
>
<
tr
class
=
"
border-b border-gray-200 text-gray-500 dark:border-dark-700 dark:text-dark-400
"
>
<
th
class
=
"
px-3 py-2 font-medium
"
>
{{
t
(
'
affiliate.invitees.columns.email
'
)
}}
<
/th
>
<
th
class
=
"
px-3 py-2 font-medium
"
>
{{
t
(
'
affiliate.invitees.columns.email
'
)
}}
<
/th
>
<
th
class
=
"
px-3 py-2 font-medium
"
>
{{
t
(
'
affiliate.invitees.columns.username
'
)
}}
<
/th
>
<
th
class
=
"
px-3 py-2 font-medium
"
>
{{
t
(
'
affiliate.invitees.columns.username
'
)
}}
<
/th
>
<
th
class
=
"
px-3 py-2 font-medium text-right
"
>
{{
t
(
'
affiliate.invitees.columns.rebate
'
)
}}
<
/th
>
<
th
class
=
"
px-3 py-2 font-medium
"
>
{{
t
(
'
affiliate.invitees.columns.joinedAt
'
)
}}
<
/th
>
<
th
class
=
"
px-3 py-2 font-medium
"
>
{{
t
(
'
affiliate.invitees.columns.joinedAt
'
)
}}
<
/th
>
<
/tr
>
<
/tr
>
<
/thead
>
<
/thead
>
...
@@ -126,6 +127,7 @@
...
@@ -126,6 +127,7 @@
>
>
<
td
class
=
"
px-3 py-3 text-gray-900 dark:text-white
"
>
{{
item
.
email
||
'
-
'
}}
<
/td
>
<
td
class
=
"
px-3 py-3 text-gray-900 dark:text-white
"
>
{{
item
.
email
||
'
-
'
}}
<
/td
>
<
td
class
=
"
px-3 py-3 text-gray-700 dark:text-gray-300
"
>
{{
item
.
username
||
'
-
'
}}
<
/td
>
<
td
class
=
"
px-3 py-3 text-gray-700 dark:text-gray-300
"
>
{{
item
.
username
||
'
-
'
}}
<
/td
>
<
td
class
=
"
px-3 py-3 text-right font-medium text-emerald-600 dark:text-emerald-400
"
>
{{
formatCurrency
(
item
.
total_rebate
)
}}
<
/td
>
<
td
class
=
"
px-3 py-3 text-gray-700 dark:text-gray-300
"
>
{{
formatDateTime
(
item
.
created_at
)
||
'
-
'
}}
<
/td
>
<
td
class
=
"
px-3 py-3 text-gray-700 dark:text-gray-300
"
>
{{
formatDateTime
(
item
.
created_at
)
||
'
-
'
}}
<
/td
>
<
/tr
>
<
/tr
>
<
/tbody
>
<
/tbody
>
...
...
Prev
1
2
3
Next
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment