feat(order): 重构订单页面- 新增 config.js 文件,定义订单搜索和表格配置

- 添加自定义搜索表单和内容组件
- 优化订单列表页面布局和功能
- 实现订单状态标签和操作按钮的自定义渲染
This commit is contained in:
zhangpeng 2025-04-28 11:19:47 +08:00
parent 0e5f07eaf7
commit 5dcda80717
4 changed files with 637 additions and 64 deletions

View File

@ -0,0 +1,287 @@
<template>
<div class="content">
<el-table
:border="!!config?.border"
:data="computedData"
ref="table"
row-key="nanoId"
v-bind="computedStyle"
:show-overflow-tooltip="config.tooltip"
:header-row-style="headerRowStyle"
:highlight-current-row="!!config.radio ? true : false"
:span-method="config?.spanMethod"
:tooltip-options="config?.tooltipOptions"
@current-change="handleTableCurrentChange"
@selection-change="hanldleTableSelectChange"
>
<el-table-column v-if="config.drag" label="排序" align="center" width="50px" prop="nanoId">
<!-- <template #default="{ row, column }"> -->
<!-- <span v-show="false">{{ row[column.property] }}</span> -->
<div class="move" style="width: 100%; height: 100%">
<svg-icon style="cursor: move" icon-class="move" />
</div>
<!-- </template> -->
</el-table-column>
<el-table-column v-if="config.select" type="selection" width="50px" />
<el-table-column
:index="indexMethod"
v-if="config.index"
type="index"
align="center"
width="50px"
label="序号"
/>
<template v-for="scheme in config.schemes" :key="scheme.attrs.label">
<el-table-column v-bind="scheme.attrs">
<template #default="{ row, column, $index }">
<slot
v-if="!!scheme.slot"
:name="scheme.slot"
:row="row"
:column="column"
:index="$index"
></slot>
</template>
</el-table-column>
</template>
<template #empty>
<el-empty description="获取数据为空" />
</template>
</el-table>
<div class="bottom">
<el-pagination
class="pagination"
v-show="isShow"
v-model:current-page="current"
v-model:page-size="size"
:background="props.background"
:layout="props.layout"
:total="props.total"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
/>
</div>
</div>
</template>
<script setup>
import { computed, onBeforeMount, onMounted, watch } from 'vue'
import Sortable from 'sortablejs'
import { nanoid } from '@/utils/jsencrypt'
const props = defineProps({
data: {
type: Array,
default: () => []
},
config: {
type: Object,
required: true
},
maxHeight: {
type: [String, Number],
default: () => ''
},
headerRowStyle: {
type: [Function, Object],
default: () => ({
background: '#F1F3F7'
})
},
showPagination: {
type: Boolean,
default: () => true
},
layout: {
type: String,
default: () => 'total, sizes, prev, pager, next, jumper'
},
background: {
type: Boolean,
default: () => true
},
minHieght: {
type: String,
default: ''
},
pageSize: {
type: Number,
default: () => 10
},
total: {
type: Number,
default: () => 400
},
currentPage: {
type: Number,
default: () => 1
}
})
const emits = defineEmits([
'update:page-size',
'update:current-page',
'update:checkData',
'selection',
'changePage',
'changeSize',
'rowSort'
])
const current = ref(props.currentPage)
const size = ref(props.pageSize)
const computedData = ref([])
const table = ref(null)
const isShow = computed(() => {
return props.showPagination && !!computedData.value.length
})
const computedStyle = computed(() => {
if (props.maxHeight) {
return {
'max-height': props.maxHeight
}
} else {
return null
}
})
watch(
() => props.data,
(data) => {
// if (props.config.drag) {
computedData.value = data.map((item) => {
if (!item.nanoId) {
item.nanoId = nanoid()
}
return item
})
// } else {
// computedData.value = data
// }
},
{
deep: true
}
)
watch(
() => current.value,
(newVal) => {
emits('update:current-page', newVal)
}
)
watch(
() => size.value,
(newVal) => {
emits('update:page-size', newVal)
}
)
watch(
() => props.currentPage,
(newVal) => {
current.value = newVal
}
)
watch(
() => props.pageSize,
(newVal) => {
size.value = newVal
}
)
function reset() {
current.value = 1
size.value = 10
}
function handleSizeChange(v) {
emits('changeSize', v)
}
function handleCurrentChange(v) {
emits('changePage', v)
}
function hanldleTableSelectChange(newSelection) {
console.log(newSelection, 'newSelection')
emits('selection', { newSelection })
}
function handleTableCurrentChange(data) {
console.log(data, 'data')
emits('update:checkData', data)
}
function indexMethod(index) {
const cur = size.value * (current.value - 1) + index + 1
return cur
}
function rowDrag() {
const tbody = table.value.$refs.bodyWrapper.querySelector('tbody')
Sortable.create(tbody, {
ghostClass: 'sortable-ghost',
animation: 150,
handle: '.move',
onEnd: (event) => {
if (event.oldIndex !== undefined && event.newIndex !== undefined) {
let newArr = computedData.value
const curRow = newArr.splice(event.oldIndex, 1)[0]
newArr.splice(event.newIndex, 0, curRow)
computedData.value = [...newArr]
emits('rowSort', newArr)
}
}
})
}
onBeforeMount(() => {
// if (props.config.drag) {
computedData.value = props.data.map((item) => {
if (!item.nanoId) {
item.nanoId = nanoid()
}
return item
})
// } else {
// computedData.value = props.data
// }
})
onMounted(() => {
props.config.drag && rowDrag()
})
defineExpose({
table,
reset
})
</script>
<style lang="scss" scoped>
.content {
background: #fff;
border-radius: 4px;
padding: 20px 20px 0 20px;
box-sizing: border-box;
:deep(.el-table) {
.current-row {
background-color: #e5f1fd;
}
.sortable-ghost {
opacity: 1;
cursor: grab !important;
background-color: #ebf5ff !important;
}
}
.bottom {
display: flex;
align-items: center;
justify-content: flex-end;
.right {
margin-top: 20px;
flex: 1 0 auto;
}
.pagination {
margin-top: 20px;
height: 75px;
}
}
}
</style>

