添加页面
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 CustomerListDialog from '@/components/common/Dialog.vue'
|
||||||
import CustomerListForm from '@/components/common/Form.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'
|
import { search, tableColumns, marginRatioTableColumns, commissionRatioFormItem } from './options'
|
||||||
|
|
|
||||||
|
|
@ -19,10 +19,10 @@
|
||||||
</el-button>
|
</el-button>
|
||||||
</template>
|
</template>
|
||||||
<template #action="{ row }">
|
<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-button>
|
||||||
<!-- <el-divider direction="vertical" /> -->
|
<el-divider direction="vertical" v-if="row.userId" />
|
||||||
<el-button link type="primary" size="small" @click="handleEditCustomer(row)">
|
<el-button link type="primary" size="small" @click="handleEditCustomer(row)">
|
||||||
编辑
|
编辑
|
||||||
</el-button>
|
</el-button>
|
||||||
|
|
@ -101,6 +101,10 @@
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
</CustomerListDialog>
|
</CustomerListDialog>
|
||||||
|
|
||||||
|
<!-- ===================== 代理详情抽屉 ===================== -->
|
||||||
|
<UserDetailDrawer v-model:drawer-visible="detailDrawerVisible" :query-id="detailQueryId"
|
||||||
|
:drawer-title="detailDrawerTitle" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
@ -116,6 +120,7 @@ import { usePageLoading, useButtonLoading } from '@/composables/useLoading'
|
||||||
import CustomerListPagination from '@/components/common/Pagination.vue'
|
import CustomerListPagination from '@/components/common/Pagination.vue'
|
||||||
import CustomerListDialog from '@/components/common/Dialog.vue'
|
import CustomerListDialog from '@/components/common/Dialog.vue'
|
||||||
import CustomerListForm from '@/components/common/Form.vue'
|
import CustomerListForm from '@/components/common/Form.vue'
|
||||||
|
import UserDetailDrawer from '@/components/common/UserDetailDrawer.vue'
|
||||||
// 配置以及表单验证
|
// 配置以及表单验证
|
||||||
import { search, tableColumns, marginRatioTableColumns, commissionRatioFormItem } from './options'
|
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: 角色映射方法
|
* @description: 角色映射方法
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,10 @@
|
||||||
{{ row.status === 1 ? '正常' : '禁用' }}
|
{{ row.status === 1 ? '正常' : '禁用' }}
|
||||||
</template>
|
</template>
|
||||||
<template #action="{ row }">
|
<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 type="primary" size="small" @click="handleChange(row)" v-anth="'user_editUser'">
|
||||||
修改
|
修改
|
||||||
</el-button>
|
</el-button>
|
||||||
|
|
@ -28,6 +32,11 @@
|
||||||
<ClientPagination v-model="currentPage" v-model:pageSizeValue="pageSize" :total="total"
|
<ClientPagination v-model="currentPage" v-model:pageSizeValue="pageSize" :total="total"
|
||||||
@pagination-change="handlePaginationChange" />
|
@pagination-change="handlePaginationChange" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- ===================== 客户详情抽屉 ===================== -->
|
||||||
|
<UserDetailDrawer v-model:drawer-visible="detailDrawerVisible" :query-id="detailQueryId"
|
||||||
|
:drawer-title="detailDrawerTitle" />
|
||||||
|
|
||||||
<!-- 客户弹窗 -->
|
<!-- 客户弹窗 -->
|
||||||
<ClientDialog :visible="dialogVisible" :title="dialogTitle" :type="dialogType" :button-loading="submitLoading"
|
<ClientDialog :visible="dialogVisible" :title="dialogTitle" :type="dialogType" :button-loading="submitLoading"
|
||||||
@confirm="handleSubmit" @cancel="handleCancel" @close="handleClose">
|
@confirm="handleSubmit" @cancel="handleCancel" @close="handleClose">
|
||||||
|
|
@ -100,6 +109,7 @@ import { usePageLoading, useButtonLoading } from '@/composables/useLoading'
|
||||||
import ClientPagination from '@/components/common/Pagination.vue'
|
import ClientPagination from '@/components/common/Pagination.vue'
|
||||||
import ClientDialog from '@/components/common/Dialog.vue'
|
import ClientDialog from '@/components/common/Dialog.vue'
|
||||||
import ClientForm from '@/components/common/Form.vue'
|
import ClientForm from '@/components/common/Form.vue'
|
||||||
|
import UserDetailDrawer from '@/components/common/UserDetailDrawer.vue'
|
||||||
// 接口
|
// 接口
|
||||||
import {
|
import {
|
||||||
getClientListApi,
|
getClientListApi,
|
||||||
|
|
@ -160,6 +170,17 @@ const dialogType = ref('add')
|
||||||
const adjustAmountVisible = ref(false)
|
const adjustAmountVisible = ref(false)
|
||||||
const adjustAmountFormRef = ref()
|
const adjustAmountFormRef = ref()
|
||||||
const adjustUserId = ref<number>(0)
|
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({
|
const adjustAmountForm = ref({
|
||||||
username: '',
|
username: '',
|
||||||
currency: '',
|
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