refactor(apple-card-info): 将苹果账户管理页面拆分为账户管理和历史记录两个子页面

- 新增 card-info 和 card-info-history 两个组件
- 在 index.vue 中使用 radio 按钮切换两个子页面
- 移除 index.vue 中的冗余代码,简化结构
This commit is contained in:
danial
2025-02-09 18:27:49 +08:00
parent 26c7739c9a
commit a6d6760300
3 changed files with 579 additions and 445 deletions

View File

@@ -0,0 +1,116 @@
<template>
<div>
<a-row>
<a-col :flex="1">
<a-form
:model="formModel"
:label-col-props="{ span: 6 }"
:wrapper-col-props="{ span: 18 }"
label-align="left"
>
<a-row :gutter="16">
<a-col :span="8">
<a-form-item field="account" label="账号">
<a-input v-model="formModel.account" placeholder="请输入账号" />
</a-form-item>
</a-col>
</a-row>
</a-form>
</a-col>
<a-divider style="height: 42px" direction="vertical" />
<a-col flex="172px" style="text-align: right">
<a-space direction="horizontal" :size="18">
<a-button type="primary" @click="search">
<template #icon>
<icon-search />
</template>
搜索
</a-button>
<a-button @click="reset">
<template #icon>
<icon-refresh />
</template>
重置
</a-button>
</a-space>
</a-col>
</a-row>
<a-divider style="margin-top: 0" />
<a-table
:loading="loading"
:pagination="{
current: pagination.current,
pageSize: pagination.pageSize,
total: pagination.total,
pageSizeOptions: [10, 20, 50, 100],
showPageSize: true
}"
:columns="columns"
:data="renderData"
:scroll="{ x: 1080 }"
@page-change="onPageChange"
@page-size-change="onPageSizeChange"
>
<template #index="{ rowIndex }">
{{ rowIndex + 1 + (pagination.current - 1) * pagination.pageSize }}
</template>
</a-table>
</div>
</template>
<script lang="ts" setup>
import { apiClient } from '@/api';
import {
ApiCardInfoAppleCardAccountGetWalletListGetPageSizeEnum,
KamiApiCardInfoAppleV1CardHistoryModel
} from '@/api/generated';
import useLoading from '@/hooks/loading';
import { Pagination } from '@/types/global';
import { reactive, ref } from 'vue';
const columns = [{}];
const formModel = reactive({
account: ''
});
const basePagination: Pagination = {
current: 1,
pageSize: 20
};
const pagination = reactive({
...basePagination
});
const reset = () => {
formModel.account = '';
pagination.current = 1;
search();
};
const { loading, setLoading } = useLoading(false);
const renderData = ref<KamiApiCardInfoAppleV1CardHistoryModel[]>([]);
const search = async () => {
try {
setLoading(true);
const result = await apiClient.apiCardInfoAppleCardAccountGetWalletListGet(
pagination.current,
pagination.pageSize as ApiCardInfoAppleCardAccountGetWalletListGetPageSizeEnum,
formModel.account
);
renderData.value = result.data.list;
pagination.total = result.data.total;
} finally {
setLoading(false);
}
};
const onPageChange = (page: number) => {
pagination.current = page;
search();
};
const onPageSizeChange = (pageSize: number) => {
pagination.pageSize = pageSize;
search();
};
</script>

View File