View File

@ -0,0 +1,182 @@
<template>
<div class="search-form">
<!-- 头部搜索栏 -->
<el-form v-model="formData" inline>
<el-form-item
v-for="(item, index) in searchFormData"
:key="index"
:label="item.label"
:prop="item.prop"
>
<template v-if="item.type === 'select'">
<template v-if="item.remote">
<el-select
v-model="formData[item.prop]"
:filterable="item.filterable ?? true"
remote
:placeholder="item.placeholder"
:remote-method="item.remoteMethod"
:clearable="item.clearable ?? true"
:remote-show-suffix="item.remoteShowSuffix ?? true"
:style="{ width: item.width || '100%' }"
>
<el-option
v-for="val in options"
:key="val.value"
:label="val.label"
:value="val.value"
/>
</el-select>
</template>
<template v-else>
<el-select
v-model="formData[item.prop]"
:filterable="item.filterable ?? true"
:placeholder="item.placeholder"
:clearable="item.clearable ?? true"
:style="{ width: item.width || '100%' }"
>
<el-option
v-for="val in item.children.list"
:key="val.value"
:label="val.label"
:value="val.value"
/>
</el-select>
</template>
</template>
<template v-else-if="item.type === 'daterange'">
<el-date-picker
v-model="formData[item.prop]"
type="daterange"
range-separator="至"
start-placeholder="开始日期"
value-format="YYYY-MM-DD"
end-placeholder="结束日期"
:style="{ width: item.width || '100%' }"
/>
</template>
<template v-else-if="item.type === 'monthrange'">
<el-date-picker
v-model="formData[item.prop]"
type="monthrange"
range-separator="至"
start-placeholder="开始日期"
value-format="YYYY-MM"
end-placeholder="结束日期"
:style="{ width: item.width || '100%' }"
/>
</template>
<template v-else-if="item.type === 'treeSelect'">
<el-tree-select
filterable
v-model="formData[item.prop]"
:data="treeData"
placeholder="请选择"
:render-after-expand="false"
:style="{ width: item.width || '100%' }"
:props="defaultProps"
value-key="id"
/>
</template>
<template v-else>
<component
:is="`el-${item.type}`"
v-model="formData[item.prop]"
:placeholder="item.placeholder"
:style="{ width: item.width || '100%' }"
>
</component>
</template>
</el-form-item>
<el-form-item v-if="showBtn">
<el-button type="primary" @click="searchForm">查询</el-button>
<el-button @click="reset">重置</el-button>
</el-form-item>
</el-form>
</div>
</template>
<script setup>
import { onBeforeMount, watch, watchEffect, ref, reactive } from 'vue'
let obj = {}
const props = defineProps({
/**
* @param {Array} search 搜索栏
*/
search: { type: Array, default: () => [] },
showBtn: {
type: Boolean,
default: () => true
}
})
const treeData = ref([])
const defaultProps = { value: 'id', label: 'label', children: 'children' }
const options = ref([])
const formData = ref({})
const searchFormData = ref(props.search)
const emits = defineEmits(['searchData'])
watchEffect(() => {
const items = props.search
items.forEach((item) => {
obj[item.prop] = undefined
})
formData.value = JSON.parse(JSON.stringify(obj))
})
watch(
() => formData.value,
(newVal) => {
console.log('newVal', newVal, !props.showBtn)
!props.showBtn && searchForm()
}
)
function searchForm() {
emits('searchData', formData.value)
}
function reset() {
formData.value = JSON.parse(JSON.stringify(obj))
emits('resetData', formData.value)
}
function createRemoteMethod(fn) {
return async (query) => {
const res = await fn(query)
options.value = res.data
}
}
// const getTreeData = async () => {
// const response = await getDeptTree()
// treeData.value = response
// }
onBeforeMount(() => {
// Object.keys(obj).includes('treeSelect') && getTreeData()
const findItem = searchFormData.value.find((item) => item.type === 'select' && item.remote)
if (findItem) {
findItem.remoteMethod = createRemoteMethod(findItem.remoteMethod)
}
})
</script>
<style lang="scss" scoped>
.search-form{
background: white;
padding: 1rem;
//padding-bottom: 0;
margin-top: 0.5rem;
}
div {
:deep(.el-form) {
display: flex;
flex-wrap: wrap;
row-gap: 20px;
.el-form-item {
margin-bottom: 0;
}
}
}
</style>

