This commit is contained in:
@@ -21,6 +21,8 @@
|
||||
<span>加载中...</span>
|
||||
</div>
|
||||
</ElCard>
|
||||
|
||||
<PaymentVoucherDialog :file-key="paymentVoucherFileKey" @close="paymentVoucherFileKey = ''" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -34,7 +36,7 @@
|
||||
import { AgentRechargeService } from '@/api/modules'
|
||||
import type { AgentRecharge, AgentRechargeStatus, AgentRechargePaymentMethod } from '@/types/api'
|
||||
import { formatDateTime } from '@/utils/business/format'
|
||||
import { RoutesAlias } from '@/router/routesAlias'
|
||||
import PaymentVoucherDialog from '@/components/business/PaymentVoucherDialog.vue'
|
||||
|
||||
defineOptions({ name: 'AgentRechargeDetail' })
|
||||
|
||||
@@ -43,6 +45,7 @@
|
||||
|
||||
const loading = ref(false)
|
||||
const detailData = ref<AgentRecharge | null>(null)
|
||||
const paymentVoucherFileKey = ref('')
|
||||
|
||||
const pageTitle = computed(() => `充值订单详情`)
|
||||
|
||||
@@ -124,6 +127,24 @@
|
||||
prop: 'payment_transaction_id',
|
||||
formatter: (value) => value || '-',
|
||||
fullWidth: true
|
||||
},
|
||||
{
|
||||
label: '支付凭证',
|
||||
fullWidth: true,
|
||||
render: (data) =>
|
||||
data.payment_voucher_key
|
||||
? h(
|
||||
ElButton,
|
||||
{
|
||||
type: 'primary',
|
||||
link: true,
|
||||
onClick: () => {
|
||||
paymentVoucherFileKey.value = data.payment_voucher_key
|
||||
}
|
||||
},
|
||||
() => '查看支付凭证'
|
||||
)
|
||||
: h('span', '-')
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
@@ -96,32 +96,25 @@
|
||||
label="支付凭证"
|
||||
prop="payment_voucher_key"
|
||||
>
|
||||
<ElUpload
|
||||
<VoucherUpload
|
||||
ref="uploadRef"
|
||||
class="payment-voucher-upload"
|
||||
drag
|
||||
:auto-upload="false"
|
||||
:on-change="handleVoucherFileChange"
|
||||
:on-remove="handleRemoveVoucher"
|
||||
accept="image/*"
|
||||
list-type="picture"
|
||||
:limit="1"
|
||||
>
|
||||
<el-icon class="payment-voucher-upload__icon"><UploadFilled /></el-icon>
|
||||
<div class="payment-voucher-upload__text">
|
||||
将支付凭证拖到此处,或<em>点击上传</em>
|
||||
</div>
|
||||
<template #tip>
|
||||
<div class="el-upload__tip">支持 jpg、png 格式,文件大小不超过 5MB</div>
|
||||
</template>
|
||||
</ElUpload>
|
||||
v-model="createForm.payment_voucher_key"
|
||||
voucher-name="支付凭证"
|
||||
@uploading-change="voucherUploading = $event"
|
||||
@change="createFormRef?.validateField('payment_voucher_key')"
|
||||
/>
|
||||
</ElFormItem>
|
||||
</ElForm>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<ElButton @click="createDialogVisible = false">取消</ElButton>
|
||||
<ElButton type="primary" @click="handleCreateRecharge" :loading="createLoading">
|
||||
确认创建
|
||||
<ElButton
|
||||
type="primary"
|
||||
@click="handleCreateRecharge"
|
||||
:loading="createLoading || voucherUploading"
|
||||
:disabled="voucherUploading"
|
||||
>
|
||||
{{ voucherUploading ? '凭证上传中...' : '确认创建' }}
|
||||
</ElButton>
|
||||
</div>
|
||||
</template>
|
||||
@@ -178,19 +171,16 @@
|
||||
<script setup lang="ts">
|
||||
import { h } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { AgentRechargeService, ShopService, StorageService } from '@/api/modules'
|
||||
import { AgentRechargeService, ShopService } from '@/api/modules'
|
||||
import {
|
||||
ElMessage,
|
||||
ElTag,
|
||||
ElTreeSelect,
|
||||
ElButton,
|
||||
ElCascader,
|
||||
ElInputNumber,
|
||||
ElSelect,
|
||||
ElOption,
|
||||
ElUpload
|
||||
ElOption
|
||||
} from 'element-plus'
|
||||
import { UploadFilled } from '@element-plus/icons-vue'
|
||||
import type { FormInstance, FormRules } from 'element-plus'
|
||||
import type {
|
||||
AgentRecharge,
|
||||
@@ -198,25 +188,23 @@
|
||||
AgentRechargeStatus,
|
||||
AgentRechargePaymentMethod,
|
||||
CreateAgentRechargeRequest,
|
||||
ConfirmOfflinePaymentRequest,
|
||||
ShopResponse
|
||||
ConfirmOfflinePaymentRequest
|
||||
} from '@/types/api'
|
||||
import type { SearchFormItem } from '@/types'
|
||||
import { useCheckedColumns } from '@/composables/useCheckedColumns'
|
||||
import { useUserStore } from '@/store/modules/user'
|
||||
import { formatDateTime } from '@/utils/business/format'
|
||||
import { RoutesAlias } from '@/router/routesAlias'
|
||||
import { useAuth } from '@/composables/useAuth'
|
||||
import PaymentVoucherDialog from '@/components/business/PaymentVoucherDialog.vue'
|
||||
import VoucherUpload from '@/components/business/VoucherUpload.vue'
|
||||
|
||||
defineOptions({ name: 'AgentRechargeList' })
|
||||
|
||||
const router = useRouter()
|
||||
const userStore = useUserStore()
|
||||
const { hasAuth } = useAuth()
|
||||
|
||||
const loading = ref(false)
|
||||
const createLoading = ref(false)
|
||||
const voucherUploading = ref(false)
|
||||
const confirmPayLoading = ref(false)
|
||||
const tableRef = ref()
|
||||
const createDialogVisible = ref(false)
|
||||
@@ -238,8 +226,6 @@
|
||||
|
||||
// 店铺选项
|
||||
const shopOptions = ref<any[]>([])
|
||||
const shopTreeData = ref<ShopResponse[]>([])
|
||||
|
||||
// 店铺级联选择相关
|
||||
const shopCascadeOptions = ref<any[]>([])
|
||||
const shopCascadeProps = {
|
||||
@@ -340,7 +326,7 @@
|
||||
|
||||
const createFormRef = ref<FormInstance>()
|
||||
const confirmPayFormRef = ref<FormInstance>()
|
||||
const uploadRef = ref()
|
||||
const uploadRef = ref<InstanceType<typeof VoucherUpload>>()
|
||||
const MIN_RECHARGE_AMOUNT = 0.01
|
||||
|
||||
const createRules = computed<FormRules>(() => {
|
||||
@@ -523,33 +509,6 @@
|
||||
isFirstActivation = false
|
||||
})
|
||||
|
||||
// 构建树形数据
|
||||
const buildTreeData = (items: ShopResponse[]) => {
|
||||
const map = new Map<number, ShopResponse & { children?: ShopResponse[] }>()
|
||||
const tree: ShopResponse[] = []
|
||||
|
||||
// 先将所有项放入 map
|
||||
items.forEach((item) => {
|
||||
map.set(item.id, { ...item, children: [] })
|
||||
})
|
||||
|
||||
// 构建树形结构
|
||||
items.forEach((item) => {
|
||||
const node = map.get(item.id)!
|
||||
if (item.parent_id && map.has(item.parent_id)) {
|
||||
// 有父节点,添加到父节点的 children 中
|
||||
const parent = map.get(item.parent_id)!
|
||||
if (!parent.children) parent.children = []
|
||||
parent.children.push(node)
|
||||
} else {
|
||||
// 没有父节点或父节点不存在,作为根节点
|
||||
tree.push(node)
|
||||
}
|
||||
})
|
||||
|
||||
return tree
|
||||
}
|
||||
|
||||
// 加载店铺列表 - 改用级联查询
|
||||
const loadShops = async () => {
|
||||
try {
|
||||
@@ -671,60 +630,18 @@
|
||||
createForm.payment_method = ''
|
||||
createForm.shop_id = null
|
||||
createForm.payment_voucher_key = undefined
|
||||
uploadRef.value?.clearFiles()
|
||||
}
|
||||
|
||||
// 支付凭证文件变化
|
||||
const handleVoucherFileChange = async (file: any) => {
|
||||
const isImage = file.raw.type.startsWith('image/')
|
||||
const isLt5M = file.raw.size / 1024 / 1024 < 5
|
||||
|
||||
if (!isImage) {
|
||||
ElMessage.error('只能上传图片文件')
|
||||
uploadRef.value?.clearFiles()
|
||||
return
|
||||
}
|
||||
if (!isLt5M) {
|
||||
ElMessage.error('图片大小不能超过 5MB')
|
||||
uploadRef.value?.clearFiles()
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
const uploadUrlRes = await StorageService.getUploadUrl({
|
||||
file_name: file.name,
|
||||
content_type: file.raw.type,
|
||||
purpose: 'attachment'
|
||||
})
|
||||
|
||||
if (uploadUrlRes.code !== 0) {
|
||||
ElMessage.error(uploadUrlRes.msg || '获取上传地址失败')
|
||||
uploadRef.value?.clearFiles()
|
||||
return
|
||||
}
|
||||
|
||||
const { upload_url, file_key } = uploadUrlRes.data
|
||||
|
||||
await StorageService.uploadFile(upload_url, file.raw, file.raw.type)
|
||||
|
||||
createForm.payment_voucher_key = file_key
|
||||
ElMessage.success('上传成功')
|
||||
} catch (error: any) {
|
||||
console.error('上传失败:', error)
|
||||
createForm.payment_voucher_key = undefined
|
||||
uploadRef.value?.clearFiles()
|
||||
}
|
||||
}
|
||||
|
||||
// 删除支付凭证
|
||||
const handleRemoveVoucher = () => {
|
||||
createForm.payment_voucher_key = undefined
|
||||
voucherUploading.value = false
|
||||
uploadRef.value?.clearFiles(false)
|
||||
}
|
||||
|
||||
// 创建充值订单
|
||||
const handleCreateRecharge = async () => {
|
||||
const formRef = createFormRef.value
|
||||
if (!formRef) return
|
||||
if (voucherUploading.value) {
|
||||
ElMessage.warning('支付凭证上传中,请稍候')
|
||||
return
|
||||
}
|
||||
|
||||
await formRef.validate(async (valid) => {
|
||||
if (valid) {
|
||||
@@ -854,34 +771,4 @@
|
||||
.agent-recharge-page {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.payment-voucher-upload {
|
||||
width: 100%;
|
||||
|
||||
:deep(.el-upload) {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
:deep(.el-upload-dragger) {
|
||||
width: 100%;
|
||||
padding: 24px 16px;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
&__icon {
|
||||
margin-bottom: 12px;
|
||||
font-size: 28px;
|
||||
color: var(--el-color-primary);
|
||||
}
|
||||
|
||||
&__text {
|
||||
font-size: 14px;
|
||||
color: var(--el-text-color-regular);
|
||||
|
||||
em {
|
||||
font-style: normal;
|
||||
color: var(--el-color-primary);
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -22,13 +22,7 @@
|
||||
</div>
|
||||
</ElCard>
|
||||
|
||||
<ElImageViewer
|
||||
v-if="voucherPreviewVisible"
|
||||
:url-list="refundVoucherUrls"
|
||||
:initial-index="voucherPreviewIndex"
|
||||
hide-on-click-modal
|
||||
@close="voucherPreviewVisible = false"
|
||||
/>
|
||||
<PaymentVoucherDialog :file-key="refundVoucherFileKey" @close="refundVoucherFileKey = ''" />
|
||||
|
||||
<!-- 审批通过对话框 -->
|
||||
<ElDialog
|
||||
@@ -172,11 +166,11 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted, computed, h, reactive } from 'vue'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import { ElCard, ElImageViewer, ElIcon, ElMessage, ElTag } from 'element-plus'
|
||||
import { ElButton, ElCard, ElIcon, ElMessage, ElTag } from 'element-plus'
|
||||
import { ArrowLeft, Loading } from '@element-plus/icons-vue'
|
||||
import DetailPage from '@/components/common/DetailPage.vue'
|
||||
import type { DetailSection } from '@/components/common/DetailPage.vue'
|
||||
import { RefundService, StorageService } from '@/api/modules'
|
||||
import { RefundService } from '@/api/modules'
|
||||
import type {
|
||||
Refund,
|
||||
RefundStatus,
|
||||
@@ -185,6 +179,7 @@
|
||||
ResubmitRefundRequest
|
||||
} from '@/types/api'
|
||||
import { formatDateTime, yuanToFen } from '@/utils/business/format'
|
||||
import PaymentVoucherDialog from '@/components/business/PaymentVoucherDialog.vue'
|
||||
|
||||
defineOptions({ name: 'RefundDetail' })
|
||||
|
||||
@@ -193,9 +188,7 @@
|
||||
|
||||
const loading = ref(false)
|
||||
const refund = ref<Refund | null>(null)
|
||||
const refundVoucherUrls = ref<string[]>([])
|
||||
const voucherPreviewVisible = ref(false)
|
||||
const voucherPreviewIndex = ref(0)
|
||||
const refundVoucherFileKey = ref('')
|
||||
|
||||
const pageTitle = computed(() => `退款详情`)
|
||||
|
||||
@@ -262,14 +255,6 @@
|
||||
return statusMap[status] || '-'
|
||||
}
|
||||
|
||||
const getVoucherKeys = (data: Refund): string[] => {
|
||||
const voucherKey = data.refund_voucher_key || ''
|
||||
return voucherKey
|
||||
.split(',')
|
||||
.map((key) => key.trim())
|
||||
.filter(Boolean)
|
||||
}
|
||||
|
||||
const detailSections = computed((): DetailSection[] => [
|
||||
{
|
||||
title: '退款信息',
|
||||
@@ -309,18 +294,18 @@
|
||||
{
|
||||
label: '退款凭证',
|
||||
fullWidth: true,
|
||||
render: () =>
|
||||
refundVoucherUrls.value.length
|
||||
render: (data) =>
|
||||
data.refund_voucher_key
|
||||
? h(
|
||||
'div',
|
||||
{ class: 'refund-voucher-list' },
|
||||
refundVoucherUrls.value.map((url, index) =>
|
||||
h('img', {
|
||||
class: 'refund-voucher-image',
|
||||
src: url,
|
||||
onClick: () => handlePreviewRefundVoucher(index)
|
||||
})
|
||||
)
|
||||
ElButton,
|
||||
{
|
||||
type: 'primary',
|
||||
link: true,
|
||||
onClick: () => {
|
||||
refundVoucherFileKey.value = data.refund_voucher_key
|
||||
}
|
||||
},
|
||||
() => '查看退款凭证'
|
||||
)
|
||||
: h('span', '-')
|
||||
},
|
||||
@@ -391,7 +376,6 @@
|
||||
const res = await RefundService.getRefundById(id)
|
||||
if (res.code === 0) {
|
||||
refund.value = res.data
|
||||
await loadRefundVoucherUrls(res.data)
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
@@ -405,26 +389,6 @@
|
||||
router.back()
|
||||
}
|
||||
|
||||
const loadRefundVoucherUrls = async (data: Refund) => {
|
||||
const fileKeys = getVoucherKeys(data)
|
||||
refundVoucherUrls.value = []
|
||||
if (!fileKeys.length) return
|
||||
|
||||
try {
|
||||
const res = await StorageService.batchDownloadUrls({ file_keys: fileKeys })
|
||||
if (res.code === 0) {
|
||||
refundVoucherUrls.value = fileKeys.map((key) => res.data.urls[key]).filter(Boolean)
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取退款凭证失败:', error)
|
||||
}
|
||||
}
|
||||
|
||||
const handlePreviewRefundVoucher = (index: number) => {
|
||||
voucherPreviewIndex.value = index
|
||||
voucherPreviewVisible.value = true
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
const id = route.params.id as string
|
||||
if (id) {
|
||||
@@ -556,21 +520,5 @@
|
||||
font-size: 32px;
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.refund-voucher-list) {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 12px;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
:deep(.refund-voucher-image) {
|
||||
width: 96px;
|
||||
height: 96px;
|
||||
cursor: zoom-in;
|
||||
object-fit: cover;
|
||||
border: 1px solid var(--el-border-color);
|
||||
border-radius: 6px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<ArtSearchBar
|
||||
v-model:filter="searchForm"
|
||||
:items="searchFormItems"
|
||||
:show-expand="false"
|
||||
show-expand
|
||||
@reset="handleReset"
|
||||
@search="handleSearch"
|
||||
></ArtSearchBar>
|
||||
@@ -279,7 +279,8 @@
|
||||
const initialSearchState: RefundQueryParams = {
|
||||
status: undefined,
|
||||
order_id: undefined,
|
||||
shop_id: undefined
|
||||
shop_id: undefined,
|
||||
asset_identifier: ''
|
||||
}
|
||||
|
||||
// 搜索表单
|
||||
@@ -325,6 +326,15 @@
|
||||
remoteMethod: (query: string) => searchOrdersForFilter(query)
|
||||
}
|
||||
},
|
||||
{
|
||||
label: '资产标识',
|
||||
prop: 'asset_identifier',
|
||||
type: 'input',
|
||||
placeholder: '请输入ICCID或设备虚拟号',
|
||||
config: {
|
||||
clearable: true
|
||||
}
|
||||
},
|
||||
{
|
||||
label: '状态',
|
||||
prop: 'status',
|
||||
@@ -622,12 +632,14 @@
|
||||
const getTableData = async () => {
|
||||
loading.value = true
|
||||
try {
|
||||
const assetIdentifier = searchForm.asset_identifier?.trim()
|
||||
const params: RefundQueryParams = {
|
||||
page: pagination.page,
|
||||
page_size: pagination.page_size,
|
||||
status: searchForm.status,
|
||||
order_id: searchForm.order_id,
|
||||
shop_id: searchForm.shop_id
|
||||
shop_id: searchForm.shop_id,
|
||||
asset_identifier: assetIdentifier || undefined
|
||||
}
|
||||
const res = await RefundService.getRefunds(params)
|
||||
if (res.code === 0) {
|
||||
|
||||
Reference in New Issue
Block a user