Commit b50250e2 by wuqian

Merge branch 'dev' of 47.122.114.111:qinjianhui/factory_front into wq

parents c3f44acd 7d2db864
...@@ -4,7 +4,7 @@ import { showError } from '@/utils/ui.ts' ...@@ -4,7 +4,7 @@ import { showError } from '@/utils/ui.ts'
const axios = Axios.create({ const axios = Axios.create({
baseURL: import.meta.env.VITE_API_BASE, baseURL: import.meta.env.VITE_API_BASE,
timeout: 30000, timeout: 300000, //凯哥让改的超时时间
}) })
const TOKEN_KEY = 'token' const TOKEN_KEY = 'token'
......
...@@ -18,6 +18,38 @@ export interface LogisticsData { ...@@ -18,6 +18,38 @@ export interface LogisticsData {
partition: string // 所在分区 partition: string // 所在分区
logisticsWayId?: number | null logisticsWayId?: number | null
} }
export interface AddressInfo {
id?:string
receiverName: string,
receiverPhone: string,
receiverCountry: string,
receiverProvince: string,
receiverCity: string,
receiverDistrict: string,
receiverAddress1: string,
receiverAddress2: string,
receiverPostCode: string,
}
// 同步收货地址
export function syncReceiverAddress(data: number[]) {
return axios.post<never, BaseRespData<never>>(
'factory/podJomallOrderUs/syncReceiverAddress',
data,
)
}
export function refreshAddressApi(idList: number[]) {
return axios.post<never, BaseRespData<never>>(
'factory/podJomallOrderUs/syncReceiverAddress',
idList,
)
}
export function updateAddressApi(data: PodUsOrderListData) {
return axios.post<never, BaseRespData<never>>(
'factory/podJomallOrderUs/updateReceiverAddress',
data,
)
}
export function getOrderTabData() { export function getOrderTabData() {
return axios.get<never, BaseRespData<Tab[]>>( return axios.get<never, BaseRespData<Tab[]>>(
'/factory/podJomallOrderUs/findStateGroupList', '/factory/podJomallOrderUs/findStateGroupList',
...@@ -306,6 +338,14 @@ export function createLogisticsOrderApi(params: { ...@@ -306,6 +338,14 @@ export function createLogisticsOrderApi(params: {
params, params,
) )
} }
// 更改物流
export function composingDesignImages(data:number[]) {
return axios.post<never, BaseRespData<never>>(
'factory/podJomallOrderUs/composingDesignImages',
data
)
}
// 转至待拣胚 // 转至待拣胚
export function updateLogisticsToPickingApi(params: { ids: string }) { export function updateLogisticsToPickingApi(params: { ids: string }) {
return axios.get<never, BaseRespData<never>>( return axios.get<never, BaseRespData<never>>(
......
...@@ -2,12 +2,12 @@ import { defineComponent, PropType, ref, computed, h } from 'vue' ...@@ -2,12 +2,12 @@ import { defineComponent, PropType, ref, computed, h } from 'vue'
import type { Component } from 'vue' import type { Component } from 'vue'
import { ElInput, ElForm, ElFormItem } from 'element-plus' import { ElInput, ElForm, ElFormItem } from 'element-plus'
import type { FormInstance } from 'element-plus' import type { FormInstance } from 'element-plus'
import AmountInput from './Form.vue/AmountInput.vue' // 金额输入框 import AmountInput from './Form/AmountInput.vue' // 金额输入框
import DateRangePicker from './Form.vue/DateRangePicker.vue' // 时间范围选择器 import DateRangePicker from './Form/DateRangePicker.vue' // 时间范围选择器
import DatePicker from './Form.vue/DatePicker.vue' // 单个日期选择器 import DatePicker from './Form/DatePicker.vue' // 单个日期选择器
import Select from './Form.vue/Select.vue' // 普通下拉选择框 import Select from './Form/Select.vue' // 普通下拉选择框
import Switch from './Form.vue/Switch .vue' import Switch from './Form/Switch .vue'
import './Form.vue/form.scss' import './Form/form.scss'
import type { FormItemRule } from 'element-plus' import type { FormItemRule } from 'element-plus'
......
<template>
<div
v-show="visible"
class="right-click-menu"
:style="{ left: position.x + 'px', top: position.y + 'px' }"
>
<div class="menu-item" @click="onChangeCopy('select-all')">全部选择</div>
<div class="menu-item" @click="onChangeCopy('cancel-select')">取消选择</div>
<div
v-if="showCopySubShopNumber"
class="menu-item"
@click="onChangeCopy('copy_code')"
>
复制生产单号
</div>
<div
v-if="showCopyShopNumber"
class="menu-item"
@click="onChangeCopy('copy_shopNumber')"
>
复制店铺单号
</div>
<div v-if="showCopyCount" class="menu-item" @click="onChangeCopy('count')">
统计数量
</div>
<slot></slot>
</div>
</template>
<script setup lang="ts">
import { ref, defineExpose, onMounted, onUnmounted } from 'vue'
const visible = ref(false)
const position = ref({ x: 0, y: 0 })
const emit = defineEmits(['onChange'])
defineProps({
showCopyShopNumber: {
type: Boolean,
default: true,
},
showCopyCount: {
type: Boolean,
default: true,
},
showCopySubShopNumber: {
type: Boolean,
default: true,
},
})
const setPosition = ({ x, y }: { x: number; y: number }) => {
position.value = { x, y }
visible.value = true
}
const onChangeCopy = (value: string) => {
emit('onChange', value)
visible.value = false
}
// 点击其他地方关闭菜单
const handleClickOutside = () => {
if (visible.value) {
visible.value = false
}
}
// 监听全局点击事件
onMounted(() => {
document.addEventListener('click', handleClickOutside)
})
onUnmounted(() => {
document.removeEventListener('click', handleClickOutside)
})
defineExpose({
setPosition,
})
</script>
<style scoped lang="scss">
.right-click-menu {
position: fixed;
background: white;
border: 1px solid #e4e7ed;
border-radius: 4px;
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
z-index: 3000;
}
.right-click-menu {
:deep(.menu-item) {
padding: 8px 16px;
cursor: pointer;
font-size: 14px;
color: #606266;
}
}
.right-click-menu {
:deep(.menu-item):hover {
background-color: #f5f7fa;
color: #409eff;
}
}
</style>
import { defineComponent, PropType, ref, computed, watch, h } from 'vue' import { defineComponent, PropType, ref, computed, watch, h } from 'vue'
import type { Component } from 'vue' import type { Component } from 'vue'
import AmountInput from './Form.vue/AmountInput.vue' // 金额输入框 import AmountInput from './Form/AmountInput.vue' // 金额输入框
import DateRangePicker from './Form.vue/DateRangePicker.vue' // 时间范围选择器 import DateRangePicker from './Form/DateRangePicker.vue' // 时间范围选择器
import DatePicker from './Form.vue/DatePicker.vue' // 单个日期选择器 import DatePicker from './Form/DatePicker.vue' // 单个日期选择器
import Select from './Form.vue/Select.vue' // 普通下拉选择框 import Select from './Form/Select.vue' // 普通下拉选择框
import { ElInput, ElForm, ElFormItem, ElButton } from 'element-plus' import { ElInput, ElForm, ElFormItem, ElButton } from 'element-plus'
import { import {
ISearchForm, ISearchForm,
......
<template>
<div
v-if="tableRightMenuVisible"
class="table-right-menu"
:style="{
left: rightMenuOptions.contextMenuX + 'px',
top: rightMenuOptions.contextMenuY + 'px',
}"
>
<div
class="table-right-menu-item"
@click="handleChange('copy_shop_number')"
>
<span>复制店铺单号</span>
</div>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
defineProps({
tableRightMenuVisible: {
type: Boolean,
default: false,
},
})
const emit = defineEmits(['onCopyChange'])
const handleChange = (type: string) => {
emit('onCopyChange', type)
}
const rightMenuOptions = ref({
contextMenuX: 0,
contextMenuY: 0,
})
const setRightMenuOptions = (options: {
contextMenuX: number
contextMenuY: number
}) => {
rightMenuOptions.value = options
}
defineExpose({
setRightMenuOptions,
})
</script>
<style lang="scss" scoped>
.table-right-menu {
position: fixed;
z-index: 1000;
border: 1px solid #ccc;
box-shadow: 0 0 10px 0 rgba(0, 0, 0, 0.1);
padding: 6px;
}
.table-right-menu-item {
padding: 5px;
cursor: pointer;
font-size: 14px;
&:hover {
background-color: #f0f0f0;
}
}
</style>
...@@ -64,7 +64,10 @@ import { type Slot, useAttrs, useSlots, type PropType, shallowRef } from 'vue' ...@@ -64,7 +64,10 @@ import { type Slot, useAttrs, useSlots, type PropType, shallowRef } from 'vue'
import type { CustomColumn } from '@/types/table' import type { CustomColumn } from '@/types/table'
import RenderColumn from './RenderColumn.vue' import RenderColumn from './RenderColumn.vue'
import { ElTable } from 'element-plus' import { ElTable } from 'element-plus'
const tableRef = shallowRef<InstanceType<typeof ElTable>>() import type { TableInstance } from 'element-plus'
const tableRef = shallowRef<TableInstance>()
// eslint-disable-next-line @typescript-eslint/no-explicit-any // eslint-disable-next-line @typescript-eslint/no-explicit-any
defineProps({ defineProps({
paginatedData: { paginatedData: {
...@@ -88,23 +91,31 @@ defineProps({ ...@@ -88,23 +91,31 @@ defineProps({
default: false, default: false,
}, },
}) })
const attrs = useAttrs() const attrs = useAttrs()
const slots = useSlots() as Record<string, Slot> const slots = useSlots() as Record<string, Slot>
const setCurrentRow = (row: T) => { const setCurrentRow = (row: T) => {
tableRef.value?.setCurrentRow(row) tableRef.value?.setCurrentRow(row)
} }
const toggleRowSelection = (row: T, selected: boolean = true) => { const toggleRowSelection = (row: T, selected: boolean = true) => {
tableRef.value?.toggleRowSelection(row, selected) tableRef.value?.toggleRowSelection(row, selected)
} }
const clearSelection = () => { const clearSelection = () => {
tableRef.value?.clearSelection() tableRef.value?.clearSelection()
} }
const toggleAllSelection = () => {
tableRef.value?.toggleAllSelection()
}
defineExpose({ defineExpose({
tableRef, tableRef,
setCurrentRow, setCurrentRow,
toggleRowSelection, toggleRowSelection,
clearSelection, clearSelection,
toggleAllSelection,
}) })
</script> </script>
...@@ -112,5 +123,6 @@ defineExpose({ ...@@ -112,5 +123,6 @@ defineExpose({
.table-view { .table-view {
height: 100%; height: 100%;
overflow: hidden; overflow: hidden;
position: relative;
} }
</style> </style>
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
<div class="upload-area" @click="triggerFileInput"> <div class="upload-area" @click="triggerFileInput">
<el-icon class="el-icon--upload"><UploadFilled /></el-icon> <el-icon class="el-icon--upload"><UploadFilled /></el-icon>
<div class="el-upload__text"> <div class="el-upload__text">
将文件拖到此处,或<span style="color: #409eff">点击上传</span> <span style="color: #409eff">点击上传</span>
</div> </div>
</div> </div>
<input <input
...@@ -46,7 +46,14 @@ ...@@ -46,7 +46,14 @@
<script setup lang="ts"> <script setup lang="ts">
import * as XLSX from 'xlsx' import * as XLSX from 'xlsx'
import { ref, watch, defineProps, defineEmits, computed } from 'vue' import {
ref,
watch,
defineProps,
defineEmits,
computed,
defineExpose,
} from 'vue'
import { UploadFilled, Document, Close } from '@element-plus/icons-vue' import { UploadFilled, Document, Close } from '@element-plus/icons-vue'
import { ElMessage } from 'element-plus' import { ElMessage } from 'element-plus'
import { uploadFileApi } from '@/api/common' import { uploadFileApi } from '@/api/common'
...@@ -89,7 +96,7 @@ watch( ...@@ -89,7 +96,7 @@ watch(
const triggerFileInput = () => { const triggerFileInput = () => {
if (loading.value) return if (loading.value) return
fileInputRef.value?.click() if (fileInputRef.value) fileInputRef.value?.click()
} }
const downloadFile = (path: string) => { const downloadFile = (path: string) => {
window.open(path) window.open(path)
...@@ -120,8 +127,17 @@ const onFileChange = async (e: Event) => { ...@@ -120,8 +127,17 @@ const onFileChange = async (e: Event) => {
ElMessage.error('文件大小不能超过50MB') ElMessage.error('文件大小不能超过50MB')
return return
} }
if (props.importType === 'local') { if (props.importType === 'localAndUpload') {
// 本地解析 // 本地直接获取上传内容
emit('imported', { path: file.name, data: file })
if (fileInputRef.value) {
fileInputRef.value.value = ''
}
loading.value = false
return
}
if (props.importType === 'localAndXlsx') {
// 本地解析,并自行处理xlsx数据
const reader = new FileReader() const reader = new FileReader()
reader.onload = (evt) => { reader.onload = (evt) => {
const data = evt.target?.result const data = evt.target?.result
...@@ -134,6 +150,7 @@ const onFileChange = async (e: Event) => { ...@@ -134,6 +150,7 @@ const onFileChange = async (e: Event) => {
fileList.value = [{ path: file.name, filename: file.name }] fileList.value = [{ path: file.name, filename: file.name }]
value.value = file.name || '' value.value = file.name || ''
ElMessage.success('导入成功') ElMessage.success('导入成功')
loading.value = false
} }
reader.readAsBinaryString(file) reader.readAsBinaryString(file)
if (fileInputRef.value) fileInputRef.value.value = '' if (fileInputRef.value) fileInputRef.value.value = ''
...@@ -143,7 +160,18 @@ const onFileChange = async (e: Event) => { ...@@ -143,7 +160,18 @@ const onFileChange = async (e: Event) => {
await fileUpload(file) await fileUpload(file)
if (fileInputRef.value) fileInputRef.value.value = '' if (fileInputRef.value) fileInputRef.value.value = ''
} }
const resetUpload = () => {
fileList.value = []
value.value = ''
if (fileInputRef.value) {
fileInputRef.value.value = ''
}
}
// 暴露重置方法给父组件
defineExpose({
resetUpload,
})
const fileUpload = async (file: File) => { const fileUpload = async (file: File) => {
const formData = new FormData() const formData = new FormData()
const filename = file.name const filename = file.name
......
...@@ -6,6 +6,7 @@ export interface Tab { ...@@ -6,6 +6,7 @@ export interface Tab {
export interface SearchForm { export interface SearchForm {
timeType: number | null timeType: number | null
shopNumber: string shopNumber: string
shipmentType: string
userMark: string userMark: string
logisticsTracking: string logisticsTracking: string
baseSku: string baseSku: string
...@@ -18,7 +19,7 @@ export interface SearchForm { ...@@ -18,7 +19,7 @@ export interface SearchForm {
endTime: string | null endTime: string | null
exceptionHandling: number | undefined exceptionHandling: number | undefined
platform: string platform: string
productionClient:string productionClient: string
} }
export interface PodUsOrderListData { export interface PodUsOrderListData {
id: number id: number
...@@ -92,6 +93,7 @@ export interface ProductList { ...@@ -92,6 +93,7 @@ export interface ProductList {
tagIds?: string tagIds?: string
isProduction?: boolean isProduction?: boolean
createTime?: string createTime?: string
startStockingTime?: string | null
updateTime?: string updateTime?: string
remark?: string | null remark?: string | null
version?: number version?: number
......
...@@ -101,6 +101,7 @@ const [editForm, resetEditForm] = useValue<AddDeclarationRuleObj>({ ...@@ -101,6 +101,7 @@ const [editForm, resetEditForm] = useValue<AddDeclarationRuleObj>({
currency: 'USD', currency: 'USD',
}) })
const { const {
loading,
currentPage, currentPage,
pageSize, pageSize,
total, total,
...@@ -466,7 +467,7 @@ const tableConfig = ref<TableColumn[]>([ ...@@ -466,7 +467,7 @@ const tableConfig = ref<TableColumn[]>([
}, },
]) ])
const loading = ref(false) // const loading = ref(false)
/** /**
* @description: 取消按钮 * @description: 取消按钮
......
...@@ -436,7 +436,7 @@ onMounted(() => { ...@@ -436,7 +436,7 @@ onMounted(() => {
}) })
async function search() { async function search() {
loading.value = true // loading.value = true
const isEqual = const isEqual =
JSON.stringify(searchForm.value) === JSON.stringify({ logisticsIdList: [] }) JSON.stringify(searchForm.value) === JSON.stringify({ logisticsIdList: [] })
try { try {
...@@ -448,7 +448,7 @@ async function search() { ...@@ -448,7 +448,7 @@ async function search() {
} catch (e) { } catch (e) {
console.log(e) console.log(e)
} finally { } finally {
loading.value = false // loading.value = false
} }
} }
...@@ -729,6 +729,7 @@ function handleSizeChange(val: number) { ...@@ -729,6 +729,7 @@ function handleSizeChange(val: number) {
} }
async function getList() { async function getList() {
loading.value = true
try { try {
const { data } = await getlogisticsQuotationPage({ const { data } = await getlogisticsQuotationPage({
...searchForm.value, ...searchForm.value,
...@@ -741,10 +742,13 @@ async function getList() { ...@@ -741,10 +742,13 @@ async function getList() {
mergeCells.value = [] mergeCells.value = []
} catch (error) { } catch (error) {
console.log(error) console.log(error)
} finally {
loading.value = false
} }
} }
const templeData = ref<LogisticsQuotation[]>([]) const templeData = ref<LogisticsQuotation[]>([])
async function getSearchList() { async function getSearchList() {
loading.value = true
try { try {
const { data } = await getlogisticsQuotationList({ const { data } = await getlogisticsQuotationList({
...searchForm.value, ...searchForm.value,
...@@ -784,6 +788,8 @@ async function getSearchList() { ...@@ -784,6 +788,8 @@ async function getSearchList() {
mergeCells.value = newMergeCells mergeCells.value = newMergeCells
} catch (error) { } catch (error) {
console.log(error) console.log(error)
} finally {
loading.value = false
} }
} }
......
...@@ -271,9 +271,11 @@ ...@@ -271,9 +271,11 @@
v-loading="loading" v-loading="loading"
element-loading-text="加载中..." element-loading-text="加载中..."
class="order-list flex-1 overflow-hidden" class="order-list flex-1 overflow-hidden"
@contextmenu.prevent="handleContextMenu"
> >
<ElTable <ElTable
v-show="statusCode !== 6" v-show="statusCode !== 6"
ref="tableRef"
:data="tableData" :data="tableData"
:span-method="arraySpanMethod" :span-method="arraySpanMethod"
default-expand-all default-expand-all
...@@ -597,6 +599,7 @@ ...@@ -597,6 +599,7 @@
</ElTable> </ElTable>
<ElTable <ElTable
v-show="statusCode === 6" v-show="statusCode === 6"
ref="tableQaRef"
:data="tableData" :data="tableData"
:span-method="arraySpanMethod" :span-method="arraySpanMethod"
default-expand-all default-expand-all
...@@ -973,6 +976,13 @@ ...@@ -973,6 +976,13 @@
> >
<OrderDetail :order-detail-data="orderDetailData" /> <OrderDetail :order-detail-data="orderDetailData" />
</ElDrawer> </ElDrawer>
<RightClickMenu
ref="rightMenuRef"
:show-copy-shop-number="true"
:show-copy-count="false"
:show-copy-sub-shop-number="true"
@on-change="rightChange"
/>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import Shipment from './Shipment.vue' import Shipment from './Shipment.vue'
...@@ -990,7 +1000,12 @@ import type { ...@@ -990,7 +1000,12 @@ import type {
} from '@/types/api/order' } from '@/types/api/order'
import { nextTick, onMounted, reactive, ref } from 'vue' import { nextTick, onMounted, reactive, ref } from 'vue'
import useElTableColumnWidth from '@/utils/hooks/useElTableColumnWidth' import useElTableColumnWidth from '@/utils/hooks/useElTableColumnWidth'
import { ArrowDown, ArrowUp, DocumentCopy, EditPen } from '@element-plus/icons-vue' import {
ArrowDown,
ArrowUp,
DocumentCopy,
EditPen,
} from '@element-plus/icons-vue'
import usePageList from '@/utils/hooks/usePageList' import usePageList from '@/utils/hooks/usePageList'
import { import {
getOrderList, getOrderList,
...@@ -1023,6 +1038,7 @@ import OrderDetail from './OrderDetail.vue' ...@@ -1023,6 +1038,7 @@ import OrderDetail from './OrderDetail.vue'
import useShipment from './hook/useShipment' import useShipment from './hook/useShipment'
import useQuarantine from './hook/useQuarantine' import useQuarantine from './hook/useQuarantine'
import Quarantine from './Quarantine.vue' import Quarantine from './Quarantine.vue'
import RightClickMenu from '@/components/RightClickMenu.vue'
const [searchForm] = useValue<SearchForm>({ const [searchForm] = useValue<SearchForm>({
mainSku: '', mainSku: '',
...@@ -1644,7 +1660,70 @@ const cancelOrder = async (id: number) => { ...@@ -1644,7 +1660,70 @@ const cancelOrder = async (id: number) => {
// showError(e) // showError(e)
} }
} }
const rightMenuRef = ref()
const handleContextMenu = (e: MouseEvent) => {
rightMenuRef.value?.setPosition({
x: e.clientX,
y: e.clientY,
cardItem: e.clientY,
el: e,
})
}
const tableQaRef = ref()
const tableRef = ref()
const rightChange = (key: string) => {
if (key === 'select-all') {
if (statusCode.value === 6) {
tableQaRef.value?.toggleAllSelection()
} else {
tableRef.value?.toggleAllSelection()
}
} else if (key === 'cancel-select') {
if (statusCode.value === 6) {
tableQaRef.value?.clearSelection()
} else {
tableRef.value?.clearSelection()
}
} else if (key === 'copy_shopNumber') {
let shopNumberList: string[] = []
if (statusCode.value === 6) {
shopNumberList = tableData.value
.map((item) => item.detailList)
.flat()
.map((e) => e?.shopNumber)
.filter((shopNumber): shopNumber is string => shopNumber !== undefined)
} else {
shopNumberList = tableData.value
.map((item) => item.productList)
.flat()
.map((e) => e?.shopNumber)
.filter((shopNumber): shopNumber is string => shopNumber !== undefined)
}
copy(shopNumberList.join(','))
} else if (key === 'copy_code') {
let subOrderNumber: string[] = []
if (statusCode.value === 6) {
subOrderNumber = tableData.value
.map((item) => item.detailList)
.flat()
.map((e) => e?.subOrderNumber)
.filter(
(subOrderNumber): subOrderNumber is string =>
subOrderNumber !== undefined,
)
} else {
subOrderNumber = tableData.value
.map((item) => item.productList)
.flat()
.map((e) => e?.subOrderNumber)
.filter(
(subOrderNumber): subOrderNumber is string =>
subOrderNumber !== undefined,
)
}
copy(subOrderNumber.join(','))
}
}
const onChangeCurrentRow = (item: ProductList) => { const onChangeCurrentRow = (item: ProductList) => {
currentRow.value = item currentRow.value = item
} }
......
...@@ -231,7 +231,12 @@ ...@@ -231,7 +231,12 @@
v-if="['TO_BE_CONFIRMED', 'IN_PRODUCTION'].includes(status)" v-if="['TO_BE_CONFIRMED', 'IN_PRODUCTION'].includes(status)"
class="item" class="item"
> >
<ElButton :loading="syncLoading" type="warning" is-dark @click="synchronousPlan"> <ElButton
:loading="syncLoading"
type="warning"
is-dark
@click="synchronousPlan"
>
同步素材图</ElButton 同步素材图</ElButton
> >
</span> </span>
...@@ -481,7 +486,7 @@ ...@@ -481,7 +486,7 @@
" "
> >
{{ cardItem?.processName }} {{ cardItem?.processName }}
</span> </span>
</el-col> </el-col>
<el-col <el-col
:span="12" :span="12"
...@@ -544,11 +549,10 @@ ...@@ -544,11 +549,10 @@
> >
<img width="20" src="@/assets/images/id.png" /> <img width="20" src="@/assets/images/id.png" />
<el-tooltip :content="cardItem?.productionFileId"> <el-tooltip :content="cardItem?.productionFileId">
<span class="over-hidden" title="素材ID"> <span class="over-hidden" title="素材ID">
{{ cardItem?.productionFileId }} {{ cardItem?.productionFileId }}
</span> </span>
</el-tooltip> </el-tooltip>
</a> </a>
</el-col> </el-col>
<el-col <el-col
...@@ -586,15 +590,17 @@ ...@@ -586,15 +590,17 @@
v-loading="loading" v-loading="loading"
element-loading-text="加载中..." element-loading-text="加载中..."
class="order-list flex-1 overflow-hidden" class="order-list flex-1 overflow-hidden"
@contextmenu.prevent="(v) => rightClick(v, null)"
> >
<TableView <TableView
ref="tableRef"
:paginated-data="tableData" :paginated-data="tableData"
:columns="tableColumns" :columns="tableColumns"
:selectionable="true" :selectionable="true"
default-expand-all default-expand-all
:span-method="arraySpanMethod" :span-method="arraySpanMethod"
@selection-change="handleSelectionChange" @selection-change="handleSelectionChange"
@contextmenu.prevent="handleContextMenu" @on-change="onChange"
> >
<template #expand="{ row }"> <template #expand="{ row }">
<div v-if="row.productList" class="table-expand"> <div v-if="row.productList" class="table-expand">
...@@ -969,12 +975,20 @@ ...@@ -969,12 +975,20 @@
基版 <b>{{ cardItem?.baseSku }}</b> 的统计数量为:<b> {{ count }}</b> 基版 <b>{{ cardItem?.baseSku }}</b> 的统计数量为:<b> {{ count }}</b>
</p> </p>
</el-dialog> </el-dialog>
<right-menu <RightClickMenu
ref="rightMenuRef" ref="rightMenuRef"
:show-copy-shop-number=" :show-copy-shop-number="true"
['IN_PRODUCTION', 'TO_BE_CONFIRMED', 'WAIT_SHIPMENT'].includes(status) :show-copy-count="
[
'IN_PRODUCTION',
'TO_BE_CONFIRMED',
'WAIT_SHIPMENT',
'TO_BE_REPLENISHMENT',
'INVALID',
].includes(status)
" "
@change="rightChange" :show-copy-sub-shop-number="true"
@on-change="rightChange"
/> />
<fastProduction <fastProduction
v-model:detailVisible="detailVisible" v-model:detailVisible="detailVisible"
...@@ -986,11 +1000,6 @@ ...@@ -986,11 +1000,6 @@
@on-success="handleSuccess" @on-success="handleSuccess"
@close="fastClose" @close="fastClose"
></fastProduction> ></fastProduction>
<table-right-menu
ref="tableRightMenuRef"
:table-right-menu-visible="tableRightMenuVisible"
@on-copy-change="onCopyChange"
/>
<el-dialog <el-dialog
v-model="completeShipmentVisible" v-model="completeShipmentVisible"
title="完成发货" title="完成发货"
...@@ -1056,7 +1065,6 @@ ...@@ -1056,7 +1065,6 @@
</template> </template>
<script setup lang="tsx"> <script setup lang="tsx">
// refreshJMProductInfo,reasonInvalidationApi, // refreshJMProductInfo,reasonInvalidationApi,
import RightMenu from './rightMenu.vue'
import { import {
getOrderTabData, getOrderTabData,
getOrderList, getOrderList,
...@@ -1105,7 +1113,6 @@ import { ...@@ -1105,7 +1113,6 @@ import {
SearchForm, SearchForm,
Tab, Tab,
LogListData, LogListData,
PodProductList, PodProductList,
cardImages, cardImages,
imageAryInter, imageAryInter,
...@@ -1119,6 +1126,7 @@ import { filePath } from '@/api/axios' ...@@ -1119,6 +1126,7 @@ import { filePath } from '@/api/axios'
import { CustomColumn } from '@/types/table' import { CustomColumn } from '@/types/table'
import LogList from '@/components/LogList.vue' import LogList from '@/components/LogList.vue'
import CommonCard from '@/components/CommonCard.vue' import CommonCard from '@/components/CommonCard.vue'
import RightClickMenu from '@/components/RightClickMenu.vue'
const tableRef = ref() const tableRef = ref()
const loading = ref(false) const loading = ref(false)
const currentPage = ref(1) const currentPage = ref(1)
...@@ -1153,13 +1161,14 @@ const completeShipmentForm = ref({ ...@@ -1153,13 +1161,14 @@ const completeShipmentForm = ref({
logisticsTracking: '', // 物流跟踪号 logisticsTracking: '', // 物流跟踪号
carriageAmount: '', // 物流费用 carriageAmount: '', // 物流费用
}) })
const rightClick = (e: MouseEvent, item: PodProductList | CardOrderData) => { const rightClick = (
cardItem.value = item e: MouseEvent,
item: PodProductList | CardOrderData | null,
) => {
cardItem.value = item || undefined
rightMenuRef.value.setPosition({ rightMenuRef.value.setPosition({
x: e.clientX, x: e.clientX,
y: e.clientY, y: e.clientY,
cardItem: e.clientY,
el: e,
}) })
} }
const handleSizeChange = (size: number) => { const handleSizeChange = (size: number) => {
...@@ -1171,10 +1180,14 @@ const handleCurrentChange = (page: number) => { ...@@ -1171,10 +1180,14 @@ const handleCurrentChange = (page: number) => {
loadDiffList() loadDiffList()
} }
const rightChange = async (code: string) => { const rightChange = async (code: string) => {
const flag = ['IN_PRODUCTION', 'TO_BE_CONFIRMED', 'WAIT_SHIPMENT'].includes( const flag = [
status.value, 'IN_PRODUCTION',
) 'TO_BE_CONFIRMED',
if (code === 'check_all') { 'WAIT_SHIPMENT',
'TO_BE_REPLENISHMENT',
'INVALID',
].includes(status.value)
if (code === 'select-all') {
if (flag) { if (flag) {
selection.value = JSON.parse(JSON.stringify(CardOrderList.value)) selection.value = JSON.parse(JSON.stringify(CardOrderList.value))
} else { } else {
...@@ -1183,17 +1196,35 @@ const rightChange = async (code: string) => { ...@@ -1183,17 +1196,35 @@ const rightChange = async (code: string) => {
} }
// selection.value = JSON.parse(JSON.stringify(tableData.value)) // selection.value = JSON.parse(JSON.stringify(tableData.value))
} }
} else if (code === 'clear_check') { } else if (code === 'cancel-select') {
selection.value = [] selection.value = []
tableRef.value?.tableRef.toggleAllSelection() tableRef.value?.tableRef.toggleAllSelection()
} else if (code === 'copy_code') { } else if (code === 'copy_code') {
const str = selection.value let str
.map((item) => item.factorySubOrderNumber || item.factoryOrderNumber) if (flag) {
.join() str = CardOrderList.value.map((item) => item.factorySubOrderNumber).join()
navigator.clipboard.writeText(str) } else {
str = tableData.value
.map((item) => item.productList)
.flat()
.map((item) => item?.factorySubOrderNumber)
.filter((item) => item)
.join()
}
navigator.clipboard.writeText(str || '')
ElMessage.success('复制成功') ElMessage.success('复制成功')
} else if (code === 'copy_shopNumber') { } else if (code === 'copy_shopNumber') {
const str = selection.value.map((item) => item.shopNumber).join() let str
if (flag) {
str = CardOrderList.value.map((item) => item.shopNumber).join()
} else {
str = tableData.value
.map((item) => item.productList)
.flat()
.map((item) => item?.shopNumber)
.filter((item) => item)
.join()
}
navigator.clipboard.writeText(str) navigator.clipboard.writeText(str)
ElMessage.success('复制成功') ElMessage.success('复制成功')
} else if (code === 'count') { } else if (code === 'count') {
...@@ -1341,6 +1372,7 @@ const saveCompleteShipment = async () => { ...@@ -1341,6 +1372,7 @@ const saveCompleteShipment = async () => {
const CardOrderList = ref<(PodProductList | CardOrderData)[]>([]) const CardOrderList = ref<(PodProductList | CardOrderData)[]>([])
const loadCardList = async () => { const loadCardList = async () => {
loading.value = true
try { try {
const res = await getCardOrderList( const res = await getCardOrderList(
{ {
...@@ -1379,7 +1411,17 @@ const loadCardList = async () => { ...@@ -1379,7 +1411,17 @@ const loadCardList = async () => {
item.images = images item.images = images
} else { } else {
if (item.imageAry) { if (item.imageAry) {
const images = JSON.parse(item.imageAry as string) console.log(item.imageAry)
let images
if (
typeof item.imageAry === 'string' &&
item.imageAry.startsWith('https')
) {
images = []
} else {
images = JSON.parse(item.imageAry as string)
}
if (Array.isArray(images)) { if (Array.isArray(images)) {
item.images = images.map((e: imageAryInter) => { item.images = images.map((e: imageAryInter) => {
return { return {
...@@ -1395,10 +1437,14 @@ const loadCardList = async () => { ...@@ -1395,10 +1437,14 @@ const loadCardList = async () => {
}) })
total.value = res.data.total total.value = res.data.total
} catch (error) { } catch (error) {
console.error(error)
// showError(error) // showError(error)
} finally {
loading.value = false
} }
} }
const loadOrderList = async () => { const loadOrderList = async () => {
loading.value = true
try { try {
const res = await getOrderList( const res = await getOrderList(
{ {
...@@ -1421,6 +1467,8 @@ const loadOrderList = async () => { ...@@ -1421,6 +1467,8 @@ const loadOrderList = async () => {
total.value = res.data.total total.value = res.data.total
} catch (error) { } catch (error) {
// showError(error) // showError(error)
} finally {
loading.value = false
} }
} }
const currentImage = ref('') const currentImage = ref('')
...@@ -1825,7 +1873,9 @@ const synchronousPlan = async () => { ...@@ -1825,7 +1873,9 @@ const synchronousPlan = async () => {
return return
} }
try { try {
const res = await syncSubOrderDesignImages(selection.value.map(item=>item.id)) const res = await syncSubOrderDesignImages(
selection.value.map((item) => item.id),
)
await loadDiffList() await loadDiffList()
await loadTabData() await loadTabData()
syncLoading.value = false syncLoading.value = false
...@@ -2161,10 +2211,8 @@ const invalidOrder = async (row: ProductList) => { ...@@ -2161,10 +2211,8 @@ const invalidOrder = async (row: ProductList) => {
} }
}) })
} }
const tableRightMenuVisible = ref(false) const onChange = (type: string) => {
const tableRightMenuRef = ref() if (type === 'shop-numbers') {
const onCopyChange = (type: string) => {
if (type === 'copy_shop_number') {
const shopNumberList = [] const shopNumberList = []
for (const item of tableData.value) { for (const item of tableData.value) {
if (item.productList) { if (item.productList) {
...@@ -2174,16 +2222,6 @@ const onCopyChange = (type: string) => { ...@@ -2174,16 +2222,6 @@ const onCopyChange = (type: string) => {
copy(shopNumberList.join(',')) copy(shopNumberList.join(','))
} }
} }
const handleContextMenu = (e: MouseEvent) => {
tableRightMenuVisible.value = true
tableRightMenuRef.value?.setRightMenuOptions({
contextMenuX: e.clientX,
contextMenuY: e.clientY,
})
}
const listenerClick = () => {
tableRightMenuVisible.value = false
}
const getUserMark = async () => { const getUserMark = async () => {
try { try {
...@@ -2208,7 +2246,6 @@ const openMaterial = async (item: PodProductList) => { ...@@ -2208,7 +2246,6 @@ const openMaterial = async (item: PodProductList) => {
} }
onMounted(async () => { onMounted(async () => {
document.addEventListener('keydown', listenerKeydown) document.addEventListener('keydown', listenerKeydown)
document.addEventListener('click', listenerClick)
await loadTabData() await loadTabData()
getUserMark() getUserMark()
getLogisticsList() getLogisticsList()
...@@ -2216,7 +2253,6 @@ onMounted(async () => { ...@@ -2216,7 +2253,6 @@ onMounted(async () => {
}) })
onBeforeUnmount(() => { onBeforeUnmount(() => {
document.removeEventListener('keydown', listenerKeydown) document.removeEventListener('keydown', listenerKeydown)
document.removeEventListener('click', listenerClick)
}) })
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
...@@ -2491,9 +2527,9 @@ onBeforeUnmount(() => { ...@@ -2491,9 +2527,9 @@ onBeforeUnmount(() => {
display: flex; display: flex;
align-items: center; align-items: center;
} }
.over-hidden{ .over-hidden {
width: 150px; width: 150px;
display:inline-block; display: inline-block;
white-space: nowrap; white-space: nowrap;
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
......
<template>
<!-- <div class="wrap" @click="close"> -->
<div v-if="show" ref="right_menu" class="right_menu">
<button @click="$emit('change', 'check_all')">
全部选择
</button>
<button @click="$emit('change', 'clear_check')">取消选择</button>
<button @click="$emit('change', 'copy_code')">复制选中生产单号</button>
<button v-if="showCopyShopNumber" @click="$emit('change', 'copy_shopNumber')">复制选中店铺单号</button>
<button v-if="showCopyShopNumber" @click="$emit('change', 'count')">统计数量</button>
</div>
<!-- </div> -->
</template>
<script setup lang="ts">
import {ref,defineProps,defineExpose} from 'vue'
interface E{
x:number
el:HTMLElement
y:number
}
defineProps({
showCopyShopNumber:{
type:Boolean,
default:true
}
})
const row = ref()
const show = ref(false)
const right_menu = ref<HTMLElement>()
const close = ()=>{
show.value = false
document.body.onclick = null
}
const setPosition = (o:E)=> {
console.log(o)
show.value = true
const clientX = o.x
const x = document.body.clientWidth - clientX
document.body.onclick = function () {
close()
}
row.value =
setTimeout(() => {
if(!right_menu.value) return
if (x < 150) {
right_menu.value.style.cssText = `top:${o.y}px;right:${x}px`
} else {
right_menu.value.style.cssText = `top:${o.y}px;left:${o.x}px`
}
}, 1)
}
defineExpose({
setPosition
})
</script>
<style scoped>
.wrap {
position: fixed;
top: 0;
right: 0;
bottom: 0;
left: 0;
}
.right_menu {
position: fixed;
max-width: 200px;
min-height: 50px;
background: #fff;
box-shadow: 1px 1px 10px 1px rgba(0, 0, 0, 0.5);
border-radius: 5px;
overflow: hidden;
z-index: 999;
/* left: -500px; */
top: -500px;
}
.right_menu button {
display: block;
line-height: 30px;
font-size: 14px;
padding: 0 15px;
text-align: left;
width: 100%;
cursor: pointer;
}
.right_menu button:hover {
background: rgb(65, 192, 251);
color: white;
}
button{
outline: none;
border: none;
}
</style>
...@@ -74,7 +74,7 @@ const checkAll = ref(false) ...@@ -74,7 +74,7 @@ const checkAll = ref(false)
const selectedList = ref<(string | number)[]>([]) const selectedList = ref<(string | number)[]>([])
// 显示弹窗 // 显示弹窗
const showDialog = (type: string) => { const showDialog = (type?: string) => {
console.log(type) console.log(type)
resultDialog.value = true resultDialog.value = true
......
<script setup lang="ts">
import { defineModel } from 'vue'
import { updateAddressApi,AddressInfo } from '@/api/podUsOrder.ts'
const emits = defineEmits(['success'])
defineProps<{
countryList: { countryCode: string }[]
}>()
const visible = defineModel<boolean>('visible')
const form = defineModel<AddressInfo>('form', {
default: {
receiverName: '',
receiverPhone: '',
receiverCountry: '',
receiverProvince: '',
receiverCity: '',
receiverDistrict: '',
receiverAddress1: '',
receiverAddress2: '',
receiverPostCode: '',
},
})
const formRef = ref()
const rules = {
receiverName: [{ required: true, message: '请输入姓名', trigger: 'blur' }],
receiverPhone: [{ required: true, message: '请输入电话', trigger: 'blur' }],
receiverCountry: [{ required: true, message: '请输入国家', trigger: 'blur' }],
receiverProvince: [{ required: true, message: '请输入省/州', trigger: 'blur' }],
receiverCity: [{ required: true, message: '请输入市', trigger: 'blur' }],
receiverAddress1: [{ required: true, message: '请输入地址1', trigger: 'blur' }],
receiverPostCode: [{ required: true, message: '请输入邮政编码', trigger: 'blur' }],
}
const submitForm = async () => {
formRef?.value.validate(async (valid: boolean) => {
if (valid) {
await updateAddressApi(form.value as never)
visible.value = false
emits('success')
await ElMessageBox.alert('请修改/刷新地址后取消物流重新创建物流订单、获取跟踪号、获取打印面单。', '提示', {
type: 'warning',
confirmButtonText:'确定',
cancelButtonText:'取消',
showCancelButton: true,
})
}
})
}
</script>
<template>
<el-dialog
v-model="visible"
:close-on-click-modal="false"
title="修改揽收信息"
width="50%"
>
<el-form ref="formRef" :model="form" :rules="rules" label-width="120px">
<el-form-item label="姓名" prop="receiverName">
<el-input v-model="form.receiverName" clearable placeholder="请输入姓名" />
</el-form-item>
<el-form-item label="电话" prop="receiverPhone">
<el-input v-model="form.receiverPhone" clearable placeholder="请输入电话" />
</el-form-item>
<el-form-item label="国家" prop="receiverCountry">
<el-select v-model="form.receiverCountry" clearable filterable>
<el-option
v-for="it in countryList" :key="it.countryCode" :label="it.countryCode"
:value="it.countryCode"></el-option>
</el-select>
</el-form-item>
<el-form-item label="省/州" prop="receiverProvince">
<el-input v-model="form.receiverProvince" clearable placeholder="请输入省/州" />
</el-form-item>
<el-form-item label="市" prop="receiverCity">
<el-input v-model="form.receiverCity" clearable placeholder="请输入市" />
</el-form-item>
<el-form-item label="区/县" prop="receiverDistrict">
<el-input v-model="form.receiverDistrict" clearable placeholder="请输入区/县" />
</el-form-item>
<el-form-item label="地址1" prop="receiverAddress1">
<el-input v-model="form.receiverAddress1" clearable placeholder="请输入地址1" />
</el-form-item>
<el-form-item label="地址2" prop="receiverAddress2">
<el-input v-model="form.receiverAddress2" clearable placeholder="请输入地址2" />
</el-form-item>
<el-form-item label="邮政编码" prop="receiverPostCode">
<el-input v-model="form.receiverPostCode" clearable placeholder="请输入邮政编码" />
</el-form-item>
</el-form>
<template #footer>
<el-button @click="visible=false">取消</el-button>
<el-button type="primary" @click="submitForm">提交</el-button>
</template>
</el-dialog>
</template>
<style scoped lang="scss">
</style>
...@@ -75,7 +75,7 @@ ...@@ -75,7 +75,7 @@
style="width: 160px" style="width: 160px"
> >
<img <img
:src="`../../../src/assets/${item.icon}`" :src="`/images/icon/${item.icon.split('/').pop()}`"
style="height: 20px; margin: 5px 10px 0 0" style="height: 20px; margin: 5px 10px 0 0"
/> />
<span :title="item.type">{{ item.type }}</span> <span :title="item.type">{{ item.type }}</span>
...@@ -145,7 +145,21 @@ ...@@ -145,7 +145,21 @@
style="width: 150px" style="width: 150px"
/> />
</ElFormItem> </ElFormItem>
<ElFormItem label="物流类型">
<ElSelect
v-model="searchForm.shipmentType"
placeholder="物流类型"
clearable
style="width: 130px"
>
<ElOption
v-for="(item, index) in ['自有物流', '工厂物流']"
:key="index"
:value="index"
:label="item"
></ElOption>
</ElSelect>
</ElFormItem>
<ElFormItem> <ElFormItem>
<ElButton type="primary" @click="search">查询</ElButton> <ElButton type="primary" @click="search">查询</ElButton>
</ElFormItem> </ElFormItem>
...@@ -230,6 +244,15 @@ ...@@ -230,6 +244,15 @@
</div> </div>
</div> </div>
<div class="operation-box mb-10"> <div class="operation-box mb-10">
<span v-if="status === 'PICKING'" class="item">
<ElButton
:loading="downloadLoading"
type="primary"
@click="downloadTif"
>
排版
</ElButton>
</span>
<span v-if="status === 'TO_BE_CONFIRMED'" class="item"> <span v-if="status === 'TO_BE_CONFIRMED'" class="item">
<ElButton type="success" @click="confirmOrder"> 确认 </ElButton> <ElButton type="success" @click="confirmOrder"> 确认 </ElButton>
</span> </span>
...@@ -237,6 +260,14 @@ ...@@ -237,6 +260,14 @@
<ElButton type="success" @click="updateOrder"> 转至待确认 </ElButton> <ElButton type="success" @click="updateOrder"> 转至待确认 </ElButton>
</span> </span>
<span <span
v-if="status === 'EXCEPTION_ORDER' && exceptionStatus === 3"
class="item"
>
<ElButton type="success" @click="asyncOrderAddress">
同步收货地址
</ElButton>
</span>
<span
v-if="status === 'EXCEPTION_ORDER' && exceptionStatus === 1" v-if="status === 'EXCEPTION_ORDER' && exceptionStatus === 1"
class="item" class="item"
> >
...@@ -354,6 +385,7 @@ ...@@ -354,6 +385,7 @@
v-loading="loading" v-loading="loading"
element-loading-text="加载中..." element-loading-text="加载中..."
class="table-wrapper flex-1 flex-column overflow-hidden" class="table-wrapper flex-1 flex-column overflow-hidden"
@contextmenu.prevent="(v) => rightClick(v)"
> >
<TableView <TableView
ref="tableRef" ref="tableRef"
...@@ -573,6 +605,12 @@ ...@@ -573,6 +605,12 @@
</el-icon> </el-icon>
</div> </div>
<div class="order-detail-item"> <div class="order-detail-item">
<span class="order-detail-item-label">物流类型:</span>
<span class="order-detail-item-value">
{{ ['自有物流', '工厂物流'][row.shipmentType] }}
</span>
</div>
<div class="order-detail-item">
<span class="order-detail-item-label">物流跟踪号:</span> <span class="order-detail-item-label">物流跟踪号:</span>
<span class="order-detail-item-value"> <span class="order-detail-item-value">
<el-button <el-button
...@@ -670,6 +708,15 @@ ...@@ -670,6 +708,15 @@
{{ row.lanshouAddress }} {{ row.lanshouAddress }}
</span> </span>
</div> </div>
<div
v-if="['TO_BE_CONFIRMED', 'STOCK_OUT'].includes(status)"
class="order-detail-item"
>
<span class="order-detail-item-label">订单延期时间:</span>
<span class="order-detail-item-value red-time">
{{ calculateDelayTime(row) }}
</span>
</div>
</div> </div>
</template> </template>
<template #price="{ row }"> <template #price="{ row }">
...@@ -731,6 +778,9 @@ ...@@ -731,6 +778,9 @@
</div> </div>
</div> --> </div> -->
</template> </template>
<template #exceptionReason="{row}">
<div v-html="row.exceptionReason"></div>
</template>
<template #innerLabel="{ row }"> <template #innerLabel="{ row }">
<div v-if="row.internalMemoList" class="inner-label-box"> <div v-if="row.internalMemoList" class="inner-label-box">
<div <div
...@@ -748,7 +798,17 @@ ...@@ -748,7 +798,17 @@
</template> </template>
<template #operate="{ row }"> <template #operate="{ row }">
<div class="operate-box"> <div class="operate-box">
<span class="operate-item"> <span
class="operate-item"
style="display: flex; flex-direction: column"
>
<ElButton
link
type="primary"
@click="operationLog(row.id, null)"
>
操作日志
</ElButton>
<ElButton <ElButton
v-if="!row.expressSheet && row.status === 'TO_BE_CONFIRMED'" v-if="!row.expressSheet && row.status === 'TO_BE_CONFIRMED'"
link link
...@@ -758,11 +818,26 @@ ...@@ -758,11 +818,26 @@
确认 确认
</ElButton> </ElButton>
<ElButton <ElButton
v-if="
row.shipmentType === 1 &&
['CREATE_LOGISTICS', 'WAIT_SHIPMENT'].includes(status)
"
link link
type="primary" type="primary"
@click="operationLog(row.id, null)" @click="handleUpdateAddress(row)"
> >
操作日志 修改地址
</ElButton>
<ElButton
v-if="
row.shipmentType === 0 &&
['CREATE_LOGISTICS', 'WAIT_SHIPMENT'].includes(status)
"
link
type="primary"
@click="handleRefreshAddress(row)"
>
刷新地址
</ElButton> </ElButton>
</span> </span>
</div> </div>
...@@ -906,11 +981,18 @@ ...@@ -906,11 +981,18 @@
></ElPagination> ></ElPagination>
</div> </div>
</div> </div>
<right-menu <RightClickMenu
ref="rightMenuRef" ref="rightMenuRef"
:show-copy-shop-number="false" :show-copy-count="false"
@change="rightChange" :show-copy-sub-shop-number="false"
/> @on-change="rightChange"
>
<template #default
><div class="menu-item" @click="rightChange('order-number')">
复制订单号
</div>
</template>
</RightClickMenu>
<el-dialog <el-dialog
v-model="confirmDialogShow" v-model="confirmDialogShow"
:close-on-click-modal="false" :close-on-click-modal="false"
...@@ -1064,8 +1146,8 @@ ...@@ -1064,8 +1146,8 @@
:title="wayDialogTitle" :title="wayDialogTitle"
> >
<el-table <el-table
v-loading="isChangeWayLoading"
ref="changeWayRef" ref="changeWayRef"
v-loading="isChangeWayLoading"
height="400px" height="400px"
class="production-client-table" class="production-client-table"
:data="logisticsWayData" :data="logisticsWayData"
...@@ -1121,7 +1203,13 @@ ...@@ -1121,7 +1203,13 @@
</el-button> </el-button>
</template> </template>
</el-dialog> </el-dialog>
<UpdateAddress
v-if="updateAddVisible"
v-model:form="currentRow"
v-model:visible="updateAddVisible"
:country-list="countryList"
@success="search"
></UpdateAddress>
<ElDialog <ElDialog
v-model="exceptionDialogVisible" v-model="exceptionDialogVisible"
title="转为异常单" title="转为异常单"
...@@ -1210,14 +1298,19 @@ import { ...@@ -1210,14 +1298,19 @@ import {
getTrackingNumberApi, getTrackingNumberApi,
getfaceSimplexFileApi, getfaceSimplexFileApi,
cancelLogisticsOrderApi, cancelLogisticsOrderApi,
composingDesignImages,
changeLogisticsApi, changeLogisticsApi,
createLogisticsOrderApi, createLogisticsOrderApi,
updateLogisticsToPickingApi, updateLogisticsToPickingApi,
createLogisticsOrdersApi, createLogisticsOrdersApi,
syncReceiverAddress,
refreshAddressApi,
AddressInfo,
// handleExceptionOrderApi, // handleExceptionOrderApi,
} from '@/api/podUsOrder' } from '@/api/podUsOrder'
import UpdateAddress from './components/updateAddress.vue'
import { BaseRespData } from '@/types/api' import { BaseRespData } from '@/types/api'
import { getAllCountryApi } from '@/api/logistics.ts'
import TableView from '@/components/TableView.vue' import TableView from '@/components/TableView.vue'
import { import {
LogListData, LogListData,
...@@ -1234,14 +1327,14 @@ import { useValue } from '@/utils/hooks/useValue' ...@@ -1234,14 +1327,14 @@ import { useValue } from '@/utils/hooks/useValue'
import { showConfirm } from '@/utils/ui' import { showConfirm } from '@/utils/ui'
import { DocumentCopy, EditPen } from '@element-plus/icons-vue' import { DocumentCopy, EditPen } from '@element-plus/icons-vue'
import { Column, ElFormItem } from 'element-plus' import { Column, ElFormItem } from 'element-plus'
import { computed, onMounted, ref, nextTick } from 'vue' import { computed, onMounted, ref, nextTick, onBeforeUnmount } from 'vue'
import FastProduction from './FastProduction.vue' import FastProduction from './FastProduction.vue'
import { filePath } from '@/api/axios' import { filePath } from '@/api/axios'
import PodMakeOrder from './PodMakeOrder.vue' import PodMakeOrder from './PodMakeOrder.vue'
import { OrderData } from '@/types/api/podMakeOrder' import { OrderData } from '@/types/api/podMakeOrder'
import useLodop, { LODOPObject } from '@/utils/hooks/useLodop' import useLodop, { LODOPObject } from '@/utils/hooks/useLodop'
import dayjs from 'dayjs' import dayjs from 'dayjs'
import rightMenu from '../pod/rightMenu.vue' import RightClickMenu from '@/components/RightClickMenu.vue'
import ResultInfo from './components/ResultInfo.vue' import ResultInfo from './components/ResultInfo.vue'
import { isArray, isString } from '@/utils/validate' import { isArray, isString } from '@/utils/validate'
import platformJson from '../../../json/platform.json' import platformJson from '../../../json/platform.json'
...@@ -1260,8 +1353,10 @@ declare global { ...@@ -1260,8 +1353,10 @@ declare global {
} }
} }
const tabsNav = ref<Tab[]>() const tabsNav = ref<Tab[]>()
const updateAddVisible = ref(false)
const resultRefs = ref<InstanceType<typeof ResultInfo> | null>(null) const resultRefs = ref<InstanceType<typeof ResultInfo> | null>(null)
const confirmDialogShow = ref(false) const confirmDialogShow = ref(false)
const downloadLoading = ref(false)
const isChangeWay = ref(false) const isChangeWay = ref(false)
const confirmData = ref([]) const confirmData = ref([])
const logisticsWayData = ref([]) const logisticsWayData = ref([])
...@@ -1272,6 +1367,7 @@ const detailData = ref({}) ...@@ -1272,6 +1367,7 @@ const detailData = ref({})
const [searchForm] = useValue<SearchForm>({ const [searchForm] = useValue<SearchForm>({
timeType: null, timeType: null,
shopNumber: '', shopNumber: '',
shipmentType: '',
userMark: '', userMark: '',
logisticsTracking: '', logisticsTracking: '',
baseSku: '', baseSku: '',
...@@ -1286,6 +1382,18 @@ const [searchForm] = useValue<SearchForm>({ ...@@ -1286,6 +1382,18 @@ const [searchForm] = useValue<SearchForm>({
platform: '', platform: '',
productionClient: '', productionClient: '',
}) })
const countryList = ref([])
const currentRow = ref<AddressInfo>({
receiverName: '',
receiverPhone: '',
receiverCountry: '',
receiverProvince: '',
receiverCity: '',
receiverDistrict: '',
receiverAddress1: '',
receiverAddress2: '',
receiverPostCode: '',
})
const exceptionStatus = ref(1) const exceptionStatus = ref(1)
const userMarkList = ref<string[]>([]) const userMarkList = ref<string[]>([])
const selection = ref<PodUsOrderListData[]>([]) const selection = ref<PodUsOrderListData[]>([])
...@@ -1344,7 +1452,34 @@ const getDateRange = (days = 0, type: 'past' | 'future' = 'past') => { ...@@ -1344,7 +1452,34 @@ const getDateRange = (days = 0, type: 'past' | 'future' = 'past') => {
type === 'past' ? end.subtract(days, 'day') : end.add(days, 'day') type === 'past' ? end.subtract(days, 'day') : end.add(days, 'day')
return [start.startOf('day').toDate(), end.endOf('day').toDate()] return [start.startOf('day').toDate(), end.endOf('day').toDate()]
} }
const handleRefreshAddress = async (row: PodUsOrderListData) => {
try {
await showConfirm('确定刷新地址吗?', {
confirmButtonText: '确认',
cancelButtonText: '取消',
type: 'warning',
})
} catch {
return
}
await refreshAddressApi([row.id])
await ElMessageBox.alert(
'请修改/刷新地址后取消物流重新创建物流订单、获取跟踪号、获取打印面单。',
'提示',
{
type: 'warning',
confirmButtonText: '确定',
cancelButtonText: '取消',
showCancelButton: true,
},
)
}
const handleUpdateAddress = async (row: PodUsOrderListData) => {
const { data } = await getAllCountryApi()
countryList.value = data
currentRow.value = JSON.parse(JSON.stringify(row))
updateAddVisible.value = true
}
const getMonthRange = (months = 0, type: 'past' | 'future' = 'past') => { const getMonthRange = (months = 0, type: 'past' | 'future' = 'past') => {
const now = dayjs() const now = dayjs()
const start = const start =
...@@ -1392,6 +1527,12 @@ const tableColumns = computed(() => [ ...@@ -1392,6 +1527,12 @@ const tableColumns = computed(() => [
prop: 'innerLabel', prop: 'innerLabel',
}, },
{ {
label: '异常原因',
width: 300,
prop: 'exceptionReason',
slot: 'exceptionReason',
},
{
label: '操作', label: '操作',
slot: 'operate', slot: 'operate',
width: 180, width: 180,
...@@ -1405,8 +1546,6 @@ const rightClick = (e: MouseEvent) => { ...@@ -1405,8 +1546,6 @@ const rightClick = (e: MouseEvent) => {
rightMenuRef.value.setPosition({ rightMenuRef.value.setPosition({
x: e.clientX, x: e.clientX,
y: e.clientY, y: e.clientY,
cardItem: e.clientY,
el: e,
}) })
} }
...@@ -1417,6 +1556,44 @@ const handleSelectionChange = (val: PodUsOrderListData[]) => { ...@@ -1417,6 +1556,44 @@ const handleSelectionChange = (val: PodUsOrderListData[]) => {
stockOutSuccessIds.value = [] stockOutSuccessIds.value = []
} }
} }
const asyncOrderAddress = async () => {
if (selection.value.length === 0) {
return ElMessage.warning('请选择数据')
}
try {
await showConfirm('确定同步收货地址吗?', {
confirmButtonText: '确认',
cancelButtonText: '取消',
type: 'warning',
})
} catch {
return
}
const ids = selection.value.map((item) => item.id)
const loading = ElLoading.service({
fullscreen: true,
text: '操作中...',
background: 'rgba(0, 0, 0, 0.3)',
})
try {
const res = await syncReceiverAddress(ids)
if (res.code !== 200) return
await ElMessageBox.alert('同步收货地址成功,请刷新查看。', '提示', {
type: 'warning',
confirmButtonText: '确定',
cancelButtonText: '取消',
showCancelButton: true,
})
search()
await loadTabData()
} catch (e) {
console.error(e)
} finally {
loading.close()
}
}
const changeTab = (item: Tab) => { const changeTab = (item: Tab) => {
status.value = item.status || '' status.value = item.status || ''
selection.value = [] selection.value = []
...@@ -1592,6 +1769,21 @@ const confirmOrder = async () => { ...@@ -1592,6 +1769,21 @@ const confirmOrder = async () => {
productionClientValue.value = '' productionClientValue.value = ''
productionClientVisible.value = true productionClientVisible.value = true
} }
const downloadTif = async () => {
if (!selection.value.length) {
return ElMessage.warning('请选择数据')
}
downloadLoading.value = true
try {
const res = await composingDesignImages(selection.value.map((el) => el.id))
window.open(filePath + res.message, '_blank')
downloadLoading.value = false
} catch (e) {
downloadLoading.value = false
}
}
const loadProductionClient = async () => { const loadProductionClient = async () => {
try { try {
const res = await getProductionClientApi() const res = await getProductionClientApi()
...@@ -1871,13 +2063,28 @@ const isSelectStatused = (data: ProductList) => { ...@@ -1871,13 +2063,28 @@ const isSelectStatused = (data: ProductList) => {
return index !== -1 return index !== -1
} }
const rightChange = async (code: string) => { const rightChange = async (code: string) => {
if (code === 'check_all') { const flat = status.value !== 'IN_PRODUCTION'
cardSelection.value = JSON.parse(JSON.stringify(tableData.value)) if (code === 'select-all') {
} else if (code === 'clear_check') { if (flat) {
cardSelection.value = [] tableRef.value?.toggleAllSelection()
} else if (code === 'copy_code') { } else {
const str = cardSelection.value cardSelection.value = JSON.parse(JSON.stringify(tableData.value))
.map((item) => item.factorySubOrderNumber) }
} else if (code === 'cancel-select') {
if (flat) {
tableRef.value?.clearSelection()
} else {
cardSelection.value = []
}
} else if (code === 'copy_shopNumber') {
const str = (tableData.value as ProductList[] | PodUsOrderListData[])
.map((item) => item?.shopNumber)
.join()
navigator.clipboard.writeText(str)
ElMessage.success('复制成功')
} else if (code === 'order-number') {
const str = (tableData.value as ProductList[] | PodUsOrderListData[])
.map((item) => item?.factoryOrderNumber)
.join() .join()
navigator.clipboard.writeText(str) navigator.clipboard.writeText(str)
ElMessage.success('复制成功') ElMessage.success('复制成功')
...@@ -2024,7 +2231,7 @@ const getOrderByIdApi = async (type: string) => { ...@@ -2024,7 +2231,7 @@ const getOrderByIdApi = async (type: string) => {
const res = await operation.Fn(ids) const res = await operation.Fn(ids)
if (res.code === 200) { if (res.code === 200) {
if (isArray(res.data)) { if (isArray(res.data)) {
resultInfo.value = res.data resultInfo.value = res.data || []
resultRefs.value?.showDialog(type) resultRefs.value?.showDialog(type)
} else if (isString(res.data)) { } else if (isString(res.data)) {
window.open(filePath + res.data) window.open(filePath + res.data)
...@@ -2034,6 +2241,7 @@ const getOrderByIdApi = async (type: string) => { ...@@ -2034,6 +2241,7 @@ const getOrderByIdApi = async (type: string) => {
} }
loading.close() loading.close()
} catch (e) { } catch (e) {
resultInfo.value = []
console.error(e) console.error(e)
} }
} else { } else {
...@@ -2236,10 +2444,13 @@ const logisticsToPicking = async () => { ...@@ -2236,10 +2444,13 @@ const logisticsToPicking = async () => {
ids: selection.value.map((item) => item.id).join(','), ids: selection.value.map((item) => item.id).join(','),
}) })
if (res.code !== 200) return if (res.code !== 200) return
resultInfo.value = res.data || []
resultRefs.value?.showDialog()
ElMessage.success('操作成功') ElMessage.success('操作成功')
search() search()
loadTabData() loadTabData()
} catch (e) { } catch (e) {
resultInfo.value = []
console.error(e) console.error(e)
} finally { } finally {
loading.close() loading.close()
...@@ -2480,13 +2691,56 @@ const refreshMaterial = async () => { ...@@ -2480,13 +2691,56 @@ const refreshMaterial = async () => {
loading.close() loading.close()
} }
} }
// 当前时间响应式变量(每60秒更新一次)
const currentTime = ref(new Date())
// 定时器实例
let timer: number | null = null
onMounted(() => { onMounted(() => {
loadTabData() loadTabData()
getUserMark() getUserMark()
loadProductionClient() loadProductionClient()
loadWarehouseList() loadWarehouseList()
// 每60秒更新一次当前时间
timer = window.setInterval(() => {
currentTime.value = new Date()
}, 60000)
}) })
// 计算时间差(小时数)并向上取整
const calculateHoursDifference = (startDateString?: string): string => {
if (!startDateString) return '--'
try {
const startTime = new Date(startDateString)
const diffInMs = currentTime.value.getTime() - startTime.getTime()
// 转换为小时并取整:ceil向上或者round:四舍五入
const hours = Math.round(diffInMs / (1000 * 60 * 60))
return `${hours}小时`
} catch (error) {
console.error('日期解析错误:', error)
return '--'
}
}
// 根据订单状态计算延期时间
const calculateDelayTime = computed(() => (row: ProductList) => {
switch (status.value) {
case 'TO_BE_CONFIRMED':
return row.createTime ? calculateHoursDifference(row.createTime) : '--'
case 'STOCK_OUT':
return row.startStockingTime
? calculateHoursDifference(row.startStockingTime)
: '--'
default:
return '--'
}
})
onBeforeUnmount(() => {
// 组件卸载时清除定时器
if (timer) {
clearInterval(timer)
timer = null
}
})
const handleExceptionCommand = (command: number) => { const handleExceptionCommand = (command: number) => {
exceptionStatus.value = command exceptionStatus.value = command
search() search()
...@@ -2572,6 +2826,10 @@ const handleExceptionCommand = (command: number) => { ...@@ -2572,6 +2826,10 @@ const handleExceptionCommand = (command: number) => {
text-overflow: ellipsis; text-overflow: ellipsis;
white-space: nowrap; white-space: nowrap;
} }
.red-time {
color: red;
font-weight: bold;
}
} }
.card-list { .card-list {
...@@ -2700,6 +2958,13 @@ const handleExceptionCommand = (command: number) => { ...@@ -2700,6 +2958,13 @@ const handleExceptionCommand = (command: number) => {
} }
} }
} }
.operate-item {
.el-button {
margin-left: 0 !important;
margin-top: 20px !important;
}
}
</style> </style>
<style> <style>
.customize-select-style { .customize-select-style {
......
...@@ -400,10 +400,16 @@ ...@@ -400,10 +400,16 @@
:close-on-click-modal="false" :close-on-click-modal="false"
> >
<div class="import-dialog"> <div class="import-dialog">
<div class="import-header">
<el-button type="primary" link @click="downloadTemplate">
<el-icon><Download /></el-icon>
下载模板
</el-button>
</div>
<div class="import-content"> <div class="import-content">
<UploadExcel <UploadExcel
v-model="importedFileUrl" v-model="importedFileUrl"
:import-type="'local'" :import-type="'localAndXlsx'"
@imported="handleLocalImport" @imported="handleLocalImport"
/> />
</div> </div>
...@@ -791,7 +797,7 @@ ...@@ -791,7 +797,7 @@
<script setup lang="ts"> <script setup lang="ts">
import { ElMessage, ElRadioGroup, ElTree } from 'element-plus' import { ElMessage, ElRadioGroup, ElTree } from 'element-plus'
import { CirclePlusFilled } from '@element-plus/icons-vue' import { CirclePlusFilled,Download } from '@element-plus/icons-vue'
import splitDiv from '@/components/splitDiv/splitDiv.vue' import splitDiv from '@/components/splitDiv/splitDiv.vue'
import { ElTable } from 'element-plus' import { ElTable } from 'element-plus'
import usePageList from '@/utils/hooks/usePageList' import usePageList from '@/utils/hooks/usePageList'
...@@ -951,6 +957,14 @@ function getStartTime() { ...@@ -951,6 +957,14 @@ function getStartTime() {
const day = date.getDate() const day = date.getDate()
return `${year}-${month}-${day} 00:00:00` return `${year}-${month}-${day} 00:00:00`
} }
const downloadTemplate = () => {
const link = document.createElement('a')
link.href = '/files/outboundOrder.xlsx'
link.download = 'outboundOrder.xlsx'
document.body.appendChild(link)
link.click()
document.body.removeChild(link)
}
const selectSku = ref('') const selectSku = ref('')
const treeData = ref<InterWarehouseTree[]>() const treeData = ref<InterWarehouseTree[]>()
const [searchForm, resetSearchForm] = useValue<warehouseSearchForm>({}) const [searchForm, resetSearchForm] = useValue<warehouseSearchForm>({})
...@@ -1097,9 +1111,9 @@ const batchAddCommodity = async (sku: string): Promise<InterskuList[]> => { ...@@ -1097,9 +1111,9 @@ const batchAddCommodity = async (sku: string): Promise<InterskuList[]> => {
} }
} }
interface InterImportData { interface InterImportData {
warehouseSku: string; warehouseSku: string
remark?: string | null; remark?: string | null
buyStored?: string | number | null | object; // 扩大 buyStored 类型,以兼容原始数据 buyStored?: string | number | null | object // 扩大 buyStored 类型,以兼容原始数据
[propName: string]: string | number | boolean | undefined | unknown [propName: string]: string | number | boolean | undefined | unknown
} }
// 前端导入Excel // 前端导入Excel
...@@ -1122,91 +1136,108 @@ const handleLocalImport = async ({ ...@@ -1122,91 +1136,108 @@ const handleLocalImport = async ({
data: Record<string, any>[] data: Record<string, any>[]
}) => { }) => {
// 1. 将原始导入数据映射到 InterImportData[] // 1. 将原始导入数据映射到 InterImportData[]
const importedData: InterImportData[] = data.map(item => { const importedData: InterImportData[] = data
const obj: InterImportData = { warehouseSku: '' }; .map((item) => {
Object.keys(excelFieldMap).forEach(excelKey => { const obj: InterImportData = { warehouseSku: '' }
const field = excelFieldMap[excelKey]; Object.keys(excelFieldMap).forEach((excelKey) => {
const value = item[excelKey]; const field = excelFieldMap[excelKey]
const value = item[excelKey]
if (field === 'warehouseSku') { if (field === 'warehouseSku') {
obj[field] = typeof value === 'string' ? value : String(value ?? ''); obj[field] = typeof value === 'string' ? value : String(value ?? '')
} else if (field === 'remark') { } else if (field === 'remark') {
obj[field] = typeof value === 'string' ? value : (value === null || value === undefined ? null : String(value)); obj[field] =
typeof value === 'string'
? value
: value === null || value === undefined
? null
: String(value)
} else if (field === 'buyStored') { } else if (field === 'buyStored') {
// 处理 buyStored: 确保它是一个数字、数字字符串,否则设置为 null // 处理 buyStored: 确保它是一个数字、数字字符串,否则设置为 null
if (typeof value === 'number') { if (typeof value === 'number') {
obj[field] = String(value); // 将数字转换为字符串 obj[field] = String(value) // 将数字转换为字符串
} else if (typeof value === 'string' && !isNaN(Number(value))) { } else if (typeof value === 'string' && !isNaN(Number(value))) {
obj[field] = value; // 保持有效的数字字符串 obj[field] = value // 保持有效的数字字符串
} else { } else {
// 如果不是数字或有效的数字字符串,则设置为 null // 如果不是数字或有效的数字字符串,则设置为 null
obj[field] = null; obj[field] = null
} }
} else { } else {
obj[field] = value; obj[field] = value
} }
}); })
return obj; return obj
}).filter(item => item.warehouseSku); })
.filter((item) => item.warehouseSku)
if (importedData.length === 0) { if (importedData.length === 0) {
ElMessage.warning('导入数据中没有有效的商品SKU'); ElMessage.warning('导入数据中没有有效的商品SKU')
importDialogVisible.value = false; importDialogVisible.value = false
return; return
} }
// 2. 提取导入的 SKU 列表 // 2. 提取导入的 SKU 列表
const importedSkus = importedData.map(item => item.warehouseSku).join(','); const importedSkus = importedData.map((item) => item.warehouseSku).join(',')
// 3. 调用 batchAddCommodity 获取商品的完整信息并过滤掉已有的 SKU // 3. 调用 batchAddCommodity 获取商品的完整信息并过滤掉已有的 SKU
const filteredSkusList = await batchAddCommodity(importedSkus); const filteredSkusList = await batchAddCommodity(importedSkus)
if (filteredSkusList.length === 0) { if (filteredSkusList.length === 0) {
ElMessage.warning('导入的商品SKU已存在或无效'); ElMessage.warning('导入的商品SKU已存在或无效')
importedFileUrl.value = path; importedFileUrl.value = path
importDialogVisible.value = false; importDialogVisible.value = false
return; return
} }
// 4. 将备注信息合并到获取到的商品列表中 // 4. 将备注信息合并到获取到的商品列表中
const mergedProductList = filteredSkusList.map(skuItem => { const mergedProductList = filteredSkusList.map((skuItem) => {
const importedItem = importedData.find(item => item.warehouseSku === skuItem.warehouseSku); const importedItem = importedData.find(
(item) => item.warehouseSku === skuItem.warehouseSku,
let outCountValueForBigNumber: string | number = '0'; // 初始化为安全默认值 )
if (importedItem?.buyStored !== null && importedItem?.buyStored !== undefined) { let outCountValueForBigNumber: string | number = '0' // 初始化为安全默认值
if (typeof importedItem.buyStored === 'string' && !isNaN(Number(importedItem.buyStored))) {
outCountValueForBigNumber = importedItem.buyStored; if (
} else if (typeof importedItem.buyStored === 'number') { importedItem?.buyStored !== null &&
outCountValueForBigNumber = importedItem.buyStored; importedItem?.buyStored !== undefined
} else { ) {
// 如果是对象或其他意外类型,则默认为 '0' if (
outCountValueForBigNumber = '0'; typeof importedItem.buyStored === 'string' &&
} !isNaN(Number(importedItem.buyStored))
) {
outCountValueForBigNumber = importedItem.buyStored
} else if (typeof importedItem.buyStored === 'number') {
outCountValueForBigNumber = importedItem.buyStored
} else {
// 如果是对象或其他意外类型,则默认为 '0'
outCountValueForBigNumber = '0'
} }
}
const calculatedOutCount = new BigNumber(outCountValueForBigNumber).toNumber();
return { const calculatedOutCount = new BigNumber(
skuImage: skuItem.image, outCountValueForBigNumber,
warehouseSku: skuItem.warehouseSku, ).toNumber()
skuName: skuItem.skuName,
productNo: skuItem.productNumber,
locationCode: skuItem.locationCode ?? '',
locationId: skuItem.locationId ?? null,
costPrice: skuItem.price,
outCount: calculatedOutCount,
totalPrice: new BigNumber(calculatedOutCount).multipliedBy(skuItem.price ?? 0).toNumber(),
usableInventory: skuItem.usableInventory,
inventoryId: skuItem.id,
remark: importedItem?.remark ?? skuItem.remark ?? null,
} as InterProductList;
});
return {
skuImage: skuItem.image,
warehouseSku: skuItem.warehouseSku,
skuName: skuItem.skuName,
productNo: skuItem.productNumber,
locationCode: skuItem.locationCode ?? '',
locationId: skuItem.locationId ?? null,
costPrice: skuItem.price,
outCount: calculatedOutCount,
totalPrice: new BigNumber(calculatedOutCount)
.multipliedBy(skuItem.price ?? 0)
.toNumber(),
usableInventory: skuItem.usableInventory,
inventoryId: skuItem.id,
remark: importedItem?.remark ?? skuItem.remark ?? null,
} as InterProductList
})
// 5. 更新 otherPurchaseData // 5. 更新 otherPurchaseData
otherPurchaseData.value = [...otherPurchaseData.value, ...mergedProductList]; otherPurchaseData.value = [...otherPurchaseData.value, ...mergedProductList]
importedFileUrl.value = path importedFileUrl.value = path
importDialogVisible.value = false importDialogVisible.value = false
...@@ -1974,7 +2005,7 @@ $border: solid 1px #ddd; ...@@ -1974,7 +2005,7 @@ $border: solid 1px #ddd;
} }
.import-content { .import-content {
padding: 20px 0; padding-bottom: 20px;
} }
} }
......
...@@ -13,11 +13,57 @@ import { ...@@ -13,11 +13,57 @@ import {
factoryWarehouseInfoPrint, factoryWarehouseInfoPrint,
importWarehouseLocation, importWarehouseLocation,
} from '@/api/warehouse.ts' } from '@/api/warehouse.ts'
const uploadExcelRef = ref()
import { Download } from '@element-plus/icons-vue'
import { nextTick, ref } from 'vue' import { nextTick, ref } from 'vue'
import SplitDiv from '@/components/splitDiv/splitDiv.vue' import SplitDiv from '@/components/splitDiv/splitDiv.vue'
import { filePath } from '@/api/axios.ts' import { filePath } from '@/api/axios.ts'
import UploadExcel from '@/components/UploadExcel.vue'
const importLoading = ref(false) const importDialogVisible = ref(false)
const importedFileUrl = ref('')
const importLocation = async () => {
importDialogVisible.value = true
importedFileUrl.value = ''
nextTick(() => {
uploadExcelRef.value?.resetUpload()
})
}
const handleLocalImport = async ({
path,
data,
}: {
path: string
data: Blob
}) => {
try {
const formData = new FormData()
formData.append('importExcel', data)
const res = await importWarehouseLocation(formData)
if (res.code == 200) {
importedFileUrl.value = path
ElMessage.success('导入成功')
await getData()
} else {
importedFileUrl.value = ''
ElMessage.error(res.message || '导入失败')
}
} catch (error) {
importedFileUrl.value = ''
ElMessage.error('导入失败')
} finally {
importDialogVisible.value = false
importedFileUrl.value = ''
}
}
const downloadTemplate = () => {
const link = document.createElement('a')
link.href = '/files/warehouseLocationTemplate.xlsx'
link.download = 'warehouseLocationTemplate.xlsx'
document.body.appendChild(link)
link.click()
document.body.removeChild(link)
}
// const importLoading = ref(false)
const searchForm = ref({ const searchForm = ref({
warehouseId: '', warehouseId: '',
locationCode: '', locationCode: '',
...@@ -60,9 +106,7 @@ const rules = { ...@@ -60,9 +106,7 @@ const rules = {
pickingOrder: [ pickingOrder: [
{ required: true, message: '请输入拣货顺序', trigger: 'blur' }, { required: true, message: '请输入拣货顺序', trigger: 'blur' },
], ],
warehouseId: [ warehouseId: [{ required: true, message: '请选择仓库', trigger: 'change' }],
{ required: true, message: '请选择仓库', trigger: 'change' },
],
} }
const leftData = ref<positionInfo[]>([]) const leftData = ref<positionInfo[]>([])
const pagination = ref<factoryWarehouseInfo>({ const pagination = ref<factoryWarehouseInfo>({
...@@ -129,41 +173,43 @@ const handleBatchDelete = async (row: positionInfo | null) => { ...@@ -129,41 +173,43 @@ const handleBatchDelete = async (row: positionInfo | null) => {
cancelButtonText: '取消', cancelButtonText: '取消',
type: 'warning', type: 'warning',
}) })
const str = row && row.id ? row.id?.toString() : selections.value.map((el) => el.id).join(',') const str =
row && row.id
? row.id?.toString()
: selections.value.map((el) => el.id).join(',')
await deleteLocationApi(str) await deleteLocationApi(str)
ElMessage.success('删除成功') ElMessage.success('删除成功')
await getData() await getData()
} }
const importLocation = async () => { // const importLocation = async () => {
const i = document.createElement('input') // const i = document.createElement('input')
i.type = 'file' // i.type = 'file'
i.style.display = 'none' // i.style.display = 'none'
i.accept = '.xlsx,.xls' // i.accept = '.xlsx,.xls'
i.click() // i.click()
i.onchange = async () => {
if (i && i.files) {
importLoading.value = true
try {
console.log(i.files[0])
const formData = new FormData()
formData.append('importExcel', i.files[0])
await importWarehouseLocation(formData)
ElMessage.success('导入成功')
await getData()
} finally {
importLoading.value = false
}
} // i.onchange = async () => {
} // if (i && i.files) {
} // importLoading.value = true
// try {
// console.log(i.files[0])
// // const formData = new FormData()
// // formData.append('importExcel', i.files[0])
// // await importWarehouseLocation(formData)
// // ElMessage.success('导入成功')
// // await getData()
// } finally {
// importLoading.value = false
// }
// }
// }
// }
const createWarehouse = () => { const createWarehouse = () => {
createData.value.show = true createData.value.show = true
createData.value.isEdit = false createData.value.isEdit = false
createData.value.title = '新增库位' createData.value.title = '新增库位'
const warehouse = warehouseList.value.find(w => w.defaulted === 1) const warehouse = warehouseList.value.find((w) => w.defaulted === 1)
form.value = { form.value = {
locationName: '', locationName: '',
locationCode: '', locationCode: '',
...@@ -207,7 +253,7 @@ const getWarehouse = async () => { ...@@ -207,7 +253,7 @@ const getWarehouse = async () => {
warehouseList.value = data warehouseList.value = data
} }
const warehouseChange = (v: number) => { const warehouseChange = (v: number) => {
const warehouse = warehouseList.value.find(w => w.id == v) const warehouse = warehouseList.value.find((w) => w.id == v)
form.value.warehouseName = warehouse ? warehouse.name : '' form.value.warehouseName = warehouse ? warehouse.name : ''
} }
getWarehouse() getWarehouse()
...@@ -219,37 +265,75 @@ getWarehouse() ...@@ -219,37 +265,75 @@ getWarehouse()
<el-card> <el-card>
<el-form inline :model="searchForm"> <el-form inline :model="searchForm">
<el-form-item label="仓库"> <el-form-item label="仓库">
<el-select v-model="searchForm.warehouseId" placeholder="请选择仓库" clearable filterable <el-select
style="width: 150px;"> v-model="searchForm.warehouseId"
<el-option v-for="item in warehouseList" :key="item.id" :label="item.name" :value="item.id"></el-option> placeholder="请选择仓库"
clearable
filterable
style="width: 150px"
>
<el-option
v-for="item in warehouseList"
:key="item.id"
:label="item.name"
:value="item.id"
></el-option>
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item label="库位名称"> <el-form-item label="库位名称">
<el-input <el-input
v-model="searchForm.locationName" style="width: 140px;" placeholder="请输入库位名称" v-model="searchForm.locationName"
clearable></el-input> style="width: 140px"
placeholder="请输入库位名称"
clearable
></el-input>
</el-form-item> </el-form-item>
<el-form-item label="库位编码"> <el-form-item label="库位编码">
<el-input <el-input
v-model="searchForm.locationCode" style="width: 140px;" placeholder="请输入库位编码" v-model="searchForm.locationCode"
clearable></el-input> style="width: 140px"
placeholder="请输入库位编码"
clearable
></el-input>
</el-form-item> </el-form-item>
<el-form-item label="备注"> <el-form-item label="备注">
<el-input <el-input
v-model="searchForm.remark" style="width: 140px;" placeholder="请输入备注" v-model="searchForm.remark"
clearable></el-input> style="width: 140px"
placeholder="请输入备注"
clearable
></el-input>
</el-form-item> </el-form-item>
<el-form-item label="状态"> <el-form-item label="状态">
<el-select v-model="searchForm.status" placeholder="请选择状态" clearable filterable style="width: 150px;"> <el-select
<el-option v-for="(item,index) in ['禁用','启用']" :key="index" :label="item" :value="index"></el-option> v-model="searchForm.status"
placeholder="请选择状态"
clearable
filterable
style="width: 150px"
>
<el-option
v-for="(item, index) in ['禁用', '启用']"
:key="index"
:label="item"
:value="index"
></el-option>
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item> <el-form-item>
<el-button type="primary" @click="getData">查询</el-button> <el-button type="primary" @click="getData">查询</el-button>
<el-button type="success" @click="createWarehouse">新增库位</el-button> <el-button type="success" @click="createWarehouse"
<el-button :loading="importLoading" type="primary" @click="importLocation">导入库位</el-button> >新增库位</el-button
<el-button type="primary" @click="printLocationTag">打印库位标签</el-button> >
<el-button type="danger" @click="handleBatchDelete(null)">删除</el-button> <el-button type="primary" @click="importLocation"
>导入库位</el-button
>
<el-button type="primary" @click="printLocationTag"
>打印库位标签</el-button
>
<el-button type="danger" @click="handleBatchDelete(null)"
>删除</el-button
>
</el-form-item> </el-form-item>
</el-form> </el-form>
</el-card> </el-card>
...@@ -257,30 +341,66 @@ getWarehouse() ...@@ -257,30 +341,66 @@ getWarehouse()
<template #bottom> <template #bottom>
<el-card style="height: 100%"> <el-card style="height: 100%">
<div class="manage"> <div class="manage">
<div class="table-flex"> <div class="table-flex">
<div class="left-table"> <div class="left-table">
<div class="table-container"> <div class="table-container">
<el-table height="100%" :data="leftData" border @selection-change="handleSelectionChange"> <el-table
height="100%"
:data="leftData"
border
@selection-change="handleSelectionChange"
>
<el-table-column type="selection" /> <el-table-column type="selection" />
<el-table-column type="index" label="序号" width="60" /> <el-table-column type="index" label="序号" width="60" />
<el-table-column width="100" label="启用状态" prop="status" align="center"> <el-table-column
<template #default="{row}"> width="100"
label="启用状态"
prop="status"
align="center"
>
<template #default="{ row }">
<el-switch <el-switch
v-model="row.status" :active-value="1" :inactive-value="0" v-model="row.status"
@click="updateStatus(row)"></el-switch> :active-value="1"
:inactive-value="0"
@click="updateStatus(row)"
></el-switch>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column align="center" label="所属仓库" prop="warehouseName"></el-table-column> <el-table-column
<el-table-column align="center" label="库位名称" prop="locationName"></el-table-column> align="center"
<el-table-column align="center" label="库位编码" prop="locationCode"></el-table-column> label="所属仓库"
<el-table-column align="center" label="拣货顺序" prop="pickingOrder"></el-table-column> prop="warehouseName"
<el-table-column align="center" label="备注" prop="remark"></el-table-column> ></el-table-column>
<el-table-column
align="center"
label="库位名称"
prop="locationName"
></el-table-column>
<el-table-column
align="center"
label="库位编码"
prop="locationCode"
></el-table-column>
<el-table-column
align="center"
label="拣货顺序"
prop="pickingOrder"
></el-table-column>
<el-table-column
align="center"
label="备注"
prop="remark"
></el-table-column>
<el-table-column label="操作" align="center"> <el-table-column label="操作" align="center">
<template #default="{row}"> <template #default="{ row }">
<el-button type="primary" @click="updateWarehouse(row)">编辑</el-button> <el-button type="primary" @click="updateWarehouse(row)"
<el-button type="danger" @click="handleBatchDelete(row)">删除</el-button> >编辑</el-button
>
<el-button type="danger" @click="handleBatchDelete(row)"
>删除</el-button
>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
...@@ -296,40 +416,68 @@ getWarehouse() ...@@ -296,40 +416,68 @@ getWarehouse()
@current-change="handleCurrentChange" @current-change="handleCurrentChange"
/> />
</div> </div>
</div> </div>
</div> </div>
<el-dialog v-model="createData.show" width="500px" :title="createData.title"> <el-dialog
<el-form ref="formRef" label-width="110px" :rules="rules" :model="form"> v-model="createData.show"
width="500px"
:title="createData.title"
>
<el-form
ref="formRef"
label-width="110px"
:rules="rules"
:model="form"
>
<el-form-item label="库位名称" prop="locationName"> <el-form-item label="库位名称" prop="locationName">
<el-input v-model="form.locationName" clearable placeholder="请输入库位名称"></el-input> <el-input
v-model="form.locationName"
clearable
placeholder="请输入库位名称"
></el-input>
</el-form-item> </el-form-item>
<el-form-item label="库位编码" prop="locationCode"> <el-form-item label="库位编码" prop="locationCode">
<el-input v-model="form.locationCode" clearable placeholder="请输入库位编码(例如:A1-01-03)"></el-input> <el-input
v-model="form.locationCode"
clearable
placeholder="请输入库位编码(例如:A1-01-03)"
></el-input>
</el-form-item> </el-form-item>
<el-form-item label="拣货顺序" prop="pickingOrder"> <el-form-item label="拣货顺序" prop="pickingOrder">
<el-input-number v-model="form.pickingOrder" placeholder="拣货顺序"></el-input-number> <el-input-number
v-model="form.pickingOrder"
placeholder="拣货顺序"
></el-input-number>
</el-form-item> </el-form-item>
<el-form-item label="所属仓库" prop="warehouseId"> <el-form-item label="所属仓库" prop="warehouseId">
<el-select v-model="form.warehouseId" @change="warehouseChange"> <el-select v-model="form.warehouseId" @change="warehouseChange">
<el-option <el-option
v-for="item in warehouseList" :key="item.id" :label="item.name" v-for="item in warehouseList"
:value="item.id"></el-option> :key="item.id"
:label="item.name"
:value="item.id"
></el-option>
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item label="备注" prop="remark"> <el-form-item label="备注" prop="remark">
<el-input <el-input
v-model="form.remark" type="textarea" :rows="4" clearable v-model="form.remark"
placeholder="请输入备注"></el-input> type="textarea"
:rows="4"
clearable
placeholder="请输入备注"
></el-input>
</el-form-item> </el-form-item>
<el-form-item label="状态" prop="status"> <el-form-item label="状态" prop="status">
<el-switch <el-switch
v-model="form.status" :active-value="1" :inactive-value="0" v-model="form.status"
:active-value="1"
:inactive-value="0"
></el-switch> ></el-switch>
</el-form-item> </el-form-item>
</el-form> </el-form>
<template #footer> <template #footer>
<el-button @click="createData.show=false">取消</el-button> <el-button @click="createData.show = false">取消</el-button>
<el-button type="primary" @click="handleConfirm">确定</el-button> <el-button type="primary" @click="handleConfirm">确定</el-button>
</template> </template>
</el-dialog> </el-dialog>
...@@ -337,6 +485,29 @@ getWarehouse() ...@@ -337,6 +485,29 @@ getWarehouse()
</el-card> </el-card>
</template> </template>
</split-div> </split-div>
<ElDialog
v-model="importDialogVisible"
title="导入"
width="500px"
:close-on-click-modal="false"
>
<div class="import-dialog">
<div class="import-header">
<el-button type="primary" link @click="downloadTemplate">
<el-icon><Download /></el-icon>
下载模板
</el-button>
</div>
<div class="import-content">
<UploadExcel
ref="uploadExcelRef"
v-model="importedFileUrl"
:import-type="'localAndUpload'"
@imported="handleLocalImport"
/>
</div>
</div>
</ElDialog>
</template> </template>
<style scoped lang="scss"> <style scoped lang="scss">
...@@ -345,7 +516,16 @@ getWarehouse() ...@@ -345,7 +516,16 @@ getWarehouse()
height: 100%; height: 100%;
} }
} }
.import-dialog {
.import-header {
display: flex;
justify-content: flex-end;
}
.import-content {
padding-bottom: 20px;
}
}
.manage { .manage {
height: 100%; height: 100%;
display: flex; display: flex;
......
...@@ -432,16 +432,16 @@ ...@@ -432,16 +432,16 @@
:close-on-click-modal="false" :close-on-click-modal="false"
> >
<div class="import-dialog"> <div class="import-dialog">
<!-- <div class="import-header"> <div class="import-header">
<el-button type="primary" link @click="downloadTemplate"> <el-button type="primary" link @click="downloadTemplate">
<el-icon><Download /></el-icon> <el-icon><Download /></el-icon>
下载模板 下载模板
</el-button> </el-button>
</div> --> </div>
<div class="import-content"> <div class="import-content">
<UploadExcel <UploadExcel
v-model="importedFileUrl" v-model="importedFileUrl"
:import-type="'local'" :import-type="'localAndXlsx'"
@imported="handleLocalImport" @imported="handleLocalImport"
/> />
</div> </div>
...@@ -827,7 +827,7 @@ ...@@ -827,7 +827,7 @@
<script setup lang="ts"> <script setup lang="ts">
import { ElMessage, ElRadioGroup, ElTree } from 'element-plus' import { ElMessage, ElRadioGroup, ElTree } from 'element-plus'
import { CirclePlusFilled } from '@element-plus/icons-vue' import { CirclePlusFilled,Download } from '@element-plus/icons-vue'
import splitDiv from '@/components/splitDiv/splitDiv.vue' import splitDiv from '@/components/splitDiv/splitDiv.vue'
import { ElTable } from 'element-plus' import { ElTable } from 'element-plus'
import usePageList from '@/utils/hooks/usePageList' import usePageList from '@/utils/hooks/usePageList'
...@@ -986,6 +986,14 @@ function getStartTime() { ...@@ -986,6 +986,14 @@ function getStartTime() {
const day = date.getDate() const day = date.getDate()
return `${year}-${month}-${day} 00:00:00` return `${year}-${month}-${day} 00:00:00`
} }
const downloadTemplate = () => {
const link = document.createElement('a')
link.href = '/files/warehousingEntry.xlsx'
link.download = 'warehousingEntry.xlsx'
document.body.appendChild(link)
link.click()
document.body.removeChild(link)
}
const selectSku = ref('') const selectSku = ref('')
const treeData = ref<InterWarehouseTree[]>() const treeData = ref<InterWarehouseTree[]>()
const [searchForm, resetSearchForm] = useValue<warehouseSearchForm>({}) const [searchForm, resetSearchForm] = useValue<warehouseSearchForm>({})
...@@ -1936,7 +1944,7 @@ $border: solid 1px #ddd; ...@@ -1936,7 +1944,7 @@ $border: solid 1px #ddd;
} }
.import-content { .import-content {
padding: 20px 0; padding-bottom: 20px;
} }
} }
......
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