优化样式

This commit is contained in:
speakeloudest 2025-12-31 06:47:39 -08:00
parent 2b0741fdb9
commit 4d153b4149
5 changed files with 102 additions and 40 deletions

View File

@ -39,10 +39,12 @@
</div> </div>
<div class="px-6 pt-[85px] pb-[23px]"> <div class="px-6 pt-[85px] pb-[23px]">
<Button <Button
:disabled="isPaying"
@click="$emit('pay', selectedValue)" @click="$emit('pay', selectedValue)"
class="h-[50px] w-full rounded-[32px] border-none bg-black text-[16px] font-black text-white transition-all hover:bg-black/90 active:scale-[0.98]" class="h-[50px] w-full rounded-[32px] border-none bg-black text-[16px] font-black text-white transition-all hover:bg-black/90 active:scale-[0.98] disabled:opacity-50"
> >
立即支付 <Loader2 v-if="isPaying" class="mr-2 h-4 w-4 animate-spin" />
{{ isPaying ? '正在支付...' : '立即支付' }}
</Button> </Button>
</div> </div>
</div> </div>
@ -51,6 +53,7 @@
<script setup lang="ts"> <script setup lang="ts">
import { ref, computed } from 'vue' import { ref, computed } from 'vue'
import { Button } from '@/components/ui/button' import { Button } from '@/components/ui/button'
import { Loader2 } from 'lucide-vue-next'
interface PaymentOption { interface PaymentOption {
label: string label: string
@ -60,6 +63,7 @@ interface PaymentOption {
const props = defineProps<{ const props = defineProps<{
methods: any[] methods: any[]
selectedPlan: any selectedPlan: any
isPaying?: boolean
}>() }>()
const list = computed(() => const list = computed(() =>

View File

@ -52,6 +52,7 @@
<PaymentMethod <PaymentMethod
:methods="payments" :methods="payments"
:selectedPlan="selectedPlan" :selectedPlan="selectedPlan"
:is-paying="isPaying"
@pay="(id: number | string) => $emit('pay', id)" @pay="(id: number | string) => $emit('pay', id)"
/> />
</div> </div>
@ -76,6 +77,7 @@ const props = defineProps<{
userInfo: { email: string } userInfo: { email: string }
selectedPlanId: string selectedPlanId: string
selectedPlan: any selectedPlan: any
isPaying: boolean
}>() }>()
const emit = defineEmits(['select-plan', 'pay', 'refresh']) const emit = defineEmits(['select-plan', 'pay', 'refresh'])

View File

@ -22,6 +22,7 @@
<PaymentMethod <PaymentMethod
:methods="payments" :methods="payments"
:selectedPlan="selectedPlan" :selectedPlan="selectedPlan"
:is-paying="isPaying"
@pay="(id: number | string) => $emit('pay', id)" @pay="(id: number | string) => $emit('pay', id)"
/> />
</div> </div>
@ -63,6 +64,7 @@ const props = defineProps<{
userInfo: { email: string } userInfo: { email: string }
selectedPlanId: string selectedPlanId: string
selectedPlan: any selectedPlan: any
isPaying: boolean
}>() }>()
const emit = defineEmits(['select-plan', 'pay', 'refresh']) const emit = defineEmits(['select-plan', 'pay', 'refresh'])

View File

@ -32,6 +32,7 @@
:user-info="userSubInfo" :user-info="userSubInfo"
:selected-plan-id="selectedPlanId" :selected-plan-id="selectedPlanId"
:selected-plan="activePlan" :selected-plan="activePlan"
:is-paying="isPaying"
@select-plan="handlePlanSelect" @select-plan="handlePlanSelect"
@pay="handlePay" @pay="handlePay"
@refresh="init" @refresh="init"
@ -46,6 +47,7 @@
:user-info="userSubInfo" :user-info="userSubInfo"
:selected-plan-id="selectedPlanId" :selected-plan-id="selectedPlanId"
:selected-plan="activePlan" :selected-plan="activePlan"
:is-paying="isPaying"
@select-plan="handlePlanSelect" @select-plan="handlePlanSelect"
@pay="handlePay" @pay="handlePay"
@refresh="init" @refresh="init"
@ -70,6 +72,7 @@ const route = useRoute()
const router = useRouter() const router = useRouter()
// --- State --- // --- State ---
const selectedPlanId = ref('p2') const selectedPlanId = ref('p2')
const isPaying = ref(false)
const orderStatusDialogRef = ref<InstanceType<typeof OrderStatusDialog> | null>(null) const orderStatusDialogRef = ref<InstanceType<typeof OrderStatusDialog> | null>(null)
const selectedPayment = ref('alipay') const selectedPayment = ref('alipay')
@ -132,6 +135,7 @@ const handlePlanSelect = (id: string) => {
} }
const handlePay = (methodId: number | string) => { const handlePay = (methodId: number | string) => {
if (isPaying.value) return
const plan = plans.value.find((p) => p.id === selectedPlanId.value) const plan = plans.value.find((p) => p.id === selectedPlanId.value)
if (!plan) return if (!plan) return
@ -147,7 +151,7 @@ const handlePay = (methodId: number | string) => {
if (isRenewal) { if (isRenewal) {
params.user_subscribe_id = already.id params.user_subscribe_id = already.id
} }
console.log(params) isPaying.value = true
request.post(api, params).then((res: any) => { request.post(api, params).then((res: any) => {
request request
.post('/api/v1/public/portal/order/checkout', { .post('/api/v1/public/portal/order/checkout', {
@ -163,6 +167,11 @@ const handlePay = (methodId: number | string) => {
}) })
} }
}) })
.finally(() => {
isPaying.value = false
})
}).catch(() => {
isPaying.value = false
}) })
} }