@@ -0,0 +1,451 @@
<template>
<div>
<a-row>
<a-col :flex="1">
<a-form
:model="formModel"
:label-col-props="{ span: 6 }"
:wrapper-col-props="{ span: 18 }"
label-align="left"
>
<a-row :gutter="16">
<a-col :span="8">
<a-form-item field="account" label="账号">
<a-input v-model="formModel.account" placeholder="请输入账号" />
</a-form-item>
</a-col>
</a-row>
</a-form>
</a-col>
<a-divider style="height: 42px" direction="vertical" />
<a-col flex="172px" style="text-align: right">
<a-space direction="horizontal" :size="18">
<a-button type="primary" @click="search">
<template #icon>
<icon-search />
</template>
搜索
</a-button>
<a-button @click="reset">
<template #icon>
<icon-refresh />
</template>
重置
</a-button>
</a-space>
</a-col>
</a-row>
<a-divider style="margin-top: 0" />
<a-row style="margin-bottom: 16px">
<a-col :span="12">
<a-space>
<a-button
type="primary"
size="small"
@click="showAddModel({ account: '', password: '', id: '' })"
>
<template #icon>
<icon-plus />
</template>
添加账号
</a-button>
<a-button size="small" @click="downloadAccountTemplte">
<template #icon>
<icon-download />
</template>
下载模板
</a-button>
<a-upload
size="small"
:action="`${getAPIBaseUrl()}/api/cardInfo/AppleCard/account/batchAdd`"
:limit="1"
accept="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
:headers="{
Authorization: `Bearer ${getToken()}`,
tokenFrom: getTokenFrom()
}"
@success="uploadSucceed"
/>
</a-space>
</a-col>
</a-row>
<a-table
:loading="loading"
:pagination="{
current: pagination.current,
pageSize: pagination.pageSize,
total: pagination.total,
pageSizeOptions: [10, 20, 50, 100],
showPageSize: true
}"
:columns="columns"
:data="renderData"
:scroll="{ x: 1080 }"
@page-change="onPageChange"
@page-size-change="onPageSizeChange"
>
<template #index="{ rowIndex }">
{{ rowIndex + 1 + (pagination.current - 1) * pagination.pageSize }}
</template>
<template #status="{ record }">
<a-switch
v-if="[1, 2].includes(record.status)"
v-model="record.status"
:checked-value="2"
:unchecked-value="1"
checked-text="正常"
unchecked-text="禁用"
@change="
async () => {
await continueOrSuspendRechargeAPI({ id: record.id });
search();
}
"
/>
<a-space v-else size="small">
<a-tag
v-for="(m, index) in getStatusColorMap(record.status)"
:key="index"
:color="m.color"
>
{{ m.text }}
</a-tag>
</a-space>
</template>
<template #operations="{ record }">
<a-space size="small">
<a-tooltip content="修改">
<a-button
size="small"
status="warning"
@click="showAddModel(record)"
>
<template #icon>
<icon-pen />
</template>
</a-button>
</a-tooltip>
<a-tooltip content="详情">
<a-button size="small" @click="showDetailModel(record)">
<template #icon>
<icon-list />
</template>
</a-button>
</a-tooltip>
<a-tooltip content="删除">
<a-button
v-if="checkTokenFromIframe()"
size="small"
status="danger"
@click="deleteAppleCardHandler(record.id)"
>
<template #icon>
<icon-delete />
</template>
</a-button>
</a-tooltip>
</a-space>
</template>
</a-table>
<add-modal
:id="state.accountId"
v-model:visible="state.addModalVisible"
:account="state.account"
/>
<account-detail
v-model:visible="state.accountDetailVisible"
:accountId="state.accountId"
/>
</div>
</template>
<script lang="ts" setup>
import useLoading from '@/hooks/loading';
import { Pagination } from '@/types/global';
import { onMounted, reactive, ref, watch } from 'vue';
import {
AppleCardParams,
continueOrSuspendRechargeAPI,
deleteAppleCard,
downloadAppleAccountTemplteAPI
} from '@/api/apple-card-info';
import { FileItem, Notification, TableColumnData } from '@arco-design/web-vue';
import { getToken, checkTokenFromIframe, getTokenFrom } from '@/utils/auth';
import { getAPIBaseUrl } from '@/api/utils';
import AddModal from './components/add-modal.vue';
import AccountDetail from './components/detail.vue';
import { apiClient } from '@/api';
import {
ApiCardInfoAppleCardAccountGetListGetPageSizeEnum,
KamiApiCardInfoAppleV1AppleCardListRecord
} from '@/api/generated';
const basePagination: Pagination = {
current: 1,
pageSize: 20
};
const pagination = reactive({
...basePagination
});
const columns: TableColumnData[] = [
{
title: '序号',
dataIndex: 'index',
slotName: 'index',
width: 50,
minWidth: 40
},
{
title: '账号',
dataIndex: 'account'
},
{
title: '密码',
dataIndex: 'password'
},
{
title: '余额',
dataIndex: 'balanceItunes',
ellipsis: true,
tooltip: true
},
{
title: '总充值金额',
dataIndex: 'balance',
ellipsis: true,
tooltip: true
},
{
title: '今日充值金额',
dataIndex: 'todayRechargeAmount',
ellipsis: true,
tooltip: true
},
{
title: '今日充值笔数',
dataIndex: 'todayRechargeCount',
ellipsis: true,
tooltip: true
},
{
title: '充值限制金额',
dataIndex: 'maxAmountLimit',
ellipsis: true,
tooltip: true
},
{
title: '充值限制次数',
dataIndex: 'maxCountLimit',
ellipsis: true,
tooltip: true
},
{
title: '上传人',
dataIndex: 'uploadUser.username',
slotName: 'uploadUser'
},
{
title: '最后充值时间',
dataIndex: 'todayRechargeDatetime',
ellipsis: true,
tooltip: true
},
{
title: '创建时间',
dataIndex: 'createdAt',
slotName: 'createdAt'
},
{
title: '状态',
dataIndex: 'status',
slotName: 'status',
align: 'center',
width: 110
},
{
title: '操作',
dataIndex: 'operations',
slotName: 'operations',
fixed: 'right',
width: 220
}
];
const generateFormModel = () => {
return {
account: '',
password: ''
};
};
const { loading, setLoading } = useLoading(true);
const renderData = ref<KamiApiCardInfoAppleV1AppleCardListRecord[]>([]);
const formModel = ref(generateFormModel());
const state = reactive({
addModalVisible: false,
deployModalVisible: false,
accountDetailVisible: false,
accountId: '',
account: {
account: '',
password: '',
maxAmountLimit: 0,
maxCountLimit: 0
}
});
const fetchData = async (
params: AppleCardParams = { current: 1, pageSize: 20 }
) => {
setLoading(true);
try {
const {
data: { list, total }
} = await apiClient.apiCardInfoAppleCardAccountGetListGet(
params.current,
params.pageSize as unknown as ApiCardInfoAppleCardAccountGetListGetPageSizeEnum,
params.account
);
renderData.value = list;
pagination.current = params.current;
pagination.pageSize = params.pageSize;
pagination.total = total;
} catch (err) {
// you can report use errorHandler or other
} finally {
setLoading(false);
}
};
const onPageChange = (current: number) => {
fetchData({ ...pagination, current });
};
const onPageSizeChange = (pageSize: number) => {
fetchData({ ...pagination, pageSize });
};
const search = () => {
fetchData({
...basePagination,
...formModel.value
} as unknown as AppleCardParams);
};
const reset = () => {
formModel.value = generateFormModel();
};
const deleteAppleCardHandler = async (id: string) => {
try {
await deleteAppleCard({ id });
} catch {
Notification.error({
id: 'appleAccountNotice',
content: '删除苹果卡失败',
closable: true
});
} finally {
fetchData({ ...pagination });
}
};
const showAddModel = (record: KamiApiCardInfoAppleV1AppleCardListRecord) => {
state.addModalVisible = true;
state.accountId = record.id;
state.account = {
account: record.account,
password: record.password,
maxAmountLimit: record.maxAmountLimit,
maxCountLimit: record.maxCountLimit
};
};
const showDetailModel = (record: KamiApiCardInfoAppleV1AppleCardListRecord) => {
state.accountDetailVisible = true;
state.accountId = record.id;
};
watch(
() => state.addModalVisible,
val => {
if (!val) {
search();
}
}
);
const getStatusColorMap = (status: number) => {
type colorMap = {
color: string;
text: string;
};
const c: colorMap[] = [];
switch (status) {
case 1:
c.push();
break;
case 2:
c.push({
color: 'green',
text: '正常'
});
break;
case 3:
c.push({
color: 'red',
text: '账/密错误'
});
break;
case 4:
c.push({
color: 'orange',
text: '额度超出'
});
break;
case 5:
c.push({
color: 'pinkpurple',
text: '50元限制'
});
break;
case 6:
c.push({
color: 'magenta',
text: '安全登录限制'
});
break;
case 7:
c.push({
color: 'pinkpurple',
text: '禁用(2分钟)'
});
break;
case 8:
c.push({
color: 'red',
text: '禁用'
});
break;
case 9:
c.push({
color: 'red',
text: '禁用(1分钟)'
});
break;
default:
c.push({
color: 'grey',
text: '其他状态,待更新'
});
break;
}
return c;
};
const downloadAccountTemplte = () => {
downloadAppleAccountTemplteAPI();
};
const uploadSucceed = (fileItem: FileItem) => {
Notification.success({
id: 'appleAccountNotice',
content: fileItem.response?.data?.msg || '上传成功',
closable: true
});
search();
};
onMounted(() => {
fetchData();
});
</script>

