|
|
@@ -0,0 +1,997 @@
|
|
|
+<template>
|
|
|
+ <div class="ele-body">
|
|
|
+ <!-- 统计卡片 -->
|
|
|
+ <el-row :gutter="15" style="margin-bottom: 15px;">
|
|
|
+ <el-col :xs="12" :sm="6">
|
|
|
+ <el-card shadow="hover" class="stat-card">
|
|
|
+ <div class="stat-content">
|
|
|
+ <div class="stat-icon" style="background: #409EFF;">
|
|
|
+ <i class="el-icon-s-order"></i>
|
|
|
+ </div>
|
|
|
+ <div class="stat-info">
|
|
|
+ <div class="stat-label">总订单数</div>
|
|
|
+ <div class="stat-value">{{ statistics.total || 0 }}</div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </el-card>
|
|
|
+ </el-col>
|
|
|
+ <el-col :xs="12" :sm="6">
|
|
|
+ <el-card shadow="hover" class="stat-card">
|
|
|
+ <div class="stat-content">
|
|
|
+ <div class="stat-icon" style="background: #67C23A;">
|
|
|
+ <i class="el-icon-money"></i>
|
|
|
+ </div>
|
|
|
+ <div class="stat-info">
|
|
|
+ <div class="stat-label">总交易额</div>
|
|
|
+ <div class="stat-value">¥{{ statistics.totalAmount || 0 }}</div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </el-card>
|
|
|
+ </el-col>
|
|
|
+ <el-col :xs="12" :sm="6">
|
|
|
+ <el-card shadow="hover" class="stat-card">
|
|
|
+ <div class="stat-content">
|
|
|
+ <div class="stat-icon" style="background: #E6A23C;">
|
|
|
+ <i class="el-icon-time"></i>
|
|
|
+ </div>
|
|
|
+ <div class="stat-info">
|
|
|
+ <div class="stat-label">待处理</div>
|
|
|
+ <div class="stat-value">{{ statistics.pending || 0 }}</div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </el-card>
|
|
|
+ </el-col>
|
|
|
+ <el-col :xs="12" :sm="6">
|
|
|
+ <el-card shadow="hover" class="stat-card">
|
|
|
+ <div class="stat-content">
|
|
|
+ <div class="stat-icon" style="background: #F56C6C;">
|
|
|
+ <i class="el-icon-warning"></i>
|
|
|
+ </div>
|
|
|
+ <div class="stat-info">
|
|
|
+ <div class="stat-label">待退款</div>
|
|
|
+ <div class="stat-value">{{ statistics.refunding || 0 }}</div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </el-card>
|
|
|
+ </el-col>
|
|
|
+ </el-row>
|
|
|
+
|
|
|
+ <el-card shadow="never">
|
|
|
+ <!-- 状态tabs -->
|
|
|
+ <el-tabs v-model="activeStatus" @tab-click="handleTabClick">
|
|
|
+ <el-tab-pane v-for="item in statusTabs" :key="item.value" :name="item.value">
|
|
|
+ <span slot="label"><i :class="item.icon"></i> {{ item.label }}</span>
|
|
|
+ </el-tab-pane>
|
|
|
+ </el-tabs>
|
|
|
+ <!-- 搜索表单 -->
|
|
|
+ <el-form style="margin-top: 15px;" :model="table.where" label-width="90px" class="ele-form-search"
|
|
|
+ @keyup.enter.native="$refs.table.reload()" @submit.native.prevent>
|
|
|
+ <el-row :gutter="15">
|
|
|
+ <el-col :md="8" :sm="12">
|
|
|
+ <el-form-item label="关键词:">
|
|
|
+ <el-input v-model="table.where.keyword" placeholder="订单号/商品名称" clearable />
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ <el-col :md="16" :sm="12">
|
|
|
+ <div class="ele-form-actions">
|
|
|
+ <el-button type="primary" @click="$refs.table.reload()" icon="el-icon-search"
|
|
|
+ class="ele-btn-icon">查询</el-button>
|
|
|
+ <el-button @click="handleReset()">重置</el-button>
|
|
|
+ <el-button type="warning" icon="el-icon-download" @click="handleExport"
|
|
|
+ v-if="permission.includes('sys:order:index')">导出</el-button>
|
|
|
+ <el-button type="danger" icon="el-icon-delete" @click="removeBatch"
|
|
|
+ v-if="permission.includes('sys:order:delete')">批量删除</el-button>
|
|
|
+ </div>
|
|
|
+ </el-col>
|
|
|
+ </el-row>
|
|
|
+ </el-form>
|
|
|
+ <!-- 数据表格 -->
|
|
|
+ <ele-data-table ref="table" :config="table" :choose.sync="choose" height="calc(100vh - 380px)"
|
|
|
+ highlight-current-row>
|
|
|
+ <template>
|
|
|
+ <el-table-column type="selection" width="45" align="center" fixed="left" />
|
|
|
+ <el-table-column prop="id" label="ID" width="60" align="center" fixed="left" />
|
|
|
+ <el-table-column prop="order_no" label="订单号" min-width="180" />
|
|
|
+ <el-table-column prop="user" label="下单用户" min-width="120">
|
|
|
+ <template slot-scope="{row}">
|
|
|
+ <span>{{ row.user ? row.user.realname || row.user.nickname : '' }}</span>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column prop="goods" label="商品信息" min-width="200">
|
|
|
+ <template slot-scope="{row}">
|
|
|
+ <div v-if="row.goods" style="display:flex;align-items:center;">
|
|
|
+ <el-image :src="row.goods.thumb" style="width:50px;height:50px;margin-right:10px;" fit="cover" />
|
|
|
+ <span>{{ row.goods.goods_name }}</span>
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column prop="total" label="订单金额" width="100" align="center">
|
|
|
+ <template slot-scope="{row}">
|
|
|
+ <span>¥{{ row.total }}</span>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column prop="receiver_address" label="收货地址" min-width="200" />
|
|
|
+ <el-table-column prop="status" label="订单状态" width="160" align="center">
|
|
|
+ <template slot-scope="{row}">
|
|
|
+ <div style="display: flex; flex-direction: column; gap: 5px; align-items: center;">
|
|
|
+ <div style="display: flex; align-items: center; gap: 5px;">
|
|
|
+ <span style="font-size: 12px; color: #909399;">订单:</span>
|
|
|
+ <el-tag :type="getStatusType(row.status)" size="small">{{ getStatusText(row.status) }}</el-tag>
|
|
|
+ </div>
|
|
|
+ <div v-if="row.refund_status == 3" style="display: flex; align-items: center; gap: 5px;">
|
|
|
+ <span style="font-size: 12px; color: #909399;">退款:</span>
|
|
|
+ <el-tag type="warning" size="mini">
|
|
|
+ <i class="el-icon-warning"></i> 待审核
|
|
|
+ </el-tag>
|
|
|
+ </div>
|
|
|
+ <div v-if="row.refund_status == 1" style="display: flex; align-items: center; gap: 5px;">
|
|
|
+ <span style="font-size: 12px; color: #909399;">退款:</span>
|
|
|
+ <el-tag type="success" size="mini">
|
|
|
+ <i class="el-icon-circle-check"></i> 已退款
|
|
|
+ </el-tag>
|
|
|
+ </div>
|
|
|
+ <div v-if="row.refund_status == 4" style="display: flex; align-items: center; gap: 5px;">
|
|
|
+ <span style="font-size: 12px; color: #909399;">退款:</span>
|
|
|
+ <el-tag type="danger" size="mini">
|
|
|
+ <i class="el-icon-circle-close"></i> 已驳回
|
|
|
+ </el-tag>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column prop="create_time" label="下单时间" min-width="160" align="center" />
|
|
|
+ <el-table-column label="操作" width="280px" align="center" :resizable="false" fixed="right">
|
|
|
+ <template slot-scope="{row}">
|
|
|
+ <el-link @click="viewDetail(row)" icon="el-icon-view" type="primary" :underline="false">查看</el-link>
|
|
|
+
|
|
|
+ <!-- 待付款:完成支付 -->
|
|
|
+ <el-link @click="quickCompletePay(row)" icon="el-icon-check" type="success" :underline="false"
|
|
|
+ v-if="row.status == 1 && permission.includes('sys:order:status')" class="ele-action">支付</el-link>
|
|
|
+
|
|
|
+ <!-- 已付款:发货 -->
|
|
|
+ <el-link @click="quickDeliver(row)" icon="el-icon-truck" type="warning" :underline="false"
|
|
|
+ v-if="row.status == 2 && permission.includes('sys:order:status')" class="ele-action">发货</el-link>
|
|
|
+
|
|
|
+ <!-- 已发货:完成 -->
|
|
|
+ <el-link @click="quickComplete(row)" icon="el-icon-circle-check" type="success" :underline="false"
|
|
|
+ v-if="row.status == 3 && permission.includes('sys:order:status')" class="ele-action">完成</el-link>
|
|
|
+
|
|
|
+ <!-- 删除 -->
|
|
|
+ <el-popconfirm title="确定要删除此订单吗?" @confirm="remove(row)" class="ele-action">
|
|
|
+ <el-link slot="reference" icon="el-icon-delete" type="danger" :underline="false"
|
|
|
+ v-if="permission.includes('sys:order:delete')">删除</el-link>
|
|
|
+ </el-popconfirm>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ </template>
|
|
|
+ </ele-data-table>
|
|
|
+ </el-card>
|
|
|
+
|
|
|
+ <!-- 发货弹窗 -->
|
|
|
+ <el-dialog title="订单发货" :visible.sync="deliverVisible" width="500px" :close-on-click-modal="false">
|
|
|
+ <el-form :model="deliverForm" label-width="100px">
|
|
|
+ <el-form-item label="快递单号" required>
|
|
|
+ <el-input v-model="deliverForm.delivery_no" placeholder="请输入快递单号" clearable />
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="快递公司" required>
|
|
|
+ <el-select v-model="deliverForm.delivery_company" placeholder="请选择快递公司" clearable filterable
|
|
|
+ style="width: 100%;">
|
|
|
+ <el-option label="顺丰速运" value="顺丰速运"></el-option>
|
|
|
+ <el-option label="圆通速递" value="圆通速递"></el-option>
|
|
|
+ <el-option label="中通快递" value="中通快递"></el-option>
|
|
|
+ <el-option label="韵达快递" value="韵达快递"></el-option>
|
|
|
+ <el-option label="申通快递" value="申通快递"></el-option>
|
|
|
+ <el-option label="百世快递" value="百世快递"></el-option>
|
|
|
+ <el-option label="京东物流" value="京东物流"></el-option>
|
|
|
+ <el-option label="邮政EMS" value="邮政EMS"></el-option>
|
|
|
+ <el-option label="德邦快递" value="德邦快递"></el-option>
|
|
|
+ <el-option label="天天快递" value="天天快递"></el-option>
|
|
|
+ </el-select>
|
|
|
+ </el-form-item>
|
|
|
+ </el-form>
|
|
|
+ <div slot="footer">
|
|
|
+ <el-button @click="deliverVisible = false">取消</el-button>
|
|
|
+ <el-button type="primary" @click="confirmDeliver">确认发货</el-button>
|
|
|
+ </div>
|
|
|
+ </el-dialog>
|
|
|
+
|
|
|
+ <!-- 订单详情弹窗 -->
|
|
|
+ <el-dialog title="订单详情" :visible.sync="detailVisible" width="85%" top="3vh" :close-on-click-modal="false"
|
|
|
+ custom-class="order-detail-dialog">
|
|
|
+ <div v-loading="detailLoading" class="order-detail-content">
|
|
|
+ <!-- 订单状态卡片 -->
|
|
|
+ <el-card shadow="hover" class="status-card">
|
|
|
+ <div class="status-header">
|
|
|
+ <div class="status-info">
|
|
|
+ <span class="order-no">订单号:{{ orderInfo.order_no }}</span>
|
|
|
+ <div class="status-tags">
|
|
|
+ <div class="status-item">
|
|
|
+ <span class="status-label">订单状态:</span>
|
|
|
+ <el-tag :type="getStatusType(orderInfo.status)" size="medium" class="status-tag">
|
|
|
+ {{ getStatusText(orderInfo.status) }}
|
|
|
+ </el-tag>
|
|
|
+ </div>
|
|
|
+ <div v-if="orderInfo.refund_status > 0" class="status-item">
|
|
|
+ <span class="status-label">退款状态:</span>
|
|
|
+ <el-tag v-if="orderInfo.refund_status == 3" type="warning" size="medium" class="status-tag">
|
|
|
+ <i class="el-icon-warning"></i> 待审核
|
|
|
+ </el-tag>
|
|
|
+ <el-tag v-if="orderInfo.refund_status == 1" type="success" size="medium" class="status-tag">
|
|
|
+ <i class="el-icon-circle-check"></i> 已退款
|
|
|
+ </el-tag>
|
|
|
+ <el-tag v-if="orderInfo.refund_status == 4" type="danger" size="medium" class="status-tag">
|
|
|
+ <i class="el-icon-circle-close"></i> 已驳回
|
|
|
+ </el-tag>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div class="amount-info">
|
|
|
+ <div class="amount-item">
|
|
|
+ <span class="label">订单金额</span>
|
|
|
+ <span class="value total">¥{{ orderInfo.total }}</span>
|
|
|
+ </div>
|
|
|
+ <div class="amount-item">
|
|
|
+ <span class="label">实付金额</span>
|
|
|
+ <span class="value paid">¥{{ orderInfo.pay_total }}</span>
|
|
|
+ </div>
|
|
|
+ <div class="amount-item" v-if="orderInfo.bonus > 0">
|
|
|
+ <span class="label">商家佣金</span>
|
|
|
+ <span class="value bonus">¥{{ orderInfo.bonus }}</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </el-card>
|
|
|
+
|
|
|
+ <!-- 订单基本信息 -->
|
|
|
+ <el-card shadow="hover" class="info-card">
|
|
|
+ <div slot="header" class="card-header">
|
|
|
+ <i class="el-icon-document"></i> 订单信息
|
|
|
+ </div>
|
|
|
+ <el-row :gutter="20">
|
|
|
+ <el-col :span="12">
|
|
|
+ <div class="info-item">
|
|
|
+ <span class="info-label">下单用户:</span>
|
|
|
+ <span class="info-value">{{ orderInfo.user ? (orderInfo.user.realname || orderInfo.user.nickname) : '-'
|
|
|
+ }}</span>
|
|
|
+ </div>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="12">
|
|
|
+ <div class="info-item">
|
|
|
+ <span class="info-label">用户手机:</span>
|
|
|
+ <span class="info-value">{{ orderInfo.user ? orderInfo.user.mobile : '-' }}</span>
|
|
|
+ </div>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="12">
|
|
|
+ <div class="info-item">
|
|
|
+ <span class="info-label">所属店铺:</span>
|
|
|
+ <span class="info-value">{{ orderInfo.store ? orderInfo.store.store_name : '-' }}</span>
|
|
|
+ </div>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="12">
|
|
|
+ <div class="info-item">
|
|
|
+ <span class="info-label">交易单号:</span>
|
|
|
+ <span class="info-value">{{ orderInfo.transaction_id || '-' }}</span>
|
|
|
+ </div>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="12">
|
|
|
+ <div class="info-item">
|
|
|
+ <span class="info-label">下单时间:</span>
|
|
|
+ <span class="info-value">{{ orderInfo.create_time }}</span>
|
|
|
+ </div>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="12">
|
|
|
+ <div class="info-item">
|
|
|
+ <span class="info-label">更新时间:</span>
|
|
|
+ <span class="info-value">{{ orderInfo.update_time }}</span>
|
|
|
+ </div>
|
|
|
+ </el-col>
|
|
|
+ </el-row>
|
|
|
+ </el-card>
|
|
|
+
|
|
|
+ <!-- 收货信息 -->
|
|
|
+ <el-card shadow="hover" class="info-card">
|
|
|
+ <div slot="header" class="card-header">
|
|
|
+ <i class="el-icon-location"></i> 收货信息
|
|
|
+ </div>
|
|
|
+ <el-row :gutter="20">
|
|
|
+ <el-col :span="8">
|
|
|
+ <div class="info-item">
|
|
|
+ <span class="info-label">收货人:</span>
|
|
|
+ <span class="info-value">{{ orderInfo.receiver_name }}</span>
|
|
|
+ </div>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="16">
|
|
|
+ <div class="info-item">
|
|
|
+ <span class="info-label">所在地区:</span>
|
|
|
+ <span class="info-value">{{ orderInfo.receiver_area }}</span>
|
|
|
+ </div>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="24">
|
|
|
+ <div class="info-item">
|
|
|
+ <span class="info-label">详细地址:</span>
|
|
|
+ <span class="info-value">{{ orderInfo.receiver_address }}</span>
|
|
|
+ </div>
|
|
|
+ </el-col>
|
|
|
+ </el-row>
|
|
|
+ </el-card>
|
|
|
+
|
|
|
+ <!-- 物流信息 -->
|
|
|
+ <el-card shadow="hover" class="info-card" v-if="orderInfo.delivery_no">
|
|
|
+ <div slot="header" class="card-header">
|
|
|
+ <i class="el-icon-truck"></i> 物流信息
|
|
|
+ </div>
|
|
|
+ <el-row :gutter="20">
|
|
|
+ <el-col :span="12">
|
|
|
+ <div class="info-item">
|
|
|
+ <span class="info-label">快递公司:</span>
|
|
|
+ <span class="info-value">{{ orderInfo.delivery_company }}</span>
|
|
|
+ </div>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="12">
|
|
|
+ <div class="info-item">
|
|
|
+ <span class="info-label">快递单号:</span>
|
|
|
+ <span class="info-value highlight">{{ orderInfo.delivery_no }}</span>
|
|
|
+ </div>
|
|
|
+ </el-col>
|
|
|
+ </el-row>
|
|
|
+ </el-card>
|
|
|
+
|
|
|
+ <!-- 商品信息 -->
|
|
|
+ <el-card shadow="hover" class="info-card">
|
|
|
+ <div slot="header" class="card-header">
|
|
|
+ <i class="el-icon-goods"></i> 商品信息
|
|
|
+ </div>
|
|
|
+ <el-table :data="orderInfo.order_goods" border stripe style="width: 100%">
|
|
|
+ <el-table-column label="商品图片" width="100" align="center">
|
|
|
+ <template slot-scope="{row}">
|
|
|
+ <el-image :src="row.thumb" style="width: 60px; height: 60px; border-radius: 4px;" fit="cover"
|
|
|
+ :preview-src-list="[row.thumb]" />
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column prop="goods_name" label="商品名称" min-width="200" show-overflow-tooltip />
|
|
|
+ <el-table-column prop="price" label="单价" width="100" align="center">
|
|
|
+ <template slot-scope="{row}">
|
|
|
+ <span class="price-text">¥{{ row.price }}</span>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column prop="stock" label="数量" width="80" align="center">
|
|
|
+ <template slot-scope="{row}">
|
|
|
+ <el-tag type="info" size="small">{{ row.stock }}</el-tag>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column prop="unit" label="单位" width="80" align="center" />
|
|
|
+ <el-table-column prop="weight" label="重量(KG)" width="100" align="center" />
|
|
|
+ <el-table-column label="小计" width="120" align="center">
|
|
|
+ <template slot-scope="{row}">
|
|
|
+ <span class="subtotal-text">¥{{ (row.price * row.stock).toFixed(2) }}</span>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ </el-table>
|
|
|
+ </el-card>
|
|
|
+
|
|
|
+ <!-- 售后/退款信息 -->
|
|
|
+ <el-card shadow="hover" class="info-card" v-if="orderInfo.after_type > 0 || orderInfo.refund_status > 0">
|
|
|
+ <div slot="header" class="card-header">
|
|
|
+ <i class="el-icon-warning"></i> 售后/退款信息
|
|
|
+ </div>
|
|
|
+ <el-row :gutter="20">
|
|
|
+ <el-col :span="12">
|
|
|
+ <div class="info-item">
|
|
|
+ <span class="info-label">售后类型:</span>
|
|
|
+ <el-tag v-if="orderInfo.after_type == 1" type="warning" size="small">售后</el-tag>
|
|
|
+ <el-tag v-else-if="orderInfo.after_type == 2" type="danger" size="small">退款</el-tag>
|
|
|
+ <span v-else class="info-value">无</span>
|
|
|
+ </div>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="12">
|
|
|
+ <div class="info-item">
|
|
|
+ <span class="info-label">退款状态:</span>
|
|
|
+ <el-tag :type="getRefundStatusType(orderInfo.refund_status)" size="small">
|
|
|
+ {{ getRefundStatusText(orderInfo.refund_status) }}
|
|
|
+ </el-tag>
|
|
|
+ </div>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="12" v-if="orderInfo.after_realname">
|
|
|
+ <div class="info-item">
|
|
|
+ <span class="info-label">联系人:</span>
|
|
|
+ <span class="info-value">{{ orderInfo.after_realname }}</span>
|
|
|
+ </div>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="12" v-if="orderInfo.after_phone">
|
|
|
+ <div class="info-item">
|
|
|
+ <span class="info-label">联系电话:</span>
|
|
|
+ <span class="info-value">{{ orderInfo.after_phone }}</span>
|
|
|
+ </div>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="24" v-if="orderInfo.after_remark">
|
|
|
+ <div class="info-item">
|
|
|
+ <span class="info-label">用户反馈:</span>
|
|
|
+ <span class="info-value">{{ orderInfo.after_remark }}</span>
|
|
|
+ </div>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="24" v-if="orderInfo.refund_remark">
|
|
|
+ <div class="info-item">
|
|
|
+ <span class="info-label">审核备注:</span>
|
|
|
+ <span class="info-value">{{ orderInfo.refund_remark }}</span>
|
|
|
+ </div>
|
|
|
+ </el-col>
|
|
|
+ </el-row>
|
|
|
+ </el-card>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div slot="footer">
|
|
|
+ <el-button @click="detailVisible = false">关闭</el-button>
|
|
|
+ <!-- 待付款:完成支付、取消订单 -->
|
|
|
+ <el-button type="success" @click="handleCompletePay"
|
|
|
+ v-if="orderInfo.status == 1 && permission.includes('sys:order:status')">
|
|
|
+ 完成支付
|
|
|
+ </el-button>
|
|
|
+ <el-button type="info" @click="handleCancel"
|
|
|
+ v-if="orderInfo.status == 1 && permission.includes('sys:order:delete')">
|
|
|
+ 取消订单
|
|
|
+ </el-button>
|
|
|
+ <!-- 已付款:发货 -->
|
|
|
+ <el-button type="primary" @click="handleDeliver"
|
|
|
+ v-if="orderInfo.status == 2 && permission.includes('sys:order:status')">
|
|
|
+ 订单发货
|
|
|
+ </el-button>
|
|
|
+ <!-- 已发货:确认完成 -->
|
|
|
+ <el-button type="success" @click="handleComplete"
|
|
|
+ v-if="orderInfo.status == 3 && permission.includes('sys:order:status')">
|
|
|
+ 确认完成
|
|
|
+ </el-button>
|
|
|
+ </div>
|
|
|
+ </el-dialog>
|
|
|
+ </div>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script>
|
|
|
+import { mapGetters } from "vuex";
|
|
|
+export default {
|
|
|
+ name: "Order",
|
|
|
+ data() {
|
|
|
+ return {
|
|
|
+ table: { url: '/order/index', where: { status: 0 } },
|
|
|
+ choose: [],
|
|
|
+ statusTabs: [
|
|
|
+ { value: '0', label: '全部订单', icon: 'el-icon-s-grid' },
|
|
|
+ { value: '1', label: '待付款', icon: 'el-icon-time' },
|
|
|
+ { value: '2', label: '已付款', icon: 'el-icon-check' },
|
|
|
+ { value: '3', label: '已发货', icon: 'el-icon-truck' },
|
|
|
+ { value: '4', label: '已完成', icon: 'el-icon-circle-check' },
|
|
|
+ ],
|
|
|
+ activeStatus: '0',
|
|
|
+ detailVisible: false,
|
|
|
+ detailLoading: false,
|
|
|
+ orderInfo: {
|
|
|
+ user: {},
|
|
|
+ store: {},
|
|
|
+ order_goods: []
|
|
|
+ },
|
|
|
+ statistics: {
|
|
|
+ total: 0,
|
|
|
+ totalAmount: 0,
|
|
|
+ pending: 0,
|
|
|
+ refunding: 0
|
|
|
+ },
|
|
|
+ deliverVisible: false,
|
|
|
+ deliverForm: {
|
|
|
+ id: 0,
|
|
|
+ delivery_no: '',
|
|
|
+ delivery_company: ''
|
|
|
+ }
|
|
|
+ }
|
|
|
+ },
|
|
|
+ mounted() {
|
|
|
+ this.loadStatistics();
|
|
|
+ },
|
|
|
+ computed: {
|
|
|
+ ...mapGetters(["permission"]),
|
|
|
+ },
|
|
|
+ methods: {
|
|
|
+ handleTabClick(tab) {
|
|
|
+ this.activeStatus = tab.name;
|
|
|
+ this.table.where.status = parseInt(tab.name);
|
|
|
+ this.$refs.table.reload();
|
|
|
+ },
|
|
|
+ handleReset() {
|
|
|
+ this.activeStatus = '0';
|
|
|
+ this.table.where = { status: 0 };
|
|
|
+ this.$refs.table.reload();
|
|
|
+ },
|
|
|
+ // 加载统计数据
|
|
|
+ loadStatistics() {
|
|
|
+ this.$http.get('/order/statistics').then(res => {
|
|
|
+ if (res.data.code === 0) {
|
|
|
+ this.statistics = res.data.data;
|
|
|
+ }
|
|
|
+ }).catch(e => {
|
|
|
+ console.error('加载统计数据失败:', e);
|
|
|
+ });
|
|
|
+ },
|
|
|
+ viewDetail(row) {
|
|
|
+ this.detailVisible = true;
|
|
|
+ this.loadOrderDetail(row.id);
|
|
|
+ },
|
|
|
+ loadOrderDetail(id) {
|
|
|
+ this.detailLoading = true;
|
|
|
+ this.$http.get('/order/info', { params: { id } }).then(res => {
|
|
|
+ this.detailLoading = false;
|
|
|
+ if (res.data.code === 0) {
|
|
|
+ this.orderInfo = res.data.data;
|
|
|
+ } else {
|
|
|
+ this.$message.error(res.data.msg);
|
|
|
+ }
|
|
|
+ }).catch(e => {
|
|
|
+ this.detailLoading = false;
|
|
|
+ this.$message.error(e.message);
|
|
|
+ });
|
|
|
+ },
|
|
|
+ remove(row) {
|
|
|
+ const loading = this.$loading({ lock: true });
|
|
|
+ this.$http.post('/order/delete', { id: row.id }).then(res => {
|
|
|
+ loading.close();
|
|
|
+ if (res.data.code === 0) {
|
|
|
+ this.$message({ type: 'success', message: res.data.msg });
|
|
|
+ this.$refs.table.reload();
|
|
|
+ } else {
|
|
|
+ this.$message.error(res.data.msg);
|
|
|
+ }
|
|
|
+ }).catch(e => {
|
|
|
+ loading.close();
|
|
|
+ this.$message.error(e.message);
|
|
|
+ });
|
|
|
+ },
|
|
|
+ // 批量删除
|
|
|
+ removeBatch() {
|
|
|
+ if (!this.choose.length) {
|
|
|
+ this.$message.error('请至少选择一条数据');
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ this.$confirm('确定要删除选中的 ' + this.choose.length + ' 条订单吗?', '提示', {
|
|
|
+ type: 'warning'
|
|
|
+ }).then(() => {
|
|
|
+ const loading = this.$loading({ lock: true });
|
|
|
+ const ids = this.choose.map(item => item.id);
|
|
|
+ this.$http.post('/order/delete', { id: ids }).then(res => {
|
|
|
+ loading.close();
|
|
|
+ if (res.data.code === 0) {
|
|
|
+ this.$message.success(res.data.msg);
|
|
|
+ this.$refs.table.reload();
|
|
|
+ } else {
|
|
|
+ this.$message.error(res.data.msg);
|
|
|
+ }
|
|
|
+ }).catch(e => {
|
|
|
+ loading.close();
|
|
|
+ this.$message.error(e.message);
|
|
|
+ });
|
|
|
+ }).catch(() => { });
|
|
|
+ },
|
|
|
+ // 导出订单
|
|
|
+ handleExport() {
|
|
|
+ this.$confirm('确定要导出当前筛选条件下的订单数据吗?', '提示', {
|
|
|
+ type: 'warning'
|
|
|
+ }).then(() => {
|
|
|
+ const loading = this.$loading({ lock: true, text: '正在导出...' });
|
|
|
+ // 构建导出参数
|
|
|
+ const params = { ...this.table.where, export: 1 };
|
|
|
+ const queryString = Object.keys(params)
|
|
|
+ .map(key => `${key}=${encodeURIComponent(params[key])}`)
|
|
|
+ .join('&');
|
|
|
+
|
|
|
+ // 创建隐藏的下载链接
|
|
|
+ const link = document.createElement('a');
|
|
|
+ link.href = `/order/export?${queryString}`;
|
|
|
+ link.download = `订单列表_${new Date().getTime()}.xlsx`;
|
|
|
+ document.body.appendChild(link);
|
|
|
+ link.click();
|
|
|
+ document.body.removeChild(link);
|
|
|
+
|
|
|
+ setTimeout(() => {
|
|
|
+ loading.close();
|
|
|
+ this.$message.success('导出成功');
|
|
|
+ }, 1000);
|
|
|
+ }).catch(() => { });
|
|
|
+ },
|
|
|
+ // 完成支付
|
|
|
+ handleCompletePay() {
|
|
|
+ this.$confirm('确定要将此订单标记为已支付吗?', '提示', {
|
|
|
+ type: 'warning'
|
|
|
+ }).then(() => {
|
|
|
+ const loading = this.$loading({ lock: true });
|
|
|
+ this.$http.post('/order/completePay', { id: this.orderInfo.id }).then(res => {
|
|
|
+ loading.close();
|
|
|
+ if (res.data.code === 0) {
|
|
|
+ this.$message.success(res.data.msg);
|
|
|
+ this.loadOrderDetail(this.orderInfo.id);
|
|
|
+ this.$refs.table.reload();
|
|
|
+ } else {
|
|
|
+ this.$message.error(res.data.msg);
|
|
|
+ }
|
|
|
+ }).catch(e => {
|
|
|
+ loading.close();
|
|
|
+ this.$message.error(e.message);
|
|
|
+ });
|
|
|
+ }).catch(() => { });
|
|
|
+ },
|
|
|
+ // 订单发货
|
|
|
+ handleDeliver() {
|
|
|
+ this.deliverForm = {
|
|
|
+ id: this.orderInfo.id,
|
|
|
+ delivery_no: '',
|
|
|
+ delivery_company: ''
|
|
|
+ };
|
|
|
+ this.deliverVisible = true;
|
|
|
+ },
|
|
|
+ // 确认发货
|
|
|
+ confirmDeliver() {
|
|
|
+ if (!this.deliverForm.delivery_no) {
|
|
|
+ this.$message.error('请输入快递单号');
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ if (!this.deliverForm.delivery_company) {
|
|
|
+ this.$message.error('请选择快递公司');
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ const loading = this.$loading({ lock: true });
|
|
|
+ this.$http.post('/order/deliver', this.deliverForm).then(res => {
|
|
|
+ loading.close();
|
|
|
+ if (res.data.code === 0) {
|
|
|
+ this.$message.success(res.data.msg);
|
|
|
+ this.deliverVisible = false;
|
|
|
+ // 如果详情弹窗打开,则刷新详情
|
|
|
+ if (this.detailVisible) {
|
|
|
+ this.loadOrderDetail(this.deliverForm.id);
|
|
|
+ }
|
|
|
+ this.$refs.table.reload();
|
|
|
+ } else {
|
|
|
+ this.$message.error(res.data.msg);
|
|
|
+ }
|
|
|
+ }).catch(e => {
|
|
|
+ loading.close();
|
|
|
+ this.$message.error(e.message);
|
|
|
+ });
|
|
|
+ },
|
|
|
+ // 确认完成
|
|
|
+ handleComplete() {
|
|
|
+ this.$confirm('确定要将此订单标记为已完成吗?', '提示', {
|
|
|
+ type: 'warning'
|
|
|
+ }).then(() => {
|
|
|
+ const loading = this.$loading({ lock: true });
|
|
|
+ this.$http.post('/order/complete', { id: this.orderInfo.id }).then(res => {
|
|
|
+ loading.close();
|
|
|
+ if (res.data.code === 0) {
|
|
|
+ this.$message.success(res.data.msg);
|
|
|
+ this.loadOrderDetail(this.orderInfo.id);
|
|
|
+ this.$refs.table.reload();
|
|
|
+ } else {
|
|
|
+ this.$message.error(res.data.msg);
|
|
|
+ }
|
|
|
+ }).catch(e => {
|
|
|
+ loading.close();
|
|
|
+ this.$message.error(e.message);
|
|
|
+ });
|
|
|
+ }).catch(() => { });
|
|
|
+ },
|
|
|
+ // 取消订单
|
|
|
+ handleCancel() {
|
|
|
+ this.$confirm('确定要取消此订单吗?', '提示', {
|
|
|
+ type: 'warning'
|
|
|
+ }).then(() => {
|
|
|
+ const loading = this.$loading({ lock: true });
|
|
|
+ this.$http.post('/order/cancel', { id: this.orderInfo.id }).then(res => {
|
|
|
+ loading.close();
|
|
|
+ if (res.data.code === 0) {
|
|
|
+ this.$message.success(res.data.msg);
|
|
|
+ this.detailVisible = false;
|
|
|
+ this.$refs.table.reload();
|
|
|
+ } else {
|
|
|
+ this.$message.error(res.data.msg);
|
|
|
+ }
|
|
|
+ }).catch(e => {
|
|
|
+ loading.close();
|
|
|
+ this.$message.error(e.message);
|
|
|
+ });
|
|
|
+ }).catch(() => { });
|
|
|
+ },
|
|
|
+ getStatusText(status) {
|
|
|
+ const map = { 1: '待付款', 2: '已付款', 3: '已发货', 4: '已完成' };
|
|
|
+ return map[status] || '未知';
|
|
|
+ },
|
|
|
+ getStatusType(status) {
|
|
|
+ const map = { 1: 'info', 2: 'warning', 3: 'primary', 4: 'success' };
|
|
|
+ return map[status] || '';
|
|
|
+ },
|
|
|
+ getRefundStatusText(status) {
|
|
|
+ const map = { 0: '无', 1: '已退款', 2: '已审核', 3: '待审核', 4: '审核驳回' };
|
|
|
+ return map[status] || '未知';
|
|
|
+ },
|
|
|
+ getRefundStatusType(status) {
|
|
|
+ const map = { 0: 'info', 1: 'success', 2: 'primary', 3: 'warning', 4: 'danger' };
|
|
|
+ return map[status] || '';
|
|
|
+ },
|
|
|
+ // 快捷完成支付
|
|
|
+ quickCompletePay(row) {
|
|
|
+ this.$confirm('确定要将此订单标记为已支付吗?', '提示', {
|
|
|
+ type: 'warning'
|
|
|
+ }).then(() => {
|
|
|
+ const loading = this.$loading({ lock: true });
|
|
|
+ this.$http.post('/order/completePay', { id: row.id }).then(res => {
|
|
|
+ loading.close();
|
|
|
+ if (res.data.code === 0) {
|
|
|
+ this.$message.success(res.data.msg);
|
|
|
+ this.$refs.table.reload();
|
|
|
+ } else {
|
|
|
+ this.$message.error(res.data.msg);
|
|
|
+ }
|
|
|
+ }).catch(e => {
|
|
|
+ loading.close();
|
|
|
+ this.$message.error(e.message);
|
|
|
+ });
|
|
|
+ }).catch(() => { });
|
|
|
+ },
|
|
|
+ // 快捷发货
|
|
|
+ quickDeliver(row) {
|
|
|
+ this.deliverForm = {
|
|
|
+ id: row.id,
|
|
|
+ delivery_no: '',
|
|
|
+ delivery_company: ''
|
|
|
+ };
|
|
|
+ this.deliverVisible = true;
|
|
|
+ },
|
|
|
+ // 快捷完成订单
|
|
|
+ quickComplete(row) {
|
|
|
+ this.$confirm('确定要将此订单标记为已完成吗?', '提示', {
|
|
|
+ type: 'warning'
|
|
|
+ }).then(() => {
|
|
|
+ const loading = this.$loading({ lock: true });
|
|
|
+ this.$http.post('/order/complete', { id: row.id }).then(res => {
|
|
|
+ loading.close();
|
|
|
+ if (res.data.code === 0) {
|
|
|
+ this.$message.success(res.data.msg);
|
|
|
+ this.$refs.table.reload();
|
|
|
+ } else {
|
|
|
+ this.$message.error(res.data.msg);
|
|
|
+ }
|
|
|
+ }).catch(e => {
|
|
|
+ loading.close();
|
|
|
+ this.$message.error(e.message);
|
|
|
+ });
|
|
|
+ }).catch(() => { });
|
|
|
+ },
|
|
|
+ // 快捷申请退款
|
|
|
+ quickApplyRefund(row) {
|
|
|
+ this.refundForm = {
|
|
|
+ id: row.id,
|
|
|
+ after_type: 2,
|
|
|
+ after_realname: '',
|
|
|
+ after_phone: '',
|
|
|
+ after_remark: ''
|
|
|
+ };
|
|
|
+ this.refundVisible = true;
|
|
|
+ },
|
|
|
+ }
|
|
|
+}
|
|
|
+</script>
|
|
|
+
|
|
|
+<style scoped>
|
|
|
+.el-descriptions {
|
|
|
+ margin-bottom: 20px;
|
|
|
+}
|
|
|
+
|
|
|
+.stat-card {
|
|
|
+ cursor: pointer;
|
|
|
+ transition: all 0.3s;
|
|
|
+}
|
|
|
+
|
|
|
+.stat-card:hover {
|
|
|
+ transform: translateY(-5px);
|
|
|
+}
|
|
|
+
|
|
|
+.stat-content {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+}
|
|
|
+
|
|
|
+.stat-icon {
|
|
|
+ width: 60px;
|
|
|
+ height: 60px;
|
|
|
+ border-radius: 10px;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+ font-size: 28px;
|
|
|
+ color: #fff;
|
|
|
+ margin-right: 15px;
|
|
|
+}
|
|
|
+
|
|
|
+.stat-info {
|
|
|
+ flex: 1;
|
|
|
+}
|
|
|
+
|
|
|
+.stat-label {
|
|
|
+ font-size: 14px;
|
|
|
+ color: #909399;
|
|
|
+ margin-bottom: 5px;
|
|
|
+}
|
|
|
+
|
|
|
+.stat-value {
|
|
|
+ font-size: 24px;
|
|
|
+ font-weight: bold;
|
|
|
+ color: #303133;
|
|
|
+}
|
|
|
+
|
|
|
+/* 订单详情弹窗样式 */
|
|
|
+.order-detail-content {
|
|
|
+ max-height: 75vh;
|
|
|
+ overflow-y: auto;
|
|
|
+ padding: 10px;
|
|
|
+}
|
|
|
+
|
|
|
+.status-card {
|
|
|
+ margin-bottom: 20px;
|
|
|
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
|
+ color: #fff;
|
|
|
+}
|
|
|
+
|
|
|
+.status-card>>>.el-card__body {
|
|
|
+ padding: 20px;
|
|
|
+}
|
|
|
+
|
|
|
+.status-header {
|
|
|
+ display: flex;
|
|
|
+ justify-content: space-between;
|
|
|
+ align-items: center;
|
|
|
+ flex-wrap: wrap;
|
|
|
+ gap: 15px;
|
|
|
+}
|
|
|
+
|
|
|
+.status-info {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ gap: 15px;
|
|
|
+ flex-wrap: wrap;
|
|
|
+}
|
|
|
+
|
|
|
+.order-no {
|
|
|
+ font-size: 16px;
|
|
|
+ font-weight: bold;
|
|
|
+ color: #fff;
|
|
|
+}
|
|
|
+
|
|
|
+.status-tags {
|
|
|
+ display: flex;
|
|
|
+ gap: 15px;
|
|
|
+ flex-wrap: wrap;
|
|
|
+ align-items: center;
|
|
|
+}
|
|
|
+
|
|
|
+.status-item {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ gap: 8px;
|
|
|
+}
|
|
|
+
|
|
|
+.status-label {
|
|
|
+ font-size: 14px;
|
|
|
+ color: rgba(255, 255, 255, 0.9);
|
|
|
+ font-weight: 500;
|
|
|
+}
|
|
|
+
|
|
|
+.status-tag {
|
|
|
+ font-size: 14px;
|
|
|
+ padding: 2px 16px;
|
|
|
+}
|
|
|
+
|
|
|
+.status-tag i {
|
|
|
+ margin-right: 3px;
|
|
|
+}
|
|
|
+
|
|
|
+.amount-info {
|
|
|
+ display: flex;
|
|
|
+ gap: 30px;
|
|
|
+}
|
|
|
+
|
|
|
+.amount-item {
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ align-items: flex-end;
|
|
|
+}
|
|
|
+
|
|
|
+.amount-item .label {
|
|
|
+ font-size: 12px;
|
|
|
+ color: rgba(255, 255, 255, 0.8);
|
|
|
+ margin-bottom: 5px;
|
|
|
+}
|
|
|
+
|
|
|
+.amount-item .value {
|
|
|
+ font-size: 20px;
|
|
|
+ font-weight: bold;
|
|
|
+ color: #fff;
|
|
|
+}
|
|
|
+
|
|
|
+.amount-item .total {
|
|
|
+ color: #ffd700;
|
|
|
+}
|
|
|
+
|
|
|
+.amount-item .paid {
|
|
|
+ color: #67c23a;
|
|
|
+}
|
|
|
+
|
|
|
+.amount-item .bonus {
|
|
|
+ color: #409eff;
|
|
|
+}
|
|
|
+
|
|
|
+.info-card {
|
|
|
+ margin-bottom: 20px;
|
|
|
+}
|
|
|
+
|
|
|
+.info-card>>>.el-card__header {
|
|
|
+ background: #f5f7fa;
|
|
|
+ padding: 15px 20px;
|
|
|
+}
|
|
|
+
|
|
|
+.card-header {
|
|
|
+ font-size: 16px;
|
|
|
+ font-weight: bold;
|
|
|
+ color: #303133;
|
|
|
+}
|
|
|
+
|
|
|
+.card-header i {
|
|
|
+ margin-right: 8px;
|
|
|
+ color: #409eff;
|
|
|
+}
|
|
|
+
|
|
|
+.info-item {
|
|
|
+ padding: 10px 0;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+}
|
|
|
+
|
|
|
+.info-label {
|
|
|
+ font-size: 14px;
|
|
|
+ color: #606266;
|
|
|
+ min-width: 90px;
|
|
|
+ font-weight: 500;
|
|
|
+}
|
|
|
+
|
|
|
+.info-value {
|
|
|
+ font-size: 14px;
|
|
|
+ color: #303133;
|
|
|
+ flex: 1;
|
|
|
+}
|
|
|
+
|
|
|
+.info-value.highlight {
|
|
|
+ color: #409eff;
|
|
|
+ font-weight: bold;
|
|
|
+}
|
|
|
+
|
|
|
+.price-text {
|
|
|
+ color: #f56c6c;
|
|
|
+ font-weight: 500;
|
|
|
+}
|
|
|
+
|
|
|
+.subtotal-text {
|
|
|
+ color: #f56c6c;
|
|
|
+ font-weight: bold;
|
|
|
+ font-size: 15px;
|
|
|
+}
|
|
|
+
|
|
|
+/* 滚动条样式 */
|
|
|
+.order-detail-content::-webkit-scrollbar {
|
|
|
+ width: 6px;
|
|
|
+}
|
|
|
+
|
|
|
+.order-detail-content::-webkit-scrollbar-thumb {
|
|
|
+ background: #dcdfe6;
|
|
|
+ border-radius: 3px;
|
|
|
+}
|
|
|
+
|
|
|
+.order-detail-content::-webkit-scrollbar-thumb:hover {
|
|
|
+ background: #c0c4cc;
|
|
|
+}
|
|
|
+
|
|
|
+.order-detail-content::-webkit-scrollbar-track {
|
|
|
+ background: #f5f7fa;
|
|
|
+}
|
|
|
+</style>
|