feat(camel-oil): 更新账号相关API模型和界面组件

- 从账号历史记录模型中移除historyUuid字段
- 在创建令牌请求模型中新增phone字段用于绑定手机号
- 在令牌信息模型中新增phone字段
- 移除账号详情页面中的账号ID显示和统计信息卡片
- 更新账号历史记录组件的筛选条件和展示字段
- 修改账号列表中的状态选项和显示逻辑
- 移除账号关联订单统计弹窗及相关功能
- 更新订单历史记录模型枚举值,新增FillCard类型
- 调整.clau

```
This commit is contained in:
danial
2025-11-23 01:34:04 +08:00
parent 5c1505449b
commit 6dc807dfc3
19 changed files with 421 additions and 803 deletions

View File

@@ -9,7 +9,9 @@
"Bash(pnpm lint:*)", "Bash(pnpm lint:*)",
"Bash(pnpm prettier:fix)", "Bash(pnpm prettier:fix)",
"Bash(pnpm eslint:*)", "Bash(pnpm eslint:*)",
"Bash(mkdir:*)" "Bash(mkdir:*)",
"Bash(pnpm build)",
"Bash(pnpm eslint:fix)"
], ],
"deny": [], "deny": [],
"ask": [] "ask": []

View File

@@ -4,7 +4,6 @@
| Name | Type | Description | Notes | | Name | Type | Description | Notes |
| ---------------- | ---------- | ------------ | --------------------------------- | | ---------------- | ---------- | ------------ | --------------------------------- |
| **historyUuid** | **string** | 历史记录UUID | [optional] [default to undefined] |
| **accountId** | **number** | 账号ID | [optional] [default to undefined] | | **accountId** | **number** | 账号ID | [optional] [default to undefined] |
| **changeType** | **string** | 变更类型 | [optional] [default to undefined] | | **changeType** | **string** | 变更类型 | [optional] [default to undefined] |
| **changeText** | **string** | 变更类型文本 | [optional] [default to undefined] | | **changeText** | **string** | 变更类型文本 | [optional] [default to undefined] |
@@ -20,7 +19,6 @@
import { KamiApiCamelOilV1AccountHistoryItem } from './api'; import { KamiApiCamelOilV1AccountHistoryItem } from './api';
const instance: KamiApiCamelOilV1AccountHistoryItem = { const instance: KamiApiCamelOilV1AccountHistoryItem = {
historyUuid,
accountId, accountId,
changeType, changeType,
changeText, changeText,

View File

@@ -2,11 +2,12 @@
## Properties ## Properties
| Name | Type | Description | Notes | | Name | Type | Description | Notes |
| -------------- | ---------- | ----------- | --------------------------------- | | -------------- | ---------- | ------------ | --------------------------------- |
| **tokenName** | **string** | Token名称 | [default to undefined] | | **tokenName** | **string** | Token名称 | [default to undefined] |
| **tokenValue** | **string** | Token值 | [default to undefined] | | **tokenValue** | **string** | Token值 | [default to undefined] |
| **remark** | **string** | 备注 | [optional] [default to undefined] | | **phone** | **string** | 绑定的手机号 | [optional] [default to undefined] |
| **remark** | **string** | 备注 | [optional] [default to undefined] |
## Example ## Example
@@ -16,6 +17,7 @@ import { KamiApiCamelOilV1CreateTokenReq } from './api';
const instance: KamiApiCamelOilV1CreateTokenReq = { const instance: KamiApiCamelOilV1CreateTokenReq = {
tokenName, tokenName,
tokenValue, tokenValue,
phone,
remark remark
}; };
``` ```

View File

@@ -4,7 +4,6 @@
| Name | Type | Description | Notes | | Name | Type | Description | Notes |
| --------------- | ---------- | ------------ | --------------------------------- | | --------------- | ---------- | ------------ | --------------------------------- |
| **historyUuid** | **string** | 历史记录UUID | [optional] [default to undefined] |
| **orderNo** | **string** | 订单号 | [optional] [default to undefined] | | **orderNo** | **string** | 订单号 | [optional] [default to undefined] |
| **changeType** | **string** | 变更类型 | [optional] [default to undefined] | | **changeType** | **string** | 变更类型 | [optional] [default to undefined] |
| **changeText** | **string** | 变更类型文本 | [optional] [default to undefined] | | **changeText** | **string** | 变更类型文本 | [optional] [default to undefined] |
@@ -20,7 +19,6 @@
import { KamiApiCamelOilV1OrderHistoryItem } from './api'; import { KamiApiCamelOilV1OrderHistoryItem } from './api';
const instance: KamiApiCamelOilV1OrderHistoryItem = { const instance: KamiApiCamelOilV1OrderHistoryItem = {
historyUuid,
orderNo, orderNo,
changeType, changeType,
changeText, changeText,

View File

@@ -7,6 +7,7 @@
| **id** | **number** | Token ID | [optional] [default to undefined] | | **id** | **number** | Token ID | [optional] [default to undefined] |
| **tokenName** | **string** | Token名称 | [optional] [default to undefined] | | **tokenName** | **string** | Token名称 | [optional] [default to undefined] |
| **tokenValue** | **string** | Token值 | [optional] [default to undefined] | | **tokenValue** | **string** | Token值 | [optional] [default to undefined] |
| **phone** | **string** | 绑定的手机号 | [optional] [default to undefined] |
| **status** | **number** | 状态 | [optional] [default to undefined] | | **status** | **number** | 状态 | [optional] [default to undefined] |
| **bindCount** | **number** | 已绑定卡密数量 | [optional] [default to undefined] | | **bindCount** | **number** | 已绑定卡密数量 | [optional] [default to undefined] |
| **totalBindAmount** | **object** | | [optional] [default to undefined] | | **totalBindAmount** | **object** | | [optional] [default to undefined] |
@@ -25,6 +26,7 @@ const instance: KamiApiCamelOilV1TokenInfo = {
id, id,
tokenName, tokenName,
tokenValue, tokenValue,
phone,
status, status,
bindCount, bindCount,
totalBindAmount, totalBindAmount,

View File

@@ -13,10 +13,6 @@
*/ */
export interface KamiApiCamelOilV1AccountHistoryItem { export interface KamiApiCamelOilV1AccountHistoryItem {
/**
* 历史记录UUID
*/
historyUuid?: string;
/** /**
* 账号ID * 账号ID
*/ */

View File

@@ -21,6 +21,10 @@ export interface KamiApiCamelOilV1CreateTokenReq {
* Token值 * Token值
*/ */
tokenValue: string; tokenValue: string;
/**
* 绑定的手机号
*/
phone?: string;
/** /**
* 备注 * 备注
*/ */

View File

@@ -13,10 +13,6 @@
*/ */
export interface KamiApiCamelOilV1OrderHistoryItem { export interface KamiApiCamelOilV1OrderHistoryItem {
/**
* 历史记录UUID
*/
historyUuid?: string;
/** /**
* 订单号 * 订单号
*/ */
@@ -57,6 +53,7 @@ export enum KamiApiCamelOilV1OrderHistoryItemChangeTypeEnum {
CheckPay = 'check_pay', CheckPay = 'check_pay',
Create = 'create', Create = 'create',
Fail = 'fail', Fail = 'fail',
FillCard = 'fill_card',
GetPayUrl = 'get_pay_url', GetPayUrl = 'get_pay_url',
Paid = 'paid', Paid = 'paid',
Submit = 'submit', Submit = 'submit',

View File

@@ -25,6 +25,10 @@ export interface KamiApiCamelOilV1TokenInfo {
* Token值 * Token值
*/ */
tokenValue?: string; tokenValue?: string;
/**
* 绑定的手机号
*/
phone?: string;
/** /**
* 状态 * 状态
*/ */

View File

@@ -1,257 +0,0 @@
<template>
<a-modal
v-model:visible="modalVisible"
title="账号关联订单统计"
width="600px"
:footer="false"
>
<a-spin :loading="loading" style="width: 100%">
<div v-if="accountOrderStats" class="orders-stats">
<!-- 订单统计卡片 -->
<a-row :gutter="16" style="margin-bottom: 24px">
<a-col :span="6">
<a-statistic
title="总订单数"
:value="statistics.totalOrders"
suffix="单"
:value-style="{ color: '#165dff', fontSize: '24px' }"
/>
</a-col>
<a-col :span="6">
<a-statistic
title="已支付订单"
:value="statistics.paidOrders"
suffix="单"
:value-style="{ color: '#00b42a', fontSize: '24px' }"
/>
</a-col>
<a-col :span="6">
<a-statistic
title="待支付订单"
:value="statistics.pendingOrders"
suffix="单"
:value-style="{ color: '#ff7d00', fontSize: '24px' }"
/>
</a-col>
<a-col :span="6">
<a-statistic
title="超时订单"
:value="statistics.timeoutOrders"
suffix="单"
:value-style="{ color: '#f53f3f', fontSize: '24px' }"
/>
</a-col>
</a-row>
<!-- 详细统计信息 -->
<a-card title="详细统计" class="detail-stats-card">
<a-descriptions :column="2" bordered>
<a-descriptions-item label="总订单数">
<a-tag color="blue">{{ statistics.totalOrders }} </a-tag>
</a-descriptions-item>
<a-descriptions-item label="已支付订单">
<a-tag color="green">{{ statistics.paidOrders }} </a-tag>
</a-descriptions-item>
<a-descriptions-item label="待支付订单">
<a-tag color="orange">{{ statistics.pendingOrders }} </a-tag>
</a-descriptions-item>
<a-descriptions-item label="超时订单">
<a-tag color="red">{{ statistics.timeoutOrders }} </a-tag>
</a-descriptions-item>
<a-descriptions-item label="支付成功率">
<span :class="paymentRateClass">{{ paymentRate }}%</span>
</a-descriptions-item>
<a-descriptions-item label="待支付率">
<span :class="pendingRateClass">{{ pendingRate }}%</span>
</a-descriptions-item>
</a-descriptions>
</a-card>
</div>
<!-- 空状态 -->
<a-empty v-else description="暂无订单统计数据" />
</a-spin>
</a-modal>
</template>
<script lang="ts" setup>
import { computed, onMounted, reactive, ref, watch } from 'vue';
import { jdV2OrderClient } from '@/api/index.ts';
import type { KamiApiCamelOilV1AccountOrderListResOrderStats } from '@/api/generated/models/index.ts';
// Props 定义
interface Props {
visible: boolean;
accountId: string;
}
const props = withDefaults(defineProps<Props>(), {
visible: false,
accountId: ''
});
// Emits 定义
interface Emits {
(e: 'update:visible', value: boolean): void;
}
const emit = defineEmits<Emits>();
// 响应式数据
const loading = ref(false);
const accountOrderStats =
ref<KamiApiCamelOilV1AccountOrderListResOrderStats | null>(null);
// 统计数据
const statistics = reactive({
totalOrders: 0,
paidOrders: 0,
pendingOrders: 0,
timeoutOrders: 0
});
// 计算属性
const modalVisible = computed({
get: () => props.visible,
set: value => emit('update:visible', value)
});
const paymentRate = computed(() => {
if (statistics.totalOrders === 0) return 0;
return ((statistics.paidOrders / statistics.totalOrders) * 100).toFixed(2);
});
const pendingRate = computed(() => {
if (statistics.totalOrders === 0) return 0;
return ((statistics.pendingOrders / statistics.totalOrders) * 100).toFixed(2);
});
const paymentRateClass = computed(() => {
const rate = parseFloat(paymentRate.value.toString());
if (rate >= 90) return 'rate-excellent';
if (rate >= 70) return 'rate-good';
return 'rate-poor';
});
const pendingRateClass = computed(() => {
const rate = parseFloat(pendingRate.value.toString());
if (rate <= 10) return 'rate-excellent';
if (rate <= 30) return 'rate-good';
return 'rate-poor';
});
/**
* 获取账号订单统计数据
*/
const fetchAccountOrderStats = async () => {
if (!props.accountId) return;
loading.value = true;
try {
// 由于API结构问题这里暂时使用模拟数据
// 实际应该调用: camelOilOrderClient.apiCamelOilOrderAccountOrdersGet()
// 但是这个API返回的是单个账号的订单统计不是列表
// 模拟数据等待API完善
accountOrderStats.value = {
totalOrders: 150,
paidOrders: 120,
pendingOrders: 25,
timeoutOrders: 5
};
// 更新统计数据
if (accountOrderStats.value) {
Object.assign(statistics, {
totalOrders: accountOrderStats.value.totalOrders || 0,
paidOrders: accountOrderStats.value.paidOrders || 0,
pendingOrders: accountOrderStats.value.pendingOrders || 0,
timeoutOrders: accountOrderStats.value.timeoutOrders || 0
});
}
} catch (error) {
console.error('获取账号订单统计失败:', error);
accountOrderStats.value = null;
} finally {
loading.value = false;
}
};
// 监听弹窗显示状态
watch(
() => props.visible,
visible => {
if (visible && props.accountId) {
fetchAccountOrderStats();
}
},
{ immediate: true }
);
// 监听账号ID变化
watch(
() => props.accountId,
accountId => {
if (accountId && props.visible) {
fetchAccountOrderStats();
}
}
);
</script>
<style scoped>
.orders-stats {
padding: 16px 0;
}
.detail-stats-card {
margin-top: 16px;
}
.rate-excellent {
color: #00b42a;
font-weight: 600;
font-size: 16px;
}
.rate-good {
color: #ff7d00;
font-weight: 600;
font-size: 16px;
}
.rate-poor {
color: #f53f3f;
font-weight: 600;
font-size: 16px;
}
:deep(.arco-statistic) {
text-align: center;
padding: 16px;
border-radius: 6px;
background-color: #f7f8fa;
}
:deep(.arco-statistic-title) {
color: #86909c;
font-size: 14px;
margin-bottom: 8px;
}
:deep(.arco-statistic-value) {
color: #1d2129;
font-size: 24px;
font-weight: 600;
}
:deep(.arco-descriptions-item-label) {
font-weight: 600;
background-color: #f7f8fa;
}
:deep(.arco-modal-body) {
max-height: 70vh;
overflow-y: auto;
}
</style>

View File

@@ -10,9 +10,6 @@
<!-- 基本信息 --> <!-- 基本信息 -->
<a-card title="基本信息" class="detail-card"> <a-card title="基本信息" class="detail-card">
<a-descriptions :column="2" bordered> <a-descriptions :column="2" bordered>
<a-descriptions-item label="账号ID">
{{ accountDetail.accountId }}
</a-descriptions-item>
<a-descriptions-item label="账号名称"> <a-descriptions-item label="账号名称">
{{ accountDetail.accountName }} {{ accountDetail.accountName }}
</a-descriptions-item> </a-descriptions-item>
@@ -35,33 +32,6 @@
</a-descriptions-item> </a-descriptions-item>
</a-descriptions> </a-descriptions>
</a-card> </a-card>
<!-- 统计信息 -->
<a-card title="统计信息" class="detail-card">
<a-row :gutter="16">
<a-col :span="8">
<a-statistic
title="今日充值金额"
:value="parseFloat(statistics.todayAmount)"
prefix="¥"
/>
</a-col>
<a-col :span="8">
<a-statistic
title="今日充值次数"
:value="parseFloat(statistics.todayCount)"
suffix="次"
/>
</a-col>
<a-col :span="8">
<a-statistic
title="本月充值金额"
:value="parseFloat(statistics.monthAmount)"
prefix="¥"
/>
</a-col>
</a-row>
</a-card>
</div> </div>
<!-- 空状态 --> <!-- 空状态 -->
@@ -98,13 +68,6 @@ const loading = ref(false);
const accountDetail = const accountDetail =
ref<KamiApiCamelOilV1AccountStatisticsResAccountInfo | null>(null); ref<KamiApiCamelOilV1AccountStatisticsResAccountInfo | null>(null);
// 统计数据
const statistics = ref({
todayAmount: '0',
todayCount: '0',
monthAmount: '0'
});
// 计算属性 // 计算属性
const modalVisible = computed({ const modalVisible = computed({
get: () => props.visible, get: () => props.visible,
@@ -112,26 +75,19 @@ const modalVisible = computed({
}); });
/** /**
* 获取账号详情和统计信息 * 获取账号详情
*/ */
const fetchAccountDetail = async () => { const fetchAccountDetail = async () => {
if (!props.accountId) return; if (!props.accountId) return;
loading.value = true; loading.value = true;
try { try {
// 获取账号统计信息 // 获取账号详情信息
const { data } = await jdV2AccountClient.apiJdV2AccountStatisticsGet({ const { data } = await jdV2AccountClient.apiJdV2AccountStatisticsGet({
accountId: parseInt(props.accountId) accountId: parseInt(props.accountId)
}); });
accountDetail.value = data.accountInfo; accountDetail.value = data.accountInfo;
// 暂时使用默认统计信息等待API完善
statistics.value = {
todayAmount: '0',
todayCount: '0',
monthAmount: '0'
};
} catch (error) { } catch (error) {
console.error('获取账号详情失败:', error); console.error('获取账号详情失败:', error);
accountDetail.value = null; accountDetail.value = null;
@@ -148,20 +104,20 @@ const fetchAccountDetail = async () => {
const statusMapper = ( const statusMapper = (
status: number | undefined status: number | undefined
): { text: string; color: string } => { ): { text: string; color: string } => {
if (!status) return { text: '未知', color: 'gray' }; if (!status) return { text: '未知状态', color: 'gray' };
switch (status) { switch (status) {
case 0: case 0:
return { text: '失效', color: 'red' }; return { text: '待登录', color: 'orange' }; // 需要登录验证
case 1: case 1:
return { text: '正常', color: 'green' }; return { text: '发送验证码', color: 'blue' }; // 正在发送验证码
case 2: case 2:
return { text: '充值过快', color: 'orange' }; return { text: '在线', color: 'green' }; // 账户在线,可用
case 3: case 3:
return { text: '账号受限', color: 'red' }; return { text: '已暂停', color: 'orange' }; // 账户被暂停使用
case 4: case 4:
return { text: '异常', color: 'gray' }; return { text: '已失效', color: 'red' }; // 账户已失效
default: default:
return { text: '未知', color: 'gray' }; return { text: '未知状态', color: 'gray' };
} }
}; };
@@ -200,39 +156,8 @@ watch(
margin-bottom: 0; margin-bottom: 0;
} }
.balance-text {
color: #00b42a;
font-weight: 600;
font-size: 16px;
}
.recharge-text {
color: #165dff;
font-weight: 600;
font-size: 16px;
}
:deep(.arco-descriptions-item-label) { :deep(.arco-descriptions-item-label) {
font-weight: 600; font-weight: 600;
background-color: #f7f8fa; background-color: var(--color-fill-2);
}
:deep(.arco-statistic) {
text-align: center;
padding: 16px;
border-radius: 6px;
background-color: #f7f8fa;
}
:deep(.arco-statistic-title) {
color: #86909c;
font-size: 14px;
margin-bottom: 8px;
}
:deep(.arco-statistic-value) {
color: #1d2129;
font-size: 24px;
font-weight: 600;
} }
</style> </style>

View File

@@ -27,18 +27,26 @@
</a-form-item> </a-form-item>
</a-col> </a-col>
<a-col :span="8"> <a-col :span="8">
<a-form-item field="action" label="操作类型"> <a-form-item field="changeType" label="变更类型">
<a-select <a-select
v-model="filterForm.action" v-model="filterForm.changeType"
placeholder="请选择操作类型" placeholder="请选择变更类型"
allow-clear allow-clear
style="width: 100%" style="width: 100%"
> >
<a-option value="create">创建</a-option> <a-option value="create">创建账号</a-option>
<a-option value="update">更新</a-option> <a-option value="login">登录成功</a-option>
<a-option value="delete">删除</a-option> <a-option value="offline">检测到掉线</a-option>
<a-option value="recharge">充值</a-option> <a-option value="login_fail">登录失败</a-option>
<a-option value="consume">消费</a-option> <a-option value="pause">订单数达到10暂停使用</a-option>
<a-option value="resume">次日重置恢复使用</a-option>
<a-option value="invalidate">
单日下单不足10个账号失效
</a-option>
<a-option value="order_bind">绑定订单</a-option>
<a-option value="order_complete">订单完成</a-option>
<a-option value="update">更新账号信息</a-option>
<a-option value="delete">删除账号</a-option>
</a-select> </a-select>
</a-form-item> </a-form-item>
</a-col> </a-col>
@@ -79,31 +87,39 @@
@page-change="onPageChange" @page-change="onPageChange"
@page-size-change="onPageSizeChange" @page-size-change="onPageSizeChange"
> >
<template #action="{ record }"> <template #changeType="{ record }">
<a-tag :color="actionMapper(record.action).color"> <a-tag :color="changeTypeMapper(record.changeType).color">
{{ actionMapper(record.action).text }} {{ changeTypeMapper(record.changeType).text }}
</a-tag> </a-tag>
</template> </template>
<template #amount="{ record }"> <template #beforeStatus="{ record }">
<span <a-tag
v-if="record.amount" v-if="
:class="{ record.statusBefore !== undefined &&
'amount-positive': record.amount > 0, record.statusBefore !== null
'amount-negative': record.amount < 0 "
}" :color="statusColorMapper(record.statusBefore)"
> >
{{ record.amount > 0 ? '+' : '' }}¥{{ record.amount }} {{ statusMapper(record.statusBefore).text }}
</span> </a-tag>
<span v-else>-</span> <span v-else>-</span>
</template> </template>
<template #balance="{ record }"> <template #afterStatus="{ record }">
<span class="balance-text">¥{{ record.balance || 0 }}</span> <a-tag
v-if="
record.statusAfter !== undefined && record.statusAfter !== null
"
:color="statusColorMapper(record.statusAfter)"
>
{{ statusMapper(record.statusAfter).text }}
</a-tag>
<span v-else>-</span>
</template> </template>
<template #details="{ record }"> <template #remark="{ record }">
<a-tooltip v-if="record.details" :content="record.details"> <a-tooltip v-if="record.remark" :content="record.remark">
<a-button size="small" type="text"> <a-button size="small" type="text">
<template #icon> <template #icon>
<icon-eye /> <icon-eye />
@@ -120,10 +136,9 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { computed, onMounted, reactive, ref, watch } from 'vue'; import { computed, reactive, ref, watch } from 'vue';
import { Message } from '@arco-design/web-vue'; import { Message } from '@arco-design/web-vue';
import { TableColumnData } from '@arco-design/web-vue'; import { TableColumnData } from '@arco-design/web-vue';
import useLoading from '@/hooks/loading';
import { Pagination } from '@/types/global'; import { Pagination } from '@/types/global';
import { jdV2AccountClient } from '@/api/index.ts'; import { jdV2AccountClient } from '@/api/index.ts';
import type { KamiApiCamelOilV1AccountHistoryItem } from '@/api/generated/models/index.ts'; import type { KamiApiCamelOilV1AccountHistoryItem } from '@/api/generated/models/index.ts';
@@ -148,7 +163,7 @@ interface Emits {
const emit = defineEmits<Emits>(); const emit = defineEmits<Emits>();
// 响应式数据 // 响应式数据
const { loading, setLoading } = useLoading(false); const loading = ref(false);
const tableLoading = ref(false); const tableLoading = ref(false);
const historyData = ref<KamiApiCamelOilV1AccountHistoryItem[]>([]); const historyData = ref<KamiApiCamelOilV1AccountHistoryItem[]>([]);
@@ -163,7 +178,7 @@ const pagination = reactive({ ...basePagination });
// 筛选表单 // 筛选表单
const filterForm = reactive({ const filterForm = reactive({
dateRange: [] as Date[], dateRange: [] as Date[],
action: undefined as string | undefined changeType: undefined as string | undefined
}); });
// 时间快捷选项 // 时间快捷选项
@@ -220,43 +235,37 @@ const columns: TableColumnData[] = [
} }
}, },
{ {
title: '操作类型', title: '变更类型',
dataIndex: 'action', dataIndex: 'changeType',
slotName: 'action', slotName: 'changeType',
width: 180
},
{
title: '变更前状态',
dataIndex: 'statusBefore',
slotName: 'beforeStatus',
width: 100 width: 100
}, },
{ {
title: '操作人', title: '变更后状态',
dataIndex: 'operator', dataIndex: 'statusAfter',
width: 120 slotName: 'afterStatus',
width: 100
}, },
{ {
title: '金额变化', title: '备注说明',
dataIndex: 'amount', dataIndex: 'remark',
slotName: 'amount',
width: 120
},
{
title: '操作后余额',
dataIndex: 'balance',
slotName: 'balance',
width: 120
},
{
title: '操作说明',
dataIndex: 'description',
width: 200, width: 200,
ellipsis: true, ellipsis: true,
tooltip: true tooltip: true
}, },
{ {
title: '详细信息', title: '失败次数',
dataIndex: 'details', dataIndex: 'failureCount',
slotName: 'details',
width: 100 width: 100
}, },
{ {
title: '操作时间', title: '创建时间',
dataIndex: 'createdAt', dataIndex: 'createdAt',
width: 180 width: 180
} }
@@ -338,31 +347,90 @@ const searchHistory = () => {
*/ */
const resetFilter = () => { const resetFilter = () => {
filterForm.dateRange = []; filterForm.dateRange = [];
filterForm.action = undefined; filterForm.changeType = undefined;
searchHistory(); searchHistory();
}; };
/** /**
* 操作类型映射器 * 变更类型映射器
* @param action 操作类型 * 基于后端Go定义的CamelOilAccountChangeType
* @returns 操作类型文本和颜色 * @param changeType 变更类型
* @returns 变更类型文本和颜色
*/ */
const actionMapper = (action: string): { text: string; color: string } => { const changeTypeMapper = (
switch (action) { changeType: string
): { text: string; color: string } => {
switch (changeType) {
case 'create': case 'create':
return { text: '创建', color: 'green' }; return { text: '创建账号', color: 'green' };
case 'login':
return { text: '登录成功', color: 'blue' };
case 'offline':
return { text: '检测到掉线', color: 'orange' };
case 'login_fail':
return { text: '登录失败', color: 'red' };
case 'pause':
return { text: '订单数达到10暂停使用', color: 'orange' };
case 'resume':
return { text: '次日重置,恢复使用', color: 'green' };
case 'invalidate':
return { text: '单日下单不足10个账号失效', color: 'red' };
case 'order_bind':
return { text: '绑定订单', color: 'blue' };
case 'order_complete':
return { text: '订单完成', color: 'green' };
case 'update': case 'update':
return { text: '更新', color: 'blue' }; return { text: '更新账号信息', color: 'blue' };
case 'delete': case 'delete':
return { text: '删除', color: 'red' }; return { text: '删除账号', color: 'red' };
case 'recharge':
return { text: '充值', color: 'green' };
case 'consume':
return { text: '消费', color: 'orange' };
case 'status_change':
return { text: '状态变更', color: 'purple' };
default: default:
return { text: '未知', color: 'gray' }; return { text: '未知变更', color: 'gray' };
}
};
/**
* 状态颜色映射器
* 基于后端Go定义的CamelOilAccountStatus
* @param status 状态值
* @returns 状态颜色
*/
const statusColorMapper = (status: number): string => {
switch (status) {
case 0:
return 'orange'; // 待登录
case 1:
return 'blue'; // 发送验证码
case 2:
return 'green'; // 在线
case 3:
return 'orange'; // 已暂停
case 4:
return 'red'; // 已失效
default:
return 'gray'; // 未知状态
}
};
/**
* 状态文本映射器
* 基于后端Go定义的CamelOilAccountStatus
* @param status 状态值
* @returns 状态文本
*/
const statusMapper = (status: number): { text: string } => {
switch (status) {
case 0:
return { text: '待登录' };
case 1:
return { text: '发送验证码' };
case 2:
return { text: '在线' };
case 3:
return { text: '已暂停' };
case 4:
return { text: '已失效' };
default:
return { text: '未知状态' };
} }
}; };
@@ -393,23 +461,25 @@ watch(
padding: 16px 0; padding: 16px 0;
} }
.amount-positive {
color: #00b42a;
font-weight: 600;
}
.amount-negative {
color: #f53f3f;
font-weight: 600;
}
.balance-text {
color: #165dff;
font-weight: 600;
}
:deep(.arco-modal-body) { :deep(.arco-modal-body) {
max-height: 70vh; max-height: 70vh;
overflow-y: auto; overflow-y: auto;
/* 确保滚动条样式跟随主题 */
scrollbar-width: thin;
scrollbar-color: var(--color-border-3) transparent;
}
:deep(.arco-modal-body::-webkit-scrollbar) {
width: 6px;
}
:deep(.arco-modal-body::-webkit-scrollbar-thumb) {
background-color: var(--color-border-3);
border-radius: 3px;
}
:deep(.arco-modal-body::-webkit-scrollbar-track) {
background-color: transparent;
} }
</style> </style>

View File

@@ -1,135 +0,0 @@
<template>
<a-modal
v-model:visible="modalVisible"
title="订单详情"
width="700px"
:footer="false"
>
<div v-if="order" class="order-detail">
<a-descriptions :column="2" bordered>
<a-descriptions-item label="订单号">
{{ order.orderNo }}
</a-descriptions-item>
<a-descriptions-item label="商户订单号">
{{ order.merchantOrderNo || '-' }}
</a-descriptions-item>
<a-descriptions-item label="订单金额">
<span class="amount-text">¥{{ order.amount }}</span>
</a-descriptions-item>
<a-descriptions-item label="订单状态">
<a-tag :color="statusMapper(order.status).color">
{{ statusMapper(order.status).text }}
</a-tag>
</a-descriptions-item>
<a-descriptions-item label="支付状态">
<a-tag :color="payStatusMapper(order.payStatus).color">
{{ payStatusMapper(order.payStatus).text }}
</a-tag>
</a-descriptions-item>
<a-descriptions-item label="充值账号">
{{ order.accountName }}
</a-descriptions-item>
<a-descriptions-item label="充值手机">
{{ order.accountPhone }}
</a-descriptions-item>
<a-descriptions-item label="回调状态">
<a-tag :color="order.callbackStatus === 1 ? 'green' : 'red'">
{{ order.callbackStatus === 1 ? '成功' : '失败' }}
</a-tag>
</a-descriptions-item>
<a-descriptions-item label="创建时间" :span="2">
{{ order.createdAt }}
</a-descriptions-item>
<a-descriptions-item label="更新时间" :span="2">
{{ order.updatedAt }}
</a-descriptions-item>
<a-descriptions-item label="备注" :span="2">
{{ order.remark || '-' }}
</a-descriptions-item>
</a-descriptions>
</div>
</a-modal>
</template>
<script lang="ts" setup>
import { computed } from 'vue';
// Props 定义
interface Props {
visible: boolean;
order: any;
}
const props = withDefaults(defineProps<Props>(), {
visible: false,
order: null
});
// Emits 定义
interface Emits {
(e: 'update:visible', value: boolean): void;
}
const emit = defineEmits<Emits>();
// 计算属性
const modalVisible = computed({
get: () => props.visible,
set: value => emit('update:visible', value)
});
/**
* 订单状态映射器
* @param status 状态值
* @returns 状态文本和颜色
*/
const statusMapper = (status: number): { text: string; color: string } => {
switch (status) {
case 0:
return { text: '待处理', color: 'orange' };
case 1:
return { text: '成功', color: 'green' };
case 2:
return { text: '失败', color: 'red' };
case 3:
return { text: '已取消', color: 'gray' };
default:
return { text: '未知', color: 'gray' };
}
};
/**
* 支付状态映射器
* @param status 支付状态
* @returns 支付状态文本和颜色
*/
const payStatusMapper = (status: number): { text: string; color: string } => {
switch (status) {
case 0:
return { text: '未支付', color: 'orange' };
case 1:
return { text: '已支付', color: 'green' };
case 2:
return { text: '支付失败', color: 'red' };
default:
return { text: '未知', color: 'gray' };
}
};
</script>
<style scoped>
.order-detail {
padding: 16px 0;
}
.amount-text {
color: #165dff;
font-weight: 600;
font-size: 16px;
}
:deep(.arco-descriptions-item-label) {
font-weight: 600;
background-color: #f7f8fa;
}
</style>

View File

@@ -27,11 +27,11 @@
placeholder="请选择状态" placeholder="请选择状态"
allow-clear allow-clear
> >
<a-option :value="0">失效</a-option> <a-option :value="0">待登录</a-option>
<a-option :value="1">正常</a-option> <a-option :value="1">发送验证码</a-option>
<a-option :value="2">充值过快</a-option> <a-option :value="2">在线</a-option>
<a-option :value="3">账号受限</a-option> <a-option :value="3">已暂停</a-option>
<a-option :value="4">异常</a-option> <a-option :value="4">已失效</a-option>
</a-select> </a-select>
</a-form-item> </a-form-item>
</a-col> </a-col>
@@ -88,8 +88,8 @@
> >
<!-- 状态列模板 --> <!-- 状态列模板 -->
<template #status="{ record }"> <template #status="{ record }">
<a-tag size="small" :color="statusMapper(record.status).color"> <a-tag size="small" :color="statusColorMapper(record.status)">
{{ statusMapper(record.status).text }} {{ record.statusText || statusMapper(record.status).text }}
</a-tag> </a-tag>
</template> </template>
@@ -110,13 +110,6 @@
</template> </template>
</a-button> </a-button>
</a-tooltip> </a-tooltip>
<a-tooltip content="关联订单">
<a-button size="small" @click="showAccountOrders(record)">
<template #icon>
<icon-order />
</template>
</a-button>
</a-tooltip>
</a-space> </a-space>
</template> </template>
</a-table> </a-table>
@@ -133,33 +126,22 @@
v-model:visible="state.historyModalVisible" v-model:visible="state.historyModalVisible"
:account-id="state.accountId" :account-id="state.accountId"
/> />
<!-- 账号关联订单弹窗 -->
<account-orders
v-model:visible="state.ordersModalVisible"
:account-id="state.accountId"
/>
</div> </div>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { computed, onMounted, reactive, ref, watchEffect } from 'vue'; import { onMounted, reactive, ref } from 'vue';
import { Notification, TableColumnData } from '@arco-design/web-vue'; import { Notification, TableColumnData } from '@arco-design/web-vue';
import useLoading from '@/hooks/loading'; import useLoading from '@/hooks/loading';
import { Pagination } from '@/types/global'; import { Pagination } from '@/types/global';
import { checkTokenFromIframe, checkTokenFromLogin } from '@/utils/auth';
import { jdV2AccountClient } from '@/api/index.ts'; import { jdV2AccountClient } from '@/api/index.ts';
import type { import type { KamiApiCamelOilV1AccountListItem } from '@/api/generated/models/index.ts';
KamiApiCamelOilV1AccountListItem,
KamiApiCamelOilV1AccountListItemStatusEnum
} from '@/api/generated/models/index.ts';
import type { import type {
ApiJdV2AccountListGetPageSizeEnum, ApiJdV2AccountListGetPageSizeEnum,
ApiJdV2AccountListGetStatusEnum ApiJdV2AccountListGetStatusEnum
} from '@/api/generated/apis/jdv2-account-api'; } from '@/api/generated/apis/jdv2-account-api';
import AccountDetail from './components/detail.vue'; import AccountDetail from './components/detail.vue';
import AccountHistory from './components/history.vue'; import AccountHistory from './components/history.vue';
import AccountOrders from './components/account-orders.vue';
// 基础分页配置 // 基础分页配置
const basePagination: Pagination = { const basePagination: Pagination = {
@@ -182,13 +164,6 @@ const columns: TableColumnData[] = [
return rowIndex + 1 + (pagination.current - 1) * pagination.pageSize; return rowIndex + 1 + (pagination.current - 1) * pagination.pageSize;
} }
}, },
{
title: '账号ID',
dataIndex: 'accountId',
width: 120,
ellipsis: true,
tooltip: true
},
{ {
title: '账号名称', title: '账号名称',
dataIndex: 'accountName', dataIndex: 'accountName',
@@ -201,21 +176,6 @@ const columns: TableColumnData[] = [
ellipsis: true, ellipsis: true,
tooltip: true tooltip: true
}, },
{
title: '当日订单数',
dataIndex: 'dailyOrderCount',
width: 100
},
{
title: '累计订单数',
dataIndex: 'totalOrderCount',
width: 100
},
{
title: '剩余可下单',
dataIndex: 'remainingOrders',
width: 100
},
{ {
title: '最后使用时间', title: '最后使用时间',
dataIndex: 'lastUsedAt', dataIndex: 'lastUsedAt',
@@ -229,6 +189,20 @@ const columns: TableColumnData[] = [
slotName: 'status', slotName: 'status',
width: 100 width: 100
}, },
{
title: '失败原因',
dataIndex: 'failureReason',
width: 150,
ellipsis: true,
tooltip: true
},
{
title: '备注',
dataIndex: 'remark',
width: 150,
ellipsis: true,
tooltip: true
},
{ {
title: '创建时间', title: '创建时间',
dataIndex: 'createdAt', dataIndex: 'createdAt',
@@ -254,32 +228,12 @@ const generateSearchFormModel = () => ({
status: undefined as ApiJdV2AccountListGetStatusEnum | undefined status: undefined as ApiJdV2AccountListGetStatusEnum | undefined
}); });
// 账号数据模型
const generateAccountModel = () => ({
accountId: '',
accountName: '',
phone: '',
status: undefined as KamiApiCamelOilV1AccountListItemStatusEnum | undefined,
statusText: '',
dailyOrderCount: 0,
totalOrderCount: 0,
remainingOrders: 0,
lastUsedAt: '',
lastLoginAt: '',
tokenExpireAt: '',
failureReason: '',
remark: '',
createdAt: '',
updatedAt: ''
});
const formModel = ref(generateSearchFormModel()); const formModel = ref(generateSearchFormModel());
// 弹窗状态管理 // 弹窗状态管理
const state = reactive({ const state = reactive({
detailModalVisible: false, detailModalVisible: false,
historyModalVisible: false, historyModalVisible: false,
ordersModalVisible: false,
accountId: '', accountId: '',
account: null as KamiApiCamelOilV1AccountListItem | null account: null as KamiApiCamelOilV1AccountListItem | null
}); });
@@ -385,33 +339,54 @@ const showHistoryModal = (record: KamiApiCamelOilV1AccountListItem) => {
}; };
/** /**
* 显示账号关联订单弹窗 * 状态颜色映射器
* @param record 账号记录 * 基于后端Go定义的状态
* - 0: 待登录
* - 1: 发送验证码
* - 2: 在线
* - 3: 已暂停
* - 4: 已失效
* 使用 Arco Design 标准颜色以兼容主题切换
* @param status 状态值
* @returns 状态颜色
*/ */
const showAccountOrders = (record: KamiApiCamelOilV1AccountListItem) => { const statusColorMapper = (status: number): string => {
state.accountId = record.accountId?.toString() || ''; switch (status) {
state.ordersModalVisible = true; case 0:
return 'orange'; // 待登录 - 橙色(等待状态)
case 1:
return 'blue'; // 发送验证码 - 蓝色(进行中状态)
case 2:
return 'green'; // 在线 - 绿色(正常状态)
case 3:
return 'orange'; // 已暂停 - 橙色(警告状态)
case 4:
return 'red'; // 已失效 - 红色(危险状态)
default:
return 'gray'; // 未知状态 - 灰色(中性状态)
}
}; };
/** /**
* 状态映射器 * 状态文本映射器(作为备用)
* 基于后端Go定义的状态
* @param status 状态值 * @param status 状态值
* @returns 状态文本和颜色 * @returns 状态文本
*/ */
const statusMapper = (status: number): { text: string; color: string } => { const statusMapper = (status: number): { text: string } => {
switch (status) { switch (status) {
case 0: case 0:
return { text: '失效', color: 'red' }; return { text: '待登录' }; // 需要登录验证
case 1: case 1:
return { text: '正常', color: 'green' }; return { text: '发送验证码' }; // 正在发送验证码
case 2: case 2:
return { text: '充值过快', color: 'orange' }; return { text: '在线' }; // 账户在线,可用
case 3: case 3:
return { text: '账号受限', color: 'red' }; return { text: '已暂停' }; // 账户被暂停使用
case 4: case 4:
return { text: '异常', color: 'gray' }; return { text: '已失效' }; // 账户已失效
default: default:
return { text: '未知', color: 'gray' }; return { text: '未知状态' };
} }
}; };

View File

@@ -51,14 +51,6 @@
{{ payStatusMapper(order.payStatus).text }} {{ payStatusMapper(order.payStatus).text }}
</a-tag> </a-tag>
</a-descriptions-item> </a-descriptions-item>
<a-descriptions-item label="回调状态">
<a-tag :color="order.callbackStatus === 1 ? 'green' : 'red'">
{{ order.callbackStatus === 1 ? '成功' : '失败' }}
</a-tag>
</a-descriptions-item>
<a-descriptions-item label="回调次数">
{{ order.callbackCount || 0 }}
</a-descriptions-item>
</a-descriptions> </a-descriptions>
</a-card> </a-card>
@@ -68,42 +60,14 @@
<a-descriptions-item label="创建时间"> <a-descriptions-item label="创建时间">
{{ order.createdAt }} {{ order.createdAt }}
</a-descriptions-item> </a-descriptions-item>
<a-descriptions-item label="支付时间"> <a-descriptions-item label="支付完成时间">
{{ order.payTime || '-' }} {{ order.paidAt || '-' }}
</a-descriptions-item>
<a-descriptions-item label="完成时间">
{{ order.completeTime || '-' }}
</a-descriptions-item>
<a-descriptions-item label="最后回调时间">
{{ order.lastCallbackTime || '-' }}
</a-descriptions-item> </a-descriptions-item>
<a-descriptions-item label="更新时间" :span="2"> <a-descriptions-item label="更新时间" :span="2">
{{ order.updatedAt }} {{ order.updatedAt }}
</a-descriptions-item> </a-descriptions-item>
</a-descriptions> </a-descriptions>
</a-card> </a-card>
<!-- 其他信息 -->
<a-card title="其他信息" class="detail-card">
<a-descriptions :column="1" bordered>
<a-descriptions-item label="回调地址">
<code class="callback-url">{{ order.callbackUrl || '-' }}</code>
</a-descriptions-item>
<a-descriptions-item label="请求参数">
<pre class="request-params">{{
formatJson(order.requestParams) || '-'
}}</pre>
</a-descriptions-item>
<a-descriptions-item label="响应参数">
<pre class="response-params">{{
formatJson(order.responseParams) || '-'
}}</pre>
</a-descriptions-item>
<a-descriptions-item label="备注">
{{ order.remark || '-' }}
</a-descriptions-item>
</a-descriptions>
</a-card>
</div> </div>
</a-modal> </a-modal>
</template> </template>
@@ -137,56 +101,43 @@ const modalVisible = computed({
/** /**
* 订单状态映射器 * 订单状态映射器
* 基于后端Go定义的CamelOilOrderStatus
* @param status 状态值 * @param status 状态值
* @returns 状态文本和颜色 * @returns 状态文本和颜色
*/ */
const statusMapper = (status: number): { text: string; color: string } => { const statusMapper = (status: number): { text: string; color: string } => {
switch (status) { switch (status) {
case 0: case 0:
return { text: '待处理', color: 'orange' }; return { text: '待处理', color: 'orange' }; // CamelOilOrderStatusPending
case 1: case 1:
return { text: '成功', color: 'green' }; return { text: '处理中', color: 'blue' }; // CamelOilOrderStatusProcessing
case 2: case 2:
return { text: '失败', color: 'red' }; return { text: '已完成', color: 'green' }; // CamelOilOrderStatusCompleted
case 3: case 3:
return { text: '已取消', color: 'gray' }; return { text: '已失败', color: 'red' }; // CamelOilOrderStatusFailed
default: default:
return { text: '未知', color: 'gray' }; return { text: '未知状态', color: 'gray' };
} }
}; };
/** /**
* 支付状态映射器 * 支付状态映射器
* 基于后端Go定义的CamelOilPayStatus
* @param status 支付状态 * @param status 支付状态
* @returns 支付状态文本和颜色 * @returns 支付状态文本和颜色
*/ */
const payStatusMapper = (status: number): { text: string; color: string } => { const payStatusMapper = (status: number): { text: string; color: string } => {
switch (status) { switch (status) {
case 0: case 0:
return { text: '未支付', color: 'orange' }; return { text: '未支付', color: 'orange' }; // CamelOilPaymentStatusUnpaid
case 1: case 1:
return { text: '已支付', color: 'green' }; return { text: '已支付', color: 'green' }; // CamelOilPaymentStatusPaid
case 2: case 2:
return { text: '支付失败', color: 'red' }; return { text: '已退款', color: 'blue' }; // CamelOilPaymentStatusRefunded
case 3:
return { text: '已超时', color: 'red' }; // CamelOilPaymentStatusTimeout
default: default:
return { text: '未知', color: 'gray' }; return { text: '未知状态', color: 'gray' };
}
};
/**
* 格式化JSON字符串
* @param json JSON对象或字符串
* @returns 格式化后的字符串
*/
const formatJson = (json: any): string => {
if (!json) return '';
try {
if (typeof json === 'string') {
return JSON.stringify(JSON.parse(json), null, 2);
}
return JSON.stringify(json, null, 2);
} catch {
return String(json);
} }
}; };
</script> </script>
@@ -205,48 +156,41 @@ const formatJson = (json: any): string => {
} }
.amount-text { .amount-text {
color: #165dff; color: rgb(var(--primary-6));
font-weight: 600; font-weight: 600;
font-size: 16px; font-size: 16px;
} }
.actual-amount-text { .actual-amount-text {
color: #00b42a; color: rgb(var(--success-6));
font-weight: 600; font-weight: 600;
font-size: 16px; font-size: 16px;
} }
.callback-url {
background-color: #f7f8fa;
padding: 4px 8px;
border-radius: 4px;
font-family: Monaco, Menlo, 'Ubuntu Mono', monospace;
font-size: 12px;
word-break: break-all;
}
.request-params,
.response-params {
background-color: #f7f8fa;
padding: 12px;
border-radius: 6px;
font-family: Monaco, Menlo, 'Ubuntu Mono', monospace;
font-size: 12px;
line-height: 1.4;
white-space: pre-wrap;
word-break: break-all;
max-height: 200px;
overflow-y: auto;
margin: 0;
}
:deep(.arco-descriptions-item-label) { :deep(.arco-descriptions-item-label) {
font-weight: 600; font-weight: 600;
background-color: #f7f8fa; background-color: var(--color-fill-2);
} }
:deep(.arco-modal-body) { :deep(.arco-modal-body) {
max-height: 80vh; max-height: 80vh;
overflow-y: auto; overflow-y: auto;
/* 确保滚动条样式跟随主题 */
scrollbar-width: thin;
scrollbar-color: var(--color-border-3) transparent;
}
:deep(.arco-modal-body::-webkit-scrollbar) {
width: 6px;
}
:deep(.arco-modal-body::-webkit-scrollbar-thumb) {
background-color: var(--color-border-3);
border-radius: 3px;
}
:deep(.arco-modal-body::-webkit-scrollbar-track) {
background-color: transparent;
} }
</style> </style>

View File

@@ -21,21 +21,13 @@
</a-form-item> </a-form-item>
</a-col> </a-col>
<a-col :span="8"> <a-col :span="8">
<a-form-item field="merchantOrderNo" label="商户订单号"> <a-form-item field="merchantOrderId" label="商户订单号">
<a-input <a-input
v-model="formModel.merchantOrderNo" v-model="formModel.merchantOrderId"
placeholder="请输入商户订单号" placeholder="请输入商户订单号"
/> />
</a-form-item> </a-form-item>
</a-col> </a-col>
<a-col :span="8">
<a-form-item field="accountPhone" label="充值手机">
<a-input
v-model="formModel.accountPhone"
placeholder="请输入充值手机号"
/>
</a-form-item>
</a-col>
<a-col :span="8"> <a-col :span="8">
<a-form-item field="status" label="订单状态"> <a-form-item field="status" label="订单状态">
<a-select <a-select
@@ -44,9 +36,9 @@
allow-clear allow-clear
> >
<a-option :value="0">待处理</a-option> <a-option :value="0">待处理</a-option>
<a-option :value="1">成功</a-option> <a-option :value="1">处理中</a-option>
<a-option :value="2">失败</a-option> <a-option :value="2">已完成</a-option>
<a-option :value="3">取消</a-option> <a-option :value="3">失败</a-option>
</a-select> </a-select>
</a-form-item> </a-form-item>
</a-col> </a-col>
@@ -140,8 +132,8 @@
<!-- 回调状态模板 --> <!-- 回调状态模板 -->
<template #callbackStatus="{ record }"> <template #callbackStatus="{ record }">
<a-tag :color="record.callbackStatus === 1 ? 'green' : 'red'"> <a-tag :color="callbackStatusMapper(record.notifyStatus).color">
{{ record.callbackStatus === 1 ? '成功' : '失败' }} {{ callbackStatusMapper(record.notifyStatus).text }}
</a-tag> </a-tag>
</template> </template>
@@ -198,6 +190,11 @@ import type {
ApiJdV2OrderListGetStatusEnum, ApiJdV2OrderListGetStatusEnum,
ApiJdV2OrderListGetPayStatusEnum ApiJdV2OrderListGetPayStatusEnum
} from '@/api/generated/apis/jdv2-order-api'; } from '@/api/generated/apis/jdv2-order-api';
import type {
KamiApiCamelOilV1OrderListItemStatusEnum,
KamiApiCamelOilV1OrderListItemPayStatusEnum,
KamiApiCamelOilV1OrderListItemNotifyStatusEnum
} from '@/api/generated/models/index.ts';
import OrderDetailModal from './components/order-detail-modal.vue'; import OrderDetailModal from './components/order-detail-modal.vue';
import OrderHistoryModal from './components/order-history-modal.vue'; import OrderHistoryModal from './components/order-history-modal.vue';
@@ -280,14 +277,9 @@ const columns: TableColumnData[] = [
tooltip: true tooltip: true
}, },
{ {
title: '账号ID', title: '账号名称',
dataIndex: 'accountId', dataIndex: 'accountName',
width: 120 width: 150
},
{
title: '充值手机',
dataIndex: 'accountPhone',
width: 130
}, },
{ {
title: '订单金额', title: '订单金额',
@@ -309,18 +301,27 @@ const columns: TableColumnData[] = [
}, },
{ {
title: '回调状态', title: '回调状态',
dataIndex: 'callbackStatus', dataIndex: 'notifyStatus',
slotName: 'callbackStatus', slotName: 'callbackStatus',
width: 100 width: 100
}, },
{ {
title: '创建时间', title: '支付完成时间',
dataIndex: 'createdAt', dataIndex: 'paidAt',
width: 180 width: 180,
ellipsis: true,
tooltip: true
}, },
{ {
title: '更新时间', title: '失败原因',
dataIndex: 'updatedAt', dataIndex: 'failureReason',
width: 150,
ellipsis: true,
tooltip: true
},
{
title: '创建时间',
dataIndex: 'createdAt',
width: 180 width: 180
}, },
{ {
@@ -335,8 +336,7 @@ const columns: TableColumnData[] = [
// 表单模型 // 表单模型
const generateFormModel = () => ({ const generateFormModel = () => ({
orderNo: '', orderNo: '',
merchantOrderNo: '', merchantOrderId: '',
accountPhone: '',
status: undefined as ApiJdV2OrderListGetStatusEnum | undefined, status: undefined as ApiJdV2OrderListGetStatusEnum | undefined,
payStatus: undefined as ApiJdV2OrderListGetPayStatusEnum | undefined, payStatus: undefined as ApiJdV2OrderListGetPayStatusEnum | undefined,
dateRange: [] as Date[] dateRange: [] as Date[]
@@ -456,39 +456,64 @@ const showOrderHistory = (record: KamiApiCamelOilV1OrderListItem) => {
/** /**
* 订单状态映射器 * 订单状态映射器
* 基于后端Go定义的CamelOilOrderStatus
* @param status 状态值 * @param status 状态值
* @returns 状态文本和颜色 * @returns 状态文本和颜色
*/ */
const statusMapper = (status: number): { text: string; color: string } => { const statusMapper = (status: number): { text: string; color: string } => {
switch (status) { switch (status) {
case 0: case 0:
return { text: '待处理', color: 'orange' }; return { text: '待处理', color: 'orange' }; // CamelOilOrderStatusPending
case 1: case 1:
return { text: '成功', color: 'green' }; return { text: '处理中', color: 'blue' }; // CamelOilOrderStatusProcessing
case 2: case 2:
return { text: '失败', color: 'red' }; return { text: '已完成', color: 'green' }; // CamelOilOrderStatusCompleted
case 3: case 3:
return { text: '已取消', color: 'gray' }; return { text: '已失败', color: 'red' }; // CamelOilOrderStatusFailed
default: default:
return { text: '未知', color: 'gray' }; return { text: '未知状态', color: 'gray' };
} }
}; };
/** /**
* 支付状态映射器 * 支付状态映射器
* 基于后端Go定义的CamelOilPayStatus
* @param status 支付状态 * @param status 支付状态
* @returns 支付状态文本和颜色 * @returns 支付状态文本和颜色
*/ */
const payStatusMapper = (status: number): { text: string; color: string } => { const payStatusMapper = (status: number): { text: string; color: string } => {
switch (status) { switch (status) {
case 0: case 0:
return { text: '未支付', color: 'orange' }; return { text: '未支付', color: 'orange' }; // CamelOilPaymentStatusUnpaid
case 1: case 1:
return { text: '已支付', color: 'green' }; return { text: '已支付', color: 'green' }; // CamelOilPaymentStatusPaid
case 2: case 2:
return { text: '支付失败', color: 'red' }; return { text: '已退款', color: 'blue' }; // CamelOilPaymentStatusRefunded
case 3:
return { text: '已超时', color: 'red' }; // CamelOilPaymentStatusTimeout
default: default:
return { text: '未知', color: 'gray' }; return { text: '未知状态', color: 'gray' };
}
};
/**
* 回调状态映射器
* 基于后端Go定义的CamelOilNotifyStatus
* @param status 回调状态
* @returns 回调状态文本和颜色
*/
const callbackStatusMapper = (
status: number
): { text: string; color: string } => {
switch (status) {
case 0:
return { text: '未回调', color: 'orange' }; // CamelOilCallbackStatusPending
case 1:
return { text: '回调成功', color: 'green' }; // CamelOilCallbackStatusSuccess
case 2:
return { text: '回调失败', color: 'red' }; // CamelOilCallbackStatusFailed
default:
return { text: '未知状态', color: 'gray' };
} }
}; };
@@ -516,7 +541,7 @@ onMounted(() => {
} }
.amount-text { .amount-text {
color: #165dff; color: rgb(var(--primary-6));
font-weight: 600; font-weight: 600;
} }
</style> </style>

View File

@@ -31,6 +31,13 @@
show-word-limit show-word-limit
/> />
</a-form-item> </a-form-item>
<a-form-item field="phone" label="绑定手机号" required>
<a-input
v-model="formModel.phone"
placeholder="请输入绑定的手机号"
:max-length="11"
/>
</a-form-item>
<a-form-item field="remark" label="备注"> <a-form-item field="remark" label="备注">
<a-textarea <a-textarea
v-model="formModel.remark" v-model="formModel.remark"
@@ -74,6 +81,7 @@ const generateFormModel = () => {
return { return {
tokenName: '', tokenName: '',
tokenValue: '', tokenValue: '',
phone: '',
remark: '' remark: ''
}; };
}; };
@@ -89,6 +97,10 @@ const rules = {
{ required: true, message: '请输入Token值' }, { required: true, message: '请输入Token值' },
{ min: 1, max: 1000, message: 'Token值长度为1-1000个字符' } { min: 1, max: 1000, message: 'Token值长度为1-1000个字符' }
], ],
phone: [
{ required: true, message: '请输入绑定手机号' },
{ match: /^1[3-9]\d{9}$/, message: '请输入正确的手机号码' }
],
remark: [{ max: 500, message: '备注长度不能超过500个字符' }] remark: [{ max: 500, message: '备注长度不能超过500个字符' }]
}; };
@@ -144,8 +156,26 @@ const handleBeforeOk = async () => {
</script> </script>
<style scoped> <style scoped>
/* 使用 Arco Design 的原生样式,确保主题兼容性 */
:deep(.arco-modal-body) { :deep(.arco-modal-body) {
max-height: 60vh; max-height: 60vh;
overflow-y: auto; overflow-y: auto;
/* 确保滚动条样式跟随主题 */
scrollbar-width: thin;
scrollbar-color: var(--color-border-3) transparent;
}
:deep(.arco-modal-body::-webkit-scrollbar) {
width: 6px;
}
:deep(.arco-modal-body::-webkit-scrollbar-thumb) {
background-color: var(--color-border-3);
border-radius: 3px;
}
:deep(.arco-modal-body::-webkit-scrollbar-track) {
background-color: transparent;
} }
</style> </style>

View File

@@ -211,8 +211,26 @@ watch(
</script> </script>
<style scoped> <style scoped>
/* 使用 Arco Design 的原生样式,确保主题兼容性 */
:deep(.arco-modal-body) { :deep(.arco-modal-body) {
max-height: 70vh; max-height: 70vh;
overflow-y: auto; overflow-y: auto;
/* 确保滚动条样式跟随主题 */
scrollbar-width: thin;
scrollbar-color: var(--color-border-3) transparent;
}
:deep(.arco-modal-body::-webkit-scrollbar) {
width: 6px;
}
:deep(.arco-modal-body::-webkit-scrollbar-thumb) {
background-color: var(--color-border-3);
border-radius: 3px;
}
:deep(.arco-modal-body::-webkit-scrollbar-track) {
background-color: transparent;
} }
</style> </style>

View File

@@ -178,12 +178,31 @@ const columns: TableColumnData[] = [
width: 150 width: 150
}, },
{ {
title: 'Token', title: 'Token',
dataIndex: 'token', dataIndex: 'tokenValue',
width: 300, width: 300,
ellipsis: true, ellipsis: true,
tooltip: true tooltip: true
}, },
{
title: '绑定手机号',
dataIndex: 'phone',
width: 130,
ellipsis: true,
tooltip: true
},
{
title: '绑定数量',
dataIndex: 'bindCount',
width: 100
},
{
title: '最后使用时间',
dataIndex: 'lastUsedAt',
width: 180,
ellipsis: true,
tooltip: true
},
{ {
title: '状态', title: '状态',
dataIndex: 'status', dataIndex: 'status',
@@ -191,20 +210,17 @@ const columns: TableColumnData[] = [
width: 100 width: 100
}, },
{ {
title: '创建人', title: '备注',
dataIndex: 'createdBy', dataIndex: 'remark',
width: 120 width: 150,
ellipsis: true,
tooltip: true
}, },
{ {
title: '创建时间', title: '创建时间',
dataIndex: 'createdAt', dataIndex: 'createdAt',
width: 180 width: 180
}, },
{
title: '更新时间',
dataIndex: 'updatedAt',
width: 180
},
{ {
title: '操作', title: '操作',
dataIndex: 'operations', dataIndex: 'operations',
@@ -342,4 +358,8 @@ onMounted(() => {
.container { .container {
padding: 0 20px 20px; padding: 0 20px 20px;
} }
.general-card {
min-height: calc(100vh - 170px);
}
</style> </style>