admin模块接入数据库
This commit is contained in:
@@ -15,12 +15,12 @@
|
||||
|
||||
<view class="filter-item">
|
||||
<text class="label-txt">退款时间:</text>
|
||||
<view class="date-picker-mock">
|
||||
<text class="date-txt">开始日期</text>
|
||||
<text class="date-split">-</text>
|
||||
<text class="date-txt">结束日期</text>
|
||||
<text class="calendar-ic">📅</text>
|
||||
</view>
|
||||
<AnalyticsDateRangePicker
|
||||
:initialStartDate="startDate"
|
||||
:initialEndDate="endDate"
|
||||
@apply="onApplyRange"
|
||||
@clear="onClearRange"
|
||||
/>
|
||||
</view>
|
||||
|
||||
<view class="filter-item">
|
||||
@@ -103,6 +103,7 @@
|
||||
|
||||
<script setup lang="uts">
|
||||
import { ref, onMounted, computed } from 'vue'
|
||||
import AnalyticsDateRangePicker from '@/components/analytics/AnalyticsDateRangePicker.uvue'
|
||||
import { fetchRefundOrderPage } from '@/services/orderService.uts'
|
||||
|
||||
interface RefundOrder {
|
||||
@@ -127,6 +128,9 @@ const page = ref(1)
|
||||
const pageSize = ref(15)
|
||||
const jumpPage = ref('')
|
||||
|
||||
const startDate = ref('')
|
||||
const endDate = ref('')
|
||||
|
||||
const totalPages = computed((): number => {
|
||||
if (pageSize.value <= 0) return 1
|
||||
const pages = Math.ceil(total.value / pageSize.value)
|
||||
@@ -149,9 +153,24 @@ const onStatusChange = (e : any) => {
|
||||
handleQuery()
|
||||
}
|
||||
|
||||
function onApplyRange(payload : any) {
|
||||
startDate.value = payload?.start ?? ''
|
||||
endDate.value = payload?.end ?? ''
|
||||
handleQuery()
|
||||
}
|
||||
|
||||
function onClearRange() {
|
||||
startDate.value = ''
|
||||
endDate.value = ''
|
||||
handleQuery()
|
||||
}
|
||||
|
||||
const loadRefundOrders = async () => {
|
||||
loading.value = true
|
||||
try {
|
||||
const st = startDate.value ? (startDate.value + ' 00:00:00') : null
|
||||
const et = endDate.value ? (endDate.value + ' 23:59:59') : null
|
||||
|
||||
const res = await fetchRefundOrderPage(
|
||||
page.value,
|
||||
pageSize.value,
|
||||
@@ -159,7 +178,7 @@ const loadRefundOrders = async () => {
|
||||
searchQuery.value
|
||||
)
|
||||
|
||||
orderList.value = res.items.map((item: any): RefundOrder => {
|
||||
orderList.value = res.items.map((item : any) : RefundOrder => {
|
||||
const refundStatusCode = parseInt(String(item.refund_status ?? '0'))
|
||||
const orderStatusCode = parseInt(String(item.order_status ?? '0'))
|
||||
|
||||
|
||||
@@ -5,12 +5,12 @@
|
||||
<view class="filter-card border-shadow">
|
||||
<view class="filter-item">
|
||||
<text class="label-txt">创建时间:</text>
|
||||
<view class="date-picker-mock">
|
||||
<text class="date-txt">开始日期</text>
|
||||
<text class="date-split">-</text>
|
||||
<text class="date-txt">结束日期</text>
|
||||
<text class="calendar-ic">📅</text>
|
||||
</view>
|
||||
<AnalyticsDateRangePicker
|
||||
:initialStartDate="startDate"
|
||||
:initialEndDate="endDate"
|
||||
@apply="onApplyRange"
|
||||
@clear="onClearRange"
|
||||
/>
|
||||
</view>
|
||||
|
||||
<view class="filter-item">
|
||||
@@ -114,6 +114,7 @@
|
||||
|
||||
<script setup lang="uts">
|
||||
import { ref, onMounted, computed } from 'vue'
|
||||
import AnalyticsDateRangePicker from '@/components/analytics/AnalyticsDateRangePicker.uvue'
|
||||
import { fetchCashierOrderPage } from '@/services/orderService.uts'
|
||||
|
||||
interface CashierOrder {
|
||||
@@ -133,19 +134,28 @@ const page = ref(1)
|
||||
const pageSize = ref(15)
|
||||
const jumpPage = ref('')
|
||||
|
||||
const startDate = ref('')
|
||||
const endDate = ref('')
|
||||
|
||||
const totalPages = computed((): number => {
|
||||
if (pageSize.value <= 0) return 1
|
||||
return Math.ceil(total.value / pageSize.value)
|
||||
const pages = Math.ceil(total.value / pageSize.value)
|
||||
return pages <= 0 ? 1 : pages
|
||||
})
|
||||
|
||||
const loadCashierOrders = async () => {
|
||||
loading.value = true
|
||||
try {
|
||||
const st = startDate.value ? (startDate.value + ' 00:00:00') : null
|
||||
const et = endDate.value ? (endDate.value + ' 23:59:59') : null
|
||||
|
||||
const res = await fetchCashierOrderPage(
|
||||
page.value,
|
||||
pageSize.value,
|
||||
orderId.value ? orderId.value : null,
|
||||
username.value ? username.value : null
|
||||
username.value ? username.value : null,
|
||||
st,
|
||||
et
|
||||
)
|
||||
|
||||
orderList.value = res.items.map((item: any): CashierOrder => {
|
||||
@@ -174,6 +184,18 @@ const handleQuery = () => {
|
||||
loadCashierOrders()
|
||||
}
|
||||
|
||||
function onApplyRange(payload : any) {
|
||||
startDate.value = payload?.start ?? ''
|
||||
endDate.value = payload?.end ?? ''
|
||||
handleQuery()
|
||||
}
|
||||
|
||||
function onClearRange() {
|
||||
startDate.value = ''
|
||||
endDate.value = ''
|
||||
handleQuery()
|
||||
}
|
||||
|
||||
const prevPage = () => {
|
||||
if (page.value > 1) {
|
||||
page.value--
|
||||
|
||||
@@ -1,536 +1,42 @@
|
||||
<template>
|
||||
<view class="order-list-page">
|
||||
<!-- 筛选区域 -->
|
||||
<view class="filter-card">
|
||||
<view class="filter-row">
|
||||
<view class="filter-item">
|
||||
<text class="label">订单类型:</text>
|
||||
<view class="mock-select">
|
||||
<text>全部订单</text>
|
||||
<view class="arrow-down"></view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="filter-item">
|
||||
<text class="label">支付方式:</text>
|
||||
<view class="mock-select">
|
||||
<text>全部</text>
|
||||
<view class="arrow-down"></view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="filter-item long">
|
||||
<text class="label">创建时间:</text>
|
||||
<view class="mock-date-range">
|
||||
<image class="cal-icon" src="/static/icons/calendar.png" mode="aspectFit" />
|
||||
<text class="placeholder">开始日期 - 结束日期</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="filter-item search">
|
||||
<text class="label">订单搜索:</text>
|
||||
<view class="search-group">
|
||||
<view class="search-select">
|
||||
<text>全部</text>
|
||||
<view class="arrow-down"></view>
|
||||
</view>
|
||||
<input class="search-input" placeholder="请输入" v-model="searchText" @confirm="handleQuery" />
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="btn-row">
|
||||
<button class="btn btn-primary" @click="handleQuery">查询</button>
|
||||
<button class="btn btn-default" @click="handleReset">重置</button>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 列表数据区域 -->
|
||||
<view class="content-card">
|
||||
<!-- 状态 Tabs -->
|
||||
<view class="status-tabs">
|
||||
<view
|
||||
v-for="(tab, index) in statusTabs"
|
||||
:key="index"
|
||||
class="tab-item"
|
||||
:class="{ active: activeTab === index }"
|
||||
@click="handleTabChange(index)"
|
||||
>
|
||||
<text class="tab-text">{{ tab.name }}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 操作按钮 -->
|
||||
<view class="action-bar">
|
||||
<button class="action-btn btn-blue">订单核销</button>
|
||||
<button class="action-btn btn-outline">批量发货</button>
|
||||
<button class="action-btn btn-outline">批量删除</button>
|
||||
<button class="action-btn btn-outline">订单导出</button>
|
||||
</view>
|
||||
|
||||
<!-- 数据表格 -->
|
||||
<view class="order-table">
|
||||
<view class="thead">
|
||||
<view class="th col-check">
|
||||
<checkbox :checked="false" color="#1890ff" />
|
||||
</view>
|
||||
<view class="th col-order">订单号 | 类型</view>
|
||||
<view class="th col-product">商品信息</view>
|
||||
<view class="th col-user">用户信息</view>
|
||||
<view class="th col-price">实际支付</view>
|
||||
<view class="th col-pay">支付方式</view>
|
||||
<view class="th col-time">支付时间</view>
|
||||
<view class="th col-status">订单状态</view>
|
||||
<view class="th col-op">操作</view>
|
||||
</view>
|
||||
|
||||
<view class="tbody">
|
||||
<view v-if="loading" class="table-loading" style="padding: 40px; text-align: center;">
|
||||
<text>加载中...</text>
|
||||
</view>
|
||||
<view v-else-if="orderData.length === 0" class="table-empty" style="padding: 40px; text-align: center;">
|
||||
<text>暂无订单数据</text>
|
||||
</view>
|
||||
<view v-else v-for="(item, index) in orderData" :key="index" class="tr">
|
||||
<view class="td col-check">
|
||||
<checkbox :checked="false" color="#1890ff" />
|
||||
</view>
|
||||
<!-- 订单号|类型 -->
|
||||
<view class="td col-order">
|
||||
<text class="order-sn">{{ item.order_no }}</text>
|
||||
<text class="order-type blue">[{{ getChannelName(item.channel_type) }}]</text>
|
||||
</view>
|
||||
<!-- 商品信息 -->
|
||||
<view class="td col-product">
|
||||
<view class="product-info-wrap">
|
||||
<image class="p-img" :src="item.first_item_summary?.image_url" mode="aspectFill" />
|
||||
<text class="p-name">{{ item.first_item_summary?.product_name || '多商品订单' }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<!-- 用户信息 -->
|
||||
<view class="td col-user">
|
||||
<text class="u-info">{{ item.buyer_name }} | {{ item.buyer_phone }}</text>
|
||||
</view>
|
||||
<!-- 实际支付 -->
|
||||
<view class="td col-price">
|
||||
<text class="price-val">¥{{ item.total_amount }}</text>
|
||||
</view>
|
||||
<!-- 支付方式 -->
|
||||
<view class="td col-pay">
|
||||
<text class="pay-text">{{ getPayTypeName(item.pay_type) }}</text>
|
||||
</view>
|
||||
<!-- 支付时间 -->
|
||||
<view class="td col-time">
|
||||
<text class="time-text">{{ item.paid_at || '-' }}</text>
|
||||
</view>
|
||||
<!-- 订单状态 -->
|
||||
<view class="td col-status">
|
||||
<text class="status-text">{{ getStatusName(item.order_status) }}</text>
|
||||
</view>
|
||||
<!-- 操作 -->
|
||||
<view class="td col-op">
|
||||
<view class="op-links">
|
||||
<text class="op-link primary">详情</text>
|
||||
<view class="op-link-more">
|
||||
<text class="more-text">更多</text>
|
||||
<view class="arrow-down-blue"></view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<!-- 分页区域 -->
|
||||
<view class="pagination-row" style="padding: 16px 20px;">
|
||||
<text class="total-text">共 {{ total }} 条</text>
|
||||
<view class="page-btns">
|
||||
<view class="page-btn" :class="{ disabled: page <= 1 }" @click="prevPage"><text>‹</text></view>
|
||||
<view class="page-btn active"><text>{{ page }}</text></view>
|
||||
<view class="page-btn" :class="{ disabled: page >= totalPages }" @click="nextPage"><text>›</text></view>
|
||||
</view>
|
||||
<view class="page-jump">
|
||||
<text>前往</text>
|
||||
<input class="jump-input" v-model="jumpPage" type="number" @confirm="goToJumpPage" />
|
||||
<text>页</text>
|
||||
</view>
|
||||
<view class="page-container">
|
||||
<view class="page-content">
|
||||
<view class="placeholder-card">
|
||||
<text class="placeholder-title">正在跳转...</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang="uts">
|
||||
import { ref, onMounted, computed } from 'vue'
|
||||
import { fetchOrderPage } from '@/services/orderService.uts'
|
||||
|
||||
const activeTab = ref(0)
|
||||
const searchText = ref('')
|
||||
const total = ref(0)
|
||||
const orderData = ref<any[]>([])
|
||||
const loading = ref(false)
|
||||
const page = ref(1)
|
||||
const pageSize = ref(15)
|
||||
const jumpPage = ref('')
|
||||
|
||||
const statusTabs = [
|
||||
{ name: '全部', value: null as number | null },
|
||||
{ name: '待付款', value: 1 as number | null },
|
||||
{ name: '待发货', value: 2 as number | null },
|
||||
{ name: '待收货', value: 3 as number | null },
|
||||
{ name: '已完成', value: 4 as number | null },
|
||||
{ name: '已取消', value: 5 as number | null },
|
||||
{ name: '退款中', value: 6 as number | null },
|
||||
{ name: '已退款', value: 7 as number | null }
|
||||
]
|
||||
|
||||
const totalPages = computed((): number => {
|
||||
if (pageSize.value <= 0) return 1
|
||||
return Math.ceil(total.value / pageSize.value)
|
||||
})
|
||||
|
||||
const loadOrders = async () => {
|
||||
loading.value = true
|
||||
try {
|
||||
const res = await fetchOrderPage(
|
||||
page.value,
|
||||
pageSize.value,
|
||||
statusTabs[activeTab.value].value,
|
||||
searchText.value || null
|
||||
)
|
||||
|
||||
orderData.value = res.items
|
||||
total.value = res.total
|
||||
} catch (e) {
|
||||
uni.showToast({ title: '加载订单失败', icon: 'none' })
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
import { onMounted } from 'vue'
|
||||
import { openRoute } from '@/layouts/admin/store/adminNavStore.uts'
|
||||
|
||||
onMounted(() => {
|
||||
loadOrders()
|
||||
// 跳转到最新的订单管理页面
|
||||
openRoute('OrderList')
|
||||
})
|
||||
|
||||
const handleQuery = () => {
|
||||
page.value = 1
|
||||
loadOrders()
|
||||
}
|
||||
|
||||
const handleReset = () => {
|
||||
searchText.value = ''
|
||||
page.value = 1
|
||||
loadOrders()
|
||||
}
|
||||
|
||||
const handleTabChange = (index: number) => {
|
||||
activeTab.value = index
|
||||
page.value = 1
|
||||
loadOrders()
|
||||
}
|
||||
|
||||
const prevPage = () => {
|
||||
if (page.value > 1) {
|
||||
page.value--
|
||||
loadOrders()
|
||||
}
|
||||
}
|
||||
|
||||
const nextPage = () => {
|
||||
if (page.value < totalPages.value) {
|
||||
page.value++
|
||||
loadOrders()
|
||||
}
|
||||
}
|
||||
|
||||
const goToJumpPage = () => {
|
||||
const targetPage = parseInt(jumpPage.value)
|
||||
if (!isNaN(targetPage) && targetPage >= 1 && targetPage <= totalPages.value) {
|
||||
page.value = targetPage
|
||||
loadOrders()
|
||||
jumpPage.value = ''
|
||||
} else {
|
||||
uni.showToast({ title: '页码无效', icon: 'none' })
|
||||
}
|
||||
}
|
||||
|
||||
function getStatusName(status: number): string {
|
||||
const tab = statusTabs.find(t => t.value === status)
|
||||
return tab?.name ?? '未知'
|
||||
}
|
||||
|
||||
function getPayTypeName(type: number | null): string {
|
||||
switch (type) {
|
||||
case 1: return '余额支付'
|
||||
case 2: return '微信支付'
|
||||
case 3: return '支付宝'
|
||||
case 4: return '线下支付'
|
||||
default: return '其他'
|
||||
}
|
||||
}
|
||||
|
||||
function getChannelName(type: number | null): string {
|
||||
switch (type) {
|
||||
case 1: return '公众号'
|
||||
case 2: return '小程序'
|
||||
case 3: return 'H5'
|
||||
case 4: return 'PC'
|
||||
case 5: return 'APP'
|
||||
default: return '普通订单'
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.order-list-page {
|
||||
padding: 16px;
|
||||
background-color: #f0f2f5;
|
||||
.page-container {
|
||||
padding: 20px;
|
||||
min-height: 100vh;
|
||||
background: #f5f5f5;
|
||||
}
|
||||
|
||||
.filter-card {
|
||||
background-color: #fff;
|
||||
.page-content {
|
||||
background: #fff;
|
||||
border-radius: 4px;
|
||||
padding: 24px;
|
||||
border-radius: 4px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.filter-row {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
gap: 24px;
|
||||
.placeholder-card {
|
||||
text-align: center;
|
||||
padding: 60px 20px;
|
||||
}
|
||||
|
||||
.filter-item {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
|
||||
.label {
|
||||
font-size: 14px;
|
||||
color: #333;
|
||||
width: 70px;
|
||||
}
|
||||
}
|
||||
|
||||
.mock-select {
|
||||
width: 160px;
|
||||
height: 32px;
|
||||
border: 1px solid #d9d9d9;
|
||||
border-radius: 4px;
|
||||
padding: 0 12px;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
text { font-size: 14px; color: #595959; }
|
||||
}
|
||||
|
||||
.mock-date-range {
|
||||
width: 240px;
|
||||
height: 32px;
|
||||
border: 1px solid #d9d9d9;
|
||||
border-radius: 4px;
|
||||
padding: 0 12px;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
.cal-icon { width: 14px; height: 14px; margin-right: 8px; opacity: 0.4; }
|
||||
.placeholder { font-size: 14px; color: #bfbfbf; }
|
||||
}
|
||||
|
||||
.search-group {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
border: 1px solid #d9d9d9;
|
||||
border-radius: 4px;
|
||||
height: 32px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.search-select {
|
||||
width: 80px;
|
||||
border-right: 1px solid #d9d9d9;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 4px;
|
||||
background-color: #fafafa;
|
||||
text { font-size: 14px; color: #595959; }
|
||||
}
|
||||
|
||||
.search-input {
|
||||
flex: 1;
|
||||
border: none;
|
||||
padding: 0 12px;
|
||||
font-size: 14px;
|
||||
width: 120px;
|
||||
}
|
||||
|
||||
.arrow-down {
|
||||
width: 0; height: 0;
|
||||
border-left: 4px solid transparent;
|
||||
border-right: 4px solid transparent;
|
||||
border-top: 5px solid #bfbfbf;
|
||||
}
|
||||
|
||||
.btn-row {
|
||||
margin-top: 16px;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.btn {
|
||||
height: 32px;
|
||||
padding: 0 16px;
|
||||
font-size: 14px;
|
||||
border-radius: 4px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.btn-primary { background-color: #1890ff; color: #fff; border: none; }
|
||||
.btn-default { background-color: #fff; color: #595959; border: 1px solid #d9d9d9; }
|
||||
|
||||
.content-card {
|
||||
background-color: #fff;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.status-tabs {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
padding: 0 16px;
|
||||
}
|
||||
|
||||
.tab-item {
|
||||
padding: 16px 20px;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: 2px;
|
||||
|
||||
.tab-text { font-size: 14px; color: #595959; }
|
||||
.tab-count { font-size: 14px; color: #595959; }
|
||||
|
||||
&.active {
|
||||
.tab-text, .tab-count { color: #1890ff; font-weight: 500; }
|
||||
&::after {
|
||||
content: '';
|
||||
position: absolute; bottom: 0; left: 0; right: 0; height: 2px; background: #1890ff;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.action-bar {
|
||||
padding: 16px 20px;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.action-btn {
|
||||
height: 32px;
|
||||
padding: 0 16px;
|
||||
font-size: 14px;
|
||||
border-radius: 4px;
|
||||
margin: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.btn-blue { background-color: #1890ff; color: #fff; border: none; }
|
||||
.btn-outline { background-color: #fff; color: #595959; border: 1px solid #d9d9d9; }
|
||||
|
||||
/* 表格样式 */
|
||||
.order-table {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.thead {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
background-color: #f0f7ff;
|
||||
}
|
||||
|
||||
.th {
|
||||
padding: 12px 8px;
|
||||
font-size: 14px;
|
||||
color: #595959;
|
||||
font-weight: 500;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.tr {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
&:hover { background-color: #fafafa; }
|
||||
}
|
||||
|
||||
.td {
|
||||
padding: 16px 8px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
/* 列宽控制 */
|
||||
.col-check { width: 50px; justify-content: center; align-items: center; }
|
||||
.col-order { width: 220px; }
|
||||
.col-product { flex: 1; }
|
||||
.col-user { width: 160px; }
|
||||
.col-price { width: 100px; }
|
||||
.col-pay { width: 100px; }
|
||||
.col-time { width: 160px; }
|
||||
.col-status { width: 100px; }
|
||||
.col-op { width: 120px; }
|
||||
|
||||
/* 单元格具体内容样式 */
|
||||
.order-sn { font-size: 13px; color: #262626; margin-bottom: 4px; }
|
||||
.order-type { font-size: 12px; }
|
||||
.blue { color: #1890ff; }
|
||||
.purple { color: #722ed1; }
|
||||
.green { color: #52c41a; }
|
||||
.cancel-text { font-size: 12px; color: #ff4d4f; margin-top: 4px; }
|
||||
|
||||
.product-info-wrap {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: flex-start;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.p-img { width: 44px; height: 44px; border-radius: 4px; background-color: #f5f5f5; flex-shrink: 0; }
|
||||
.p-name { font-size: 13px; color: #595959; line-height: 1.5; }
|
||||
|
||||
.u-info { font-size: 13px; color: #595959; }
|
||||
.price-val { font-size: 14px; color: #262626; }
|
||||
.pay-text, .time-text, .status-text { font-size: 13px; color: #595959; }
|
||||
|
||||
.op-links {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.op-link { font-size: 13px; cursor: pointer; }
|
||||
.primary { color: #1890ff; }
|
||||
|
||||
.op-link-more {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
}
|
||||
.more-text { font-size: 13px; color: #1890ff; }
|
||||
.arrow-down-blue {
|
||||
width: 0; height: 0;
|
||||
border-left: 3px solid transparent;
|
||||
border-right: 3px solid transparent;
|
||||
border-top: 4px solid #1890ff;
|
||||
.placeholder-title {
|
||||
display: block;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: #666;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,25 +1,414 @@
|
||||
<template>
|
||||
<AdminLayout :currentPage="currentPage">
|
||||
<view class="page">
|
||||
<view class="header">
|
||||
<text class="title">{{ title }}</text>
|
||||
<text class="sub-title">页面已修复 (UTF-8)</text>
|
||||
<view class="order-management-page">
|
||||
<!-- 1. 顶部筛选卡片 -->
|
||||
<view class="filter-card">
|
||||
<view class="filter-row">
|
||||
<view class="filter-item">
|
||||
<text class="label">订单号:</text>
|
||||
<input class="filter-input" v-model="searchOrderNo" placeholder="请输入订单号" @confirm="onSearch" />
|
||||
</view>
|
||||
<view class="filter-item">
|
||||
<text class="label">用户信息:</text>
|
||||
<input class="filter-input" v-model="searchUser" placeholder="姓名/手机号" @confirm="onSearch" />
|
||||
</view>
|
||||
<view class="filter-item">
|
||||
<text class="label">下单时间:</text>
|
||||
<AnalyticsDateRangePicker
|
||||
:initialStartDate="startDate"
|
||||
:initialEndDate="endDate"
|
||||
@apply="onApplyRange"
|
||||
@clear="onClearRange"
|
||||
/>
|
||||
</view>
|
||||
<view class="filter-btns">
|
||||
<button class="btn primary" @click="onSearch">查询</button>
|
||||
<button class="btn reset" @click="onReset">重置</button>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 2. 状态选项卡 -->
|
||||
<view class="status-tabs-card">
|
||||
<view class="status-tabs">
|
||||
<view
|
||||
v-for="tab in statusTabs"
|
||||
:key="tab.value"
|
||||
class="tab-item"
|
||||
:class="{ active: activeStatus === tab.value }"
|
||||
@click="onStatusChange(tab.value)"
|
||||
>
|
||||
<text class="tab-text">{{ tab.label }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 3. 订单列表表格 -->
|
||||
<view class="content-card">
|
||||
<view class="table-container">
|
||||
<!-- Loading 遮罩 -->
|
||||
<view v-if="isLoading" class="loading-mask">
|
||||
<text class="loading-text">订单数据加载中...</text>
|
||||
</view>
|
||||
|
||||
<view class="table-header">
|
||||
<view class="col col-no"><text>订单号</text></view>
|
||||
<view class="col col-user"><text>用户信息</text></view>
|
||||
<view class="col col-amount"><text>支付金额</text></view>
|
||||
<view class="col col-status"><text>订单状态</text></view>
|
||||
<view class="col col-time"><text>下单时间</text></view>
|
||||
<view class="col col-ops"><text>操作</text></view>
|
||||
</view>
|
||||
|
||||
<view class="table-body">
|
||||
<view v-if="orderList.length === 0 && !isLoading" class="empty-row">
|
||||
<text>未找到相关订单</text>
|
||||
</view>
|
||||
<view v-for="item in orderList" :key="item.id" class="table-row">
|
||||
<view class="col col-no">
|
||||
<text class="order-no">{{ item.order_no }}</text>
|
||||
</view>
|
||||
<view class="col col-user">
|
||||
<view class="user-info-box">
|
||||
<text class="u-name">{{ item.username || '匿名用户' }}</text>
|
||||
<text class="u-phone">{{ item.phone || '-' }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="col col-amount">
|
||||
<text class="amount-val">¥{{ item.paid_amount?.toFixed(2) || '0.00' }}</text>
|
||||
</view>
|
||||
<view class="col col-status">
|
||||
<text class="status-tag" :class="getStatusClass(item.order_status)">
|
||||
{{ getStatusLabel(item.order_status) }}
|
||||
</text>
|
||||
</view>
|
||||
<view class="col col-time">
|
||||
<text class="time-text">{{ formatDateTime(item.created_at) }}</text>
|
||||
</view>
|
||||
<view class="col col-ops">
|
||||
<text class="op-link" @click="goDetail(item.id)">详情</text>
|
||||
<text class="op-divider">|</text>
|
||||
<text class="op-link">备注</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 4. 分页控制 -->
|
||||
<view class="pagination">
|
||||
<view class="pager-btns">
|
||||
<button class="btn small" :disabled="page <= 1" @click="onPrevPage">上一页</button>
|
||||
<text class="page-num">第 {{ page }} 页</text>
|
||||
<button class="btn small" :disabled="orderList.length < pageSize" @click="onNextPage">下一页</button>
|
||||
</view>
|
||||
<text class="page-info">当前页 {{ orderList.length }} 条 / 总计 {{ total }} 条</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</AdminLayout>
|
||||
</template>
|
||||
|
||||
<script setup lang="uts">
|
||||
import { ref } from 'vue'
|
||||
import { ref, onMounted } from 'vue'
|
||||
import AdminLayout from '@/layouts/admin/AdminLayout.uvue'
|
||||
const currentPage = ref<string>('order-list')
|
||||
const title = ref<string>('index')
|
||||
import AnalyticsDateRangePicker from '@/components/analytics/AnalyticsDateRangePicker.uvue'
|
||||
import { fetchOrderPage } from '@/services/orderService.uts'
|
||||
import AkReq from '@/uni_modules/ak-req/ak-req.uts'
|
||||
|
||||
const currentPage = ref<string>('OrderList')
|
||||
|
||||
// --- 数据状态 ---
|
||||
const orderList = ref<any[]>([])
|
||||
const total = ref(0)
|
||||
const isLoading = ref(false)
|
||||
|
||||
// --- 筛选条件 ---
|
||||
const searchOrderNo = ref('')
|
||||
const searchUser = ref('')
|
||||
const activeStatus = ref<number | null>(null)
|
||||
const startDate = ref('')
|
||||
const endDate = ref('')
|
||||
|
||||
const page = ref(1)
|
||||
const pageSize = 20
|
||||
|
||||
const statusTabs = [
|
||||
{ label: '全部', value: null },
|
||||
{ label: '待付款', value: 1 },
|
||||
{ label: '待发货', value: 2 },
|
||||
{ label: '待收货', value: 3 },
|
||||
{ label: '已完成', value: 4 },
|
||||
{ label: '已取消', value: 5 },
|
||||
{ label: '退款中', value: 6 },
|
||||
{ label: '已退款', value: 7 }
|
||||
]
|
||||
|
||||
onMounted(() => {
|
||||
if (AkReq.getToken() == null || AkReq.getToken() === '') return
|
||||
loadOrders()
|
||||
})
|
||||
|
||||
async function loadOrders() {
|
||||
isLoading.value = true
|
||||
try {
|
||||
const st = startDate.value ? (startDate.value + ' 00:00:00') : null
|
||||
const et = endDate.value ? (endDate.value + ' 23:59:59') : null
|
||||
|
||||
// 合并搜索词
|
||||
const combinedSearch = (searchOrderNo.value || searchUser.value)
|
||||
? `${searchOrderNo.value} ${searchUser.value}`.trim()
|
||||
: null
|
||||
|
||||
const res = await fetchOrderPage(
|
||||
page.value,
|
||||
pageSize,
|
||||
activeStatus.value,
|
||||
combinedSearch,
|
||||
st,
|
||||
et
|
||||
)
|
||||
|
||||
orderList.value = res.items
|
||||
total.value = res.total
|
||||
} catch (e) {
|
||||
uni.showToast({ title: '加载订单失败', icon: 'none' })
|
||||
} finally {
|
||||
isLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// --- 事件处理 ---
|
||||
|
||||
function onSearch() {
|
||||
page.value = 1
|
||||
loadOrders()
|
||||
}
|
||||
|
||||
function onReset() {
|
||||
searchOrderNo.value = ''
|
||||
searchUser.value = ''
|
||||
activeStatus.value = null
|
||||
startDate.value = ''
|
||||
endDate.value = ''
|
||||
page.value = 1
|
||||
loadOrders()
|
||||
}
|
||||
|
||||
function onStatusChange(status: number | null) {
|
||||
activeStatus.value = status
|
||||
page.value = 1
|
||||
loadOrders()
|
||||
}
|
||||
|
||||
function onApplyRange(payload: any) {
|
||||
startDate.value = payload?.start ?? ''
|
||||
endDate.value = payload?.end ?? ''
|
||||
page.value = 1
|
||||
loadOrders()
|
||||
}
|
||||
|
||||
function onClearRange() {
|
||||
startDate.value = ''
|
||||
endDate.value = ''
|
||||
page.value = 1
|
||||
loadOrders()
|
||||
}
|
||||
|
||||
function onPrevPage() {
|
||||
if (page.value <= 1) return
|
||||
page.value--
|
||||
loadOrders()
|
||||
}
|
||||
|
||||
function onNextPage() {
|
||||
if (orderList.value.length < pageSize) return
|
||||
page.value++
|
||||
loadOrders()
|
||||
}
|
||||
|
||||
// --- 辅助方法 ---
|
||||
|
||||
function getStatusLabel(status: number): string {
|
||||
const found = statusTabs.find(t => t.value === status)
|
||||
return found ? found.label : '未知'
|
||||
}
|
||||
|
||||
function getStatusClass(status: number): string {
|
||||
switch (status) {
|
||||
case 1: return 'status-pending'
|
||||
case 2: return 'status-shipping'
|
||||
case 3: return 'status-delivery'
|
||||
case 4: return 'status-completed'
|
||||
case 5: return 'status-cancelled'
|
||||
case 6: case 7: return 'status-refund'
|
||||
default: return ''
|
||||
}
|
||||
}
|
||||
|
||||
function formatDateTime(iso: string | null): string {
|
||||
if (!iso) return '-'
|
||||
return iso.replace('T', ' ').split('.')[0]
|
||||
}
|
||||
|
||||
function goDetail(id: string) {
|
||||
uni.showToast({ title: '详情页开发中', icon: 'none' })
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import '@/uni.scss';
|
||||
.page { padding: $space-lg; }
|
||||
.header { padding: $space-lg; border-radius: $radius; background: $background-primary; box-shadow: $shadow-xs; }
|
||||
.title { font-size: $font-size-lg; font-weight: $font-weight-bold; color: $text-primary; }
|
||||
.sub-title { margin-top: $space-xs; font-size: $font-size-md; color: $text-secondary; }
|
||||
.order-management-page {
|
||||
padding: 20px;
|
||||
background-color: #f5f7f9;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.filter-card {
|
||||
background: #fff;
|
||||
padding: 24px;
|
||||
border-radius: 4px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.filter-row {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.filter-item {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
.label { font-size: 14px; color: #333; margin-right: 8px; }
|
||||
}
|
||||
|
||||
.filter-input {
|
||||
border: 1px solid #d9d9d9;
|
||||
border-radius: 4px;
|
||||
height: 32px;
|
||||
width: 180px;
|
||||
padding: 0 12px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.filter-btns {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.status-tabs-card {
|
||||
background: #fff;
|
||||
margin-bottom: 16px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.status-tabs {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
padding: 0 24px;
|
||||
}
|
||||
|
||||
.tab-item {
|
||||
padding: 16px 20px;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
.tab-text { font-size: 14px; color: #666; }
|
||||
&.active {
|
||||
.tab-text { color: #1890ff; font-weight: bold; }
|
||||
&::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
bottom: 0; left: 0; right: 0;
|
||||
height: 2px; background: #1890ff;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.content-card {
|
||||
background: #fff;
|
||||
border-radius: 4px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.table-container {
|
||||
padding: 24px;
|
||||
min-height: 400px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.table-header {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
background-color: #f8faff;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
padding: 12px 0;
|
||||
}
|
||||
|
||||
.table-row {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
padding: 16px 0;
|
||||
align-items: center;
|
||||
&:hover { background: #fafafa; }
|
||||
}
|
||||
|
||||
.col { padding: 0 12px; font-size: 14px; color: #333; display: flex; align-items: center; }
|
||||
.col-no { width: 200px; }
|
||||
.col-user { flex: 1; }
|
||||
.col-amount { width: 120px; justify-content: center; }
|
||||
.col-status { width: 120px; justify-content: center; }
|
||||
.col-time { width: 180px; justify-content: center; }
|
||||
.col-ops { width: 150px; justify-content: flex-end; }
|
||||
|
||||
.order-no { font-family: monospace; color: #1890ff; }
|
||||
.user-info-box { display: flex; flex-direction: column; gap: 4px; }
|
||||
.u-name { font-weight: 500; }
|
||||
.u-phone { font-size: 12px; color: #999; }
|
||||
.amount-val { color: #f5222d; font-weight: bold; }
|
||||
|
||||
.status-tag {
|
||||
padding: 2px 10px; border-radius: 4px; font-size: 12px;
|
||||
&.status-pending { background: #fff7e6; color: #faad14; border: 1px solid #ffe58f; }
|
||||
&.status-shipping { background: #e6f7ff; color: #1890ff; border: 1px solid #bae7ff; }
|
||||
&.status-completed { background: #f6ffed; color: #52c41a; border: 1px solid #b7eb8f; }
|
||||
&.status-cancelled { background: #f5f5f5; color: #999; border: 1px solid #d9d9d9; }
|
||||
&.status-refund { background: #fff1f0; color: #f5222d; border: 1px solid #ffa39e; }
|
||||
}
|
||||
|
||||
.op-link { color: #1890ff; cursor: pointer; }
|
||||
.op-divider { margin: 0 8px; color: #eee; }
|
||||
|
||||
.pagination {
|
||||
padding: 16px 24px;
|
||||
border-top: 1px solid #f0f0f0;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.pager-btns { display: flex; flex-direction: row; align-items: center; gap: 12px; }
|
||||
.page-num { font-size: 14px; color: #333; }
|
||||
.page-info { font-size: 14px; color: #999; }
|
||||
|
||||
.loading-mask {
|
||||
position: absolute;
|
||||
top: 0; left: 0; right: 0; bottom: 0;
|
||||
background: rgba(255, 255, 255, 0.7);
|
||||
display: flex; align-items: center; justify-content: center;
|
||||
z-index: 10;
|
||||
}
|
||||
.empty-row { padding: 60px 0; text-align: center; color: #999; }
|
||||
|
||||
.btn {
|
||||
height: 32px; padding: 0 16px; font-size: 14px; border-radius: 4px; cursor: pointer;
|
||||
&.primary { background: #1890ff; color: #fff; border: none; }
|
||||
&.reset { background: #fff; color: #666; border: 1px solid #d9d9d9; }
|
||||
&.small { height: 28px; padding: 0 12px; font-size: 13px; }
|
||||
&[disabled] { opacity: 0.5; cursor: not-allowed; }
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -5,10 +5,12 @@
|
||||
<view class="filter-card">
|
||||
<view class="filter-item">
|
||||
<text class="filter-label">时间选择:</text>
|
||||
<view class="date-picker-mock">
|
||||
<image class="calendar-icon" src="/static/icons/calendar.png" mode="aspectFit" />
|
||||
<text class="date-range">2026/01/04 - 2026/02/02</text>
|
||||
</view>
|
||||
<AnalyticsDateRangePicker
|
||||
:initialStartDate="startDate"
|
||||
:initialEndDate="endDate"
|
||||
@apply="onApplyRange"
|
||||
@clear="onClearRange"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
@@ -123,7 +125,9 @@
|
||||
import { ref, onMounted } from 'vue'
|
||||
import AdminLayout from '@/layouts/admin/AdminLayout.uvue'
|
||||
import EChartsView from '@/uni_modules/charts/EChartsView.vue'
|
||||
import AnalyticsDateRangePicker from '@/components/analytics/AnalyticsDateRangePicker.uvue'
|
||||
import { fetchOrderStats, fetchOrderTrend, fetchOrderSourceStats, fetchOrderTypeStats } from '@/services/orderService.uts'
|
||||
import AkReq from '@/uni_modules/ak-req/ak-req.uts'
|
||||
|
||||
const currentPage = ref<string>('order_statistic')
|
||||
|
||||
@@ -132,32 +136,57 @@ const trendOption = ref<any>({})
|
||||
const sourceOption = ref<any>({})
|
||||
const orderStats = ref<any>(null)
|
||||
|
||||
const startDate = ref<string>('')
|
||||
const endDate = ref<string>('')
|
||||
|
||||
const orderTypeData = ref([
|
||||
{ name: '普通订单', amount: '0.00', rate: '0.00' }
|
||||
])
|
||||
|
||||
onMounted(() => {
|
||||
if (AkReq.getToken() == null || AkReq.getToken() === '') {
|
||||
return
|
||||
}
|
||||
|
||||
// 默认最近 30 天
|
||||
const end = new Date()
|
||||
const start = new Date(Date.now() - 30 * 24 * 60 * 60 * 1000)
|
||||
startDate.value = start.toISOString().substring(0, 10)
|
||||
endDate.value = end.toISOString().substring(0, 10)
|
||||
|
||||
loadAllData()
|
||||
})
|
||||
|
||||
function onApplyRange(payload: any) {
|
||||
startDate.value = payload?.start ?? ''
|
||||
endDate.value = payload?.end ?? ''
|
||||
loadAllData()
|
||||
}
|
||||
|
||||
function onClearRange() {
|
||||
startDate.value = ''
|
||||
endDate.value = ''
|
||||
loadAllData()
|
||||
}
|
||||
|
||||
async function loadAllData() {
|
||||
const endTime = new Date().toISOString()
|
||||
const startTime = new Date(Date.now() - 30 * 24 * 60 * 60 * 1000).toISOString()
|
||||
const st = startDate.value ? (startDate.value + ' 00:00:00') : new Date(Date.now() - 30 * 24 * 60 * 60 * 1000).toISOString()
|
||||
const et = endDate.value ? (endDate.value + ' 23:59:59') : new Date().toISOString()
|
||||
|
||||
try {
|
||||
// 1. 加载汇总数据
|
||||
orderStats.value = await fetchOrderStats(startTime, endTime)
|
||||
orderStats.value = await fetchOrderStats(st, et)
|
||||
|
||||
// 2. 加载趋势数据
|
||||
const trendData = await fetchOrderTrend(startTime, endTime)
|
||||
const trendData = await fetchOrderTrend(st, et)
|
||||
initTrendChart(trendData)
|
||||
|
||||
// 3. 加载来源数据
|
||||
const sourceData = await fetchOrderSourceStats(startTime, endTime)
|
||||
const sourceData = await fetchOrderSourceStats(st, et)
|
||||
initSourceChart(sourceData)
|
||||
|
||||
// 4. 加载订单类型数据
|
||||
const typeData = await fetchOrderTypeStats(startTime, endTime)
|
||||
const typeData = await fetchOrderTypeStats(st, et)
|
||||
orderTypeData.value = typeData
|
||||
} catch (e) {
|
||||
uni.showToast({ title: '加载统计数据失败', icon: 'none' })
|
||||
|
||||
@@ -5,12 +5,12 @@
|
||||
<view class="filter-card border-shadow">
|
||||
<view class="filter-item">
|
||||
<text class="label-txt">核销日期:</text>
|
||||
<view class="date-picker-mock">
|
||||
<text class="date-txt">开始日期</text>
|
||||
<text class="date-split">-</text>
|
||||
<text class="date-txt">结束日期</text>
|
||||
<text class="calendar-ic">📅</text>
|
||||
</view>
|
||||
<AnalyticsDateRangePicker
|
||||
:initialStartDate="startDate"
|
||||
:initialEndDate="endDate"
|
||||
@apply="onApplyRange"
|
||||
@clear="onClearRange"
|
||||
/>
|
||||
</view>
|
||||
|
||||
<view class="filter-item">
|
||||
@@ -104,6 +104,7 @@
|
||||
|
||||
<script setup lang="uts">
|
||||
import { ref, onMounted, computed } from 'vue'
|
||||
import AnalyticsDateRangePicker from '@/components/analytics/AnalyticsDateRangePicker.uvue'
|
||||
import { fetchWriteOffRecordPage } from '@/services/orderService.uts'
|
||||
|
||||
interface WriteOffRecord {
|
||||
@@ -127,6 +128,9 @@ const page = ref(1)
|
||||
const pageSize = ref(15)
|
||||
const jumpPage = ref('')
|
||||
|
||||
const startDate = ref('')
|
||||
const endDate = ref('')
|
||||
|
||||
const totalPages = computed((): number => {
|
||||
if (pageSize.value <= 0) return 1
|
||||
return Math.ceil(total.value / pageSize.value)
|
||||
@@ -135,10 +139,15 @@ const totalPages = computed((): number => {
|
||||
const loadRecords = async () => {
|
||||
loading.value = true
|
||||
try {
|
||||
const st = startDate.value ? (startDate.value + ' 00:00:00') : null
|
||||
const et = endDate.value ? (endDate.value + ' 23:59:59') : null
|
||||
|
||||
const res = await fetchWriteOffRecordPage(
|
||||
page.value,
|
||||
pageSize.value,
|
||||
searchQuery.value || null
|
||||
searchQuery.value || null,
|
||||
st,
|
||||
et
|
||||
)
|
||||
|
||||
recordList.value = res.items.map((item: any): WriteOffRecord => {
|
||||
@@ -172,6 +181,18 @@ const handleQuery = () => {
|
||||
loadRecords()
|
||||
}
|
||||
|
||||
function onApplyRange(payload : any) {
|
||||
startDate.value = payload?.start ?? ''
|
||||
endDate.value = payload?.end ?? ''
|
||||
handleQuery()
|
||||
}
|
||||
|
||||
function onClearRange() {
|
||||
startDate.value = ''
|
||||
endDate.value = ''
|
||||
handleQuery()
|
||||
}
|
||||
|
||||
const prevPage = () => {
|
||||
if (page.value > 1) {
|
||||
page.value--
|
||||
|
||||
Reference in New Issue
Block a user