80
src/config.js Normal file
View File

@ -0,0 +1,80 @@
export const ORDER_SEARCH_CONFIG = [
{
type: 'input',
prop:'orderId',
label: '订单号',
placeholder: '请输入',
},
{
type: 'daterange',
prop: 'times',
label: '申请时间',
valueFormat: 'YYYY-MM-DD',
placeholder: '请选择申请时间',
startPlaceholder: '开始日期',
endPlaceholder: '结束日期',
rangeSeparator: '-'
},
]
// 经费申请列表
export const ORDER_TABLE_CONFIG = {
index: true,
schemes: [
{
attrs: {
label: '订单号',
prop: 'order_no',
align: 'center'
}
},
{
attrs: {
label: '商品名称',
prop: 'name',
align: 'center'
}
},
{
attrs: {
label: '价格',
align: 'center',
prop: 'price'
}
},
{
attrs: {
label: '类型',
align: 'center',
prop: 'order_type'
}
},
{
attrs: {
label: '状态',
prop: 'status',
align: 'center'
},
slot: 'status'
},{
attrs: {
label: '创建时间',
prop: 'CreatedAt',
align: 'center'
},
},{
attrs: {
label: '更新时间',
prop: 'UpdatedAt',
align: 'center'
},
},
{
attrs: {
label: '操作',
prop: 'operate',
align: 'center'
},
slot: 'operate'
}
]
}

View File

