Commit 2b8b9c96 by wuqian

Merge branch 'dev' into 'master'

Dev

See merge request !272
parents 6846d780 be024416
......@@ -12,16 +12,12 @@ declare module 'vue' {
DatePicker: typeof import('./src/components/Form/DatePicker.vue')['default']
DateRangePicker: typeof import('./src/components/Form/DateRangePicker.vue')['default']
ElButton: typeof import('element-plus/es')['ElButton']
ElCard: typeof import('element-plus/es')['ElCard']
ElCarousel: typeof import('element-plus/es')['ElCarousel']
ElCarouselItem: typeof import('element-plus/es')['ElCarouselItem']
ElCheckbox: typeof import('element-plus/es')['ElCheckbox']
ElCheckboxGroup: typeof import('element-plus/es')['ElCheckboxGroup']
ElCol: typeof import('element-plus/es')['ElCol']
ElConfigProvider: typeof import('element-plus/es')['ElConfigProvider']
ElDatePicker: typeof import('element-plus/es')['ElDatePicker']
ElDescriptions: typeof import('element-plus/es')['ElDescriptions']
ElDescriptionsItem: typeof import('element-plus/es')['ElDescriptionsItem']
ElDialog: typeof import('element-plus/es')['ElDialog']
ElDrawer: typeof import('element-plus/es')['ElDrawer']
ElDropdown: typeof import('element-plus/es')['ElDropdown']
......@@ -33,15 +29,12 @@ declare module 'vue' {
ElIcon: typeof import('element-plus/es')['ElIcon']
ElImage: typeof import('element-plus/es')['ElImage']
ElInput: typeof import('element-plus/es')['ElInput']
ElInputNumber: typeof import('element-plus/es')['ElInputNumber']
ElLink: typeof import('element-plus/es')['ElLink']
ElOption: typeof import('element-plus/es')['ElOption']
ElPagination: typeof import('element-plus/es')['ElPagination']
ElPopover: typeof import('element-plus/es')['ElPopover']
ElRadio: typeof import('element-plus/es')['ElRadio']
ElRadioButton: typeof import('element-plus/es')['ElRadioButton']
ElRadioGroup: typeof import('element-plus/es')['ElRadioGroup']
ElRow: typeof import('element-plus/es')['ElRow']
ElSelect: typeof import('element-plus/es')['ElSelect']
ElSwitch: typeof import('element-plus/es')['ElSwitch']
ElTable: typeof import('element-plus/es')['ElTable']
......@@ -49,13 +42,12 @@ declare module 'vue' {
ElTabPane: typeof import('element-plus/es')['ElTabPane']
ElTabs: typeof import('element-plus/es')['ElTabs']
ElTag: typeof import('element-plus/es')['ElTag']
ElTimeline: typeof import('element-plus/es')['ElTimeline']
ElTimelineItem: typeof import('element-plus/es')['ElTimelineItem']
ElTooltip: typeof import('element-plus/es')['ElTooltip']
ElTree: typeof import('element-plus/es')['ElTree']
ElUpload: typeof import('element-plus/es')['ElUpload']
ElTreeSelect: typeof import('element-plus/es')['ElTreeSelect']
Icon: typeof import('./src/components/Icon.vue')['default']
ImageView: typeof import('./src/components/ImageView.vue')['default']
LeftRightLayout: typeof import('./src/components/leftRightLayout.vue')['default']
LogList: typeof import('./src/components/LogList.vue')['default']
NavMenu: typeof import('./src/components/NavMenu.vue')['default']
NavMenuItem: typeof import('./src/components/NavMenuItem.vue')['default']
......
......@@ -11,6 +11,7 @@ import type {
RestockData,
SearchForm,
StatusTreeNode,
StatusListNode,
} from '@/types/api/factoryOrderNew'
import { ResultInfoDataItem } from '@/types/api/order/common'
import { ExportParams } from '@/types/api/order/factoryOrderNew'
......@@ -37,6 +38,11 @@ export function getPodOrderStateGroupListApi() {
'factory/podOrder/findStateGroupList',
)
}
export function getStatusPushApi() {
return axios.get<never, BaseRespData<StatusListNode[]>>(
'factory/podOrderOperation/get-operation-order-status',
)
}
export function getFactoryOrderNewListApi(
data: SearchForm,
......
......@@ -3,9 +3,13 @@ export interface StatusTreeNode {
status: string
statusName: string
quantity: number
unit?: string // 数量单位
children: StatusTreeNode[] | null
}
export interface StatusListNode {
status: string
statusName: string
}
export interface SearchForm {
status?: string
pauseReason?: number
......
......@@ -2,11 +2,13 @@ export interface StatusTreeNode {
status: string
statusName: string
quantity: number
unit?: string // 数量单位
children: StatusTreeNode[] | null
}
export interface SearchForm {
status?: string
operationStatus?: string | string[]
pauseReason?: number
platform?: string
craftCode?: string | string[]
......@@ -103,6 +105,7 @@ export interface FactoryOrderNewListData {
totalCustomsValue?: number
pauseStatus?: number
statusName?: string
finishTime?:string
thirdOrderNumber?: string
customTagList?: { id: string; name: string }[]
}
......
......@@ -80,6 +80,7 @@ class Im {
private _onWsOpen(): void {
console.log('服务器连接成功')
this.num = 0
localStorage.setItem('socket_connect', 'online')
this._onOpenCallback?.()
this._startHeartbeat()
......
......@@ -27,6 +27,17 @@ export function useOrderStatusTree(options: UseOrderStatusTreeOptions) {
]
const specialLayoutStatuses = ['BATCH_MANAGE', 'AWAITING_RESTOCK']
/** 左侧状态栏数量单位 */
const STATUS_QUANTITY_UNIT: Record<string, string> = {
PENDING_SCHEDULE: '件',
PENDING_PICK: '件',
PENDING_REPLENISH: '件',
IN_PRODUCTION: '件',
PENDING_PACKING: '件',
BATCH_MANAGE: '批',
AWAITING_RESTOCK: '个',
}
const statusTree = ref<StatusTreeNode[]>()
const status = ref<string>('PENDING_RECEIVE')
const pendingAcceptSubTab = ref<
......@@ -149,7 +160,11 @@ export function useOrderStatusTree(options: UseOrderStatusTreeOptions) {
hasPermission ||
(isGroupContainer && filteredChildren.length > 0)
) {
result.push({ ...node, children: filteredChildren })
result.push({
...node,
children: filteredChildren,
unit: STATUS_QUANTITY_UNIT[node.status] ?? '单',
})
}
}
return result
......
......@@ -28,7 +28,7 @@
"
class="tree-node-count"
>
{{ `(${data.quantity})` }}
{{ `(${data.quantity}${data.unit ?? '单'})` }}
</span>
</div>
<span
......@@ -62,7 +62,7 @@
class="search-form"
:model="searchForm"
:inline="true"
label-width="70px"
label-width="110px"
>
<ElFormItem v-if="status === 'ALL'" label="订单状态">
<ElTreeSelect
......@@ -178,7 +178,24 @@
width="140px"
/>
</ElFormItem>
<ElFormItem v-if="status === 'PICKING'" label="操作单状态">
<ElSelect
v-model="searchFormOperationStatusSelect"
placeholder="请选择"
clearable
multiple
collapse-tags
filterable
style="width: 170px"
>
<ElOption
v-for="(item, index) in statusList"
:key="index"
:value="item.status"
:label="item.statusName"
></ElOption>
</ElSelect>
</ElFormItem>
<ElFormItem label="数量">
<el-radio-group v-model="searchForm.multi">
<el-radio-button :value="false" @click="toggleMulti(false)"
......@@ -1241,6 +1258,7 @@
</template>
<script setup lang="tsx">
import dayjs from 'dayjs'
import {
ArrowDown,
ArrowUp,
......@@ -1298,6 +1316,7 @@ import {
interceptUpdateApi,
interceptSuccessApi,
deliveryCompleteApi,
getStatusPushApi,
} from '@/api/factoryOrderNew'
import { getConfigApi } from '@/api/order'
import { getLogisticsWayApi } from '@/api/podUsOrder'
......@@ -1329,6 +1348,7 @@ import ChangeWayDialog from '@/views/order/podCN/components/ChangeWayDialog.vue'
import { ResultInfoDataItem } from '@/types/api/order/common'
import type { FactoryOrderSearchQueryVisibilityContext } from '@/types/api/factoryOrderNew/searchQueryVisibility'
import type { StatusTreeNode } from '@/types/api/order/factoryOrderNew'
import type { StatusListNode } from '@/types/api/factoryOrderNew'
import { useOrderDictionaries } from './hooks/useOrderDictionaries'
import { useOrderSearchForm } from './hooks/useOrderSearchForm'
import { useOrderStatusTree } from './hooks/useOrderStatusTree'
......@@ -1456,7 +1476,17 @@ const searchFormStatusSelect = computed({
searchForm.value.status = arr.length ? arr.join(',') : undefined
},
})
const searchFormOperationStatusSelect = computed({
get() {
const val = searchForm.value.operationStatus
if (!val) return []
return String(val).split(',').filter(Boolean)
},
set(val: string | string[] | undefined) {
const arr = Array.isArray(val) ? val : val ? [val] : []
searchForm.value.operationStatus = arr.length ? arr.join(',') : undefined
},
})
getFactoryOrderSearchVisibilityContext = () => ({
status: searchForm.value.status ?? status.value,
isCardLayout: isCardLayout.value,
......@@ -1699,11 +1729,28 @@ const handleStatusNodeClick = (node: { status: string }) => {
if (node.status !== 'CANCELLED') {
cancelledSubTab.value = 1
}
if (node.status === 'PICKING') {
getOrderStatus()
}
clearTableState()
if (!isSpecialLayout.value) {
refreshCurrentView()
}
}
const statusList = ref<StatusListNode[]>([])
const getOrderStatus = async () => {
try {
const res = await getStatusPushApi()
if (res.code === 200) {
statusList.value = res.data
} else {
ElMessage.error(res.message)
}
} catch (error) {
console.error(error)
ElMessage.error('订单状态刷新失败')
}
}
const handlePendingAcceptTabClick = (
tab: 'PENDING_RECEIVE' | 'ACCEPT_FAIL_OUT_OF_STOCK',
) => handlePendingAcceptTabClickRaw(tab, refreshTableList)
......@@ -1896,6 +1943,29 @@ const mainColumns = computed(() => [
width: 140,
align: 'center',
},
// 订单状态为已取消时隐藏延期时间列
...(status.value === 'CANCELLED'
? []
: [
{
prop: 'createTime',
label: '延期时间',
width: 140,
align: 'center',
render: (row: FactoryOrderNewListData) => {
const delayText = getOrderDelayText(
row.createTime ?? '',
row.finishTime ?? '',
row.status ?? '',
)
return (
<div style={{ color: 'red', fontWeight: 'bolder' }}>
{delayText}
</div>
)
},
},
]),
{
prop: 'logisticsWayName',
label: '物流类型',
......@@ -2588,6 +2658,34 @@ const handleUpdateCustomsInfo = async () => {
await nextTick()
updateCustomsDialogRef.value?.resetForm()
}
// 值为空:待接单和已取消状态
const NULL_STATUSES = ['PENDING_RECEIVE', 'CANCELLED']
// 终态订单:已完成/已归档
const TERMINAL_ORDER_STATUSES = ['COMPLETED', 'ARCHIVE']
const getOrderDelayText = (
createTime?: string | null,
finishTime?: string | null,
statusValue?: string | null,
) => {
if (!createTime) return ''
const currentStatus = statusValue ?? ''
if (TERMINAL_ORDER_STATUSES.includes(currentStatus) && !finishTime) return ''
const createAt = dayjs(createTime)
if (!createAt.isValid()) return ''
// 终态订单用 finishTime - createTime 的差值
// 其他状态用 当前时间 - createTime 的差值
const endTime =
TERMINAL_ORDER_STATUSES.includes(currentStatus) && finishTime
? dayjs(finishTime)
: dayjs()
const diffMinutes = Math.max(0, endTime.diff(createAt, 'minute'))
const baseHours = Math.floor(diffMinutes / 60)
const remainMinutes = diffMinutes % 60
const hours = baseHours + (remainMinutes > 30 ? 1 : 0)
const result = !NULL_STATUSES.includes(currentStatus) ? `${hours}h` : ''
return result
}
const handleTransferToArrange = async () => {
if (!ensureSelection('请选择订单')) return
......
......@@ -624,20 +624,16 @@ watch(visible, async (value: boolean) => {
if (userStore.user?.factory.id) {
try {
// 断线自动重连成功时会走 open 回调重新订阅
await socket.init(
{
account: userStore.user?.account.toString(),
factoryNo: userStore.user?.factory.id.toString(),
},
messageChange,
sendWsSubscribe,
)
socket.send({
code: props.wsOpenCode,
factoryNo: userStore.user?.factory.id,
warehouseId: warehouseId.value,
packingId:
props.wallType === 'sort' ? sortingAreaId.value : undefined,
})
sendWsSubscribe()
} catch (error) {
console.error(error)
}
......@@ -1536,6 +1532,17 @@ const _warehouseId = ref<string | number>('')
const sortingAreaList = ref<SortingList[]>([])
const sortingAreaId = ref<number | string>('')
const isAutoPrint = ref<boolean>(false) // 当前配货区是否自动打单
// WS 消息订阅
const sendWsSubscribe = (factoryNo?: number | string) => {
const no = factoryNo ?? userStore.user?.factory.id
if (!no) return
socket.send({
code: props.wsOpenCode,
factoryNo: no,
warehouseId: warehouseId.value,
packingId: props.wallType === 'sort' ? sortingAreaId.value : undefined,
})
}
// 配货分拣 更新配货区
const syncSortingArea = (
packingId: number | string,
......@@ -1547,12 +1554,7 @@ const syncSortingArea = (
warehouseId.value = currentArea.warehouseId
sortingAreaId.value = packingId
isAutoPrint.value = currentArea.autoPrint
socket.send({
code: props.wsOpenCode,
factoryNo,
warehouseId: warehouseId.value,
packingId: sortingAreaId.value,
})
sendWsSubscribe(factoryNo)
}
// 配货分拣 手动切换配货区
const handleSortingAreaChange = (
......@@ -1582,11 +1584,7 @@ const handleWarehouseChange = (value: string | number) => {
}
warehouseId.value = value
emit('set-warehouseId', value)
socket.send({
code: props.wsOpenCode,
factoryNo: userStore.user?.factory.id,
warehouseId: warehouseId.value,
})
sendWsSubscribe()
_warehouseId.value = value
initOrderDetailBox()
resetPlatform()
......@@ -1627,13 +1625,9 @@ const reconnectWebSocket = async () => {
factoryNo: userStore.user?.factory.id.toString(),
},
messageChange,
sendWsSubscribe,
)
socket.send({
code: props.wsOpenCode,
factoryNo: userStore.user?.factory.id,
warehouseId: warehouseId.value,
packingId: sortingAreaId.value,
})
sendWsSubscribe()
} catch (error) {
console.error('WebSocket 重连失败:', error)
ElMessage.error('WebSocket 重连失败,请稍后重试')
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment