Commit 95aa07a3 by qinjianhui

Merge branch 'dev' into 'master'

Dev

See merge request !96
parents 283e4e3c e8b276ce
......@@ -54,6 +54,7 @@ declare module 'vue' {
ElTag: typeof import('element-plus/es')['ElTag']
ElTimeline: typeof import('element-plus/es')['ElTimeline']
ElTimelineItem: typeof import('element-plus/es')['ElTimelineItem']
ElTimePicker: typeof import('element-plus/es')['ElTimePicker']
ElTooltip: typeof import('element-plus/es')['ElTooltip']
ElTree: typeof import('element-plus/es')['ElTree']
ElUpload: typeof import('element-plus/es')['ElUpload']
......
......@@ -626,3 +626,11 @@ export function getCustomTagListApi() {
`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({
},
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',
meta: {
title: 'POD(CN)订单跟踪',
......
......@@ -137,6 +137,11 @@ const menu: MenuItem[] = [
id: 10,
label: 'POD(US)订单跟踪',
},
{
index: '/pod-us-order/podUsSchedulingRules',
id: 11,
label: 'POD(US)排单规则',
},
],
},
......
......@@ -38,7 +38,7 @@ export interface SearchForm {
thirdSkuCode?: string
supplierProductNo?: string
batchArrangeNumber?: string
craftCode?: string
craftCode?: string[] | string
thirdStockSku?: string
exceptionHandling?: number | undefined
interceptStatus?: number | string
......@@ -221,6 +221,7 @@ export interface LogisticsFormData {
export interface CraftListData {
craftName: string
craftCode: string
craftType: string
}
export interface PackingData {
podProductionNo?: string // 生产单号(PSCD 开头)
......
......@@ -34,7 +34,7 @@ export interface SearchForm {
thirdSkuCode?: string
supplierProductNo?: string
batchArrangeNumber?: string
craftCode?: string
craftCode?: string | string[]
craftCodeArr?: string[]
thirdStockSku?: string
interceptStatus?: number | string
......@@ -141,6 +141,7 @@ export interface ProductList {
batchArrangeNumber?: string | null
interceptStatus?: number | null
sizeType?: number | null
productMark?: string | null
customTagList?: { name: string }[]
}
export interface cardImages {
......@@ -216,6 +217,7 @@ export interface LogisticsFormData {
export interface CraftListData {
craftName: string
craftCode: string
craftType: string
}
export interface InterceptStateGroupData {
......@@ -226,3 +228,8 @@ export interface InterceptStateGroupData {
[key: string]: number
}
}
export interface IAllList {
id: string
name: string
warehouseName?: string
}
......@@ -43,7 +43,7 @@ interface IwayList {
}
interface IAllList {
factoryId?: number
id: number
id: number | string
name: string
serviceCode?: string
siteUrl?: string
......@@ -83,6 +83,18 @@ export default defineComponent({
type: String,
default: 'id',
},
searchPlaceholder: {
type: String,
default: '请搜索承运商',
},
startPlaceholder: {
type: String,
default: '请选择物流方式',
},
startWidth: {
type: String,
default: '100%',
},
},
emits: ['update:modelValue'],
setup(props, { emit }) {
......@@ -161,7 +173,20 @@ export default defineComponent({
},
{ 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) {
if (event) {
selectedList.value = [
......@@ -252,8 +277,10 @@ export default defineComponent({
reference: () => (
<ElInput
modelValue={waysName.value}
style={{ width: '100%' }}
placeholder="请选择物流方式"
style={{ width: props.startWidth }}
placeholder={props.startPlaceholder}
clearable
onClear={handleClearSelection}
/>
),
}}
......@@ -271,7 +298,7 @@ export default defineComponent({
}}
clearable
style={{ width: '200px', padding: '10px' }}
placeholder="请搜索承运商"
placeholder={props.searchPlaceholder}
/>
<ElScrollbar
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 @@
</ElFormItem>
<ElFormItem label="工艺">
<ElSelect
<LogisticsWaySelect
v-model="searchForm.craftCode"
clearable
filterable
placeholder="请选择工艺"
style="width: 150px"
>
<el-option
v-for="item in craftList"
:key="item.craftCode"
:label="item.craftName"
:value="item.craftCode"
></el-option>
</ElSelect>
:company-list="craftList"
:start-width="'150px'"
search-placeholder="搜索工艺名称"
start-placeholder="请选择工艺名称"
></LogisticsWaySelect>
</ElFormItem>
<ElFormItem label="库存SKU">
<ElInput
......@@ -2476,6 +2469,7 @@
</template>
<script setup lang="ts">
import { getUserMarkList } from '@/api/common'
import LogisticsWaySelect from '../../logistics/components/LogisticsWaySelect.tsx'
// import { AnyObject } from '@/types/api/warehouse'
import {
InfoFilled,
......@@ -2544,7 +2538,7 @@ import { BaseRespData } from '@/types/api'
import UpdateAddress from './components/updateAddress.vue'
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 {
LogListData,
......@@ -2776,7 +2770,7 @@ const [searchForm, resetSearchForm] = useValue<SearchForm>({
thirdSkuCode: '',
supplierProductNo: '',
batchArrangeNumber: '',
craftCode: '',
craftCode: [],
thirdStockSku: '',
})
const shipmentArea = ref(0)
......@@ -4982,14 +4976,48 @@ const loadWarehouseList = async () => {
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 () => {
try {
const res = await getListCraftApi()
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) {
console.error(e)
}
......
......@@ -172,6 +172,7 @@
@click="podOrderDetailsData && print(podOrderDetailsData, true)"
>手动打印</ElButton
>
<ElButton type="primary" @click="printNormal">普货拣货 </ElButton>
<ElButton type="success" @click="handlePrintFinish"
>打单完成</ElButton
>
......@@ -210,6 +211,17 @@
<span v-if="item.data" class="number">
{{ item.data.pickingNumber }}/{{ item.data.purchaseNumber }}
</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>
......@@ -234,11 +246,13 @@ import {
getPackingDataApi,
getPodBoxListApi,
submitInspectionApi,
printNormalPickPdfApi,
} from '@/api/podUsOrder'
import useUserStore from '@/store/user'
import { Check } from '@element-plus/icons-vue'
import socket from '@/utils/websocket'
import { WarehouseListData } from '@/types/api/podUsOrder'
import { filePath } from '@/api/axios.ts'
const { getCLodop } = useLodop()
......@@ -274,7 +288,6 @@ const podOrderDetailsColumns = computed(() => [
width: 250,
slot: 'image',
align: 'center',
fixed: 'left',
},
{
label: '生产单号',
......@@ -282,21 +295,23 @@ const podOrderDetailsColumns = computed(() => [
width: 150,
align: 'center',
},
// {
// label: 'base SKU',
// prop: 'baseSku',
// width: 140,
// align: 'center',
// },
{
label: '库存 SKU',
prop: 'thirdSkuCode',
width: 180,
align: 'center',
},
{
label: 'variant SKU',
prop: 'variantSku',
width: 140,
width: 160,
align: 'center',
},
{
label: '商品名称',
prop: 'productName',
minWidth: 100,
},
{
......@@ -304,18 +319,21 @@ const podOrderDetailsColumns = computed(() => [
prop: 'purchaseNumber',
width: 90,
align: 'center',
fixed: 'right',
},
{
label: '拣货数量',
prop: 'count',
width: 90,
align: 'center',
fixed: 'right',
},
{
label: '验证结果',
slot: 'verifyResult',
width: 90,
align: 'center',
fixed: 'right',
},
])
const boxChange = ref<boolean>(false)
......@@ -401,7 +419,15 @@ watch(
(val) => {
if (val && val.productList?.length)
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 },
......@@ -426,7 +452,13 @@ const renderItemBox = (bool: boolean) => {
if (!boxItem) boxItem = { data: { productList: [] } }
const { data } = boxItem
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) {
renderLock = false
......@@ -690,7 +722,15 @@ const initOrderDetailBox = async () => {
boxIndex.value = boxList.find((item) => item.data)?.box || null
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 =
podOrderDetailsData.value?.productList?.[0]?.previewImgs?.[0].url || ''
......@@ -971,6 +1011,31 @@ const handleWarehouseChange = (value: string | number) => {
_warehouseId.value = value
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>
<style scoped lang="scss">
......@@ -1149,6 +1214,17 @@ const handleWarehouseChange = (value: string | number) => {
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 lang="scss">
.pod-make-order-dialog {
......
......@@ -53,23 +53,13 @@
</ElFormItem>
<ElFormItem label="工艺">
<ElSelect
v-model="searchForm.craftCodeArr"
clearable
filterable
multiple
collapse-tags
collapse-tags-tooltip
placeholder="请输入工艺"
style="width: 150px"
>
<el-option
v-for="item in craftList"
:key="item.craftCode"
:label="item.craftName"
:value="item.craftCode"
></el-option>
</ElSelect>
<LogisticsWaySelect
v-model="searchForm.craftCode"
:company-list="craftList"
:start-width="'178px'"
search-placeholder="搜索工艺名称"
start-placeholder="请选择工艺名称"
></LogisticsWaySelect>
</ElFormItem>
<ElFormItem label="库存SKU">
<ElInput
......@@ -135,6 +125,7 @@
>
<el-radio-button label="single">单面</el-radio-button>
<el-radio-button label="multiple">多面</el-radio-button>
<el-radio-button label="normal">普品</el-radio-button>
</el-radio-group>
</ElFormItem>
<ElFormItem label="数量">
......@@ -742,6 +733,11 @@
</ElButton>
</span>
</ElFormItem>
<ElFormItem v-if="['WAIT_SHIPMENT'].includes(status)">
<span class="item">
<ElButton type="primary" @click="printNormal"> 普货拣货 </ElButton>
</span>
</ElFormItem>
<ElFormItem
v-if="
[
......@@ -1110,29 +1106,50 @@
class="goods-item-img"
style="display: flex; flex-direction: column"
>
<div
v-for="img in item.previewImgs"
:key="img"
style="text-align: center"
>
<template v-if="item.previewImgs?.length">
<div
v-for="img in item.previewImgs"
: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
:src="img.url"
:src="item.variantImage"
alt="商品图片"
style="cursor: pointer"
@click="handlePictureCardPreview(img.url)"
@click="handlePictureCardPreview(item.variantImage)"
/>
<div
v-if="item.customizedQuantity"
class="triangle-box"
:title="`类型:${getQuantityText(
item.customizedQuantity,
)}面`"
>
<div class="multi-text">
{{ getQuantityText(item.customizedQuantity) }}
<div class="triangle-container-wrap">
<div class="triangle-container">
<div class="triangle-marker"></div>
<div
class="content"
:title="handleMark(item.productMark).label"
>
{{ handleMark(item.productMark).name }}
</div>
</div>
</div>
</div>
</template>
</div>
<div class="goods-item-info">
<div class="goods-item-info-item">
......@@ -1374,7 +1391,10 @@
</template>
</ElDropdown> -->
<div
v-if="status === 'WAIT_SHIPMENT'"
v-if="
status === 'WAIT_SHIPMENT' &&
item.productMark !== 'custom_normal'
"
style="
display: flex;
flex-direction: column;
......@@ -1394,7 +1414,10 @@
<!-- f -->
</div>
<div
v-if="status === 'WAIT_SHIPMENT'"
v-if="
status === 'WAIT_SHIPMENT' &&
item.productMark !== 'custom_normal'
"
style="display: flex; justify-content: space-between"
>
<el-button
......@@ -1416,7 +1439,10 @@
</el-button>
</div>
<div
v-if="status === 'WAIT_SHIPMENT'"
v-if="
status === 'WAIT_SHIPMENT' &&
item.productMark !== 'custom_normal'
"
style="
display: flex;
flex-direction: column;
......@@ -2852,6 +2878,7 @@
</ElDialog>
</template>
<script setup lang="ts">
import LogisticsWaySelect from '../../logistics/components/LogisticsWaySelect.tsx'
import { getUserMarkList } from '@/api/common'
// import { AnyObject } from '@/types/api/warehouse'
import { convertToChinaTime } from '@/utils/index'
......@@ -2926,6 +2953,7 @@ import {
countTrackRegisterApi,
getCustomTagListApi,
getLogisticsWayApi,
printNormalPickPdfApi,
} from '@/api/podUsOrder'
import { BaseRespData } from '@/types/api'
......@@ -2949,6 +2977,7 @@ import {
CraftListData,
ExportParams,
InterceptStateGroupData,
IAllList,
} from '@/types/api/podUsOrder'
import usePageList from '@/utils/hooks/usePageList'
import { useValue } from '@/utils/hooks/useValue'
......@@ -3184,8 +3213,8 @@ const [searchForm, resetSearchForm] = useValue<SearchForm>({
thirdSkuCode: '',
supplierProductNo: '',
batchArrangeNumber: '',
craftCode: '',
craftCodeArr: [],
craftCode: [],
// craftCodeArr: [],
thirdStockSku: '',
})
const exceptionStatus = ref(1)
......@@ -3823,10 +3852,10 @@ const {
factorySubOrderNumber,
startTime: timeRange.value?.[0] || null,
endTime: timeRange.value?.[1] || null,
craftCode: searchForm.value?.craftCodeArr?.join(',') || '',
// craftCode: searchForm.value?.craftCodeArr?.join(',') || '',
tagsId: searchForm.value?.tagsIdArr?.join(',') || '',
}
baseparams.craftCodeArr && delete baseparams.craftCodeArr
// baseparams.craftCodeArr && delete baseparams.craftCodeArr
baseparams.tagsIdArr && delete baseparams.tagsIdArr
// 批量下载
if (status.value === 'BATCH_DOWNLOAD') {
......@@ -3881,10 +3910,14 @@ watch(
order.productList?.forEach((product) => {
if (!product.previewImgs && product.imageAry) {
try {
product.previewImgs =
JSON.parse(product.imageAry)?.filter(
(el: { title: string }) => el.title,
) || []
if (product.productMark === 'custom_normal') {
product.previewImgs = []
} else {
product.previewImgs =
JSON.parse(product.imageAry)?.filter(
(el: { title: string }) => el.title,
) || []
}
} catch (error) {
console.error('JSON解析失败:', error)
product.previewImgs = []
......@@ -5176,7 +5209,58 @@ const resultConfirm = () => {
search()
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: 更改物流方式
*/
......@@ -5648,14 +5732,49 @@ const loadWarehouseList = async () => {
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 () => {
try {
const res = await getListCraftApi()
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) {
console.error(e)
}
......@@ -6211,6 +6330,37 @@ const isPermissionBtn = globalProperties?.$isPermissionBtn
useRouter().beforeEach((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>
<style lang="scss" scoped>
.header-filter-form {
......@@ -6268,10 +6418,6 @@ useRouter().beforeEach((to, from, next) => {
// width: 100px;
// height: 65px;
position: relative;
img {
width: 65%;
}
}
&:not(:last-child) {
......@@ -6552,12 +6698,54 @@ useRouter().beforeEach((to, from, next) => {
color: white;
font-weight: bold;
}
.search-form {
::v-deep .el-radio-button {
width: 75px;
.el-radio-button__inner {
width: 100%;
}
.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;
}
}
.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>
......
<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'
// https://vitejs.dev/config/
export default defineConfig({
base: '/',
plugins: [
vue(),
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