chore: 提交全部剩余改动
All checks were successful
Build and Deploy TenantUI / build-and-deploy (push) Successful in 53s

This commit is contained in:
2026-02-27 17:18:33 +08:00
parent 01ac80d67d
commit 376aad92b6
9 changed files with 287 additions and 96 deletions

View File

@@ -1,7 +1,7 @@
<script setup lang="ts">
import type { OrderAllDetailDto } from '#/api/order';
import { Drawer, Empty, Spin, Tag } from 'ant-design-vue';
import { Button, Drawer, Empty, Spin, Tag } from 'ant-design-vue';
import { formatCurrency } from '../composables/order-all-page/helpers';
@@ -17,6 +17,13 @@ const emit = defineEmits<{
(event: 'close'): void;
}>();
function resolveChannelTagColor(channel: string) {
if (channel.includes('外卖')) return 'blue';
if (channel.includes('自提')) return 'green';
if (channel.includes('堂食')) return 'orange';
return 'default';
}
function resolveStatusTagColor(status: string) {
if (status.includes('退款')) return 'red';
if (status.includes('取消')) return 'default';
@@ -40,10 +47,14 @@ function resolveStatusTagColor(status: string) {
<div class="oa-section-title">基本信息</div>
<div class="oa-info-grid">
<div>
<span class="label">订单号</span>{{ props.detail.orderNo }}
<span class="label">订单号</span>
<span class="oa-order-no">{{ props.detail.orderNo }}</span>
</div>
<div>
<span class="label">渠道</span>{{ props.detail.channel }}
<span class="label">渠道</span>
<Tag :color="resolveChannelTagColor(props.detail.channel)">
{{ props.detail.channel }}
</Tag>
</div>
<div>
<span class="label">状态</span>
@@ -144,7 +155,6 @@ function resolveStatusTagColor(status: string) {
:key="`${item.time}-${index}`"
class="oa-timeline-item"
>
<span class="dot"></span>
<span class="text">{{ item.label }}</span>
<span class="time">{{ item.time }}</span>
</div>
@@ -158,5 +168,9 @@ function resolveStatusTagColor(status: string) {
</template>
<Empty v-else description="暂无详情" />
</Spin>
<template #footer>
<Button @click="emit('close')">关闭</Button>
</template>
</Drawer>
</template>

View File

@@ -1,7 +1,7 @@
<script setup lang="ts">
import { Button, Input, Select } from 'ant-design-vue';
import { IconifyIcon } from '@vben/icons';
import StoreScopeToolbar from '../../../shared/components/StoreScopeToolbar.vue';
import { Button, Input, Select } from 'ant-design-vue';
import {
CHANNEL_OPTIONS,
PAYMENT_OPTIONS,
@@ -34,7 +34,6 @@ const props = defineProps<Props>();
const emit = defineEmits<{
(event: 'export'): void;
(event: 'reset'): void;
(event: 'search'): void;
(event: 'update:channel', value: string): void;
(event: 'update:endDate', value: string): void;
@@ -44,76 +43,112 @@ const emit = defineEmits<{
(event: 'update:startDate', value: string): void;
(event: 'update:status', value: string): void;
}>();
function handleStoreChange(value: unknown) {
if (typeof value === 'number' || typeof value === 'string') {
emit('update:selectedStoreId', String(value));
return;
}
emit('update:selectedStoreId', '');
}
function handleStartDateChange(value: string | undefined) {
emit('update:startDate', String(value ?? ''));
emit('search');
}
function handleEndDateChange(value: string | undefined) {
emit('update:endDate', String(value ?? ''));
emit('search');
}
function handleStatusChange(value: unknown) {
emit('update:status', String(value ?? 'all'));
emit('search');
}
function handleChannelChange(value: unknown) {
emit('update:channel', String(value ?? 'all'));
emit('search');
}
function handlePaymentChange(value: unknown) {
emit('update:paymentMethod', String(value ?? 'all'));
emit('search');
}
function handleKeywordChange(value: string | undefined) {
emit('update:keyword', String(value ?? ''));
}
</script>
<template>
<StoreScopeToolbar
:selected-store-id="props.selectedStoreId"
:store-options="props.storeOptions"
:is-store-loading="props.isStoreLoading"
:show-copy-button="false"
@update:selected-store-id="(value) => emit('update:selectedStoreId', value)"
>
<template #actions>
<div class="oa-filter-actions">
<Input
class="oa-date-input"
type="date"
:value="props.filters.startDate"
@update:value="
(value) => emit('update:startDate', String(value ?? ''))
"
/>
<span class="oa-date-sep">~</span>
<Input
class="oa-date-input"
type="date"
:value="props.filters.endDate"
@update:value="(value) => emit('update:endDate', String(value ?? ''))"
/>
<div class="oa-toolbar">
<Select
class="oa-store-select"
:value="props.selectedStoreId"
placeholder="全部门店"
:loading="props.isStoreLoading"
:options="props.storeOptions"
:disabled="props.isStoreLoading || props.storeOptions.length === 0"
@update:value="(value) => handleStoreChange(value)"
/>
<Select
class="oa-select"
:value="props.filters.status"
:options="STATUS_OPTIONS"
@update:value="
(value) => emit('update:status', String(value ?? 'all'))
"
/>
<Input
class="oa-date-input"
type="date"
:value="props.filters.startDate"
@update:value="(value) => handleStartDateChange(value)"
/>
<span class="oa-date-sep">~</span>
<Input
class="oa-date-input"
type="date"
:value="props.filters.endDate"
@update:value="(value) => handleEndDateChange(value)"
/>
<Select
class="oa-select"
:value="props.filters.channel"
:options="CHANNEL_OPTIONS"
@update:value="
(value) => emit('update:channel', String(value ?? 'all'))
"
/>
<Select
class="oa-status-select"
:value="props.filters.status"
:options="STATUS_OPTIONS"
@update:value="(value) => handleStatusChange(value)"
/>
<Select
class="oa-select"
:value="props.filters.paymentMethod"
:options="PAYMENT_OPTIONS"
@update:value="
(value) => emit('update:paymentMethod', String(value ?? 'all'))
"
/>
<Select
class="oa-channel-select"
:value="props.filters.channel"
:options="CHANNEL_OPTIONS"
@update:value="(value) => handleChannelChange(value)"
/>
<Input
class="oa-search"
:value="props.filters.keyword"
placeholder="订单号/手机号"
allow-clear
@update:value="(value) => emit('update:keyword', String(value ?? ''))"
@press-enter="emit('search')"
/>
<Select
class="oa-payment-select"
:value="props.filters.paymentMethod"
:options="PAYMENT_OPTIONS"
@update:value="(value) => handlePaymentChange(value)"
/>
<Button @click="emit('reset')">重置</Button>
<Button type="primary" @click="emit('search')">查询</Button>
<Button :loading="props.isExporting" @click="emit('export')">
导出
</Button>
</div>
</template>
</StoreScopeToolbar>
<Input
class="oa-search"
:value="props.filters.keyword"
placeholder="订单号/手机号"
allow-clear
@update:value="(value) => handleKeywordChange(value)"
@press-enter="emit('search')"
>
<template #prefix>
<IconifyIcon icon="lucide:search" class="oa-search-icon" />
</template>
</Input>
<div class="oa-toolbar-right">
<Button class="oa-export-btn" :loading="props.isExporting" @click="emit('export')">
<template #icon>
<IconifyIcon icon="lucide:download" />
</template>
导出
</Button>
</div>
</div>
</template>

View File

@@ -49,6 +49,8 @@ const columns: TableProps['columns'] = [
title: '订单号',
dataIndex: 'orderNo',
width: 170,
customRender: ({ text }) =>
h('span', { class: 'oa-order-no' }, String(text ?? '--')),
},
{
title: '下单时间',
@@ -73,12 +75,15 @@ const columns: TableProps['columns'] = [
title: '商品',
dataIndex: 'itemsSummary',
ellipsis: true,
customRender: ({ text }) =>
h('span', { class: 'oa-items' }, String(text ?? '--')),
},
{
title: '金额',
dataIndex: 'amount',
width: 120,
customRender: ({ text }) => formatCurrency(Number(text || 0)),
customRender: ({ text }) =>
h('span', { class: 'oa-amount' }, formatCurrency(Number(text || 0))),
},
{
title: '状态',
@@ -98,6 +103,7 @@ const columns: TableProps['columns'] = [
Button,
{
type: 'link',
class: 'oa-detail-action',
onClick: () => emit('detail', String(record.orderNo ?? '')),
},
() => '详情',
@@ -125,7 +131,7 @@ function resolveRowClassName(record: OrderAllListItemDto) {
current: props.pagination.page,
pageSize: props.pagination.pageSize,
total: props.pagination.total,
showSizeChanger: true,
showSizeChanger: false,
showTotal: (total: number) => `共 ${total} 条`,
}"
:row-class-name="resolveRowClassName"

View File

@@ -12,7 +12,6 @@ const {
filters,
handleExport,
handlePageChange,
handleReset,
handleSearch,
isDetailLoading,
isDrawerOpen,
@@ -53,7 +52,6 @@ const {
@update:payment-method="setPaymentMethod"
@update:keyword="setKeyword"
@search="handleSearch"
@reset="handleReset"
@export="handleExport"
/>

View File

@@ -1,9 +1,27 @@
.page-order-all {
.ant-drawer {
.ant-drawer-header {
padding: 14px 18px;
border-bottom: 1px solid #f0f0f0;
}
.ant-drawer-body {
padding: 16px 20px;
}
.ant-drawer-footer {
padding: 12px 20px;
border-top: 1px solid #f0f0f0;
}
}
}
.oa-section {
margin-bottom: 20px;
margin-bottom: 22px;
.oa-section-title {
padding-left: 10px;
margin-bottom: 12px;
margin-bottom: 14px;
font-size: 14px;
font-weight: 600;
color: rgb(0 0 0 / 88%);
@@ -26,6 +44,10 @@
}
}
.oa-order-no {
font-family: ui-monospace, sfmono-regular, menlo, consolas, monospace;
}
.oa-detail-table {
width: 100%;
font-size: 13px;
@@ -60,7 +82,7 @@
display: flex;
flex-direction: column;
gap: 6px;
margin-top: 12px;
padding: 12px 10px 0;
font-size: 13px;
> div {
@@ -79,37 +101,62 @@
font-weight: 600;
color: rgb(0 0 0 / 88%);
border-top: 1px solid #f0f0f0;
span:last-child {
font-size: 15px;
color: #1677ff;
}
}
}
.oa-timeline {
padding-left: 4px;
position: relative;
padding-left: 22px;
.oa-timeline-item {
position: relative;
display: flex;
gap: 8px;
align-items: center;
padding: 0 0 12px 16px;
padding-bottom: 18px;
font-size: 13px;
.dot {
&::before {
position: absolute;
top: 5px;
left: 0;
width: 8px;
height: 8px;
left: -22px;
width: 10px;
height: 10px;
content: '';
background: #1677ff;
border: 2px solid #d6e4ff;
border-radius: 50%;
}
&::after {
position: absolute;
top: 17px;
left: -18px;
width: 2px;
height: calc(100% - 12px);
content: '';
background: #e8e8e8;
}
&:last-child {
padding-bottom: 0;
&::after {
display: none;
}
}
.text {
font-size: 13px;
font-weight: 500;
color: rgb(0 0 0 / 88%);
}
.time {
font-size: 13px;
color: rgb(0 0 0 / 45%);
}
}

View File

@@ -1,31 +1,76 @@
.oa-filter-actions {
.oa-toolbar {
display: flex;
flex-wrap: wrap;
gap: 10px;
align-items: center;
padding: 16px 20px;
background: #fff;
border: 1px solid #f0f0f0;
border-radius: 10px;
box-shadow: 0 2px 8px rgb(15 23 42 / 6%);
.oa-store-select {
width: 200px;
}
.oa-date-input {
width: 145px;
}
.oa-date-sep {
font-size: 13px;
color: rgb(0 0 0 / 45%);
.oa-status-select {
width: 120px;
}
.oa-select {
.oa-channel-select {
width: 110px;
}
.oa-payment-select {
width: 120px;
}
.oa-search {
width: 200px;
}
.oa-toolbar-right {
margin-left: auto;
}
.oa-date-sep {
font-size: 13px;
line-height: 32px;
color: rgb(0 0 0 / 45%);
}
.oa-search-icon {
width: 14px;
height: 14px;
color: rgb(0 0 0 / 45%);
}
.oa-export-btn {
display: inline-flex;
gap: 4px;
align-items: center;
}
.ant-select-selector,
.ant-input,
.ant-input-affix-wrapper {
height: 32px;
font-size: 13px;
}
.ant-input-affix-wrapper .ant-input {
height: 100%;
}
}
.oa-stats {
display: flex;
gap: 24px;
padding: 0 4px;
padding: 12px 0 4px;
font-size: 13px;
color: rgb(0 0 0 / 65%);

View File

@@ -6,13 +6,28 @@
}
@media (max-width: 768px) {
.oa-filter-actions {
.oa-toolbar {
padding: 14px 12px;
.oa-store-select,
.oa-date-input,
.oa-select,
.oa-status-select,
.oa-channel-select,
.oa-payment-select,
.oa-search {
width: 100%;
}
.oa-toolbar-right {
width: 100%;
margin-left: 0;
}
.oa-export-btn {
width: 100%;
justify-content: center;
}
.oa-date-sep {
display: none;
}

View File

@@ -1,17 +1,47 @@
.oa-table-card {
padding: 6px 8px;
background: #fff;
border: 1px solid #f0f0f0;
border-radius: 10px;
overflow: hidden;
.ant-table-wrapper {
.ant-table-thead > tr > th {
white-space: nowrap;
font-size: 13px;
}
.ant-table-tbody > tr > td {
vertical-align: middle;
}
}
.ant-pagination {
margin: 14px 16px;
}
}
.oa-row-dim {
.oa-order-no {
font-family: ui-monospace, sfmono-regular, menlo, consolas, monospace;
}
.oa-items {
display: inline-block;
max-width: 180px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.oa-amount {
font-weight: 600;
white-space: nowrap;
}
.oa-detail-action {
padding-inline: 0;
}
.oa-row-dim td {
opacity: 0.55;
}

View File

@@ -38,6 +38,7 @@ catalog:
'@intlify/unplugin-vue-i18n': ^6.0.8
'@jspm/generator': ^2.9.0
'@manypkg/get-packages': ^3.1.0
'@microsoft/signalr': ^8.0.7
'@nolebase/vitepress-plugin-git-changelog': ^2.18.2
'@playwright/test': ^1.58.0
'@pnpm/workspace.read-manifest': ^1000.2.10