@ -1,92 +1,116 @@
<template>
<div >
<data class="gva-search-box2">
<el-form :inline="true" ref="formRef" :model="paramsQuery" class="demo-form-inline">
<el-form-item label="订单号">
<el-input v-model="paramsQuery.orderId" placeholder="请输入" clearable />
</el-form-item>
<el-form-item label="订单时间">
<el-date-picker
v-model="queryTime"
type="daterange"
unlink-panels
range-separator="至"
start-placeholder="开始时间"
end-placeholder="结束时间"
value-format="YYYY-MM-DD"
style="width:100%"
/>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="onSubmit(formRef)">查询</el-button>
<el-button @click="reset(formRef)">重置</el-button>
</el-form-item>
</el-form>
</data>
<div class="searchForm">
<searchForm
:search="ORDER_SEARCH_CONFIG"
@searchData="searchData"
@resetData="resetData"
class="search-box searchForm"
/>
</div>
<div class="gva-table-box">
<el-table :data="tableData" style="width: 100%">
<el-table-column type="index" label="序号" width="120" />
<el-table-column prop="order_no" label="订单号" width="180" />
<el-table-column prop="name" label="商品名称" />
<el-table-column prop="price" label="价格" />
<el-table-column prop="order_type" label="类型" />
<el-table-column prop="status" label="状态" />
<el-table-column prop="CreatedAt" label="创建时间" />
<el-table-column prop="UpdatedAt" label="更新时间" />
<el-table-column label="操作" fixed="right" width="100">
<template #default="scope">
<!-- <el-button type="text" @click="handleEdit(scope.row)">编辑</el-button> -->
<el-button type="text" @click="handleDelete(scope.row)">删除</el-button>
</template>
</el-table-column>
</el-table>
<!-- <el-table :data="tableData" style="width: 100%">-->
<!-- <el-table-column type="index" label="序号" width="120" />-->
<!-- <el-table-column prop="order_no" label="订单号" width="180" />-->
<!-- <el-table-column prop="name" label="商品名称" />-->
<!-- <el-table-column prop="price" label="价格" />-->
<!-- <el-table-column prop="order_type" label="类型" />-->
<!-- <el-table-column prop="status" label="状态" />-->
<!-- <el-table-column prop="CreatedAt" label="创建时间" />-->
<!-- <el-table-column prop="UpdatedAt" label="更新时间" />-->
<!-- <el-table-column label="操作" fixed="right" width="100">-->
<!-- <template #default="scope">-->
<!-- <el-button type="text" @click="handleDelete(scope.row)">删除</el-button>-->
<!-- </template>-->
<!-- </el-table-column>-->
<!-- </el-table>-->
<Content
@changePage="changePage"
:total="total"
v-model:tabloading="tableLoading"
v-model:currentPage="queryParams.page"
v-model:pageSize="queryParams.pageSize"
:data="tableData"
:config="ORDER_TABLE_CONFIG"
>
<template #status="{ row }">
<el-tag :type="tag(row.status).type">{{ tag(row.status).label }}</el-tag>
</template>
<template #operate="{ row }">
<el-button type="text" @click="handleDetail(row)">详情</el-button>
<el-button type="text" @click="handleDelete(row)">删除</el-button>
</template>
</Content>
</div>
</div>
</template>
<script setup>
import searchForm from '@/components/searchForm/index.vue'
import Content from '@/components/content/index.vue'
import {list} from '@/api/order'
import { ref, reactive, onMounted } from 'vue'
const formRef = ref()
const paramsQuery = reactive({
import { ORDER_SEARCH_CONFIG, ORDER_TABLE_CONFIG } from '@/config'
const tableLoading = ref(true), tableData = ref([{ status: 1 }])
const queryParams = reactive({
page: 1,
pageSize: 10,
orderId: '',
startTime: '',
endTime: ''
}), total = ref(0), queryTime = ref([])
const tableData = ref([])
}), total = ref(0)
const EMPTY_STR = '- -'
const tag = (status) => {
const map = {
1: { type: 'success', label: '已完成' },
2: { type: 'info', label: '待处理' },
3: { type: 'warning', label: '处理中' },
4: { type: 'danger', label: '已拒绝' }
}
return map[status] || { type: '', label: EMPTY_STR }
}
const searchData = (data) => {
if (data.times) {
data.startTime = data.times[0]
data.endTime = data.times[1]
}
queryParams.value.page = 1
queryParams.value = { ...queryParams.value, ...data }
delete queryParams.value.times
// console.log(queryParams.value)
getList()
}
const resetData = () => {
queryParams.value = {
page: 1,
pageSize: 10,
}
getList()
}
onMounted(() => {
getList()
})
async function getList() {
const res = await list(paramsQuery)
const res = await list(queryParams)
if(res.code === 0) {
tableData.value = res.data.list
total.value = res.data.total
}
}
function onSubmit() {
console.log(queryTime.value)
paramsQuery.startTime = queryTime.value[0]
paramsQuery.endTime = queryTime.value[1]
getList()
}
function reset(formEl) {
if (!formEl) return
formEl.resetFields()
getList()
}
function handleDelete(row) {
console.log(row)
}
function handleDetail(row) {
}
function changePage(data) {
queryParams.value.pageNum = data
getList()
}
</script>
<style lang="scss" scoped>
.demo-form-inline{
background: white;
padding: 1rem;
padding-bottom: 0;
margin-top: 0.5rem;
}
</style>
</style>