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

View File

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

View File

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

View File

@@ -12,7 +12,6 @@ const {
filters, filters,
handleExport, handleExport,
handlePageChange, handlePageChange,
handleReset,
handleSearch, handleSearch,
isDetailLoading, isDetailLoading,
isDrawerOpen, isDrawerOpen,
@@ -53,7 +52,6 @@ const {
@update:payment-method="setPaymentMethod" @update:payment-method="setPaymentMethod"
@update:keyword="setKeyword" @update:keyword="setKeyword"
@search="handleSearch" @search="handleSearch"
@reset="handleReset"
@export="handleExport" @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 { .oa-section {
margin-bottom: 20px; margin-bottom: 22px;
.oa-section-title { .oa-section-title {
padding-left: 10px; padding-left: 10px;
margin-bottom: 12px; margin-bottom: 14px;
font-size: 14px; font-size: 14px;
font-weight: 600; font-weight: 600;
color: rgb(0 0 0 / 88%); 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 { .oa-detail-table {
width: 100%; width: 100%;
font-size: 13px; font-size: 13px;
@@ -60,7 +82,7 @@
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 6px; gap: 6px;
margin-top: 12px; padding: 12px 10px 0;
font-size: 13px; font-size: 13px;
> div { > div {
@@ -79,37 +101,62 @@
font-weight: 600; font-weight: 600;
color: rgb(0 0 0 / 88%); color: rgb(0 0 0 / 88%);
border-top: 1px solid #f0f0f0; border-top: 1px solid #f0f0f0;
span:last-child {
font-size: 15px;
color: #1677ff;
}
} }
} }
.oa-timeline { .oa-timeline {
padding-left: 4px; position: relative;
padding-left: 22px;
.oa-timeline-item { .oa-timeline-item {
position: relative; position: relative;
display: flex; display: flex;
gap: 8px; gap: 8px;
align-items: center; align-items: center;
padding: 0 0 12px 16px; padding-bottom: 18px;
font-size: 13px;
.dot { &::before {
position: absolute; position: absolute;
top: 5px; top: 5px;
left: 0; left: -22px;
width: 8px; width: 10px;
height: 8px; height: 10px;
content: '';
background: #1677ff; background: #1677ff;
border: 2px solid #d6e4ff;
border-radius: 50%; 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 { .text {
font-size: 13px;
font-weight: 500; font-weight: 500;
color: rgb(0 0 0 / 88%); color: rgb(0 0 0 / 88%);
} }
.time { .time {
font-size: 13px;
color: rgb(0 0 0 / 45%); color: rgb(0 0 0 / 45%);
} }
} }

View File

@@ -1,31 +1,76 @@
.oa-filter-actions { .oa-toolbar {
display: flex; display: flex;
flex-wrap: wrap; flex-wrap: wrap;
gap: 10px; gap: 10px;
align-items: center; 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 { .oa-date-input {
width: 145px; width: 145px;
} }
.oa-date-sep { .oa-status-select {
font-size: 13px; width: 120px;
color: rgb(0 0 0 / 45%);
} }
.oa-select { .oa-channel-select {
width: 110px;
}
.oa-payment-select {
width: 120px; width: 120px;
} }
.oa-search { .oa-search {
width: 200px; 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 { .oa-stats {
display: flex; display: flex;
gap: 24px; gap: 24px;
padding: 0 4px; padding: 12px 0 4px;
font-size: 13px; font-size: 13px;
color: rgb(0 0 0 / 65%); color: rgb(0 0 0 / 65%);

View File

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

View File

@@ -1,17 +1,47 @@
.oa-table-card { .oa-table-card {
padding: 6px 8px;
background: #fff; background: #fff;
border: 1px solid #f0f0f0; border: 1px solid #f0f0f0;
border-radius: 10px; border-radius: 10px;
overflow: hidden;
.ant-table-wrapper { .ant-table-wrapper {
.ant-table-thead > tr > th { .ant-table-thead > tr > th {
white-space: nowrap; 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; opacity: 0.55;
} }

View File

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