引入订单数据
This commit is contained in:
@@ -38,7 +38,7 @@ import ProductLabel from '@/pages/mall/admin/product/labels/index.uvue'
|
|||||||
import ProductProtection from '@/pages/mall/admin/product/protection/index.uvue'
|
import ProductProtection from '@/pages/mall/admin/product/protection/index.uvue'
|
||||||
|
|
||||||
// --- 订单模块 ---
|
// --- 订单模块 ---
|
||||||
import OrderList from '@/pages/mall/admin/order/list.uvue'
|
import OrderList from '@/pages/mall/admin/order/order-management/index.uvue'
|
||||||
import OrderStatistic from '@/pages/mall/admin/order/order-statistics/index.uvue'
|
import OrderStatistic from '@/pages/mall/admin/order/order-statistics/index.uvue'
|
||||||
import OrderRefund from '@/pages/mall/admin/order/aftersales-order/index.uvue'
|
import OrderRefund from '@/pages/mall/admin/order/aftersales-order/index.uvue'
|
||||||
import OrderCashier from '@/pages/mall/admin/order/cashier-order/index.uvue'
|
import OrderCashier from '@/pages/mall/admin/order/cashier-order/index.uvue'
|
||||||
|
|||||||
@@ -79,7 +79,7 @@ export const topMenus: TopMenu[] = [
|
|||||||
id: 'order',
|
id: 'order',
|
||||||
title: '订单',
|
title: '订单',
|
||||||
icon: 'order',
|
icon: 'order',
|
||||||
path: '/pages/mall/admin/order/list',
|
path: '/pages/mall/admin/order/order-management/index',
|
||||||
order: 3,
|
order: 3,
|
||||||
groups: [
|
groups: [
|
||||||
{ id: 'order-manage', title: '', order: 1 }
|
{ id: 'order-manage', title: '', order: 1 }
|
||||||
@@ -400,7 +400,7 @@ export const routes: RouteRecord[] = [
|
|||||||
{
|
{
|
||||||
id: 'order_list',
|
id: 'order_list',
|
||||||
title: '订单管理',
|
title: '订单管理',
|
||||||
path: '/pages/mall/admin/order/list',
|
path: '/pages/mall/admin/order/order-management/index',
|
||||||
componentKey: 'OrderList',
|
componentKey: 'OrderList',
|
||||||
parentId: 'order',
|
parentId: 'order',
|
||||||
groupId: 'order-manage',
|
groupId: 'order-manage',
|
||||||
|
|||||||
11
pages.json
11
pages.json
@@ -452,22 +452,15 @@
|
|||||||
{
|
{
|
||||||
"root": "pages/mall/admin",
|
"root": "pages/mall/admin",
|
||||||
"pages": [
|
"pages": [
|
||||||
{
|
|
||||||
"path": "order-management",
|
|
||||||
"style": {
|
|
||||||
"navigationBarTitleText": "订单管理",
|
|
||||||
"navigationStyle": "custom"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"root": "pages/mall/admin/order",
|
"root": "pages/mall/admin/order",
|
||||||
"pages": [
|
"pages": [
|
||||||
{
|
{
|
||||||
"path": "list",
|
"path": "order-management/index",
|
||||||
"style": {
|
"style": {
|
||||||
"navigationBarTitleText": "订单列表",
|
"navigationBarTitleText": "订单管理",
|
||||||
"navigationStyle": "custom"
|
"navigationStyle": "custom"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,63 +0,0 @@
|
|||||||
<template>
|
|
||||||
<AdminLayout :currentPage="currentPage">
|
|
||||||
<view class="admin-page">
|
|
||||||
<view class="admin-sections">
|
|
||||||
<view class="admin-card Header">
|
|
||||||
<text class="Title">订单</text>
|
|
||||||
<text class="SubTitle">order-management</text>
|
|
||||||
</view>
|
|
||||||
|
|
||||||
<view class="admin-card Card">
|
|
||||||
<text class="Label">页面参数(query)</text>
|
|
||||||
<text class="Mono">{{ params }}</text>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
</AdminLayout>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup lang="uts">
|
|
||||||
import { ref } from 'vue'
|
|
||||||
import { onLoad } from '@dcloudio/uni-app'
|
|
||||||
import AdminLayout from '@/layouts/admin/AdminLayout.uvue'
|
|
||||||
|
|
||||||
const params = ref('')
|
|
||||||
const currentPage = ref('order-list')
|
|
||||||
|
|
||||||
onLoad((options: Record<string, string>) => {
|
|
||||||
params.value = JSON.stringify(options ?? {})
|
|
||||||
const tab = options['tab'] || ''
|
|
||||||
if (tab == 'stats') currentPage.value = 'order-stats'
|
|
||||||
else if (tab == 'aftersale') currentPage.value = 'order-aftersale'
|
|
||||||
else if (tab == 'cashier') currentPage.value = 'order-cashier'
|
|
||||||
else if (tab == 'verify') currentPage.value = 'order-verify'
|
|
||||||
else if (tab == 'config') currentPage.value = 'order-config'
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
.Header {
|
|
||||||
}
|
|
||||||
.Title {
|
|
||||||
font-size: 36rpx;
|
|
||||||
font-weight: 700;
|
|
||||||
}
|
|
||||||
.SubTitle {
|
|
||||||
margin-top: 8rpx;
|
|
||||||
font-size: 24rpx;
|
|
||||||
opacity: 0.7;
|
|
||||||
}
|
|
||||||
.Card {
|
|
||||||
}
|
|
||||||
.Label {
|
|
||||||
font-size: 26rpx;
|
|
||||||
font-weight: 600;
|
|
||||||
margin-bottom: 12rpx;
|
|
||||||
}
|
|
||||||
.Mono {
|
|
||||||
font-size: 24rpx;
|
|
||||||
font-family: monospace;
|
|
||||||
line-height: 36rpx;
|
|
||||||
word-break: break-all;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@@ -1,481 +0,0 @@
|
|||||||
<template>
|
|
||||||
<view class="admin-page">
|
|
||||||
<view class="admin-sections">
|
|
||||||
<!-- 筛选区域 -->
|
|
||||||
<view class="admin-card 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="请输入" />
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
<view class="btn-row">
|
|
||||||
<button class="btn btn-primary">查询</button>
|
|
||||||
<button class="btn btn-default">重置</button>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
|
|
||||||
<!-- 列表数据区域 -->
|
|
||||||
<view class="admin-card content-card">
|
|
||||||
<!-- 状态 Tabs -->
|
|
||||||
<view class="status-tabs">
|
|
||||||
<view
|
|
||||||
v-for="(tab, index) in statusTabs"
|
|
||||||
:key="index"
|
|
||||||
class="tab-item"
|
|
||||||
:class="{ active: activeTab === index }"
|
|
||||||
@click="activeTab = index"
|
|
||||||
>
|
|
||||||
<text class="tab-text">{{ tab.name }}</text>
|
|
||||||
<text v-if="tab.count" class="tab-count">({{ tab.count }})</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-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.sn }}</text>
|
|
||||||
<text class="order-type" :class="item.typeColor">[{{ item.typeName }}]</text>
|
|
||||||
<text v-if="item.cancelStatus" class="cancel-text">{{ item.cancelStatus }}</text>
|
|
||||||
</view>
|
|
||||||
<!-- 商品信息 -->
|
|
||||||
<view class="td col-product">
|
|
||||||
<view class="product-info-wrap">
|
|
||||||
<image class="p-img" :src="item.product.img" mode="aspectFill" />
|
|
||||||
<text class="p-name">{{ item.product.name }}</text>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
<!-- 用户信息 -->
|
|
||||||
<view class="td col-user">
|
|
||||||
<text class="u-info">{{ item.user.phone }} | {{ item.user.id }}</text>
|
|
||||||
</view>
|
|
||||||
<!-- 实际支付 -->
|
|
||||||
<view class="td col-price">
|
|
||||||
<text class="price-val">{{ item.actualPrice }}</text>
|
|
||||||
</view>
|
|
||||||
<!-- 支付方式 -->
|
|
||||||
<view class="td col-pay">
|
|
||||||
<text class="pay-text">{{ item.payMethod }}</text>
|
|
||||||
</view>
|
|
||||||
<!-- 支付时间 -->
|
|
||||||
<view class="td col-time">
|
|
||||||
<text class="time-text">{{ item.payTime }}</text>
|
|
||||||
</view>
|
|
||||||
<!-- 订单状态 -->
|
|
||||||
<view class="td col-status">
|
|
||||||
<text class="status-text">{{ item.statusName }}</text>
|
|
||||||
</view>
|
|
||||||
<!-- 操作 -->
|
|
||||||
<view class="td col-op">
|
|
||||||
<view class="op-links">
|
|
||||||
<text class="op-link primary" v-if="item.primaryAction">{{ item.primaryAction }}</text>
|
|
||||||
<view class="op-link-more">
|
|
||||||
<text class="more-text">更多</text>
|
|
||||||
<view class="arrow-down-blue"></view>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup lang="uts">
|
|
||||||
import { ref } from 'vue'
|
|
||||||
|
|
||||||
const activeTab = ref(2) // 默认选中待发货或待核销(手动对齐截图)
|
|
||||||
|
|
||||||
const statusTabs = [
|
|
||||||
{ name: '全部', count: null },
|
|
||||||
{ name: '待支付', count: 793 },
|
|
||||||
{ name: '待发货', count: 3695 },
|
|
||||||
{ name: '待核销', count: null },
|
|
||||||
{ name: '待收货', count: null },
|
|
||||||
{ name: '待评价', count: null },
|
|
||||||
{ name: '已完成', count: null },
|
|
||||||
{ name: '已退款', count: null },
|
|
||||||
{ name: '已删除', count: null }
|
|
||||||
]
|
|
||||||
|
|
||||||
const orderData = ref([
|
|
||||||
{
|
|
||||||
sn: 'cp541336970228400128',
|
|
||||||
typeName: '秒杀订单',
|
|
||||||
typeColor: 'blue',
|
|
||||||
cancelStatus: '用户已取消',
|
|
||||||
product: {
|
|
||||||
img: '/static/logo.png',
|
|
||||||
name: '爱奇艺智能 奇遇LT01 投影仪 家用卧室 超高清手机便携投影机 (4K超清 支持...'
|
|
||||||
},
|
|
||||||
user: { phone: '188****4074', id: '82694' },
|
|
||||||
actualPrice: '未支付',
|
|
||||||
payMethod: '--',
|
|
||||||
payTime: '--',
|
|
||||||
statusName: '未支付',
|
|
||||||
primaryAction: ''
|
|
||||||
},
|
|
||||||
{
|
|
||||||
sn: 'cp541289248708362240',
|
|
||||||
typeName: '核销订单',
|
|
||||||
typeColor: 'purple',
|
|
||||||
cancelStatus: '',
|
|
||||||
product: {
|
|
||||||
img: '/static/logo.png',
|
|
||||||
name: '阿迪达斯官网 adidas BBALL CAP COT 男女训练运动帽子FQ5270 传奇墨水...'
|
|
||||||
},
|
|
||||||
user: { phone: '你就给', id: '82703' },
|
|
||||||
actualPrice: '90.1',
|
|
||||||
payMethod: '余额支付',
|
|
||||||
payTime: '2026-02-02 16:10:17',
|
|
||||||
statusName: '未核销',
|
|
||||||
primaryAction: '立即核销'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
sn: 'cp541268226856714240',
|
|
||||||
typeName: '普通订单',
|
|
||||||
typeColor: 'green',
|
|
||||||
cancelStatus: '',
|
|
||||||
product: {
|
|
||||||
img: '/static/logo.png',
|
|
||||||
name: 'UR2024夏季新款女装复古纯欲氛围感一字肩短款T恤衫UWG440060'
|
|
||||||
},
|
|
||||||
user: { phone: '王毅不睡了', id: '82689' },
|
|
||||||
actualPrice: '未支付',
|
|
||||||
payMethod: '--',
|
|
||||||
payTime: '--',
|
|
||||||
statusName: '未支付',
|
|
||||||
primaryAction: '编辑'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
sn: 'cp541262080745930752',
|
|
||||||
typeName: '秒杀订单',
|
|
||||||
typeColor: 'blue',
|
|
||||||
cancelStatus: '',
|
|
||||||
product: {
|
|
||||||
img: '/static/logo.png',
|
|
||||||
name: '爱奇艺智能 奇遇LT01 投影仪 家用卧室 超高清手机便携投影机 (4K超清 支持...'
|
|
||||||
},
|
|
||||||
user: { phone: '177****8361', id: '82697' },
|
|
||||||
actualPrice: '未支付',
|
|
||||||
payMethod: '--',
|
|
||||||
payTime: '--',
|
|
||||||
statusName: '未支付',
|
|
||||||
primaryAction: '编辑'
|
|
||||||
}
|
|
||||||
])
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style scoped lang="scss">
|
|
||||||
.admin-page {
|
|
||||||
/* 使用 Layout 的背景和内边距 */
|
|
||||||
min-height: 100vh;
|
|
||||||
}
|
|
||||||
|
|
||||||
.filter-card {
|
|
||||||
padding: var(--admin-card-padding);
|
|
||||||
}
|
|
||||||
|
|
||||||
.filter-row {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
gap: 24px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.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 {
|
|
||||||
}
|
|
||||||
|
|
||||||
.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%;
|
|
||||||
overflow-x: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.thead {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
background-color: #f0f7ff;
|
|
||||||
min-width: 1200px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.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;
|
|
||||||
min-width: 1200px;
|
|
||||||
&: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;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@@ -0,0 +1,414 @@
|
|||||||
|
<template>
|
||||||
|
<view v-if="visible" class="drawer-mask" @click="close">
|
||||||
|
<view class="drawer-container" @click.stop>
|
||||||
|
<view class="drawer-header">
|
||||||
|
<view class="header-left">
|
||||||
|
<text class="title">订单详情</text>
|
||||||
|
</view>
|
||||||
|
<view class="close-btn" @click="close">
|
||||||
|
<text class="close-icon">×</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<scroll-view class="drawer-body" scroll-y>
|
||||||
|
<!-- 订单概况 KPIS -->
|
||||||
|
<view class="order-summary-card">
|
||||||
|
<view class="order-type-icon">
|
||||||
|
<image src="/static/icons/order_blue.png" mode="aspectFit" class="type-icon" />
|
||||||
|
</view>
|
||||||
|
<view class="summary-info">
|
||||||
|
<view class="top-row">
|
||||||
|
<text class="order-type-text">{{ orderInfo['typeName'] || '普通订单' }}</text>
|
||||||
|
<text class="order-sn-text">订单号:{{ orderInfo['sn'] }}</text>
|
||||||
|
<text class="shop-tag" v-if="orderInfo['store_name'] != '--'">{{ orderInfo['store_name'] }}</text>
|
||||||
|
</view>
|
||||||
|
<view class="bottom-grids">
|
||||||
|
<view class="summary-grid">
|
||||||
|
<text class="label">订单状态</text>
|
||||||
|
<text class="value status-val">{{ orderInfo['statusName'] }}</text>
|
||||||
|
</view>
|
||||||
|
<view class="summary-grid">
|
||||||
|
<text class="label">总金额</text>
|
||||||
|
<text class="value price-val">¥ {{ orderInfo['actualPrice'] }}</text>
|
||||||
|
</view>
|
||||||
|
<view class="summary-grid">
|
||||||
|
<text class="label">已支付</text>
|
||||||
|
<text class="value status-val">¥ {{ orderInfo['paidAmount'] }}</text>
|
||||||
|
</view>
|
||||||
|
<view class="summary-grid">
|
||||||
|
<text class="label">支付方式</text>
|
||||||
|
<text class="value">{{ orderInfo['payMethod'] }}</text>
|
||||||
|
</view>
|
||||||
|
<view class="summary-grid">
|
||||||
|
<text class="label">配送人员</text>
|
||||||
|
<text class="value">{{ orderInfo['delivery_name'] }}</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- Tabs -->
|
||||||
|
<view class="drawer-tabs">
|
||||||
|
<view v-for="(tab, index) in tabs" :key="index" class="tab-item" :class="{ active: activeTab === index }" @click="activeTab = index">
|
||||||
|
<text class="tab-text">{{ tab }}</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- Tab Content -->
|
||||||
|
<view class="tab-content">
|
||||||
|
<!-- 订单信息 -->
|
||||||
|
<view v-if="activeTab === 0" class="info-section">
|
||||||
|
<view class="section-block">
|
||||||
|
<view class="section-title">
|
||||||
|
<view class="blue-bar"></view>
|
||||||
|
<text>用户信息</text>
|
||||||
|
</view>
|
||||||
|
<view class="info-grid">
|
||||||
|
<view class="info-item">
|
||||||
|
<text class="label">用户名称:</text>
|
||||||
|
<text class="value">{{ orderInfo['user']['name'] }}</text>
|
||||||
|
</view>
|
||||||
|
<view class="info-item">
|
||||||
|
<text class="label">绑定电话:</text>
|
||||||
|
<text class="value">{{ orderInfo['user']['phone'] }}</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="section-block">
|
||||||
|
<view class="section-title">
|
||||||
|
<view class="blue-bar"></view>
|
||||||
|
<text>收货信息</text>
|
||||||
|
</view>
|
||||||
|
<view class="info-grid">
|
||||||
|
<view class="info-item">
|
||||||
|
<text class="label">收货人:</text>
|
||||||
|
<text class="value">{{ orderInfo['real_name'] || orderInfo['user']['name'] }}</text>
|
||||||
|
</view>
|
||||||
|
<view class="info-item">
|
||||||
|
<text class="label">收货电话:</text>
|
||||||
|
<text class="value">{{ orderInfo['user_phone'] || orderInfo['user']['phone'] }}</text>
|
||||||
|
</view>
|
||||||
|
<view class="info-item full">
|
||||||
|
<text class="label">收货地址:</text>
|
||||||
|
<text class="value">{{ orderInfo['user_address'] || '暂无地址信息' }}</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="section-block">
|
||||||
|
<view class="section-title">
|
||||||
|
<view class="blue-bar"></view>
|
||||||
|
<text>订单信息</text>
|
||||||
|
</view>
|
||||||
|
<view class="info-grid">
|
||||||
|
<view class="info-item">
|
||||||
|
<text class="label">创建时间:</text>
|
||||||
|
<text class="value">{{ orderInfo['created_at'] || '--' }}</text>
|
||||||
|
</view>
|
||||||
|
<view class="info-item">
|
||||||
|
<text class="label">商品总数:</text>
|
||||||
|
<text class="value">{{ orderInfo['total_num'] || '0' }}</text>
|
||||||
|
</view>
|
||||||
|
<view class="info-item">
|
||||||
|
<text class="label">产品金额:</text>
|
||||||
|
<text class="value">¥ {{ orderInfo['total_price'] || '0.00' }}</text>
|
||||||
|
</view>
|
||||||
|
<view class="info-item">
|
||||||
|
<text class="label">运费:</text>
|
||||||
|
<text class="value">¥ {{ orderInfo['shipping_fee'] || '0.00' }}</text>
|
||||||
|
</view>
|
||||||
|
<view class="info-item">
|
||||||
|
<text class="label">折扣金额:</text>
|
||||||
|
<text class="value">- ¥ {{ orderInfo['coupon_price'] || '0.00' }}</text>
|
||||||
|
</view>
|
||||||
|
<view class="info-item">
|
||||||
|
<text class="label">总金额:</text>
|
||||||
|
<text class="value">¥ {{ orderInfo['actualPrice'] }}</text>
|
||||||
|
</view>
|
||||||
|
<view class="info-item">
|
||||||
|
<text class="label">支付金额:</text>
|
||||||
|
<text class="value price-red">¥ {{ orderInfo['paidAmount'] }}</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="section-block">
|
||||||
|
<view class="section-title">
|
||||||
|
<view class="blue-bar"></view>
|
||||||
|
<text>买家留言</text>
|
||||||
|
</view>
|
||||||
|
<view class="info-grid">
|
||||||
|
<view class="info-item full">
|
||||||
|
<text class="value">{{ orderInfo['mark'] || '-' }}</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="section-block">
|
||||||
|
<view class="section-title">
|
||||||
|
<view class="blue-bar"></view>
|
||||||
|
<text>订单备注</text>
|
||||||
|
</view>
|
||||||
|
<view class="info-grid">
|
||||||
|
<view class="info-item full">
|
||||||
|
<text class="value">{{ orderInfo['remark'] || '-' }}</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 商品信息 -->
|
||||||
|
<view v-if="activeTab === 1" class="info-section">
|
||||||
|
<view class="product-table">
|
||||||
|
<view class="product-thead">
|
||||||
|
<text class="p-th p-info">商品信息</text>
|
||||||
|
<text class="p-th p-sku">规格</text>
|
||||||
|
<text class="p-th p-price">单价</text>
|
||||||
|
<text class="p-th p-num">数量</text>
|
||||||
|
<text class="p-th p-total">小计</text>
|
||||||
|
</view>
|
||||||
|
<view class="product-tbody">
|
||||||
|
<view v-for="(p, pi) in productItems" :key="pi" class="p-tr">
|
||||||
|
<view class="p-td p-info">
|
||||||
|
<image :src="p['image'] || '/static/logo.png'" mode="aspectFill" class="p-img" />
|
||||||
|
<text class="p-name">{{ p['name'] }}</text>
|
||||||
|
</view>
|
||||||
|
<view class="p-td p-sku">
|
||||||
|
<text class="p-sku-txt">{{ p['sku_info'] || '-' }}</text>
|
||||||
|
</view>
|
||||||
|
<view class="p-td p-price">¥{{ p['price'] }}</view>
|
||||||
|
<view class="p-td p-num">{{ p['quantity'] }}</view>
|
||||||
|
<view class="p-td p-total">¥{{ (parseFloat(p['price'] as string) * parseInt(p['quantity'] as string)).toFixed(2) }}</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 订单记录 -->
|
||||||
|
<view v-if="activeTab === 2" class="info-section">
|
||||||
|
<view class="timeline">
|
||||||
|
<view v-for="(log, li) in logs" :key="li" class="timeline-item">
|
||||||
|
<view class="dot"></view>
|
||||||
|
<view class="line" v-if="li !== logs.length - 1"></view>
|
||||||
|
<view class="log-content">
|
||||||
|
<text class="log-title">{{ log.title }}</text>
|
||||||
|
<text class="log-time">{{ log.time }}</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</scroll-view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="uts">
|
||||||
|
import { ref, computed, watch } from 'vue'
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
visible: { type: Boolean, default: false },
|
||||||
|
orderInfo: { type: Object, default: () : UTSJSONObject => ({}) as UTSJSONObject }
|
||||||
|
})
|
||||||
|
|
||||||
|
const emit = defineEmits(['update:visible'])
|
||||||
|
|
||||||
|
const activeTab = ref(0)
|
||||||
|
const tabs = ['订单信息', '商品信息', '订单记录']
|
||||||
|
|
||||||
|
const productItems = computed<UTSJSONObject[]>(() => {
|
||||||
|
return (props.orderInfo['items'] || []) as UTSJSONObject[]
|
||||||
|
})
|
||||||
|
|
||||||
|
const logs = ref([
|
||||||
|
{ title: '订单生成', time: '2026-02-27 15:47:25' },
|
||||||
|
{ title: '支付成功', time: '2026-02-27 15:48:30' }
|
||||||
|
])
|
||||||
|
|
||||||
|
const close = () => {
|
||||||
|
emit('update:visible', false)
|
||||||
|
}
|
||||||
|
|
||||||
|
watch(() => props.visible, (newVal) => {
|
||||||
|
if (newVal) {
|
||||||
|
activeTab.value = 0
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.drawer-mask {
|
||||||
|
position: fixed;
|
||||||
|
top: 0; left: 0; right: 0; bottom: 0;
|
||||||
|
background-color: rgba(0,0,0,0.5);
|
||||||
|
z-index: 2000;
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
}
|
||||||
|
|
||||||
|
.drawer-container {
|
||||||
|
width: 800px;
|
||||||
|
max-width: 90%;
|
||||||
|
height: 100vh;
|
||||||
|
background-color: #fff;
|
||||||
|
box-shadow: -2px 0 8px rgba(0,0,0,0.1);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.drawer-header {
|
||||||
|
height: 56px;
|
||||||
|
padding: 0 24px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
border-bottom: 1px solid #f0f0f0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title { font-size: 16px; font-weight: 600; color: #333; }
|
||||||
|
.close-btn {
|
||||||
|
width: 32px; height: 32px;
|
||||||
|
display: flex; align-items: center; justify-content: center;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
.close-icon { font-size: 24px; color: #999; line-height: 1; }
|
||||||
|
|
||||||
|
.drawer-body {
|
||||||
|
flex: 1;
|
||||||
|
background-color: #f5f7f9;
|
||||||
|
}
|
||||||
|
|
||||||
|
.order-summary-card {
|
||||||
|
background-color: #fff;
|
||||||
|
padding: 24px;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
gap: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.type-icon { width: 48px; height: 48px; }
|
||||||
|
|
||||||
|
.summary-info {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.top-row {
|
||||||
|
margin-bottom: 16px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
gap: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.order-type-text { font-size: 16px; font-weight: 600; color: #333; }
|
||||||
|
.order-sn-text { font-size: 14px; color: #666; }
|
||||||
|
.shop-tag {
|
||||||
|
font-size: 12px; color: #1890ff; background: #e6f7ff;
|
||||||
|
border: 1px solid #91d5ff; padding: 2px 8px; border-radius: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bottom-grids {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
gap: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.summary-grid {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 4px;
|
||||||
|
.label { font-size: 12px; color: #999; }
|
||||||
|
.value { font-size: 14px; color: #333; }
|
||||||
|
.status-val { font-weight: 600; }
|
||||||
|
.price-val { color: #f5222d; font-weight: 600; }
|
||||||
|
}
|
||||||
|
|
||||||
|
.drawer-tabs {
|
||||||
|
background-color: #fff;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
border-bottom: 1px solid #f0f0f0;
|
||||||
|
padding: 0 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-item {
|
||||||
|
padding: 12px 20px;
|
||||||
|
margin-right: 12px;
|
||||||
|
position: relative;
|
||||||
|
cursor: pointer;
|
||||||
|
.tab-text { font-size: 14px; color: #595959; }
|
||||||
|
&.active {
|
||||||
|
.tab-text { color: #1890ff; font-weight: 500; }
|
||||||
|
&::after {
|
||||||
|
content: '';
|
||||||
|
position: absolute; bottom: 0; left: 0; right: 0; height: 2px; background: #1890ff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-content {
|
||||||
|
padding: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-block {
|
||||||
|
background-color: #fff;
|
||||||
|
padding: 24px;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-title {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
text { font-size: 14px; font-weight: 600; color: #333; }
|
||||||
|
}
|
||||||
|
|
||||||
|
.blue-bar { width: 3px; height: 14px; background-color: #1890ff; }
|
||||||
|
|
||||||
|
.info-grid {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: y;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-item {
|
||||||
|
width: 33.33%;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
.label { font-size: 13px; color: #666; width: 80px; flex-shrink: 0; }
|
||||||
|
.value { font-size: 13px; color: #333; line-height: 1.4; word-break: break-all; }
|
||||||
|
.price-red { color: #f5222d; font-weight: 600; }
|
||||||
|
&.full { width: 100%; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 商品表格 */
|
||||||
|
.product-table { padding: 8px; background-color: #fff; }
|
||||||
|
.product-thead { display: flex; flex-direction: row; background-color: #fafafa; border-bottom: 1px solid #f0f0f0; }
|
||||||
|
.p-th { padding: 12px 8px; font-size: 13px; font-weight: 500; color: #333; text-align: left; }
|
||||||
|
.p-tr { display: flex; flex-direction: row; border-bottom: 1px solid #f0f0f0; }
|
||||||
|
.p-td { padding: 12px 8px; font-size: 13px; color: #595959; display: flex; align-items: center; }
|
||||||
|
|
||||||
|
.p-info { flex: 1; display: flex; flex-direction: row; align-items: center; gap: 8px; }
|
||||||
|
.p-img { width: 40px; height: 40px; border-radius: 4px; }
|
||||||
|
.p-sku { width: 120px; }
|
||||||
|
.p-price { width: 100px; }
|
||||||
|
.p-num { width: 80px; }
|
||||||
|
.p-total { width: 100px; }
|
||||||
|
|
||||||
|
/* 记录 */
|
||||||
|
.timeline { padding: 24px; background-color: #fff; }
|
||||||
|
.timeline-item { position: relative; padding-left: 24px; padding-bottom: 24px; }
|
||||||
|
.dot { position: absolute; left: 0; top: 4px; width: 10px; height: 10px; border-radius: 5px; background-color: #1890ff; z-index: 2; }
|
||||||
|
.line { position: absolute; left: 4.5px; top: 14px; bottom: -4px; width: 1px; background-color: #e8e8e8; }
|
||||||
|
.log-content { display: flex; flex-direction: column; gap: 4px; }
|
||||||
|
.log-title { font-size: 14px; color: #333; }
|
||||||
|
.log-time { font-size: 12px; color: #999; }
|
||||||
|
</style>
|
||||||
@@ -1,25 +1,828 @@
|
|||||||
<template>
|
<template>
|
||||||
<AdminLayout :currentPage="currentPage">
|
<view class="admin-page">
|
||||||
<view class="page">
|
<view class="admin-sections" @click="closeDropdowns">
|
||||||
<view class="header">
|
<!-- 筛选区域 -->
|
||||||
<text class="title">{{ title }}</text>
|
<view class="admin-card filter-card">
|
||||||
<text class="sub-title">页面已修复 (UTF-8)</text>
|
<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>
|
</view>
|
||||||
</AdminLayout>
|
<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" v-model="searchKeyword" placeholder="请输入订单号/手机号" />
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view class="btn-row">
|
||||||
|
<button class="btn btn-primary" @click="fetchData">查询</button>
|
||||||
|
<button class="btn btn-default" @click="resetQuery">重置</button>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 列表数据区域 -->
|
||||||
|
<view class="admin-card content-card">
|
||||||
|
<!-- 状态 Tabs -->
|
||||||
|
<view class="status-tabs">
|
||||||
|
<view
|
||||||
|
v-for="(tab, index) in statusTabs"
|
||||||
|
:key="index"
|
||||||
|
class="tab-item"
|
||||||
|
:class="{ active: activeTab === index }"
|
||||||
|
@click="handleTabClick(index)"
|
||||||
|
>
|
||||||
|
<text class="tab-text">{{ tab.name }}</text>
|
||||||
|
<text v-if="tab.count !== null" class="tab-count">({{ tab.count }})</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-expand"></view>
|
||||||
|
<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">
|
||||||
|
<!-- Loading 状态 -->
|
||||||
|
<view v-if="loading" class="loading-state">
|
||||||
|
<text>加载中...</text>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 无数据状态 -->
|
||||||
|
<view v-else-if="filteredOrders.length === 0" class="empty-state">
|
||||||
|
<text>暂无订单数据</text>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view v-for="(item, index) in filteredOrders" :key="index" class="tr">
|
||||||
|
<view class="td col-expand">
|
||||||
|
<text class="expand-arrow">›</text>
|
||||||
|
</view>
|
||||||
|
<view class="td col-check">
|
||||||
|
<checkbox :checked="false" color="#1890ff" />
|
||||||
|
</view>
|
||||||
|
<!-- 订单号|类型 -->
|
||||||
|
<view class="td col-order">
|
||||||
|
<text class="order-sn">{{ item['sn'] }}</text>
|
||||||
|
<text class="order-type" :class="item['typeColor']">[{{ item['typeName'] }}]</text>
|
||||||
|
<text v-if="item['cancelStatus'] != ''" class="cancel-text">{{ item['cancelStatus'] }}</text>
|
||||||
|
</view>
|
||||||
|
<!-- 商品信息 -->
|
||||||
|
<view class="td col-product">
|
||||||
|
<view class="product-info-list" v-if="item['items'] != null">
|
||||||
|
<view v-for="(prod, pidx) in (item['items'] as UTSJSONObject[])" :key="pidx" class="product-info-wrap">
|
||||||
|
<image class="p-img" :src="prod['image'] != null ? (prod['image'] as string) : '/static/logo.png'" mode="aspectFill" />
|
||||||
|
<text class="p-name">{{ prod['name'] }}</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view v-else class="product-info-wrap">
|
||||||
|
<image class="p-img" :src="item['product']['img']" mode="aspectFill" />
|
||||||
|
<text class="p-name">{{ item['product']['name'] }}</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<!-- 用户信息 -->
|
||||||
|
<view class="td col-user">
|
||||||
|
<text class="u-info">{{ item['user']['name'] }} | {{ item['user']['id'] || '--' }}</text>
|
||||||
|
</view>
|
||||||
|
<!-- 实际支付 -->
|
||||||
|
<view class="td col-price">
|
||||||
|
<text class="price-val">{{ item['isPaid'] === true ? '¥' + item['actualPrice'] : '未支付' }}</text>
|
||||||
|
</view>
|
||||||
|
<!-- 支付方式 -->
|
||||||
|
<view class="td col-pay">
|
||||||
|
<text class="pay-text">{{ item['payMethod'] }}</text>
|
||||||
|
</view>
|
||||||
|
<!-- 支付时间 -->
|
||||||
|
<view class="td col-time">
|
||||||
|
<text class="time-text">{{ item['payTime'] }}</text>
|
||||||
|
</view>
|
||||||
|
<!-- 订单状态 -->
|
||||||
|
<view class="td col-status">
|
||||||
|
<text class="status-text">{{ item['statusName'] }}</text>
|
||||||
|
</view>
|
||||||
|
<!-- 操作 -->
|
||||||
|
<view class="td col-op overflow-visible no-wrap">
|
||||||
|
<view class="op-links">
|
||||||
|
<text class="op-link primary" @click.stop="handleAction('edit', item['sn'] as string)">编辑</text>
|
||||||
|
<view class="op-dropdown-container">
|
||||||
|
<view class="op-link-more" @click.stop="toggleDropdown(item['sn'] as string)">
|
||||||
|
<text :class="{ 'more-text-active': activeDropdownId === item['sn'] }" class="more-text">更多</text>
|
||||||
|
<view :class="{ 'arrow-up-blue': activeDropdownId === item['sn'], 'arrow-down-blue': activeDropdownId !== item['sn'] }"></view>
|
||||||
|
</view>
|
||||||
|
<!-- 浮动菜单 -->
|
||||||
|
<view v-if="activeDropdownId === (item['sn'] as string)" class="dropdown-menu">
|
||||||
|
<view class="dropdown-item" @click.stop="viewDetail(item)">
|
||||||
|
<text class="item-text">订单详情</text>
|
||||||
|
</view>
|
||||||
|
<view class="dropdown-item danger" @click.stop="deleteOrder(item)">
|
||||||
|
<text class="item-text text-red">删除订单</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 分页 -->
|
||||||
|
<view class="pagination-footer">
|
||||||
|
<view class="page-left">
|
||||||
|
<text class="count-text">共 {{ orderData.length }} 条</text>
|
||||||
|
<view class="page-size-select">
|
||||||
|
<text>10条/页</text>
|
||||||
|
<view class="arrow-down"></view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view class="page-right">
|
||||||
|
<view class="page-btn disabled"><text>‹</text></view>
|
||||||
|
<view class="page-num active"><text>1</text></view>
|
||||||
|
<view class="page-num"><text>2</text></view>
|
||||||
|
<view class="page-num"><text>3</text></view>
|
||||||
|
<view class="page-btns-more"><text>...</text></view>
|
||||||
|
<view class="page-num"><text>10</text></view>
|
||||||
|
<view class="page-btn"><text>›</text></view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 抽屉详情 -->
|
||||||
|
<OrderDetailDrawer
|
||||||
|
v-model:visible="showDetail"
|
||||||
|
:order-info="selectedOrder"
|
||||||
|
/>
|
||||||
|
</view>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="uts">
|
<script setup lang="uts">
|
||||||
import { ref } from 'vue'
|
import { ref, computed, onMounted } from 'vue'
|
||||||
import AdminLayout from '@/layouts/admin/AdminLayout.uvue'
|
import { supabase } from '@/components/supadb/aksupainstance.uts'
|
||||||
const currentPage = ref<string>('order-list')
|
import OrderDetailDrawer from './components/OrderDetailDrawer.uvue'
|
||||||
const title = ref<string>('index')
|
|
||||||
|
const activeTab = ref(0)
|
||||||
|
const loading = ref(false)
|
||||||
|
const searchKeyword = ref('')
|
||||||
|
|
||||||
|
// Dropdown 状态
|
||||||
|
const activeDropdownId = ref('')
|
||||||
|
const showDetail = ref(false)
|
||||||
|
const selectedOrder = ref<UTSJSONObject>({} as UTSJSONObject)
|
||||||
|
|
||||||
|
// 时间显示格式化
|
||||||
|
const formatTime = (timeStr : string | null) : string => {
|
||||||
|
if (timeStr == null || timeStr == '' || timeStr == '--') return '--'
|
||||||
|
if (timeStr.indexOf('T') > -1) {
|
||||||
|
const parts = timeStr.split('T')
|
||||||
|
const date = parts[0]
|
||||||
|
const timeFull = parts[1]
|
||||||
|
const time = timeFull.split('.')[0]
|
||||||
|
return date + ' ' + time
|
||||||
|
}
|
||||||
|
return timeStr
|
||||||
|
}
|
||||||
|
|
||||||
|
const statusTabs = [
|
||||||
|
{ name: '全部', count: null, status: 0 },
|
||||||
|
{ name: '待支付', count: null, status: 1 },
|
||||||
|
{ name: '待发货', count: null, status: 2 },
|
||||||
|
{ name: '待收货', count: null, status: 3 },
|
||||||
|
{ name: '已完成', count: null, status: 4 },
|
||||||
|
{ name: '已取消', count: null, status: 5 },
|
||||||
|
{ name: '退款中', count: null, status: 6 },
|
||||||
|
{ name: '已退款', count: null, status: 7 }
|
||||||
|
]
|
||||||
|
|
||||||
|
const orderData = ref<UTSJSONObject[]>([])
|
||||||
|
|
||||||
|
const filteredOrders = computed<UTSJSONObject[]>(() => {
|
||||||
|
let list = orderData.value
|
||||||
|
|
||||||
|
// Tab 过滤
|
||||||
|
if (activeTab.value > 0) {
|
||||||
|
const targetStatus = statusTabs[activeTab.value].status
|
||||||
|
list = list.filter((o : UTSJSONObject) : boolean => (o['orderStatus'] as number) === targetStatus)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 搜索过滤
|
||||||
|
if (searchKeyword.value.trim() !== '') {
|
||||||
|
const kw = searchKeyword.value.toLowerCase()
|
||||||
|
list = list.filter((o : UTSJSONObject) : boolean => {
|
||||||
|
const sn = (o['sn'] as string).toLowerCase()
|
||||||
|
const user = o['user'] as UTSJSONObject
|
||||||
|
const phone = user['phone'] as string
|
||||||
|
return sn.includes(kw) || phone.includes(kw)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return list
|
||||||
|
})
|
||||||
|
|
||||||
|
const fetchData = async () => {
|
||||||
|
loading.value = true
|
||||||
|
try {
|
||||||
|
// 从 ml_orders_detail_view 视图读取数据
|
||||||
|
const res = await supabase
|
||||||
|
.from('ml_orders_detail_view')
|
||||||
|
.select('*')
|
||||||
|
.order('created_at', { ascending: false })
|
||||||
|
.execute()
|
||||||
|
|
||||||
|
if (res.error == null && res.data != null) {
|
||||||
|
const rawData = res.data as UTSJSONObject[]
|
||||||
|
orderData.value = rawData.map((item: UTSJSONObject) : UTSJSONObject => {
|
||||||
|
return {
|
||||||
|
sn: item['order_no'],
|
||||||
|
typeName: '普通订单',
|
||||||
|
typeColor: 'green',
|
||||||
|
orderStatus: item['order_status'],
|
||||||
|
cancelStatus: item['order_status'] === 5 ? '用户已取消' : '',
|
||||||
|
product: {
|
||||||
|
img: '/static/logo.png', // 默认占位图
|
||||||
|
name: '订单概要 (详情查看)'
|
||||||
|
} as UTSJSONObject,
|
||||||
|
items: item['order_items'] as UTSJSONObject[], // 尝试获取子项
|
||||||
|
user: {
|
||||||
|
name: (item['customer_name'] || '未知用户') as string,
|
||||||
|
id: (item['user_id'] || '--') as string,
|
||||||
|
phone: (item['customer_phone'] || '--') as string
|
||||||
|
} as UTSJSONObject,
|
||||||
|
isPaid: item['payment_status'] === 2,
|
||||||
|
actualPrice: item['total_amount'], // 总金额
|
||||||
|
paidAmount: item['paid_amount'] != null ? item['paid_amount'] : 0.00, // 支付金额
|
||||||
|
payMethod: item['payment_status_name'] || '--',
|
||||||
|
payTime: formatTime(item['paid_at'] as string | null),
|
||||||
|
created_at: formatTime(item['created_at'] as string | null),
|
||||||
|
statusName: item['order_status_name'] || '未知',
|
||||||
|
primaryAction: item['order_status'] === 1 ? '立即支付' : '',
|
||||||
|
total_num: Array.isArray(item['order_items']) ? (item['order_items'] as Array<any>).length : 0,
|
||||||
|
total_price: item['product_amount'] != null ? item['product_amount'] : 0.00, // 产品价格
|
||||||
|
coupon_price: item['discount_amount'] != null ? item['discount_amount'] : 0.00, // 折扣价
|
||||||
|
shipping_fee: item['shipping_fee'] != null ? item['shipping_fee'] : 0.00, // 运费
|
||||||
|
deduction_price: 0,
|
||||||
|
user_address: item['shipping_address'] != null ? (typeof item['shipping_address'] === 'string' ? item['shipping_address'] : JSON.stringify(item['shipping_address'])) : '暂无地址信息',
|
||||||
|
real_name: item['customer_name'] != null ? item['customer_name'] : '', // 消费者名字
|
||||||
|
user_phone: item['customer_phone'] != null ? item['customer_phone'] : '',
|
||||||
|
delivery_name: item['merchant_name'] != null ? item['merchant_name'] : '--', // 配送人员
|
||||||
|
store_name: item['shop_name'] != null ? item['shop_name'] : '--', // 店铺名字
|
||||||
|
mark: item['remark'] != null ? item['remark'] : '-',
|
||||||
|
remark: item['merchant_memo'] != null ? item['merchant_memo'] : '-'
|
||||||
|
} as UTSJSONObject
|
||||||
|
})
|
||||||
|
|
||||||
|
// 更新统计数据
|
||||||
|
updateTabCounts()
|
||||||
|
} else {
|
||||||
|
console.error('Fetch orders error:', res.error)
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error('Fetch orders exception:', e)
|
||||||
|
} finally {
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const updateTabCounts = () => {
|
||||||
|
statusTabs.forEach((tab, index) => {
|
||||||
|
if (index === 0) {
|
||||||
|
tab.count = orderData.value.length
|
||||||
|
} else {
|
||||||
|
tab.count = orderData.value.filter((o : UTSJSONObject) : boolean => (o['orderStatus'] as number) === tab.status).length
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleTabClick = (index: number) => {
|
||||||
|
activeTab.value = index
|
||||||
|
}
|
||||||
|
|
||||||
|
const resetQuery = () => {
|
||||||
|
searchKeyword.value = ''
|
||||||
|
activeTab.value = 0
|
||||||
|
fetchData()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更多菜单控制
|
||||||
|
const toggleDropdown = (id: string) => {
|
||||||
|
if (activeDropdownId.value === id) {
|
||||||
|
activeDropdownId.value = ''
|
||||||
|
} else {
|
||||||
|
activeDropdownId.value = id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 关闭下拉
|
||||||
|
const closeDropdowns = () => {
|
||||||
|
activeDropdownId.value = ''
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查看详情
|
||||||
|
const viewDetail = (order: UTSJSONObject) => {
|
||||||
|
selectedOrder.value = order
|
||||||
|
showDetail.value = true
|
||||||
|
closeDropdowns()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除订单 (权限占位代码)
|
||||||
|
const deleteOrder = async (order: UTSJSONObject) => {
|
||||||
|
closeDropdowns()
|
||||||
|
// 模拟权限验证: admin-order-storeOrder-index
|
||||||
|
const hasAuth = true // 实际项目中应通过 store 读取权限
|
||||||
|
if (!hasAuth) {
|
||||||
|
uni.showToast({ title: '无权限操作', icon: 'none' })
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
uni.showModal({
|
||||||
|
title: '确认删除',
|
||||||
|
content: `确定要删除订单 ${order['sn']} 吗?删除后不可恢复。`,
|
||||||
|
success: async (res) => {
|
||||||
|
if (res.confirm) {
|
||||||
|
try {
|
||||||
|
const { error } = await supabase
|
||||||
|
.from('ml_orders')
|
||||||
|
.delete()
|
||||||
|
.eq('order_no', order['sn'])
|
||||||
|
.execute()
|
||||||
|
|
||||||
|
if (!error) {
|
||||||
|
uni.showToast({ title: '删除成功' })
|
||||||
|
fetchData()
|
||||||
|
} else {
|
||||||
|
uni.showToast({ title: '删除失败: ' + error.message, icon: 'none' })
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleAction = (action: string, sn: string) => {
|
||||||
|
uni.showToast({ title: `执行操作: ${action} - ${sn}`, icon: 'none' })
|
||||||
|
closeDropdowns()
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
fetchData()
|
||||||
|
// 点击空白处关闭下拉
|
||||||
|
// uni.onWindowClick(() => closeDropdowns())
|
||||||
|
// UVUE 不支持 window click,可用 view 遮罩或在主容器加点击
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
@import '@/uni.scss';
|
.admin-page {
|
||||||
.page { padding: $space-lg; }
|
/* 基础背景由 Layout 提供 */
|
||||||
.header { padding: $space-lg; border-radius: $radius; background: $background-primary; box-shadow: $shadow-xs; }
|
min-height: calc(100vh - 120px);
|
||||||
.title { font-size: $font-size-lg; font-weight: $font-weight-bold; color: $text-primary; }
|
padding: 16px;
|
||||||
.sub-title { margin-top: $space-xs; font-size: $font-size-md; color: $text-secondary; }
|
}
|
||||||
|
|
||||||
|
.admin-card {
|
||||||
|
background-color: #ffffff;
|
||||||
|
border-radius: 4px;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
box-shadow: 0 1px 2px rgba(0,0,0,0.05);
|
||||||
|
overflow: visible !important; /* 关键:确保内部内容不被裁切 */
|
||||||
|
}
|
||||||
|
|
||||||
|
.content-card {
|
||||||
|
overflow: visible !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filter-card {
|
||||||
|
padding: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filter-row {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filter-item {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
.label {
|
||||||
|
font-size: 14px;
|
||||||
|
color: #333;
|
||||||
|
width: 80px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.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: 180px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.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; }
|
||||||
|
|
||||||
|
.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%;
|
||||||
|
overflow-x: auto;
|
||||||
|
overflow-y: visible !important; /* 关键:确保垂直方向不被裁切 */
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.thead {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
background-color: #f0f7ff;
|
||||||
|
min-width: 1300px;
|
||||||
|
position: relative;
|
||||||
|
z-index: 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
.th {
|
||||||
|
padding: 12px 8px;
|
||||||
|
font-size: 14px;
|
||||||
|
color: #595959;
|
||||||
|
font-weight: 500;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: flex-start; /* 左对齐内容 */
|
||||||
|
}
|
||||||
|
|
||||||
|
.tbody {
|
||||||
|
overflow: visible !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tr {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
border-bottom: 1px solid #f0f0f0;
|
||||||
|
min-width: 1300px;
|
||||||
|
position: relative; /* 定位参考 */
|
||||||
|
z-index: 10;
|
||||||
|
overflow: visible !important; /* 关键:确保子菜单可溢出 */
|
||||||
|
&:hover { background-color: #fafafa; z-index: 100; }
|
||||||
|
}
|
||||||
|
|
||||||
|
.td {
|
||||||
|
padding: 16px 8px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: flex-start; /* 左对齐内容 */
|
||||||
|
}
|
||||||
|
|
||||||
|
.no-wrap {
|
||||||
|
flex-direction: row !important;
|
||||||
|
flex-wrap: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.overflow-visible {
|
||||||
|
overflow: visible !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 更多下拉菜单 */
|
||||||
|
.op-dropdown-container {
|
||||||
|
position: relative;
|
||||||
|
display: inline-block;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dropdown-menu {
|
||||||
|
position: absolute;
|
||||||
|
top: 100%;
|
||||||
|
right: 0;
|
||||||
|
width: 100px;
|
||||||
|
background-color: #ffffff;
|
||||||
|
border-radius: 4px;
|
||||||
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.25);
|
||||||
|
margin-top: 5px;
|
||||||
|
z-index: 99999; /* 极大层级 */
|
||||||
|
border: 1px solid #f0f0f0;
|
||||||
|
pointer-events: auto;
|
||||||
|
display: block; /* 显式设置为 block */
|
||||||
|
}
|
||||||
|
|
||||||
|
.dropdown-item {
|
||||||
|
height: 36px;
|
||||||
|
padding: 0 12px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
transition: background-color 0.2s;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
.item-text { font-size: 13px; color: #595959; }
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: #f5f5f5;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-red { color: #ff4d4f !important; }
|
||||||
|
|
||||||
|
.loading-state, .empty-state {
|
||||||
|
padding: 40px;
|
||||||
|
text-align: center;
|
||||||
|
color: #999;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 列宽控制 */
|
||||||
|
.col-expand { width: 40px; justify-content: center; align-items: center; }
|
||||||
|
.expand-arrow { color: #bfbfbf; font-size: 18px; }
|
||||||
|
|
||||||
|
.col-check { width: 50px; justify-content: center; align-items: center; }
|
||||||
|
.col-order { width: 220px; }
|
||||||
|
.col-product { flex: 1; min-width: 300px; }
|
||||||
|
.col-user { width: 180px; }
|
||||||
|
.col-price { width: 100px; }
|
||||||
|
.col-pay { width: 100px; }
|
||||||
|
.col-time { width: 160px; }
|
||||||
|
.col-status { width: 100px; }
|
||||||
|
.col-op { width: 140px; }
|
||||||
|
|
||||||
|
.order-sn { font-size: 13px; color: #262626; margin-bottom: 4px; text-align: left; }
|
||||||
|
.order-type { font-size: 12px; text-align: left; }
|
||||||
|
|
||||||
|
.product-info-list {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 8px;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.product-info-wrap {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: flex-start;
|
||||||
|
gap: 12px; /* 间距拉开一点 */
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.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; transition: color 0.1s; }
|
||||||
|
.more-text-active { font-weight: bold; }
|
||||||
|
|
||||||
|
.arrow-down-blue {
|
||||||
|
width: 0; height: 0;
|
||||||
|
border-left: 3px solid transparent;
|
||||||
|
border-right: 3px solid transparent;
|
||||||
|
border-top: 4px solid #1890ff;
|
||||||
|
margin-top: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.arrow-up-blue {
|
||||||
|
width: 0; height: 0;
|
||||||
|
border-left: 3px solid transparent;
|
||||||
|
border-right: 3px solid transparent;
|
||||||
|
border-bottom: 4px solid #1890ff;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 分页 */
|
||||||
|
.pagination-footer {
|
||||||
|
padding: 16px 20px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-left {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
gap: 16px;
|
||||||
|
.count-text { font-size: 14px; color: #595959; }
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-size-select {
|
||||||
|
height: 28px;
|
||||||
|
padding: 0 10px;
|
||||||
|
border: 1px solid #d9d9d9;
|
||||||
|
border-radius: 4px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
text { font-size: 12px; color: #595959; }
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-right {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-num, .page-btn, .page-btns-more {
|
||||||
|
width: 28px;
|
||||||
|
height: 28px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
border: 1px solid #d9d9d9;
|
||||||
|
border-radius: 4px;
|
||||||
|
text { font-size: 14px; color: #595959; }
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-num.active {
|
||||||
|
border-color: #1890ff;
|
||||||
|
text { color: #1890ff; }
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-btn.disabled {
|
||||||
|
opacity: 0.4;
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-btns-more { border: none; }
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user