View File

@@ -2,456 +2,23 @@
<div class="container">
<Breadcrumb :items="['充值账户管理', '苹果账户管理']" />
<a-card class="general-card" title="苹果账户管理">
<a-row>
<a-col :flex="1">
<a-form
:model="formModel"
:label-col-props="{ span: 6 }"
:wrapper-col-props="{ span: 18 }"
label-align="left"
>
<a-row :gutter="16">
<a-col :span="8">
<a-form-item field="account" label="账号">
<a-input
v-model="formModel.account"
placeholder="请输入账号"
/>
</a-form-item>
</a-col>
</a-row>
</a-form>
</a-col>
<a-divider style="height: 42px" direction="vertical" />
<a-col flex="172px" style="text-align: right">
<a-space direction="horizontal" :size="18">
<a-button type="primary" @click="search">
<template #icon>
<icon-search />
</template>
搜索
</a-button>
<a-button @click="reset">
<template #icon>
<icon-refresh />
</template>
重置
</a-button>
</a-space>
</a-col>
</a-row>
<a-divider style="margin-top: 0" />
<a-row style="margin-bottom: 16px">
<a-col :span="12">
<a-space>
<a-button
type="primary"
size="small"
@click="showAddModel({ account: '', password: '', id: '' })"
>
<template #icon>
<icon-plus />
</template>
添加账号
</a-button>
<a-button size="small" @click="downloadAccountTemplte">
<template #icon>
<icon-download />
</template>
下载模板
</a-button>
<a-upload
size="small"
:action="`${getAPIBaseUrl()}/api/cardInfo/AppleCard/account/batchAdd`"
:limit="1"
accept="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
:headers="{
Authorization: `Bearer ${getToken()}`,
tokenFrom: getTokenFrom()
}"
@success="uploadSucceed"
/>
</a-space>
</a-col>
</a-row>
<a-table
:loading="loading"
:pagination="{
current: pagination.current,
pageSize: pagination.pageSize,
total: pagination.total,
pageSizeOptions: [10, 20, 50, 100],
showPageSize: true
}"
:columns="columns"
:data="renderData"
:scroll="{ x: 1080 }"
@page-change="onPageChange"
@page-size-change="onPageSizeChange"
>
<template #index="{ rowIndex }">
{{ rowIndex + 1 + (pagination.current - 1) * pagination.pageSize }}
</template>
<template #status="{ record }">
<a-switch
v-if="[1, 2].includes(record.status)"
v-model="record.status"
:checked-value="2"
:unchecked-value="1"
checked-text="正常"
unchecked-text="禁用"
@change="
async () => {
await continueOrSuspendRechargeAPI({ id: record.id });
search();
}
"
/>
<a-space v-else size="small">
<a-tag
v-for="(m, index) in getStatusColorMap(record.status)"
:key="index"
:color="m.color"
>
{{ m.text }}
</a-tag>
</a-space>
</template>
<template #operations="{ record }">
<a-space size="small">
<a-tooltip content="修改">
<a-button
size="small"
status="warning"
@click="showAddModel(record)"
>
<template #icon>
<icon-pen />
</template>
</a-button>
</a-tooltip>
<a-tooltip content="详情">
<a-button size="small" @click="showDetailModel(record)">
<template #icon>
<icon-list />
</template>
</a-button>
</a-tooltip>
<a-tooltip content="删除">
<a-button
v-if="checkTokenFromIframe()"
size="small"
status="danger"
@click="deleteAppleCardHandler(record.id)"
>
<template #icon>
<icon-delete />
</template>
</a-button>
</a-tooltip>
</a-space>
</template>
</a-table>
<template #extra>
<a-radio-group v-model="state.module" type="button">
<a-radio value="info">账户管理</a-radio>
<a-radio value="history">历史记录</a-radio>
</a-radio-group>
</template>
<card-info v-if="state.module === 'info'" />
<card-info-history v-if="state.module === 'history'" />
</a-card>
<add-modal
:id="state.accountId"
v-model:visible="state.addModalVisible"
:account="state.account"
/>
<account-detail
v-model:visible="state.accountDetailVisible"
:accountId="state.accountId"
/>
</div>
</template>
<script lang="ts" setup>
import useLoading from '@/hooks/loading';
import { Pagination } from '@/types/global';
import { onMounted, reactive, ref, watch } from 'vue';
import {
AppleCardParams,
continueOrSuspendRechargeAPI,
deleteAppleCard,
downloadAppleAccountTemplteAPI
} from '@/api/apple-card-info';
import { FileItem, Notification, TableColumnData } from '@arco-design/web-vue';
import { getToken, checkTokenFromIframe, getTokenFrom } from '@/utils/auth';
import { getAPIBaseUrl } from '@/api/utils';
import AddModal from './components/add-modal.vue';
import AccountDetail from './components/detail.vue';
import { apiClient } from '@/api';
import {
ApiCardInfoAppleCardAccountGetListGetPageSizeEnum,
KamiApiCardInfoAppleV1AppleCardListRecord
} from '@/api/generated';
const basePagination: Pagination = {
current: 1,
pageSize: 20
};
const pagination = reactive({
...basePagination
});
const columns: TableColumnData[] = [
{
title: '序号',
dataIndex: 'index',
slotName: 'index',
width: 50,
minWidth: 40
},
{
title: '账号',
dataIndex: 'account'
},
{
title: '密码',
dataIndex: 'password'
},
{
title: '余额',
dataIndex: 'balanceItunes',
ellipsis: true,
tooltip: true
},
{
title: '总充值金额',
dataIndex: 'balance',
ellipsis: true,
tooltip: true
},
{
title: '今日充值金额',
dataIndex: 'todayRechargeAmount',
ellipsis: true,
tooltip: true
},
{
title: '今日充值笔数',
dataIndex: 'todayRechargeCount',
ellipsis: true,
tooltip: true
},
{
title: '充值限制金额',
dataIndex: 'maxAmountLimit',
ellipsis: true,
tooltip: true
},
{
title: '充值限制次数',
dataIndex: 'maxCountLimit',
ellipsis: true,
tooltip: true
},
{
title: '上传人',
dataIndex: 'uploadUser.username',
slotName: 'uploadUser'
},
{
title: '最后充值时间',
dataIndex: 'todayRechargeDatetime',
ellipsis: true,
tooltip: true
},
{
title: '创建时间',
dataIndex: 'createdAt',
slotName: 'createdAt'
},
{
title: '状态',
dataIndex: 'status',
slotName: 'status',
align: 'center',
width: 110
},
{
title: '操作',
dataIndex: 'operations',
slotName: 'operations',
fixed: 'right',
width: 220
}
];
const generateFormModel = () => {
return {
account: '',
password: ''
};
};
const { loading, setLoading } = useLoading(true);
const renderData = ref<KamiApiCardInfoAppleV1AppleCardListRecord[]>([]);
const formModel = ref(generateFormModel());
import { reactive } from 'vue';
import cardInfo from './card-info.vue';
import cardInfoHistory from './card-info-history.vue';
const state = reactive({
addModalVisible: false,
deployModalVisible: false,
accountDetailVisible: false,
accountId: '',
account: {
account: '',
password: '',
maxAmountLimit: 0,
maxCountLimit: 0
}
});
const fetchData = async (
params: AppleCardParams = { current: 1, pageSize: 20 }
) => {
setLoading(true);
try {
const {
data: { list, total }
} = await apiClient.apiCardInfoAppleCardAccountGetListGet(
params.current,
params.pageSize as unknown as ApiCardInfoAppleCardAccountGetListGetPageSizeEnum,
params.account
);
renderData.value = list;
pagination.current = params.current;
pagination.pageSize = params.pageSize;
pagination.total = total;
} catch (err) {
// you can report use errorHandler or other
} finally {
setLoading(false);
}
};
const onPageChange = (current: number) => {
fetchData({ ...pagination, current });
};
const onPageSizeChange = (pageSize: number) => {
fetchData({ ...pagination, pageSize });
};
const search = () => {
fetchData({
...basePagination,
...formModel.value
} as unknown as AppleCardParams);
};
const reset = () => {
formModel.value = generateFormModel();
};
const deleteAppleCardHandler = async (id: string) => {
try {
await deleteAppleCard({ id });
} catch {
Notification.error({
id: 'appleAccountNotice',
content: '删除苹果卡失败',
closable: true
});
} finally {
fetchData({ ...pagination });
}
};
const showAddModel = (record: KamiApiCardInfoAppleV1AppleCardListRecord) => {
state.addModalVisible = true;
state.accountId = record.id;
state.account = {
account: record.account,
password: record.password,
maxAmountLimit: record.maxAmountLimit,
maxCountLimit: record.maxCountLimit
};
};
const showDetailModel = (record: KamiApiCardInfoAppleV1AppleCardListRecord) => {
state.accountDetailVisible = true;
state.accountId = record.id;
};
watch(
() => state.addModalVisible,
val => {
if (!val) {
search();
}
}
);
const getStatusColorMap = (status: number) => {
type colorMap = {
color: string;
text: string;
};
const c: colorMap[] = [];
switch (status) {
case 1:
c.push();
break;
case 2:
c.push({
color: 'green',
text: '正常'
});
break;
case 3:
c.push({
color: 'red',
text: '账/密错误'
});
break;
case 4:
c.push({
color: 'orange',
text: '额度超出'
});
break;
case 5:
c.push({
color: 'pinkpurple',
text: '50元限制'
});
break;
case 6:
c.push({
color: 'magenta',
text: '安全登录限制'
});
break;
case 7:
c.push({
color: 'pinkpurple',
text: '禁用(2分钟)'
});
break;
case 8:
c.push({
color: 'red',
text: '禁用'
});
break;
case 9:
c.push({
color: 'red',
text: '禁用(1分钟)'
});
break;
default:
c.push({
color: 'grey',
text: '其他状态,待更新'
});
break;
}
return c;
};
const downloadAccountTemplte = () => {
downloadAppleAccountTemplteAPI();
};
const uploadSucceed = (fileItem: FileItem) => {
Notification.success({
id: 'appleAccountNotice',
content: fileItem.response?.data?.msg || '上传成功',
closable: true
});
search();
};
onMounted(() => {
fetchData();
module: 'rule'
});
</script>