Commit 8edcc9d1 by wuqian

Merge branch 'master' of http://47.99.244.21:9999/qinjianhui/factory_front into wq

parents 51a7f963 c65167f8
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
// Generated by unplugin-auto-import // Generated by unplugin-auto-import
export {} export {}
declare global { declare global {
const ElLoading: typeof import('element-plus/es')['ElLoading']
const ElMessage: typeof import('element-plus/es')['ElMessage'] const ElMessage: typeof import('element-plus/es')['ElMessage']
const ElMessageBox: typeof import('element-plus/es')['ElMessageBox'] const ElMessageBox: typeof import('element-plus/es')['ElMessageBox']
} }
import { BasePaginationData, BaseRespData } from '@/types/api'
import axios from './axios'
import {
SearchForm,
Tab,
ShipmentOrderRes,
PodDeliveryNoteSearchForm,
DeliveryNoteData,
LogListData,
} from '@/types/api/podOrder'
import { OrderData, ShipmentForm } from '@/types/api/order'
export function getOrderTabData() {
return axios.get<never, BaseRespData<Tab[]>>(
'factory/podJomallOrder/findStateGroupList',
)
}
export function getOrderList(
data: SearchForm,
currentPage: number,
pageSize: number,
) {
return axios.post<never, BasePaginationData<OrderData>>(
'/factory/podJomallOrder/list_page ',
{ ...data, currentPage, pageSize },
)
}
export function getOrderBySubOrderNumber(orderNumber: string) {
return axios.get<never, BaseRespData<OrderData>>(
'factory/podJomallOrder/getOrderBySubOrderNumber',
{
params: {
factorySubOrderNumber: orderNumber,
},
},
)
}
// 发货保存
export function saveOrder(
sumbitSendOutList: ShipmentOrderRes[],
from: ShipmentForm,
) {
return axios.post<never, BaseRespData<never>>(
'factory/podJomallOrder/sendOut',
{ sumbitSendOutList, ...from },
)
}
// 确认生产
export function confirmProductionOrder(ids: number[]) {
return axios.post<never, BaseRespData<never>>(
'factory/podJomallOrder/confirmProduce',
ids,
)
}
// 取消
export function cancelOrderApi(id: number) {
return axios.get<never, BaseRespData<never>>(
'factory/podJomallOrder/cancel',
{ params: { id } },
)
}
// 下载素材
export function downloadMaterialApi(id: Array<number>) {
return axios.post<never, BaseRespData<Array<string>>>(
'factory/podJomallOrder/downloadByProduction',
id,
)
}
// 添加内部便签
export function addInternalTagApi(idList: number[], memo: string) {
return axios.post<never, BaseRespData<never>>(
'factory/podJomallOrder/batchAddInternalMemo',
{ idList, content: memo },
)
}
// 打印生产单
export function printOrder(ids: number[]) {
return axios.post<never, BaseRespData<never>>(
'factory/podJomallOrder/printProducePdf',
ids,
)
}
// 发货单
export function getDeliveryNoteList(
data: PodDeliveryNoteSearchForm,
currentPage: number,
pageSize: number,
) {
return axios.post<never, BasePaginationData<DeliveryNoteData>>(
'factory/podJomallShipment/list_page',
{ ...data, currentPage, pageSize },
)
}
// 操作日志
export function getLogList(id: number) {
return axios.get<never, BaseRespData<LogListData[]>>(
'factory/logPodJomallOrder/getLogByOrderId',
{
params: {
id,
},
},
)
}
export function printDeliveryNote(data: string[], userMark?: string) {
return axios.post<never, BaseRespData<never>>(
'factory/podJomallShipment/printInvoiceStatistics',
{
startTime: data && data[0],
userMark,
endTime: data && data[1],
},
)
}
export function updateRemarkApi(id: number, remark: string) {
return axios.get<never, BaseRespData<never>>(
'factory/podJomallOrder/setRemark',
{
params: {
productId:id,
remark,
},
},
)
}
<template> <template>
<template v-if="col.subs?.length"> <template v-if="col.subs?.length">
<ElTableColumn header-align="center" :label="col.label" :align="col.align" v-bind="col"> <ElTableColumn
<RenderColumn v-for="item in col.children" :col="item" :key="item.prop"> header-align="center"
:label="col.label"
:align="col.align"
v-bind="col"
>
<RenderColumn v-for="item in col.subs" :col="item" :key="item.prop">
<template <template
v-for="slot in Object.keys($slots)" v-for="(_, name) of slots"
#[slot]="scope: Record<string, any>" #[name]="scope: Record<string, any>"
> >
<slot :name="slot" v-bind="scope" /> <slot :name="name" v-bind="scope" />
</template> </template>
</RenderColumn> </RenderColumn>
<template #header="{ column, $index }"> <template #header="{ column, $index }">
...@@ -66,7 +71,7 @@ ...@@ -66,7 +71,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import type { Column } from 'element-plus' import type { Column } from 'element-plus'
import type { PropType } from 'vue' import { type Slot, useSlots, type PropType } from 'vue'
defineProps({ defineProps({
col: { col: {
...@@ -74,4 +79,5 @@ defineProps({ ...@@ -74,4 +79,5 @@ defineProps({
required: true, required: true,
}, },
}) })
const slots = useSlots() as Record<string, Slot>
</script> </script>
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
:data="paginatedData" :data="paginatedData"
border border
:stripe="stripe" :stripe="stripe"
v-bind="attrs"
header-align="center" header-align="center"
height="100%" height="100%"
> >
...@@ -18,61 +19,51 @@ ...@@ -18,61 +19,51 @@
header-align="center" header-align="center"
> >
<!-- 当type等于expand时, 配置通过h函数渲染、txs语法或者插槽自定义内容 --> <!-- 当type等于expand时, 配置通过h函数渲染、txs语法或者插槽自定义内容 -->
<template #default="{ row, $index }"> <template #default="scope">
<component <component
:is="column.render" :is="column.render"
v-if="column.render" v-if="column.render"
:row="row" :row="scope.row"
:index="$index" :index="scope.$index"
/> />
<slot <slot
v-else-if="column.slot" v-else-if="column.slot"
name="expand" name="expand"
:row="row" :row="scope.row"
:index="$index" :index="scope.$index"
></slot> ></slot>
</template> </template>
</ElTableColumn> </ElTableColumn>
<RenderColumn v-else :col="column"> <RenderColumn v-else :col="column">
<template <template v-for="(_, name) of slots" #[name]="scope">
v-for="slot in Object.keys($slots)" <slot :name="name" v-bind="scope" />
#[slot]="scope: Record<string, any>"
>
<slot :name="slot" v-bind="scope" />
</template> </template>
</RenderColumn> </RenderColumn>
</template> </template>
</ElTable> </ElTable>
</div> </div>
</template> </template>
<script setup lang="tsx"> <script setup lang="tsx" generic="T">
import type { Column } from 'element-plus/lib/components/index.js' import { type Slot, useAttrs, useSlots, type PropType } from 'vue'
import type { PropType } from 'vue' import type { CustomColumn } from '@/types/table'
import RenderColumn from './RenderColumn.vue' import RenderColumn from './RenderColumn.vue'
// eslint-disable-next-line @typescript-eslint/no-explicit-any // eslint-disable-next-line @typescript-eslint/no-explicit-any
export type DataItemType = Record<string, any>
defineProps({ defineProps({
paginatedData: { paginatedData: {
type: Array as PropType<DataItemType[]>, type: Array,
required: true, required: true,
}, },
columns: { columns: {
type: Array as PropType<Column[]>, type: Array as PropType<CustomColumn<T>>,
required: true, required: true,
}, },
stripe: { stripe: {
type: Boolean, type: Boolean,
default: false, default: false,
}, },
defaultSortProp: {
type: String,
default: '',
},
defaultSortOrder: {
type: String,
default: '',
},
}) })
const attrs = useAttrs()
const slots = useSlots() as Record<string, Slot>
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
......
...@@ -17,6 +17,8 @@ import UserPage from '@/views/UserPage.vue' ...@@ -17,6 +17,8 @@ import UserPage from '@/views/UserPage.vue'
import DeliveryNotePage from '@/views/DeliveryNotePage.vue' import DeliveryNotePage from '@/views/DeliveryNotePage.vue'
import AccountStatementNote from '@/views/AccountStatementNote.vue' import AccountStatementNote from '@/views/AccountStatementNote.vue'
import TypeseetingManagement from '@/views/typesetting/TypesettingManagement.vue' import TypeseetingManagement from '@/views/typesetting/TypesettingManagement.vue'
import PodOrderList from '@/views/order/pod/index.vue'
import PodDeliveryNoteList from '@/views/order/pod/deliveryOrderList.vue'
const router = createRouter({ const router = createRouter({
history: createWebHistory(), history: createWebHistory(),
...@@ -35,6 +37,14 @@ const router = createRouter({ ...@@ -35,6 +37,14 @@ const router = createRouter({
component: OrderList, component: OrderList,
}, },
{ {
path: '/pod-order/list',
component: PodOrderList,
},
{
path: '/pod-delivery-note/list',
component: PodDeliveryNoteList,
},
{
path: '/production/complete', path: '/production/complete',
component: ProductionComplete, component: ProductionComplete,
}, },
......
export interface MenuItem { export interface MenuItem {
index: string, index: string
id: number, id: number
label: string, label: string
children?: MenuItem[], children?: MenuItem[]
} }
const menu: MenuItem[] = [ const menu: MenuItem[] = [
...@@ -15,19 +15,31 @@ const menu: MenuItem[] = [ ...@@ -15,19 +15,31 @@ const menu: MenuItem[] = [
index: '/order/list', index: '/order/list',
id: 2, id: 2,
label: '订单', label: '订单',
},{ },
{
index: '/pod-order/list',
id: 7,
label: 'POD订单',
},
{
index: '/account/statement-note', index: '/account/statement-note',
id: 3, id: 3,
label: '对账单', label: '对账单',
},{ },
{
index: '/system/delivery-note', index: '/system/delivery-note',
id: 4, id: 4,
label: '发货单', label: '发货单',
}, },
{ {
index: '/pod-delivery-note/list',
id: 8,
label: 'POD发货单',
},
{
index: '/typesetting-management/list', index: '/typesetting-management/list',
id: 5, id: 5,
label:'打版管理' label: '打版管理',
}, },
{ {
index: '', index: '',
...@@ -39,8 +51,7 @@ const menu: MenuItem[] = [ ...@@ -39,8 +51,7 @@ const menu: MenuItem[] = [
id: 4, id: 4,
label: '用户管理', label: '用户管理',
}, },
],
]
}, },
// { // {
// index: '', // index: '',
......
...@@ -111,3 +111,6 @@ img { ...@@ -111,3 +111,6 @@ img {
flex: 1; flex: 1;
overflow: hidden; overflow: hidden;
} }
.mt-10 {
margin-top: 10px;
}
...@@ -24,7 +24,6 @@ export interface Tab { ...@@ -24,7 +24,6 @@ export interface Tab {
quantity: number quantity: number
} }
export interface OrderData { export interface OrderData {
id: number id: number
namespace?: string namespace?: string
...@@ -71,6 +70,7 @@ export interface OrderData { ...@@ -71,6 +70,7 @@ export interface OrderData {
sourceType?: string sourceType?: string
moreable?: boolean moreable?: boolean
manuscriptStatus?: number manuscriptStatus?: number
factoryOrderNumber?: string | undefined
} }
export interface ProductList { export interface ProductList {
...@@ -99,6 +99,11 @@ export interface ProductList { ...@@ -99,6 +99,11 @@ export interface ProductList {
version?: string | number version?: string | number
passNum?: number passNum?: number
notPassNum?: number notPassNum?: number
sendOutQuantity?: number
factoryOrderNumber?: string | undefined
thirdSubOrderNumber?: string
namespace?: string
factorySubOrderNumber?: string
} }
export interface MemoList { export interface MemoList {
......
export interface Tab {
status?: string
statusName?: string
quantity?: number
}
export interface SearchForm {
status?: string | null
factorySubOrderNumber?: string
factoryOrderNumber?: string
shipmentNumber?: string
order?: string
prop?: string
shopNumber?: string
shopNumbers?: string[]
sku?: string
timeType?: number | null
startTime?: string | null
endTime?: string | null
internalMemo?: string
}
export interface OrderData {
id: number
moreable: boolean
thirdOrderNumber: string
factoryOrderNumber: string
factoryOnlineId: number | null
factoryNo: number | null
factoryCode: string | null
status: string | number
weight: number | null
outCurrencyCode: string | null
factoryProductAmount: number | null
thirdProductAmount: number | null
productAmount: number | null
carriageAmount: number | null
totalAmount: number | null
productNum: number | null
trackStatus: string | null
finishTime: string | null
paymentTime: string | null
startStockingTime: string | null
lanshouName: string | null
lanshouPhone: string | null
lanshouRegion: string | null
lanshouAddress: string | null
lanshouPost: string | null
manuscriptUrl: string | null
manuscriptStatus: number | null
createTime: string | null
updateTime: string | null
remark: string | null
userMark: string | null
namespace: string | null
productList: ProductList[]
internalMemoList: string | null
}
export interface ProductList {
id: number
podOrderId?: number
thirdSubOrderNumber?: string
factorySubOrderNumber?: string
shopNumber?: string
baseSku?: string
productName?: string
variantSku?: string
variantImage?: string
imageAry?: string
designImages?: string
process?: string
num?: number
shipmentNu?: number
costPrice?: number
price?: number
processPrice?: number
weight?: number
version?: number
remark?: string
createTime?: string
updateTime?: string
}
export interface ShipmentOrderRes {
factoryOrderNumber?: string
thirdSubOrderNumber?: string
sendOutQuantity?: number
version?: number
namespace?: string
}
export interface PodDeliveryNoteSearchForm {
startTime?: string
endTime?: string
shipmentNumber?: string
factoryOrderNumber?: string
factorySubOrderNumber?: string
shippingWay?: number
logisticsTracking?: string
lanshouName?: string
sku?: string
userMark?: string
}
export interface DeliveryNoteData {
id: number
factoryOrderNumber?: string
factoryNo?: number
shipmentNumber?: string
shipmentUid?: string
shippingWay?: number
carriageAmount?: string
carriageName?: string
logisticsTracking?: string
shippingStatus?: string
lanshouName?: string
lanshouPhone?: string
lanshouRegion?: string
lanshouAddress?: string
lanshouPost?: string
updateTime?: string
createTime?: string
shipmentDetails?: ShipmentDetail[]
}
export interface ShipmentDetail {
id: number
shipmentId?: number
podOrderId?: number
podOrderNumber?: string
subOrderNumber?: string
baseSku?: string
variantSku?: string
variantImage?: string
shipmentNum?: number
updateTime?: string
createTime?: string
}
export interface LogListData {
id: number
bizId?: number
userId?: number
employeeName?: string
description?: string
createTime?: string
}
export interface LogListData {
id: number
bizId?: number
userId?: number
employeeName?: string
description?: string
createTime?: string
}
import type { Column as ElColumn } from 'element-plus/lib/components/index.js'
interface Column<D> extends Omit<ElColumn, 'width'> {
width?: number | string
minWidth?: number | string
key?: string
slot?: string
headerSlot?: string
headerRender?: (scope: { column: never; index: number }) => JSX.Element
render?: (scope: { row: D; index: number }) => JSX.Element
formatDate?: (row: D) => string
children?: Column<D>[]
subs?: Column<D>[]
buttons?: {
text: string
type?: 'primary' | 'success' | 'warning' | 'danger' | 'info'
onClick: (row: D) => void
show?: (row: D) => boolean
}[]
}
export interface TableProps<T> {
data: T[]
columns: Column<T>[]
stripe?: boolean
border?: boolean
height?: string | number
maxHeight?: string | number
showPagination?: boolean
pageSize?: number
currentPage?: number
total?: number
loading?: boolean
rowKey?: string | ((row: T) => string)
selectable?: boolean
selectedRows?: T[]
}
export type CustomColumn<D> = Column<D>
...@@ -38,14 +38,14 @@ export default function usePageList<T>(options: UsePageListOptions<T>) { ...@@ -38,14 +38,14 @@ export default function usePageList<T>(options: UsePageListOptions<T>) {
currentPage.value = res.page!.current currentPage.value = res.page!.current
pageSize.value = res.page!.size pageSize.value = res.page!.size
data.value = res.page!.records data.value = res.page!.records
}else{ } else {
total.value = res.total total.value = res.total
currentPage.value = res.current currentPage.value = res.current
pageSize.value = res.size pageSize.value = res.size
data.value = res.records data.value = res.records
} }
} catch (error) { } catch (error) {
console.error(error)
// showError(error) // showError(error)
} finally { } finally {
loading.value = false loading.value = false
......
...@@ -72,13 +72,26 @@ ...@@ -72,13 +72,26 @@
</span> </span>
</div> </div>
<div <div
:title="item.subOrderNumber || ''" :title="
isPod ? item.factorySubOrderNumber || '' : item.subOrderNumber || ''
"
class="order-list-expand_item_info_title" class="order-list-expand_item_info_title"
> >
<span class="order-list-expand_item_label">生产单号:</span> <span class="order-list-expand_item_label">生产单号:</span>
<span class="order-list-expand_item_value" <span class="order-list-expand_item_value"
>{{ item.subOrderNumber || '--' >{{
}}<el-icon class="icon" @click="copy(item.subOrderNumber || '')" isPod
? item.factorySubOrderNumber || ''
: item.subOrderNumber || '--'
}}<el-icon
class="icon"
@click="
copy(
isPod
? item.factorySubOrderNumber || ''
: item.subOrderNumber || '',
)
"
><DocumentCopy ><DocumentCopy
/></el-icon> /></el-icon>
</span> </span>
...@@ -108,8 +121,10 @@ ...@@ -108,8 +121,10 @@
</span> </span>
</div> </div>
<div <div
v-if="!isPod"
:title="item.material || ''" :title="item.material || ''"
class="order-list-expand_item_info_title" class="order-list-expand_item_info_title"
> >
<span class="order-list-expand_item_label">材质:</span> <span class="order-list-expand_item_label">材质:</span>
<span class="order-list-expand_item_value" <span class="order-list-expand_item_value"
...@@ -155,20 +170,20 @@ ...@@ -155,20 +170,20 @@
(item.num || 0) - ((item.shipmentNum || 0) - (item.notPassNum || 0)) (item.num || 0) - ((item.shipmentNum || 0) - (item.notPassNum || 0))
}}</span> }}</span>
</div> </div>
<div class="order-list-expand_item_info_title"> <div v-if="!isPod" class="order-list-expand_item_info_title">
<span class="order-list-expand_item_label">待质检:</span> <span class="order-list-expand_item_label">待质检:</span>
<span class="order-list-expand_item_value">{{ <span class="order-list-expand_item_value">{{
(item.shipmentNum || 0) - (item.shipmentNum || 0) -
((item.passNum || 0) + (item.notPassNum || 0)) ((item.passNum || 0) + (item.notPassNum || 0))
}}</span> }}</span>
</div> </div>
<div class="order-list-expand_item_info_title"> <div v-if="!isPod" class="order-list-expand_item_info_title">
<span class="order-list-expand_item_label">质检(通过):</span> <span class="order-list-expand_item_label">质检(通过):</span>
<span class="order-list-expand_item_value">{{ <span class="order-list-expand_item_value">{{
item.passNum || 0 item.passNum || 0
}}</span> }}</span>
</div> </div>
<div class="order-list-expand_item_info_title"> <div v-if="!isPod" class="order-list-expand_item_info_title">
<span class="order-list-expand_item_label">质检(不通过):</span> <span class="order-list-expand_item_label">质检(不通过):</span>
<span class="order-list-expand_item_value">{{ <span class="order-list-expand_item_value">{{
item.notPassNum || 0 item.notPassNum || 0
...@@ -214,7 +229,11 @@ defineProps({ ...@@ -214,7 +229,11 @@ defineProps({
default: false, default: false,
}, },
status: { status: {
type: Number, type: [Number, String],
},
isPod: {
type: Boolean,
default: false,
}, },
}) })
interface InterItem { interface InterItem {
......
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
<div class="shipment-info-item-header--title"> <div class="shipment-info-item-header--title">
<span class="shipment-info-item-header__label">主单号:</span> <span class="shipment-info-item-header__label">主单号:</span>
<span class="shipment-info-item-header__value">{{ <span class="shipment-info-item-header__value">{{
o.orderNumber || '--' isPod ? o.factoryOrderNumber || '--' : o.orderNumber || '--'
}}</span> }}</span>
</div> </div>
<div class="shipment-info-item-header--title"> <div class="shipment-info-item-header--title">
...@@ -60,7 +60,10 @@ ...@@ -60,7 +60,10 @@
:key="item.id" :key="item.id"
:data-id="item.id" :data-id="item.id"
:class="{ :class="{
'active-row': item.subOrderNumber === currentRow.subOrderNumber, 'active-row': isPod
? item.factorySubOrderNumber ===
currentRow.factorySubOrderNumber
: item.subOrderNumber === currentRow.subOrderNumber,
}" }"
class="shipment-info-item-content-item" class="shipment-info-item-content-item"
@click="onRowClick(item, o)" @click="onRowClick(item, o)"
...@@ -74,7 +77,11 @@ ...@@ -74,7 +77,11 @@
</div> </div>
<div class="shipment-info-item-content-item--info"> <div class="shipment-info-item-content-item--info">
<span class="label">生产单号:</span> <span class="label">生产单号:</span>
<span class="value">{{ item.subOrderNumber || '--' }}</span> <span class="value">{{
isPod
? item.factorySubOrderNumber || '--'
: item.subOrderNumber || '--'
}}</span>
</div> </div>
<div <div
class="shipment-info-item-content-item--info" class="shipment-info-item-content-item--info"
...@@ -148,8 +155,13 @@ ...@@ -148,8 +155,13 @@
<span class="order-norm-label" style="margin-right: 6px"> <span class="order-norm-label" style="margin-right: 6px">
订单号: 订单号:
</span> </span>
<span class="order-norm-value" :title="currentRow.orderNumber"> <span
{{ currentRow.orderNumber }} class="order-norm-value"
:title="
isPod ? currentRow.factoryOrderNumber : currentRow.orderNumber
"
>
{{ isPod ? currentRow.factoryOrderNumber : currentRow.orderNumber }}
</span> </span>
</div> </div>
</div> </div>
...@@ -168,7 +180,7 @@ import { showConfirm } from '@/utils/ui' ...@@ -168,7 +180,7 @@ import { showConfirm } from '@/utils/ui'
const emit = defineEmits<{ const emit = defineEmits<{
(e: 'changeCurrentRow', v: ProductList): void (e: 'changeCurrentRow', v: ProductList): void
}>() }>()
defineProps({ const props = defineProps({
orderList: { orderList: {
type: Array as PropType<OrderData[]>, type: Array as PropType<OrderData[]>,
default: () => [], default: () => [],
...@@ -177,6 +189,10 @@ defineProps({ ...@@ -177,6 +189,10 @@ defineProps({
type: Object as PropType<ProductList>, type: Object as PropType<ProductList>,
default: () => {}, default: () => {},
}, },
isPod: {
type: Boolean,
default: false,
},
}) })
const listRef = ref<HTMLDivElement>() const listRef = ref<HTMLDivElement>()
...@@ -192,7 +208,11 @@ defineExpose<ShipmentType>({ ...@@ -192,7 +208,11 @@ defineExpose<ShipmentType>({
}) })
const onRowClick = (item: ProductList, o: OrderData) => { const onRowClick = (item: ProductList, o: OrderData) => {
item.orderNumber = o.orderNumber if (props.isPod) {
item.factoryOrderNumber = o.factoryOrderNumber
} else {
item.orderNumber = o.orderNumber
}
emit('changeCurrentRow', item) emit('changeCurrentRow', item)
} }
const onShipment = async (item: ProductList) => { const onShipment = async (item: ProductList) => {
......
import {
ICompareObjects,
OrderData,
ProductList,
ShipmentForm,
} from '@/types/api/order'
import { useValue } from '@/utils/hooks/useValue'
import { nextTick, ref } from 'vue'
import { ShipmentType } from '../Shipment.vue'
import { getOrderBySubOrderNumber, saveOrder } from '@/api/podOrder'
import { ShipmentOrderRes } from '@/types/api/podOrder'
export default function useShipment(callback?: () => void) {
const [orderList, resetOrderList] = useValue<OrderData[]>([])
const [shipmentForm, resetShipmentForm] = useValue<ShipmentForm>({
shippingWay: '',
carriageName: '',
logisticsTracking: '',
carriageAmount: '',
namespace: '',
})
const firstResult = ref<ICompareObjects | null>(null)
const shipmentFormRef = ref()
const productionOrderNumber = ref('')
const shipmentVisible = ref(false)
const isLock = ref(false)
const inputRef = ref()
// const currentRow = ref<ProductList>()
const currentRow = ref<ProductList | undefined>(undefined)
const shipmentLoading = ref(false)
const shipmentOrderRef = ref<ShipmentType>()
const searchShipmentByOrderNumber = async () => {
const code = productionOrderNumber.value
shipmentVisible.value = true
if (!code) {
isLock.value = false
inputRef.value.focus()
playAudio('picking_warning')
return ElMessage({
message: '请录入生产单号',
type: 'warning',
offset: window.innerHeight / 2,
})
}
let rowData
const code1 = code?.split('_')[0]
for (const item of orderList.value) {
rowData = item.productList?.find((jj) => jj.factorySubOrderNumber === code1)
if (rowData) {
rowData.factoryOrderNumber = item.factoryOrderNumber
break
}
}
if (rowData) {
currentRow.value = rowData
const unShipmentNum =
(rowData.num || 0) -
(rowData.shipmentNum || 0) +
(rowData.notPassNum || 0)
if (unShipmentNum > (rowData.count || 0)) {
rowData.count = (rowData.count || 0) + 1
playAudio('picking_check_success')
} else {
playAudio('picking_beyond')
ElMessage({
message: '拣货数不能大于未发数',
type: 'warning',
offset: window.innerHeight / 2,
})
}
inputRef.value.focus()
productionOrderNumber.value = ''
isLock.value = false
await nextTick()
if (shipmentOrderRef.value) {
const rowEl = shipmentOrderRef.value.listRef?.querySelector(
`div[data-id="${rowData.id}"]`,
)
if (!rowEl) return
rowEl.scrollIntoView()
}
} else {
getPackingData(code)
}
}
const getPackingData = async (code: string) => {
try {
shipmentLoading.value = true
const res = await getOrderBySubOrderNumber(code)
if (res.data) {
if (firstResult.value === null) {
firstResult.value = res.data
canJoin(res.data, code)
} else {
if (!compareObjects(firstResult.value, res.data)) {
ElMessageBox.confirm('不能加入,地址信息不一致!', '重要提示', {
confirmButtonText: '确定',
type: 'warning',
}).catch(() => {})
} else {
canJoin(res.data, code)
}
}
}
inputRef.value.focus()
productionOrderNumber.value = ''
isLock.value = false
} catch (e) {
productionOrderNumber.value = ''
isLock.value = false
inputRef.value.focus()
//showError(e)
playAudio('picking_search_error')
} finally {
shipmentLoading.value = false
}
}
const canJoin = async (data: OrderData, code: string) => {
const code1 = code?.split('_')[0]
for (const item of data.productList || []) {
item.count = 0
if (item.factorySubOrderNumber === code1) {
item.count = 1
currentRow.value = item
if (currentRow.value) {
currentRow.value.factoryOrderNumber = data.factoryOrderNumber
}
}
}
const index = orderList.value.findIndex((item) => item.id === data.id)
if (index === -1) {
orderList.value.unshift(data)
}
playAudio('picking_search_success')
await nextTick()
if (shipmentOrderRef.value) {
const rowEl = shipmentOrderRef.value.listRef?.querySelector(
`div[data-id="${currentRow.value?.id}"]`,
)
if (!rowEl) return
rowEl.scrollIntoView()
}
}
const compareObjects = (
obj1: ICompareObjects,
obj2: ICompareObjects,
): boolean => {
const keysToCompare: (keyof ICompareObjects)[] = [
'lanshouAddress',
'lanshouName',
'lanshouPhone',
'lanshouPost',
'lanshouRegion',
]
return keysToCompare.every((key) => obj1[key] === obj2[key])
} //将后面的查询结果和第一次的结果进行比较,相同才能加入,反之不能
const saveShipment = async () => {
if (orderList.value.length === 0) {
return ElMessage({
message: '请选择订单',
type: 'warning',
offset: window.innerHeight / 2,
})
}
try {
await shipmentFormRef.value?.validate()
} catch {
return
}
const isEqual = orderList.value.some(
(item) => item.namespace === orderList.value[0].namespace,
)
if (!isEqual) {
ElMessage({
message: '请选择同一工厂的订单',
type: 'warning',
offset: window.innerHeight / 2,
})
return
}
const data: ShipmentOrderRes[] = []
orderList.value.forEach((item) => {
shipmentForm.value.namespace = item.namespace
const order = []
for (const jj of item.productList || []) {
if (jj.count === 0) continue
const subOrder = {
factorySubOrderNumber: jj.factorySubOrderNumber,
thirdSubOrderNumber: jj.thirdSubOrderNumber,
sendOutQuantity: jj.count,
version: jj.version as number,
namespace: item.namespace,
}
order.push(subOrder)
}
if (order) {
data.push(...order)
}
})
try {
const res = await saveOrder(data, shipmentForm.value)
fetch(res.message || '')
.then((response) => {
// 确保响应是 OK
if (!response.ok) {
throw new Error('网络响应错误')
}
// 返回图片的二进制数据(Blob)
return response.blob()
})
.then((blob) => {
const a = document.createElement('a')
a.href = window.URL.createObjectURL(blob)
a.target = '_blank'
a.download = (res.message as string).split('/')[
(res.message as string).split('/').length - 1
]
a.click()
})
.catch((error) => {
console.error('下载图片时出错:', error)
})
ElMessage.success('发货成功')
shipmentVisible.value = false
callback && callback()
} catch (e) {
// showError(e)
}
}
const confirmDelivery = async () => {
shipmentVisible.value = true
productionOrderNumber.value = ''
firstResult.value = null //弹窗打开前先清空第一次的结果
resetOrderList()
resetShipmentForm()
}
const onShipmentDialogOpened = () => {
inputRef.value?.focus()
shipmentFormRef.value?.clearValidate()
}
/**
* 拣货
* picking_warning 输入错误
* picking_beyond SKU数量超出
* picking_check_success 验证成功
* picking_search_error 查询失败
* picking_search_success 查询成功
*/
const playAudio = (key: string) => {
const audio = new Audio()
const audioPath = new URL(
`../../../assets/audio/${key}.mp3`,
import.meta.url,
).href
audio.src = audioPath
audio.play()
}
return {
shipmentFormRef,
shipmentOrderRef,
productionOrderNumber,
shipmentVisible,
inputRef,
shipmentForm,
currentRow,
shipmentLoading,
orderList,
searchShipmentByOrderNumber,
saveShipment,
confirmDelivery,
onShipmentDialogOpened,
}
}
...@@ -1163,17 +1163,17 @@ const { ...@@ -1163,17 +1163,17 @@ const {
{ {
...searchForm.value, ...searchForm.value,
status: statusCode.value, status: statusCode.value,
timeType: timeRange.value.length > 0 ? searchForm.value.timeType : null, timeType: timeRange.value && timeRange.value.length > 0 ? searchForm.value.timeType : null,
startTime: startTime:
searchForm.value.timeType === '' searchForm.value.timeType === ''
? null ? null
: timeRange.value.length > 0 : timeRange.value && timeRange.value.length > 0
? timeRange.value[0] ? timeRange.value[0]
: null, : null,
endTime: endTime:
searchForm.value.timeType === '' searchForm.value.timeType === ''
? null ? null
: timeRange.value.length > 0 : timeRange.value && timeRange.value.length > 0
? timeRange.value[1] ? timeRange.value[1]
: null, : null,
}, },
......
<template>
<div class="card flex-column h-100 overflow-hidden">
<div class="header-filter-form">
<ElForm :model="searchForm" inline>
<ElFormItem label="发货时间">
<el-date-picker
:default-time="[
new Date(0, 0, 0, 0, 0, 0),
new Date(0, 0, 0, 23, 59, 59),
]"
v-model="dateRange"
type="datetimerange"
start-placeholder="开始时间"
end-placeholder="结束时间"
clearable
style="width: 360px"
format="YYYY-MM-DD HH:mm:ss"
value-format="YYYY-MM-DD HH:mm:ss"
/>
</ElFormItem>
<ElFormItem label="客户">
<ElSelect
v-model="searchForm.userMark"
clearable
placeholder="请选择客户"
style="width: 160px"
>
<ElOption
v-for="item in nameSpaceList"
:key="item"
:label="item"
:value="item"
></ElOption>
</ElSelect>
</ElFormItem>
<ElFormItem label="订单号">
<ElInput
v-model="searchForm.factoryOrderNumber"
clearable
placeholder="请输入订单号"
style="width: 160px"
/>
</ElFormItem>
<ElFormItem label="子单号">
<ElInput
v-model="searchForm.factorySubOrderNumber"
placeholder="子订单"
clearable
style="width: 160px"
></ElInput>
</ElFormItem>
<ElFormItem label="收货人">
<ElInput
v-model="searchForm.lanshouName"
clearable
placeholder="请输入收货人"
style="width: 160px"
/>
</ElFormItem>
<ElFormItem label="送货方式">
<ElSelect
v-model="searchForm.shippingWay"
clearable
placeholder="请选择送货方式"
style="width: 160px"
>
<ElOption label="快递" :value="2"></ElOption>
<ElOption label="送货上门" :value="1"></ElOption>
</ElSelect>
</ElFormItem>
<ElFormItem label="物流跟踪号" prop="logisticsTracking">
<ElInput
v-model="searchForm.logisticsTracking"
placeholder="请输入物流跟踪号"
clearable
style="width: 160px"
/>
</ElFormItem>
<ElFormItem>
<ElButton type="primary" @click="search">查询</ElButton>
</ElFormItem>
<ElFormItem>
<ElButton @click="resetSearchForm">重置</ElButton>
</ElFormItem>
<ElFormItem>
<ElButton type="success" @click="onConfirmDateRange"
>导出发货单</ElButton
>
</ElFormItem>
</ElForm>
</div>
<div class="delivery-note-content flex-1 flex-column overflow-hidden">
<div class="delivery-note-list flex-1 overflow-hidden">
<TableView
:paginated-data="tableData"
:columns="tableColumns"
:cell-class-name="onCellClassName"
>
<template #deliveryNote="{ row }">
<div class="send-order-column">
<div
v-for="od in row.shipmentDetails"
:key="od.id"
class="send-order-product-item"
>
<div class="send-order-product-image">
<img :src="od.variantImage" :alt="od.productName" />
</div>
<div
v-for="(p, i) in productProps"
:key="i"
class="send-order-prop-list"
>
<div
v-for="(pi, index) in p"
:key="index"
:title="pi.label + ':' + val(od, pi.key || '')"
class="send-order-prop-item"
>
<span> {{ pi.label }}{{ val(od, pi.key || '') }} </span>
<el-icon
v-if="pi.copyable"
class="icon"
@click="copy(val(od, pi.key || ''))"
>
<DocumentCopy />
</el-icon>
</div>
</div>
</div>
</div>
</template>
<template #logisticsInfo="{ row }">
<div class="send-order-logistics">
<div
v-for="pi in logisticsProps"
:key="pi.label"
:title="pi.label + ':' + val(row, pi.key)"
class="send-order-prop-item"
>
<span v-if="val(row, pi.key)"
>{{ pi.label }}{{ val(row, pi.key) }}</span
>
<el-icon
v-if="pi.copyable && val(row, pi.key)"
class="icon"
@click="copy(val(row, pi.key || ''))"
>
<DocumentCopy />
</el-icon>
</div>
</div>
</template>
</TableView>
</div>
<ElPagination
v-model:current-page="currentPage"
v-model:page-size="pageSize"
:page-sizes="[100, 200, 300, 400]"
background
layout="total, sizes, prev, pager, next, jumper"
:total="total"
style="margin: 10px auto 0; text-align: right"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
></ElPagination>
</div>
</div>
</template>
<script lang="tsx" setup>
import { getUserMarkList } from '@/api/auth'
import { getDeliveryNoteList, printDeliveryNote } from '@/api/podOrder'
import TableView from '@/components/TableView.vue'
import {
DeliveryNoteData,
PodDeliveryNoteSearchForm,
} from '@/types/api/podOrder'
import usePageList from '@/utils/hooks/usePageList'
import { computed, onMounted, ref } from 'vue'
import { val } from '@/utils'
import { CellCls } from 'element-plus'
import { useValue } from '@/utils/hooks/useValue'
import { filePath } from '@/api/axios'
const [searchForm, resetSearchForm] = useValue<PodDeliveryNoteSearchForm>({})
const dateRange = ref<string[]>([])
const {
currentPage,
pageSize,
total,
data: tableData,
refresh: search,
onCurrentPageChange: handleCurrentChange,
onPageSizeChange: handleSizeChange,
} = usePageList({
query: (page, pageSize) =>
getDeliveryNoteList(
{
...searchForm.value,
startTime: dateRange.value && dateRange.value[0],
endTime: dateRange.value && dateRange.value[1],
},
page,
pageSize,
).then((res) => res.data),
})
const nameSpaceList = ref<string[]>([])
onMounted(() => {
getNameSpaceList()
})
const getNameSpaceList = async () => {
try {
const res = await getUserMarkList()
nameSpaceList.value = res.data
} catch (e) {
// showError(e)
console.error(e)
}
}
const tableColumns = computed(() => {
return [
{
label: '序号',
type: 'index',
width: 60,
align: 'center',
},
{
label: '发货单号',
prop: 'shipmentNumber',
align: 'center',
width: 180,
},
{
label: '订单号',
prop: 'factoryOrderNumber',
align: 'center',
width: 180,
},
{
label: '发货单信息',
minWidth: 280,
prop: 'shipmentNumberInfo',
slot: 'deliveryNote',
},
{
label: '物流信息',
prop: 'logisticsInfo',
width: 280,
slot: 'logisticsInfo',
},
]
})
const productProps = [
[
{ label: '商品名', key: 'productName' },
{ label: 'Base SKU', key: 'baseSku', copyable: true },
{ label: '变体SKU', key: 'variantSku', copyable: true },
],
[{ label: '生产单号', key: 'subOrderNumber', copyable: true }],
[
// { label: '生产数', key: 'productionNum' },
{ label: '发货数', key: 'shipmentNum' },
],
]
const logisticsProps = [
{ label: '订单号', key: 'orderNumber', copyable: true },
{
label: '发货方式',
key: (d: DeliveryNoteData) =>
d.shippingWay && { 1: '送货上门', 2: '快递' }[d.shippingWay],
},
{
label: '物流跟踪号',
key: 'logisticsTracking',
copyable: true,
},
{ label: '创建时间', key: 'createTime' },
{ label: '收货人', key: 'lanshouName' },
{ label: '收货人电话', key: 'lanshouPhone' },
{ label: '邮编', key: 'lanshouPost' },
{
label: '收货地址',
key: (d: DeliveryNoteData) =>
`${d.lanshouRegion || ''}${d.lanshouAddress || ''}`,
},
]
const onCellClassName: CellCls<DeliveryNoteData> = ({ column }) => {
console.log('column', column.property)
if (
column.property === 'shipmentNumberInfo' ||
column.property === 'logisticsInfo' ||
column.property === 'operation'
) {
return 'vertical-align-top'
} else {
return ''
}
}
const copy = (text: string) => {
navigator.clipboard.writeText(text)
ElMessage.success('复制成功')
}
const onConfirmDateRange = async () => {
if (dateRange.value.length === 0) {
return ElMessage({
message: '请选择时间',
type: 'warning',
offset: window.innerHeight / 2,
})
}
const loading = ElLoading.service({
lock: true,
text: 'Loading',
background: 'rgba(0, 0, 0, 0.7)',
})
try {
const res = await printDeliveryNote(
dateRange.value,
searchForm.value?.userMark,
)
window.open(filePath + res.message)
dateRange.value = []
search()
} catch (e) {
// showError(e)
console.error(e)
} finally {
loading.close()
}
}
</script>
<style lang="scss" scoped>
.header-filter-form {
margin-bottom: 20px;
:deep(.el-form-item) {
margin-right: 14px;
margin-bottom: 10px;
}
}
$border: solid 1px #ddd;
.send-order-list {
display: grid;
grid-template-columns: 2fr 1fr;
border-left: $border;
border-top: $border;
}
.send-order-column {
padding: 10px 16px;
line-height: 1.5;
}
.send-order-header {
font-weight: bold;
text-align: center;
background-color: #f8f8f9;
}
.send-order-product-item {
display: flex;
justify-content: space-between;
gap: 20px;
&:not(:first-child) {
border-top: $border;
padding: 10px 0;
}
.send-order-prop-list {
flex: 1;
}
}
.send-order-product-image {
width: 100px;
}
.dialog-footer {
text-align: center;
}
.delivery-note-list {
:deep(.vertical-align-top) {
vertical-align: top;
}
}
</style>
<template>
<div class="card flex-column h-100 overflow-hidden">
<div class="header-filter-form">
<ElForm :model="searchForm" size="default" inline>
<ElFormItem>
<el-select
v-model="searchForm.timeType"
style="width: 100px; margin-right: 5px"
clearable
placeholder="时间类型"
>
<el-option :value="1" label="创建时间"></el-option>
<el-option :value="2" label="确认时间"></el-option>
<el-option :value="3" label="完成时间"></el-option>
<!-- <el-option :value="4" label="发货时间"></el-option> -->
</el-select>
<el-date-picker
v-model="timeRange"
:default-time="[
new Date(0, 0, 0, 0, 0, 0),
new Date(0, 0, 0, 23, 59, 59),
]"
placeholder="收货人"
value-format="YYYY-MM-DD HH:mm:ss"
type="datetimerange"
style="width: 280px"
:shortcuts="pickerOptions.shortcuts"
start-placeholder="开始时间"
end-placeholder="结束时间"
clearable
>
</el-date-picker>
</ElFormItem>
<ElFormItem label="SKU">
<ElInput
v-model.trim="searchForm.sku"
placeholder=" SKU"
clearable
style="width: 130px"
></ElInput>
</ElFormItem>
<ElFormItem label="发货单号">
<ElInput
v-model="searchForm.shipmentNumber"
placeholder="发货单号"
clearable
style="width: 130px"
/>
</ElFormItem>
<ElFormItem label="定制生产单号">
<ElInput
v-model="searchForm.factorySubOrderNumber"
placeholder="定制生产单号"
clearable
style="width: 130px"
/>
</ElFormItem>
<ElFormItem label="定制订单号">
<ElInput
v-model="searchForm.factoryOrderNumber"
placeholder="定制订单号"
clearable
style="width: 130px"
/>
</ElFormItem>
<ElFormItem label="店铺单号">
<ElInput
v-model="searchForm.shopNumber"
placeholder="店铺单号"
clearable
style="width: 130px"
/>
</ElFormItem>
<ElFormItem label="内部标签">
<ElInput
v-model.trim="searchForm.internalMemo"
placeholder="内部标签"
clearable
style="width: 130px"
></ElInput>
</ElFormItem>
<ElFormItem label="排序">
<el-select
v-model="searchForm.order"
clearable
style="width: 100px"
placeholder="排序类型"
>
<el-option value="asc" label="正序"></el-option>
<el-option value="desc" label="倒序"></el-option>
</el-select>
</ElFormItem>
<ElFormItem>
<ElButton type="primary" @click="search">查询</ElButton>
</ElFormItem>
<ElFormItem>
<ElButton @click="resetSearchForm">重置</ElButton>
</ElFormItem>
</ElForm>
</div>
<div class="header-filter-tab">
<div class="tabs">
<div
v-for="item in tabsNav"
:key="item.status"
class="tabs-node"
:class="item.status === status ? 'tabs-node_active' : ''"
@click="changeTab(item)"
>
<span class="tabs-node_label">{{ item.statusName }}</span>
<span class="tabs-node_count">{{ `(${item.quantity})` }}</span>
</div>
</div>
</div>
<div class="order-content flex-1 flex-column overflow-hidden mt-10">
<div class="order-operate-btn">
<span v-if="status === 'TO_BE_CONFIRMED'" class="item">
<ElButton type="success" @click="confirmProduce">确认生产</ElButton>
</span>
<span
v-if="
status === 'TO_BE_CONFIRMED' ||
status === 'IN_PRODUCTION' ||
status === 'PART_SHIPPING'
"
class="item"
>
<ElButton type="primary" dark @click="printManuscript"
>打印生产单</ElButton
>
</span>
<span class="item">
<ElButton type="warning" @click="addInternalTag"
>添加内部标签</ElButton
>
</span>
<span
v-if="
status === 'TO_BE_CONFIRMED' ||
status === 'IN_PRODUCTION' ||
status === 'PART_SHIPPING'
"
class="item"
>
<ElButton type="warning" is-dark @click="downloadMaterial">
下载素材</ElButton
>
</span>
<span v-if="status === 'IN_PRODUCTION' || status === 'PART_SHIPPING'" class="item">
<ElButton type="success" @click="confirmDelivery">发货</ElButton>
</span>
</div>
<div
ref="tableWrapperRef"
v-loading="loading"
element-loading-text="加载中..."
class="order-list flex-1 overflow-hidden"
>
<TableView
:paginated-data="tableData"
:columns="tableColumns"
default-expand-all
:span-method="arraySpanMethod"
@selection-change="handleSelectionChange"
>
<template #expand="{ row }">
<div v-if="row.productList" class="table-expand">
<div
class="order-list-expand"
:style="{ width: `${thOrderDetailWidth + 50}px` }"
>
<ProductInfo
:row="row"
:status="status"
:is-pod="true"
@update-remark="handleUpdateRemark"
/>
<template v-if="row.productList.length > 2">
<div class="order-list-expand_more">
<span @click="openAll(row)">
<template v-if="!row.moreable">
展开全部<strong style="color: red"
>({{ row.productList.length }})</strong
>条商品信息<el-icon style="vertical-align: middle"
><ArrowDown /></el-icon
></template>
<template v-else>
收起商品信息<el-icon style="vertical-align: middle"
><ArrowUp
/></el-icon>
</template>
</span>
</div>
</template>
</div>
<div class="order-actual-payment">
<div class="order-actual-payment_info">
<div class="order-list-expand_item_info_title">
<span class="order-list-expand_item_label">商品价:</span>
<span class="order-list-expand_item_value">{{
row.thirdProductAmount
}}</span>
</div>
<div class="order-list-expand_item_info_title">
<span class="order-list-expand_item_label">小计:</span>
<span class="order-list-expand_item_value">{{
row.totalAmount || '--'
}}</span>
</div>
<div
v-if="row.totolBuyNumber"
class="order-list-expand_item_info_title"
>
<span class="order-list-expand_item_value"
>{{ row.totolBuyNumber }}</span
>
</div>
</div>
</div>
<div class="order-memo">
<div class="order-memo-info">
<div
v-for="memo in row.internalMemoList || []"
:key="memo.id"
class="order-memo-item"
>
<div
v-if="memo.operatorEmployeeName"
class="order-memo-item__name"
>
<span>{{ memo.operatorEmployeeName || '' }}</span>
</div>
<div v-if="memo.operatorTime" class="order-memo-item__time">
<span>{{ memo.operatorTime || '' }}</span>
</div>
<div v-if="memo.content" class="order-memo-item__content">
<span>{{ memo.content || '' }}</span>
</div>
</div>
</div>
</div>
<div class="order-time">
<div class="order-time_info">
<div class="order-list-expand_item_info_title">
<span class="order-list-expand_item_label">创建时间:</span>
<span class="order-list-expand_item_value">{{
row.createTime || '--'
}}</span>
</div>
<div class="order-list-expand_item_info_title">
<span class="order-list-expand_item_label">确认时间:</span>
<span class="order-list-expand_item_value">{{
row.startStockingTime || '--'
}}</span>
</div>
<div class="order-list-expand_item_info_title">
<span class="order-list-expand_item_label">完成时间:</span>
<span class="order-list-expand_item_value">{{
row.finishTime || '--'
}}</span>
</div>
</div>
</div>
<div class="order-operate">
<div class="order-operate_info">
<div
v-if="
status === 'IN_PRODUCTION' || status === 'PART_SHIPPING'
"
class="order-list-expand_item_info_title"
>
<ElButton text type="danger" @click="cancelOrder(row.id)"
>取消
</ElButton>
</div>
<div class="order-list-expand_item_info_title">
<ElButton text type="primary" @click="openLog(row.id)"
>操作日志
</ElButton>
</div>
</div>
</div>
</div>
</template>
<template #orderDetail="scope">
<div class="order-detail">
<div
:title="scope.row.factoryOrderNumber"
class="order-detail_item"
>
<span class="label">订单号:</span>
<span class="value">{{ scope.row.factoryOrderNumber }}</span>
</div>
<div
:title="scope.row.thirdOrderNumber"
class="order-detail_item"
>
<span class="label">第三方订单号:</span>
<span class="value" :title="scope.row.thirdOrderNumber">{{
scope.row.thirdOrderNumber || '--'
}}</span>
</div>
<!-- <div
:title="scope.row.manuscriptStatusStr"
class="order-detail_item"
>
<span class="label">稿件状态:</span>
<span class="value" :title="scope.row.manuscriptStatusStr">{{
scope.row.manuscriptStatusStr || '--'
}}</span>
</div> -->
<div :title="scope.row.lanshouName" class="order-detail_item">
<span class="label">收货人:</span>
<span class="value">{{ scope.row.lanshouName }}</span>
</div>
<div :title="scope.row.lanshouPhone" class="order-detail_item">
<span class="label">电话:</span>
<span class="value">{{ scope.row.lanshouPhone }}</span>
</div>
<div :title="scope.row.lanshouAddress" class="order-detail_item">
<span class="label">收货地址:</span>
<span class="value" :title="scope.row.lanshouAddress">{{
scope.row.lanshouRegion + scope.row.lanshouAddress
}}</span>
</div>
<div
v-if="scope.row.shipmentUid"
:title="scope.row.shipmentUid"
class="order-detail_item"
>
<span class="label">提货码:</span>
<span class="value" :title="scope.row.shipmentUid">{{
scope.row.shipmentUid
}}</span>
</div>
<div :title="scope.row.lanshouPost" class="order-detail_item">
<span class="label">邮编:</span>
<span class="value">{{ scope.row.lanshouPost }}</span>
</div>
</div>
</template>
</TableView>
</div>
<el-pagination
v-model:current-page="currentPage"
v-model:page-size="pageSize"
:page-sizes="[100, 200, 300, 400]"
background
layout="total, sizes, prev, pager, next, jumper"
:total="total"
style="margin: 10px auto 10px; text-align: right"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
/>
</div>
</div>
<el-dialog
v-model="shipmentVisible"
title="发货"
:close-on-click-modal="false"
width="1600px"
top="6vh"
@opened="onShipmentDialogOpened"
>
<div class="header-search">
<el-input
ref="inputRef"
v-model="productionOrderNumber"
clearable
size="large"
placeholder="请输入生产单号"
@keyup.enter="searchShipmentByOrderNumber"
/>
<el-button
type="primary"
size="large"
@click="searchShipmentByOrderNumber"
>查询
</el-button>
</div>
<Shipment
ref="shipmentOrderRef"
v-loading="shipmentLoading"
:order-list="orderList"
:current-row="currentRow"
:is-pod="true"
@change-current-row="onChangeCurrentRow"
/>
<div class="shipment-logistics-info margin-top-20">
<ElForm ref="shipmentFormRef" :model="shipmentForm" :rules="rules">
<ElRow :gutter="20">
<ElCol :span="6">
<ElFormItem label="发货方式" prop="shippingWay">
<ElSelect
v-model="shipmentForm.shippingWay"
clearable
placeholder="请选择"
style="width: 100%"
>
<ElOption label="送货上门" value="1"></ElOption>
<ElOption label="快递" value="2"></ElOption>
</ElSelect>
</ElFormItem>
</ElCol>
<ElCol v-if="shipmentForm.shippingWay === '2'" :span="6">
<ElFormItem label="物流名称" prop="carriageName">
<ElSelect
v-model="shipmentForm.carriageName"
filterable
placeholder="请输入关键字"
clearable
style="width: 100%"
>
<ElOption
v-for="item in logisticsCompanyList"
:key="item.id"
:value="item.name"
></ElOption>
</ElSelect>
</ElFormItem>
</ElCol>
<ElCol v-if="shipmentForm.shippingWay === '2'" :span="6">
<ElFormItem label="物流跟踪号" prop="logisticsTracking">
<ElInput
v-model="shipmentForm.logisticsTracking"
placeholder="请输入物流跟踪号"
clearable
style="width: 100%"
/>
</ElFormItem>
</ElCol>
<ElCol v-if="shipmentForm.shippingWay === '2'" :span="6">
<ElFormItem label="物流费用" prop="carriageAmount">
<ElInput
v-model="shipmentForm.carriageAmount"
clearable
placeholder="请输入物流费用"
style="width: 100%"
/>
</ElFormItem>
</ElCol>
</ElRow>
</ElForm>
</div>
<template #footer>
<div class="dialog-footer">
<el-button size="large" @click="shipmentVisible = false"
>取消
</el-button>
<el-button size="large" type="primary" @click="saveShipment"
>发货
</el-button>
</div>
</template>
</el-dialog>
<el-dialog
v-model="logVisible"
title="操作日志"
width="1000px"
:close-on-click-modal="false"
>
<LogList :log-list="logList" />
</el-dialog>
</template>
<script setup lang="tsx">
import {
getOrderTabData,
getOrderList,
confirmProductionOrder,
cancelOrderApi,
downloadMaterialApi,
addInternalTagApi,
printOrder,
getLogList,
updateRemarkApi,
} from '@/api/podOrder'
import TableView from '@/components/TableView.vue'
import {
OrderData,
ProductList,
LogisticsData,
ShipmentForm,
} from '@/types/api/order'
import { CustomColumn } from '@/types/table'
import usePageList from '@/utils/hooks/usePageList'
import { useValue } from '@/utils/hooks/useValue'
import { computed, onMounted, reactive, ref } from 'vue'
import dayjs from 'dayjs'
import useElTableColumnWidth from '@/utils/hooks/useElTableColumnWidth'
import ProductInfo from '../ProductInfo.vue'
import { SpanMethodProps } from '@/types/api/order'
import useShipment from '../hook/podUseShipMent'
import Shipment from '../Shipment.vue'
import { SearchForm, Tab, LogListData } from '@/types/api/podOrder'
import { getLogisticsCompanyList } from '@/api/common'
import { ElButton, type FormRules } from 'element-plus'
import { showConfirm } from '@/utils/ui'
import { filePath } from '@/api/axios'
import LogList from '@/components/LogList.vue'
// 日期工具函数
const getDateRange = (days = 0, type: 'past' | 'future' = 'past') => {
const end = dayjs()
const start =
type === 'past' ? end.subtract(days, 'day') : end.add(days, 'day')
return [start.startOf('day').toDate(), end.endOf('day').toDate()]
}
const getMonthRange = (months = 0, type: 'past' | 'future' = 'past') => {
const now = dayjs()
const start =
type === 'past' ? now.subtract(months, 'month') : now.add(months, 'month')
return [start.startOf('month').toDate(), start.endOf('month').toDate()]
}
const getWeekRange = (weeks = 0, type: 'past' | 'future' = 'past') => {
const now = dayjs()
const start =
type === 'past' ? now.subtract(weeks, 'week') : now.add(weeks, 'week')
return [start.startOf('week').toDate(), start.endOf('week').toDate()]
}
const pickerOptions = {
shortcuts: [
{
text: '今日',
value: () => getDateRange(0),
},
{
text: '昨天',
value: () => getDateRange(1),
},
{
text: '最近7天',
value: () => getDateRange(6),
},
{
text: '最近14天',
value: () => getDateRange(13),
},
{
text: '最近30天',
value: () => getDateRange(29),
},
{
text: '本周',
value: () => getWeekRange(0),
},
{
text: '上周',
value: () => getWeekRange(1),
},
{
text: '本月',
value: () => getMonthRange(0),
},
{
text: '上月',
value: () => getMonthRange(1),
},
{
text: '历史',
value: () => {
const end = dayjs().endOf('day').toDate()
const start = dayjs('2000-01-01').startOf('day').toDate()
return [start, end]
},
},
],
}
const timeRange = ref<string[]>([])
const tabsNav = ref<Tab[]>()
const status = ref('TO_BE_CONFIRMED')
const [tableWrapperRef, thOrderDetailWidth] = useElTableColumnWidth(
'table th.th-order-detail',
)
const loadTabData = async () => {
try {
const res = await getOrderTabData()
tabsNav.value = res.data
} catch (error) {
// showError(error)
}
}
const changeTab = (item: Tab) => {
status.value = item.status ?? ''
searchForm.value.timeType = null
search()
}
const [searchForm, resetSearchForm] = useValue<SearchForm>({
timeType: null,
shopNumber: '',
order: 'desc',
})
const tableColumns = computed<CustomColumn<OrderData[]>>(() => {
return [
{
type: 'selection',
width: 50,
align: 'center',
},
{
type: 'expand',
align: 'center',
slot: 'expand',
width: 1,
},
{
label: '订单详情',
key: 'id',
minWidth: 800,
slot: 'orderDetail',
className: 'th-order-detail',
showOverflowTooltip: true,
},
{
label: '实付款',
key: '',
width: 280,
},
{
label: '内部便签',
key: 'customerName',
width: 280,
showOverflowTooltip: true,
},
{
label: '时间',
key: 'status',
width: 280,
align: 'center',
},
{
label: '操作',
key: 'status',
width: 100,
align: 'center',
},
]
})
const {
loading,
currentPage,
pageSize,
total,
data: tableData,
refresh: search,
onCurrentPageChange: handleCurrentChange,
onPageSizeChange: handleSizeChange,
} = usePageList({
query: (page, pageSize) =>
getOrderList(
{
...searchForm.value,
status: status.value,
timeType: searchForm.value.timeType,
startTime:
timeRange.value && timeRange.value.length > 0
? timeRange.value[0]
: null,
endTime:
timeRange.value && timeRange.value.length > 0
? timeRange.value[1]
: null,
},
page,
pageSize,
).then((res) => res.data),
})
// 发货
const {
shipmentFormRef,
productionOrderNumber,
shipmentVisible,
inputRef,
shipmentForm,
currentRow,
shipmentLoading,
orderList,
shipmentOrderRef,
searchShipmentByOrderNumber,
saveShipment,
confirmDelivery,
onShipmentDialogOpened,
} = useShipment(() => {
loadTabData()
search()
})
const rules = reactive<FormRules<ShipmentForm>>({
shippingWay: [
{
required: true,
message: '请选择物流方式',
},
],
carriageName: [
{
required: true,
message: '请选择物流公司',
},
],
carriageAmount: [
{
required: true,
message: '请输入物流费用',
},
],
logisticsTracking: [
{
required: true,
message: '请输入物流跟踪号',
},
],
})
const logisticsCompanyList = ref<LogisticsData[]>([])
const getLogisticsList = async () => {
try {
const res = await getLogisticsCompanyList()
logisticsCompanyList.value = res.data
} catch (error) {
//showError(error)
}
}
const openAll = (row: OrderData) => {
row.moreable = !row.moreable
}
const selection = ref<OrderData[]>([])
const handleSelectionChange = (s: OrderData[]) => {
selection.value = s
}
// 确认生产
const confirmProduce = async () => {
if (selection.value.length === 0) {
return ElMessage({
message: '请选择订单',
type: 'warning',
offset: window.innerHeight / 2,
})
}
try {
await showConfirm('是否确认生产', {
confirmButtonText: '确认',
cancelButtonText: '取消',
type: 'warning',
})
} catch {
return
}
const ids = selection.value.map((item) => item.id)
try {
const res = await confirmProductionOrder(ids)
search()
await loadTabData()
ElMessage.success(res.message)
} catch (e) {
// showError(e)
}
}
// 下载稿件
const downloadMaterial = async () => {
if (selection.value.length === 0) {
return ElMessage({
message: '请选择订单',
type: 'warning',
offset: window.innerHeight / 2,
})
}
try {
const res = await downloadMaterialApi(
selection.value.map((item) => item.id),
)
const { data } = res
data.forEach((item: string) => {
window.open(filePath + item)
})
} catch (e) {
// showError(e)
console.error(e)
}
}
const arraySpanMethod = ({ columnIndex }: SpanMethodProps) => {
if (columnIndex === 0 || columnIndex === 1) {
return [1, 1]
} else {
return [1, 6]
}
}
const onChangeCurrentRow = (item: ProductList) => {
currentRow.value = item
}
const cancelOrder = async (id: number) => {
try {
await showConfirm('是否确认取消', {
confirmButtonText: '确认',
cancelButtonText: '取消',
type: 'warning',
})
} catch {
return
}
try {
const res = await cancelOrderApi(id)
ElMessage({
message: res.message,
type: 'success',
offset: window.innerHeight / 2,
})
loadTabData()
search()
} catch (e) {
// showError(e)
}
}
// 添加内部便签
const addInternalTag = async () => {
if (selection.value.length === 0) {
return ElMessage({
message: '请选择订单',
type: 'warning',
offset: window.innerHeight / 2,
})
}
ElMessageBox.prompt('', '添加内部便签', {
confirmButtonText: '确认',
cancelButtonText: '取消',
inputType: 'textarea',
inputPlaceholder: '请输入内部便签',
inputPattern: /.+/,
inputErrorMessage: '内部便签不能为空',
}).then(async ({ value }) => {
try {
const res = await addInternalTagApi(
selection.value.map((item) => item.id),
value,
)
ElMessage.success(res.message)
loadTabData()
search()
} catch (e) {
// showError(e)
}
})
}
const printManuscript = async () => {
if (selection.value.length === 0) {
return ElMessage({
message: '请选择订单',
type: 'warning',
offset: window.innerHeight / 2,
})
}
const loading = ElLoading.service({
lock: true,
text: '加载中...',
background: 'rgba(0, 0, 0, 0.7)',
})
const ids = selection.value.map((item) => item.id)
try {
await showConfirm('是否打印生产单', {
confirmButtonText: '确认',
cancelButtonText: '取消',
type: 'warning',
})
} catch {
return
}
try {
const res = await printOrder(ids)
window.open(filePath + res.message)
} catch (e) {
// showError(e)
console.error(e)
} finally {
loading.close()
}
}
const logVisible = ref(false)
const logList = ref<LogListData[]>([])
// 操作日志
const openLog = async (id: number) => {
try {
const res = await getLogList(id)
logList.value = res.data
logVisible.value = true
} catch (e) {
// showError(e)
console.error(e)
}
}
const handleUpdateRemark = (payload: { id: number; remark: string }) => {
const { id, remark } = payload
ElMessageBox.prompt('备注', '备注', {
confirmButtonText: '确定',
cancelButtonText: '取消',
inputPattern: /.+/,
inputValue: remark,
inputErrorMessage: '请输入备注',
inputPlaceholder: '备注',
}).then(async ({ value = remark }) => {
try {
await updateRemarkApi(id, value)
ElMessage.success('操作成功')
search()
} catch (e) {
console.error(e)
}
})
}
onMounted(() => {
getLogisticsList()
loadTabData()
})
</script>
<style lang="scss" scoped>
.tabs {
display: flex;
align-items: center;
gap: 10px;
border-bottom: 1px solid #f0f0f0;
height: 42px;
.tabs-node {
padding: 10px;
font-size: 14px;
color: #000;
cursor: pointer;
height: 40px;
}
.tabs-node:hover {
color: #409eff;
}
.tabs-node.tabs-node_active {
color: #409eff;
border-bottom: 2px solid #409eff;
font-weight: 600;
}
}
.expand-detail {
padding: 10px;
font-size: 14px;
color: #000;
.detail-item {
margin-bottom: 10px;
.label {
font-weight: 600;
}
.value {
color: #666;
}
}
}
.order-list-expand {
border-right: 1px solid #eee;
font-size: 14px;
color: #606266;
overflow: auto;
max-height: 600px;
}
.table-expand {
display: flex;
}
.order-list-expand_more {
text-align: center;
padding: 10px 0;
span {
cursor: pointer;
}
}
.order-list {
margin-top: 10px;
flex: 1;
:deep(.el-table__expand-icon > .el-icon) {
display: none;
}
:deep(.el-table__row) {
background-color: #f5f5f5;
}
:deep(.el-table__expanded-cell) {
padding: 0;
}
}
.order-detail {
display: flex;
font-size: 14px;
}
.order-detail_item {
flex: 1;
display: flex;
align-items: center;
.label {
color: #aaa;
margin-right: 6px;
}
.value {
color: #000;
flex: 1;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
}
.order-actual-payment {
width: 280px;
border-right: 1px solid #eee;
}
.order-memo {
width: 280px;
border-right: 1px solid #eee;
}
.order-time_info {
padding: 20px;
font-size: 14px;
}
.order-memo-info {
padding: 20px;
font-size: 14px;
}
.order-memo-item {
display: flex;
align-items: center;
line-height: 26px;
}
.order-memo-item div:not(:last-child) {
margin-right: 6px;
}
.order-time {
width: 280px;
border-right: 1px solid #eee;
}
.order-operate {
width: 100px;
}
.order-actual-payment_info {
font-size: 14px;
padding: 20px;
}
.order-operate_info {
padding: 20px;
:deep(.el-button) {
padding: 0;
font-size: 14px;
}
}
.header-search {
display: flex;
gap: 10px;
}
.dialog-footer {
text-align: center;
}
.order-list-expand_item_info_title {
line-height: 26px;
display: flex;
align-items: center;
}
.order-list-expand_item_label {
margin-right: 6px;
}
.order-operate_info {
padding: 20px;
:deep(.el-button) {
padding: 0;
font-size: 14px;
}
}
</style>
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