fix(operator): fix operator edit issue
All checks were successful
构建并部署前端到测试环境 / build-and-deploy (push) Successful in 4m35s

This commit is contained in:
sexygoat
2026-04-10 09:50:00 +08:00
parent c2c1644799
commit 9a6f085cde
20 changed files with 4082 additions and 2782 deletions

View File

@@ -0,0 +1,326 @@
<template>
<ElCard shadow="never" class="info-card wallet-info">
<template #header>
<div class="card-header wallet-header">
<div class="header-left">
<span>钱包流水</span>
<ElTag type="info" size="small"> {{ total }} </ElTag>
</div>
<div class="filter-section">
<ElSelect
v-model="filterForm.transaction_type"
placeholder="交易类型"
clearable
style="width: 150px"
@change="handleFilterChange"
>
<ElOption label="充值" value="recharge" />
<ElOption label="扣款" value="deduct" />
<ElOption label="退款" value="refund" />
</ElSelect>
<ElDatePicker
v-model="filterForm.date_range"
type="datetimerange"
range-separator=""
start-placeholder="开始时间"
end-placeholder="结束时间"
style="width: 360px"
@change="handleDateChange"
/>
<ElButton type="primary" @click="handleFilterChange">查询</ElButton>
<ElButton @click="handleResetFilter">重置</ElButton>
</div>
</div>
</template>
<div class="wallet-table-wrapper">
<!-- 流水表格 -->
<ElTable
v-if="transactionList && transactionList.length > 0"
:data="transactionList"
class="wallet-table"
border
v-loading="loading"
>
<ElTableColumn label="交易类型" width="100" align="center">
<template #default="scope">
<ElTag :type="getTransactionTypeTag(scope.row.transaction_type)" size="small">
{{ scope.row.transaction_type_text }}
</ElTag>
</template>
</ElTableColumn>
<ElTableColumn label="变动金额" width="120" align="right">
<template #default="scope">
<span
:style="{
color: scope.row.amount > 0 ? '#67c23a' : '#f56c6c',
fontWeight: 600
}"
>
{{ formatAmount(scope.row.amount) }}
</span>
</template>
</ElTableColumn>
<ElTableColumn label="变动前余额" width="120" align="right">
<template #default="scope">
{{ formatAmount(scope.row.balance_before) }}
</template>
</ElTableColumn>
<ElTableColumn label="变动后余额" width="120" align="right">
<template #default="scope">
{{ formatAmount(scope.row.balance_after) }}
</template>
</ElTableColumn>
<ElTableColumn label="关联业务" width="100" align="center">
<template #default="scope">
<ElTag v-if="scope.row.reference_type" type="info" size="small">
{{ scope.row.reference_type === 'recharge' ? '充值' : '订单' }}
</ElTag>
<span v-else>--</span>
</template>
</ElTableColumn>
<ElTableColumn prop="reference_no" label="业务编号" min-width="180" show-overflow-tooltip>
<template #default="scope">
<span v-if="scope.row.reference_no" class="reference-no-link">
{{ scope.row.reference_no }}
</span>
<span v-else>--</span>
</template>
</ElTableColumn>
<ElTableColumn prop="remark" label="备注" min-width="150" show-overflow-tooltip>
<template #default="scope">
{{ scope.row.remark || '-' }}
</template>
</ElTableColumn>
<ElTableColumn label="创建时间" width="170" show-overflow-tooltip>
<template #default="scope">
{{ formatDateTime(scope.row.created_at) }}
</template>
</ElTableColumn>
</ElTable>
<ElEmpty v-else-if="!loading" description="暂无钱包流水数据" :image-size="100" />
<!-- 分页器 -->
<div v-if="total > 0" class="pagination-wrapper">
<ElPagination
v-model:current-page="pagination.page"
v-model:page-size="pagination.page_size"
:total="total"
:page-sizes="[10, 20, 50, 100]"
layout="total, sizes, prev, pager, next, jumper"
@size-change="handlePageSizeChange"
@current-change="handlePageChange"
/>
</div>
</div>
</ElCard>
</template>
<script setup lang="ts">
import { ref, watch, onMounted } from 'vue'
import {
ElCard,
ElTable,
ElTableColumn,
ElTag,
ElButton,
ElSelect,
ElOption,
ElDatePicker,
ElPagination,
ElEmpty,
ElMessage
} from 'element-plus'
import { useAssetFormatters } from '../composables/useAssetFormatters'
import { formatDateTime } from '@/utils/business/format'
import { AssetService } from '@/api/modules'
// Props
interface Props {
assetIdentifier: string // ICCID or virtual_no
}
const props = defineProps<Props>()
// 使用格式化工具
const { formatAmount, getTransactionTypeTag } = useAssetFormatters()
// State
const loading = ref(false)
const transactionList = ref<any[]>([])
const total = ref(0)
const pagination = ref({
page: 1,
page_size: 20
})
const filterForm = ref<{
transaction_type: string | null
date_range: [Date, Date] | null
}>({
transaction_type: null,
date_range: null
})
// API调用参数
const queryParams = ref({
page: 1,
page_size: 20,
transaction_type: null as string | null,
start_time: null as string | null,
end_time: null as string | null
})
// 加载钱包流水列表
const loadTransactions = async () => {
if (!props.assetIdentifier) return
try {
loading.value = true
const response = await AssetService.getWalletTransactions(
props.assetIdentifier,
queryParams.value
)
if (response.code === 0 && response.data) {
transactionList.value = response.data.list || []
total.value = response.data.total || 0
}
} catch (error: any) {
if (error?.response?.status === 403) {
ElMessage.warning('企业账号禁止查看钱包流水')
} else {
console.error('获取钱包流水失败:', error)
}
} finally {
loading.value = false
}
}
// 处理日期范围变化
const handleDateChange = (value: [Date, Date] | null) => {
if (value && value.length === 2) {
// 转换为 RFC3339 格式
queryParams.value.start_time = value[0].toISOString()
queryParams.value.end_time = value[1].toISOString()
} else {
queryParams.value.start_time = null
queryParams.value.end_time = null
}
}
// 处理筛选条件变化
const handleFilterChange = () => {
queryParams.value.page = 1
pagination.value.page = 1
queryParams.value.transaction_type = filterForm.value.transaction_type
loadTransactions()
}
// 重置筛选条件
const handleResetFilter = () => {
filterForm.value = {
transaction_type: null,
date_range: null
}
queryParams.value = {
page: 1,
page_size: 20,
transaction_type: null,
start_time: null,
end_time: null
}
pagination.value = {
page: 1,
page_size: 20
}
loadTransactions()
}
// 处理分页大小变化
const handlePageSizeChange = (size: number) => {
queryParams.value.page_size = size
queryParams.value.page = 1
pagination.value.page = 1
loadTransactions()
}
// 处理页码变化
const handlePageChange = (page: number) => {
queryParams.value.page = page
loadTransactions()
}
// 监听资产标识符变化
watch(
() => props.assetIdentifier,
(newVal) => {
if (newVal) {
// 重置状态
handleResetFilter()
}
},
{ immediate: true }
)
// 组件挂载时加载数据
onMounted(() => {
if (props.assetIdentifier) {
loadTransactions()
}
})
</script>
<style scoped lang="scss">
.info-card {
&.wallet-info {
.wallet-header {
display: flex;
flex-wrap: wrap;
gap: 16px;
align-items: center;
justify-content: space-between;
.header-left {
display: flex;
gap: 8px;
align-items: center;
}
.filter-section {
display: flex;
flex-wrap: wrap;
gap: 12px;
align-items: center;
}
}
.wallet-table-wrapper {
.wallet-table {
:deep(.el-table__header) {
th {
font-weight: 600;
color: var(--el-text-color-primary, #374151);
}
}
.reference-no-link {
color: var(--el-color-primary);
text-decoration: underline;
text-decoration-style: dashed;
text-underline-offset: 2px;
cursor: pointer;
&:hover {
color: var(--el-color-primary-light-3);
text-decoration-style: solid;
}
}
}
.pagination-wrapper {
display: flex;
justify-content: flex-end;
margin-top: 20px;
}
}
}
}
</style>