View File

@ -52,6 +52,68 @@ interface ResponseType extends AxiosResponse {
config: RequestConfig config: RequestConfig
} }
const ERROR_MESSAGES: Record<number | string, string> = {
'200': '成功',
'500': '内部服务器错误',
'10001': '数据库查询错误',
'10002': '数据库更新错误',
'10003': '数据库插入错误',
'10004': '数据库删除错误',
'20001': '用户已存在',
'20002': '用户不存在',
'20003': '用户密码错误',
'20004': '用户已禁用',
'20005': '余额不足',
'20006': '停止注册',
'20007': '未绑定Telegram',
'20008': '用户未绑定OAuth方式',
'20009': '邀请码错误',
'30001': '节点已存在',
'30002': '节点不存在',
'30003': '节点组已存在',
'30004': '节点组不存在',
'30005': '节点组不为空',
'400': '参数错误',
'40002': '用户令牌为空',
'40003': '用户令牌无效',
'40004': '用户令牌已过期',
'40005': '您还没有登录',
'401': '请求过多',
'50001': '优惠券不存在',
'50002': '优惠券已被使用',
'50003': '优惠券不匹配',
'60001': '订阅已过期',
'60002': '订阅不可用',
'60003': '用户已有订阅',
'60004': '订阅已被使用',
'60005': '单一订阅模式超出限制',
'60006': '订阅配额限制',
'70001': '验证码错误',
'80001': '队列入队错误',
'90001': '调试模式已启用',
'90002': '发送短信错误',
'90003': '短信功能未启用',
'90004': '电子邮件功能未启用',
'90005': '不支持的登录方式',
'90006': '身份验证器不支持此方式',
'90007': '电话区号为空',
'90008': '密码为空',
'90009': '区号为空',
'90010': '需要密码或验证码',
'90011': '电子邮件已存在',
'90012': '电话号码已存在',
'90013': '设备已存在',
'90014': '电话号码错误',
'90015': '此账户今日已达到发送次数限制',
'90017': '设备不存在',
'90018': '用户 ID 不匹配',
'61001': '订单不存在',
'61002': '支付方式未找到',
'61003': '订单状态错误',
'61004': '重置周期不足',
'61005': '存在没用完的流量',
}
export default class Request { export default class Request {
public axiosInstance: AxiosInstance public axiosInstance: AxiosInstance
@ -93,34 +155,26 @@ export default class Request {
if (config.data && !(config.data instanceof FormData)) { if (config.data && !(config.data instanceof FormData)) {
const plainText = JSON.stringify(config.data) const plainText = JSON.stringify(config.data)
// 加密后config.data 会变成 { data: '...', time: '...' }
config.data = HiAesUtil.encryptData(plainText, encryptionKey) config.data = HiAesUtil.encryptData(plainText, encryptionKey)
} }
if (config.method?.toLowerCase() === 'get' || config.params) { if (config.method?.toLowerCase() === 'get' || config.params) {
const paramsToEncrypt = config.params || {} // 为空则加密 "{}" const paramsToEncrypt = config.params || {}
const plainParamsText = JSON.stringify(paramsToEncrypt) const plainParamsText = JSON.stringify(paramsToEncrypt)
const encryptedParams = HiAesUtil.encryptData(plainParamsText, encryptionKey) const encryptedParams = HiAesUtil.encryptData(plainParamsText, encryptionKey)
// 将原参数替换为加密后的 data 和 time 字段
config.params = { config.params = {
data: encryptedParams.data, data: encryptedParams.data,
time: encryptedParams.time, time: encryptedParams.time,
} }
} }
if (config.data?.time) {
console.log(
'解密',
HiAesUtil.decryptData(config.data.data, config.data.time, encryptionKey),
)
}
config.headers = mergeExtraConfig.formatHeader({ config.headers = mergeExtraConfig.formatHeader({
...this.config.headers, ...this.config.headers,
...config.headers, ...config.headers,
lang: 'zh_CN', lang: 'zh_CN',
'login-type': 'device', 'login-type': 'device',
'user-agent': 'android',
...(mergeExtraConfig.withToken && { ...(mergeExtraConfig.withToken && {
[mergeExtraConfig.tokenKey]: mergeExtraConfig.getToken(), [mergeExtraConfig.tokenKey]: mergeExtraConfig.getToken(),
}), }),
@ -142,7 +196,6 @@ export default class Request {
const { data, config } = response const { data, config } = response
let responseData = response.data.data let responseData = response.data.data
// 假设后端返回格式为 { data: "base64...", time: "..." }
if (responseData && responseData.data && responseData.time) { if (responseData && responseData.data && responseData.time) {
try { try {
const decryptedStr = HiAesUtil.decryptData( const decryptedStr = HiAesUtil.decryptData(
@ -150,7 +203,6 @@ export default class Request {
responseData.time, responseData.time,
encryptionKey, encryptionKey,
) )
// 解密后转化为 JSON 对象供后续业务使用
responseData = JSON.parse(decryptedStr) responseData = JSON.parse(decryptedStr)
} catch (e) { } catch (e) {
console.error('解密失败:', e) console.error('解密失败:', e)
@ -158,39 +210,32 @@ export default class Request {
} }
} }
axiosCanceler.removePending(config) axiosCanceler.removePending(config)
if (data.code == 40004) {
toast.error('登录状态已失效, 请重新登录') if (data.code !== 200) {
redirectLogin() const msg = ERROR_MESSAGES[data.code] || response.data?.msg || data?.error || '未知错误'
return
} if (data.code == 40004 || data.code == 40003 || data.code == 40005) {
if (data.code == 70001) { toast.error(msg)
toast.error('验证码错误') redirectLogin()
redirectLogin() return
return }
}
const resData = config.extraConfig?.originResponseData ? response : responseData if (config.extraConfig?.handleResponse) {
if (config.extraConfig?.handleResponse) { this.errorReport(config.extraConfig.errorLevel ?? 2, msg)
if (data.code === 200) { return Promise.reject({
return resData ...data,
message: msg,
})
} }
this.errorReport(
config.extraConfig.errorLevel ?? 2,
response.data?.msg ?? data?.error ?? '未知错误',
)
return Promise.reject({
...data,
message: response.data?.msg ?? data?.error ?? '未知错误',
})
} else {
return resData
} }
return config.extraConfig?.originResponseData ? response : responseData
}, },
(error) => { (error) => {
const status = error?.response?.status const status = error?.response?.status
const code = error?.code const code = error?.code
let message = error?.message let message = error?.message
if (status === 401) { if (status === 401) {
// message = '未登录或登录状态失效'
redirectLogin() redirectLogin()
return return
} }