Browse Source

增加订单详情查看功能

Pchen0 1 month ago
parent
commit
c36ef5756e

+ 19 - 1
src/api/order.js

@@ -3,7 +3,9 @@ import request from '../utils/request'
 const api = {
 const api = {
   Create: '/Order/CreateOrder',
   Create: '/Order/CreateOrder',
   Detail: '/Order/Detail',
   Detail: '/Order/Detail',
-  GetMyOrder: '/Order/GetMyOrders'
+  GetMyOrder: '/Order/GetMyOrders',
+  AdminOrderList: '/Admin/Order/List',
+  AdminOrderDetail: '/Admin/Order/Detail'
 }
 }
 
 
 export function createOrder(parameter) {
 export function createOrder(parameter) {
@@ -29,3 +31,19 @@ export function getMyOrder(parameter) {
     params: parameter
     params: parameter
   })
   })
 }
 }
+
+export function adminOrderList(parameter) {
+  return request({
+    url: api.AdminOrderList,
+    method: 'get',
+    params: parameter
+  })
+}
+
+export function adminOrderDetail(parameter) {
+  return request({
+    url: api.AdminOrderDetail,
+    method: 'get',
+    params: parameter
+  })
+}

+ 183 - 0
src/pages/admin/goods/orderDetail.vue

@@ -0,0 +1,183 @@
+<template>
+    <div class="container">
+        <Breadcrumb />
+        <a-spin :loading="loading" style="width: 100%">
+            <a-space direction="vertical" :size="16" fill>
+                <a-card class="general-card" title="订单状态">
+                    <a-steps :current="current" :status="stepStatus">
+                        <a-step description="用户提交订单">待支付</a-step>
+                        <a-step description="支付成功,系统处理中">待处理</a-step>
+                        <a-step description="商品权益已发放">已完成</a-step>
+                    </a-steps>
+                    <div class="state-tip">
+                        <a-tag :color="getStateColor(data.state)" size="large">{{ getStateLabel(data.state) }}</a-tag>
+                    </div>
+                </a-card>
+
+                <a-card class="general-card" title="订单信息">
+                    <a-descriptions :data="orderDescriptions" size="large" :column="2" />
+                </a-card>
+
+                <a-card class="general-card" title="买家信息">
+                    <a-descriptions :data="buyerDescriptions" size="large" :column="2" />
+                </a-card>
+
+                <a-card v-if="goodsContent" class="general-card" title="商品详情">
+                    <div class="goods-content" v-html="goodsContent"></div>
+                </a-card>
+            </a-space>
+        </a-spin>
+    </div>
+</template>
+
+<script setup>
+import { ref, computed, onMounted } from 'vue'
+import { useRoute } from 'vue-router'
+import { adminOrderDetail } from '@/api/order'
+import { Notification } from '@arco-design/web-vue'
+
+const route = useRoute()
+const loading = ref(true)
+const data = ref({})
+const goodsContent = ref('')
+
+const getStateLabel = (state) => {
+    const map = {
+        0: '待支付',
+        1: '待处理',
+        2: '已完成',
+        3: '已关闭',
+        4: '处理异常'
+    }
+    return map[state] ?? '未知'
+}
+
+const getStateColor = (state) => {
+    const map = {
+        0: 'orangered',
+        1: 'arcoblue',
+        2: 'green',
+        3: 'gray',
+        4: 'red'
+    }
+    return map[state] ?? 'gray'
+}
+
+const getPayTypeLabel = (type) => {
+    const map = { wxpay: '微信支付', alipay: '支付宝', qqpay: 'QQ支付' }
+    return map[type] || '-'
+}
+
+const stramptoTime = (time) => {
+    if (!time) return '-'
+    return new Date(time).toLocaleString('zh-CN', {
+        year: 'numeric',
+        month: '2-digit',
+        day: '2-digit',
+        hour: '2-digit',
+        minute: '2-digit',
+        second: '2-digit'
+    })
+}
+
+const current = computed(() => {
+    const state = data.value?.state
+    if (state === 0 || state === 3) return 1
+    if (state === 1) return 2
+    if (state === 2 || state === 4) return 3
+    return 1
+})
+
+const stepStatus = computed(() => {
+    const state = data.value?.state
+    if (state === 3) return 'error'
+    if (state === 2) return 'finish'
+    if (state === 4) return 'error'
+    return 'process'
+})
+
+const orderDescriptions = computed(() => {
+    const d = data.value
+    if (!d.orderId) return []
+    const items = [
+        { label: '订单号', value: d.orderId },
+        { label: '商品名称', value: d.name || '-' },
+        { label: '订单金额', value: d.price != null ? `¥${d.price}` : '-' },
+        { label: '支付方式', value: getPayTypeLabel(d.pay_type) },
+        { label: '下单时间', value: stramptoTime(d.create_time) },
+        { label: '支付时间', value: stramptoTime(d.pay_time) }
+    ]
+    if (d.pay_id) {
+        items.push({ label: '支付平台订单号', value: d.pay_id })
+    }
+    if (d.lepao_count != null) {
+        items.push({ label: '乐跑次数', value: String(d.lepao_count) })
+    }
+    if (d.ic_count != null) {
+        items.push({ label: 'IC次数', value: String(d.ic_count) })
+    }
+    if (d.vip != null) {
+        items.push({ label: 'VIP', value: d.vip ? '是' : '否' })
+    }
+    return items
+})
+
+const buyerDescriptions = computed(() => {
+    const d = data.value
+    if (!d.orderId) return []
+    return [
+        { label: '用户名', value: d.username || '-' },
+        { label: '邮箱', value: d.user_email || '-' },
+        { label: '用户UUID', value: d.create_user || '-' }
+    ]
+})
+
+const fetchDetail = async () => {
+    try {
+        loading.value = true
+        const res = await adminOrderDetail({ orderId: route.params.orderId })
+        if (!res || res.code !== 0) {
+            return Notification.error({
+                title: '获取订单详情失败',
+                content: res?.msg ?? '请稍后再试'
+            })
+        }
+        data.value = res.data || {}
+        try {
+            goodsContent.value = decodeURI(atob(res.data?.content || ''))
+        } catch {
+            goodsContent.value = res.data?.content || ''
+        }
+    } catch (error) {
+        Notification.error({
+            title: '获取订单详情失败',
+            content: error.message || '请稍后再试'
+        })
+    } finally {
+        loading.value = false
+    }
+}
+
+onMounted(() => {
+    fetchDetail()
+})
+</script>
+
+<style scoped>
+.container {
+    padding: 0 20px 20px 20px;
+}
+
+.general-card {
+    border-radius: 5px;
+}
+
+.state-tip {
+    margin-top: 20px;
+    text-align: center;
+}
+
+.goods-content {
+    line-height: 1.6;
+}
+</style>

