订单管理

master
lzq 2026-01-29 14:51:16 +08:00
parent 7dcb5d53ff
commit 03eb338eb1
16 changed files with 351 additions and 23 deletions

View File

@ -30,7 +30,6 @@
margin: 0; margin: 0;
padding: 0; padding: 0;
border: 0; border: 0;
user-select: none;
} }
</style> </style>
<title></title> <title></title>

View File

@ -24,6 +24,7 @@ function createTs(text: string) {
const tsContent = `export const icons = ${text.trim()} as const const tsContent = `export const icons = ${text.trim()} as const
export type IconName = (typeof icons.glyphs)[number]['font_class'] export type IconName = (typeof icons.glyphs)[number]['font_class']
export const iconNames = icons.glyphs.map(it => it.font_class)
export interface IconGlyphs { export interface IconGlyphs {
icon_id: string icon_id: string

View File

@ -47,7 +47,7 @@
text-align-last: justify; text-align-last: justify;
display: inline-block !important; display: inline-block !important;
position: relative; position: relative;
padding 0 12px !important; padding 0 12px 0 0 !important;
} }
.el-form-item.is-error { .el-form-item.is-error {

View File

@ -24,6 +24,15 @@
} }
} }
.el-menu--collapse .el-sub-menu.is-active .el-sub-menu__title {
color #5D87FF !important;
}
.el-sub-menu.is-active .el-sub-menu__title {
border-bottom-color: #5D87FF !important;
}
.el-sub-menu__title, .el-sub-menu__title,
.el-menu-item { .el-menu-item {
height: 42px !important; height: 42px !important;

View File

@ -20,6 +20,18 @@ body {
.region .el-radio-button__original-radio:checked + .el-radio-button__inner { .region .el-radio-button__original-radio:checked + .el-radio-button__inner {
color: var(--theme-color) !important; color: var(--theme-color) !important;
} }
letter-spacing: 1px;
user-select: none;
}
.el-message-box {
--el-messagebox-width 300px !important
max-width unset !important
width unset !important;
}
.el-message-box__container {
justify-content center
} }
:focus-visible { :focus-visible {

View File

@ -1,8 +1,8 @@
@font-face { @font-face {
font-family: "iconfont"; /* 项目名称 再昇云 */ font-family: "iconfont"; /* 项目名称 再昇云 */
src: url('@/components/a-icon/iconfont.woff2?t=1769142012944') format('woff2'), src: url('@/components/a-icon/iconfont.woff2?t=1769666423879') format('woff2'),
url('@/components/a-icon/iconfont.woff?t=1769142012944') format('woff'), url('@/components/a-icon/iconfont.woff?t=1769666423879') format('woff'),
url('@/components/a-icon/iconfont.ttf?t=1769142012944') format('truetype'); url('@/components/a-icon/iconfont.ttf?t=1769666423879') format('truetype');
} }
.iconfont { .iconfont {
@ -13,6 +13,30 @@
-moz-osx-font-smoothing: grayscale; -moz-osx-font-smoothing: grayscale;
} }
.icon-liandan:before {
content: "\e65e";
}
.icon-chakanliaodan:before {
content: "\e6fc";
}
.icon-approach:before {
content: "\e69d";
}
.icon-carexit:before {
content: "\e69f";
}
.icon-fapiaotaitou:before {
content: "\e85d";
}
.icon-qiehuanyonghu:before {
content: "\e893";
}
.icon-dianhua-1:before { .icon-dianhua-1:before {
content: "\e65d"; content: "\e65d";
} }

View File

@ -5,6 +5,48 @@
"css_prefix_text": "icon-", "css_prefix_text": "icon-",
"description": "", "description": "",
"glyphs": [ "glyphs": [
{
"icon_id": "15056965",
"name": "联单",
"font_class": "liandan",
"unicode": "e65e",
"unicode_decimal": 58974
},
{
"icon_id": "32234102",
"name": "查看料单",
"font_class": "chakanliaodan",
"unicode": "e6fc",
"unicode_decimal": 59132
},
{
"icon_id": "17660527",
"name": "车辆进场",
"font_class": "approach",
"unicode": "e69d",
"unicode_decimal": 59037
},
{
"icon_id": "17660543",
"name": "车辆出场",
"font_class": "carexit",
"unicode": "e69f",
"unicode_decimal": 59039
},
{
"icon_id": "23736496",
"name": "发票抬头",
"font_class": "fapiaotaitou",
"unicode": "e85d",
"unicode_decimal": 59485
},
{
"icon_id": "17789296",
"name": "切换用户",
"font_class": "qiehuanyonghu",
"unicode": "e893",
"unicode_decimal": 59539
},
{ {
"icon_id": "1272171", "icon_id": "1272171",
"name": "电话", "name": "电话",

View File

@ -5,6 +5,48 @@ export const icons = {
'css_prefix_text': 'icon-', 'css_prefix_text': 'icon-',
'description': '', 'description': '',
'glyphs': [ 'glyphs': [
{
'icon_id': '15056965',
'name': '联单',
'font_class': 'liandan',
'unicode': 'e65e',
'unicode_decimal': 58974,
},
{
'icon_id': '32234102',
'name': '查看料单',
'font_class': 'chakanliaodan',
'unicode': 'e6fc',
'unicode_decimal': 59132,
},
{
'icon_id': '17660527',
'name': '车辆进场',
'font_class': 'approach',
'unicode': 'e69d',
'unicode_decimal': 59037,
},
{
'icon_id': '17660543',
'name': '车辆出场',
'font_class': 'carexit',
'unicode': 'e69f',
'unicode_decimal': 59039,
},
{
'icon_id': '23736496',
'name': '发票抬头',
'font_class': 'fapiaotaitou',
'unicode': 'e85d',
'unicode_decimal': 59485,
},
{
'icon_id': '17789296',
'name': '切换用户',
'font_class': 'qiehuanyonghu',
'unicode': 'e893',
'unicode_decimal': 59539,
},
{ {
'icon_id': '1272171', 'icon_id': '1272171',
'name': '电话', 'name': '电话',
@ -761,7 +803,7 @@ export const icons = {
'unicode': 'e639', 'unicode': 'e639',
'unicode_decimal': 58937, 'unicode_decimal': 58937,
}, },
], ]
} as const } as const
export type IconName = (typeof icons.glyphs)[number]['font_class'] export type IconName = (typeof icons.glyphs)[number]['font_class']

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -69,6 +69,7 @@ interface ActionColumnType<T> {
* 80px * 80px
*/ */
width: number width: number
foldLimit: number
/** /**
* *
*/ */
@ -197,6 +198,7 @@ function buildTable<P extends object, T extends object>(table: DeepPartial<Table
if (table.actionColumn == null) { if (table.actionColumn == null) {
table.actionColumn = { table.actionColumn = {
width: 80, width: 80,
foldLimit: 3,
tableActions: [], tableActions: [],
} }
} }
@ -210,9 +212,14 @@ function buildTable<P extends object, T extends object>(table: DeepPartial<Table
if (tableAction.confirm.width == null) tableAction.confirm.width = '180' if (tableAction.confirm.width == null) tableAction.confirm.width = '180'
} }
} }
if (table.actionColumn.width == null) { if (table.actionColumn.foldLimit == null) {
table.actionColumn.width = Math.min(table.actionColumn.tableActions.length, 3) * 50 table.actionColumn.foldLimit = 3
} }
if (table.actionColumn.width == null) {
table.actionColumn.width = Math.min(table.actionColumn.tableActions.length, table.actionColumn.foldLimit) * 50
}
if (table.emptyText == null) { if (table.emptyText == null) {
table.emptyText = '暂无数据' table.emptyText = '暂无数据'
} }
@ -357,13 +364,13 @@ const component = defineComponent(
} }
const actionColumnRender = () => { const actionColumnRender = () => {
const actionColumn = props.table.actionColumn const actionColumn = props.table.actionColumn
const len = actionColumn.tableActions.length
if (Colls.isEmpty(actionColumn.tableActions)) return (<></>) if (Colls.isEmpty(actionColumn.tableActions)) return (<></>)
return (<ElTableColumn width={actionColumn.width + 'px'} fixed="right" label="操作" prop="tableAction"> return (<ElTableColumn width={actionColumn.width + 'px'} fixed="right" label="操作" prop="tableAction">
{{ {{
default: (scope: ColumnScopeType<T>) => { default: (scope: ColumnScopeType<T>) => {
const len = actionColumn.tableActions.filter(it => it.show == null || it.show(scope)).length
let btns: any[] let btns: any[]
if (len <= 3) { if (len <= actionColumn.foldLimit) {
btns = (actionColumn.tableActions btns = (actionColumn.tableActions
.filter(it => (it.show == null ? true : it.show(scope))) .filter(it => (it.show == null ? true : it.show(scope)))
.map((tableAction, i) => (tableAction.confirm != null ? .map((tableAction, i) => (tableAction.confirm != null ?
@ -386,7 +393,7 @@ const component = defineComponent(
} else { } else {
btns = (actionColumn.tableActions btns = (actionColumn.tableActions
.filter(it => (it.show == null ? true : it.show(scope))) .filter(it => (it.show == null ? true : it.show(scope)))
.filter((_, i) => i < 2) .filter((_, i) => i < actionColumn.foldLimit - 1)
.map((tableAction, i) => (tableAction.confirm != null ? .map((tableAction, i) => (tableAction.confirm != null ?
(<ElPopconfirm (<ElPopconfirm
key={'action-btn-' + i} key={'action-btn-' + i}
@ -433,7 +440,7 @@ const component = defineComponent(
{ {
actionColumn.tableActions actionColumn.tableActions
.filter(it => (it.show == null ? true : it.show(scope))) .filter(it => (it.show == null ? true : it.show(scope)))
.filter((_, i) => i >= 2) .filter((_, i) => i >= actionColumn.foldLimit - 1)
.map((it, i) => { .map((it, i) => {
let elIcon: any | undefined = undefined let elIcon: any | undefined = undefined
if (iconNames.includes(it.icon as IconName)) { if (iconNames.includes(it.icon as IconName)) {

View File

@ -49,12 +49,12 @@ declare module 'vue' {
ElTableColumn: typeof import('element-plus/es')['ElTableColumn'] ElTableColumn: typeof import('element-plus/es')['ElTableColumn']
ElTabPane: typeof import('element-plus/es')['ElTabPane'] ElTabPane: typeof import('element-plus/es')['ElTabPane']
ElTabs: typeof import('element-plus/es')['ElTabs'] ElTabs: typeof import('element-plus/es')['ElTabs']
ElTag: typeof import('element-plus/es')['ElTag']
ElTooltip: typeof import('element-plus/es')['ElTooltip'] ElTooltip: typeof import('element-plus/es')['ElTooltip']
ElUpload: typeof import('element-plus/es')['ElUpload'] ElUpload: typeof import('element-plus/es')['ElUpload']
RouterLink: typeof import('vue-router')['RouterLink'] RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView'] RouterView: typeof import('vue-router')['RouterView']
} }
export interface GlobalDirectives { export interface GlobalDirectives {
vLoading: typeof import('element-plus/es')['ElLoadingDirective'] vLoading: typeof import('element-plus/es')['ElLoadingDirective']
} }
@ -100,6 +100,7 @@ declare global {
const ElTableColumn: typeof import('element-plus/es')['ElTableColumn'] const ElTableColumn: typeof import('element-plus/es')['ElTableColumn']
const ElTabPane: typeof import('element-plus/es')['ElTabPane'] const ElTabPane: typeof import('element-plus/es')['ElTabPane']
const ElTabs: typeof import('element-plus/es')['ElTabs'] const ElTabs: typeof import('element-plus/es')['ElTabs']
const ElTag: typeof import('element-plus/es')['ElTag']
const ElTooltip: typeof import('element-plus/es')['ElTooltip'] const ElTooltip: typeof import('element-plus/es')['ElTooltip']
const ElUpload: typeof import('element-plus/es')['ElUpload'] const ElUpload: typeof import('element-plus/es')['ElUpload']
const RouterLink: typeof import('vue-router')['RouterLink'] const RouterLink: typeof import('vue-router')['RouterLink']

View File

@ -119,7 +119,7 @@ const tablePageProps = buildTablePageProps<OrderTypes.SearchOrderParam, OrderTyp
{ {
tooltip: '三联单', tooltip: '三联单',
type: 'info', type: 'info',
icon: 'Position', icon: 'liandan',
action({row}) { action({row}) {
bookDetailIns.value?.open(row) bookDetailIns.value?.open(row)
}, },

View File

@ -0,0 +1,122 @@
<script lang="ts" setup>
import Uploader from '@/components/uploader/Uploader.vue'
import Utils from '@/common/utils'
import ADialog from '@/components/a-dialog/ADialog.vue'
import FormUtil from '@/common/utils/formUtil.ts'
import {
ElMessage,
type FormInstance,
} from 'element-plus'
const orderData = Utils.resetAble(reactive<{
id: string
licensePlate: string,
state: '进场' | '出场',
}>({
id: '',
licensePlate: '',
state: '进场',
}))
const title = computed(() => {
return `${orderData.licensePlate} ${orderData.state}`
})
const formIns = useTemplateRef<FormInstance>('formRef')
const inOutData = Utils.resetAble(reactive({
weight: undefined,
headerPhoto: undefined,
bodyPhoto: undefined,
}))
const showDialog = ref(false)
const submiting = ref(false)
function dialogCloseHandler() {
inOutData.$reset()
}
function submitHandler() {
submiting.value = true
FormUtil.submit(formIns, () => Promise.resolve())
.then(res => {
ElMessage.success(`${title.value}成功`)
if ((res ?? true)) {
showDialog.value = false
}
})
.finally(() => {
submiting.value = false
})
}
defineExpose({
open(id: string, licensePlate: string, state: '进场' | '出场') {
orderData.$reset({
id,
licensePlate,
state,
})
showDialog.value = true
},
})
</script>
<template>
<ADialog
v-model:show="showDialog"
:closed="dialogCloseHandler"
:submit-handler="submitHandler"
:title="title"
>
<ElForm ref="formRef"
class="in-out-panel-form"
@submit.prevent>
<ElFormItem label="磅重" prop="weight">
<ElInputNumber v-model="inOutData.weight" :max="200" :min="1" controls-position="right">
<template #suffix></template>
</ElInputNumber>
</ElFormItem>
<ElFormItem label="车头照" prop="headerPhoto">
<Uploader v-model:file="inOutData.headerPhoto"/>
</ElFormItem>
<ElFormItem label="车斗照" prop="bodyPhoto">
<Uploader v-model:file="inOutData.bodyPhoto"/>
</ElFormItem>
</ElForm>
<template #footer>
<ElButton @click="showDialog = false">关闭</ElButton>
<ElButton :loading="submiting" type="primary" @click="submitHandler">{{ orderData.state }}</ElButton>
</template>
</ADialog>
</template>
<style lang="stylus" scoped>
.in-out-panel-form {
display grid
grid-template-areas "weight weight" \
"headerPhoto bodyPhoto"
gap: 0 20px;
padding: 10px;
:deep(.el-input-number) {
.el-input__suffix-inner {
padding-left 10px
}
}
:deep(.el-form-item) {
&:nth-child(1) {
grid-area weight
}
&:nth-child(2) {
grid-area headerPhoto
}
&:nth-child(3) {
grid-area bodyPhoto
}
}
}
</style>

View File

@ -81,6 +81,7 @@
</template> </template>
<OrderForm ref="orderForm" :research="research"/> <OrderForm ref="orderForm" :research="research"/>
<BookDetail ref="bookDetail"/> <BookDetail ref="bookDetail"/>
<InOutPanel ref="inOutPanel"/>
</ATablePage> </ATablePage>
</template> </template>
@ -92,10 +93,17 @@ import ATablePage, {
buildTablePageProps, buildTablePageProps,
} from '@/components/a-page/a-table-page/ATablePage.tsx' } from '@/components/a-page/a-table-page/ATablePage.tsx'
import BookDetail from '@/pages/order/book/BookDetail.vue' import BookDetail from '@/pages/order/book/BookDetail.vue'
import {
checkStatus,
transStatus,
} from '@/pages/order/constants.ts'
import InOutPanel from '@/pages/order/realtime/InOutPanel.vue'
import { ElMessage } from 'element-plus'
import { useTemplateRef } from 'vue'
const tablePageIns = useTemplateRef<ATablePageInstance>('tablePage') const tablePageIns = useTemplateRef<ATablePageInstance>('tablePage')
const bookDetailIns = useTemplateRef<InstanceType<typeof BookDetail>>('bookDetail') const bookDetailIns = useTemplateRef<InstanceType<typeof BookDetail>>('bookDetail')
const inOutPanelIns = useTemplateRef<InstanceType<typeof InOutPanel>>('inOutPanel')
function research() { function research() {
tablePageIns.value?.doSearch() tablePageIns.value?.doSearch()
@ -107,6 +115,7 @@ const tablePageProps = buildTablePageProps<OrderTypes.SearchOrderParam, OrderTyp
}, },
table: { table: {
actionColumn: { actionColumn: {
width: 100,
tableActions: [ tableActions: [
{ {
tooltip: '详情', tooltip: '详情',
@ -119,25 +128,37 @@ const tablePageProps = buildTablePageProps<OrderTypes.SearchOrderParam, OrderTyp
{ {
tooltip: '进场', tooltip: '进场',
type: 'info', type: 'info',
icon: 'Position', icon: 'approach',
show({row}) {
return row.transStatus === transStatus.YunShuZhong
},
action({row}) { action({row}) {
bookDetailIns.value?.open(row) inOutPanelIns.value?.open(row.id!, row.truckLicensePlate!, '进场')
}, },
}, },
{ {
tooltip: '出场', tooltip: '出场',
type: 'info', type: 'info',
icon: 'Position', icon: 'carexit',
show({row}) {
return row.transStatus === transStatus.YiJinChang && (row.checkStatus === checkStatus.Wu || row.checkStatus === checkStatus.YiKanLiao)
},
action({row}) { action({row}) {
bookDetailIns.value?.open(row) inOutPanelIns.value?.open(row.id!, row.truckLicensePlate!, '出场')
}, },
}, },
{ {
tooltip: '勘料', tooltip: '勘料',
type: 'info', type: 'info',
icon: 'Position', icon: 'chakanliaodan',
confirm: {
title: '是否确认勘料',
},
show({row}) {
return row.transStatus === transStatus.YiJinChang && row.checkStatus === checkStatus.WeiKanLiao
},
action({row}) { action({row}) {
bookDetailIns.value?.open(row) ElMessage.success(`${row.truckLicensePlate!}勘料成功`)
}, },
}, },
], ],
@ -192,10 +213,58 @@ const tablePageProps = buildTablePageProps<OrderTypes.SearchOrderParam, OrderTyp
estimatedTrainNum: 2, estimatedTrainNum: 2,
transDistance: 500, transDistance: 500,
orderCategoryTxt: '订单类型A', orderCategoryTxt: '订单类型A',
transStatusTxt: '运输中', transStatusTxt: '已进场',
transStatus: 'DaiPaiDan', transStatus: 'YiJinChang',
checkStatus: 'YiKanLiao',
checkStatusTxt: '未勘料', checkStatusTxt: '未勘料',
paymentStatusTxt: '未支付', paymentStatusTxt: '未支付',
truckLicensePlate: '苏A9B905',
},
{
rowCount: 1,
id: '123',
sn: '202308240001',
orderTime: '2023-08-24 10:00:00',
contacts: '张三',
phone: '13800000000',
projectName: '项目A',
stationName: '站点B',
goodsName: '商品X',
unit: '吨',
customerMemo: '客户备注',
estimatedQuantity: 100,
estimatedTrainNum: 2,
transDistance: 500,
orderCategoryTxt: '订单类型A',
transStatusTxt: '运输中',
transStatus: 'YunShuZhong',
checkStatus: 'WeiKanLiao',
checkStatusTxt: '未勘料',
paymentStatusTxt: '未支付',
truckLicensePlate: '苏A9B905',
},
{
rowCount: 1,
id: '123',
sn: '202308240001',
orderTime: '2023-08-24 10:00:00',
contacts: '张三',
phone: '13800000000',
projectName: '项目A',
stationName: '站点B',
goodsName: '商品X',
unit: '吨',
customerMemo: '客户备注',
estimatedQuantity: 100,
estimatedTrainNum: 2,
transDistance: 500,
orderCategoryTxt: '订单类型A',
transStatusTxt: '已进场',
transStatus: 'YiJinChang',
checkStatus: 'WeiKanLiao',
checkStatusTxt: '未勘料',
paymentStatusTxt: '未支付',
truckLicensePlate: '苏A9B905',
}, },
], ],
}, },