Merge branch 'yueyixin'

This commit is contained in:
2026-05-21 17:01:06 +08:00
commit 609764b39c
55 changed files with 1533 additions and 1133 deletions

View File

@ -1,8 +1,21 @@
<template> <template>
<div class="app-container"> <div class="app-container">
<TopLoading ref="topLoadingRef" />
<KeepAlive> <KeepAlive>
<router-view></router-view> <router-view></router-view>
</KeepAlive> </KeepAlive>
</div> </div>
</template> </template>
<script setup lang="ts">
import { ref, onMounted } from 'vue'
import TopLoading from '@/components/common/TopLoading.vue'
const topLoadingRef = ref<InstanceType<typeof TopLoading>>()
onMounted(() => {
// TopLoading window api
// @ts-ignore
window.__topLoading__ = topLoadingRef.value
})
</script>

View File

@ -1,35 +1,33 @@
import axios from 'axios' import axios from 'axios'
import type { AxiosResponse, AxiosError, InternalAxiosRequestConfig } from 'axios' import type { AxiosResponse, AxiosError, InternalAxiosRequestConfig } from 'axios'
import type { ApiResponse } from '@/api/types' import type { ApiResponse } from '@/api/types'
import { ElLoading, ElNotification } from 'element-plus' import { ElNotification } from 'element-plus'
import { getStorage, setStorage, removeStorage } from '@/utils/storage' import { getStorage, setStorage, removeStorage } from '@/utils/storage'
import router from '../router' import router from '../router'
// 定义loading实例类型用于控制loading的显示和关闭 // 记录当前请求数量,用于控制顶部进度条
let loadingInstance: ReturnType<typeof ElLoading.service> | null = null
// 记录当前请求数量,防止多个请求重复显示/关闭loading
let requestCount = 0 let requestCount = 0
// 显示loading的方法 // 获取 TopLoading 实例
const getTopLoading = () => {
// @ts-ignore
return window.__topLoading__
}
// 显示顶部进度条
const showLoading = () => { const showLoading = () => {
if (requestCount === 0 && !loadingInstance) { if (requestCount === 0) {
loadingInstance = ElLoading.service({ getTopLoading()?.start()
lock: true, // 锁定屏幕滚动
fullscreen: true, // 全屏显示
})
} }
requestCount++ requestCount++
} }
// 关闭loading的方法 // 关闭顶部进度条
const hideLoading = () => { const hideLoading = () => {
requestCount-- requestCount--
if (requestCount <= 0) { if (requestCount <= 0) {
if (loadingInstance) { getTopLoading()?.complete()
loadingInstance.close() requestCount = 0
loadingInstance = null
}
requestCount = 0 // 重置计数,防止负数
} }
} }
@ -49,10 +47,8 @@ const service = axios.create({
const requestInterceptor = (config: InternalAxiosRequestConfig) => { const requestInterceptor = (config: InternalAxiosRequestConfig) => {
console.log('请求配置', config) console.log('请求配置', config)
// 显示loading可通过config配置项控制是否显示比如某些请求不需要loading // 显示顶部进度条
if (config.headers?.showLoading !== false) {
showLoading() showLoading()
}
// 添加token到请求头 // 添加token到请求头
const token = getStorage('token') const token = getStorage('token')
if (token) { if (token) {

View File

@ -14,3 +14,8 @@ export const getSelfVarietyPointDiffConfigApi = (params: any) => {
export const editCustomerApi = (params: any) => { export const editCustomerApi = (params: any) => {
return request.post('/agent/editAgent', {...params}) return request.post('/agent/editAgent', {...params})
} }
//编辑代理客户接口
export const updateToAgent = (params: any) => {
return request.post('/agent/updateToAgent', {...params})
}

View File

@ -16,7 +16,7 @@
<template #footer v-if="showFooter"> <template #footer v-if="showFooter">
<slot name="footer"> <slot name="footer">
<el-button @click="handleCancel">取消</el-button> <el-button @click="handleCancel">取消</el-button>
<el-button type="primary" @click="handleConfirm">确定</el-button> <el-button type="primary" :loading="buttonLoading" @click="handleConfirm">确定</el-button>
</slot> </slot>
</template> </template>
</el-dialog> </el-dialog>
@ -31,6 +31,7 @@ interface Props {
type?: string type?: string
showFooter?: boolean showFooter?: boolean
fullscreen?: boolean fullscreen?: boolean
buttonLoading?: boolean
} }
interface Emits { interface Emits {
@ -45,6 +46,7 @@ const props = withDefaults(defineProps<Props>(), {
destroyOnClose: true, destroyOnClose: true,
showFooter: true, showFooter: true,
fullscreen: false, fullscreen: false,
buttonLoading: false,
}) })
const emit = defineEmits<Emits>() const emit = defineEmits<Emits>()

View File

@ -44,10 +44,10 @@
<!-- 操作按钮 --> <!-- 操作按钮 -->
<el-form-item> <el-form-item>
<el-button type="primary" @click="$emit('search')"> <el-button type="primary" :loading="buttonLoading" @click="$emit('search')">
{{ buttonConfig.searchText || '搜索' }} {{ buttonConfig.searchText || '搜索' }}
</el-button> </el-button>
<el-button @click="$emit('reset')"> <el-button :loading="buttonLoading" @click="$emit('reset')">
{{ buttonConfig.resetText || '重置' }} {{ buttonConfig.resetText || '重置' }}
</el-button> </el-button>
<!-- 按钮区插槽 --> <!-- 按钮区插槽 -->
@ -99,6 +99,10 @@ const props = defineProps({
type: Object as () => ButtonConfig, type: Object as () => ButtonConfig,
default: () => ({}), default: () => ({}),
}, },
buttonLoading: {
type: Boolean,
default: false,
},
}) })
// //

View File

@ -8,10 +8,16 @@
:tree-props="treeProps" :tree-props="treeProps"
:border="border" :border="border"
:highlight-current-row="highlightCurrentRow" :highlight-current-row="highlightCurrentRow"
v-loading="loading"
v-bind="$attrs" v-bind="$attrs"
@row-click="handleRowClick" @row-click="handleRowClick"
@row-contextmenu="handleRowContextmenu" @row-contextmenu="handleRowContextmenu"
@selection-change="handleSelectionChange"
@current-change="handleCurrentChange"
> >
<!-- 勾选列 -->
<el-table-column v-if="showSelection" type="selection" width="55" align="center" />
<!-- 动态表头列 --> <!-- 动态表头列 -->
<template v-for="(column, index) in columns" :key="index"> <template v-for="(column, index) in columns" :key="index">
<el-table-column <el-table-column
@ -28,7 +34,11 @@
:width="column.width" :width="column.width"
:align="column.align || 'left'" :align="column.align || 'left'"
:sortable="column.sortable" :sortable="column.sortable"
/> >
<template v-if="column.isNumber" #default="{ row }">
<span>{{ formatDecimalTruncate(row[column.prop]) }}</span>
</template>
</el-table-column>
<!-- 具名插槽列 --> <!-- 具名插槽列 -->
<el-table-column <el-table-column
@ -49,6 +59,7 @@
<script setup lang="ts"> <script setup lang="ts">
import { ref, watch } from 'vue' import { ref, watch } from 'vue'
import type { TableInstance } from 'element-plus' import type { TableInstance } from 'element-plus'
import { formatDecimalTruncate } from '@/utils/formatDecimal'
// //
interface TableColumn { interface TableColumn {
@ -59,6 +70,7 @@ interface TableColumn {
align?: 'left' | 'center' | 'right' align?: 'left' | 'center' | 'right'
slotName?: string // slotName?: string //
sortable?: boolean // sortable?: boolean //
isNumber?: boolean //
} }
// //
@ -71,19 +83,39 @@ interface Props {
columns?: TableColumn[] columns?: TableColumn[]
border?: boolean // border?: boolean //
highlightCurrentRow?: boolean // highlightCurrentRow?: boolean //
loading?: boolean //
showSelection?: boolean //
} }
const props = withDefaults(defineProps<Props>(), { const props = withDefaults(defineProps<Props>(), {
border: true, border: true,
highlightCurrentRow: true, highlightCurrentRow: true,
loading: false,
showSelection: false,
}) })
// //
const emit = defineEmits<{ const emit = defineEmits<{
(e: 'row-click', row: any): void // (e: 'row-click', row: any): void //
(e: 'row-contextmenu', row: any, column: any, event: MouseEvent): void (e: 'row-contextmenu', row: any, column: any, event: MouseEvent): void
(e: 'selection-change', rows: any[]): void //
(e: 'current-change', row: any): void //
}>() }>()
//
const handleSelectionChange = (rows: any[]) => {
emit('selection-change', rows)
}
//
const handleCurrentChange = (row: any) => {
// null Vue
if (row === null || row === undefined) {
return
}
emit('current-change', row)
}
const tableRef = ref<TableInstance>() const tableRef = ref<TableInstance>()
// //
@ -100,7 +132,7 @@ const handleRowClick = (row: any) => {
if (selectedRow.value && selectedRow.value === row) { if (selectedRow.value && selectedRow.value === row) {
// //
selectedRow.value = null selectedRow.value = null
tableRef.value?.setCurrentRow() // [^14^] tableRef.value?.setCurrentRow(null) // null
} else { } else {
// //
selectedRow.value = row selectedRow.value = row
@ -124,16 +156,29 @@ watch(
() => { () => {
// //
selectedRow.value = null selectedRow.value = null
tableRef.value?.setCurrentRow() // [^14^] tableRef.value?.setCurrentRow(null)
}, },
{ deep: true }, // { deep: true }, //
) )
// loading
watch(
() => props.loading,
(newVal) => {
// loading
console.log('Table loading status changed:', newVal)
},
{ flush: 'sync' }, // 使 sync
)
// //
defineExpose({ defineExpose({
clearCurrentRow: () => { clearCurrentRow: () => {
selectedRow.value = null selectedRow.value = null
tableRef.value?.setCurrentRow() tableRef.value?.setCurrentRow(null)
},
setCurrentRow: (row: any) => {
tableRef.value?.setCurrentRow(row)
}, },
}) })
</script> </script>

View File

@ -0,0 +1,62 @@
<template>
<Teleport to="body">
<div v-if="visible" class="top-loading-bar">
<div class="loading-progress" :style="{ width: progress + '%' }"></div>
</div>
</Teleport>
</template>
<script setup lang="ts">
import { ref } from 'vue'
const visible = ref(false)
const progress = ref(0)
let timer: ReturnType<typeof setInterval> | null = null
const start = () => {
visible.value = true
progress.value = 0
// 90%
timer = setInterval(() => {
if (progress.value < 90) {
progress.value += Math.random() * 15 + 5
}
}, 150)
}
const complete = () => {
if (timer) {
clearInterval(timer)
timer = null
}
progress.value = 100
setTimeout(() => {
visible.value = false
progress.value = 0
}, 200)
}
//
defineExpose({
start,
complete,
})
</script>
<style scoped lang="scss">
.top-loading-bar {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 3px;
z-index: 9999;
background: transparent;
}
.loading-progress {
height: 100%;
background: #409eff; // Element Plus
transition: width 0.15s linear;
}
</style>

View File

@ -11,7 +11,7 @@
<!-- 右侧内容区页签 + 主体内容 --> <!-- 右侧内容区页签 + 主体内容 -->
<section class="content-container"> <section class="content-container">
<!-- 页签标签栏 --> <!-- 页签标签栏 -->
<!-- <Tabs /> --> <Tabs />
<!-- 主体内容区 --> <!-- 主体内容区 -->
<main class="main-content"> <main class="main-content">
@ -22,9 +22,9 @@
> >
<router-view v-slot="{ Component }"> <router-view v-slot="{ Component }">
<KeepAlive v-if="route.meta.keepAlive"> <KeepAlive v-if="route.meta.keepAlive">
<component :is="Component" :key="route.path" /> <component :is="Component" :key="refreshKey" />
</KeepAlive> </KeepAlive>
<component v-else :is="Component" :key="route.path" /> <component v-else :is="Component" :key="refreshKey" />
</router-view> </router-view>
</transition> </transition>
</main> </main>
@ -40,8 +40,19 @@ defineOptions({
import Header from './Header.vue' import Header from './Header.vue'
import Sidebar from './Sidebar.vue' import Sidebar from './Sidebar.vue'
import Tabs from './Tabs.vue' import Tabs from './Tabs.vue'
import { useTabsStore } from '@/stores/modules/tabs'
const route = useRoute() const route = useRoute()
const tabsStore = useTabsStore()
// key
const refreshKey = computed(() => {
// refreshKey
if (tabsStore.refreshKey.startsWith(route.path)) {
return tabsStore.refreshKey
}
return route.path
})
// //
const sidebarCollapsed = ref(false) const sidebarCollapsed = ref(false)

View File

@ -82,7 +82,11 @@
:style="contextMenuStyle" :style="contextMenuStyle"
@click.stop @click.stop
> >
<div class="context-menu-item" @click="handleContextMenuCommand('refresh')"> <div
class="context-menu-item"
:class="{ 'is-disabled': !canRefresh }"
@click="canRefresh && handleContextMenuCommand('refresh')"
>
<el-icon><Refresh /></el-icon> <el-icon><Refresh /></el-icon>
<span>刷新当前页</span> <span>刷新当前页</span>
</div> </div>
@ -140,6 +144,12 @@ const canCloseCurrent = computed(() => {
return !isFixedTab(path) return !isFixedTab(path)
}) })
// tab
const canRefresh = computed(() => {
const path = contextMenuPath.value || ''
return path === route.path
})
// //
const canCloseOther = computed(() => { const canCloseOther = computed(() => {
const path = contextMenuPath.value || tabsStore.activeTab || '' const path = contextMenuPath.value || tabsStore.activeTab || ''
@ -393,11 +403,32 @@ const handleDragEnd = () => {
const handleContextMenuCommand = (command: string) => { const handleContextMenuCommand = (command: string) => {
// 使 // 使
const currentPath = contextMenuPath.value || tabsStore.activeTab const currentPath = contextMenuPath.value || tabsStore.activeTab
//
const rightClickedPath = contextMenuPath.value
closeContextMenu() closeContextMenu()
switch (command) { switch (command) {
case 'refresh': case 'refresh':
router.go(0) // tab tab
const refreshPath = rightClickedPath || tabsStore.activeTab
if (refreshPath) {
const targetTab = tabsStore.tabs.find(item => item.path === refreshPath)
// activeTab
if (refreshPath !== route.path) {
tabsStore.setActiveTab(refreshPath)
router.push({ path: refreshPath, query: targetTab?.query }).then(() => {
//
nextTick(() => {
tabsStore.refreshTab(refreshPath)
})
})
} else {
//
tabsStore.refreshTab(refreshPath)
}
}
break break
case 'close': case 'close':
if (currentPath) { if (currentPath) {

View File

@ -0,0 +1,44 @@
import { ref } from 'vue'
/**
*
* @param initialLoading loading false
*/
export function usePageLoading(initialLoading = false) {
const loading = ref(initialLoading)
const startLoading = () => {
loading.value = true
}
const stopLoading = () => {
loading.value = false
}
return {
loading,
startLoading,
stopLoading,
}
}
/**
*
*/
export function useButtonLoading() {
const buttonLoading = ref(false)
const startButtonLoading = () => {
buttonLoading.value = true
}
const stopButtonLoading = () => {
buttonLoading.value = false
}
return {
buttonLoading,
startButtonLoading,
stopButtonLoading,
}
}

View File

@ -241,6 +241,14 @@ export const useTabsStore = defineStore('tabs', () => {
saveTabs() saveTabs()
} }
// 刷新指定 tab 的 key用于触发组件刷新
const refreshKey = ref<string>('')
// 刷新指定路径的 tab
const refreshTab = (path: string) => {
refreshKey.value = path + '-' + Date.now()
}
// 保存到本地存储 // 保存到本地存储
const saveTabs = () => { const saveTabs = () => {
setStorage('tabs', tabs.value) setStorage('tabs', tabs.value)
@ -283,6 +291,8 @@ export const useTabsStore = defineStore('tabs', () => {
closeRightTabs, closeRightTabs,
setActiveTab, setActiveTab,
clearTabs, clearTabs,
setTabs setTabs,
refreshKey,
refreshTab
} }
}) })

View File

@ -0,0 +1,46 @@
/**
* @description:
*/
/**
* @description:
* @param value
* @param decimals 2
* @returns
*/
export function formatDecimalTruncate(value: string | number | null | undefined, decimals: number = 2): string | number {
// 处理空值
if (value === null || value === undefined || value === '') {
return value ?? ''
}
// 转换为字符串检查原始格式
const originalStr = String(value)
// 转换为数字检查是否有效
const num = Number(value)
// 如果不是有效数字,返回原值
if (isNaN(num)) {
return value
}
// 分割整数和小数部分
const dotIndex = originalStr.indexOf('.')
// 如果没有小数点,添加小数位后返回
if (dotIndex === -1) {
return `${originalStr}.${'0'.repeat(decimals)}`
}
const integerPart = originalStr.substring(0, dotIndex)
const decimalPart = originalStr.substring(dotIndex + 1)
// 截取到指定小数位(不四舍五入)
const truncatedDecimal = decimalPart.substring(0, decimals)
// 补齐小数位到指定位数
const paddedDecimal = truncatedDecimal.padEnd(decimals, '0')
return `${integerPart}.${paddedDecimal}`
}

View File

@ -1,3 +1,6 @@
import dayjs from 'dayjs'
/** /**
* *
* @param format 'YYYY-MM-DD HH:mm:ss' * @param format 'YYYY-MM-DD HH:mm:ss'
@ -37,22 +40,22 @@ export const getRecentSevenDays = (format = 'YYYY-MM-DD HH:mm:ss'):{ start: stri
* @returns {string} UTC+8 YYYY-MM-DD HH:mm:ss * @returns {string} UTC+8 YYYY-MM-DD HH:mm:ss
*/ */
export function utcToUtc8(utcStr: string): string { export function utcToUtc8(utcStr: string): string {
// 将字符串解析为 UTC 时间 // // 将字符串解析为 UTC 时间
const utcDate = new Date(utcStr.replace(' ', 'T') + 'Z') // const utcDate = new Date(utcStr.replace(' ', 'T') + 'Z')
// 转换为 UTC+8东八区 // // 转换为 UTC+8东八区
const utc8Date = new Date(utcDate.getTime() + 8 * 60 * 60 * 1000) // const utc8Date = new Date(utcDate.getTime() + 8 * 60 * 60 * 1000)
// 格式化为 YYYY-MM-DD HH:mm:ss // // 格式化为 YYYY-MM-DD HH:mm:ss
const pad = (n) => String(n).padStart(2, '0') // const pad = (n) => String(n).padStart(2, '0')
const year = utc8Date.getUTCFullYear() // const year = utc8Date.getUTCFullYear()
const month = pad(utc8Date.getUTCMonth() + 1) // const month = pad(utc8Date.getUTCMonth() + 1)
const day = pad(utc8Date.getUTCDate()) // const day = pad(utc8Date.getUTCDate())
const hour = pad(utc8Date.getUTCHours()) // const hour = pad(utc8Date.getUTCHours())
const minute = pad(utc8Date.getUTCMinutes()) // const minute = pad(utc8Date.getUTCMinutes())
const second = pad(utc8Date.getUTCSeconds()) // const second = pad(utc8Date.getUTCSeconds())
return `${year}-${month}-${day} ${hour}:${minute}:${second}` return dayjs(utcStr).format('YYYY-MM-DD HH:mm:ss')
} }

View File

@ -1,12 +1,12 @@
<template> <template>
<div class="table_form-container"> <div class="table_form-container">
<!-- 搜索 --> <!-- 搜索 -->
<CustomerListSearch :search-form="searchForm" :search-options="searchOptions" @search="handleSearch" <CustomerListSearch :search-form="searchForm" :search-options="searchOptions" :button-loading="searchLoading" @search="handleSearch"
@reset="handleReset"></CustomerListSearch> @reset="handleReset"></CustomerListSearch>
<!-- 表格 --> <!-- 表格 -->
<CustomerListTable :table-data="tableTree" :columns="tableColumnsOptions" row-key="userId" :tree-props="treeProps" <CustomerListTable :table-data="tableTree" :columns="tableColumnsOptions" row-key="userId" :tree-props="treeProps"
:default-sort="{ prop: 'createTime', order: 'descending' }" @expand-change="handleGetSubList"> :default-sort="{ prop: 'createTime', order: 'descending' }" :loading="loading" @expand-change="handleGetSubList">
<template #marginRatio="{ row }"> <template #marginRatio="{ row }">
<el-button v-if="row.role_id === 5" type="primary" size="small" @click="handleMarginRatio(row)"> <el-button v-if="row.role_id === 5" type="primary" size="small" @click="handleMarginRatio(row)">
修改 修改
@ -40,7 +40,7 @@
<!-- dialog --> <!-- dialog -->
<CustomerListDialog :visible="dialogVisible" :title="dialogTitle" :type="dialogType" :fullscreen="dialogFullscreen" <CustomerListDialog :visible="dialogVisible" :title="dialogTitle" :type="dialogType" :fullscreen="dialogFullscreen"
:show-footer="dialogType !== 'view'" @confirm="handleSubmit" @cancel="handleCancel" @close="handleClose"> :show-footer="dialogType !== 'view'" :button-loading="submitLoading" @confirm="handleSubmit" @cancel="handleCancel" @close="handleClose">
<CustomerListTable :table-data="marginRatioTree" :columns="marginRatioTableColumnsOptions" row-key="id" <CustomerListTable :table-data="marginRatioTree" :columns="marginRatioTableColumnsOptions" row-key="id"
v-if="dialogType === 'editMarginRatio'" style="height: 600px"> v-if="dialogType === 'editMarginRatio'" style="height: 600px">
<template #assignedPointDiff="{ row }"> <template #assignedPointDiff="{ row }">
@ -57,7 +57,7 @@
</CustomerListDialog> </CustomerListDialog>
<!-- 调整金额弹窗 --> <!-- 调整金额弹窗 -->
<CustomerListDialog :visible="adjustAmountVisible" title="调整金额" type="adjust" @confirm="handleAdjustAmountSubmit" <CustomerListDialog :visible="adjustAmountVisible" title="调整金额" type="adjust" :button-loading="adjustAmountLoading" @confirm="handleAdjustAmountSubmit"
@cancel="handleAdjustAmountCancel" @close="handleAdjustAmountClose"> @cancel="handleAdjustAmountCancel" @close="handleAdjustAmountClose">
<el-form :model="adjustAmountForm" :rules="adjustAmountRules" ref="adjustAmountFormRef" label-width="120px"> <el-form :model="adjustAmountForm" :rules="adjustAmountRules" ref="adjustAmountFormRef" label-width="120px">
<el-form-item label="账号名" prop="username"> <el-form-item label="账号名" prop="username">
@ -81,7 +81,7 @@
<!-- 修改客户弹窗 --> <!-- 修改客户弹窗 -->
<CustomerListDialog :visible="editCustomerVisible" title="" type="editCustomer" <CustomerListDialog :visible="editCustomerVisible" title="" type="editCustomer"
@confirm="handleEditCustomerSubmit" @cancel="handleEditCustomerCancel" @close="handleEditCustomerClose"> :button-loading="editCustomerLoading" @confirm="handleEditCustomerSubmit" @cancel="handleEditCustomerCancel" @close="handleEditCustomerClose">
<el-form :model="editCustomerForm" :rules="editCustomerRules" ref="editCustomerFormRef" label-width="140px"> <el-form :model="editCustomerForm" :rules="editCustomerRules" ref="editCustomerFormRef" label-width="140px">
<!-- <el-form-item label="手续费模板" prop="feeSettingId"> <!-- <el-form-item label="手续费模板" prop="feeSettingId">
<el-select v-model="editCustomerForm.feeSettingId" placeholder="请选择手续费模板" style="width: 100%"> <el-select v-model="editCustomerForm.feeSettingId" placeholder="请选择手续费模板" style="width: 100%">
@ -126,6 +126,7 @@ defineOptions({
// //
import CustomerListSearch from '@/components/common/Search.vue' import CustomerListSearch from '@/components/common/Search.vue'
import CustomerListTable from '@/components/common/Table.vue' import CustomerListTable from '@/components/common/Table.vue'
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'
@ -170,6 +171,11 @@ const searchForm = ref({
const searchOptions = ref(search) const searchOptions = ref(search)
const tableColumnsOptions = ref(tableColumns) const tableColumnsOptions = ref(tableColumns)
const { loading, startLoading, stopLoading } = usePageLoading()
const { buttonLoading: searchLoading, startButtonLoading: startSearchLoading, stopButtonLoading: stopSearchLoading } = useButtonLoading()
const { buttonLoading: submitLoading, startButtonLoading: startSubmitLoading, stopButtonLoading: stopSubmitLoading } = useButtonLoading()
const { buttonLoading: adjustAmountLoading, startButtonLoading: startAdjustAmountLoading, stopButtonLoading: stopAdjustAmountLoading } = useButtonLoading()
const { buttonLoading: editCustomerLoading, startButtonLoading: startEditCustomerLoading, stopButtonLoading: stopEditCustomerLoading } = useButtonLoading()
/** /**
* @description: 定义表格数据 * @description: 定义表格数据
*/ */
@ -323,6 +329,8 @@ const roleMap: any = {
*/ */
// //
const getCustomerList = async (id: string) => { const getCustomerList = async (id: string) => {
startLoading()
try {
const params = { const params = {
user_id: id, user_id: id,
page: currentPage.value, page: currentPage.value,
@ -363,10 +371,16 @@ const getCustomerList = async (id: string) => {
total.value = data.total total.value = data.total
currentPage.value = data.current_page currentPage.value = data.current_page
pageSize.value = Number(data.per_page) pageSize.value = Number(data.per_page)
} catch (err) {
console.error('获取客户列表失败:', err)
} finally {
stopLoading()
}
} }
// //
const getSelfVarietyPointDiffConfig = async (id: string) => { const getSelfVarietyPointDiffConfig = async (id: string) => {
try {
const params = { const params = {
user_id: id, user_id: id,
} }
@ -381,6 +395,9 @@ const getSelfVarietyPointDiffConfig = async (id: string) => {
} }
}) })
console.log('marginRatioTree', marginRatioTree.value) console.log('marginRatioTree', marginRatioTree.value)
} catch (err) {
console.error('获取代理点差数据失败:', err)
}
} }
/** /**
@ -394,7 +411,15 @@ onMounted(() => {
* @description: 定义搜索方法 * @description: 定义搜索方法
*/ */
const handleSearch = async () => { const handleSearch = async () => {
getCustomerList(userId.value) currentPage.value = 1
startSearchLoading()
try {
await getCustomerList(userId.value)
} catch (err) {
console.error('搜索失败:', err)
} finally {
stopSearchLoading()
}
} }
const handleReset = () => { const handleReset = () => {
searchForm.value = { searchForm.value = {
@ -403,7 +428,7 @@ const handleReset = () => {
endTime: '', endTime: '',
status: '', status: '',
} }
getCustomerList(userId.value) handleSearch()
} }
/** /**
@ -444,6 +469,7 @@ const handleCommissionRatio = (row: any) => {
// //
const handleGetSubList = async (row: any) => { const handleGetSubList = async (row: any) => {
try {
const params = { const params = {
user_name: '', user_name: '',
user_id: row.userId, user_id: row.userId,
@ -480,6 +506,9 @@ const handleGetSubList = async (row: any) => {
] ]
: undefined, : undefined,
})) }))
} catch (err) {
console.error('获取子列表失败:', err)
}
} }
/** /**
@ -496,6 +525,8 @@ const handlePaginationChange = (page: { currentPage: number; pageSize: number })
*/ */
// //
const handleSubmit = async () => { const handleSubmit = async () => {
startSubmitLoading()
try {
const params = { const params = {
user_id: selectId.value, user_id: selectId.value,
toucun_percent: '', toucun_percent: '',
@ -528,7 +559,7 @@ const handleSubmit = async () => {
dialogVisible.value = false dialogVisible.value = false
await getCustomerList(userId.value) await getCustomerList(userId.value)
} catch (err) { } catch (err) {
console.log('提交失败:', err) console.error('提交失败:', err)
} }
} }
if (dialogType.value === 'editCommissionRatio') { if (dialogType.value === 'editCommissionRatio') {
@ -541,9 +572,12 @@ const handleSubmit = async () => {
dialogVisible.value = false dialogVisible.value = false
await getCustomerList(userId.value) await getCustomerList(userId.value)
} catch (err) { } catch (err) {
console.log('提交失败:', err) console.error('提交失败:', err)
} }
} }
} finally {
stopSubmitLoading()
}
} }
// //
const handleCancel = () => { const handleCancel = () => {
@ -583,6 +617,7 @@ const handleAdjustAmountInput = () => {
// //
const handleAdjustAmountSubmit = async () => { const handleAdjustAmountSubmit = async () => {
startAdjustAmountLoading()
try { try {
await adjustAmountFormRef.value?.validate() await adjustAmountFormRef.value?.validate()
await updateCapitalApi({ await updateCapitalApi({
@ -593,6 +628,8 @@ const handleAdjustAmountSubmit = async () => {
adjustAmountVisible.value = false adjustAmountVisible.value = false
} catch (err) { } catch (err) {
console.error('调整金额表单验证失败', err) console.error('调整金额表单验证失败', err)
} finally {
stopAdjustAmountLoading()
} }
} }
@ -655,6 +692,7 @@ const handleEditCustomer = async (row: any) => {
// //
const handleEditCustomerSubmit = async () => { const handleEditCustomerSubmit = async () => {
startEditCustomerLoading()
try { try {
await editCustomerFormRef.value?.validate() await editCustomerFormRef.value?.validate()
await editUserApi({ await editUserApi({
@ -669,6 +707,8 @@ const handleEditCustomerSubmit = async () => {
editCustomerVisible.value = false editCustomerVisible.value = false
} catch (err) { } catch (err) {
console.error('修改客户表单验证失败', err) console.error('修改客户表单验证失败', err)
} finally {
stopEditCustomerLoading()
} }
} }

View File

@ -4,10 +4,12 @@
<FundDetailHeader title="入金货币" :buttons="buttonsOptions" @button-click="handleButtonClick"> <FundDetailHeader title="入金货币" :buttons="buttonsOptions" @button-click="handleButtonClick">
</FundDetailHeader> </FundDetailHeader>
<!-- 表格 --> <!-- 表格 -->
<FundDetailTable :table-data="tableData" :columns="tableColumnsOptions" @row-click="handleRowClick" row-key="id" /> <FundDetailTable :table-data="tableData" :columns="tableColumnsOptions" @row-click="handleRowClick" row-key="id"
<FundDetailDialog :visible="visible" :title="dialogTitle" @close="visible = false" @cancel="visible = false" :loading="loading" />
@confirm="handleConfirm"> <FundDetailDialog :visible="visible" :title="dialogTitle" :button-loading="buttonLoading" @close="visible = false"
<FundDetailForm :formData="formData" :formItems="formItems" :formRules="formRules" :optionsMap="optionsMap"> @cancel="visible = false" @confirm="handleConfirm">
<FundDetailForm :formData="formData" ref="commissionRatioFormRef" :formItems="formItems" :formRules="formRules"
:optionsMap="optionsMap">
</FundDetailForm> </FundDetailForm>
</FundDetailDialog> </FundDetailDialog>
</div> </div>
@ -20,6 +22,7 @@ defineOptions({
}) })
import FundDetailHeader from '@/components/common/header.vue' import FundDetailHeader from '@/components/common/header.vue'
import FundDetailTable from '@/components/common/Table.vue' import FundDetailTable from '@/components/common/Table.vue'
import { usePageLoading, useButtonLoading } from '@/composables/useLoading'
import FundDetailDialog from '@/components/common/Dialog.vue' import FundDetailDialog from '@/components/common/Dialog.vue'
import FundDetailForm from '@/components/common/Form.vue' import FundDetailForm from '@/components/common/Form.vue'
import type { ButtonProps } from 'element-plus' import type { ButtonProps } from 'element-plus'
@ -41,11 +44,14 @@ interface HeaderButton {
key?: string | number // key?: string | number //
} }
const { loading, startLoading, stopLoading } = usePageLoading()
const { buttonLoading, startButtonLoading, stopButtonLoading } = useButtonLoading()
const buttonsOptions = ref(buttons) const buttonsOptions = ref(buttons)
const formItems = ref(form) const formItems = ref(form)
const tableData = ref([]) const tableData = ref([])
const tableColumnsOptions = ref(tableColumns) const tableColumnsOptions = ref(tableColumns)
const dialogTitle = ref('') const dialogTitle = ref('')
const commissionRatioFormRef = ref()
const formRules = ref(rules) const formRules = ref(rules)
const formData = ref({ const formData = ref({
name: '', name: '',
@ -70,7 +76,11 @@ let rowData: any = {}
const visible = ref<boolean>(false) const visible = ref<boolean>(false)
const getTableList = async () => { const getTableList = async () => {
try {
startLoading()
const data = await getCurrencyList() const data = await getCurrencyList()
tableData.value = (data || []).map((item: any) => { tableData.value = (data || []).map((item: any) => {
return { return {
...item, ...item,
@ -79,6 +89,9 @@ const getTableList = async () => {
} }
}) })
console.log('tableData.value', tableData.value) console.log('tableData.value', tableData.value)
} finally {
stopLoading()
}
} }
const handleRowClick = (row: any) => { const handleRowClick = (row: any) => {
@ -136,6 +149,9 @@ const handleEdit = () => {
} }
const handleConfirm = async () => { const handleConfirm = async () => {
await commissionRatioFormRef.value.validate()
startButtonLoading()
try {
if (dialogTitle.value === '添加') { if (dialogTitle.value === '添加') {
await createCurrency(formData.value) await createCurrency(formData.value)
} else { } else {
@ -144,6 +160,9 @@ const handleConfirm = async () => {
visible.value = false visible.value = false
resetForm() resetForm()
await getTableList() await getTableList()
} finally {
stopButtonLoading()
}
} }
onMounted(() => { onMounted(() => {

View File

@ -1,25 +1,17 @@
<template> <template>
<div class="table_form-container"> <div class="table_form-container">
<!-- 搜索 --> <!-- 搜索 -->
<FundDetailSearch <FundDetailSearch :search-form="searchForm" :search-options="searchOptions" @search="handleSearch"
:search-form="searchForm" @reset="handleReset">
:search-options="searchOptions"
@search="handleSearch"
@reset="handleReset"
>
<template #button="{ form }"> <template #button="{ form }">
<el-button type="primary" @click="handleExport()"> 导出 </el-button> <el-button type="primary" @click="handleExport()"> 导出 </el-button>
</template> </template>
</FundDetailSearch> </FundDetailSearch>
<!-- 表格 --> <!-- 表格 -->
<FundDetailTable :table-data="tableTree" :columns="tableColumnsOptions" row-key="id" /> <FundDetailTable :table-data="tableTree" :columns="tableColumnsOptions" row-key="id" :loading="loading" />
<!-- 分页 --> <!-- 分页 -->
<FundDetailPagination <FundDetailPagination v-model="currentPage" v-model:pageSizeValue="pageSize" :total="total"
v-model="currentPage" @pagination-change="handlePaginationChange" />
v-model:pageSizeValue="pageSize"
:total="total"
@pagination-change="handlePaginationChange"
/>
</div> </div>
</template> </template>
@ -32,6 +24,7 @@ defineOptions({
import FundDetailSearch from '@/components/common/Search.vue' import FundDetailSearch from '@/components/common/Search.vue'
import FundDetailTable from '@/components/common/Table.vue' import FundDetailTable from '@/components/common/Table.vue'
import FundDetailPagination from '@/components/common/Pagination.vue' import FundDetailPagination from '@/components/common/Pagination.vue'
import { usePageLoading } from '@/composables/useLoading'
// //
import { search, tableColumns } from './options' import { search, tableColumns } from './options'
// //
@ -51,6 +44,7 @@ const searchForm = ref({
startTime: initTime.start, startTime: initTime.start,
endTime: initTime.end, endTime: initTime.end,
}) })
const { loading, startLoading, stopLoading } = usePageLoading()
/** /**
* @description: 定义表格数据 * @description: 定义表格数据
*/ */
@ -68,6 +62,8 @@ const total = ref(0)
*/ */
// //
const getFundDetailList = async () => { const getFundDetailList = async () => {
startLoading()
try {
const params = { const params = {
page: currentPage.value, page: currentPage.value,
page_size: pageSize.value, page_size: pageSize.value,
@ -102,6 +98,9 @@ const getFundDetailList = async () => {
total.value = data.total total.value = data.total
currentPage.value = data.current_page currentPage.value = data.current_page
pageSize.value = Number(data.per_page) pageSize.value = Number(data.per_page)
} finally {
stopLoading()
}
} }
/** /**

View File

@ -62,9 +62,9 @@ export const tableColumns = [
//资金流向 //资金流向
// { prop: 'fundDirection', label: '资金流向' }, // { prop: 'fundDirection', label: '资金流向' },
//原有金额 //原有金额
{ prop: 'amount', label: '变动金额' }, { prop: 'amount', label: '变动金额', isNumber: true },
//余额变动 //余额变动
{ prop: 'balance', label: '变动后余额' }, { prop: 'balance', label: '变动后余额' , isNumber: true},
//现有余额 //现有余额
// { prop: 'currentBalance', label: '现有余额' }, // { prop: 'currentBalance', label: '现有余额' },
//状态 //状态

View File

@ -4,7 +4,7 @@
<div class="page-title">存款</div> <div class="page-title">存款</div>
<!-- 存款渠道列表每行提供一个"存款"按钮 --> <!-- 存款渠道列表每行提供一个"存款"按钮 -->
<RechangeTable :table-data="channelList" :columns="tableColumns" :highlight-current-row="false"> <RechangeTable :table-data="channelList" :columns="tableColumns" :highlight-current-row="false" :loading="loading">
<template #action="{ row }"> <template #action="{ row }">
<el-button type="primary" size="small" @click="handleOpenDialog(row)"> <el-button type="primary" size="small" @click="handleOpenDialog(row)">
存款 存款
@ -113,6 +113,7 @@ import { ElMessage } from 'element-plus'
import { nextTick, onMounted, ref } from 'vue' import { nextTick, onMounted, ref } from 'vue'
import RechangeTable from '@/components/common/Table.vue' import RechangeTable from '@/components/common/Table.vue'
import RechangeDialog from '@/components/common/Dialog.vue' import RechangeDialog from '@/components/common/Dialog.vue'
import { usePageLoading } from '@/composables/useLoading'
import { rechangeTableColumns } from './options' import { rechangeTableColumns } from './options'
import { uploadImageApi } from '@/api/modules/upload' import { uploadImageApi } from '@/api/modules/upload'
import { getRechargeChannelList, rechargeApi } from '@/api/modules/capital/rechangeChanl' import { getRechargeChannelList, rechargeApi } from '@/api/modules/capital/rechangeChanl'
@ -151,6 +152,7 @@ const submitLoading = ref(false)
const voucherUrl = ref('') const voucherUrl = ref('')
const uploadLoading = ref(false) const uploadLoading = ref(false)
const { loading, startLoading, stopLoading } = usePageLoading()
// //
const channelList = ref<ChannelRow[]>([]) const channelList = ref<ChannelRow[]>([])
@ -202,10 +204,13 @@ const rechangeFormRules = ref<FormRules<RechangeFormData>>({
// //
const fetchChannelList = async () => { const fetchChannelList = async () => {
startLoading()
try { try {
const res = await getRechargeChannelList() as ChannelRow[] const res = await getRechargeChannelList() as ChannelRow[]
stopLoading()
channelList.value = res || [] channelList.value = res || []
} catch (error) { } catch (error) {
stopLoading()
console.error('获取充值渠道列表失败', error) console.error('获取充值渠道列表失败', error)
} }
} }

View File

@ -6,6 +6,6 @@ export const rechangeTableColumns = [
label: '支持货币', label: '支持货币',
align: 'center' as const align: 'center' as const
}, },
{ prop: 'channel_fee', label: '渠道费用', align: 'center' as const }, { prop: 'channel_fee', label: '渠道费用', align: 'center' as const , isNumber: true },
{ label: '操作', slotName: 'action', align: 'center' as const, width: 120 }, { label: '操作', slotName: 'action', align: 'center' as const, width: 120 },
] ]

View File

@ -5,28 +5,14 @@
<el-button type="primary" @click="handleEdit" v-auth="'channel_editChannel'">编辑</el-button> <el-button type="primary" @click="handleEdit" v-auth="'channel_editChannel'">编辑</el-button>
<el-button type="danger" @click="handleDelete" v-auth="'channel_delChannel'">删除</el-button> <el-button type="danger" @click="handleDelete" v-auth="'channel_delChannel'">删除</el-button>
</div> </div>
<DepositChannelTable <DepositChannelTable ref="tableRef" :table-data="tableData" :columns="tableColumnsOptions" row-key="id"
ref="tableRef" :loading="loading" @rowClick="handleRowClick" />
:table-data="tableData" <el-dialog v-model="dialogVisible" :title="dialogTitleType === 1 ? '添加充值渠道' : '编辑充值渠道'" width="700">
:columns="tableColumnsOptions" <Form :formItems="formItems" ref="formRef" :formRules="formRules" :formData="formData" :optionsMap="optionsMap" />
row-key="id"
@rowClick="handleRowClick"
/>
<el-dialog
v-model="dialogVisible"
:title="dialogTitleType === 1 ? '添加充值渠道' : '编辑充值渠道'"
width="700"
>
<Form
:formItems="formItems"
:formRules="formRules"
:formData="formData"
:optionsMap="optionsMap"
/>
<template #footer> <template #footer>
<div class="dialog-footer"> <div class="dialog-footer">
<el-button @click="dialogVisible = false">取消</el-button> <el-button @click="dialogVisible = false">取消</el-button>
<el-button type="primary" @click="submitDetail">确认</el-button> <el-button type="primary" :loading="submitLoading" @click="submitDetail">确认</el-button>
</div> </div>
</template> </template>
</el-dialog> </el-dialog>
@ -40,6 +26,7 @@ defineOptions({
// //
import DepositChannelTable from '@/components/common/Table.vue' import DepositChannelTable from '@/components/common/Table.vue'
import Form from '@/components/common/Form.vue' import Form from '@/components/common/Form.vue'
import { usePageLoading, useButtonLoading } from '@/composables/useLoading'
import { tableColumns, formItemsData, formRulesData, optionsMapData } from './options' import { tableColumns, formItemsData, formRulesData, optionsMapData } from './options'
import { import {
getChannelList, getChannelList,
@ -65,7 +52,12 @@ const getStatusText = (status: number | string) => {
return ['', '正常', '禁用'][status] return ['', '正常', '禁用'][status]
} }
const formRef = ref()
const { loading, startLoading, stopLoading } = usePageLoading()
const { buttonLoading: submitLoading, startButtonLoading: startSubmitLoading, stopButtonLoading: stopSubmitLoading } = useButtonLoading()
const initData = async () => { const initData = async () => {
try {
startLoading()
const res = await getChannelList() const res = await getChannelList()
console.log(res) console.log(res)
tableData.value = res.map((item: ChannelType) => ({ tableData.value = res.map((item: ChannelType) => ({
@ -73,6 +65,9 @@ const initData = async () => {
statusStr: getStatusText(item.status), statusStr: getStatusText(item.status),
})) as ChannelType[] })) as ChannelType[]
clearSelection() clearSelection()
} finally {
stopLoading()
}
} }
// //
@ -102,12 +97,18 @@ const formItems = ref(formItemsData)
const formRules = ref(formRulesData) const formRules = ref(formRulesData)
const optionsMap = ref(optionsMapData) const optionsMap = ref(optionsMapData)
const submitDetail = async () => { const submitDetail = async () => {
dialogVisible.value = false await formRef.value.validate()
startSubmitLoading();
try {
if (dialogTitleType.value === 1) { if (dialogTitleType.value === 1) {
await createChannel(formData.value) await createChannel(formData.value)
} else { } else {
await editChannel(formData.value) await editChannel(formData.value)
} }
} finally {
dialogVisible.value = false
stopSubmitLoading();
}
await initData() await initData()
} }
@ -199,4 +200,3 @@ onMounted(() => {
} }
} }
</style> </style>

View File

@ -4,7 +4,7 @@ export const tableColumns = [
//存款渠道名称 //存款渠道名称
{ label: '渠道名称', prop: 'name' }, { label: '渠道名称', prop: 'name' },
//存款渠道费用 //存款渠道费用
{ label: '渠道费用', prop: 'channel_fee' }, { label: '渠道费用', prop: 'channel_fee', isNumber: true },
//存款渠道类型 //存款渠道类型
{ label: '入金货币', prop: 'currency.symbol' }, { label: '入金货币', prop: 'currency.symbol' },
// 接口地址 // 接口地址

View File

@ -19,36 +19,16 @@
</template> </template>
</RechangeSearch> </RechangeSearch>
<!-- 列表区域改为真实接口数据仍保留勾选列供后续审核动作使用 --> <!-- 列表区域使用公共 Table 组件 -->
<div class="table-wrapper"> <Table
<el-table
ref="tableRef" ref="tableRef"
v-loading="loading" :table-data="tableData"
:data="tableData" :columns="tableColumns"
border
height="100%"
row-key="id" row-key="id"
:loading="loading"
:show-selection="true"
@selection-change="handleSelectionChange" @selection-change="handleSelectionChange"
>
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="序号" width="70" align="center">
<template #default="scope">
{{ (currentPage - 1) * pageSize + scope.$index + 1 }}
</template>
</el-table-column>
<el-table-column
v-for="column in tableColumns"
:key="column.prop"
:prop="column.prop"
:label="column.label"
:min-width="column.width"
:align="column.align || 'center'"
show-overflow-tooltip
/> />
</el-table>
</div>
<!-- 分页区域使用接口返回的 total / current_page / per_page --> <!-- 分页区域使用接口返回的 total / current_page / per_page -->
<RechangePagination <RechangePagination
@ -65,9 +45,11 @@ defineOptions({
name: 'RechangeCheck', name: 'RechangeCheck',
}) })
import { ElMessage } from 'element-plus' import { ElMessage, ElMessageBox } from 'element-plus'
import { onMounted, ref } from 'vue' import { onMounted, ref } from 'vue'
import RechangeSearch from '@/components/common/Search.vue' import RechangeSearch from '@/components/common/Search.vue'
import Table from '@/components/common/Table.vue'
import { usePageLoading } from '@/composables/useLoading'
import RechangePagination from '@/components/common/Pagination.vue' import RechangePagination from '@/components/common/Pagination.vue'
import { exportToExcel } from '@/utils/exportToExcel' import { exportToExcel } from '@/utils/exportToExcel'
import { getRechargeCheckListApi, reviewRechargeOrder } from '@/api/modules/capital/rechargeCheck' import { getRechargeCheckListApi, reviewRechargeOrder } from '@/api/modules/capital/rechargeCheck'
@ -101,10 +83,10 @@ interface SearchFormData {
const searchOptions = ref(rechangeCheckSearch) const searchOptions = ref(rechangeCheckSearch)
const tableColumns = ref(rechangeCheckTableColumns) const tableColumns = ref(rechangeCheckTableColumns)
const { loading, startLoading, stopLoading } = usePageLoading()
// loading // loading
const tableData = ref<TableRow[]>([]) const tableData = ref<TableRow[]>([])
const selectedRows = ref<TableRow[]>([]) const selectedRows = ref<TableRow[]>([])
const loading = ref(false)
// //
const total = ref(0) const total = ref(0)
@ -137,7 +119,7 @@ const getRechargeStatusText = (status: string | number | null | undefined) => {
// page / page_size / start_time / end_time null // page / page_size / start_time / end_time null
const getList = async () => { const getList = async () => {
loading.value = true startLoading()
selectedRows.value = [] selectedRows.value = []
const params = { const params = {
@ -175,7 +157,7 @@ const getList = async () => {
total.value = 0 total.value = 0
console.error('获取充值审核列表失败', error) console.error('获取充值审核列表失败', error)
} finally { } finally {
loading.value = false stopLoading()
} }
} }
@ -220,10 +202,23 @@ const handleApprove = async () => {
ElMessage.warning('请先选择要通过的记录') ElMessage.warning('请先选择要通过的记录')
return return
} }
console.log(selectedRows.value) try {
await ElMessageBox.confirm(`是否确认通过该审核?订单号:${selectedRows.value[0].orderNo}`, '审核确认', {
confirmButtonText: '确认',
cancelButtonText: '取消',
type: 'warning',
})
} catch {
return
}
startLoading()
try {
await reviewRechargeOrder({ id: selectedRows.value[0].id, status: '3' }) await reviewRechargeOrder({ id: selectedRows.value[0].id, status: '3' })
ElMessage.success('审核通过')
await getList() await getList()
} finally {
stopLoading()
}
} }
// //
@ -232,10 +227,24 @@ const handleReject = async () => {
ElMessage.warning('请先选择要驳回的记录') ElMessage.warning('请先选择要驳回的记录')
return return
} }
try {
await ElMessageBox.confirm(`是否确认驳回该审核?订单号:${selectedRows.value[0].orderNo}`, '审核确认', {
confirmButtonText: '确认',
cancelButtonText: '取消',
type: 'warning',
})
} catch {
return
}
//:1,2,3,4 //:1,2,3,4
startLoading()
try {
await reviewRechargeOrder({ id: selectedRows.value[0].id, status: '4' }) await reviewRechargeOrder({ id: selectedRows.value[0].id, status: '4' })
ElMessage.success('审核驳回')
await getList() await getList()
} finally {
stopLoading()
}
} }
// //

View File

@ -39,12 +39,12 @@ export const rechangeCheckSearch = [
// 表格列配置:返回数据与 `rechargeOrder` 一致,因此按同一份结构展示。 // 表格列配置:返回数据与 `rechargeOrder` 一致,因此按同一份结构展示。
export const rechangeCheckTableColumns = [ export const rechangeCheckTableColumns = [
{ prop: 'orderNo', label: '订单号', align: 'center' as const, width: 220 }, { prop: 'orderNo', label: '订单号', align: 'center' as const, width: 220 },
{ prop: 'userName', label: '用户名', align: 'center' as const, width: 160 }, { prop: 'userName', label: '用户名', align: 'center' as const },
{ prop: 'currency', label: '充值币种', align: 'center' as const, width: 120 }, { prop: 'currency', label: '充值币种', align: 'center' as const, width: 120 },
{ prop: 'amount', label: '充值金额', align: 'center' as const, width: 120 }, { prop: 'amount', label: '充值金额', align: 'center' as const, width: 120, isNumber: true },
{ prop: 'realCurrency', label: '实际币种', align: 'center' as const, width: 120 }, { prop: 'realCurrency', label: '实际币种', align: 'center' as const, width: 120 },
{ prop: 'realAmount', label: '实际金额', align: 'center' as const, width: 120 }, { prop: 'realAmount', label: '实际金额', align: 'center' as const, width: 120, isNumber: true },
{ prop: 'rate', label: '汇率', align: 'center' as const, width: 100 }, { prop: 'rate', label: '汇率', align: 'center' as const, width: 100 },
{ prop: 'statusText', label: '状态', align: 'center' as const, width: 100 }, { prop: 'statusText', label: '状态', align: 'center' as const, width: 100 },
{ prop: 'rechangeTime', label: '充值时间', align: 'center' as const, width: 180 }, { prop: 'rechangeTime', label: '充值时间', align: 'center' as const },
] ]

View File

@ -5,7 +5,7 @@
:table-data="tableData" :table-data="tableData"
:columns="tableColumnsOptions" :columns="tableColumnsOptions"
row-key="id" row-key="id"
v-loading="tableLoading" :loading="loading"
@rowClick="handleRowClick" @rowClick="handleRowClick"
> >
<template #action="{ row }"> <template #action="{ row }">
@ -33,6 +33,7 @@ import { ref, reactive, onMounted } from 'vue'
import { ElMessage } from 'element-plus' import { ElMessage } from 'element-plus'
import Table from '@/components/common/Table.vue' import Table from '@/components/common/Table.vue'
import Pagination from '@/components/common/Pagination.vue' import Pagination from '@/components/common/Pagination.vue'
import { usePageLoading } from '@/composables/useLoading'
import WithdrawDialog from './components/WithdrawDialog.vue' import WithdrawDialog from './components/WithdrawDialog.vue'
import { tableColumns } from './options' import { tableColumns } from './options'
import { getWithdrawChannel } from '@/api/modules/capital/withdraw' import { getWithdrawChannel } from '@/api/modules/capital/withdraw'
@ -54,7 +55,7 @@ interface TableRow {
const tableRef = ref() const tableRef = ref()
const tableData = ref<TableRow[]>([]) const tableData = ref<TableRow[]>([])
const tableLoading = ref(false) const { loading, startLoading, stopLoading } = usePageLoading()
const total = ref(0) const total = ref(0)
const withdrawDialogVisible = ref(false) const withdrawDialogVisible = ref(false)
const currentChannelId = ref<number>() const currentChannelId = ref<number>()
@ -70,7 +71,7 @@ const tableColumnsOptions = tableColumns
// //
const getList = async () => { const getList = async () => {
tableLoading.value = true startLoading()
try { try {
const res: any = await getWithdrawChannel() const res: any = await getWithdrawChannel()
if (res) { if (res) {
@ -83,7 +84,7 @@ const getList = async () => {
} catch (error) { } catch (error) {
console.error('获取列表失败', error) console.error('获取列表失败', error)
} finally { } finally {
tableLoading.value = false stopLoading()
} }
} }

View File

@ -5,7 +5,7 @@ export const tableColumns = [
// 渠道名称 // 渠道名称
{ label: '渠道名称', prop: 'name' }, { label: '渠道名称', prop: 'name' },
// 渠道费用 // 渠道费用
{ label: '渠道费用', prop: 'channel_fee' }, { label: '渠道费用', prop: 'channel_fee', isNumber: true },
{ {
label: '出金货币', label: '出金货币',
prop: 'currency.symbol', prop: 'currency.symbol',

View File

@ -10,6 +10,7 @@
:table-data="tableData" :table-data="tableData"
:columns="tableColumnsOptions" :columns="tableColumnsOptions"
row-key="id" row-key="id"
:loading="loading"
@rowClick="handleRowClick" @rowClick="handleRowClick"
/> />
<el-dialog <el-dialog
@ -18,6 +19,7 @@
width="700" width="700"
> >
<Form <Form
ref="formRef"
:formItems="formItems" :formItems="formItems"
:formRules="formRules" :formRules="formRules"
:formData="formData" :formData="formData"
@ -26,7 +28,7 @@
<template #footer> <template #footer>
<div class="dialog-footer"> <div class="dialog-footer">
<el-button @click="dialogVisible = false">取消</el-button> <el-button @click="dialogVisible = false">取消</el-button>
<el-button type="primary" @click="submitDetail">确认</el-button> <el-button type="primary" :loading="submitLoading" @click="submitDetail">确认</el-button>
</div> </div>
</template> </template>
</el-dialog> </el-dialog>
@ -39,6 +41,7 @@ defineOptions({
// //
import Table from '@/components/common/Table.vue' import Table from '@/components/common/Table.vue'
import Form from '@/components/common/Form.vue' import Form from '@/components/common/Form.vue'
import { usePageLoading, useButtonLoading } from '@/composables/useLoading'
import { tableColumns, formItemsData, formRulesData, optionsMapData } from './options' import { tableColumns, formItemsData, formRulesData, optionsMapData } from './options'
import { import {
getChannelWithdrawList, getChannelWithdrawList,
@ -65,8 +68,13 @@ const getStatusText = (status: number | string) => {
return ['', '正常', '禁用'][status] return ['', '正常', '禁用'][status]
} }
const formRef = ref()
const { loading, startLoading, stopLoading } = usePageLoading()
const { buttonLoading: submitLoading, startButtonLoading: startSubmitLoading, stopButtonLoading: stopSubmitLoading } = useButtonLoading()
const initData = async () => { const initData = async () => {
startLoading()
const res = await getChannelWithdrawList() const res = await getChannelWithdrawList()
stopLoading()
console.log(res) console.log(res)
tableData.value = res.map((item: ChannelType) => ({ tableData.value = res.map((item: ChannelType) => ({
...item, ...item,
@ -96,6 +104,9 @@ const formItems = ref(formItemsData)
const formRules = ref(formRulesData) const formRules = ref(formRulesData)
const optionsMap = ref(optionsMapData) const optionsMap = ref(optionsMapData)
const submitDetail = async () => { const submitDetail = async () => {
await formRef.value.validate()
startSubmitLoading();
try {
dialogVisible.value = false dialogVisible.value = false
if (dialogTitleType.value === 1) { if (dialogTitleType.value === 1) {
await createChannelWithdraw(formData.value) await createChannelWithdraw(formData.value)
@ -103,6 +114,9 @@ const submitDetail = async () => {
await editChannelWithdraw(formData.value) await editChannelWithdraw(formData.value)
} }
await initData() await initData()
} finally {
stopSubmitLoading();
}
} }
let currentRow: any = {} let currentRow: any = {}

View File

@ -4,7 +4,7 @@ export const tableColumns = [
//渠道名称 //渠道名称
{ label: '渠道名称', prop: 'name' }, { label: '渠道名称', prop: 'name' },
//渠道费用 //渠道费用
{ label: '渠道费用', prop: 'channel_fee' }, { label: '渠道费用', prop: 'channel_fee', isNumber: true },
//货币类型 //货币类型
// { label: '入金货币', prop: 'currency.name' }, // { label: '入金货币', prop: 'currency.name' },
// 接口地址 // 接口地址

View File

@ -8,25 +8,16 @@
</template> </template>
</WithdrawSearch> </WithdrawSearch>
<!-- 列表区域当前使用页面内表格避免改动公共 Table 组件 --> <!-- 列表区域使用公共 Table 组件 -->
<div class="table-wrapper"> <Table
<el-table v-loading="loading" :data="tableData" border height="100%" row-key="id" ref="tableRef"
@selection-change="handleSelectionChange"> :table-data="tableData"
<!-- 勾选列用于后续审核通过/驳回 --> :columns="tableColumns"
<el-table-column type="selection" width="55" align="center" /> row-key="id"
:loading="loading"
<!-- 序号列根据当前分页动态计算 --> :show-selection="true"
<el-table-column label="序号" width="70" align="center"> @selection-change="handleSelectionChange"
<template #default="scope"> />
{{ (currentPage - 1) * pageSize + scope.$index + 1 }}
</template>
</el-table-column>
<!-- 业务字段列通过配置项统一渲染 -->
<el-table-column v-for="column in tableColumns" :key="column.prop" :prop="column.prop" :label="column.label"
:width="column.width" :align="column.align || 'center'" show-overflow-tooltip />
</el-table>
</div>
<!-- 分页区域复用项目内分页组件 --> <!-- 分页区域复用项目内分页组件 -->
<WithdrawPagination v-model="currentPage" v-model:pageSizeValue="pageSize" :total="total" <WithdrawPagination v-model="currentPage" v-model:pageSizeValue="pageSize" :total="total"
@ -43,6 +34,8 @@ import { ElMessage, ElMessageBox } from 'element-plus'
import { computed, onMounted, ref } from 'vue' import { computed, onMounted, ref } from 'vue'
import dayjs from 'dayjs' import dayjs from 'dayjs'
import WithdrawSearch from '@/components/common/Search.vue' import WithdrawSearch from '@/components/common/Search.vue'
import Table from '@/components/common/Table.vue'
import { usePageLoading } from '@/composables/useLoading'
import WithdrawPagination from '@/components/common/Pagination.vue' import WithdrawPagination from '@/components/common/Pagination.vue'
import { withdrawCheckSearch, withdrawCheckTableColumns } from './options' import { withdrawCheckSearch, withdrawCheckTableColumns } from './options'
import { getWithdrawReviewList, withdrawReviewOrder } from '@/api/modules/capital/withdraw' import { getWithdrawReviewList, withdrawReviewOrder } from '@/api/modules/capital/withdraw'
@ -117,11 +110,11 @@ const searchForm = ref({
timeRange: [] as string[] timeRange: [] as string[]
}) })
const { loading, startLoading, stopLoading } = usePageLoading()
// loading // loading
const tableColumns = computed(() => withdrawCheckTableColumns) const tableColumns = computed(() => withdrawCheckTableColumns)
const tableData = ref<TransformedTableRow[]>([]) const tableData = ref<TransformedTableRow[]>([])
const selectedRows = ref<TableRow[]>([]) const selectedRows = ref<TableRow[]>([])
const loading = ref(false)
// loading // loading
const approveLoading = ref(false) const approveLoading = ref(false)
@ -172,7 +165,7 @@ interface ListResponse {
// //
const getList = async () => { const getList = async () => {
loading.value = true startLoading()
selectedRows.value = [] selectedRows.value = []
const [start_time, end_time] = searchForm.value.timeRange || [] const [start_time, end_time] = searchForm.value.timeRange || []
@ -192,7 +185,7 @@ const getList = async () => {
console.error('获取提现审核列表失败:', error) console.error('获取提现审核列表失败:', error)
ElMessage.error('获取列表失败') ElMessage.error('获取列表失败')
} finally { } finally {
loading.value = false stopLoading()
} }
} }

View File

@ -25,18 +25,18 @@ export const withdrawCheckSearch = [
// 表格列配置 // 表格列配置
export const withdrawCheckTableColumns = [ export const withdrawCheckTableColumns = [
{ prop: 'trade_id', label: '订单号', align: 'center' as const, width: 200 }, { prop: 'trade_id', label: '订单号', align: 'center' as const, width: 200 },
{ prop: 'user_name', label: '账户持有人', align: 'center' as const, width: 120 }, { prop: 'user_name', label: '账户持有人', align: 'center' as const},
{ prop: 'withdraw_currency_symbol', label: '取款币种', align: 'center' as const, width: 100 }, { prop: 'withdraw_currency_symbol', label: '取款币种', align: 'center' as const, width: 100 },
{ prop: 'withdraw_amount', label: '取款金额', align: 'center' as const, width: 120 }, { prop: 'withdraw_amount', label: '取款金额', align: 'center' as const, width: 120, isNumber: true },
{ prop: 'real_currency_symbol', label: '到账币种', align: 'center' as const, width: 100 }, { prop: 'real_currency_symbol', label: '到账币种', align: 'center' as const, width: 100 },
{ prop: 'real_amount', label: '到账金额', align: 'center' as const, width: 120 }, { prop: 'real_amount', label: '到账金额', align: 'center' as const, width: 120, isNumber: true },
{ prop: 'rate', label: '汇率', align: 'center' as const, width: 100 }, { prop: 'rate', label: '汇率', align: 'center' as const, width: 100 },
{ prop: 'channel_name', label: '渠道', align: 'center' as const, width: 140 }, { prop: 'channel_name', label: '渠道', align: 'center' as const, width: 140 },
{ prop: 'withdraw_service_fee', label: '服务费', align: 'center' as const, width: 100 }, { prop: 'withdraw_service_fee', label: '服务费', align: 'center' as const, width: 100, isNumber: true },
{ prop: 'withdraw_head_fee', label: '总部手续费', align: 'center' as const, width: 120 }, { prop: 'withdraw_head_fee', label: '总部手续费', align: 'center' as const, width: 120, isNumber: true },
{ prop: 'withdraw_fee_handle', label: '代理手续费', align: 'center' as const, width: 120 }, { prop: 'withdraw_fee_handle', label: '代理手续费', align: 'center' as const, width: 120, isNumber: true },
{ prop: 'withdraw_point_diff', label: '点差', align: 'center' as const, width: 100 }, { prop: 'withdraw_point_diff', label: '点差', align: 'center' as const, width: 100, isNumber: true },
{ prop: 'withdraw_risk_fee', label: '风控费', align: 'center' as const, width: 100 }, { prop: 'withdraw_risk_fee', label: '风控费', align: 'center' as const, isNumber: true },
{ prop: 'withdraw_address', label: '提现地址', align: 'center' as const, width: 180 }, { prop: 'withdraw_address', label: '提现地址', align: 'center' as const},
{ prop: 'created_at', label: '申请时间', align: 'center' as const, width: 180 } { prop: 'created_at', label: '申请时间', align: 'center' as const}
] ]

View File

@ -2,63 +2,28 @@
<div class="mould-fee table_form-container"> <div class="mould-fee table_form-container">
<header> <header>
<div class="header-actions"> <div class="header-actions">
<el-button type="primary" @click="addFee" v-auth="'feeHandl_createFeeSetting'" <el-button type="primary" @click="addFee" v-auth="'feeHandl_createFeeSetting'">添加模板</el-button>
>添加模板</el-button <el-button type="primary" @click="editFee" v-auth="'feeHandl_editFeeSetting'">编辑模板</el-button>
> <el-button type="primary" @click="changeStatus(1)" v-auth="'feeHandl_editFeeSetting'">启用模板</el-button>
<el-button type="primary" @click="editFee" v-auth="'feeHandl_editFeeSetting'" <el-button type="danger" @click="changeStatus(2)" v-auth="'feeHandl_editFeeSetting'">禁用模板</el-button>
>编辑模板</el-button <el-button type="primary" @click="editFeeDetail" v-auth="'feeHandl_setFeeSettingDetail'">编辑模板详情</el-button>
>
<el-button type="primary" @click="changeStatus(1)" v-auth="'feeHandl_editFeeSetting'"
>启用模板</el-button
>
<el-button type="danger" @click="changeStatus(2)" v-auth="'feeHandl_editFeeSetting'"
>禁用模板</el-button
>
<el-button type="primary" @click="editFeeDetail" v-auth="'feeHandl_setFeeSettingDetail'"
>编辑模板详情</el-button
>
</div> </div>
</header> </header>
<div class="table-container"> <div class="table-container">
<div class="table-container-left"> <div class="table-container-left">
<el-table <Table ref="singleTableRef" :table-data="tableData" :columns="tableColumns" row-key="id" :loading="loading"
ref="singleTableRef" :highlight-current-row="true" @current-change="handleCurrentChange" />
height="100%"
:data="tableData"
highlight-current-row
border
@current-change="handleCurrentChange"
>
<el-table-column type="index" width="55" />
<el-table-column
v-for="column in tableColumns"
:key="column.prop"
:prop="column.prop"
:label="column.label"
/>
</el-table>
</div> </div>
<div class="table-container-right"> <div class="table-container-right">
<el-table height="100%" :data="tableDataDetail"> <Table row-key="id" :table-data="tableDataDetail" :columns="tableColumnsDetail" :loading="detailLoading" />
<el-table-column
v-for="column in tableColumnsDetail"
:key="column.prop"
:prop="column.prop"
:label="column.label"
/>
</el-table>
</div> </div>
</div> </div>
<el-dialog <el-dialog v-model="dialogVisible" :title="dialogTitleType === 1 ? '添加手续费模板' : '编辑手续费模板'" width="500">
v-model="dialogVisible"
:title="dialogTitleType === 1 ? '添加手续费模板' : '编辑手续费模板'"
width="500"
>
<Form :formItems="formItems" :formRules="formRules" :formData="formData" /> <Form :formItems="formItems" :formRules="formRules" :formData="formData" />
<template #footer> <template #footer>
<div class="dialog-footer"> <div class="dialog-footer">
<el-button @click="dialogVisible = false">取消</el-button> <el-button @click="dialogVisible = false">取消</el-button>
<el-button type="primary" @click="submit">确认</el-button> <el-button type="primary" :loading="submitLoading" @click="submit">确认</el-button>
</div> </div>
</template> </template>
</el-dialog> </el-dialog>
@ -67,12 +32,7 @@
<template v-for="column in tableColumnsDetail" :key="column.prop"> <template v-for="column in tableColumnsDetail" :key="column.prop">
<el-table-column v-if="column.prop === 'fee'" :prop="column.prop" :label="column.label"> <el-table-column v-if="column.prop === 'fee'" :prop="column.prop" :label="column.label">
<template #default="scope"> <template #default="scope">
<el-input-number <el-input-number :min="0" :max="999999999" :controls="false" v-model="scope.row.fee" />
:min="0"
:max="999999999"
:controls="false"
v-model="scope.row.fee"
/>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column v-else :prop="column.prop" :label="column.label" /> <el-table-column v-else :prop="column.prop" :label="column.label" />
@ -81,7 +41,7 @@
<template #footer> <template #footer>
<div class="dialog-footer"> <div class="dialog-footer">
<el-button @click="detaildialogVisible = false">取消</el-button> <el-button @click="detaildialogVisible = false">取消</el-button>
<el-button type="primary" @click="submitDetail">确认</el-button> <el-button type="primary" :loading="submitDetailLoading" @click="submitDetail">确认</el-button>
</div> </div>
</template> </template>
</el-dialog> </el-dialog>
@ -93,7 +53,9 @@
defineOptions({ defineOptions({
name: 'HandleFee' name: 'HandleFee'
}) })
import { ref, onMounted } from 'vue' import { ref, onMounted } from 'vue'
import { usePageLoading, useButtonLoading } from '@/composables/useLoading'
import { import {
getFeeSettingList, getFeeSettingList,
getFeeSettingDetail, getFeeSettingDetail,
@ -102,18 +64,42 @@ import {
setFeeSettingDetail, setFeeSettingDetail,
} from '@/api/modules/mould/handleFee.ts' } from '@/api/modules/mould/handleFee.ts'
import Form from '@/components/common/Form.vue' import Form from '@/components/common/Form.vue'
import Table from '@/components/common/Table.vue'
import { formDataList, formRulesList } from './option.ts' import { formDataList, formRulesList } from './option.ts'
//
const dialogVisible = ref(false) const dialogVisible = ref(false)
const dialogTitleType = ref(1) const dialogTitleType = ref(1)
//
const formData = ref({ const formData = ref({
name: '', name: '',
status: 1, status: 1,
type: 2, type: 2,
description: '', description: '',
}) })
const formItems = ref(formDataList) const formItems = formDataList
const formRules = ref(formRulesList) const formRules = formRulesList
//
const detaildialogVisible = ref(false)
//
const { loading, startLoading, stopLoading } = usePageLoading()
const { buttonLoading: detailLoading, startButtonLoading: startDetailLoading, stopButtonLoading: stopDetailLoading } = useButtonLoading()
const { buttonLoading: submitLoading, startButtonLoading: startSubmitLoading, stopButtonLoading: stopSubmitLoading } = useButtonLoading()
const { buttonLoading: submitDetailLoading, startButtonLoading: startSubmitDetailLoading, stopButtonLoading: stopSubmitDetailLoading } = useButtonLoading()
//
const tableData = ref<any[]>([])
const tableColumns = [
{ prop: 'name', label: '名称' },
{ prop: 'type', label: '状态' },
]
const currentRow = ref<Record<string, any>>({})
const singleTableRef = ref()
//
const resetFormData = () => { const resetFormData = () => {
formData.value = { formData.value = {
name: '', name: '',
@ -123,56 +109,72 @@ const resetFormData = () => {
} }
} }
const tableData = ref([]) //
const tableColumns = ref([ const setCurrent = (row: any) => {
{ prop: 'name', label: '名称' }, singleTableRef.value?.setCurrentRow(row)
{ prop: 'type', label: '状态' },
])
const currentRow = ref({})
const singleTableRef = ref()
const setCurrent = (row) => {
singleTableRef.value.setCurrentRow(row)
}
const handleCurrentChange = (row: any) => {
currentRow.value = row
getItemDetail(row.id)
} }
const tableDataDetail = ref([]) //
const tableColumnsDetail = ref([ const handleCurrentChange = (row: any) => {
currentRow.value = row
fetchItemDetail(row.id)
}
//
const tableDataDetail = ref<any[]>([])
const tableColumnsDetail = [
{ prop: 'name', label: '品种' }, { prop: 'name', label: '品种' },
{ prop: 'code', label: '编号' }, { prop: 'code', label: '编号' },
{ prop: 'fee', label: '开仓手续费' }, { prop: 'fee', label: '开仓手续费' },
]) ]
//
const getTableData = async () => { const getTableData = async () => {
startLoading()
try {
const res = await getFeeSettingList() const res = await getFeeSettingList()
tableData.value = Array.isArray(res) ? res : []
handleTableDataAfterLoad()
} finally {
stopLoading()
}
}
//
const handleTableDataAfterLoad = () => {
const savedId = currentRow.value?.id const savedId = currentRow.value?.id
tableData.value = res
console.log(res) if (tableData.value.length === 0) {
if (res && res.length > 0) { tableDataDetail.value = []
const targetRow = savedId ? res.find((item: any) => item.id === savedId) : null return
if (targetRow) {
setCurrent(targetRow)
handleCurrentChange(targetRow)
} else {
setCurrent(res[0])
handleCurrentChange(res[0])
}
}
} }
const getItemDetail = async (id: number) => { const targetRow = savedId ? tableData.value.find((item) => item.id === savedId) : null
const rowToSelect = targetRow || tableData.value[0]
setCurrent(rowToSelect)
handleCurrentChange(rowToSelect)
}
//
const fetchItemDetail = async (id: number) => {
startDetailLoading();
try {
const res = await getFeeSettingDetail({ id }) const res = await getFeeSettingDetail({ id })
tableDataDetail.value = res tableDataDetail.value = res || []
} finally {
stopDetailLoading();
}
} }
//
const addFee = () => { const addFee = () => {
dialogVisible.value = true dialogVisible.value = true
dialogTitleType.value = 1 dialogTitleType.value = 1
resetFormData() resetFormData()
} }
//
const editFee = () => { const editFee = () => {
dialogVisible.value = true dialogVisible.value = true
dialogTitleType.value = 2 dialogTitleType.value = 2
@ -184,6 +186,7 @@ const editFee = () => {
} }
} }
//
const changeStatus = (status: number) => { const changeStatus = (status: number) => {
dialogTitleType.value = 2 dialogTitleType.value = 2
formData.value = { formData.value = {
@ -196,8 +199,10 @@ const changeStatus = (status: number) => {
submit() submit()
} }
//
const submit = async () => { const submit = async () => {
console.log(formData.value) startSubmitLoading();
try {
if (dialogTitleType.value === 1) { if (dialogTitleType.value === 1) {
await createFeeSetting(formData.value) await createFeeSetting(formData.value)
} else { } else {
@ -206,25 +211,36 @@ const submit = async () => {
resetFormData() resetFormData()
dialogVisible.value = false dialogVisible.value = false
getTableData() getTableData()
} finally {
stopSubmitLoading();
}
} }
const detaildialogVisible = ref(false) //
const editFeeDetail = () => { const editFeeDetail = () => {
detaildialogVisible.value = true detaildialogVisible.value = true
console.log(tableDataDetail.value)
} }
//
const submitDetail = async () => { const submitDetail = async () => {
let contents_json = {} startSubmitDetailLoading();
try {
const contentsJson: Record<string, any> = {}
tableDataDetail.value.forEach((item) => { tableDataDetail.value.forEach((item) => {
contents_json[item.variety_id] = item.fee contentsJson[item.variety_id] = item.fee
}) })
await setFeeSettingDetail({ await setFeeSettingDetail({
id: currentRow.value.id, id: currentRow.value.id,
contents_json: JSON.stringify(contents_json), contents_json: JSON.stringify(contentsJson),
}) })
detaildialogVisible.value = false detaildialogVisible.value = false
getTableData() getTableData()
} finally {
stopSubmitDetailLoading();
}
} }
onMounted(() => { onMounted(() => {
@ -237,19 +253,23 @@ onMounted(() => {
// height: 100%; // height: 100%;
// overflow: hidden; // overflow: hidden;
} }
header { header {
margin-bottom: 8px; margin-bottom: 8px;
} }
.table-container { .table-container {
display: flex; display: flex;
column-gap: 20px; column-gap: 20px;
height: calc(100% - 40px); height: calc(100% - 40px);
.table-container-left { .table-container-left {
width: 300px; width: 300px;
height: 100%; height: 100%;
flex: 0 0 auto; flex: 0 0 auto;
overflow: hidden; overflow: hidden;
} }
.table-container-right { .table-container-right {
flex: 1; flex: 1;
height: 100%; height: 100%;
@ -257,3 +277,4 @@ header {
} }
} }
</style> </style>

View File

@ -2,69 +2,31 @@
<div class="risk table_form-container"> <div class="risk table_form-container">
<div class="bar"> <div class="bar">
<span>风控管理</span> <span>风控管理</span>
<el-button type="primary" @click="handleAddRisk" v-auth="'riskControl_createRiskControl'" <el-button type="primary" @click="handleAddRisk" v-auth="'riskControl_createRiskControl'">添加</el-button>
>添加</el-button <el-button type="primary" @click="handleModifyRisk" v-auth="'riskControl_editRiskControlSetting'">修改</el-button>
> <el-button type="danger" @click="handleChangeStatus(2)"
<el-button v-auth="'riskControl_editRiskControlSetting'">停用</el-button>
type="primary" <el-button type="primary" @click="handleChangeStatus(1)"
@click="handleModifyRisk" v-auth="'riskControl_editRiskControlSetting'">启用</el-button>
v-auth="'riskControl_editRiskControlSetting'"
>修改</el-button
>
<el-button
type="danger"
@click="handleChangeStatus(2)"
v-auth="'riskControl_editRiskControlSetting'"
>停用</el-button
>
<el-button
type="primary"
@click="handleChangeStatus(1)"
v-auth="'riskControl_editRiskControlSetting'"
>启用</el-button
>
</div> </div>
<!-- 表格 --> <!-- 表格 -->
<div class="risk-table-content"> <div class="risk-table-content">
<RiskTableSwitch <RiskTableSwitch :columns="tableColumnsSwitch" :table-data="tableTreeSwitch" class="table-switch"
:columns="tableColumnsSwitch" @row-click="handleRowClick" row-key="id" highlight-current-row :current-row-key="selectedRowId"
:table-data="tableTreeSwitch" :loading="loading">
class="table-switch"
@row-click="handleRowClick"
row-key="id"
highlight-current-row
:current-row-key="selectedRowId"
>
<template #status="{ row }"> <template #status="{ row }">
<span>{{ row.status === 1 ? '启用' : '停用' }}</span> <span>{{ row.status === 1 ? '启用' : '停用' }}</span>
</template> </template>
</RiskTableSwitch> </RiskTableSwitch>
<RiskTableContent <RiskTableContent :columns="tableColumnsContent" :table-data="currentContent" class="table-content" row-key="id"
:columns="tableColumnsContent" :loading="detailLoading">
:table-data="currentContent"
class="table-content"
row-key="id"
>
</RiskTableContent> </RiskTableContent>
</div> </div>
<!-- dialog --> <!-- dialog -->
<RiskDialog <RiskDialog :visible="dialogVisible" :title="dialogTitle" :type="dialogType" :button-loading="submitLoading" @confirm="handleSubmit"
:visible="dialogVisible" @cancel="handleCancel" @close="handleClose">
:title="dialogTitle" <RiskForm :form-data="dialogForm" :form-rules="riskRules" :form-items="RiskFormItems"
:type="dialogType" :options-map="riskFormOptionsMap" :row-gutter="rowGutter" :col-span="colSpan" ref="riskFormRef" />
@confirm="handleSubmit"
@cancel="handleCancel"
@close="handleClose"
>
<RiskForm
:form-data="dialogForm"
:form-rules="riskRules"
:form-items="RiskFormItems"
:options-map="riskFormOptionsMap"
:row-gutter="rowGutter"
:col-span="colSpan"
ref="riskFormRef"
/>
</RiskDialog> </RiskDialog>
</div> </div>
</template> </template>
@ -75,6 +37,10 @@ defineOptions({
name: 'Risk' name: 'Risk'
}) })
// //
import { usePageLoading, useButtonLoading } from '@/composables/useLoading'
const { loading, startLoading, stopLoading } = usePageLoading()
const { buttonLoading: submitLoading, startButtonLoading: startSubmitLoading, stopButtonLoading: stopSubmitLoading } = useButtonLoading()
import RiskTableSwitch from '@/components/common/Table.vue' import RiskTableSwitch from '@/components/common/Table.vue'
import RiskTableContent from '@/components/common/Table.vue' import RiskTableContent from '@/components/common/Table.vue'
import RiskDialog from '@/components/common/Dialog.vue' import RiskDialog from '@/components/common/Dialog.vue'
@ -132,6 +98,8 @@ const dialogForm = ref({
}) })
const riskRules = ref(rules()) const riskRules = ref(rules())
const riskFormRef = ref() const riskFormRef = ref()
const detailLoading = ref(false)
const RiskFormItems = ref(formItem) const RiskFormItems = ref(formItem)
const riskFormOptionsMap = ref({ const riskFormOptionsMap = ref({
@ -165,7 +133,9 @@ const colSpan = ref(12)
* @description: 定义请求数据方法 * @description: 定义请求数据方法
*/ */
const getRiskList = async () => { const getRiskList = async () => {
startLoading()
const data: any = await getRiskListApi() const data: any = await getRiskListApi()
stopLoading()
// //
riskOriginData.value = data riskOriginData.value = data
// //
@ -265,7 +235,7 @@ const handleModifyRisk = () => {
} as any } as any
} }
const handleChangeStatus = async (status) => { const handleChangeStatus = async (status:any) => {
dialogTitle.value = '停用' dialogTitle.value = '停用'
dialogType.value = 'status' dialogType.value = 'status'
await editRiskApi({ ...selectedRow.value, status: status }) await editRiskApi({ ...selectedRow.value, status: status })
@ -276,6 +246,7 @@ const handleChangeStatus = async (status) => {
* @description: 定义表格方法 * @description: 定义表格方法
*/ */
const handleRowClick = (row: any) => { const handleRowClick = (row: any) => {
detailLoading.value = true
// //
const originRow = riskOriginData.value.find((item) => item.id === row.id) const originRow = riskOriginData.value.find((item) => item.id === row.id)
if (originRow) { if (originRow) {
@ -285,7 +256,7 @@ const handleRowClick = (row: any) => {
} }
selectedRowId.value = row.id selectedRowId.value = row.id
// //
const targetItem = tableTreeContent.value.find((item: any) => item.id === row.id) const targetItem:any = tableTreeContent.value.find((item: any) => item.id === row.id)
const fields = [ const fields = [
{ text: '单笔开仓最大数量' + targetItem.maxOpenPosition }, { text: '单笔开仓最大数量' + targetItem.maxOpenPosition },
@ -305,12 +276,16 @@ const handleRowClick = (row: any) => {
if (targetItem) { if (targetItem) {
currentContent.value = fields currentContent.value = fields
} }
detailLoading.value = false
} }
/** /**
* @description: 定义弹窗方法 * @description: 定义弹窗方法
*/ */
const handleSubmit = async () => { const handleSubmit = async () => {
startSubmitLoading()
try {
await riskFormRef.value?.validate() await riskFormRef.value?.validate()
const params = { const params = {
name: dialogForm.value.name, name: dialogForm.value.name,
@ -358,6 +333,9 @@ const handleSubmit = async () => {
dialogTitle.value = '编辑' dialogTitle.value = '编辑'
dialogType.value = 'edit' dialogType.value = 'edit'
} }
} finally {
stopSubmitLoading()
}
} }
const handleCancel = () => { const handleCancel = () => {
dialogVisible.value = false dialogVisible.value = false

View File

@ -1,8 +1,8 @@
<template> <template>
<div class="table_form-container"> <div class="table_form-container">
<!-- 搜索 --> <!-- 搜索 -->
<CustomerTradeOrderSearch :search-form="searchForm" :search-options="searchOptions" @search="handleSearch" <CustomerTradeOrderSearch :buttonLoading="loading" :search-form="searchForm" :search-options="searchOptions"
@reset="handleReset" @blur-input="getSubAccountList"> @search="handleSearch" @reset="handleReset" @blur-input="getSubAccountList">
</CustomerTradeOrderSearch> </CustomerTradeOrderSearch>
<div class="bar" v-if="searchForm.orderId === '1'"> <div class="bar" v-if="searchForm.orderId === '1'">
<div>持仓量:0</div> <div>持仓量:0</div>
@ -10,7 +10,8 @@
<div>交易盈亏:0.00 USD</div> <div>交易盈亏:0.00 USD</div>
</div> </div>
<!-- 表格 --> <!-- 表格 -->
<CustomerTradeOrderTable :table-data="tableTree" :columns="tableColumnsOptions" row-key="id" /> <CustomerTradeOrderTable :table-data="tableTree" :columns="tableColumnsOptions" row-key="id"
:loading="loading" />
<!-- 分页 --> <!-- 分页 -->
<CustomerTradeOrderPagination v-model="currentPage" v-model:pageSizeValue="pageSize" :total="total" <CustomerTradeOrderPagination v-model="currentPage" v-model:pageSizeValue="pageSize" :total="total"
@ -27,6 +28,7 @@ defineOptions({
// //
import CustomerTradeOrderSearch from '@/components/common/Search.vue' import CustomerTradeOrderSearch from '@/components/common/Search.vue'
import CustomerTradeOrderTable from '@/components/common/Table.vue' import CustomerTradeOrderTable from '@/components/common/Table.vue'
import { usePageLoading } from '@/composables/useLoading'
import CustomerTradeOrderPagination from '@/components/common/Pagination.vue' import CustomerTradeOrderPagination from '@/components/common/Pagination.vue'
// //
import { search, tradeOrderTableColumns, positionOrderTableColumns } from './options' import { search, tradeOrderTableColumns, positionOrderTableColumns } from './options'
@ -63,6 +65,7 @@ const subAccountOptions = ref([])
// //
const userInfo: any = getStorage('userInfo') const userInfo: any = getStorage('userInfo')
const { loading, startLoading, stopLoading } = usePageLoading()
/** /**
* @description: 定义表格数据 * @description: 定义表格数据
*/ */
@ -80,6 +83,8 @@ const total = ref(0)
* @description: 定义公共请求数据方法 * @description: 定义公共请求数据方法
*/ */
const getCustomerTradeOrderList = async () => { const getCustomerTradeOrderList = async () => {
try {
startLoading()
const params = { const params = {
page: currentPage.value, page: currentPage.value,
page_size: pageSize.value, page_size: pageSize.value,
@ -92,7 +97,6 @@ const getCustomerTradeOrderList = async () => {
} }
console.log(params) console.log(params)
const data: any = await getCustomTradeOrderListApi(params) const data: any = await getCustomTradeOrderListApi(params)
// //
// table // table
tableTree.value = data.data.map((item: any) => ({ tableTree.value = data.data.map((item: any) => ({
@ -112,6 +116,10 @@ const getCustomerTradeOrderList = async () => {
total.value = data.total total.value = data.total
currentPage.value = data.current_page currentPage.value = data.current_page
pageSize.value = Number(data.per_page) pageSize.value = Number(data.per_page)
} finally {
stopLoading()
}
} }
const getSubAccountList = async () => { const getSubAccountList = async () => {

View File

@ -2,11 +2,8 @@
<div class="position-stat table_form-container"> <div class="position-stat table_form-container">
<!-- 搜索 --> <!-- 搜索 -->
<PositionStatSearch <PositionStatSearch
:search-form="searchForm" :buttonLoading="loading" :search-form="searchForm" :search-options="searchOptions" @search="handleSearch"
:search-options="searchOptions" @reset="handleReset">
@search="handleSearch"
@reset="handleReset"
>
<!-- <template #button="{ form }">--> <!-- <template #button="{ form }">-->
<!-- <el-button type="primary" @click="handleExport()">--> <!-- <el-button type="primary" @click="handleExport()">-->
<!-- 导出--> <!-- 导出-->
@ -14,11 +11,8 @@
<!-- </template>--> <!-- </template>-->
</PositionStatSearch> </PositionStatSearch>
<!-- 表格 --> <!-- 表格 -->
<PositionStatTable <PositionStatTable :table-data="orderPositionStore.positionOrderList" :columns="tableColumnsOptions"
:table-data="orderPositionStore.positionOrderList" :loading="loading" row-key="id" />
:columns="tableColumnsOptions"
row-key="id"
/>
</div> </div>
</template> </template>
@ -35,6 +29,10 @@ import { search, tableColumns } from './options'
import { useTransactionStore } from '@/stores/modules/transaction.ts' import { useTransactionStore } from '@/stores/modules/transaction.ts'
import { useOrderPositionStore } from '@/stores/modules/orderPosition.ts' import { useOrderPositionStore } from '@/stores/modules/orderPosition.ts'
import { usePageLoading } from '@/composables/useLoading'
// loading
const { loading, startLoading, stopLoading } = usePageLoading()
// //
import { exportToExcel } from '@/utils/exportToExcel' import { exportToExcel } from '@/utils/exportToExcel'
@ -57,11 +55,15 @@ const tableColumnsOptions = ref(tableColumns)
* @description: 定义搜索方法 * @description: 定义搜索方法
*/ */
const handleSearch = async () => { const handleSearch = async () => {
try {
startLoading()
const data = await checkAccountIdIsSonForUser({ account_id: searchForm.value.account_id }) const data = await checkAccountIdIsSonForUser({ account_id: searchForm.value.account_id })
if (!data.flag) { if (!data.flag) {
return ElMessage.warning('请输入下级ID') return ElMessage.warning('请输入下级ID')
} }
transactionStore.getPositionList(searchForm.value.account_id) transactionStore.getPositionList(searchForm.value.account_id)
} finally { stopLoading() }
} }
const handleReset = () => { const handleReset = () => {
console.log('重置搜索') console.log('重置搜索')

View File

@ -14,7 +14,7 @@ export const tableColumns = [
// 操作类型 // 操作类型
{ prop: 'tradeType', label: '操作类型' }, { prop: 'tradeType', label: '操作类型' },
// 金额 // 金额
{ prop: 'amount', label: '金额' }, { prop: 'amount', label: '金额', isNumber: true },
// 交易数量 // 交易数量
{ prop: 'tradeQty', label: '交易数量'}, { prop: 'tradeQty', label: '交易数量'},
// 交易时间 // 交易时间

View File

@ -2,6 +2,7 @@
<div class="order-rechange-page table_form-container"> <div class="order-rechange-page table_form-container">
<!-- 搜索区域按接口参数保留订单号用户名称状态开始/结束时间 --> <!-- 搜索区域按接口参数保留订单号用户名称状态开始/结束时间 -->
<OrderRechangeSearch <OrderRechangeSearch
:buttonLoading="loading"
:search-form="searchForm" :search-form="searchForm"
:search-options="searchOptions" :search-options="searchOptions"
@search="handleSearch" @search="handleSearch"
@ -13,6 +14,7 @@
:table-data="tableTree" :table-data="tableTree"
:columns="tableColumnsOptions" :columns="tableColumnsOptions"
:highlight-current-row="false" :highlight-current-row="false"
:loading="loading"
> >
<!-- 操作列插槽 --> <!-- 操作列插槽 -->
<!-- <template #operation="{ row }">--> <!-- <template #operation="{ row }">-->
@ -43,6 +45,7 @@ defineOptions({
import { onMounted, ref } from 'vue' import { onMounted, ref } from 'vue'
import OrderRechangeSearch from '@/components/common/Search.vue' import OrderRechangeSearch from '@/components/common/Search.vue'
import OrderRechangeTable from '@/components/common/Table.vue' import OrderRechangeTable from '@/components/common/Table.vue'
import { usePageLoading } from '@/composables/useLoading'
import OrderRechangePagination from '@/components/common/Pagination.vue' import OrderRechangePagination from '@/components/common/Pagination.vue'
import { getRechargeOrderListApi, reviewRechargeOrder } from '@/api/modules/order/rechange.ts' import { getRechargeOrderListApi, reviewRechargeOrder } from '@/api/modules/order/rechange.ts'
import { search, tableColumns } from './options' import { search, tableColumns } from './options'
@ -86,6 +89,7 @@ const createDefaultSearchForm = (): SearchFormData => ({
const searchForm = ref<SearchFormData>(createDefaultSearchForm()) const searchForm = ref<SearchFormData>(createDefaultSearchForm())
const { loading, startLoading, stopLoading } = usePageLoading()
// //
const tableTree = ref<RechangeOrderItem[]>([]) const tableTree = ref<RechangeOrderItem[]>([])
const tableColumnsOptions = ref(tableColumns) const tableColumnsOptions = ref(tableColumns)
@ -109,6 +113,7 @@ const getRechargeStatusText = (status: string | number | null | undefined) => {
// //
const getRechargeOrderList = async () => { const getRechargeOrderList = async () => {
startLoading()
const params = { const params = {
page: currentPage.value, page: currentPage.value,
page_size: pageSize.value, page_size: pageSize.value,
@ -150,6 +155,8 @@ const getRechargeOrderList = async () => {
tableTree.value = [] tableTree.value = []
total.value = 0 total.value = 0
console.error('获取充值订单列表失败', error) console.error('获取充值订单列表失败', error)
} finally {
stopLoading()
} }
} }

View File

@ -51,7 +51,7 @@ export const search = [
export const tableColumns = [ export const tableColumns = [
{ prop: 'orderNo', label: '订单号', align: 'center' as const }, { prop: 'orderNo', label: '订单号', align: 'center' as const },
{ prop: 'currency', label: '充值币种', align: 'center' as const }, { prop: 'currency', label: '充值币种', align: 'center' as const },
{ prop: 'amount', label: '金额', align: 'center' as const }, { prop: 'amount', label: '金额', align: 'center' as const, isNumber: true },
{ prop: 'statusText', label: '状态', align: 'center' as const }, { prop: 'statusText', label: '状态', align: 'center' as const },
{ prop: 'rechangeTime', label: '充值时间', align: 'center' as const }, { prop: 'rechangeTime', label: '充值时间', align: 'center' as const },
{ prop: 'rechangeAccount', label: '充值账号', align: 'center' as const }, { prop: 'rechangeAccount', label: '充值账号', align: 'center' as const },

View File

@ -2,6 +2,7 @@
<div class="table_form-container"> <div class="table_form-container">
<!-- 搜索 --> <!-- 搜索 -->
<TransferSearch <TransferSearch
:buttonLoading="loading"
:search-form="searchForm" :search-form="searchForm"
:search-options="searchOptions" :search-options="searchOptions"
@search="handleSearch" @search="handleSearch"
@ -16,6 +17,7 @@
:table-data="tableTree" :table-data="tableTree"
:columns="tableColumnsOptions" :columns="tableColumnsOptions"
row-key="id" row-key="id"
:loading="loading"
ref="tableRef" ref="tableRef"
> >
</TransferTable> </TransferTable>
@ -37,6 +39,7 @@ defineOptions({
// //
import TransferSearch from '@/components/common/Search.vue' import TransferSearch from '@/components/common/Search.vue'
import TransferTable from '@/components/common/Table.vue' import TransferTable from '@/components/common/Table.vue'
import { usePageLoading } from '@/composables/useLoading'
import TransferPagination from '@/components/common/Pagination.vue' import TransferPagination from '@/components/common/Pagination.vue'
// //
import { search, tableColumns } from './options' import { search, tableColumns } from './options'
@ -54,12 +57,13 @@ const initTime = getRecentSevenDays()
/** /**
* @description: 定义搜索数据 * @description: 定义搜索数据
*/ */
const searchForm = ref({ const searchForm = ref<any>({
transferAccount: '', transferAccount: '',
type: '', type: '',
transferTime: [initTime.start, initTime.end], transferTime: [initTime.start, initTime.end],
}) })
const searchOptions = ref(search) const searchOptions = ref(search)
const { loading, startLoading, stopLoading } = usePageLoading()
/** /**
* @description: 定义表格数据 * @description: 定义表格数据
*/ */
@ -77,6 +81,7 @@ const total = ref(0)
*/ */
const getTransferDetailList = async () => { const getTransferDetailList = async () => {
startLoading()
const params = { const params = {
page: currentPage.value, page: currentPage.value,
page_size: pageSize.value, page_size: pageSize.value,
@ -108,6 +113,8 @@ const getTransferDetailList = async () => {
})) }))
// //
total.value = data.total total.value = data.total
stopLoading()
currentPage.value = data.current_page currentPage.value = data.current_page
pageSize.value = Number(data.per_page) pageSize.value = Number(data.per_page)
} }

View File

@ -31,7 +31,7 @@ export const search = [
export const tableColumns = [ export const tableColumns = [
{ prop: 'orderNo', label: '订单号', align: 'center' as const }, { prop: 'orderNo', label: '订单号', align: 'center' as const },
{ prop: 'currency', label: '提现币种', align: 'center' as const }, { prop: 'currency', label: '提现币种', align: 'center' as const },
{ prop: 'amount', label: '金额', align: 'center' as const }, { prop: 'amount', label: '金额', align: 'center' as const, isNumber: true },
{ prop: 'statusText', label: '状态', align: 'center' as const }, { prop: 'statusText', label: '状态', align: 'center' as const },
{ prop: 'withdrawTime', label: '提现时间', align: 'center' as const }, { prop: 'withdrawTime', label: '提现时间', align: 'center' as const },
{ prop: 'withdrawAccount', label: '提现账号', align: 'center' as const }, { prop: 'withdrawAccount', label: '提现账号', align: 'center' as const },

View File

@ -1,11 +1,11 @@
<template> <template>
<div class="table_form-container"> <div class="table_form-container">
<!-- 搜索 --> <!-- 搜索 -->
<CustomerListSearch :search-form="searchForm" :search-options="searchOptions" @search="handleSearch" <CustomerListSearch :buttonLoading="loading" :search-form="searchForm" :search-options="searchOptions" @search="handleSearch"
@reset="handleReset"></CustomerListSearch> @reset="handleReset"></CustomerListSearch>
<!-- 表格 --> <!-- 表格 -->
<CustomerListTable :table-data="tableTree" :columns="tableColumnsOptions" row-key="userId" <CustomerListTable :table-data="tableTree" :columns="tableColumnsOptions" row-key="userId"
:default-sort="{ prop: 'createTime', order: 'descending' }" @expand-change="handleGetSubList"> :default-sort="{ prop: 'createTime', order: 'descending' }" :loading="loading" @expand-change="handleGetSubList">
<template #marginRatio="{ row }"> <template #marginRatio="{ row }">
<el-button v-if="row.role_id === 5" type="primary" size="small" v-auth="'agent_getSelfVarietyPointDiffConfig'" <el-button v-if="row.role_id === 5" type="primary" size="small" v-auth="'agent_getSelfVarietyPointDiffConfig'"
@click="handleMarginRatio(row)"> @click="handleMarginRatio(row)">
@ -37,7 +37,7 @@
@pagination-change="handlePaginationChange" /> @pagination-change="handlePaginationChange" />
<!-- dialog --> <!-- dialog -->
<CustomerListDialog :visible="dialogVisible" :title="dialogTitle" :type="dialogType" :fullscreen="dialogFullscreen" <CustomerListDialog :visible="dialogVisible" :title="dialogTitle" :type="dialogType" :fullscreen="dialogFullscreen"
:show-footer="dialogType !== 'view'" @confirm="handleSubmit" @cancel="handleCancel" @close="handleClose"> :show-footer="dialogType !== 'view'" :button-loading="submitLoading" @confirm="handleSubmit" @cancel="handleCancel" @close="handleClose">
<CustomerListTable :table-data="marginRatioTree" :columns="marginRatioTableColumnsOptions" row-key="id" <CustomerListTable :table-data="marginRatioTree" :columns="marginRatioTableColumnsOptions" row-key="id"
v-if="dialogType === 'editMarginRatio'" style="height: 500px"> v-if="dialogType === 'editMarginRatio'" style="height: 500px">
<template #assignedPointDiff="{ row }"> <template #assignedPointDiff="{ row }">
@ -54,7 +54,7 @@
</CustomerListDialog> </CustomerListDialog>
<!-- 调整金额弹窗 --> <!-- 调整金额弹窗 -->
<CustomerListDialog :visible="adjustAmountVisible" title="调整金额" type="adjust" @confirm="handleAdjustAmountSubmit" <CustomerListDialog :visible="adjustAmountVisible" title="调整金额" type="adjust" :button-loading="adjustAmountLoading" @confirm="handleAdjustAmountSubmit"
@cancel="handleAdjustAmountCancel" @close="handleAdjustAmountClose"> @cancel="handleAdjustAmountCancel" @close="handleAdjustAmountClose">
<el-form :model="adjustAmountForm" :rules="adjustAmountRules" ref="adjustAmountFormRef" label-width="120px"> <el-form :model="adjustAmountForm" :rules="adjustAmountRules" ref="adjustAmountFormRef" label-width="120px">
<el-form-item label="账号名" prop="username"> <el-form-item label="账号名" prop="username">
@ -78,7 +78,7 @@
<!-- 修改客户弹窗 --> <!-- 修改客户弹窗 -->
<CustomerListDialog :visible="editCustomerVisible" title="修改客户" type="editCustomer" <CustomerListDialog :visible="editCustomerVisible" title="修改客户" type="editCustomer"
@confirm="handleEditCustomerSubmit" @cancel="handleEditCustomerCancel" @close="handleEditCustomerClose"> :button-loading="editCustomerLoading" @confirm="handleEditCustomerSubmit" @cancel="handleEditCustomerCancel" @close="handleEditCustomerClose">
<el-form :model="editCustomerForm" :rules="editCustomerRules" ref="editCustomerFormRef" label-width="140px"> <el-form :model="editCustomerForm" :rules="editCustomerRules" ref="editCustomerFormRef" label-width="140px">
<el-form-item label="手续费模板" prop="feeSettingId"> <el-form-item label="手续费模板" prop="feeSettingId">
<el-select v-model="editCustomerForm.feeSettingId" placeholder="请选择手续费模板" style="width: 100%"> <el-select v-model="editCustomerForm.feeSettingId" placeholder="请选择手续费模板" style="width: 100%">
@ -112,6 +112,7 @@ defineOptions({
// //
import CustomerListSearch from '@/components/common/Search.vue' import CustomerListSearch from '@/components/common/Search.vue'
import CustomerListTable from '@/components/common/Table.vue' import CustomerListTable from '@/components/common/Table.vue'
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'
@ -145,6 +146,10 @@ const searchForm = ref({
const searchOptions = ref(search) const searchOptions = ref(search)
const tableColumnsOptions = ref(tableColumns) const tableColumnsOptions = ref(tableColumns)
const { loading, startLoading, stopLoading } = usePageLoading()
const { buttonLoading: submitLoading, startButtonLoading: startSubmitLoading, stopButtonLoading: stopSubmitLoading } = useButtonLoading()
const { buttonLoading: adjustAmountLoading, startButtonLoading: startAdjustAmountLoading, stopButtonLoading: stopAdjustAmountLoading } = useButtonLoading()
const { buttonLoading: editCustomerLoading, startButtonLoading: startEditCustomerLoading, stopButtonLoading: stopEditCustomerLoading } = useButtonLoading()
/** /**
* @description: 定义表格数据 * @description: 定义表格数据
*/ */
@ -291,6 +296,7 @@ const roleMap: any = {
*/ */
// //
const getCustomerList = async () => { const getCustomerList = async () => {
startLoading()
const params = { const params = {
page: currentPage.value, page: currentPage.value,
page_size: pageSize.value, page_size: pageSize.value,
@ -315,6 +321,8 @@ const getCustomerList = async () => {
// //
total.value = data.total total.value = data.total
stopLoading()
currentPage.value = data.current_page currentPage.value = data.current_page
pageSize.value = Number(data.per_page) pageSize.value = Number(data.per_page)
} }
@ -437,8 +445,9 @@ const handlePaginationChange = (page: { currentPage: number; pageSize: number })
/** /**
* @description: 定义弹窗方法 * @description: 定义弹窗方法
*/ */
//
const handleSubmit = async () => { const handleSubmit = async () => {
startSubmitLoading()
try {
const params = { const params = {
user_id: selectId.value, user_id: selectId.value,
toucun_percent: '', toucun_percent: '',
@ -486,6 +495,9 @@ const handleSubmit = async () => {
console.log('提交失败:', err) console.log('提交失败:', err)
} }
} }
} finally {
stopSubmitLoading()
}
} }
// //
const handleCancel = () => { const handleCancel = () => {
@ -526,6 +538,7 @@ const handleAdjustAmountInput = () => {
// //
const handleAdjustAmountSubmit = async () => { const handleAdjustAmountSubmit = async () => {
startAdjustAmountLoading()
try { try {
await adjustAmountFormRef.value?.validate() await adjustAmountFormRef.value?.validate()
await updateCapitalApi({ await updateCapitalApi({
@ -536,6 +549,8 @@ const handleAdjustAmountSubmit = async () => {
adjustAmountVisible.value = false adjustAmountVisible.value = false
} catch (err) { } catch (err) {
console.error('调整金额表单验证失败', err) console.error('调整金额表单验证失败', err)
} finally {
stopAdjustAmountLoading()
} }
} }
@ -597,6 +612,7 @@ const handleEditCustomer = async (row: any) => {
// //
const handleEditCustomerSubmit = async () => { const handleEditCustomerSubmit = async () => {
startEditCustomerLoading()
try { try {
await editCustomerFormRef.value?.validate() await editCustomerFormRef.value?.validate()
await editUserApi({ await editUserApi({
@ -611,6 +627,8 @@ const handleEditCustomerSubmit = async () => {
editCustomerVisible.value = false editCustomerVisible.value = false
} catch (err) { } catch (err) {
console.error('修改客户表单验证失败', err) console.error('修改客户表单验证失败', err)
} finally {
stopEditCustomerLoading()
} }
} }

View File

@ -38,7 +38,7 @@ export const tableColumns = [
{ prop: 'role', label: '账号角色' }, { prop: 'role', label: '账号角色' },
// 账号数量 // 账号数量
{ prop: 'accountQty', label: '账号数量' }, { prop: 'accountQty', label: '账号数量' },
{ prop: 'wallet_capital.amount', label: '资金', align: 'center' as const }, { prop: 'wallet_capital.amount', label: '资金', align: 'center' as const, isNumber: true },
// 点差分配比例 // 点差分配比例
{ prop: 'marginRatio', label: '点差分配比例', slotName: 'marginRatio' }, { prop: 'marginRatio', label: '点差分配比例', slotName: 'marginRatio' },
// 手续费分成比例 // 手续费分成比例

View File

@ -3,6 +3,7 @@
<!-- 搜索 --> <!-- 搜索 -->
<MarketSearch <MarketSearch
:search-form="marketSearchForm" :search-form="marketSearchForm"
:buttonLoading="loading"
:search-options="marketSearchOptions" :search-options="marketSearchOptions"
@search="handleSearch" @search="handleSearch"
@reset="handleReset" @reset="handleReset"
@ -27,6 +28,7 @@
:table-data="marketTree" :table-data="marketTree"
:columns="marketTableColumnsOptions" :columns="marketTableColumnsOptions"
row-key="id" row-key="id"
:loading="loading"
@row-click="handleRowClick" @row-click="handleRowClick"
@row-contextmenu="handleRowContextMenu" @row-contextmenu="handleRowContextMenu"
> >
@ -167,7 +169,7 @@
<template #footer v-if="dialogType !== 'view'"> <template #footer v-if="dialogType !== 'view'">
<div class="drawer-footer"> <div class="drawer-footer">
<el-button @click="handleCancel">取消</el-button> <el-button @click="handleCancel">取消</el-button>
<el-button type="primary" @click="handleSubmit">确定</el-button> <el-button type="primary" :loading="submitLoading" @click="handleSubmit">确定</el-button>
</div> </div>
</template> </template>
</el-drawer> </el-drawer>
@ -195,6 +197,7 @@ defineOptions({
// //
import MarketSearch from '@/components/common/Search.vue' import MarketSearch from '@/components/common/Search.vue'
import MarketTable from '@/components/common/Table.vue' import MarketTable from '@/components/common/Table.vue'
import { usePageLoading, useButtonLoading } from '@/composables/useLoading'
import MarketPagination from '@/components/common/Pagination.vue' import MarketPagination from '@/components/common/Pagination.vue'
import MarketDialog from '@/components/common/Dialog.vue' import MarketDialog from '@/components/common/Dialog.vue'
import MarketForm from '@/components/common/Form.vue' import MarketForm from '@/components/common/Form.vue'
@ -239,6 +242,8 @@ const marketSearchForm = ref({
}) })
const marketSearchOptions = ref(marketSearch) const marketSearchOptions = ref(marketSearch)
const { loading, startLoading, stopLoading } = usePageLoading()
const { buttonLoading: submitLoading, startButtonLoading: startSubmitLoading, stopButtonLoading: stopSubmitLoading } = useButtonLoading()
/** /**
* @description 定义表格数据 * @description 定义表格数据
*/ */
@ -565,6 +570,7 @@ const handleMarketFormLinkage = async (type: 'level' | 'parentAccountName', valu
*/ */
// //
const getMarketList = async () => { const getMarketList = async () => {
startLoading()
const params = { const params = {
page: currentPage.value, page: currentPage.value,
page_size: pageSize.value, page_size: pageSize.value,
@ -573,8 +579,10 @@ const getMarketList = async () => {
const data = await getMarketListApi(params) const data = await getMarketListApi(params)
// //
marketTree.value = mapMarketTree(data.data) marketTree.value = mapMarketTree(data.data)
//
total.value = data.total total.value = data.total
//
stopLoading()
currentPage.value = data.current_page currentPage.value = data.current_page
pageSize.value = Number(data.per_page) pageSize.value = Number(data.per_page)
} }
@ -795,6 +803,8 @@ const formatPointDiffToObject = () => {
// //
const handleSubmit = async () => { const handleSubmit = async () => {
startSubmitLoading()
try {
// console.log('', marketPointTree.value); // console.log('', marketPointTree.value);
await marketFormRef.value?.validate() await marketFormRef.value?.validate()
const pointDiffObj = formatPointDiffToObject() const pointDiffObj = formatPointDiffToObject()
@ -845,6 +855,9 @@ const handleSubmit = async () => {
dialogVisible.value = false dialogVisible.value = false
marketForm.value = { ...initMarketForm } marketForm.value = { ...initMarketForm }
} }
} finally {
stopSubmitLoading()
}
} }
// //

View File

@ -25,34 +25,35 @@ export const marketTableColumns = [
//基本账户 //基本账户
{ label: '基本账户', prop: 'basicAccount', align: 'center' as const, width: '200px' }, { label: '基本账户', prop: 'basicAccount', align: 'center' as const, width: '200px' },
//保证金 //保证金
{ label: '保证金', prop: 'margin', align: 'center' as const, width: '200px' }, { label: '保证金', prop: 'margin', align: 'center' as const, width: '200px', isNumber: true },
//停止接单保证金 //停止接单保证金
{ label: '停止接单保证金', prop: 'stopOrderMargin', align: 'center' as const, width: '200px' }, { label: '停止接单保证金', prop: 'stopOrderMargin', align: 'center' as const, width: '200px', isNumber: true },
//持仓转移保证金 //持仓转移保证金
{ {
label: '持仓转移保证金', label: '持仓转移保证金',
prop: 'positionTransferMargin', prop: 'positionTransferMargin',
align: 'center' as const, align: 'center' as const,
width: '200px', width: '200px',
isNumber: true,
}, },
//头寸比率 //头寸比率
{ label: '头寸比率', prop: 'positionRatio', align: 'center' as const, width: '150px' }, { label: '头寸比率', prop: 'positionRatio', align: 'center' as const, width: '150px', isNumber: true },
//点差返佣 //点差返佣
{ label: '点差返佣', prop: 'pointSpreadCommission', align: 'center' as const, width: '200px' }, { label: '点差返佣', prop: 'pointSpreadCommission', align: 'center' as const, width: '200px', isNumber: true },
//点差 //点差
{ label: '点差', slotName: 'pointSpread', align: 'center' as const, width: '150px' }, { label: '点差', slotName: 'pointSpread', align: 'center' as const, width: '150px' },
//手续费比例 //手续费比例
{ label: '手续费比例', prop: 'commissionRatio', align: 'center' as const, width: '150px' }, { label: '手续费比例', prop: 'commissionRatio', align: 'center' as const, width: '150px', isNumber: true },
//手续费返佣 //手续费返佣
{ label: '手续费返佣', prop: 'commissionReturn', align: 'center' as const, width: '150px' }, { label: '手续费返佣', prop: 'commissionReturn', align: 'center' as const, width: '150px', isNumber: true },
//浮动盈亏 //浮动盈亏
{ label: '浮动盈亏', prop: 'floatProfitLoss', align: 'center' as const, width: '100px' }, { label: '浮动盈亏', prop: 'floatProfitLoss', align: 'center' as const, width: '100px', isNumber: true },
//平仓盈亏 //平仓盈亏
{ label: '平仓盈亏', prop: 'closeProfitLoss', align: 'center' as const, width: '150px' }, { label: '平仓盈亏', prop: 'closeProfitLoss', align: 'center' as const, width: '150px', isNumber: true },
//风险金 //风险金
{ label: '风险金', prop: 'riskMargin', align: 'center' as const, width: '150px' }, { label: '风险金', prop: 'riskMargin', align: 'center' as const, width: '150px', isNumber: true },
//服务费 //服务费
{ label: '服务费', prop: 'serviceFee', align: 'center' as const, width: '150px' }, { label: '服务费', prop: 'serviceFee', align: 'center' as const, width: '150px', isNumber: true },
] ]
//做市商添加配置 //做市商添加配置

View File

@ -69,7 +69,7 @@
</div> </div>
<!-- 编辑弹窗 --> <!-- 编辑弹窗 -->
<Dialog :visible="dialogVisible" :title="dialogTitle" width="500px" @confirm="handleEditConfirm" <Dialog :visible="dialogVisible" :title="dialogTitle" width="500px" :button-loading="editLoading" @confirm="handleEditConfirm"
@cancel="handleEditDialogReset" @close="handleEditDialogReset"> @cancel="handleEditDialogReset" @close="handleEditDialogReset">
<el-form ref="editFormRef" :model="editForm" :rules="getEditRules()" label-width="80px" label-position="top"> <el-form ref="editFormRef" :model="editForm" :rules="getEditRules()" label-width="80px" label-position="top">
<el-form-item v-if="editField === 'mobile'" :label="editForm.label" prop="value" class="phone-item"> <el-form-item v-if="editField === 'mobile'" :label="editForm.label" prop="value" class="phone-item">
@ -96,7 +96,7 @@
<!-- 密码修改弹窗 验证码模式 --> <!-- 密码修改弹窗 验证码模式 -->
<Dialog :visible="passwordDialogVisible" :title="t('personalCenter.modifyPassword')" width="500px" <Dialog :visible="passwordDialogVisible" :title="t('personalCenter.modifyPassword')" width="500px"
@confirm="handlePasswordConfirm" @cancel="handlePasswordDialogReset" @close="handlePasswordDialogReset"> :button-loading="passwordLoading" @confirm="handlePasswordConfirm" @cancel="handlePasswordDialogReset" @close="handlePasswordDialogReset">
<el-form ref="passwordFormRef" :model="passwordForm" :rules="passwordFormRules" label-width="100px" <el-form ref="passwordFormRef" :model="passwordForm" :rules="passwordFormRules" label-width="100px"
label-position="top"> label-position="top">
<!-- 选择验证方式 --> <!-- 选择验证方式 -->
@ -174,6 +174,8 @@ import {
updatePwdByCaptchaApi updatePwdByCaptchaApi
} from '@/api/modules/profile/personalCenter'; } from '@/api/modules/profile/personalCenter';
import { useButtonLoading } from '@/composables/useLoading';
const userInfo = reactive<UserInfo>({ const userInfo = reactive<UserInfo>({
id: 0, id: 0,
username: '', username: '',
@ -186,6 +188,8 @@ const userInfo = reactive<UserInfo>({
const dialogVisible = ref(false); const dialogVisible = ref(false);
const passwordDialogVisible = ref(false); const passwordDialogVisible = ref(false);
const { buttonLoading: editLoading, startButtonLoading: startEditLoading, stopButtonLoading: stopEditLoading } = useButtonLoading();
const { buttonLoading: passwordLoading, startButtonLoading: startPasswordLoading, stopButtonLoading: stopPasswordLoading } = useButtonLoading();
const editFormRef = ref<FormInstance>(); const editFormRef = ref<FormInstance>();
const passwordFormRef = ref<FormInstance>(); const passwordFormRef = ref<FormInstance>();
const editField = ref<string>(''); const editField = ref<string>('');
@ -399,6 +403,7 @@ const handleLanguageChange = async (val: number) => {
// //
const handleEditConfirm = async () => { const handleEditConfirm = async () => {
if (!editFormRef.value) return; if (!editFormRef.value) return;
startEditLoading();
try { try {
await editFormRef.value.validate(); await editFormRef.value.validate();
@ -425,6 +430,8 @@ const handleEditConfirm = async () => {
} catch (err) { } catch (err) {
// APIElMessage // APIElMessage
console.error('操作失败:', err); console.error('操作失败:', err);
} finally {
stopEditLoading();
} }
}; };
@ -512,6 +519,7 @@ watch(
// //
const handlePasswordConfirm = async () => { const handlePasswordConfirm = async () => {
if (!passwordFormRef.value) return; if (!passwordFormRef.value) return;
startPasswordLoading();
try { try {
await passwordFormRef.value.validate(); await passwordFormRef.value.validate();
@ -536,6 +544,8 @@ const handlePasswordConfirm = async () => {
} catch (err) { } catch (err) {
// APIElMessage // APIElMessage
console.error('密码修改失败:', err); console.error('密码修改失败:', err);
} finally {
stopPasswordLoading();
} }
}; };

View File

@ -1,12 +1,8 @@
<template> <template>
<div class="client table_form-container"> <div class="client table_form-container">
<!-- 搜索组件 --> <!-- 搜索组件 -->
<ClientSearch <ClientSearch :search-form="clientSearchForm" :search-options="clientSearchOptions" :buttonLoading="loading"
:search-form="clientSearchForm" @search="handleSearch" @reset="handleReset">
:search-options="clientSearchOptions"
@search="handleSearch"
@reset="handleReset"
>
<template #button="{ form }"> <template #button="{ form }">
<el-button type="primary" @click="handleAdd()" v-auth="'user_createUser'"> <el-button type="primary" @click="handleAdd()" v-auth="'user_createUser'">
添加客户 添加客户
@ -15,7 +11,7 @@
</ClientSearch> </ClientSearch>
<!-- 表格组件 --> <!-- 表格组件 -->
<ClientTable :table-data="clientTree" :columns="clientTableColumns"> <ClientTable :table-data="clientTree" :columns="clientTableColumns" :loading="loading">
<template #status="{ row }"> <template #status="{ row }">
{{ row.status === 1 ? '正常' : '禁用' }} {{ row.status === 1 ? '正常' : '禁用' }}
</template> </template>
@ -29,44 +25,21 @@
</template> </template>
</ClientTable> </ClientTable>
<!-- 分页组件 --> <!-- 分页组件 -->
<ClientPagination <ClientPagination v-model="currentPage" v-model:pageSizeValue="pageSize" :total="total"
v-model="currentPage" @pagination-change="handlePaginationChange" />
v-model:pageSizeValue="pageSize"
:total="total"
@pagination-change="handlePaginationChange"
/>
</div> </div>
<!-- 客户弹窗 --> <!-- 客户弹窗 -->
<ClientDialog <ClientDialog :visible="dialogVisible" :title="dialogTitle" :type="dialogType" :button-loading="submitLoading"
:visible="dialogVisible" @confirm="handleSubmit" @cancel="handleCancel" @close="handleClose">
:title="dialogTitle" <ClientForm :form-data="clientForm" :form-rules="clientFormRules" :form-items="clientFormOptions"
:type="dialogType" :options-map="clientFormOptionsMap" ref="clientFormRef">
@confirm="handleSubmit"
@cancel="handleCancel"
@close="handleClose"
>
<ClientForm
:form-data="clientForm"
:form-rules="clientFormRules"
:form-items="clientFormOptions"
:options-map="clientFormOptionsMap"
ref="clientFormRef"
>
<template #phoneOrEmail="{ formData }" style="margin-left: 0"> <template #phoneOrEmail="{ formData }" style="margin-left: 0">
<div class="phone-email"> <div class="phone-email">
<div class="tab-btn"> <div class="tab-btn">
<el-button <el-button :type="activeTab === 'email' ? 'primary' : 'default'" size="small" @click="activeTab = 'email'">
:type="activeTab === 'email' ? 'primary' : 'default'"
size="small"
@click="activeTab = 'email'"
>
邮箱 邮箱
</el-button> </el-button>
<el-button <el-button :type="activeTab === 'phone' ? 'primary' : 'default'" size="small" @click="activeTab = 'phone'">
:type="activeTab === 'phone' ? 'primary' : 'default'"
size="small"
@click="activeTab = 'phone'"
>
手机号 手机号
</el-button> </el-button>
</div> </div>
@ -76,12 +49,7 @@
</el-form-item> </el-form-item>
<el-form-item prop="phone" class="phone-item" v-if="activeTab === 'phone'"> <el-form-item prop="phone" class="phone-item" v-if="activeTab === 'phone'">
<el-select v-model="clientForm.countryCode" placeholder="请选择国家区号"> <el-select v-model="clientForm.countryCode" placeholder="请选择国家区号">
<el-option <el-option v-for="item in countryOptions" :key="item.code" :label="item.code" :value="item.code">
v-for="item in countryOptions"
:key="item.code"
:label="item.code"
:value="item.code"
>
<div class="country-option"> <div class="country-option">
<span class="country-name">{{ item.name }}</span> <span class="country-name">{{ item.name }}</span>
<span class="country-code">{{ item.code }}</span> <span class="country-code">{{ item.code }}</span>
@ -97,14 +65,8 @@
</ClientDialog> </ClientDialog>
<!-- 调整金额弹窗 --> <!-- 调整金额弹窗 -->
<ClientDialog <ClientDialog :visible="adjustAmountVisible" title="调整金额" type="adjust" :button-loading="adjustAmountLoading"
:visible="adjustAmountVisible" @confirm="handleAdjustAmountSubmit" @cancel="handleAdjustAmountCancel" @close="handleAdjustAmountClose">
title="调整金额"
type="adjust"
@confirm="handleAdjustAmountSubmit"
@cancel="handleAdjustAmountCancel"
@close="handleAdjustAmountClose"
>
<el-form :model="adjustAmountForm" :rules="adjustAmountRules" ref="adjustAmountFormRef" label-width="120px"> <el-form :model="adjustAmountForm" :rules="adjustAmountRules" ref="adjustAmountFormRef" label-width="120px">
<el-form-item label="账号名" prop="username"> <el-form-item label="账号名" prop="username">
<el-input v-model="adjustAmountForm.username" disabled /> <el-input v-model="adjustAmountForm.username" disabled />
@ -116,11 +78,8 @@
<el-input v-model="adjustAmountForm.currentBalance" disabled /> <el-input v-model="adjustAmountForm.currentBalance" disabled />
</el-form-item> </el-form-item>
<el-form-item label="调整金额" prop="adjustAmount"> <el-form-item label="调整金额" prop="adjustAmount">
<el-input <el-input v-model="adjustAmountForm.adjustAmount" placeholder="请输入调整金额(可为负数)"
v-model="adjustAmountForm.adjustAmount" @input="handleAdjustAmountInput" />
placeholder="请输入调整金额(可为负数)"
@input="handleAdjustAmountInput"
/>
</el-form-item> </el-form-item>
<el-form-item label="调整后余额" prop="afterBalance"> <el-form-item label="调整后余额" prop="afterBalance">
<el-input v-model="adjustAmountForm.afterBalance" disabled /> <el-input v-model="adjustAmountForm.afterBalance" disabled />
@ -137,6 +96,7 @@ defineOptions({
// //
import ClientSearch from '@/components/common/Search.vue' import ClientSearch from '@/components/common/Search.vue'
import ClientTable from '@/components/common/Table.vue' import ClientTable from '@/components/common/Table.vue'
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'
@ -172,6 +132,9 @@ const clientSearchForm = ref({
endDateTime: '', endDateTime: '',
}) })
const { loading, startLoading, stopLoading } = usePageLoading()
const { buttonLoading: submitLoading, startButtonLoading: startSubmitLoading, stopButtonLoading: stopSubmitLoading } = useButtonLoading()
const { buttonLoading: adjustAmountLoading, startButtonLoading: startAdjustAmountLoading, stopButtonLoading: stopAdjustAmountLoading } = useButtonLoading()
/** /**
* @description 定义表格数据 * @description 定义表格数据
*/ */
@ -281,6 +244,8 @@ const mapObjectToOptions = (obj: Record<string, string>) => {
* @description 统一获取数据方法 * @description 统一获取数据方法
*/ */
const getClientList = async () => { const getClientList = async () => {
try {
startLoading()
const params = { const params = {
page: currentPage.value, page: currentPage.value,
page_size: pageSize.value, page_size: pageSize.value,
@ -304,10 +269,13 @@ const getClientList = async () => {
// //
agentUsername: item.par_uid === item.parent?.id ? item.parent?.username : '', agentUsername: item.par_uid === item.parent?.id ? item.parent?.username : '',
})) }))
//
total.value = Number(data.total) total.value = Number(data.total)
//
currentPage.value = Number(data.current_page) currentPage.value = Number(data.current_page)
pageSize.value = Number(data.per_page) pageSize.value = Number(data.per_page)
} finally {
stopLoading()
}
} }
// //
@ -352,11 +320,8 @@ onMounted(async () => {
* @description 搜索方法 * @description 搜索方法
*/ */
const handleSearch = async () => { const handleSearch = async () => {
try {
await getClientList() await getClientList()
} catch (err) {
console.error('获取客户列表失败', err)
}
} }
const handleReset = async () => { const handleReset = async () => {
// //
@ -366,11 +331,7 @@ const handleReset = async () => {
startDateTime: '', startDateTime: '',
endDateTime: '', endDateTime: '',
} }
try {
await getClientList() await getClientList()
} catch (err) {
console.error('获取客户列表失败', err)
}
} }
// //
const handleAdd = () => { const handleAdd = () => {
@ -431,8 +392,9 @@ const handlePaginationChange = (page: { currentPage: number; pageSize: number })
* @description 客户弹窗方法 * @description 客户弹窗方法
*/ */
const handleSubmit = async () => { const handleSubmit = async () => {
try {
await clientFormRef.value?.validate() await clientFormRef.value?.validate()
startSubmitLoading()
try {
if (dialogType.value === 'add') { if (dialogType.value === 'add') {
const params = { const params = {
reg_type: activeTab.value === 'email' ? 1 : 2, reg_type: activeTab.value === 'email' ? 1 : 2,
@ -460,6 +422,8 @@ const handleSubmit = async () => {
} }
} catch (err) { } catch (err) {
console.error('客户表单验证失败', err) console.error('客户表单验证失败', err)
} finally {
stopSubmitLoading()
} }
} }
const handleCancel = () => { const handleCancel = () => {
@ -497,16 +461,19 @@ const handleAdjustAmountInput = () => {
// //
const handleAdjustAmountSubmit = async () => { const handleAdjustAmountSubmit = async () => {
try {
await adjustAmountFormRef.value?.validate() await adjustAmountFormRef.value?.validate()
startAdjustAmountLoading()
try {
await updateCapitalApi({ await updateCapitalApi({
target_user_id: adjustUserId.value, target_user_id: adjustUserId.value,
amount: adjustAmountForm.value.adjustAmount, amount: adjustAmountForm.value.adjustAmount,
}) })
await getClientList() await getClientList()
adjustAmountVisible.value = false
} catch (err) { } catch (err) {
console.error('调整金额表单验证失败', err) console.error('调整金额表单验证失败', err)
} finally {
adjustAmountVisible.value = false
stopAdjustAmountLoading()
} }
} }
@ -522,8 +489,8 @@ const handleAdjustAmountClose = () => {
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
.client{ .client {}
}
:deep(.el-form-item__content) { :deep(.el-form-item__content) {
margin-left: 0 !important; margin-left: 0 !important;
} }

View File

@ -54,7 +54,7 @@ export const clientTableColumns = [
{ prop: 'username', label: '用户名', align: 'center' as const }, { prop: 'username', label: '用户名', align: 'center' as const },
{ prop: 'createdTime', label: '注册时间', align: 'center' as const }, { prop: 'createdTime', label: '注册时间', align: 'center' as const },
{ prop: 'agentUsername', label: '所属代理', align: 'center' as const }, { prop: 'agentUsername', label: '所属代理', align: 'center' as const },
{ prop: 'wallet_capital.amount', label: '资金', align: 'center' as const }, { prop: 'wallet_capital.amount', label: '资金', align: 'center' as const, isNumber: true },
{ label: '状态', slotName: 'status', align: 'center' as const }, { label: '状态', slotName: 'status', align: 'center' as const },
{ label: '操作', slotName: 'action', align: 'center' as const }, { label: '操作', slotName: 'action', align: 'center' as const },
] ]

View File

@ -11,6 +11,7 @@
:columns="menuTableColumns" :columns="menuTableColumns"
row-key="id" row-key="id"
:tree-props="treeProps" :tree-props="treeProps"
:loading="loading"
> >
<template #icon="{ row }"> <template #icon="{ row }">
<el-icon v-if="row.icon && menuiconMap[row.icon]"> <el-icon v-if="row.icon && menuiconMap[row.icon]">
@ -71,6 +72,9 @@ defineOptions({
}) })
import type { FormInstance } from 'element-plus' import type { FormInstance } from 'element-plus'
import { Plus } from '@element-plus/icons-vue' import { Plus } from '@element-plus/icons-vue'
import { usePageLoading } from '@/composables/useLoading'
const { loading, startLoading, stopLoading } = usePageLoading()
// //
import MenuTable from '@/components/common/Table.vue' import MenuTable from '@/components/common/Table.vue'
import MenuDialog from '@/components/common/Dialog.vue' import MenuDialog from '@/components/common/Dialog.vue'
@ -137,6 +141,7 @@ const handleAddMenu = () => {
// //
const fetchMenuList = async () => { const fetchMenuList = async () => {
startLoading()
try { try {
const data = await getMenuApi() const data = await getMenuApi()
menuTree.value = data menuTree.value = data
@ -148,6 +153,8 @@ const fetchMenuList = async () => {
menuFormOptionsMap.value.parent_id = [{ label: '一级菜单', value: '0' }, ...parentNameOptions] menuFormOptionsMap.value.parent_id = [{ label: '一级菜单', value: '0' }, ...parentNameOptions]
} catch (err) { } catch (err) {
console.error('获取菜单列表失败', err) console.error('获取菜单列表失败', err)
} finally {
stopLoading()
} }
} }
// //

View File

@ -3,6 +3,7 @@
<!-- 搜索 --> <!-- 搜索 -->
<NoticeSearch <NoticeSearch
:search-form="searchForm" :search-form="searchForm"
:buttonLoading="loading"
:search-options="searchOptions" :search-options="searchOptions"
@search="handleSearch" @search="handleSearch"
@reset="handleReset" @reset="handleReset"
@ -14,7 +15,7 @@
</template> </template>
</NoticeSearch> </NoticeSearch>
<!-- 表格 --> <!-- 表格 -->
<NoticeTable :table-data="noticeTree" :columns="noticeTableColumnsOptions" row-key="id"> <NoticeTable :table-data="noticeTree" :columns="noticeTableColumnsOptions" row-key="id" :loading="loading">
<template #status="{ row }"> <template #status="{ row }">
<!-- 修正状态显示1=草稿2=已发布 --> <!-- 修正状态显示1=草稿2=已发布 -->
{{ row.status === 1 ? '草稿' : '已发布' }} {{ row.status === 1 ? '草稿' : '已发布' }}
@ -55,6 +56,7 @@
:title="dialogTitle" :title="dialogTitle"
:type="dialogType" :type="dialogType"
:show-footer="dialogType !== 'view'" :show-footer="dialogType !== 'view'"
:button-loading="submitLoading"
@confirm="handleSubmit" @confirm="handleSubmit"
@cancel="handleCancel" @cancel="handleCancel"
@close="handleClose" @close="handleClose"
@ -99,6 +101,10 @@
defineOptions({ defineOptions({
name: 'Notice' name: 'Notice'
}) })
import { usePageLoading, useButtonLoading } from '@/composables/useLoading'
const { loading, startLoading, stopLoading } = usePageLoading()
const { buttonLoading: submitLoading, startButtonLoading: startSubmitLoading, stopButtonLoading: stopSubmitLoading } = useButtonLoading()
// //
import NoticeSearch from '@/components/common/Search.vue' import NoticeSearch from '@/components/common/Search.vue'
import NoticeTable from '@/components/common/Table.vue' import NoticeTable from '@/components/common/Table.vue'
@ -223,6 +229,7 @@ const noticeFormOptionsMap = ref({
*/ */
// //
const getNoticeList = async () => { const getNoticeList = async () => {
startLoading()
const params = { const params = {
page: currentPage.value, page: currentPage.value,
page_size: pageSize.value, page_size: pageSize.value,
@ -231,6 +238,7 @@ const getNoticeList = async () => {
publish_start_time: searchForm.value.startDateTime, publish_start_time: searchForm.value.startDateTime,
publish_end_time: searchForm.value.endDateTime, publish_end_time: searchForm.value.endDateTime,
} }
try {
const data: any = await getNoticeListApi(params) const data: any = await getNoticeListApi(params)
// //
noticeTree.value = data.data.map((item: any) => ({ noticeTree.value = data.data.map((item: any) => ({
@ -244,6 +252,9 @@ const getNoticeList = async () => {
total.value = data.total total.value = data.total
currentPage.value = data.current_page currentPage.value = data.current_page
pageSize.value = Number(data.per_page) pageSize.value = Number(data.per_page)
} finally {
stopLoading()
}
} }
/** /**
@ -337,6 +348,8 @@ const handlePaginationChange = async (page: { currentPage: number; pageSize: num
* @description: 定义弹窗方法 * @description: 定义弹窗方法
*/ */
const handleSubmit = async () => { const handleSubmit = async () => {
startSubmitLoading()
try {
await noticeFormRef.value.validate() await noticeFormRef.value.validate()
if (dialogType.value === 'create') { if (dialogType.value === 'create') {
noticeForm.value.noticeContent = valueHtml.value noticeForm.value.noticeContent = valueHtml.value
@ -360,6 +373,9 @@ const handleSubmit = async () => {
dialogVisible.value = false dialogVisible.value = false
getNoticeList() getNoticeList()
} }
} finally {
stopSubmitLoading()
}
} }
const handleCancel = () => { const handleCancel = () => {

View File

@ -6,7 +6,7 @@
<el-button type="primary" @click="handleAddRole" v-auth="'roles_create'">新增角色</el-button> <el-button type="primary" @click="handleAddRole" v-auth="'roles_create'">新增角色</el-button>
</div> </div>
<!-- 角色列表表格 --> <!-- 角色列表表格 -->
<RoleTable :table-data="roleTree" :columns="roleTableColumns" row-key="id"> <RoleTable :table-data="roleTree" :columns="roleTableColumns" row-key="id" :loading="loading">
<template #status="{ row }"> <template #status="{ row }">
<el-switch :model-value="row.status === 1" :active-value="true" :inactive-value="false" active-text="启用" <el-switch :model-value="row.status === 1" :active-value="true" :inactive-value="false" active-text="启用"
inactive-text="禁用" /> inactive-text="禁用" />
@ -32,16 +32,15 @@
<RoleForm :form-data="roleForm" :form-rules="roleFormRules" :form-items="roleFormItemOptions" ref="roleFormRef" <RoleForm :form-data="roleForm" :form-rules="roleFormRules" :form-items="roleFormItemOptions" ref="roleFormRef"
:options-map="roleFormOptionsMap" :dialog-type="dialogType"> :options-map="roleFormOptionsMap" :dialog-type="dialogType">
<template #permissions="{ formData }"> <template #permissions="{ formData }">
<el-tree ref="permissionTreeRef" :data="permissionTreeData" :props="treeProps" show-checkbox node-key="id" <el-tree v-loading="treeLoading" ref="permissionTreeRef" :data="permissionTreeData" :props="treeProps"
:default-expand-all="true" :check-strictly="false" show-checkbox node-key="id" :default-expand-all="true" :check-strictly="false" class="permission-tree" />
class="permission-tree" />
</template> </template>
</RoleForm> </RoleForm>
<template #footer> <template #footer>
<div style="flex: auto"> <div style="flex: auto">
<el-button @click="handleCancel">取消</el-button> <el-button @click="handleCancel">取消</el-button>
<el-button type="primary" @click="handleSubmit">确定</el-button> <el-button type="primary" :loading="buttonLoading" @click="handleSubmit">确定</el-button>
</div> </div>
</template> </template>
</el-drawer> </el-drawer>
@ -54,6 +53,11 @@ defineOptions({
}) })
import type { FormInstance, TreeInstance } from 'element-plus' import type { FormInstance, TreeInstance } from 'element-plus'
import { Plus } from '@element-plus/icons-vue' import { Plus } from '@element-plus/icons-vue'
import { usePageLoading, useButtonLoading } from '@/composables/useLoading'
const { loading, startLoading, stopLoading } = usePageLoading()
const { loading: treeLoading, startLoading: treeStartLoading, stopLoading: treeStopLoading } = usePageLoading()
const { buttonLoading, startButtonLoading, stopButtonLoading } = useButtonLoading()
// //
import RoleTable from '@/components/common/Table.vue' import RoleTable from '@/components/common/Table.vue'
@ -140,11 +144,14 @@ const filterRoles = (roleList: any[]) => {
// //
const fetchRoleList = async () => { const fetchRoleList = async () => {
startLoading()
try { try {
const data = await getRoleApi() const data = await getRoleApi()
roleTree.value = data roleTree.value = data
} catch (err) { } catch (err) {
console.error('获取角色列表失败', err) console.error('获取角色列表失败', err)
} finally {
stopLoading()
} }
} }
@ -265,7 +272,9 @@ const getParentNodeIds = (checkedKeys: number[], treeData: any[]): number[] => {
const handlePermission = async (row: any) => { const handlePermission = async (row: any) => {
dialogVisible.value = true dialogVisible.value = true
dialogTitle.value = '设置角色权限' dialogTitle.value = '设置角色权限'
permissionTreeData.value = []
dialogType.value = 'permissions' dialogType.value = 'permissions'
treeStartLoading()
handleDialogType(dialogType.value) handleDialogType(dialogType.value)
permissionIds.value = row.id permissionIds.value = row.id
try { try {
@ -286,6 +295,8 @@ const handlePermission = async (row: any) => {
permissionTreeRef.value?.setCheckedKeys(childPermissionIds, false) permissionTreeRef.value?.setCheckedKeys(childPermissionIds, false)
} catch (err) { } catch (err) {
console.error('获取角色权限失败', err) console.error('获取角色权限失败', err)
} finally {
treeStopLoading()
} }
} }
@ -293,6 +304,7 @@ const handlePermission = async (row: any) => {
@description: 处理弹窗提交/取消/关闭事件 @description: 处理弹窗提交/取消/关闭事件
**/ **/
const handleSubmit = async () => { const handleSubmit = async () => {
startButtonLoading()
console.log('提交菜单表单', roleForm.value.permissions, allPermissions.value) console.log('提交菜单表单', roleForm.value.permissions, allPermissions.value)
try { try {
await roleFormRef.value?.validate() await roleFormRef.value?.validate()
@ -328,6 +340,8 @@ const handleSubmit = async () => {
} catch (err) { } catch (err) {
console.log('表单验证失败') console.log('表单验证失败')
return return
} finally {
stopButtonLoading()
} }
} }

View File

@ -3,7 +3,7 @@
<!-- 菜单列表头 --> <!-- 菜单列表头 -->
<MenuHeader :title="menuTitle" :buttons="menuHeaderButtons" @click="handleAddMenu" /> <MenuHeader :title="menuTitle" :buttons="menuHeaderButtons" @click="handleAddMenu" />
<!-- 菜单列表表格 --> <!-- 菜单列表表格 -->
<MenuTable :table-data="menuTree" :columns="menuTableColumns" row-key="id"> <MenuTable :table-data="menuTree" :columns="menuTableColumns" row-key="id" :loading="loading">
<template #action="{ row }"> <template #action="{ row }">
<el-button link type="primary" size="small" @click="handleEditMenu(row)"> 编辑 </el-button> <el-button link type="primary" size="small" @click="handleEditMenu(row)"> 编辑 </el-button>
</template> </template>
@ -33,6 +33,9 @@ defineOptions({
}) })
import type { FormInstance } from 'element-plus' import type { FormInstance } from 'element-plus'
import { Plus } from '@element-plus/icons-vue' import { Plus } from '@element-plus/icons-vue'
import { usePageLoading } from '@/composables/useLoading'
const { loading, startLoading, stopLoading } = usePageLoading()
// //
import MenuHeader from '@/components/common/Header.vue' import MenuHeader from '@/components/common/Header.vue'
import MenuTable from '@/components/common/Table.vue' import MenuTable from '@/components/common/Table.vue'
@ -88,11 +91,14 @@ const handleAddMenu = () => {
// //
const fetchSettingList = async () => { const fetchSettingList = async () => {
startLoading()
try { try {
const data = await getSettingApi() const data = await getSettingApi()
menuTree.value = data menuTree.value = data
} catch (err) { } catch (err) {
console.error('获取菜单列表失败', err) console.error('获取菜单列表失败', err)
} finally {
stopLoading()
} }
} }
// //

View File

@ -1,24 +1,14 @@
<template> <template>
<div class="trade-group table_form-container"> <div class="trade-group table_form-container">
<el-form-item label="交易节假日时间设置"> <el-form-item label="交易节假日时间设置">
<el-button <el-button style="margin-left: 18px" type="primary" v-auth="'contractVariety_addHoliday'"
style="margin-left: 18px" @click="handleAdd">添加</el-button>
type="primary" <el-button style="margin-left: 18px" type="danger" v-auth="'contractVariety_addHoliday'"
v-auth="'contractVariety_addHoliday'" @click="handleDel">删除</el-button>
@click="handleAdd"
>添加</el-button
>
<el-button
style="margin-left: 18px"
type="danger"
v-auth="'contractVariety_addHoliday'"
@click="handleDel"
>删除</el-button
>
</el-form-item> </el-form-item>
<!-- 表格 --> <!-- 表格 -->
<div class="table-box"> <div class="table-box">
<table> <table v-loading="loading">
<thead> <thead>
<tr> <tr>
<th>名称</th> <th>名称</th>
@ -36,33 +26,16 @@
<span v-else>{{ item.name }}</span> <span v-else>{{ item.name }}</span>
</td> </td>
<td> <td>
<el-date-picker <el-date-picker v-if="item.editStatus" v-model="item.datePicker" type="datetimerange"
v-if="item.editStatus" start-placeholder="休市开始时间" end-placeholder="休市结束时间" format="YYYY-MM-DD HH:mm:ss"
v-model="item.datePicker" date-format="YYYY-MM-DD HH:mm:ss" time-format="YYYY-MM-DD HH:mm:ss"
type="datetimerange" value-format="YYYY-MM-DD HH:mm:ss" />
start-placeholder="休市开始时间"
end-placeholder="休市结束时间"
format="YYYY-MM-DD HH:mm:ss"
date-format="YYYY-MM-DD HH:mm:ss"
time-format="YYYY-MM-DD HH:mm:ss"
value-format="YYYY-MM-DD HH:mm:ss"
/>
<span v-else>{{ item.datePicker[0] }} - {{ item.datePicker[1] }}</span> <span v-else>{{ item.datePicker[0] }} - {{ item.datePicker[1] }}</span>
</td> </td>
<td> <td>
<el-select <el-select multiple placeholder="请选择休市市场" style="width: 240px" v-model="item.groupId"
multiple v-if="item.editStatus">
placeholder="请选择休市市场" <el-option v-for="item in marketList" :key="item.value" :label="item.label" :value="item.value" />
style="width: 240px"
v-model="item.groupId"
v-if="item.editStatus"
>
<el-option
v-for="item in marketList"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select> </el-select>
<span>{{ <span>{{
(item?.groupId || []) (item?.groupId || [])
@ -78,34 +51,14 @@
<span v-else>{{ item.description }}</span> <span v-else>{{ item.description }}</span>
</td> </td>
<td> <td>
<el-button <el-button type="primary" v-if="!item.editStatus" @click="item.editStatus = true"
type="primary" v-auth="'contractVariety_addHoliday'">编辑</el-button>
v-if="!item.editStatus" <el-button type="danger" v-if="!item.editStatus" @click="handleDel(item.id, index)"
@click="item.editStatus = true" v-auth="'contractVariety_addHoliday'">删除</el-button>
v-auth="'contractVariety_addHoliday'" <el-button type="primary" v-if="item.editStatus" @click="submitItem(index)"
>编辑</el-button v-auth="'contractVariety_addHoliday'">确认</el-button>
> <el-button type="danger" v-if="item.editStatus" @click="item.editStatus = false"
<el-button v-auth="'contractVariety_addHoliday'">取消</el-button>
type="danger"
v-if="!item.editStatus"
@click="handleDel(item.id, index)"
v-auth="'contractVariety_addHoliday'"
>删除</el-button
>
<el-button
type="primary"
v-if="item.editStatus"
@click="submitItem(index)"
v-auth="'contractVariety_addHoliday'"
>确认</el-button
>
<el-button
type="danger"
v-if="item.editStatus"
@click="item.editStatus = false"
v-auth="'contractVariety_addHoliday'"
>取消</el-button
>
</td> </td>
</tr> </tr>
</tbody> </tbody>
@ -123,6 +76,10 @@ import { ref, onMounted } from 'vue'
import { getGroupListApi } from '@/api/modules/trade/group' import { getGroupListApi } from '@/api/modules/trade/group'
import { getHolidayList, addHoliday, editHoliday, delHoliday } from '@/api/modules/trade/time' import { getHolidayList, addHoliday, editHoliday, delHoliday } from '@/api/modules/trade/time'
import { ElMessageBox } from 'element-plus' import { ElMessageBox } from 'element-plus'
import { usePageLoading } from '@/composables/useLoading'
// loading
const { loading, startLoading, stopLoading } = usePageLoading()
type DataItem = { type DataItem = {
id?: number | string id?: number | string
@ -150,6 +107,8 @@ const getMarketList = async () => {
} }
const initDate = () => { const initDate = () => {
startLoading()
try {
getHolidayList().then((res) => { getHolidayList().then((res) => {
dataList.value = res.map((item: any) => ({ dataList.value = res.map((item: any) => ({
id: item.id + '', id: item.id + '',
@ -162,6 +121,7 @@ const initDate = () => {
editStatus: false, editStatus: false,
})) }))
}) })
} finally { stopLoading() }
} }
const handleAdd = () => { const handleAdd = () => {
@ -223,6 +183,7 @@ onMounted(() => {
.trade-group { .trade-group {
overflow-y: scroll; overflow-y: scroll;
} }
/* 给 table 及所有 td 加边框 */ /* 给 table 及所有 td 加边框 */
table { table {
border-collapse: collapse; border-collapse: collapse;

View File

@ -1,13 +1,11 @@
<template> <template>
<div class="trade-group table_form-container"> <div class="trade-group table_form-container">
<el-form-item label="交易组时间设置"> <el-form-item label="交易组时间设置">
<el-button type="primary" @click="addTableItem" v-auth="'contractVariety_createGroup'" <el-button type="primary" @click="addTableItem" v-auth="'contractVariety_createGroup'">添加</el-button>
>添加</el-button
>
</el-form-item> </el-form-item>
<!-- 表格 --> <!-- 表格 -->
<div class="table-box"> <div class="table-box">
<table> <table v-loading="loading">
<template v-for="(item, index) in tradeGroupTree" :key="item.id"> <template v-for="(item, index) in tradeGroupTree" :key="item.id">
<tr> <tr>
<td style="width: 50px">顺序</td> <td style="width: 50px">顺序</td>
@ -35,23 +33,13 @@
<td class="time-picker-box"> <td class="time-picker-box">
<template v-if="item.editStatus"> <template v-if="item.editStatus">
<template v-if="item?.x_time_list"> <template v-if="item?.x_time_list">
<el-time-picker <el-time-picker v-for="(time, timeIndex) in item?.x_time_list" :key="timeIndex"
v-for="(time, timeIndex) in item?.x_time_list" v-model="item.x_time_list[timeIndex]" style="width: 180px; flex: 0 0 auto" format="HH:mm:ss"
:key="timeIndex" value-format="HH:mm:ss" :picker-options="{
v-model="item.x_time_list[timeIndex]"
style="width: 180px; flex: 0 0 auto"
format="HH:mm:ss"
value-format="HH:mm:ss"
:picker-options="{
selectableRange: '09:00:00 - 18:00:00', selectableRange: '09:00:00 - 18:00:00',
}" }" is-range />
is-range
/>
</template> </template>
<img <img src="@/assets/images/trade/group/add.png" @click="addTime(index, 'x_time_list')" />
src="@/assets/images/trade/group/add.png"
@click="addTime(index, 'x_time_list')"
/>
</template> </template>
<template v-else> <template v-else>
<template v-if="item?.x_time_list"> <template v-if="item?.x_time_list">
@ -62,34 +50,14 @@
</template> </template>
</td> </td>
<td rowspan="2" style="width: 140px"> <td rowspan="2" style="width: 140px">
<el-button <el-button type="primary" v-if="!item.editStatus" @click="item.editStatus = true"
type="primary" v-auth="'contractVariety_editGroup'">编辑</el-button>
v-if="!item.editStatus" <el-button type="danger" v-if="!item.editStatus" @click="deleteGroup(item.id, index)"
@click="item.editStatus = true" v-auth="'contractVariety_delGroup'">删除</el-button>
v-auth="'contractVariety_editGroup'" <el-button type="primary" v-if="item.editStatus" @click="submitItem(index)"
>编辑</el-button v-auth="'contractVariety_editGroup'">确认</el-button>
> <el-button type="danger" v-if="item.editStatus" @click="item.editStatus = false"
<el-button v-auth="'contractVariety_editGroup'">取消</el-button>
type="danger"
v-if="!item.editStatus"
@click="deleteGroup(item.id, index)"
v-auth="'contractVariety_delGroup'"
>删除</el-button
>
<el-button
type="primary"
v-if="item.editStatus"
@click="submitItem(index)"
v-auth="'contractVariety_editGroup'"
>确认</el-button
>
<el-button
type="danger"
v-if="item.editStatus"
@click="item.editStatus = false"
v-auth="'contractVariety_editGroup'"
>取消</el-button
>
</td> </td>
</tr> </tr>
<tr> <tr>
@ -114,23 +82,13 @@
<td class="time-picker-box"> <td class="time-picker-box">
<template v-if="item.editStatus"> <template v-if="item.editStatus">
<template v-if="item?.d_time_list"> <template v-if="item?.d_time_list">
<el-time-picker <el-time-picker v-for="(time, timeIndex) in item?.d_time_list" :key="timeIndex"
v-for="(time, timeIndex) in item?.d_time_list" v-model="item.d_time_list[timeIndex]" style="width: 180px; flex: 0 0 auto" format="HH:mm:ss"
:key="timeIndex" value-format="HH:mm:ss" :picker-options="{
v-model="item.d_time_list[timeIndex]"
style="width: 180px; flex: 0 0 auto"
format="HH:mm:ss"
value-format="HH:mm:ss"
:picker-options="{
selectableRange: '09:00:00 - 18:00:00', selectableRange: '09:00:00 - 18:00:00',
}" }" is-range />
is-range
/>
</template> </template>
<img <img src="@/assets/images/trade/group/add.png" @click="addTime(index, 'd_time_list')" />
src="@/assets/images/trade/group/add.png"
@click="addTime(index, 'd_time_list')"
/>
</template> </template>
<template v-else> <template v-else>
<template v-if="item?.d_time_list"> <template v-if="item?.d_time_list">
@ -148,7 +106,10 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { usePageLoading } from '@/composables/useLoading'
// loading
const { loading, startLoading, stopLoading } = usePageLoading()
defineOptions({ defineOptions({
name: 'Times' name: 'Times'
}) })
@ -246,6 +207,7 @@ const deleteGroup = (id: number, idx: number) => {
* @description: 定义公共请求数据方法 * @description: 定义公共请求数据方法
*/ */
const getGroupList = async () => { const getGroupList = async () => {
startLoading()
try { try {
const data = await getGroupListApi() const data = await getGroupListApi()
data.forEach((item: any) => { data.forEach((item: any) => {
@ -283,6 +245,8 @@ const getGroupList = async () => {
} catch (e: any) { } catch (e: any) {
console.log(e) console.log(e)
ElMessage.error(e.msg) ElMessage.error(e.msg)
} finally {
stopLoading()
} }
} }
@ -298,40 +262,50 @@ onMounted(() => {
.trade-group { .trade-group {
overflow-y: scroll; overflow-y: scroll;
} }
/* 给 table 及所有 td 加边框 */ /* 给 table 及所有 td 加边框 */
table { table {
border-collapse: collapse; border-collapse: collapse;
width: 100%; width: 100%;
table-layout: auto; /* 宽度自适应内容 */ table-layout: auto;
/* 宽度自适应内容 */
.time-picker-box { .time-picker-box {
display: flex; display: flex;
align-items: center; align-items: center;
flex-wrap: wrap; flex-wrap: wrap;
gap: 10px; gap: 10px;
img { img {
width: 20px; width: 20px;
height: 20px; height: 20px;
cursor: pointer; cursor: pointer;
} }
} }
td { td {
height: 59px; height: 59px;
border: 1px solid #dcdfe6; border: 1px solid #dcdfe6;
padding: 4px 8px; /* 内边距缩小 */ padding: 4px 8px;
/* 内边距缩小 */
text-align: center; text-align: center;
font-size: 12px; /* 文字变小 */ font-size: 12px;
white-space: nowrap; /* 文字不换行 */ /* 文字变小 */
vertical-align: middle; /* 垂直居中 */ white-space: nowrap;
/* 文字不换行 */
vertical-align: middle;
/* 垂直居中 */
/* 时间选择器容器:两个选择器横排不换行 */ /* 时间选择器容器:两个选择器横排不换行 */
:deep(.el-time-picker) { :deep(.el-time-picker) {
display: inline-block; display: inline-block;
.el-range-editor { .el-range-editor {
width: 100px; /* 缩小时间选择器宽度 */ width: 100px;
/* 缩小时间选择器宽度 */
font-size: 12px; font-size: 12px;
} }
.el-range-input { .el-range-input {
font-size: 12px; font-size: 12px;
} }

View File

@ -3,6 +3,7 @@
<!-- 搜索 --> <!-- 搜索 -->
<VarietySearch <VarietySearch
:search-form="searchForm" :search-form="searchForm"
:buttonLoading="loading"
:search-options="searchOptions" :search-options="searchOptions"
@search="handleSearch" @search="handleSearch"
@reset="handleReset" @reset="handleReset"
@ -28,6 +29,7 @@
:columns="varietyTableColumnsOptions" :columns="varietyTableColumnsOptions"
row-key="id" row-key="id"
@row-click="handleRowClick" @row-click="handleRowClick"
:loading="loading"
> >
<template #onlineStatus="{ row }"> <template #onlineStatus="{ row }">
{{ ['', '上线', '下线'][row.onlineStatus] }} {{ ['', '上线', '下线'][row.onlineStatus] }}
@ -42,6 +44,7 @@
:visible="dialogVisible" :visible="dialogVisible"
:title="dialogTitle" :title="dialogTitle"
:type="dialogType" :type="dialogType"
:button-loading="submitLoading"
@confirm="handleSubmit" @confirm="handleSubmit"
@cancel="handleCancel" @cancel="handleCancel"
@close="handleClose" @close="handleClose"
@ -94,6 +97,10 @@ defineOptions({
name: 'Variety' name: 'Variety'
}) })
// //
import { usePageLoading, useButtonLoading } from '@/composables/useLoading'
const { loading, startLoading, stopLoading } = usePageLoading()
const { buttonLoading: submitLoading, startButtonLoading: startSubmitLoading, stopButtonLoading: stopSubmitLoading } = useButtonLoading()
import VarietySearch from '@/components/common/Search.vue' import VarietySearch from '@/components/common/Search.vue'
import VarietyTable from '@/components/common/Table.vue' import VarietyTable from '@/components/common/Table.vue'
import VarietyDialog from '@/components/common/Dialog.vue' import VarietyDialog from '@/components/common/Dialog.vue'
@ -233,9 +240,11 @@ const typeMap: any = {
// //
const getVarietyList = async () => { const getVarietyList = async () => {
startLoading()
const params = { const params = {
name: searchForm.value.varietyName, name: searchForm.value.varietyName,
} }
try {
const data = await getVarietyListApi(params) const data = await getVarietyListApi(params)
console.log('data', data) console.log('data', data)
// //
@ -258,6 +267,9 @@ const getVarietyList = async () => {
type: typeMap[item.type], type: typeMap[item.type],
image: item.image, image: item.image,
})) }))
} finally {
stopLoading()
}
} }
// //
@ -434,6 +446,8 @@ const beforeUpload = (file: File) => {
* @description: 定义弹窗方法 * @description: 定义弹窗方法
*/ */
const handleSubmit = async () => { const handleSubmit = async () => {
startSubmitLoading()
try {
await varietyFormRef.value?.validate() await varietyFormRef.value?.validate()
console.log(varietyForm.value) console.log(varietyForm.value)
const params = { const params = {
@ -475,6 +489,9 @@ const handleSubmit = async () => {
await getVarietyList() await getVarietyList()
} }
dialogVisible.value = false dialogVisible.value = false
} finally {
stopSubmitLoading()
}
} }
const handleCancel = () => { const handleCancel = () => {
dialogVisible.value = false dialogVisible.value = false

View File

@ -33,9 +33,9 @@ export const tableColumns = [
// 交易分组 // 交易分组
{ prop: 'tradeGroup', label: '交易分组', align: 'center' as const }, { prop: 'tradeGroup', label: '交易分组', align: 'center' as const },
// 多头库存费 // 多头库存费
{ prop: 'longStockFee', label: '多头库存费', align: 'center' as const }, { prop: 'longStockFee', label: '多头库存费', align: 'center' as const, isNumber: true },
// 空头库存费 // 空头库存费
{ prop: 'shortStockFee', label: '空头库存费', align: 'center' as const }, { prop: 'shortStockFee', label: '空头库存费', align: 'center' as const, isNumber: true },
// 类型 // 类型
{ prop: 'type', label: '类型', align: 'center' as const }, { prop: 'type', label: '类型', align: 'center' as const },
// 止盈水平 // 止盈水平

View File

@ -52,7 +52,7 @@
</el-form-item> </el-form-item>
</Transition> </Transition>
<el-form-item class="form-submit"> <el-form-item class="form-submit">
<el-button type="primary" @click="handleLogin">登录</el-button> <el-button type="primary" @click="handleLogin" :loading="activeTab === 'password' && buttonLoading">登录</el-button>
</el-form-item> </el-form-item>
<router-link to="/user/register" class="to-register">没有账号去注册</router-link> <router-link to="/user/register" class="to-register">没有账号去注册</router-link>
</el-form> </el-form>
@ -74,6 +74,7 @@ import UserTabs from './components/Tabs.vue'
///store/ ///store/
import { useUserStore } from '@/stores/modules/user' import { useUserStore } from '@/stores/modules/user'
import { useButtonLoading } from '@/composables/useLoading'
import { setStorage, getStorage } from '@/utils/storage' import { setStorage, getStorage } from '@/utils/storage'
import { md5Encrypt } from '@/utils/crypto' import { md5Encrypt } from '@/utils/crypto'
@ -83,6 +84,9 @@ import { passwordRules, emailRules, phoneRules, codeRules } from './config/rules
const router = useRouter() const router = useRouter()
// loading
const { buttonLoading, startButtonLoading, stopButtonLoading } = useButtonLoading()
//userText //userText
const userText = ref({ const userText = ref({
backText: '', backText: '',
@ -179,6 +183,9 @@ const handleLogin = async () => {
else if (currentTab === 'password') { else if (currentTab === 'password') {
// + // +
await loginFormRef.value.validateField(['password', 'code']) await loginFormRef.value.validateField(['password', 'code'])
// loading
startButtonLoading()
try {
// device_no1 // device_no1
const deviceNo = generateTimestampWithRandomNum() const deviceNo = generateTimestampWithRandomNum()
// //
@ -192,6 +199,10 @@ const handleLogin = async () => {
} }
await userStore.login(loginParams) await userStore.login(loginParams)
setStorage('device_no', deviceNo) setStorage('device_no', deviceNo)
} finally {
// loading
stopButtonLoading()
}
} }
} catch (error) { } catch (error) {
console.error('表单校验失败:', error) console.error('表单校验失败:', error)

View File

@ -94,7 +94,7 @@
> >
</el-form-item> </el-form-item>
<el-form-item class="form-submit"> <el-form-item class="form-submit">
<el-button type="primary" class="form-btn" @click="handleRegister">注册</el-button> <el-button type="primary" class="form-btn" @click="handleRegister" :loading="buttonLoading">注册</el-button>
</el-form-item> </el-form-item>
<router-link to="/user/login" class="to-login">已有账号立即登录</router-link> <router-link to="/user/login" class="to-login">已有账号立即登录</router-link>
</el-form> </el-form>
@ -118,6 +118,7 @@ import UserCaptcha from './components/Captcha.vue'
import { useUserStore } from '@/stores/modules/user' import { useUserStore } from '@/stores/modules/user'
import { md5Encrypt } from '@/utils/crypto' import { md5Encrypt } from '@/utils/crypto'
import { sendCaptcha, initCaptchaState, clearCaptchaTimer } from '@/utils/sendCaptcha' import { sendCaptcha, initCaptchaState, clearCaptchaTimer } from '@/utils/sendCaptcha'
import { useButtonLoading } from '@/composables/useLoading'
// //
import { countryOptions } from '@/views/user/config/mock' import { countryOptions } from '@/views/user/config/mock'
@ -128,6 +129,9 @@ import { useRoute } from 'vue-router'
const userStore = useUserStore() const userStore = useUserStore()
const route = useRoute() const route = useRoute()
// loading
const { buttonLoading, startButtonLoading, stopButtonLoading } = useButtonLoading()
// UserText // UserText
const userText = ref({ const userText = ref({
backText: '', backText: '',
@ -215,7 +219,9 @@ const handleRegister = async () => {
try { try {
// //
await registerFormRef.value.validate() await registerFormRef.value.validate()
// loading
startButtonLoading()
try {
// //
const registerParams = { const registerParams = {
email: registerForm.value.email, email: registerForm.value.email,
@ -228,6 +234,10 @@ const handleRegister = async () => {
// //
await userStore.register(registerParams) await userStore.register(registerParams)
} finally {
// loading
stopButtonLoading()
}
} catch (error) { } catch (error) {
console.error('表单校验失败:', error) console.error('表单校验失败:', error)
} }
@ -239,7 +249,7 @@ onUnmounted(() => {
}) })
onMounted(() => { onMounted(() => {
registerForm.value.inviteCode = route.query.invite_code registerForm.value.inviteCode = route.query.invite_code || ''
}) })
</script> </script>