+ 286 - 0
src/pages/admin/goods/orderList.vue

@@ -0,0 +1,286 @@
+<template>
+    <div class="container">
+        <Breadcrumb />
+
+        <a-card title="订单管理">
+            <a-row>
+                <a-col :flex="1">
+                    <a-form :model="queryData" :label-col-props="{ span: 6 }" :wrapper-col-props="{ span: 18 }"
+                        label-align="left">
+                        <a-row :gutter="16">
+                            <a-col :span="6">
+                                <a-form-item field="orderId" label="订单号">
+                                    <a-input v-model="queryData.orderId" placeholder="支持模糊搜索" allow-clear />
+                                </a-form-item>
+                            </a-col>
+                            <a-col :span="6">
+                                <a-form-item field="goods_name" label="商品名称">
+                                    <a-input v-model="queryData.goods_name" placeholder="支持模糊搜索" allow-clear />
+                                </a-form-item>
+                            </a-col>
+                            <a-col :span="6">
+                                <a-form-item field="username" label="用户名">
+                                    <a-input v-model="queryData.username" placeholder="支持模糊搜索" allow-clear />
+                                </a-form-item>
+                            </a-col>
+                            <a-col :span="6">
+                                <a-form-item field="user_email" label="用户邮箱">
+                                    <a-input v-model="queryData.user_email" placeholder="支持模糊搜索" allow-clear />
+                                </a-form-item>
+                            </a-col>
+                            <a-col :span="6">
+                                <a-form-item field="state" label="订单状态">
+                                    <a-select v-model="queryData.state" :options="stateOptions" placeholder="请选择状态" />
+                                </a-form-item>
+                            </a-col>
+                            <a-col :span="6">
+                                <a-form-item field="pay_type" label="支付方式">
+                                    <a-select v-model="queryData.pay_type" placeholder="全部方式" allow-clear>
+                                        <a-option value="">全部</a-option>
+                                        <a-option v-for="item in payTypeOptions" :key="item.value" :value="item.value">
+                                            {{ item.label }}
+                                        </a-option>
+                                    </a-select>
+                                </a-form-item>
+                            </a-col>
+                            <a-col :span="6">
+                                <a-form-item field="queryTime" label="下单时间">
+                                    <a-range-picker v-model="queryData.queryTime" show-time format="YY-MM-DD HH:mm"
+                                        value-format="x" @ok="search()" />
+                                </a-form-item>
+                            </a-col>
+                        </a-row>
+                    </a-form>
+                </a-col>
+                <a-divider style="height: 84px" direction="vertical" />
+                <a-col :flex="'86px'" style="text-align: right">
+                    <a-space direction="vertical" :size="18">
+                        <a-button type="primary" @click="search">
+                            <template #icon>
+                                <icon-search />
+                            </template>
+                            搜索
+                        </a-button>
+                        <a-button @click="reset">
+                            <template #icon>
+                                <icon-refresh />
+                            </template>
+                            重置
+                        </a-button>
+                    </a-space>
+                </a-col>
+            </a-row>
+
+            <a-table :data="data" stripe hoverable class="table" :loading="loading" :columns="columns" :pagination="{
+                showPageSize: true,
+                showJumper: true,
+                showTotal: true,
+                pageSize: pagination.pagesize,
+                current: pagination.current,
+                total: pagination.total
+            }" @page-change="handlePageChange" @page-size-change="handlePageSizeChange">
+                <template #username="{ record }">
+                    <a-space>
+                        <a-avatar :size="28">
+                            <img v-if="record.avatar" :alt="record.username ?? ''" :src="record.avatar" />
+                            <icon-user v-else />
+                        </a-avatar>
+                        {{ record.username || '-' }}
+                    </a-space>
+                </template>
+                <template #price="{ record }">
+                    ¥{{ record.price }}
+                </template>
+                <template #pay_type="{ record }">
+                    <a-space>
+                        <icon-wechatpay v-if="record.pay_type === 'wxpay'" />
+                        <icon-alipay-circle v-else-if="record.pay_type === 'alipay'" />
+                        <icon-qq v-else-if="record.pay_type === 'qqpay'" />
+                        <span>{{ getPayTypeLabel(record.pay_type) }}</span>
+                    </a-space>
+                </template>
+                <template #state="{ record }">
+                    <a-tag :color="getStateColor(record.state)">{{ getStateLabel(record.state) }}</a-tag>
+                </template>
+                <template #create_time="{ record }">
+                    {{ stramptoTime(record.create_time) }}
+                </template>
+                <template #pay_time="{ record }">
+                    {{ record.pay_time ? stramptoTime(record.pay_time) : '-' }}
+                </template>
+                <template #optional="{ record }">
+                    <a-button @click="goDetail(record.orderId)">查看详情</a-button>
+                </template>
+            </a-table>
+        </a-card>
+    </div>
+</template>
+
+<script setup>
+import { ref, reactive, onMounted } from 'vue'
+import { useRouter } from 'vue-router'
+import { adminOrderList } from '@/api/order'
+import { Notification } from '@arco-design/web-vue'
+import { getSemesterTimestamps } from '@/utils/util'
+
+const router = useRouter()
+
+const pagination = reactive({
+    total: 0,
+    current: 1,
+    pagesize: 20
+})
+
+const stateOptions = [
+    { label: '全部', value: -1 },
+    { label: '待支付', value: 0 },
+    { label: '待处理', value: 1 },
+    { label: '已完成', value: 2 },
+    { label: '已关闭', value: 3 },
+    { label: '处理异常', value: 4 }
+]
+
+const payTypeOptions = [
+    { label: '微信支付', value: 'wxpay' },
+    { label: '支付宝', value: 'alipay' },
+    { label: 'QQ支付', value: 'qqpay' }
+]
+
+const queryData = reactive({
+    orderId: '',
+    goods_name: '',
+    username: '',
+    user_email: '',
+    state: -1,
+    pay_type: '',
+    queryTime: []
+})
+
+const loading = ref(false)
+const data = ref([])
+
+const columns = [
+    { title: '订单号', dataIndex: 'orderId', width: 180, ellipsis: true, tooltip: true },
+    { title: '商品名称', dataIndex: 'goods_name', width: 160, ellipsis: true, tooltip: true },
+    { title: '用户', slotName: 'username', width: 160 },
+    { title: '用户邮箱', dataIndex: 'user_email', width: 180, ellipsis: true, tooltip: true },
+    { title: '金额', slotName: 'price', width: 100 },
+    { title: '支付方式', slotName: 'pay_type', width: 130 },
+    { title: '订单状态', slotName: 'state', width: 110 },
+    { title: '下单时间', slotName: 'create_time', width: 170 },
+    { title: '支付时间', slotName: 'pay_time', width: 170 },
+    { title: '操作', slotName: 'optional', fixed: 'right', width: 110 }
+]
+
+const getPayTypeLabel = (type) => {
+    const map = { wxpay: '微信支付', alipay: '支付宝', qqpay: 'QQ支付' }
+    return map[type] || '-'
+}
+
+const getStateLabel = (state) => {
+    const map = {
+        0: '待支付',
+        1: '待处理',
+        2: '已完成',
+        3: '已关闭',
+        4: '处理异常'
+    }
+    return map[state] ?? '未知'
+}
+
+const getStateColor = (state) => {
+    const map = {
+        0: 'orangered',
+        1: 'arcoblue',
+        2: 'green',
+        3: 'gray',
+        4: 'red'
+    }
+    return map[state] ?? 'gray'
+}
+
+const stramptoTime = (time) => {
+    if (!time) return '-'
+    return new Date(time).toLocaleString('zh-CN', {
+        year: 'numeric',
+        month: '2-digit',
+        day: '2-digit',
+        hour: '2-digit',
+        minute: '2-digit',
+        second: '2-digit'
+    })
+}
+
+const search = () => {
+    pagination.current = 1
+    getList()
+}
+
+const reset = () => {
+    queryData.orderId = ''
+    queryData.goods_name = ''
+    queryData.username = ''
+    queryData.user_email = ''
+    queryData.state = -1
+    queryData.pay_type = ''
+    queryData.queryTime = getSemesterTimestamps()
+    pagination.current = 1
+    getList()
+}
+
+const getList = async () => {
+    try {
+        loading.value = true
+        const res = await adminOrderList({
+            ...queryData,
+            pagesize: pagination.pagesize,
+            current: pagination.current
+        })
+        if (!res || res.code !== 0) {
+            return Notification.error({
+                title: '获取订单列表失败',
+                content: res?.msg ?? '请稍后再试'
+            })
+        }
+        data.value = res.data || []
+        pagination.total = res.pagination?.total ?? 0
+    } catch (error) {
+        Notification.error({
+            title: '获取订单列表失败',
+            content: error.message || '请稍后再试'
+        })
+    } finally {
+        loading.value = false
+    }
+}
+
+const goDetail = (orderId) => {
+    router.push(`/goodsManage/goods/orderDetail/${orderId}`)
+}
+
+const handlePageChange = (page) => {
+    pagination.current = page
+    getList()
+}
+
+const handlePageSizeChange = (size) => {
+    pagination.pagesize = size
+    pagination.current = 1
+    getList()
+}
+
+onMounted(() => {
+    queryData.queryTime = getSemesterTimestamps()
+    getList()
+})
+</script>
+
+<style scoped>
+.container {
+    padding: 0 20px 20px 20px;
+}
+
+.table {
+    margin-top: 15px;
+}
+</style>

