页签导航栏

This commit is contained in:
2026-05-21 14:22:51 +08:00
parent f0aa2625e9
commit 3f1ad80a8f
6 changed files with 154 additions and 99 deletions

View File

@ -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

@ -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

@ -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,8 @@
<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>
@ -14,12 +10,8 @@
<!-- 表格 --> <!-- 表格 -->
<FundDetailTable :table-data="tableTree" :columns="tableColumnsOptions" row-key="id" :loading="loading" /> <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>
@ -71,41 +63,44 @@ const total = ref(0)
// //
const getFundDetailList = async () => { const getFundDetailList = async () => {
startLoading() startLoading()
const params = { try {
page: currentPage.value, const params = {
page_size: pageSize.value, page: currentPage.value,
start_time: page_size: pageSize.value,
searchForm.value.startTime.length > 0 start_time:
? `${searchForm.value.startTime} 0:00:00` searchForm.value.startTime.length > 0
: '2026-01-01 0:00:00', ? `${searchForm.value.startTime} 0:00:00`
end_time: : '2026-01-01 0:00:00',
searchForm.value.endTime.length > 0 end_time:
? `${searchForm.value.endTime} 23:59:59` searchForm.value.endTime.length > 0
: '2026-03-15 23:59:59', ? `${searchForm.value.endTime} 23:59:59`
user_name: searchForm.value.username, : '2026-03-15 23:59:59',
account_id: searchForm.value.subAccountId, user_name: searchForm.value.username,
} account_id: searchForm.value.subAccountId,
// }
const data: any = await getFundDetailListApi(params) //
stopLoading() const data: any = await getFundDetailListApi(params)
console.log(data) console.log(data)
tableTree.value = data.data.map((item: any) => ({ tableTree.value = data.data.map((item: any) => ({
...item, ...item,
// //
username: item.user.username, username: item.user.username,
// //
orderNo: item.trade_id, orderNo: item.trade_id,
// //
type: item.enum_dic.value, type: item.enum_dic.value,
// //
createTime: utcToUtc8(item.created_at), createTime: utcToUtc8(item.created_at),
// //
walletType: item.wallet_type === '1' ? '主钱包' : '子钱包', walletType: item.wallet_type === '1' ? '主钱包' : '子钱包',
})) }))
// //
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

@ -2,7 +2,7 @@
<div class="table_form-container"> <div class="table_form-container">
<!-- 搜索 --> <!-- 搜索 -->
<CustomerTradeOrderSearch :search-form="searchForm" :search-options="searchOptions" @search="handleSearch" <CustomerTradeOrderSearch :search-form="searchForm" :search-options="searchOptions" @search="handleSearch"
@reset="handleReset" @blur-input="getSubAccountList"> @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" :loading="loading" /> <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"
@ -22,7 +23,7 @@
<script setup lang="ts"> <script setup lang="ts">
defineOptions({ defineOptions({
name: 'History' name: 'History'
}) })
// //
import CustomerTradeOrderSearch from '@/components/common/Search.vue' import CustomerTradeOrderSearch from '@/components/common/Search.vue'
@ -82,39 +83,43 @@ const total = ref(0)
* @description: 定义公共请求数据方法 * @description: 定义公共请求数据方法
*/ */
const getCustomerTradeOrderList = async () => { const getCustomerTradeOrderList = async () => {
startLoading() try {
const params = { startLoading()
page: currentPage.value, const params = {
page_size: pageSize.value, page: currentPage.value,
user_name: searchForm.value.accountName, page_size: pageSize.value,
account_id: searchForm.value.subAccountName, user_name: searchForm.value.accountName,
order_no: '', account_id: searchForm.value.subAccountName,
type: searchForm.value.tradeType, order_no: '',
start_time: dateRange.start, type: searchForm.value.tradeType,
end_time: dateRange.end, start_time: dateRange.start,
} end_time: dateRange.end,
console.log(params) }
const data: any = await getCustomTradeOrderListApi(params) console.log(params)
stopLoading() const data: any = await getCustomTradeOrderListApi(params)
// //
// table // table
tableTree.value = data.data.map((item: any) => ({ tableTree.value = data.data.map((item: any) => ({
accountName: item.user.username, accountName: item.user.username,
orderId: item.order_id, orderId: item.order_id,
variety: item.variety.name, variety: item.variety.name,
type: item.type === 1 ? '买入' : '卖出', type: item.type === 1 ? '买入' : '卖出',
quantity: item.num, quantity: item.num,
openPrice: item.build_price, openPrice: item.build_price,
closePrice: item.close_price, closePrice: item.close_price,
closeProfitLoss: item.profit_loss, closeProfitLoss: item.profit_loss,
amount: item.amount, amount: item.amount,
createdTime: item.created_at, createdTime: item.created_at,
})) }))
// //
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 () => {