add: 订单管理的前端页面添加了,但是因为暂时不清楚订单的字段那些,后端也没有相对应的接口。
This commit is contained in:
parent
a790551325
commit
7fa5beee2f
|
@ -7,7 +7,69 @@ export {}
|
||||||
|
|
||||||
declare module 'vue' {
|
declare module 'vue' {
|
||||||
export interface GlobalComponents {
|
export interface GlobalComponents {
|
||||||
|
ApprovalAssistant: typeof import('./../components/ApprovalAssistant/index.vue')['default']
|
||||||
|
ApprovalMessageItem: typeof import('./../components/NotificationCenter/ApprovalMessageItem.vue')['default']
|
||||||
|
Avatar: typeof import('./../components/Avatar/index.vue')['default']
|
||||||
|
Breadcrumb: typeof import('./../components/Breadcrumb/index.vue')['default']
|
||||||
|
CellCopy: typeof import('./../components/CellCopy/index.vue')['default']
|
||||||
|
Chart: typeof import('./../components/Chart/index.vue')['default']
|
||||||
|
ColumnSetting: typeof import('./../components/GiTable/src/components/ColumnSetting.vue')['default']
|
||||||
|
CronForm: typeof import('./../components/GenCron/CronForm/index.vue')['default']
|
||||||
|
CronModal: typeof import('./../components/GenCron/CronModal/index.vue')['default']
|
||||||
|
DateRangePicker: typeof import('./../components/DateRangePicker/index.vue')['default']
|
||||||
|
DayForm: typeof import('./../components/GenCron/CronForm/component/day-form.vue')['default']
|
||||||
|
FilePreview: typeof import('./../components/FilePreview/index.vue')['default']
|
||||||
|
GiCellAvatar: typeof import('./../components/GiCell/GiCellAvatar.vue')['default']
|
||||||
|
GiCellGender: typeof import('./../components/GiCell/GiCellGender.vue')['default']
|
||||||
|
GiCellStatus: typeof import('./../components/GiCell/GiCellStatus.vue')['default']
|
||||||
|
GiCellTag: typeof import('./../components/GiCell/GiCellTag.vue')['default']
|
||||||
|
GiCellTags: typeof import('./../components/GiCell/GiCellTags.vue')['default']
|
||||||
|
GiCodeView: typeof import('./../components/GiCodeView/index.vue')['default']
|
||||||
|
GiDot: typeof import('./../components/GiDot/index.tsx')['default']
|
||||||
|
GiEditTable: typeof import('./../components/GiEditTable/GiEditTable.vue')['default']
|
||||||
|
GiFooter: typeof import('./../components/GiFooter/index.vue')['default']
|
||||||
|
GiForm: typeof import('./../components/GiForm/src/GiForm.vue')['default']
|
||||||
|
GiIconBox: typeof import('./../components/GiIconBox/index.vue')['default']
|
||||||
|
GiIconSelector: typeof import('./../components/GiIconSelector/index.vue')['default']
|
||||||
|
GiIframe: typeof import('./../components/GiIframe/index.vue')['default']
|
||||||
|
GiOption: typeof import('./../components/GiOption/index.vue')['default']
|
||||||
|
GiOptionItem: typeof import('./../components/GiOptionItem/index.vue')['default']
|
||||||
|
GiPageLayout: typeof import('./../components/GiPageLayout/index.vue')['default']
|
||||||
|
GiSpace: typeof import('./../components/GiSpace/index.vue')['default']
|
||||||
|
GiSplitButton: typeof import('./../components/GiSplitButton/index.vue')['default']
|
||||||
|
GiSplitPane: typeof import('./../components/GiSplitPane/index.vue')['default']
|
||||||
|
GiSplitPaneFlexibleBox: typeof import('./../components/GiSplitPane/components/GiSplitPaneFlexibleBox.vue')['default']
|
||||||
|
GiSvgIcon: typeof import('./../components/GiSvgIcon/index.vue')['default']
|
||||||
|
GiTable: typeof import('./../components/GiTable/src/GiTable.vue')['default']
|
||||||
|
GiTag: typeof import('./../components/GiTag/index.tsx')['default']
|
||||||
|
GiThemeBtn: typeof import('./../components/GiThemeBtn/index.vue')['default']
|
||||||
|
HourForm: typeof import('./../components/GenCron/CronForm/component/hour-form.vue')['default']
|
||||||
|
Icon403: typeof import('./../components/icons/Icon403.vue')['default']
|
||||||
|
Icon404: typeof import('./../components/icons/Icon404.vue')['default']
|
||||||
|
Icon500: typeof import('./../components/icons/Icon500.vue')['default']
|
||||||
|
IconBorders: typeof import('./../components/icons/IconBorders.vue')['default']
|
||||||
|
IconTableSize: typeof import('./../components/icons/IconTableSize.vue')['default']
|
||||||
|
IconTreeAdd: typeof import('./../components/icons/IconTreeAdd.vue')['default']
|
||||||
|
IconTreeReduce: typeof import('./../components/icons/IconTreeReduce.vue')['default']
|
||||||
|
ImageImport: typeof import('./../components/ImageImport/index.vue')['default']
|
||||||
|
ImageImportWizard: typeof import('./../components/ImageImportWizard/index.vue')['default']
|
||||||
|
IndustrialImageList: typeof import('./../components/IndustrialImageList/index.vue')['default']
|
||||||
|
JsonPretty: typeof import('./../components/JsonPretty/index.vue')['default']
|
||||||
|
MinuteForm: typeof import('./../components/GenCron/CronForm/component/minute-form.vue')['default']
|
||||||
|
MonthForm: typeof import('./../components/GenCron/CronForm/component/month-form.vue')['default']
|
||||||
|
NotificationCenter: typeof import('./../components/NotificationCenter/index.vue')['default']
|
||||||
|
ParentView: typeof import('./../components/ParentView/index.vue')['default']
|
||||||
RouterLink: typeof import('vue-router')['RouterLink']
|
RouterLink: typeof import('vue-router')['RouterLink']
|
||||||
RouterView: typeof import('vue-router')['RouterView']
|
RouterView: typeof import('vue-router')['RouterView']
|
||||||
|
SecondForm: typeof import('./../components/GenCron/CronForm/component/second-form.vue')['default']
|
||||||
|
SplitPanel: typeof import('./../components/SplitPanel/index.vue')['default']
|
||||||
|
TextCopy: typeof import('./../components/TextCopy/index.vue')['default']
|
||||||
|
TurbineGrid: typeof import('./../components/TurbineGrid/index.vue')['default']
|
||||||
|
UserSelect: typeof import('./../components/UserSelect/index.vue')['default']
|
||||||
|
Verify: typeof import('./../components/Verify/index.vue')['default']
|
||||||
|
VerifyPoints: typeof import('./../components/Verify/Verify/VerifyPoints.vue')['default']
|
||||||
|
VerifySlide: typeof import('./../components/Verify/Verify/VerifySlide.vue')['default']
|
||||||
|
WeekForm: typeof import('./../components/GenCron/CronForm/component/week-form.vue')['default']
|
||||||
|
YearForm: typeof import('./../components/GenCron/CronForm/component/year-form.vue')['default']
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,163 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import { ref, reactive, computed, onMounted } from 'vue'
|
||||||
|
import type { TableColumnData } from '@arco-design/web-vue'
|
||||||
|
|
||||||
|
// 订单类型与状态
|
||||||
|
type OrderStatus = '待支付' | '进行中' | '已完成' | '已取消'
|
||||||
|
interface OrderItem { id: string; orderNo: string; customer: string; amount: number; status: OrderStatus; createTime: string }
|
||||||
|
const StatusOptions: { label: string; value: OrderStatus }[] = [
|
||||||
|
{ label: '待支付', value: '待支付' },
|
||||||
|
{ label: '进行中', value: '进行中' },
|
||||||
|
{ label: '已完成', value: '已完成' },
|
||||||
|
{ label: '已取消', value: '已取消' },
|
||||||
|
]
|
||||||
|
|
||||||
|
// 假数据源与统计
|
||||||
|
const allOrders = ref<OrderItem[]>([])
|
||||||
|
const stats = computed(() => ({
|
||||||
|
total: allOrders.value.length,
|
||||||
|
pending: allOrders.value.filter(o=>o.status==='待支付').length,
|
||||||
|
progress: allOrders.value.filter(o=>o.status==='进行中').length,
|
||||||
|
done: allOrders.value.filter(o=>o.status==='已完成').length,
|
||||||
|
cancel: allOrders.value.filter(o=>o.status==='已取消').length,
|
||||||
|
}))
|
||||||
|
|
||||||
|
// 统计卡片渲染数据(去趋势化)
|
||||||
|
const statCards = computed(() => [
|
||||||
|
{ key: 'total', title: '订单总数', value: stats.value.total, color: '#2f54eb', iconChar: 'T' },
|
||||||
|
{ key: 'pending', title: '待支付', value: stats.value.pending, color: '#faad14', iconChar: 'P' },
|
||||||
|
{ key: 'progress', title: '进行中', value: stats.value.progress, color: '#1677ff', iconChar: 'R' },
|
||||||
|
{ key: 'done', title: '已完成', value: stats.value.done, color: '#52c41a', iconChar: 'D' },
|
||||||
|
{ key: 'cancel', title: '已取消', value: stats.value.cancel, color: '#8c8c8c', iconChar: 'C' },
|
||||||
|
])
|
||||||
|
|
||||||
|
// 查询表单与分页
|
||||||
|
const searchForm = reactive({ orderNo: '', status: '' as ''|OrderStatus, timeRange: [] as [string,string]|[], page: 1, size: 10 })
|
||||||
|
const filtered = computed(() => {
|
||||||
|
let list = allOrders.value.slice()
|
||||||
|
if (searchForm.orderNo) list = list.filter(o=>o.orderNo.includes(searchForm.orderNo.trim()))
|
||||||
|
if (searchForm.status) list = list.filter(o=>o.status===searchForm.status)
|
||||||
|
if (Array.isArray(searchForm.timeRange) && searchForm.timeRange.length===2) {
|
||||||
|
const [s,e] = searchForm.timeRange; const S=+new Date(s as any), E=+new Date(e as any)
|
||||||
|
list = list.filter(o=>{ const t=+new Date(o.createTime as any); return t>=S && t<=E })
|
||||||
|
}
|
||||||
|
return list
|
||||||
|
})
|
||||||
|
const paged = computed(() => {
|
||||||
|
const start = (searchForm.page-1)*searchForm.size
|
||||||
|
return filtered.value.slice(start, start+searchForm.size)
|
||||||
|
})
|
||||||
|
const pagination = reactive({ current: 1, pageSize: 10, total: 0, showTotal: true, showPageSize: true })
|
||||||
|
|
||||||
|
// 表格列
|
||||||
|
const columns: TableColumnData[] = [
|
||||||
|
{ title: '订单编号', dataIndex: 'orderNo', width: 180 },
|
||||||
|
{ title: '客户', dataIndex: 'customer', width: 160 },
|
||||||
|
{ title: '金额', dataIndex: 'amount', width: 120, render: ({record}:any)=>`¥${(record.amount||0).toLocaleString()}` },
|
||||||
|
{ title: '状态', dataIndex: 'status', width: 100, slotName: 'status' },
|
||||||
|
{ title: '下单时间', dataIndex: 'createTime', width: 180 },
|
||||||
|
{ title: '操作', slotName: 'action', width: 140, fixed: 'right' },
|
||||||
|
]
|
||||||
|
|
||||||
|
// 操作
|
||||||
|
const loading = ref(false)
|
||||||
|
const search = () => { pagination.total = filtered.value.length }
|
||||||
|
const reset = () => { Object.assign(searchForm, { orderNo: '', status: '', timeRange: [], page: 1, size: 10 }); pagination.current=1; pagination.pageSize=10; search() }
|
||||||
|
const onPageChange = (p:number)=>{ searchForm.page=p; pagination.current=p }
|
||||||
|
const onPageSizeChange=(s:number)=>{ searchForm.size=s; searchForm.page=1; pagination.pageSize=s; pagination.current=1; search() }
|
||||||
|
const getStatusColor = (s:OrderStatus)=>({ '待支付':'orange','进行中':'blue','已完成':'green','已取消':'gray' }[s])
|
||||||
|
|
||||||
|
// 新增订单(本地)
|
||||||
|
const showAdd = ref(false)
|
||||||
|
const newOrder = reactive<{orderNo:string;customer:string;amount:number;status:OrderStatus;createTime:string}>(
|
||||||
|
{ orderNo:'', customer:'', amount:0, status:'待支付', createTime:'' }
|
||||||
|
)
|
||||||
|
const openAdd = ()=>{ Object.assign(newOrder,{ orderNo:'',customer:'',amount:0,status:'待支付',createTime:new Date().toISOString().slice(0,19).replace('T',' ') }); showAdd.value=true }
|
||||||
|
const submitAdd = ()=>{
|
||||||
|
const id = Math.random().toString(36).slice(2)
|
||||||
|
allOrders.value.unshift({ id, ...newOrder })
|
||||||
|
showAdd.value=false; search()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 生成假数据
|
||||||
|
const genMock = (n=60)=>{
|
||||||
|
const pick=<T,>(arr:T[])=>arr[Math.floor(Math.random()*arr.length)]
|
||||||
|
const now=Date.now()
|
||||||
|
return Array.from({length:n}).map((_,i)=>{
|
||||||
|
const offset = Math.floor(Math.random()*60)*86400000
|
||||||
|
const dt = new Date(now-offset).toISOString().slice(0,19).replace('T',' ')
|
||||||
|
const st = pick<OrderStatus>(['待支付','进行中','已完成','已取消'])
|
||||||
|
return { id:`O${i+1}`, orderNo:`NO${100000+i}`, customer:`客户${(i%20)+1}`, amount: Math.floor(Math.random()*50000)+1000, status: st, createTime: dt }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
onMounted(()=>{ allOrders.value = genMock(88); search() })
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
<GiPageLayout>
|
||||||
|
<!-- 顶部统计(美化 + 居中 + 独立容器) -->
|
||||||
|
<a-row :gutter="12" class="mb-3 stats-row">
|
||||||
|
<a-col :span="24">
|
||||||
|
<div class="stats-wrap">
|
||||||
|
<a-row :gutter="12" justify="center">
|
||||||
|
<a-col v-for="card in statCards" :key="card.key" :xs="12" :sm="12" :md="6" :lg="4">
|
||||||
|
<a-card hoverable class="stat-card">
|
||||||
|
<div class="stat-inner">
|
||||||
|
<div class="stat-icon" :style="{ background: card.color }">
|
||||||
|
<span>{{ card.iconChar }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="stat-value">{{ card.value }}</div>
|
||||||
|
<div class="stat-title">{{ card.title }}</div>
|
||||||
|
</div>
|
||||||
|
</a-card>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</div>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
|
||||||
|
<!-- 查询表单 + 新增按钮 -->
|
||||||
|
<GiForm :columns="[
|
||||||
|
{ field:'orderNo', label:'订单编号', type:'input', props:{placeholder:'支持模糊查询'} },
|
||||||
|
{ field:'status', label:'订单状态', type:'select', props:{ placeholder:'全部', options: StatusOptions } },
|
||||||
|
{ field:'timeRange', label:'订单时间', type:'range-picker', props:{ showTime:true, format:'YYYY-MM-DD HH:mm:ss' } },
|
||||||
|
]" v-model="searchForm" search size="medium" @search="search" @reset="reset">
|
||||||
|
<template #extra>
|
||||||
|
<a-button type="primary" @click="openAdd"><icon-plus/> 新增订单</a-button>
|
||||||
|
</template>
|
||||||
|
</GiForm>
|
||||||
|
|
||||||
|
<!-- 订单列表 -->
|
||||||
|
<GiTable :data="paged" :columns="columns" :loading="loading" :pagination="pagination"
|
||||||
|
@page-change="onPageChange" @page-size-change="onPageSizeChange" @refresh="search">
|
||||||
|
<template #status="{ record }"><a-tag :color="getStatusColor(record.status)">{{ record.status }}</a-tag></template>
|
||||||
|
<template #action="{ record }"><a-space>
|
||||||
|
<a-link @click="()=>{}">查看</a-link>
|
||||||
|
<a-link @click="()=>{}">编辑</a-link>
|
||||||
|
</a-space></template>
|
||||||
|
</GiTable>
|
||||||
|
|
||||||
|
<!-- 新增订单弹窗(本地) -->
|
||||||
|
<a-modal v-model:visible="showAdd" title="新增订单" :width="520" @before-ok="submitAdd">
|
||||||
|
<a-form :model="newOrder" layout="vertical">
|
||||||
|
<a-form-item field="orderNo" label="订单编号"><a-input v-model="newOrder.orderNo" placeholder="NO123456"/></a-form-item>
|
||||||
|
<a-form-item field="customer" label="客户"><a-input v-model="newOrder.customer"/></a-form-item>
|
||||||
|
<a-form-item field="amount" label="金额"><a-input-number v-model="newOrder.amount" style="width:100%"/></a-form-item>
|
||||||
|
<a-form-item field="status" label="状态"><a-select v-model="newOrder.status" :options="StatusOptions"/></a-form-item>
|
||||||
|
<a-form-item field="createTime" label="下单时间"><a-date-picker v-model="newOrder.createTime" show-time style="width:100%"/></a-form-item>
|
||||||
|
</a-form>
|
||||||
|
</a-modal>
|
||||||
|
</GiPageLayout>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped>
|
||||||
|
.mb-3{ margin-bottom:12px; }
|
||||||
|
.stats-row{ }
|
||||||
|
.stats-wrap{ max-width: 1200px; margin: 0 auto; }
|
||||||
|
.stat-card{ border: 1px solid var(--color-border-2,#f0f0f0); background: linear-gradient(180deg,#ffffff 0%, #fafbff 100%); }
|
||||||
|
.stat-inner{ display:flex; flex-direction:column; align-items:center; justify-content:center; padding:18px 10px; text-align:center; }
|
||||||
|
.stat-icon{ width:44px; height:44px; border-radius:12px; color:#fff; display:flex; align-items:center; justify-content:center; font-weight:700; margin-bottom:8px; box-shadow: 0 6px 16px rgba(0,0,0,0.08); }
|
||||||
|
.stat-value{ font-size:28px; font-weight:700; color:#1d2129; line-height:1.2; }
|
||||||
|
.stat-title{ margin-top:6px; color:#86909c; }
|
||||||
|
|
||||||
</style>
|
</style>
|
Loading…
Reference in New Issue