Commit 95aa07a3 by qinjianhui

Merge branch 'dev' into 'master'

Dev

See merge request !96
parents 283e4e3c e8b276ce
...@@ -54,6 +54,7 @@ declare module 'vue' { ...@@ -54,6 +54,7 @@ declare module 'vue' {
ElTag: typeof import('element-plus/es')['ElTag'] ElTag: typeof import('element-plus/es')['ElTag']
ElTimeline: typeof import('element-plus/es')['ElTimeline'] ElTimeline: typeof import('element-plus/es')['ElTimeline']
ElTimelineItem: typeof import('element-plus/es')['ElTimelineItem'] ElTimelineItem: typeof import('element-plus/es')['ElTimelineItem']
ElTimePicker: typeof import('element-plus/es')['ElTimePicker']
ElTooltip: typeof import('element-plus/es')['ElTooltip'] ElTooltip: typeof import('element-plus/es')['ElTooltip']
ElTree: typeof import('element-plus/es')['ElTree'] ElTree: typeof import('element-plus/es')['ElTree']
ElUpload: typeof import('element-plus/es')['ElUpload'] ElUpload: typeof import('element-plus/es')['ElUpload']
......
...@@ -626,3 +626,11 @@ export function getCustomTagListApi() { ...@@ -626,3 +626,11 @@ export function getCustomTagListApi() {
`factory/podJomallOrderUs/getCustomTagList`, `factory/podJomallOrderUs/getCustomTagList`,
) )
} }
// 普货拣货
export function printNormalPickPdfApi(ids: string) {
return axios.get<never, BaseRespData<string>>(
'factory/podJomallOrderUs/printNormalPickPdf',
{ params: { ids } },
)
}
import axios from '@/api/axios.ts'
import { BasePaginationData, BaseRespData } from '@/types/api'
// 新增规则
interface BaseForm {
id?: number
}
type ParamKey = `param${number}`
type ParamValue = string | number | boolean | null | undefined
type PostData = BaseForm & Record<ParamKey, ParamValue>
export function usArrangeRuleApi(data: PostData) {
return axios.post<never, BasePaginationData<never>>(
'factory/podJomallOrderUs/usArrangeRule',
data,
)
}
// 编辑规则
export function updateRuleByIdApi(data: PostData) {
return axios.post<never, BasePaginationData<never>>(
'factory/podJomallOrderUsArrangeRule/updateRuleById',
data,
)
}
// us规则列表
export function getByFactoryNoApi(data: { factoryNo: number }) {
return axios.get<never, BaseRespData<never>>(
'/factory/podJomallOrderUsArrangeRule/getByFactoryNo',
{ params: data },
)
}
...@@ -88,7 +88,15 @@ const router = createRouter({ ...@@ -88,7 +88,15 @@ const router = createRouter({
}, },
component: () => import('@/views/order/orderTracking/index.vue'), component: () => import('@/views/order/orderTracking/index.vue'),
}, },
{ {
path: '/pod-us-order/podUsSchedulingRules',
meta: {
title: 'POD(US)排单规则',
},
component: () =>
import('@/views/order/podUsSchedulingRules/index.vue'),
},
{
path: '/pod-cn-order/orderTracking', path: '/pod-cn-order/orderTracking',
meta: { meta: {
title: 'POD(CN)订单跟踪', title: 'POD(CN)订单跟踪',
......
...@@ -137,6 +137,11 @@ const menu: MenuItem[] = [ ...@@ -137,6 +137,11 @@ const menu: MenuItem[] = [
id: 10, id: 10,
label: 'POD(US)订单跟踪', label: 'POD(US)订单跟踪',
}, },
{
index: '/pod-us-order/podUsSchedulingRules',
id: 11,
label: 'POD(US)排单规则',
},
], ],
}, },
......
...@@ -38,7 +38,7 @@ export interface SearchForm { ...@@ -38,7 +38,7 @@ export interface SearchForm {
thirdSkuCode?: string thirdSkuCode?: string
supplierProductNo?: string supplierProductNo?: string
batchArrangeNumber?: string batchArrangeNumber?: string
craftCode?: string craftCode?: string[] | string
thirdStockSku?: string thirdStockSku?: string
exceptionHandling?: number | undefined exceptionHandling?: number | undefined
interceptStatus?: number | string interceptStatus?: number | string
...@@ -221,6 +221,7 @@ export interface LogisticsFormData { ...@@ -221,6 +221,7 @@ export interface LogisticsFormData {
export interface CraftListData { export interface CraftListData {
craftName: string craftName: string
craftCode: string craftCode: string
craftType: string
} }
export interface PackingData { export interface PackingData {
podProductionNo?: string // 生产单号(PSCD 开头) podProductionNo?: string // 生产单号(PSCD 开头)
......
...@@ -34,7 +34,7 @@ export interface SearchForm { ...@@ -34,7 +34,7 @@ export interface SearchForm {
thirdSkuCode?: string thirdSkuCode?: string
supplierProductNo?: string supplierProductNo?: string
batchArrangeNumber?: string batchArrangeNumber?: string
craftCode?: string craftCode?: string | string[]
craftCodeArr?: string[] craftCodeArr?: string[]
thirdStockSku?: string thirdStockSku?: string
interceptStatus?: number | string interceptStatus?: number | string
...@@ -141,6 +141,7 @@ export interface ProductList { ...@@ -141,6 +141,7 @@ export interface ProductList {
batchArrangeNumber?: string | null batchArrangeNumber?: string | null
interceptStatus?: number | null interceptStatus?: number | null
sizeType?: number | null sizeType?: number | null
productMark?: string | null
customTagList?: { name: string }[] customTagList?: { name: string }[]
} }
export interface cardImages { export interface cardImages {
...@@ -216,6 +217,7 @@ export interface LogisticsFormData { ...@@ -216,6 +217,7 @@ export interface LogisticsFormData {
export interface CraftListData { export interface CraftListData {
craftName: string craftName: string
craftCode: string craftCode: string
craftType: string
} }
export interface InterceptStateGroupData { export interface InterceptStateGroupData {
...@@ -226,3 +228,8 @@ export interface InterceptStateGroupData { ...@@ -226,3 +228,8 @@ export interface InterceptStateGroupData {
[key: string]: number [key: string]: number
} }
} }
export interface IAllList {
id: string
name: string
warehouseName?: string
}
...@@ -43,7 +43,7 @@ interface IwayList { ...@@ -43,7 +43,7 @@ interface IwayList {
} }
interface IAllList { interface IAllList {
factoryId?: number factoryId?: number
id: number id: number | string
name: string name: string
serviceCode?: string serviceCode?: string
siteUrl?: string siteUrl?: string
...@@ -83,6 +83,18 @@ export default defineComponent({ ...@@ -83,6 +83,18 @@ export default defineComponent({
type: String, type: String,
default: 'id', default: 'id',
}, },
searchPlaceholder: {
type: String,
default: '请搜索承运商',
},
startPlaceholder: {
type: String,
default: '请选择物流方式',
},
startWidth: {
type: String,
default: '100%',
},
}, },
emits: ['update:modelValue'], emits: ['update:modelValue'],
setup(props, { emit }) { setup(props, { emit }) {
...@@ -161,7 +173,20 @@ export default defineComponent({ ...@@ -161,7 +173,20 @@ export default defineComponent({
}, },
{ deep: true, immediate: true }, { deep: true, immediate: true },
) )
const handleClearSelection = () => {
/* 1. 清空显示文本 */
waysName.value = ''
/* 2. 清空双向绑定结果 */
if (props.isRadio) {
selectedRadioList.value = '' // 单选
emit('update:modelValue', '')
} else {
selectedList.value = [] // 多选
emit('update:modelValue', [])
}
/* 3. 可选:把搜索框也重置 */
searchName.value = ''
}
function setCheckAll(company: ICompanyList, event: boolean) { function setCheckAll(company: ICompanyList, event: boolean) {
if (event) { if (event) {
selectedList.value = [ selectedList.value = [
...@@ -252,8 +277,10 @@ export default defineComponent({ ...@@ -252,8 +277,10 @@ export default defineComponent({
reference: () => ( reference: () => (
<ElInput <ElInput
modelValue={waysName.value} modelValue={waysName.value}
style={{ width: '100%' }} style={{ width: props.startWidth }}
placeholder="请选择物流方式" placeholder={props.startPlaceholder}
clearable
onClear={handleClearSelection}
/> />
), ),
}} }}
...@@ -271,7 +298,7 @@ export default defineComponent({ ...@@ -271,7 +298,7 @@ export default defineComponent({
}} }}
clearable clearable
style={{ width: '200px', padding: '10px' }} style={{ width: '200px', padding: '10px' }}
placeholder="请搜索承运商" placeholder={props.searchPlaceholder}
/> />
<ElScrollbar <ElScrollbar
class="scroll-container" class="scroll-container"
......
import { defineComponent, ref } from 'vue'
import { ElDialog } from 'element-plus'
export default defineComponent({
name: 'CustomizeForm',
props: {
modelValue: {
type: Boolean,
default: false,
},
title: {
type: String,
default: '',
},
dialogWidth: {
type: String,
default: '600px',
},
},
emits: ['update:modelValue', 'close'],
setup(props, { emit, attrs, slots }) {
const formRef = ref<InstanceType<typeof ElDialog> | null>(null)
const isShow = ref(false)
watch(
() => props.modelValue,
(val) => {
isShow.value = val
},
{ immediate: true },
)
return () => {
return (
<ElDialog
ref={formRef}
v-model={isShow.value}
title={props.title}
width={props.dialogWidth}
onClose={() => {
emit('close')
}}
destroy-on-close={true}
close-on-click-modal={false}
{...attrs}
>
<div class="dialog-form">
{slots.default?.()}
{slots.footer?.()}
</div>
</ElDialog>
)
}
},
})
...@@ -52,20 +52,13 @@ ...@@ -52,20 +52,13 @@
</ElFormItem> </ElFormItem>
<ElFormItem label="工艺"> <ElFormItem label="工艺">
<ElSelect <LogisticsWaySelect
v-model="searchForm.craftCode" v-model="searchForm.craftCode"
clearable :company-list="craftList"
filterable :start-width="'150px'"
placeholder="请选择工艺" search-placeholder="搜索工艺名称"
style="width: 150px" start-placeholder="请选择工艺名称"
> ></LogisticsWaySelect>
<el-option
v-for="item in craftList"
:key="item.craftCode"
:label="item.craftName"
:value="item.craftCode"
></el-option>
</ElSelect>
</ElFormItem> </ElFormItem>
<ElFormItem label="库存SKU"> <ElFormItem label="库存SKU">
<ElInput <ElInput
...@@ -2476,6 +2469,7 @@ ...@@ -2476,6 +2469,7 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { getUserMarkList } from '@/api/common' import { getUserMarkList } from '@/api/common'
import LogisticsWaySelect from '../../logistics/components/LogisticsWaySelect.tsx'
// import { AnyObject } from '@/types/api/warehouse' // import { AnyObject } from '@/types/api/warehouse'
import { import {
InfoFilled, InfoFilled,
...@@ -2544,7 +2538,7 @@ import { BaseRespData } from '@/types/api' ...@@ -2544,7 +2538,7 @@ import { BaseRespData } from '@/types/api'
import UpdateAddress from './components/updateAddress.vue' import UpdateAddress from './components/updateAddress.vue'
import { getAllCountryApi } from '@/api/logistics.ts' import { getAllCountryApi } from '@/api/logistics.ts'
import { InterceptStateGroupData } from '@/types/api/podUsOrder' import { InterceptStateGroupData, IAllList } from '@/types/api/podUsOrder'
import TableView from '@/components/TableView.vue' import TableView from '@/components/TableView.vue'
import { import {
LogListData, LogListData,
...@@ -2776,7 +2770,7 @@ const [searchForm, resetSearchForm] = useValue<SearchForm>({ ...@@ -2776,7 +2770,7 @@ const [searchForm, resetSearchForm] = useValue<SearchForm>({
thirdSkuCode: '', thirdSkuCode: '',
supplierProductNo: '', supplierProductNo: '',
batchArrangeNumber: '', batchArrangeNumber: '',
craftCode: '', craftCode: [],
thirdStockSku: '', thirdStockSku: '',
}) })
const shipmentArea = ref(0) const shipmentArea = ref(0)
...@@ -4982,14 +4976,48 @@ const loadWarehouseList = async () => { ...@@ -4982,14 +4976,48 @@ const loadWarehouseList = async () => {
console.error(e) console.error(e)
} }
} }
const processType = ref([
{
label: '烫画',
value: 'TH',
},
{
label: '直喷',
value: 'ZP',
},
{
label: '刺绣',
value: 'CX',
},
{
label: '雕刻',
value: 'DK',
},
{
label: '白胚',
value: 'BP',
},
{
label: '其他',
value: 'QT',
},
])
// 获取工艺列表 // 获取工艺列表
const craftList = ref<CraftListData[]>([]) const craftList = ref<IAllList[]>([])
const processTypeMap = processType.value.reduce((acc, cur) => {
acc[cur.value] = cur.label
return acc
}, {} as Record<string, string>)
const loadCraftList = async () => { const loadCraftList = async () => {
try { try {
const res = await getListCraftApi() const res = await getListCraftApi()
if (res.code !== 200) return if (res.code !== 200) return
craftList.value = res.data const data: CraftListData[] = res.data
craftList.value = data.map((item) => ({
id: item.craftCode,
name: item.craftName,
warehouseName: processTypeMap[item.craftType] ?? '其他', // craftType 对应中文
})) as IAllList[]
} catch (e) { } catch (e) {
console.error(e) console.error(e)
} }
......
...@@ -172,6 +172,7 @@ ...@@ -172,6 +172,7 @@
@click="podOrderDetailsData && print(podOrderDetailsData, true)" @click="podOrderDetailsData && print(podOrderDetailsData, true)"
>手动打印</ElButton >手动打印</ElButton
> >
<ElButton type="primary" @click="printNormal">普货拣货 </ElButton>
<ElButton type="success" @click="handlePrintFinish" <ElButton type="success" @click="handlePrintFinish"
>打单完成</ElButton >打单完成</ElButton
> >
...@@ -210,6 +211,17 @@ ...@@ -210,6 +211,17 @@
<span v-if="item.data" class="number"> <span v-if="item.data" class="number">
{{ item.data.pickingNumber }}/{{ item.data.purchaseNumber }} {{ item.data.pickingNumber }}/{{ item.data.purchaseNumber }}
</span> </span>
<div
v-if="
item.data &&
item.data.productList?.some(
(e) => e.productMark === 'custom_normal',
)
"
class="cb-product-mark"
>
<div class="red-circle"></div>
</div>
</div> </div>
</div> </div>
</div> </div>
...@@ -234,11 +246,13 @@ import { ...@@ -234,11 +246,13 @@ import {
getPackingDataApi, getPackingDataApi,
getPodBoxListApi, getPodBoxListApi,
submitInspectionApi, submitInspectionApi,
printNormalPickPdfApi,
} from '@/api/podUsOrder' } from '@/api/podUsOrder'
import useUserStore from '@/store/user' import useUserStore from '@/store/user'
import { Check } from '@element-plus/icons-vue' import { Check } from '@element-plus/icons-vue'
import socket from '@/utils/websocket' import socket from '@/utils/websocket'
import { WarehouseListData } from '@/types/api/podUsOrder' import { WarehouseListData } from '@/types/api/podUsOrder'
import { filePath } from '@/api/axios.ts'
const { getCLodop } = useLodop() const { getCLodop } = useLodop()
...@@ -274,7 +288,6 @@ const podOrderDetailsColumns = computed(() => [ ...@@ -274,7 +288,6 @@ const podOrderDetailsColumns = computed(() => [
width: 250, width: 250,
slot: 'image', slot: 'image',
align: 'center', align: 'center',
fixed: 'left',
}, },
{ {
label: '生产单号', label: '生产单号',
...@@ -282,21 +295,23 @@ const podOrderDetailsColumns = computed(() => [ ...@@ -282,21 +295,23 @@ const podOrderDetailsColumns = computed(() => [
width: 150, width: 150,
align: 'center', align: 'center',
}, },
// { {
// label: 'base SKU', label: '库存 SKU',
// prop: 'baseSku', prop: 'thirdSkuCode',
// width: 140, width: 180,
// align: 'center', align: 'center',
// }, },
{ {
label: 'variant SKU', label: 'variant SKU',
prop: 'variantSku', prop: 'variantSku',
width: 140, width: 160,
align: 'center', align: 'center',
}, },
{ {
label: '商品名称', label: '商品名称',
prop: 'productName', prop: 'productName',
minWidth: 100,
}, },
{ {
...@@ -304,18 +319,21 @@ const podOrderDetailsColumns = computed(() => [ ...@@ -304,18 +319,21 @@ const podOrderDetailsColumns = computed(() => [
prop: 'purchaseNumber', prop: 'purchaseNumber',
width: 90, width: 90,
align: 'center', align: 'center',
fixed: 'right',
}, },
{ {
label: '拣货数量', label: '拣货数量',
prop: 'count', prop: 'count',
width: 90, width: 90,
align: 'center', align: 'center',
fixed: 'right',
}, },
{ {
label: '验证结果', label: '验证结果',
slot: 'verifyResult', slot: 'verifyResult',
width: 90, width: 90,
align: 'center', align: 'center',
fixed: 'right',
}, },
]) ])
const boxChange = ref<boolean>(false) const boxChange = ref<boolean>(false)
...@@ -401,7 +419,15 @@ watch( ...@@ -401,7 +419,15 @@ watch(
(val) => { (val) => {
if (val && val.productList?.length) if (val && val.productList?.length)
val.productList.forEach((el) => { val.productList.forEach((el) => {
if (!el.previewImgs) el.previewImgs = JSON.parse(el.imageAry) if (!el.previewImgs) {
if (el.productMark === 'custom_normal') {
el.previewImgs = [{ url: el.variantImage || '' }]
} else {
el.previewImgs = el.imageAry
? JSON.parse(el.imageAry)
: [{ url: el.variantImage }]
}
}
}) })
}, },
{ deep: true }, { deep: true },
...@@ -426,7 +452,13 @@ const renderItemBox = (bool: boolean) => { ...@@ -426,7 +452,13 @@ const renderItemBox = (bool: boolean) => {
if (!boxItem) boxItem = { data: { productList: [] } } if (!boxItem) boxItem = { data: { productList: [] } }
const { data } = boxItem const { data } = boxItem
data?.productList?.forEach((el) => { data?.productList?.forEach((el) => {
if (!el.previewImgs) el.previewImgs = JSON.parse(el.imageAry) if (!el.previewImgs) {
if (el.productMark === 'custom_normal') {
el.previewImgs = [{ url: el.variantImage || '' }]
} else {
el.previewImgs = JSON.parse(el.imageAry)
}
}
}) })
if (!data) { if (!data) {
renderLock = false renderLock = false
...@@ -690,7 +722,15 @@ const initOrderDetailBox = async () => { ...@@ -690,7 +722,15 @@ const initOrderDetailBox = async () => {
boxIndex.value = boxList.find((item) => item.data)?.box || null boxIndex.value = boxList.find((item) => item.data)?.box || null
podOrderDetailsData.value?.productList?.forEach((el) => { podOrderDetailsData.value?.productList?.forEach((el) => {
if (!el.previewImgs) el.previewImgs = JSON.parse(el.imageAry) if (!el.previewImgs) {
if (el.productMark === 'custom_normal') {
el.previewImgs = [{ url: el.variantImage || '' }]
} else {
el.previewImgs = el.imageAry
? JSON.parse(el.imageAry)
: [{ url: el.variantImage }]
}
}
}) })
coverImage.value = coverImage.value =
podOrderDetailsData.value?.productList?.[0]?.previewImgs?.[0].url || '' podOrderDetailsData.value?.productList?.[0]?.previewImgs?.[0].url || ''
...@@ -971,6 +1011,31 @@ const handleWarehouseChange = (value: string | number) => { ...@@ -971,6 +1011,31 @@ const handleWarehouseChange = (value: string | number) => {
_warehouseId.value = value _warehouseId.value = value
initOrderDetailBox() initOrderDetailBox()
} }
const printNormal = async () => {
const arr: (number | undefined)[] = []
;(podBoxList.value || []).forEach((item: PodMakeOrderData) => {
if (item.data) {
if (item.data.productList && item.data.productList.length > 0) {
item.data.productList.forEach((item1) => {
if (
item1.productMark == 'normal' ||
item1.productMark == 'custom_normal'
) {
arr?.push(item1?.id)
}
})
}
}
})
if (!arr.length) {
ElMessage.warning('暂无可打印的普货拣货单')
return
}
const res = await printNormalPickPdfApi(arr.join())
ElMessage.success('操作成功')
window.open(filePath + res.message)
}
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
...@@ -1149,6 +1214,17 @@ const handleWarehouseChange = (value: string | number) => { ...@@ -1149,6 +1214,17 @@ const handleWarehouseChange = (value: string | number) => {
color: #ddd; color: #ddd;
} }
} }
.cb-product-mark {
position: absolute;
top: 0;
right: 0;
.red-circle {
width: 8px;
height: 8px;
border-radius: 50%;
background-color: red;
}
}
</style> </style>
<style lang="scss"> <style lang="scss">
.pod-make-order-dialog { .pod-make-order-dialog {
......
...@@ -53,23 +53,13 @@ ...@@ -53,23 +53,13 @@
</ElFormItem> </ElFormItem>
<ElFormItem label="工艺"> <ElFormItem label="工艺">
<ElSelect <LogisticsWaySelect
v-model="searchForm.craftCodeArr" v-model="searchForm.craftCode"
clearable :company-list="craftList"
filterable :start-width="'178px'"
multiple search-placeholder="搜索工艺名称"
collapse-tags start-placeholder="请选择工艺名称"
collapse-tags-tooltip ></LogisticsWaySelect>
placeholder="请输入工艺"
style="width: 150px"
>
<el-option
v-for="item in craftList"
:key="item.craftCode"
:label="item.craftName"
:value="item.craftCode"
></el-option>
</ElSelect>
</ElFormItem> </ElFormItem>
<ElFormItem label="库存SKU"> <ElFormItem label="库存SKU">
<ElInput <ElInput
...@@ -135,6 +125,7 @@ ...@@ -135,6 +125,7 @@
> >
<el-radio-button label="single">单面</el-radio-button> <el-radio-button label="single">单面</el-radio-button>
<el-radio-button label="multiple">多面</el-radio-button> <el-radio-button label="multiple">多面</el-radio-button>
<el-radio-button label="normal">普品</el-radio-button>
</el-radio-group> </el-radio-group>
</ElFormItem> </ElFormItem>
<ElFormItem label="数量"> <ElFormItem label="数量">
...@@ -742,6 +733,11 @@ ...@@ -742,6 +733,11 @@
</ElButton> </ElButton>
</span> </span>
</ElFormItem> </ElFormItem>
<ElFormItem v-if="['WAIT_SHIPMENT'].includes(status)">
<span class="item">
<ElButton type="primary" @click="printNormal"> 普货拣货 </ElButton>
</span>
</ElFormItem>
<ElFormItem <ElFormItem
v-if=" v-if="
[ [
...@@ -1110,29 +1106,50 @@ ...@@ -1110,29 +1106,50 @@
class="goods-item-img" class="goods-item-img"
style="display: flex; flex-direction: column" style="display: flex; flex-direction: column"
> >
<div <template v-if="item.previewImgs?.length">
v-for="img in item.previewImgs" <div
:key="img" v-for="img in item.previewImgs"
style="text-align: center" :key="img"
> style="text-align: center"
>
<img
:src="img.url"
alt="商品图片"
style="cursor: pointer"
@click="handlePictureCardPreview(img.url)"
/>
<div
v-if="item.customizedQuantity"
class="triangle-box"
:title="`类型:${getQuantityText(
item.customizedQuantity,
)}面`"
>
<div class="multi-text">
{{ getQuantityText(item.customizedQuantity) }}
</div>
</div>
</div>
</template>
<template v-else>
<img <img
:src="img.url" :src="item.variantImage"
alt="商品图片" alt="商品图片"
style="cursor: pointer" style="cursor: pointer"
@click="handlePictureCardPreview(img.url)" @click="handlePictureCardPreview(item.variantImage)"
/> />
<div <div class="triangle-container-wrap">
v-if="item.customizedQuantity" <div class="triangle-container">
class="triangle-box" <div class="triangle-marker"></div>
:title="`类型:${getQuantityText( <div
item.customizedQuantity, class="content"
)}面`" :title="handleMark(item.productMark).label"
> >
<div class="multi-text"> {{ handleMark(item.productMark).name }}
{{ getQuantityText(item.customizedQuantity) }} </div>
</div> </div>
</div> </div>
</div> </template>
</div> </div>
<div class="goods-item-info"> <div class="goods-item-info">
<div class="goods-item-info-item"> <div class="goods-item-info-item">
...@@ -1374,7 +1391,10 @@ ...@@ -1374,7 +1391,10 @@
</template> </template>
</ElDropdown> --> </ElDropdown> -->
<div <div
v-if="status === 'WAIT_SHIPMENT'" v-if="
status === 'WAIT_SHIPMENT' &&
item.productMark !== 'custom_normal'
"
style=" style="
display: flex; display: flex;
flex-direction: column; flex-direction: column;
...@@ -1394,7 +1414,10 @@ ...@@ -1394,7 +1414,10 @@
<!-- f --> <!-- f -->
</div> </div>
<div <div
v-if="status === 'WAIT_SHIPMENT'" v-if="
status === 'WAIT_SHIPMENT' &&
item.productMark !== 'custom_normal'
"
style="display: flex; justify-content: space-between" style="display: flex; justify-content: space-between"
> >
<el-button <el-button
...@@ -1416,7 +1439,10 @@ ...@@ -1416,7 +1439,10 @@
</el-button> </el-button>
</div> </div>
<div <div
v-if="status === 'WAIT_SHIPMENT'" v-if="
status === 'WAIT_SHIPMENT' &&
item.productMark !== 'custom_normal'
"
style=" style="
display: flex; display: flex;
flex-direction: column; flex-direction: column;
...@@ -2852,6 +2878,7 @@ ...@@ -2852,6 +2878,7 @@
</ElDialog> </ElDialog>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import LogisticsWaySelect from '../../logistics/components/LogisticsWaySelect.tsx'
import { getUserMarkList } from '@/api/common' import { getUserMarkList } from '@/api/common'
// import { AnyObject } from '@/types/api/warehouse' // import { AnyObject } from '@/types/api/warehouse'
import { convertToChinaTime } from '@/utils/index' import { convertToChinaTime } from '@/utils/index'
...@@ -2926,6 +2953,7 @@ import { ...@@ -2926,6 +2953,7 @@ import {
countTrackRegisterApi, countTrackRegisterApi,
getCustomTagListApi, getCustomTagListApi,
getLogisticsWayApi, getLogisticsWayApi,
printNormalPickPdfApi,
} from '@/api/podUsOrder' } from '@/api/podUsOrder'
import { BaseRespData } from '@/types/api' import { BaseRespData } from '@/types/api'
...@@ -2949,6 +2977,7 @@ import { ...@@ -2949,6 +2977,7 @@ import {
CraftListData, CraftListData,
ExportParams, ExportParams,
InterceptStateGroupData, InterceptStateGroupData,
IAllList,
} from '@/types/api/podUsOrder' } from '@/types/api/podUsOrder'
import usePageList from '@/utils/hooks/usePageList' import usePageList from '@/utils/hooks/usePageList'
import { useValue } from '@/utils/hooks/useValue' import { useValue } from '@/utils/hooks/useValue'
...@@ -3184,8 +3213,8 @@ const [searchForm, resetSearchForm] = useValue<SearchForm>({ ...@@ -3184,8 +3213,8 @@ const [searchForm, resetSearchForm] = useValue<SearchForm>({
thirdSkuCode: '', thirdSkuCode: '',
supplierProductNo: '', supplierProductNo: '',
batchArrangeNumber: '', batchArrangeNumber: '',
craftCode: '', craftCode: [],
craftCodeArr: [], // craftCodeArr: [],
thirdStockSku: '', thirdStockSku: '',
}) })
const exceptionStatus = ref(1) const exceptionStatus = ref(1)
...@@ -3823,10 +3852,10 @@ const { ...@@ -3823,10 +3852,10 @@ const {
factorySubOrderNumber, factorySubOrderNumber,
startTime: timeRange.value?.[0] || null, startTime: timeRange.value?.[0] || null,
endTime: timeRange.value?.[1] || null, endTime: timeRange.value?.[1] || null,
craftCode: searchForm.value?.craftCodeArr?.join(',') || '', // craftCode: searchForm.value?.craftCodeArr?.join(',') || '',
tagsId: searchForm.value?.tagsIdArr?.join(',') || '', tagsId: searchForm.value?.tagsIdArr?.join(',') || '',
} }
baseparams.craftCodeArr && delete baseparams.craftCodeArr // baseparams.craftCodeArr && delete baseparams.craftCodeArr
baseparams.tagsIdArr && delete baseparams.tagsIdArr baseparams.tagsIdArr && delete baseparams.tagsIdArr
// 批量下载 // 批量下载
if (status.value === 'BATCH_DOWNLOAD') { if (status.value === 'BATCH_DOWNLOAD') {
...@@ -3881,10 +3910,14 @@ watch( ...@@ -3881,10 +3910,14 @@ watch(
order.productList?.forEach((product) => { order.productList?.forEach((product) => {
if (!product.previewImgs && product.imageAry) { if (!product.previewImgs && product.imageAry) {
try { try {
product.previewImgs = if (product.productMark === 'custom_normal') {
JSON.parse(product.imageAry)?.filter( product.previewImgs = []
(el: { title: string }) => el.title, } else {
) || [] product.previewImgs =
JSON.parse(product.imageAry)?.filter(
(el: { title: string }) => el.title,
) || []
}
} catch (error) { } catch (error) {
console.error('JSON解析失败:', error) console.error('JSON解析失败:', error)
product.previewImgs = [] product.previewImgs = []
...@@ -5176,7 +5209,58 @@ const resultConfirm = () => { ...@@ -5176,7 +5209,58 @@ const resultConfirm = () => {
search() search()
loadTabData() loadTabData()
} }
const handleMark = (mark: string) => {
switch (mark) {
case 'virtual':
return {
name: 'VIR',
color: '#ff9900',
label: '虚拟商品',
}
case 'normal':
return {
name: 'G',
color: '#67C23A',
label: '普通商品',
}
case 'pod':
return {
name: 'P',
color: '#F56C6C',
label: 'pod商品',
}
case 'custom':
return {
name: 'C',
color: '#6d9eeb',
label: '一件定制商品',
}
case 'custom_part':
return {
name: 'CP',
color: '#6d9eeb',
label: '一件定制局部印商品',
}
case 'custom_full':
return {
name: 'CF',
color: '#6d9eeb',
label: '一件定制满印商品',
}
case 'custom_normal':
return {
name: 'CB',
color: '#67C23A',
label: '胚衣',
}
default:
return {
name: '',
color: '#FFFFFF',
width: '0',
}
}
}
/** /**
* @description: 更改物流方式 * @description: 更改物流方式
*/ */
...@@ -5648,14 +5732,49 @@ const loadWarehouseList = async () => { ...@@ -5648,14 +5732,49 @@ const loadWarehouseList = async () => {
console.error(e) console.error(e)
} }
} }
const processType = ref([
{
label: '烫画',
value: 'TH',
},
{
label: '直喷',
value: 'ZP',
},
{
label: '刺绣',
value: 'CX',
},
{
label: '雕刻',
value: 'DK',
},
{
label: '白胚',
value: 'BP',
},
{
label: '其他',
value: 'QT',
},
])
// 获取工艺列表 // 获取工艺列表
const craftList = ref<CraftListData[]>([]) const craftList = ref<IAllList[]>([])
const processTypeMap = processType.value.reduce((acc, cur) => {
acc[cur.value] = cur.label
return acc
}, {} as Record<string, string>)
const loadCraftList = async () => { const loadCraftList = async () => {
try { try {
const res = await getListCraftApi() const res = await getListCraftApi()
if (res.code !== 200) return if (res.code !== 200) return
craftList.value = res.data const data: CraftListData[] = res.data
craftList.value = data.map((item) => ({
id: item.craftCode,
name: item.craftName,
warehouseName: processTypeMap[item.craftType] ?? '其他', // craftType 对应中文
})) as IAllList[]
} catch (e) { } catch (e) {
console.error(e) console.error(e)
} }
...@@ -6211,6 +6330,37 @@ const isPermissionBtn = globalProperties?.$isPermissionBtn ...@@ -6211,6 +6330,37 @@ const isPermissionBtn = globalProperties?.$isPermissionBtn
useRouter().beforeEach((to, from, next) => { useRouter().beforeEach((to, from, next) => {
handleBeforeRouteLeave(to, from, next) handleBeforeRouteLeave(to, from, next)
}) })
const printNormal = async () => {
await showConfirm('确定普货拣货吗?', {
confirmButtonText: '确认',
cancelButtonText: '取消',
type: 'warning',
})
const arr: (number | undefined)[] = []
if (!selection.value.length) {
return ElMessage.warning('请选择订单')
}
console.log(3660, selection.value)
selection.value.forEach((s) => {
s.productList &&
s.productList.forEach((p) => {
if (p.productMark === 'normal' || p.productMark === 'custom_normal') {
arr.push(p.id)
}
})
})
console.log(3661, arr)
if (!arr.length) {
ElMessage.warning('暂无可打印的普货拣货单')
return
}
const res = await printNormalPickPdfApi(arr.join())
ElMessage.success('操作成功')
window.open(filePath + res.message)
}
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.header-filter-form { .header-filter-form {
...@@ -6268,10 +6418,6 @@ useRouter().beforeEach((to, from, next) => { ...@@ -6268,10 +6418,6 @@ useRouter().beforeEach((to, from, next) => {
// width: 100px; // width: 100px;
// height: 65px; // height: 65px;
position: relative; position: relative;
img {
width: 65%;
}
} }
&:not(:last-child) { &:not(:last-child) {
...@@ -6552,12 +6698,54 @@ useRouter().beforeEach((to, from, next) => { ...@@ -6552,12 +6698,54 @@ useRouter().beforeEach((to, from, next) => {
color: white; color: white;
font-weight: bold; font-weight: bold;
} }
.search-form { .triangle-container-wrap {
::v-deep .el-radio-button { position: absolute;
width: 75px; top: 0;
.el-radio-button__inner { right: 0;
width: 100%; }
} .triangle-container {
position: relative;
.triangle-marker {
width: 0;
height: 0;
border: 18px solid transparent;
border-right: 18px solid #e74c3c;
border-top: 18px solid #e74c3c;
}
.content {
position: absolute;
top: 0;
left: 18px;
transform: rotate(45deg);
color: white;
text-align: center;
font-weight: bold;
font-size: 12px;
}
}
.triangle-container-wrap {
position: absolute;
top: 0;
right: 0;
}
.triangle-container {
position: relative;
.triangle-marker {
width: 0;
height: 0;
border: 18px solid transparent;
border-right: 18px solid #e74c3c;
border-top: 18px solid #e74c3c;
}
.content {
position: absolute;
top: 0;
left: 18px;
transform: rotate(45deg);
color: white;
text-align: center;
font-weight: bold;
font-size: 12px;
} }
} }
</style> </style>
......
<template>
<div class="user-page flex-column card h-100 overflow-hidden">
<div class="header-filter-form">
<el-button type="success" @click="addDialog" :disabled="tableData.length">
{{ '新增' }}
</el-button>
</div>
<div class="user-content flex-1 flex-column overflow-hidden">
<div class="user-list flex-1 overflow-hidden" v-loading="loading">
<el-row :gutter="20">
<el-col v-for="(item, index) in tableData as rowData[]" :key="index">
<div class="address-item">
<div class="left">
<div class="box">
所有生产单按{{ getLabels(item.groupField) }}进行自动排单
</div>
<div class="box">批次数量:{{ item.arrangeMax }}</div>
<div class="box">每天{{ getTime(item) }}开始自动排单</div>
</div>
<div class="action">
<el-icon
size="24"
title="编辑"
color="#EF6C00"
style="cursor: pointer; vertical-align: middle"
@click="editAddress(item)"
>
<Edit />
</el-icon>
</div>
</div>
</el-col>
<el-col :span="24" v-if="tableData.length === 0">
<div class="empty-box">
<el-empty description="暂无排单规则" />
</div>
</el-col>
</el-row>
</div>
</div>
</div>
<LogDialog
:title="editForm.id ? '编辑排单规则' : '新增排单规则'"
v-model="dialogVisible"
dialogWidth="1000px"
@close="cancelFn"
width="800px"
>
<div class="cardBox">
<el-form
class="form"
ref="editFormRef"
label-width="270"
:model="editForm"
>
<div class="formBox">
<div class="formContent">
<div
v-for="(_, index) in paramsList"
:key="index"
class="select-row"
>
<el-form-item
:label="index == 0 ? `所有生产单按` : '按'"
prop="appKey"
>
<el-select
v-model="paramsList[index]"
placeholder="请选择"
style="width: 200px"
@change="disabledFn"
:disabled="index == 0"
>
<el-option
v-for="option in rulesList"
:key="option.value"
:label="option.label"
:value="option.value"
:disabled="option.disabled"
/>
</el-select>
<div
class="iconClass"
v-if="index < 3 && paramsList.length < 4"
@click="addSelect(index)"
>
<el-icon color="#fff" size="20"> <Plus /></el-icon>
</div>
<div
class="iconClass"
v-if="index !== 0"
@click="removeSelect(index)"
>
<el-icon color="#fff" size="20"><Minus /></el-icon>
</div>
</el-form-item>
</div>
<el-form-item
label="在每天"
prop="time"
:rules="[
{
required: true,
message: '请选择时间',
trigger: ['change', 'blur'],
},
]"
>
<div style="display: flex">
<!-- <el-select
v-model="editForm.hour"
placeholder="请选择时"
style="width: 200px"
>
<el-option
v-for="(_, index) in 24"
:key="index"
:label="index"
:value="index"
/>
</el-select> -->
<el-time-picker
v-model="editForm.time"
value-format="HH:mm:ss"
placeholder="任意时间点"
>
</el-time-picker>
<div
style="white-space: nowrap; margin-left: 10px; color: #606266"
>
时进行自动排单
</div>
</div>
</el-form-item>
<div
style="border-top: 1px solid; color: #dcdfe6; padding-top: 10px"
>
<el-form-item
label="自动排单一个批次里生产单的最大值为:"
prop="arrangeMax"
:rules="[
{
required: true,
message: '请输入数量',
trigger: ['blur'],
},
{
pattern: /^\d+$/,
message: '请输入整数',
trigger: ['blur', 'change'],
},
]"
>
<el-input
v-model="editForm.arrangeMax"
style="width: 200px"
placeholder="请输入数量"
></el-input>
</el-form-item>
<div style="color: #f56c6c; margin: 10px 0">
注:系统限制烫画工艺一个批次不超过50个生产单!
</div>
<div style="display: flex; align-items: center">
<div style="color: #606266; margin-right: 10px">
烫画工艺自动排版:
</div>
<el-switch
v-model="editForm.isAuto"
@change="changeSwitch"
active-text="是"
inactive-text="否"
inline-prompt
class="ml-2"
style="
--el-switch-on-color: #42b983;
--el-switch-off-color: #f56c6c;
"
/>
</div>
<div v-if="editForm.isAuto">
<div label="排版类型:">
<el-radio-group v-model="editForm.type">
<el-radio label="tiff">tiff排版</el-radio>
<el-radio label="png">png排版</el-radio>
</el-radio-group>
</div>
<div label="排版宽度:">
<el-radio-group v-model="editForm.templateWidth">
<el-radio :value="42">40+2cm</el-radio>
<el-radio :value="60">60cm</el-radio>
</el-radio-group>
</div>
</div>
</div>
<el-form-item>
<ElButton class="btn" @click="cancelFn">取消</ElButton>
<ElButton class="btn" type="primary" @click="save">保存</ElButton>
</el-form-item>
</div>
</div>
</el-form>
</div>
</LogDialog>
</template>
<script setup lang="tsx">
defineOptions({
name: 'ShippingAddress',
})
import {
usArrangeRuleApi,
getByFactoryNoApi,
updateRuleByIdApi,
} from '@/api/podUsSchedulingRules.ts'
import { Plus, Minus } from '@element-plus/icons-vue'
import LogDialog from '.././components/dialog.tsx'
import CustomizeForm from '@/components/CustomizeForm.tsx'
import { Edit } from '@element-plus/icons-vue'
import { debounce } from 'lodash-es'
const editForm = ref<BaseForm>({ isAuto: false })
const tableData = ref([])
import userUserStore from '@/store/user'
const userStore = userUserStore()
const userInfo = userStore.user
const loading = ref(false)
const getList = async () => {
loading.value = true
try {
const result = await getByFactoryNoApi({
factoryNo: userInfo?.factoryId as number,
})
tableData.value = result.data ? [result.data] : []
console.log(231, tableData.value)
} catch (error) {
console.log(error)
} finally {
loading.value = false
}
}
onMounted(() => {
getList()
})
const dialogVisible = ref(false)
const editFormRef = ref<InstanceType<typeof CustomizeForm> | null>(null)
const paramsList = ref(['craft_type'])
const rulesList = ref([
{ label: '工艺', value: 'craft_type', disabled: false },
{ label: '款号', value: 'supplier_product_no', disabled: false },
{ label: '尺码', value: 'size', disabled: false },
{ label: '类型', value: 'customized_quantity', disabled: false },
])
/**
* @description: 取消按钮
*/
function cancelFn() {
dialogVisible.value = false
editFormRef.value?.resetFields()
}
/**
* @description: 编辑按钮
*/
async function editAddress(item: rowData) {
try {
editForm.value = { ...item }
paramsList.value = item.groupField
.split(';')
.filter((value) => value.trim() !== '')
editForm.value.time = `${item.hour || '00'}:${item.minute || '00'}:${
item.second || '00'
}`
console.log(372, editForm.value)
if (editForm.value.templateWidth) {
editForm.value.isAuto = true
}
dialogVisible.value = true
} catch (e) {
console.log(e)
}
}
/**
* @description: 检查数据
*/
interface rowData extends BaseForm {
groupField: string
}
interface BaseForm {
id?: number
time?: string
arrangeMax?: number
isAuto?: boolean
factoryNo?: number
type?: string
templateWidth?: string
jobId?: string
hour?: string | number
minute?: string | number
second?: string | number
}
type ParamKey = `param${number}`
type ParamValue = string | number | boolean | null | undefined
type PostData = BaseForm & Record<ParamKey, ParamValue>
async function checkData() {
// 先进行表单校验
const isValid = await new Promise<boolean>((resolve) => {
editFormRef.value
?.validate()
.then(() => resolve(true))
.catch((err) => {
console.log('表单校验失败:', err)
resolve(false)
})
})
// 如果表单校验失败,直接返回,不处理数据
if (!isValid) {
return { isValid: false, postData: null }
}
// 表单校验通过后,再处理数据
const postData: PostData = { ...editForm.value }
console.log()
const timeArr = editForm.value.time?.split(':')
if (timeArr?.length) {
postData.hour = timeArr[0]
postData.minute = timeArr[1]
postData.second = timeArr[2]
}
paramsList.value.forEach((el: ParamValue, index: number) => {
const key: ParamKey = `param${index + 1}`
if (el) {
postData[key] = el
}
})
postData.factoryNo = userInfo?.factoryId
return { isValid, postData }
}
/**
* @description: 保存按钮
*/
const save = debounce(async () => {
const { isValid, postData } = await checkData()
if (isValid) {
try {
if (!postData?.id) {
await usArrangeRuleApi({
...postData,
})
} else {
const params = {
param: { ...postData },
id: postData.id,
jobId: postData.jobId,
}
params.param.id && delete params.param.id
params.param.jobId && delete params.param.jobId
await updateRuleByIdApi({
...params,
})
}
ElMessage({
message: postData?.id ? '更新成功' : '新增成功',
type: 'success',
offset: window.innerHeight / 2,
})
cancelFn()
getList()
} catch (e) {
return
}
}
}, 200)
const disabledFn = () => {
console.log(328, paramsList.value)
rulesList.value.forEach((el) => {
if (paramsList.value.includes(el.value)) {
el.disabled = true
} else {
el.disabled = false
}
})
}
const addSelect = (index: number) => {
paramsList.value.splice(index + 1, 0, '')
disabledFn()
}
const removeSelect = (index: number) => {
// selectItems.value.splice(index, 1)
paramsList.value.splice(index, 1)
disabledFn()
}
const getLabels = (item: string) => {
if (!item) return ''
const ruleValues = new Set(item.split(';'))
return rulesList.value
.filter((rule) => ruleValues.has(rule.value))
.map((rule) => rule.label)
.join(',')
}
/**
* @description: 新增按钮打开弹窗
*/
function addDialog() {
editForm.value = { isAuto: false }
paramsList.value = ['craft_type']
dialogVisible.value = true
}
const changeSwitch = () => {
editForm.value.type = undefined
editForm.value.templateWidth = undefined
}
const padTimeUnit = (value: string | number): string => {
const num = Number(value)
// 若转换后是 NaN(无效值),或原始值不存在(null/undefined/''),均返回 '00'
if (isNaN(num) || !value) {
return '00'
}
// 确保结果为字符串,补位后返回
return num < 10 ? `0${num}` : `${num}`
}
const getTime = (item: BaseForm) => {
const { hour, minute, second } = item
const newHour = padTimeUnit(hour as string | number)
const newMinute = padTimeUnit(minute as string | number)
const newSecond = padTimeUnit(second as string | number)
return `${newHour}:${newMinute}:${newSecond}`
}
</script>
<style lang="scss" scoped>
.header-filter-form {
margin-bottom: 20px;
:deep(.el-form-item) {
margin-right: 14px;
margin-bottom: 10px;
}
}
.user-operate-btn {
margin-bottom: 10px;
}
.dialog-footer {
text-align: center;
}
:deep() {
.rowClass {
background-color: red;
}
}
.address-item {
box-sizing: border-box;
padding: 10px;
margin-bottom: 10px;
border: 1px solid #ececec;
display: flex;
align-items: center;
justify-content: space-between;
.check {
margin-right: 10px;
}
.left {
flex: 1;
flex-shrink: 0;
overflow: hidden;
margin-right: 10px;
display: flex;
.box {
margin-right: 20px;
}
}
.address {
margin-bottom: 10px;
font-size: 14px;
overflow: hidden;
/* 确保超出的文本被裁剪 */
white-space: nowrap;
/* 确保文本在一行内显示 */
text-overflow: ellipsis;
/* 超出的文本显示为省略号 */
width: 100%;
}
.action {
.iconView {
}
}
.name {
display: flex;
align-items: center;
margin-bottom: 10px;
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
p {
flex: 1;
flex-shrink: 0;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
margin: 0 10px;
font-size: 14px;
}
b {
font-size: 17px;
}
span {
margin: 0 10px;
font-size: 14px;
}
}
}
.iconClass {
cursor: pointer;
width: 32px;
height: 32px;
background-color: #409eff;
text-align: center;
border-radius: 5px;
margin-left: 10px;
display: flex;
justify-content: center;
align-items: center;
}
</style>
...@@ -8,6 +8,7 @@ import vueJsx from '@vitejs/plugin-vue-jsx' ...@@ -8,6 +8,7 @@ import vueJsx from '@vitejs/plugin-vue-jsx'
// https://vitejs.dev/config/ // https://vitejs.dev/config/
export default defineConfig({ export default defineConfig({
base: '/', base: '/',
plugins: [ plugins: [
vue(), vue(),
vueJsx(), vueJsx(),
......
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