添加页面
This commit is contained in:
parent
1cdcbc9abe
commit
1810a7f88f
|
|
@ -0,0 +1,32 @@
|
|||
import { request } from '@/api';
|
||||
|
||||
/**
|
||||
* @description: 获取品种枚举列表
|
||||
*/
|
||||
export async function getVarietyEnumsListApi(): Promise<any> {
|
||||
return request.get<any>('/contractVariety/getVarietyEnumsList')
|
||||
}
|
||||
|
||||
/**
|
||||
* @description: 新增品种枚举
|
||||
* @param params - { code: 编码, name: 名称, status: 状态(1启用/2禁用) }
|
||||
*/
|
||||
export async function addVarietyEnumsApi(params: { code: string; name: string; status: number }): Promise<any> {
|
||||
return request.post<any>('/contractVariety/addVarietyEnums', params)
|
||||
}
|
||||
|
||||
/**
|
||||
* @description: 编辑品种枚举
|
||||
* @param params - { id, code: 编码, name: 名称, status: 状态(1启用/2禁用) }
|
||||
*/
|
||||
export async function editVarietyEnumsApi(params: { id: number; code: string; name: string; status: number }): Promise<any> {
|
||||
return request.post<any>('/contractVariety/editVarietyEnums', params)
|
||||
}
|
||||
|
||||
/**
|
||||
* @description: 删除品种枚举
|
||||
* @param params - { id }
|
||||
*/
|
||||
export async function delVarietyEnumsApi(params: { id: number }): Promise<any> {
|
||||
return request.post<any>('/contractVariety/delVarietyEnums', params)
|
||||
}
|
||||
|
|
@ -0,0 +1,79 @@
|
|||
<template>
|
||||
<span>{{ animatedValue }}</span>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
|
||||
defineOptions({
|
||||
name: 'AnimatedNumber'
|
||||
})
|
||||
import { ref, watch, onMounted, onUnmounted } from 'vue';
|
||||
|
||||
interface Props {
|
||||
value: number;
|
||||
duration?: number;
|
||||
decimals?: number;
|
||||
separator?: string;
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
duration: 1000,
|
||||
decimals: 2,
|
||||
separator: ','
|
||||
});
|
||||
|
||||
const animatedValue = ref('0.00');
|
||||
let animationFrameId: number | null = null;
|
||||
let startTime: number | null = null;
|
||||
const startValue = ref(0);
|
||||
|
||||
// 格式化数字
|
||||
const formatNumber = (num: number): string => {
|
||||
const fixed = num.toFixed(props.decimals);
|
||||
const parts = fixed.split('.');
|
||||
parts[0] && (parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, props.separator));
|
||||
return parts.join('.');
|
||||
};
|
||||
|
||||
// 动画函数
|
||||
const animate = (timestamp: number) => {
|
||||
if (!startTime) startTime = timestamp;
|
||||
const elapsed = timestamp - startTime;
|
||||
const progress = Math.min(elapsed / props.duration, 1);
|
||||
|
||||
// 使用缓动函数 easeOutQuart
|
||||
const easeProgress = 1 - Math.pow(1 - progress, 4);
|
||||
|
||||
const currentValue = startValue.value + (props.value - startValue.value) * easeProgress;
|
||||
animatedValue.value = formatNumber(currentValue);
|
||||
|
||||
if (progress < 1) {
|
||||
animationFrameId = requestAnimationFrame(animate);
|
||||
}
|
||||
};
|
||||
|
||||
// 开始动画
|
||||
const startAnimation = () => {
|
||||
if (animationFrameId) {
|
||||
cancelAnimationFrame(animationFrameId);
|
||||
}
|
||||
startTime = null;
|
||||
startValue.value = parseFloat(animatedValue.value.replace(/,/g, '')) || 0;
|
||||
animationFrameId = requestAnimationFrame(animate);
|
||||
};
|
||||
|
||||
// 监听值变化
|
||||
watch(() => props.value, () => {
|
||||
startAnimation();
|
||||
}, { immediate: false });
|
||||
|
||||
onMounted(() => {
|
||||
startAnimation();
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
if (animationFrameId) {
|
||||
cancelAnimationFrame(animationFrameId);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
|
@ -0,0 +1,354 @@
|
|||
<template>
|
||||
<el-drawer v-model="innerVisible" :title="drawerTitle" direction="rtl" size="80%" header-align="center">
|
||||
<div class="drawer-content">
|
||||
<!-- 顶部客户信息卡片 -->
|
||||
<div class="user-top" v-loading="loading">
|
||||
<div class="left-card">
|
||||
|
||||
<div class="avatar-wrapper">
|
||||
<el-icon class="avatar-icon">
|
||||
<UserFilled />
|
||||
</el-icon>
|
||||
</div>
|
||||
<div class="left-text">
|
||||
<p>{{ userInfo.name }}</p>
|
||||
<p>ID:{{ userInfo.id }}</p>
|
||||
<p>{{ userInfo.role }}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="right-card">
|
||||
<div class="item">
|
||||
<div class="item-label">
|
||||
<el-icon>
|
||||
<Phone />
|
||||
</el-icon>
|
||||
<span>{{ t('agentUser.phone') }}</span>
|
||||
</div>
|
||||
<div class="item-value">{{ userInfo.phone || '-' }}</div>
|
||||
</div>
|
||||
<div class="item">
|
||||
<div class="item-label">
|
||||
<el-icon>
|
||||
<CreditCard />
|
||||
</el-icon>
|
||||
<span>{{ t('agentUser.accountBalance') }}</span>
|
||||
</div>
|
||||
<div class="item-value text-highlight">
|
||||
<AnimatedNumber :value="Number(userInfo.balance) || 0" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="item">
|
||||
<div class="item-label">
|
||||
<el-icon>
|
||||
<Money />
|
||||
</el-icon>
|
||||
<span>{{ t('agentUser.depositAmount') }}</span>
|
||||
</div>
|
||||
<div class="item-value text-highlight">
|
||||
<AnimatedNumber :value="Number(userInfo.deposit) || 0" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="item">
|
||||
<div class="item-label">
|
||||
<el-icon>
|
||||
<Message />
|
||||
</el-icon>
|
||||
<span>{{ t('agentUser.email') }}</span>
|
||||
</div>
|
||||
<div class="item-value">{{ userInfo.email || '-' }}</div>
|
||||
</div>
|
||||
<div class="item">
|
||||
<div class="item-label">
|
||||
<el-icon>
|
||||
<Clock />
|
||||
</el-icon>
|
||||
<span>{{ t('agentUser.registerTime') }}</span>
|
||||
</div>
|
||||
<div class="item-value">{{ userInfo.registerTime || '-' }}</div>
|
||||
</div>
|
||||
<div class="item">
|
||||
<div class="item-label">
|
||||
<el-icon>
|
||||
<Wallet />
|
||||
</el-icon>
|
||||
<span>{{ t('agentUser.historyTradeAmount') }}</span>
|
||||
</div>
|
||||
<div class="item-value">
|
||||
<AnimatedNumber :value="Number(userInfo.hisMeny) || 0" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 账户表格 -->
|
||||
<div class="table-container" v-loading="tableLoading">
|
||||
<CustomerListTable :table-data="tableData" :columns="tableColumnsOptions" row-key="tradeAccount"
|
||||
:default-sort="{ prop: 'addTime', order: 'descending' }">
|
||||
</CustomerListTable>
|
||||
</div>
|
||||
</div>
|
||||
</el-drawer>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
|
||||
defineOptions({
|
||||
name: 'UserDetailDrawer'
|
||||
})
|
||||
import { ref, watch, computed } from 'vue'
|
||||
import { ElIcon, ElDrawer, ElMessage } from 'element-plus'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { UserFilled, Phone, Message, Money, Wallet, Clock, CreditCard } from '@element-plus/icons-vue'
|
||||
import CustomerListTable from '@/components/common/Table.vue'
|
||||
import { getCustomerDetailApi } from '@/api/modules/agent/customerDetail'
|
||||
import { useLanguageStore } from '@/stores/modules/language'
|
||||
import dayjs from 'dayjs'
|
||||
import AnimatedNumber from './AnimatedNumber.vue'
|
||||
|
||||
// 类型定义
|
||||
import type {
|
||||
RoleType,
|
||||
CustomerUserInfo,
|
||||
Account,
|
||||
CustomerDetailResponse,
|
||||
UserDisplayInfo,
|
||||
TableDataRow,
|
||||
TableColumn
|
||||
} from '@/api/types/agent'
|
||||
|
||||
const { t, locale } = useI18n()
|
||||
const languageStore = useLanguageStore()
|
||||
|
||||
// 使用 computed 动态生成角色映射,支持语言切换
|
||||
const roleMap = computed<Record<RoleType, string>>(() => ({
|
||||
1: t('agentUser.role.1'),
|
||||
2: t('agentUser.role.2'),
|
||||
3: t('agentUser.role.3'),
|
||||
4: t('agentUser.role.4'),
|
||||
5: t('agentUser.role.5'),
|
||||
}))
|
||||
|
||||
// Props 定义
|
||||
const props = defineProps<{
|
||||
drawerVisible: boolean
|
||||
queryId: string | number
|
||||
drawerTitle: string
|
||||
}>()
|
||||
|
||||
// 抛出事件
|
||||
const emit = defineEmits<{
|
||||
'update:drawerVisible': [value: boolean]
|
||||
}>()
|
||||
|
||||
// =============== 修复核心:computed 中转 v-model ===============
|
||||
const innerVisible = computed({
|
||||
get() {
|
||||
return props.drawerVisible
|
||||
},
|
||||
set(val) {
|
||||
emit('update:drawerVisible', val)
|
||||
}
|
||||
})
|
||||
|
||||
// 状态
|
||||
const loading = ref(false)
|
||||
const tableLoading = ref(false)
|
||||
|
||||
// 客户信息
|
||||
const userInfo = ref<UserDisplayInfo>({
|
||||
name: '',
|
||||
id: '',
|
||||
role: '',
|
||||
phone: '',
|
||||
email: '',
|
||||
deposit: 0,
|
||||
balance: 0,
|
||||
registerTime: '',
|
||||
hisMeny: 0,
|
||||
})
|
||||
|
||||
// 表格
|
||||
const tableData = ref<TableDataRow[]>([])
|
||||
|
||||
// 动态生成表格列配置,支持语言切换
|
||||
const tableColumnsOptions = computed<TableColumn[]>(() => [
|
||||
{ label: t('agentUser.tradeAccount'), prop: 'tradeAccount' },
|
||||
{ label: t('agentUser.accountName'), prop: 'accountName' },
|
||||
{ label: t('agentUser.balance'), prop: 'balance', sortable: true },
|
||||
{ label: t('agentUser.tradeLeverage'), prop: 'leverage' },
|
||||
{ label: t('agentUser.historyTradeVolume'), prop: 'tradeVolume' },
|
||||
{ label: t('agentUser.addTime'), prop: 'addTime', width: 180 }
|
||||
])
|
||||
|
||||
|
||||
// ===================== 加载详情数据 =====================
|
||||
const loadDetailData = async (id: string | number) => {
|
||||
loading.value = true
|
||||
tableLoading.value = true
|
||||
|
||||
try {
|
||||
const res: CustomerDetailResponse = await getCustomerDetailApi({ user_id: id });
|
||||
const { accounts, user } = res;
|
||||
|
||||
userInfo.value = {
|
||||
name: user.email || user.mobile || '-',
|
||||
id: user.id,
|
||||
role: roleMap.value[user.role_id] || '-',
|
||||
phone: user.mobile || '-',
|
||||
email: user.email || '-',
|
||||
balance: Number(user.capital_amount) || 0,
|
||||
deposit: Number(user.total_recharge_amount) || 0,
|
||||
registerTime: dayjs(user.created_at).format('YYYY-MM-DD HH:mm:ss'),
|
||||
hisMeny: Number(user.history_transfer_amount) || 0,
|
||||
}
|
||||
|
||||
// 处理账户列表数据
|
||||
tableData.value = accounts.map((item: Account): TableDataRow => ({
|
||||
tradeAccount: item.account_id,
|
||||
accountName: item.account_name,
|
||||
balance: Number(item.wallets_trade.amount) || 0,
|
||||
leverage: Number(item.wallets_trade.lever) || 0,
|
||||
tradeVolume: Number(item.collect_data) || 0,
|
||||
addTime: dayjs(item.created_at).format('YYYY-MM-DD HH:mm:ss'),
|
||||
}))
|
||||
} catch (err) {
|
||||
console.error('加载详情失败:', err);
|
||||
ElMessage.error(t('agentUser.loadDetailFailed'))
|
||||
} finally {
|
||||
loading.value = false
|
||||
tableLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// ===================== 监听 ID 自动加载数据 =====================
|
||||
watch(
|
||||
() => props.queryId,
|
||||
async (id) => {
|
||||
if (!id) return
|
||||
await loadDetailData(id)
|
||||
},
|
||||
{ immediate: true }
|
||||
)
|
||||
|
||||
// 监听语言切换,同步更新 i18n 的 locale
|
||||
watch(
|
||||
() => languageStore.lang,
|
||||
(newLang) => {
|
||||
locale.value = newLang
|
||||
},
|
||||
{ immediate: true }
|
||||
)
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.drawer-content {
|
||||
padding: 0px 32px 28px 32px;
|
||||
|
||||
}
|
||||
|
||||
.user-top {
|
||||
display: flex;
|
||||
gap: 24px;
|
||||
margin-bottom: 32px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.left-card {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 20px;
|
||||
padding: 20px 32px;
|
||||
background: #fafbfc;
|
||||
border-radius: 16px;
|
||||
border: 1px solid #f0f2f5;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.04);
|
||||
min-width: 300px;
|
||||
|
||||
.avatar-wrapper {
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
border-radius: 50%;
|
||||
background: #f2f3f5;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
overflow: hidden;
|
||||
|
||||
.avatar-image {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.avatar-icon {
|
||||
font-size: 48px;
|
||||
color: #909399;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.left-text {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 6px;
|
||||
font-size: 15px;
|
||||
font-weight: 500;
|
||||
color: #1d2129;
|
||||
}
|
||||
|
||||
.left-text p {
|
||||
margin: 0;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.right-card {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 28px 36px;
|
||||
padding: 24px 32px;
|
||||
background: #fafbfc;
|
||||
border-radius: 16px;
|
||||
border: 1px solid #f0f2f5;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.04);
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 6px;
|
||||
min-width: 140px;
|
||||
}
|
||||
|
||||
.item-label {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
font-size: 13px;
|
||||
color: #86909c;
|
||||
}
|
||||
|
||||
.item-label :deep(.el-icon) {
|
||||
font-size: 16px;
|
||||
color: #86909c;
|
||||
}
|
||||
|
||||
.item-value {
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
color: #1d2129;
|
||||
}
|
||||
|
||||
.text-highlight {
|
||||
color: #1677ff;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.table-container {
|
||||
width: 100%;
|
||||
height: auto;
|
||||
margin-top: 10px;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -131,8 +131,8 @@ import CustomerListPagination from '@/components/common/Pagination.vue'
|
|||
import CustomerListDialog from '@/components/common/Dialog.vue'
|
||||
import CustomerListForm from '@/components/common/Form.vue'
|
||||
|
||||
// 👇 引入同目录的抽屉组件(default.vue)
|
||||
import CustomerDetailDrawer from './default.vue'
|
||||
// 👇 引入公共的详情抽屉组件
|
||||
import CustomerDetailDrawer from '@/components/common/UserDetailDrawer.vue'
|
||||
|
||||
// 配置以及表单验证
|
||||
import { search, tableColumns, marginRatioTableColumns, commissionRatioFormItem } from './options'
|
||||
|
|
|
|||
|
|
@ -19,10 +19,10 @@
|
|||
</el-button>
|
||||
</template>
|
||||
<template #action="{ row }">
|
||||
<!-- <el-button link type="primary" size="small" @click="handleDetail(row)" v-if="row.userId">
|
||||
<el-button link type="primary" size="small" @click="openDetailDrawer(row)" v-if="row.userId">
|
||||
查看详情
|
||||
</el-button> -->
|
||||
<!-- <el-divider direction="vertical" /> -->
|
||||
</el-button>
|
||||
<el-divider direction="vertical" v-if="row.userId" />
|
||||
<el-button link type="primary" size="small" @click="handleEditCustomer(row)">
|
||||
编辑
|
||||
</el-button>
|
||||
|
|
@ -101,6 +101,10 @@
|
|||
</el-form-item>
|
||||
</el-form>
|
||||
</CustomerListDialog>
|
||||
|
||||
<!-- ===================== 代理详情抽屉 ===================== -->
|
||||
<UserDetailDrawer v-model:drawer-visible="detailDrawerVisible" :query-id="detailQueryId"
|
||||
:drawer-title="detailDrawerTitle" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
|
@ -116,6 +120,7 @@ import { usePageLoading, useButtonLoading } from '@/composables/useLoading'
|
|||
import CustomerListPagination from '@/components/common/Pagination.vue'
|
||||
import CustomerListDialog from '@/components/common/Dialog.vue'
|
||||
import CustomerListForm from '@/components/common/Form.vue'
|
||||
import UserDetailDrawer from '@/components/common/UserDetailDrawer.vue'
|
||||
// 配置以及表单验证
|
||||
import { search, tableColumns, marginRatioTableColumns, commissionRatioFormItem } from './options'
|
||||
|
||||
|
|
@ -279,6 +284,19 @@ const adjustAmountRules = {
|
|||
],
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 详情抽屉相关
|
||||
*/
|
||||
const detailDrawerVisible = ref(false)
|
||||
const detailQueryId = ref<string | number>('')
|
||||
const detailDrawerTitle = ref('代理详情')
|
||||
|
||||
// 打开详情抽屉
|
||||
const openDetailDrawer = (row: any) => {
|
||||
detailQueryId.value = row.userId
|
||||
detailDrawerVisible.value = true
|
||||
}
|
||||
|
||||
/**
|
||||
* @description: 角色映射方法
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -16,6 +16,10 @@
|
|||
{{ row.status === 1 ? '正常' : '禁用' }}
|
||||
</template>
|
||||
<template #action="{ row }">
|
||||
<el-button type="primary" size="small" @click="openDetailDrawer(row)" v-if="row.id">
|
||||
查看详情
|
||||
</el-button>
|
||||
<el-divider direction="vertical" v-if="row.id" />
|
||||
<el-button type="primary" size="small" @click="handleChange(row)" v-anth="'user_editUser'">
|
||||
修改
|
||||
</el-button>
|
||||
|
|
@ -28,6 +32,11 @@
|
|||
<ClientPagination v-model="currentPage" v-model:pageSizeValue="pageSize" :total="total"
|
||||
@pagination-change="handlePaginationChange" />
|
||||
</div>
|
||||
|
||||
<!-- ===================== 客户详情抽屉 ===================== -->
|
||||
<UserDetailDrawer v-model:drawer-visible="detailDrawerVisible" :query-id="detailQueryId"
|
||||
:drawer-title="detailDrawerTitle" />
|
||||
|
||||
<!-- 客户弹窗 -->
|
||||
<ClientDialog :visible="dialogVisible" :title="dialogTitle" :type="dialogType" :button-loading="submitLoading"
|
||||
@confirm="handleSubmit" @cancel="handleCancel" @close="handleClose">
|
||||
|
|
@ -100,6 +109,7 @@ import { usePageLoading, useButtonLoading } from '@/composables/useLoading'
|
|||
import ClientPagination from '@/components/common/Pagination.vue'
|
||||
import ClientDialog from '@/components/common/Dialog.vue'
|
||||
import ClientForm from '@/components/common/Form.vue'
|
||||
import UserDetailDrawer from '@/components/common/UserDetailDrawer.vue'
|
||||
// 接口
|
||||
import {
|
||||
getClientListApi,
|
||||
|
|
@ -160,6 +170,17 @@ const dialogType = ref('add')
|
|||
const adjustAmountVisible = ref(false)
|
||||
const adjustAmountFormRef = ref()
|
||||
const adjustUserId = ref<number>(0)
|
||||
|
||||
// 详情抽屉相关
|
||||
const detailDrawerVisible = ref(false)
|
||||
const detailQueryId = ref<string | number>('')
|
||||
const detailDrawerTitle = ref('客户详情')
|
||||
|
||||
// 打开详情抽屉
|
||||
const openDetailDrawer = (row: any) => {
|
||||
detailQueryId.value = row.id
|
||||
detailDrawerVisible.value = true
|
||||
}
|
||||
const adjustAmountForm = ref({
|
||||
username: '',
|
||||
currency: '',
|
||||
|
|
|
|||
|
|
@ -0,0 +1,254 @@
|
|||
<template>
|
||||
<div class="variety-enums table_form-container">
|
||||
<!-- 搜索 -->
|
||||
<el-button style="width: 70px;" type="primary" @click="handleAdd()" v-auth="'contractVariety_addVarietyEnums'">
|
||||
新增
|
||||
</el-button>
|
||||
|
||||
<!-- 表格 -->
|
||||
<VarietyEnumsTable
|
||||
:table-data="tableData"
|
||||
:columns="tableColumnsOptions"
|
||||
row-key="id"
|
||||
@row-click="handleRowClick"
|
||||
:loading="loading">
|
||||
<template #status="{ row }">
|
||||
<el-tag :type="row.status === 1 ? 'success' : 'danger'">
|
||||
{{ row.status === 1 ? '启用' : '禁用' }}
|
||||
</el-tag>
|
||||
</template>
|
||||
<template #action="{ row }">
|
||||
<el-button type="primary" link @click.stop="handleEdit(row)" v-auth="'contractVariety_editVarietyEnums'">
|
||||
编辑
|
||||
</el-button>
|
||||
<el-button type="danger" link @click.stop="handleDelete(row)" v-auth="'contractVariety_delVarietyEnums'">
|
||||
删除
|
||||
</el-button>
|
||||
</template>
|
||||
</VarietyEnumsTable>
|
||||
</div>
|
||||
|
||||
<!-- dialog -->
|
||||
<VarietyEnumsDialog
|
||||
:visible="dialogVisible"
|
||||
:title="dialogTitle"
|
||||
:button-loading="submitLoading"
|
||||
@confirm="handleSubmit"
|
||||
@cancel="handleCancel"
|
||||
@close="handleClose">
|
||||
<VarietyEnumsForm
|
||||
:form-data="formData"
|
||||
:form-rules="formRules"
|
||||
:form-items="dialogFormItemOptions"
|
||||
:options-map="formOptionsMap"
|
||||
:row-gutter="rowGutter"
|
||||
:col-span="colSpan"
|
||||
ref="formRef">
|
||||
</VarietyEnumsForm>
|
||||
</VarietyEnumsDialog>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { usePageLoading, useButtonLoading } from '@/composables/useLoading'
|
||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
|
||||
defineOptions({
|
||||
name: 'VarietyEnums'
|
||||
})
|
||||
|
||||
// 组件
|
||||
const { loading, startLoading, stopLoading } = usePageLoading()
|
||||
const { buttonLoading: submitLoading, startButtonLoading: startSubmitLoading, stopButtonLoading: stopSubmitLoading } = useButtonLoading()
|
||||
|
||||
import VarietyEnumsSearch from '@/components/common/Search.vue'
|
||||
import VarietyEnumsTable from '@/components/common/Table.vue'
|
||||
import VarietyEnumsDialog from '@/components/common/Dialog.vue'
|
||||
import VarietyEnumsForm from '@/components/common/Form.vue'
|
||||
|
||||
// 配置以及表单验证规则
|
||||
import { search, tableColumns, DialogFormItem } from './options'
|
||||
import { rules } from './rules'
|
||||
|
||||
// 接口
|
||||
import {
|
||||
getVarietyEnumsListApi,
|
||||
addVarietyEnumsApi,
|
||||
editVarietyEnumsApi,
|
||||
delVarietyEnumsApi,
|
||||
} from '@/api/modules/trade/varietyEnums'
|
||||
|
||||
/**
|
||||
* @description: 定义搜索数据
|
||||
*/
|
||||
const searchForm = ref({})
|
||||
const searchOptions = ref(search)
|
||||
|
||||
/**
|
||||
* @description: 定义表格数据
|
||||
*/
|
||||
const tableData = ref<any[]>([])
|
||||
const tableColumnsOptions = ref([...tableColumns, { slotName: 'action', label: '操作', align: 'center' as const }])
|
||||
|
||||
/**
|
||||
* @description: 定义弹窗数据
|
||||
*/
|
||||
const dialogVisible = ref(false)
|
||||
const dialogTitle = ref('')
|
||||
const dialogType = ref('')
|
||||
|
||||
/**
|
||||
* @description: 定义弹窗表单数据
|
||||
*/
|
||||
const formData = ref({
|
||||
id: 0,
|
||||
code: '',
|
||||
name: '',
|
||||
status: 1,
|
||||
})
|
||||
const formRef = ref<any>()
|
||||
const formRules = ref(rules())
|
||||
const dialogFormItemOptions = ref(DialogFormItem)
|
||||
const formOptionsMap = ref({
|
||||
status: [
|
||||
{ label: '启用', value: 1 },
|
||||
{ label: '禁用', value: 2 },
|
||||
],
|
||||
})
|
||||
const rowGutter = ref(20)
|
||||
const colSpan = ref(24)
|
||||
|
||||
// 选中行
|
||||
const selectedRow = ref<any>(null)
|
||||
|
||||
/**
|
||||
* @description: 获取品种枚举列表
|
||||
*/
|
||||
const getList = async () => {
|
||||
startLoading()
|
||||
try {
|
||||
const data = await getVarietyEnumsListApi()
|
||||
tableData.value = data || []
|
||||
} finally {
|
||||
stopLoading()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @description: 页面加载完成需要请求的数据
|
||||
*/
|
||||
onMounted(() => {
|
||||
getList()
|
||||
})
|
||||
|
||||
/**
|
||||
* @description: 定义搜索方法
|
||||
*/
|
||||
const handleSearch = async () => {
|
||||
getList()
|
||||
}
|
||||
|
||||
const handleReset = () => {
|
||||
searchForm.value = {}
|
||||
handleSearch()
|
||||
}
|
||||
|
||||
// 点击行事件
|
||||
const handleRowClick = (row: any) => {
|
||||
if (!row) {
|
||||
selectedRow.value = null
|
||||
return
|
||||
}
|
||||
selectedRow.value = row
|
||||
}
|
||||
|
||||
/**
|
||||
* @description: 新增
|
||||
*/
|
||||
const handleAdd = () => {
|
||||
dialogVisible.value = true
|
||||
dialogTitle.value = '新增品种枚举'
|
||||
dialogType.value = 'add'
|
||||
formData.value = {
|
||||
id: 0,
|
||||
code: '',
|
||||
name: '',
|
||||
status: 1,
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @description: 编辑
|
||||
*/
|
||||
const handleEdit = (row: any) => {
|
||||
dialogVisible.value = true
|
||||
dialogTitle.value = '编辑品种枚举'
|
||||
dialogType.value = 'edit'
|
||||
formData.value = {
|
||||
id: row.id,
|
||||
code: row.code,
|
||||
name: row.name,
|
||||
status: row.status,
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @description: 删除
|
||||
*/
|
||||
const handleDelete = async (row: any) => {
|
||||
try {
|
||||
await ElMessageBox.confirm('确定要删除该品种枚举吗?', '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
})
|
||||
await delVarietyEnumsApi({ id: row.id })
|
||||
ElMessage.success('删除成功')
|
||||
getList()
|
||||
} catch (error: any) {
|
||||
if (error !== 'cancel') {
|
||||
ElMessage.error(error.message || '删除失败')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @description: 定义弹窗方法
|
||||
*/
|
||||
const handleSubmit = async () => {
|
||||
startSubmitLoading()
|
||||
try {
|
||||
await formRef.value?.validate()
|
||||
const params = {
|
||||
code: formData.value.code,
|
||||
name: formData.value.name,
|
||||
status: formData.value.status,
|
||||
}
|
||||
|
||||
if (dialogType.value === 'edit') {
|
||||
await editVarietyEnumsApi({
|
||||
id: Number(formData.value.id),
|
||||
...params,
|
||||
})
|
||||
ElMessage.success('编辑成功')
|
||||
} else {
|
||||
await addVarietyEnumsApi(params)
|
||||
ElMessage.success('新增成功')
|
||||
}
|
||||
dialogVisible.value = false
|
||||
getList()
|
||||
} finally {
|
||||
stopSubmitLoading()
|
||||
}
|
||||
}
|
||||
|
||||
const handleCancel = () => {
|
||||
dialogVisible.value = false
|
||||
}
|
||||
|
||||
const handleClose = () => {
|
||||
dialogVisible.value = false
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
</style>
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
/**
|
||||
* @description: 搜索配置
|
||||
*/
|
||||
export const search = []
|
||||
|
||||
/**
|
||||
* @description: 表格配置
|
||||
*/
|
||||
export const tableColumns = [
|
||||
// ID
|
||||
{ prop: 'id', label: 'ID', align: 'center' as const },
|
||||
// 编码
|
||||
{ prop: 'code', label: '编码', align: 'center' as const },
|
||||
// 名称
|
||||
{ prop: 'name', label: '名称', align: 'center' as const },
|
||||
// 状态
|
||||
{ slotName: 'status', label: '状态', align: 'center' as const },
|
||||
// 创建时间
|
||||
{ prop: 'created_at', label: '创建时间', align: 'center' as const },
|
||||
// 更新时间
|
||||
{ prop: 'updated_at', label: '更新时间', align: 'center' as const },
|
||||
]
|
||||
|
||||
/**
|
||||
* @description: dialog表单配置
|
||||
*/
|
||||
export const DialogFormItem = [
|
||||
// 编码
|
||||
{ prop: 'code', label: '编码', type: 'input' as const, placeholder: '请输入编码' },
|
||||
// 名称
|
||||
{ prop: 'name', label: '名称', type: 'input' as const, placeholder: '请输入名称' },
|
||||
// 状态
|
||||
{ prop: 'status', label: '状态', type: 'select' as const, placeholder: '请选择状态' },
|
||||
]
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
import type { FormRules } from 'element-plus'
|
||||
|
||||
export const rules = (): FormRules => {
|
||||
return {
|
||||
// 编码
|
||||
code: [{ required: true, message: '请输入编码', trigger: ['blur', 'change'] }],
|
||||
// 名称
|
||||
name: [{ required: true, message: '请输入名称', trigger: ['blur', 'change'] }],
|
||||
// 状态
|
||||
status: [{ required: true, message: '请选择状态', trigger: ['blur', 'change'] }],
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue