This commit is contained in:
@@ -8,6 +8,7 @@ import type {
|
||||
WithdrawalRequestQueryParams,
|
||||
WithdrawalRequestPageResult,
|
||||
ApproveWithdrawalParams,
|
||||
WithdrawalApprovalResponse,
|
||||
RejectWithdrawalParams,
|
||||
WithdrawalSettingListResult,
|
||||
WithdrawalSettingItem,
|
||||
@@ -46,8 +47,11 @@ export class CommissionService extends BaseService {
|
||||
* 审批通过提现申请
|
||||
* POST /api/admin/commission/withdrawal-requests/{id}/approve
|
||||
*/
|
||||
static approveWithdrawal(id: number, params?: ApproveWithdrawalParams): Promise<BaseResponse> {
|
||||
return this.post<BaseResponse>(
|
||||
static approveWithdrawal(
|
||||
id: number,
|
||||
params: ApproveWithdrawalParams
|
||||
): Promise<BaseResponse<WithdrawalApprovalResponse>> {
|
||||
return this.post<BaseResponse<WithdrawalApprovalResponse>>(
|
||||
`/api/admin/commission/withdrawal-requests/${id}/approve`,
|
||||
params
|
||||
)
|
||||
|
||||
@@ -88,7 +88,23 @@ export interface WithdrawalRequestQueryParams extends PaginationParams {
|
||||
* 审批提现申请参数
|
||||
*/
|
||||
export interface ApproveWithdrawalParams {
|
||||
account_name?: string | null // 修正后的收款人姓名
|
||||
account_number?: string | null // 修正后的收款账号
|
||||
amount?: number | null // 修正后的提现金额(分)
|
||||
payment_type: PaymentType // 放款类型
|
||||
remark?: string // 审批备注
|
||||
withdrawal_method?: WithdrawalMethod | null // 修正后的收款类型
|
||||
}
|
||||
|
||||
/**
|
||||
* 审批提现申请响应
|
||||
*/
|
||||
export interface WithdrawalApprovalResponse {
|
||||
id?: number // 提现申请ID
|
||||
processed_at?: string // 处理时间
|
||||
status?: WithdrawalStatus // 状态
|
||||
status_name?: string // 状态名称
|
||||
withdrawal_no?: string // 提现单号
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
7
src/types/components.d.ts
vendored
7
src/types/components.d.ts
vendored
@@ -93,11 +93,9 @@ declare module 'vue' {
|
||||
ElAlert: typeof import('element-plus/es')['ElAlert']
|
||||
ElAvatar: typeof import('element-plus/es')['ElAvatar']
|
||||
ElButton: typeof import('element-plus/es')['ElButton']
|
||||
ElCalendar: typeof import('element-plus/es')['ElCalendar']
|
||||
ElCard: typeof import('element-plus/es')['ElCard']
|
||||
ElCascader: typeof import('element-plus/es')['ElCascader']
|
||||
ElCheckbox: typeof import('element-plus/es')['ElCheckbox']
|
||||
ElCheckboxGroup: typeof import('element-plus/es')['ElCheckboxGroup']
|
||||
ElCol: typeof import('element-plus/es')['ElCol']
|
||||
ElCollapse: typeof import('element-plus/es')['ElCollapse']
|
||||
ElCollapseItem: typeof import('element-plus/es')['ElCollapseItem']
|
||||
@@ -115,6 +113,7 @@ declare module 'vue' {
|
||||
ElForm: typeof import('element-plus/es')['ElForm']
|
||||
ElFormItem: typeof import('element-plus/es')['ElFormItem']
|
||||
ElIcon: typeof import('element-plus/es')['ElIcon']
|
||||
ElImageViewer: typeof import('element-plus/es')['ElImageViewer']
|
||||
ElInput: typeof import('element-plus/es')['ElInput']
|
||||
ElInputNumber: typeof import('element-plus/es')['ElInputNumber']
|
||||
ElMenu: typeof import('element-plus/es')['ElMenu']
|
||||
@@ -122,13 +121,13 @@ declare module 'vue' {
|
||||
ElOption: typeof import('element-plus/es')['ElOption']
|
||||
ElPagination: typeof import('element-plus/es')['ElPagination']
|
||||
ElPopover: typeof import('element-plus/es')['ElPopover']
|
||||
ElProgress: typeof import('element-plus/es')['ElProgress']
|
||||
ElRadio: typeof import('element-plus/es')['ElRadio']
|
||||
ElRadioButton: typeof import('element-plus/es')['ElRadioButton']
|
||||
ElRadioGroup: typeof import('element-plus/es')['ElRadioGroup']
|
||||
ElResult: typeof import('element-plus/es')['ElResult']
|
||||
ElRow: typeof import('element-plus/es')['ElRow']
|
||||
ElScrollbar: typeof import('element-plus/es')['ElScrollbar']
|
||||
ElSelect: typeof import('element-plus/es')['ElSelect']
|
||||
ElSkeleton: typeof import('element-plus/es')['ElSkeleton']
|
||||
ElSubMenu: typeof import('element-plus/es')['ElSubMenu']
|
||||
ElSwitch: typeof import('element-plus/es')['ElSwitch']
|
||||
ElTable: typeof import('element-plus/es')['ElTable']
|
||||
|
||||
@@ -592,7 +592,8 @@
|
||||
{
|
||||
prop: 'username',
|
||||
label: '账号名称',
|
||||
minWidth: 120
|
||||
minWidth: 160,
|
||||
showOverflowTooltip: true
|
||||
},
|
||||
{
|
||||
prop: 'phone',
|
||||
@@ -617,6 +618,7 @@
|
||||
prop: 'shop_name',
|
||||
label: '店铺名称',
|
||||
minWidth: 150,
|
||||
showOverflowTooltip: true,
|
||||
formatter: (row: any) => {
|
||||
return row.shop_name || '-'
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import { ref } from 'vue'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { AssetService } from '@/api/modules'
|
||||
import { FrontendRateLimitError } from '@/utils/business/apiRateLimit'
|
||||
import { normalizeApiError } from '@/utils/business/apiError'
|
||||
import type {
|
||||
AssetBoundCard,
|
||||
AssetWalletResponse,
|
||||
@@ -32,6 +33,53 @@ const NULLABLE_REALTIME_CARD_FIELDS = new Set([
|
||||
|
||||
// 详情接口是绑定卡实名时间的唯一来源;实时状态接口不应覆盖或保留旧值。
|
||||
const DETAIL_ONLY_BOUND_CARD_FIELDS = new Set(['real_name_at'])
|
||||
const ASSET_NOT_FOUND_CODE = 1006
|
||||
|
||||
const isLegacyNineteenDigitIdentifier = (identifier: string) => /^89\d{17}$/.test(identifier)
|
||||
|
||||
const isAssetNotFound = (value: any) =>
|
||||
value?.code === ASSET_NOT_FOUND_CODE || value?.response?.data?.code === ASSET_NOT_FOUND_CODE
|
||||
|
||||
const isUnauthorized = (value: any) =>
|
||||
value?.code === 401 || value?.response?.status === 401 || value?.response?.data?.code === 401
|
||||
|
||||
const calculateLuhnCheckDigit = (identifier: string) => {
|
||||
let sum = 0
|
||||
let doubleDigit = true
|
||||
|
||||
for (let index = identifier.length - 1; index >= 0; index -= 1) {
|
||||
let digit = Number(identifier[index])
|
||||
if (doubleDigit) {
|
||||
digit *= 2
|
||||
if (digit > 9) digit -= 9
|
||||
}
|
||||
sum += digit
|
||||
doubleDigit = !doubleDigit
|
||||
}
|
||||
|
||||
return String((10 - (sum % 10)) % 10)
|
||||
}
|
||||
|
||||
const resolveAssetForAssetInfo = async (identifier: string) => {
|
||||
const params = { include_usage_summary: true }
|
||||
if (!isLegacyNineteenDigitIdentifier(identifier)) {
|
||||
return AssetService.resolveAsset(identifier, params)
|
||||
}
|
||||
|
||||
const silentConfig = { requestOptions: { errorMessageMode: 'none' as const } }
|
||||
let firstResponse = null
|
||||
|
||||
try {
|
||||
firstResponse = await AssetService.resolveAsset(identifier, params, silentConfig)
|
||||
} catch (error) {
|
||||
if (!isAssetNotFound(error)) throw error
|
||||
}
|
||||
|
||||
if (firstResponse && !isAssetNotFound(firstResponse)) return firstResponse
|
||||
|
||||
const correctedIdentifier = `${identifier}${calculateLuhnCheckDigit(identifier)}`
|
||||
return AssetService.resolveAsset(correctedIdentifier, params, silentConfig)
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化数据大小(MB -> GB/MB)
|
||||
@@ -96,9 +144,10 @@ export function useAssetInfo() {
|
||||
* @param identifier - 资产标识符(ICCID、IMEI、SN、虚拟号或MSISDN)
|
||||
*/
|
||||
const fetchAssetDetail = async (identifier: string) => {
|
||||
const shouldShowSilentRequestError = isLegacyNineteenDigitIdentifier(identifier)
|
||||
try {
|
||||
loading.value = true
|
||||
const response = await AssetService.resolveAsset(identifier, { include_usage_summary: true })
|
||||
const response = await resolveAssetForAssetInfo(identifier)
|
||||
|
||||
if (response.code === 0 && response.data) {
|
||||
const data = response.data
|
||||
@@ -199,8 +248,12 @@ export function useAssetInfo() {
|
||||
ElMessage.error(response.msg || '查询失败')
|
||||
cardInfo.value = null
|
||||
}
|
||||
} catch {
|
||||
} catch (error: any) {
|
||||
cardInfo.value = null
|
||||
if (shouldShowSilentRequestError && !isUnauthorized(error)) {
|
||||
const backendMessage = error?.response?.data?.msg
|
||||
ElMessage.error(backendMessage || normalizeApiError(error).message)
|
||||
}
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
|
||||
@@ -409,12 +409,14 @@
|
||||
|
||||
// fetchAssetDetail 内部已经会并行加载所有相关数据,不需要重复调用
|
||||
await fetchAssetDetail(identifier)
|
||||
if (!cardInfo.value) return
|
||||
|
||||
// 如果是设备类型,需要再次调用搜索接口获取完整数据
|
||||
if (cardInfo.value?.asset_type === 'device') {
|
||||
const deviceIdentifier = cardInfo.value.virtual_no || cardInfo.value.iccid || identifier
|
||||
await fetchAssetDetail(deviceIdentifier)
|
||||
}
|
||||
if (!cardInfo.value) return
|
||||
|
||||
if (cardInfo.value) {
|
||||
walletTransactionSearchToken.value += 1
|
||||
@@ -428,7 +430,8 @@
|
||||
|
||||
// 重置分页并加载套餐列表
|
||||
packagePagination.value.page = 1
|
||||
const result = await loadPackageList(identifier, 1, packagePagination.value.pageSize)
|
||||
const resolvedIdentifier = cardInfo.value.identifier || identifier
|
||||
const result = await loadPackageList(resolvedIdentifier, 1, packagePagination.value.pageSize)
|
||||
packagePagination.value.total = result.total
|
||||
}
|
||||
|
||||
|
||||
@@ -69,6 +69,30 @@
|
||||
</div>
|
||||
</template>
|
||||
</ElDialog>
|
||||
|
||||
<!-- 审批通过对话框 -->
|
||||
<ElDialog v-model="approveDialogVisible" title="审批通过提现申请" width="40%">
|
||||
<ElForm :model="approveForm">
|
||||
<ElFormItem label="备注信息" prop="remark">
|
||||
<ElInput
|
||||
v-model="approveForm.remark"
|
||||
type="textarea"
|
||||
:rows="3"
|
||||
maxlength="500"
|
||||
show-word-limit
|
||||
placeholder="请输入备注信息(选填)"
|
||||
/>
|
||||
</ElFormItem>
|
||||
</ElForm>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<ElButton @click="approveDialogVisible = false">{{ $t('common.cancel') }}</ElButton>
|
||||
<ElButton type="primary" @click="handleApproveSubmit" :loading="approveSubmitLoading">
|
||||
{{ $t('common.confirm') }}
|
||||
</ElButton>
|
||||
</div>
|
||||
</template>
|
||||
</ElDialog>
|
||||
</ElCard>
|
||||
</div>
|
||||
</ArtTableFullScreen>
|
||||
@@ -77,28 +101,30 @@
|
||||
<script setup lang="ts">
|
||||
import { h } from 'vue'
|
||||
import { CommissionService, ShopService } from '@/api/modules'
|
||||
import { ElMessage, ElMessageBox, ElTag } from 'element-plus'
|
||||
import { ElMessage, ElTag } from 'element-plus'
|
||||
import type { FormInstance, FormRules } from 'element-plus'
|
||||
import type {
|
||||
WithdrawalRequestItem,
|
||||
WithdrawalStatus,
|
||||
WithdrawalMethod
|
||||
} from '@/types/api/commission'
|
||||
import { PaymentType } from '@/types/api/commission'
|
||||
import type { SearchFormItem } from '@/types'
|
||||
import { useCheckedColumns } from '@/composables/useCheckedColumns'
|
||||
import { formatDateTime, formatMoney } from '@/utils/business/format'
|
||||
import { WithdrawalStatusMap, WithdrawalMethodMap } from '@/config/constants'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
defineOptions({ name: 'WithdrawalApproval' })
|
||||
|
||||
const { t } = useI18n() // 仅保留用于 common.tips, common.confirm, common.cancel
|
||||
|
||||
const rejectDialogVisible = ref(false)
|
||||
const approveDialogVisible = ref(false)
|
||||
const loading = ref(false)
|
||||
const approveSubmitLoading = ref(false)
|
||||
const rejectSubmitLoading = ref(false)
|
||||
const tableRef = ref()
|
||||
const currentWithdrawalId = ref<number>(0)
|
||||
const currentWithdrawal = ref<WithdrawalRequestItem | null>(null)
|
||||
const approveForm = reactive({ remark: '' })
|
||||
|
||||
// 店铺选项
|
||||
const shopOptions = ref<any[]>([])
|
||||
@@ -216,7 +242,8 @@
|
||||
{
|
||||
prop: 'withdrawal_no',
|
||||
label: '提现单号',
|
||||
minWidth: 180
|
||||
minWidth: 180,
|
||||
showOverflowTooltip: true
|
||||
},
|
||||
{
|
||||
prop: 'shop_name',
|
||||
@@ -232,21 +259,18 @@
|
||||
prop: 'amount',
|
||||
label: '提现金额',
|
||||
width: 120,
|
||||
align: 'right',
|
||||
formatter: (row: WithdrawalRequestItem) => formatMoney(row.amount)
|
||||
},
|
||||
{
|
||||
prop: 'fee',
|
||||
label: '手续费',
|
||||
width: 100,
|
||||
align: 'right',
|
||||
formatter: (row: WithdrawalRequestItem) => formatMoney(row.fee)
|
||||
},
|
||||
{
|
||||
prop: 'actual_amount',
|
||||
label: '实际到账',
|
||||
width: 120,
|
||||
align: 'right',
|
||||
formatter: (row: WithdrawalRequestItem) => formatMoney(row.actual_amount)
|
||||
},
|
||||
{
|
||||
@@ -367,25 +391,36 @@
|
||||
getTableData()
|
||||
}
|
||||
|
||||
// 审批通过
|
||||
const handleApprove = (row: WithdrawalRequestItem) => {
|
||||
ElMessageBox.confirm('确定要通过该提现申请吗?', t('common.tips'), {
|
||||
confirmButtonText: t('common.confirm'),
|
||||
cancelButtonText: t('common.cancel'),
|
||||
type: 'warning'
|
||||
})
|
||||
.then(async () => {
|
||||
try {
|
||||
await CommissionService.approveWithdrawal(row.id)
|
||||
ElMessage.success('审批通过成功')
|
||||
getTableData()
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
// 用户取消
|
||||
// 显示审批通过对话框
|
||||
const showApproveDialog = (row: WithdrawalRequestItem) => {
|
||||
currentWithdrawal.value = row
|
||||
approveForm.remark = ''
|
||||
approveDialogVisible.value = true
|
||||
}
|
||||
|
||||
// 提交审批通过
|
||||
const handleApproveSubmit = async () => {
|
||||
const row = currentWithdrawal.value
|
||||
if (!row) return
|
||||
|
||||
approveSubmitLoading.value = true
|
||||
try {
|
||||
await CommissionService.approveWithdrawal(row.id, {
|
||||
account_name: row.account_name,
|
||||
account_number: row.account_number,
|
||||
amount: row.amount,
|
||||
payment_type: PaymentType.MANUAL,
|
||||
remark: approveForm.remark,
|
||||
withdrawal_method: row.withdrawal_method
|
||||
})
|
||||
ElMessage.success('审批通过成功')
|
||||
approveDialogVisible.value = false
|
||||
getTableData()
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
} finally {
|
||||
approveSubmitLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 显示拒绝对话框
|
||||
@@ -428,7 +463,7 @@
|
||||
return [
|
||||
{
|
||||
label: '审批通过',
|
||||
handler: () => handleApprove(row),
|
||||
handler: () => showApproveDialog(row),
|
||||
type: 'primary' as const
|
||||
},
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user