+ 0 - 3
src/pages/admin/lepaoAccount/accountList.vue

@@ -771,9 +771,6 @@ const openBindAudit = async (record) => {
         })
         })
         const fallbackData = (fallbackRes && fallbackRes.code === 0 && Array.isArray(fallbackRes.data)) ? fallbackRes.data : []
         const fallbackData = (fallbackRes && fallbackRes.code === 0 && Array.isArray(fallbackRes.data)) ? fallbackRes.data : []
         bindAuditData.value = fallbackData
         bindAuditData.value = fallbackData
-        if (!bindAuditData.value.length) {
-            Message.info('该账号暂无绑定解绑记录')
-        }
     } catch (error) {
     } catch (error) {
         Notification.error({
         Notification.error({
             title: '获取绑定解绑记录失败',
             title: '获取绑定解绑记录失败',

+ 21 - 0
src/router/index.js

@@ -440,6 +440,7 @@ const routes = [
     {
     {
         path: '/goodsManage',
         path: '/goodsManage',
         name: 'goodsManage',
         name: 'goodsManage',
+        redirect: '/goodsManage/goods/goodsList',
         component: DEFAULT_LAYOUT,
         component: DEFAULT_LAYOUT,
         meta: {
         meta: {
             title: '商品管理',
             title: '商品管理',
@@ -475,6 +476,26 @@ const routes = [
                     title: '商品管理',
                     title: '商品管理',
                     permission: ['admin', 'product']
                     permission: ['admin', 'product']
                 }
                 }
+            },
+            {
+                path: 'goods/orderList',
+                name: 'admin.goods.orderList',
+                component: () => import('../pages/admin/goods/orderList.vue'),
+                meta: {
+                    title: '订单管理',
+                    permission: ['admin', 'product']
+                }
+            },
+            {
+                path: 'goods/orderDetail/:orderId',
+                name: 'admin.goods.orderDetail',
+                component: () => import('../pages/admin/goods/orderDetail.vue'),
+                meta: {
+                    title: '订单详情',
+                    hideInMenu: true,
+                    parent: 'admin.goods.orderList',
+                    permission: ['admin', 'product']
+                }
             }
             }
         ]
         ]
     },
     },