🎨 完善订单页面和文章页

This commit is contained in:
2025-09-01 22:36:18 +08:00
parent 7b9a4f5dc1
commit 9bf02628eb
7 changed files with 862 additions and 159 deletions

View File

@@ -9,51 +9,213 @@
/>
</div>
<div class="gva-table-box">
<Content
@changePage="changePage"
:total="total"
v-model:tabloading="tableLoading"
v-model:currentPage="queryParams.page"
v-model:pageSize="queryParams.pageSize"
:data="tableData"
:config="ORDER_TABLE_CONFIG"
>
<template #status="{ row }">
<el-tag :type="tag(row.status).type">{{ tag(row.status).label }}</el-tag>
</template>
<template #operate="{ row }">
<el-button type="text" @click="handleDetail(row)">详情</el-button>
<el-button type="text" @click="handleDelete(row)">删除</el-button>
</template>
</Content>
<div class="gva-btn-list">
<el-button type="info" @click="exportOrders">导出订单</el-button>
</div>
<!-- 订单统计信息 -->
<div style="margin-bottom: 20px; padding: 15px; background: #f8f9fa; border-radius: 8px;">
<el-row :gutter="20">
<el-col :span="4">
<div style="text-align: center;">
<div style="font-size: 24px; font-weight: bold; color: #409eff;">{{ getOrderStats().total }}</div>
<div style="color: #666;">总订单数</div>
</div>
</el-col>
<el-col :span="4">
<div style="text-align: center;">
<div style="font-size: 24px; font-weight: bold; color: #e6a23c;">{{ getOrderStats().unpaid }}</div>
<div style="color: #666;">未付款</div>
</div>
</el-col>
<el-col :span="4">
<div style="text-align: center;">
<div style="font-size: 24px; font-weight: bold; color: #67c23a;">{{ getOrderStats().paid }}</div>
<div style="color: #666;">已付款</div>
</div>
</el-col>
<el-col :span="4">
<div style="text-align: center;">
<div style="font-size: 24px; font-weight: bold; color: #f56c6c;">{{ getOrderStats().expired }}</div>
<div style="color: #666;">已过期</div>
</div>
</el-col>
<el-col :span="4">
<div style="text-align: center;">
<div style="font-size: 24px; font-weight: bold; color: #909399;">{{ getOrderStats().course }}</div>
<div style="color: #666;">课程订单</div>
</div>
</el-col>
<el-col :span="4">
<div style="text-align: center;">
<div style="font-size: 24px; font-weight: bold; color: #e6a23c;">{{ getOrderStats().vip }}</div>
<div style="color: #666;">VIP订单</div>
</div>
</el-col>
</el-row>
</div>
<!-- 自定义订单表格 -->
<el-table
:data="tableData"
v-loading="tableLoading"
border
stripe
style="width: 100%"
>
<el-table-column type="index" label="序号" width="60" align="center" />
<el-table-column prop="order_no" label="订单号" align="center" min-width="180" />
<el-table-column prop="title" label="商品名称" align="center" min-width="150" />
<el-table-column prop="name" label="用户姓名" align="center" width="100" />
<el-table-column label="价格" align="center" width="100">
<template #default="{ row }">
<span class="text-green-600 font-bold">{{ formatPrice(row.price) }}</span>
</template>
</el-table-column>
<el-table-column label="订单类型" align="center" width="100">
<template #default="{ row }">
<el-tag :type="row.order_type === 1 ? 'primary' : 'success'">
{{ row.order_type === 1 ? '课程' : 'VIP' }}
</el-tag>
</template>
</el-table-column>
<el-table-column label="支付方式" align="center" width="100">
<template #default="{ row }">
<el-tag :type="getPayTypeTag(row.pay_type).type">
{{ getPayTypeTag(row.pay_type).label }}
</el-tag>
</template>
</el-table-column>
<el-table-column label="订单状态" align="center" width="100">
<template #default="{ row }">
<el-tag :type="getStatusTag(row.status).type">
{{ getStatusTag(row.status).label }}
</el-tag>
</template>
</el-table-column>
<el-table-column label="创建时间" align="center" width="180">
<template #default="{ row }">
{{ formatDate(row.CreatedAt) }}
</template>
</el-table-column>
<el-table-column label="操作" align="center" width="100">
<template #default="{ row }">
<el-button type="primary" link @click="handleDetail(row)">
<el-icon style="margin-right: 5px"><InfoFilled /></el-icon>详情
</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页 -->
<div style="margin-top: 20px; text-align: right;">
<el-pagination
v-model:current-page="queryParams.page"
v-model:page-size="queryParams.pageSize"
:page-sizes="[10, 20, 50, 100]"
:total="total"
layout="total, sizes, prev, pager, next, jumper"
@size-change="handleSizeChange"
@current-change="changePage"
/>
</div>
</div>
<!-- 订单详情弹窗 -->
<el-dialog
v-model="detailVisible"
title="订单详情"
width="70%"
destroy-on-close
>
<el-descriptions :column="2" border>
<el-descriptions-item label="订单号">{{ detailData.order_no || EMPTY_STR }}</el-descriptions-item>
<el-descriptions-item label="商品名称">{{ detailData.title || EMPTY_STR }}</el-descriptions-item>
<el-descriptions-item label="用户姓名">{{ detailData.name || EMPTY_STR }}</el-descriptions-item>
<el-descriptions-item label="用户ID">{{ detailData.user_id || EMPTY_STR }}</el-descriptions-item>
<el-descriptions-item label="价格">{{ formatPrice(detailData.price) }}</el-descriptions-item>
<el-descriptions-item label="订单类型">
<el-tag :type="detailData.order_type === 1 ? 'primary' : 'success'">
{{ detailData.order_type === 1 ? '课程' : 'VIP' }}
</el-tag>
</el-descriptions-item>
<el-descriptions-item label="支付方式">
<el-tag :type="getPayTypeTag(detailData.pay_type).type">
{{ getPayTypeTag(detailData.pay_type).label }}
</el-tag>
</el-descriptions-item>
<el-descriptions-item label="订单状态">
<el-tag :type="getStatusTag(detailData.status).type">
{{ getStatusTag(detailData.status).label }}
</el-tag>
</el-descriptions-item>
<el-descriptions-item label="手机号">{{ detailData.phone || EMPTY_STR }}</el-descriptions-item>
<el-descriptions-item label="讲师ID">{{ detailData.teacher_Id || EMPTY_STR }}</el-descriptions-item>
<el-descriptions-item label="VIP ID">{{ detailData.vip_id || EMPTY_STR }}</el-descriptions-item>
<el-descriptions-item label="微信OpenID">{{ detailData.open_id || EMPTY_STR }}</el-descriptions-item>
<el-descriptions-item label="创建时间">{{ formatDate(detailData.CreatedAt) }}</el-descriptions-item>
<el-descriptions-item label="更新时间">{{ formatDate(detailData.UpdatedAt) }}</el-descriptions-item>
<el-descriptions-item label="描述" :span="2">{{ detailData.desc || EMPTY_STR }}</el-descriptions-item>
</el-descriptions>
</el-dialog>
</div>
</template>
<script setup>
import searchForm from '@/components/searchForm/index.vue'
import Content from '@/components/content/index.vue'
import {list} from '@/api/order'
import { list } from '@/api/order'
import { ref, onMounted } from 'vue'
import { ORDER_SEARCH_CONFIG, ORDER_TABLE_CONFIG } from '@/config'
const tableLoading = ref(true), tableData = ref([])
import { ORDER_SEARCH_CONFIG } from '@/config'
import { formatDate } from '@/utils/format'
import { InfoFilled } from '@element-plus/icons-vue'
import { ElMessage } from 'element-plus'
const tableLoading = ref(true), tableData = ref([])
const detailVisible = ref(false)
const detailData = ref({})
const queryParams = ref({
page: 1,
pageSize: 10,
orderId: '',
order_no: '',
title: '',
name: '',
order_type: '',
pay_type: '',
status: '',
startTime: '',
endTime: ''
}), total = ref(0)
const EMPTY_STR = '- -'
const tag = (status) => {
// 获取订单状态标签
const getStatusTag = (status) => {
const map = {
1: { type: 'success', label: '已完成' },
2: { type: 'info', label: '待处理' },
3: { type: 'warning', label: '处理中' },
4: { type: 'danger', label: '已拒绝' }
1: { type: 'warning', label: '未付款' },
2: { type: 'success', label: '已付款' },
3: { type: 'danger', label: '已过期' }
}
return map[status] || { type: '', label: EMPTY_STR }
}
// 获取支付方式标签
const getPayTypeTag = (payType) => {
const map = {
1: { type: 'success', label: '微信' },
2: { type: 'primary', label: '支付宝' },
3: { type: 'info', label: '余额' }
}
return map[payType] || { type: '', label: EMPTY_STR }
}
// 格式化价格显示(分转元)
const formatPrice = (price) => {
if (price === null || price === undefined || price === '') return '0.00元'
if (price === 0) return '0.00元'
const yuan = (Number(price) / 100).toFixed(2)
return yuan + '元'
}
const searchData = (data) => {
if (data.times) {
data.startTime = data.times[0]
@@ -64,35 +226,129 @@ const searchData = (data) => {
delete queryParams.value.times
getList()
}
const resetData = () => {
queryParams.value = {
page: 1,
pageSize: 10,
order_no: '',
title: '',
name: '',
order_type: '',
pay_type: '',
status: '',
startTime: '',
endTime: ''
}
getList()
}
onMounted(() => {
getList()
})
async function getList() {
const res = await list(queryParams.value)
if(res.code === 0) {
tableData.value = res.data.list
total.value = res.data.total
tableLoading.value = true
try {
const res = await list(queryParams.value)
if(res.code === 0) {
tableData.value = res.data.list
total.value = res.data.total
console.log('订单数据获取成功:', {
total: res.data.total,
list: res.data.list,
firstItem: res.data.list[0]
})
} else {
ElMessage.error(res.msg || '获取订单列表失败')
}
} catch (error) {
console.error('获取订单列表失败:', error)
ElMessage.error('获取订单列表失败')
} finally {
tableLoading.value = false
}
}
function handleDelete(row) {
console.log(row)
}
function handleDetail(row) {
detailData.value = { ...row }
detailVisible.value = true
}
function changePage(data) {
queryParams.value.pageNum = data
queryParams.value.page = data
getList()
}
function handleSizeChange(val) {
queryParams.value.pageSize = val
getList()
}
// 导出订单功能
function exportOrders() {
ElMessage.success('导出功能开发中...')
}
// 获取订单统计信息
function getOrderStats() {
const stats = {
total: total.value, // 总订单数(来自后端)
unpaid: tableData.value.filter(item => item.status === 1).length,
paid: tableData.value.filter(item => item.status === 2).length,
expired: tableData.value.filter(item => item.status === 3).length,
course: tableData.value.filter(item => item.order_type === 1).length,
vip: tableData.value.filter(item => item.order_type === 2).length
}
return stats
}
</script>
<style lang="scss" scoped>
.gva-table-box {
.gva-btn-list {
margin-bottom: 20px;
}
.el-row {
.el-col {
.el-statistic {
text-align: center;
padding: 20px;
background: white;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
.el-statistic__number {
font-size: 24px;
font-weight: bold;
}
.el-statistic__label {
color: #666;
margin-top: 8px;
}
}
}
}
}
.searchForm {
background: white;
padding: 20px;
border-radius: 8px;
margin-bottom: 20px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
.el-dialog {
.el-descriptions {
.el-descriptions__body {
.el-descriptions__table {
.el-descriptions__cell {
padding: 12px 16px;
}
}
}
}
}
</style>