Commit 2d4870d1 by qinjianhui

Merge branch 'dev' into 'master'

Dev

See merge request !101
parents 02099e7c a82400be
...@@ -23,6 +23,14 @@ export function updateRuleByIdApi(data: PostData) { ...@@ -23,6 +23,14 @@ export function updateRuleByIdApi(data: PostData) {
) )
} }
// 规则开关
export function ruleSwitchApi(data: { id: number; status: string }) {
return axios.post<never, BasePaginationData<never>>(
'factory/podJomallOrderUsArrangeRule/ruleSwitch',
data,
)
}
// us规则列表 // us规则列表
export function getByFactoryNoApi(data: { factoryNo: number }) { export function getByFactoryNoApi(data: { factoryNo: number }) {
return axios.get<never, BaseRespData<never>>( return axios.get<never, BaseRespData<never>>(
......
...@@ -10,6 +10,9 @@ import { ...@@ -10,6 +10,9 @@ import {
AnyObject, AnyObject,
InterProductList, InterProductList,
ExportInWarehouseInfo, ExportInWarehouseInfo,
stockingPlanSearchForm,
InterStackingPlanDetail,
InterWarehouseBase
} from '@/types/api/warehouse' } from '@/types/api/warehouse'
export interface LogListData { export interface LogListData {
createTime: string createTime: string
...@@ -596,3 +599,121 @@ export function deleteWarehouseOutRecordApi(ids: string) { ...@@ -596,3 +599,121 @@ export function deleteWarehouseOutRecordApi(ids: string) {
}, },
) )
} }
// 备货计划 树状图
export function getStackingPlanStatusTree() {
return axios.get<never, BaseRespData<InterWarehouseTree[]>>(
'factoryStockingPlanRecord/status_tree',
)
}
// 备货计划 列表
export function getStackingPlanListPage(data: stockingPlanSearchForm, currentPage: number,
pageSize: number,) {
return axios.post<never, BasePaginationData<InterWarehousePage>>(
'factoryStockingPlanRecord/list_page',
{
...data,
currentPage,
pageSize,
},
)
}
// 备货计划 新增
export function addStackingPlanApi(data: InterWarehouseDetail) {
return axios.post<never, BaseRespData<never>>(
'factoryStockingPlanRecord/add',
data,
)
}
// 备货计划 编辑
export function updateStackingPlanApi(data: InterStackingPlanDetail) {
return axios.post<never, BaseRespData<never>>(
'factoryStockingPlanRecord/update',
data,
)
}
// 备货计划 详情
export function getStackingPlanDetailApi(id: number | undefined) {
return axios.get<never, BaseRespData<InterStackingPlanDetail>>(
'factoryStockingPlanRecord/get',
{
params: {
id,
},
},
)
}
// 备货计划 日志
export function getStackingPlanLogApi(stockingPlanRecordId?: number) {
return axios.get<never, BaseRespData<LogListData[]>>(
`factoryStockingPlanRecord/log/${stockingPlanRecordId}`,
)
}
// 备货计划 驳回
export function rejectStackingPlanApi({
list,
rejectReason,
status,
}: {
list: WarehouseParams[]
rejectReason: string
status: string
}) {
return axios.post<never, BaseRespData<never>>(
'factoryStockingPlanRecord/reject',
{
list,
rejectReason,
status,
},
)
}
// 备货计划 删除
export function deleteStackingPlanApi(ids: string) {
return axios.get<never, BaseRespData<never>>(
'factoryStockingPlanRecord/delete',
{
params: { ids },
},
)
}
// 备货计划 更新物流单号
export function updateShipmentNumberApi(data: InterWarehouseBase[]) {
return axios.post<never, BaseRespData<never>>(
'factoryStockingPlanRecord/update_shipmentNumber',
{ stockingPlanRecordParamList: data },
)
}
// 备货计划 导出
export function stackingPlanRecordExport(data: ExportInWarehouseInfo) {
return axios.post<never, BasePaginationData<never>>(
'factoryStockingPlanRecord/export',
data,
{
responseType: 'blob',
},
)
}
// 备货计划 盘点
export function scanBoxCodeApi(boxCode: string) {
return axios.get<never, BaseRespData<never>>(
'factoryStockingPlanRecord/scan_box_code',
{
params: { boxCode },
},
)
}
// 备货计划 打印箱贴
export function printBarcodeApi(data: { id: number, dataVersion: number }[]) {
return axios.post<never, BaseRespData<never[]>>(
'factoryStockingPlanRecord/print_barcode',
data,
)
}
\ No newline at end of file
...@@ -4,6 +4,7 @@ import { ...@@ -4,6 +4,7 @@ import {
RouteLocationNormalized, RouteLocationNormalized,
NavigationGuardNext, NavigationGuardNext,
} from 'vue-router' } from 'vue-router'
import { cleanupImagePreview } from '@/utils/hooks/useImagePreview.'
import Login from '@/views/Login.vue' import Login from '@/views/Login.vue'
import Reset from '@/views/Reset.vue' import Reset from '@/views/Reset.vue'
...@@ -31,6 +32,7 @@ import receiptDoc from '@/views/warehouse/receiptDoc.vue' ...@@ -31,6 +32,7 @@ import receiptDoc from '@/views/warehouse/receiptDoc.vue'
import issueDoc from '@/views/warehouse/issueDoc.vue' import issueDoc from '@/views/warehouse/issueDoc.vue'
import ExternalAuthorisationPage from '@/views/system/externalAuthorisationPage.vue' import ExternalAuthorisationPage from '@/views/system/externalAuthorisationPage.vue'
import CustomersPage from '@/views/system/CustomersPage.vue' import CustomersPage from '@/views/system/CustomersPage.vue'
import stockingPlan from '@/views/warehouse/stockingPlan.vue'
const router = createRouter({ const router = createRouter({
history: createWebHistory(), history: createWebHistory(),
routes: [ routes: [
...@@ -129,7 +131,7 @@ const router = createRouter({ ...@@ -129,7 +131,7 @@ const router = createRouter({
meta: { meta: {
title: '下载生产客户端', title: '下载生产客户端',
}, },
component: () => {}, component: () => { },
beforeEnter() { beforeEnter() {
// 假设你的下载链接是这个 // 假设你的下载链接是这个
const downloadLink = '/exeFiles/JomallProductionAssistantSetup.exe' const downloadLink = '/exeFiles/JomallProductionAssistantSetup.exe'
...@@ -280,6 +282,13 @@ const router = createRouter({ ...@@ -280,6 +282,13 @@ const router = createRouter({
component: issueDoc, component: issueDoc,
}, },
{ {
path: '/warehouse/stocking-plan',
meta: {
title: '备货计划',
},
component: stockingPlan,
},
{
path: '/warehouse/warning', path: '/warehouse/warning',
meta: { meta: {
title: '仓库预警', title: '仓库预警',
...@@ -333,6 +342,8 @@ router.beforeEach( ...@@ -333,6 +342,8 @@ router.beforeEach(
_from: RouteLocationNormalized, _from: RouteLocationNormalized,
next: NavigationGuardNext, next: NavigationGuardNext,
) => { ) => {
// 路由切换时清理图片预览,防止预览元素残留
cleanupImagePreview()
const token = getToken() const token = getToken()
if (to.query.factoryCode) { if (to.query.factoryCode) {
localStorage.setItem('factory_code', String(to.query.factoryCode)) localStorage.setItem('factory_code', String(to.query.factoryCode))
......
...@@ -88,7 +88,6 @@ const menu: MenuItem[] = [ ...@@ -88,7 +88,6 @@ const menu: MenuItem[] = [
id: 124, id: 124,
label: '出库单', label: '出库单',
}, },
{ {
index: '/warehouse/manage', index: '/warehouse/manage',
id: 121, id: 121,
...@@ -99,6 +98,11 @@ const menu: MenuItem[] = [ ...@@ -99,6 +98,11 @@ const menu: MenuItem[] = [
id: 122, id: 122,
label: '库位管理', label: '库位管理',
}, },
{
index: '/warehouse/stocking-plan',
id: 124,
label: '备货计划',
},
], ],
}, },
......
...@@ -12,6 +12,7 @@ export interface warehouseSearchForm { ...@@ -12,6 +12,7 @@ export interface warehouseSearchForm {
warehouseSku?: string warehouseSku?: string
warehouseId?: number | string warehouseId?: number | string
id?: number id?: number
remark?: string
} }
export interface InterProductList { export interface InterProductList {
createTime?: string createTime?: string
...@@ -21,6 +22,7 @@ export interface InterProductList { ...@@ -21,6 +22,7 @@ export interface InterProductList {
productNo?: string | null //custom的货号 productNo?: string | null //custom的货号
buyStored?: number | null //入库数量 buyStored?: number | null //入库数量
outCount?: number | null //出库数量 outCount?: number | null //出库数量
stockUpStored?: number | null //备货数量
remark?: string | null remark?: string | null
skuImage?: string skuImage?: string
skuName?: string skuName?: string
...@@ -53,9 +55,10 @@ export interface InterWarehouseBase { ...@@ -53,9 +55,10 @@ export interface InterWarehouseBase {
rejectReason?: string | null rejectReason?: string | null
number?: number | string | null number?: number | string | null
supplierItemNo?: string | null supplierItemNo?: string | null
sourceOn?: string | null //备货单号or入库单号
} }
// 主表列表ts // 主表列表ts
export interface InterWarehousePage extends InterWarehouseBase {} export interface InterWarehousePage extends InterWarehouseBase { }
// 子表列表ts // 子表列表ts
export interface InterWarehouseDetail extends InterWarehouseBase { export interface InterWarehouseDetail extends InterWarehouseBase {
productList: InterProductList[] productList: InterProductList[]
...@@ -80,6 +83,7 @@ export interface InterProductList { ...@@ -80,6 +83,7 @@ export interface InterProductList {
productNo?: string | null //货号 productNo?: string | null //货号
buyStored?: number | null //入库数量 buyStored?: number | null //入库数量
outCount?: number | null //出库数量 outCount?: number | null //出库数量
stockUpStored?: number | null //备货数量
usableInventory?: number | null //可用库存数量 usableInventory?: number | null //可用库存数量
remark?: string | null remark?: string | null
skuImage?: string skuImage?: string
...@@ -117,3 +121,21 @@ export interface ILocation { ...@@ -117,3 +121,21 @@ export interface ILocation {
locationId?: number | null locationId?: number | null
locationCode?: string | null locationCode?: string | null
} }
// 备货计划 查询
export interface stockingPlanSearchForm extends InterWarehouseBase {
timeType?: string
startDate?: string
endDate?: string
dateStr?: string
warehouseSku?: string
}
// 备货计划
export interface InterStackingPlanDetail extends InterWarehouseDetail {
boxSum?: number
checkBoxSum?: number
box?: number | string
}
import { ref } from 'vue' import { ref, onUnmounted } from 'vue'
export default function useImagePreview() { // 单例模式:所有实例共享同一个预览元素
const show = ref(false) let previewDiv: HTMLDivElement | null = null
const div = document.createElement('div') let previewImg: HTMLImageElement | null = null
div.style.position = 'fixed' const show = ref(false)
div.style.zIndex = '9999999999999' let currentTarget: HTMLElement | null = null
div.style.display = 'none' let mousemoveHandler: ((ev: MouseEvent) => void) | null = null
const img = document.createElement('img')
img.style.width = '300px' // 初始化预览元素(只创建一次)
div.appendChild(img) function initPreviewElement() {
document.body.appendChild(div) if (previewDiv) return
previewDiv = document.createElement('div')
previewDiv.style.position = 'fixed'
previewDiv.style.zIndex = '9999999999999'
previewDiv.style.display = 'none'
previewImg = document.createElement('img')
previewImg.style.width = '300px'
previewDiv.appendChild(previewImg)
document.body.appendChild(previewDiv)
}
// 清理函数:移除事件监听器和重置状态
function cleanup() {
if (mousemoveHandler) {
window.removeEventListener('mousemove', mousemoveHandler)
mousemoveHandler = null
}
if (previewDiv) {
previewDiv.style.display = 'none'
}
show.value = false
currentTarget = null
}
let currentTarget: HTMLElement | null = null // 导出清理函数,供路由切换时使用
export function cleanupImagePreview() {
cleanup()
}
export default function useImagePreview() {
// 确保预览元素已初始化
initPreviewElement()
const mousemoveHandler = (ev: MouseEvent) => { // 创建 mousemove 处理器(如果还没有)
if (!currentTarget) return if (!mousemoveHandler) {
mousemoveHandler = (ev: MouseEvent) => {
if (!currentTarget || !previewDiv) {
cleanup()
return
}
const rect = currentTarget.getBoundingClientRect() const rect = currentTarget.getBoundingClientRect()
const inOrigin = const inOrigin =
ev.clientX >= rect.left && ev.clientX >= rect.left &&
...@@ -22,7 +57,7 @@ export default function useImagePreview() { ...@@ -22,7 +57,7 @@ export default function useImagePreview() {
ev.clientY >= rect.top && ev.clientY >= rect.top &&
ev.clientY <= rect.bottom ev.clientY <= rect.bottom
const divRect = div.getBoundingClientRect() const divRect = previewDiv.getBoundingClientRect()
const inPreview = const inPreview =
ev.clientX >= divRect.left && ev.clientX >= divRect.left &&
ev.clientX <= divRect.right && ev.clientX <= divRect.right &&
...@@ -30,10 +65,8 @@ export default function useImagePreview() { ...@@ -30,10 +65,8 @@ export default function useImagePreview() {
ev.clientY <= divRect.bottom ev.clientY <= divRect.bottom
if (!inOrigin && !inPreview) { if (!inOrigin && !inPreview) {
div.style.display = 'none' cleanup()
show.value = false }
window.removeEventListener('mousemove', mousemoveHandler)
currentTarget = null
} }
} }
...@@ -44,12 +77,13 @@ export default function useImagePreview() { ...@@ -44,12 +77,13 @@ export default function useImagePreview() {
newBorder?: boolean, newBorder?: boolean,
positionBOOTTOM?: boolean, positionBOOTTOM?: boolean,
) => { ) => {
if (!previewDiv || !previewImg) return
ev.preventDefault() ev.preventDefault()
if (show.value === true) return if (show.value === true) return
img.src = url previewImg.src = url
img.style.backgroundColor = '#eee' previewImg.style.backgroundColor = '#eee'
if (newBorder) img.style.border = '1px solid #eee' if (newBorder) previewImg.style.border = '1px solid #eee'
if (newWitdh) img.style.width = newWitdh if (newWitdh) previewImg.style.width = newWitdh
const cW = document.body.clientWidth const cW = document.body.clientWidth
const cH = document.body.clientHeight const cH = document.body.clientHeight
const cX = ev.clientX const cX = ev.clientX
...@@ -59,14 +93,15 @@ export default function useImagePreview() { ...@@ -59,14 +93,15 @@ export default function useImagePreview() {
else y = cY - 150 else y = cY - 150
if (cX + 300 >= cW) x = cX - 300 if (cX + 300 >= cW) x = cX - 300
else x = cX + 60 else x = cX + 60
img.onload = () => { previewImg.onload = () => {
div.style.left = x + 'px' if (!previewDiv || !previewImg) return
div.style.top = y + 'px' previewDiv.style.left = x + 'px'
div.style.display = 'block' previewDiv.style.top = y + 'px'
previewDiv.style.display = 'block'
if (positionBOOTTOM) { if (positionBOOTTOM) {
// 获取图片实际尺寸 // 获取图片实际尺寸
const imgWidth = img.clientWidth const imgWidth = previewImg.clientWidth
const imgHeight = img.clientHeight const imgHeight = previewImg.clientHeight
// 计算新位置(鼠标正下方) // 计算新位置(鼠标正下方)
let x = cX / 2 let x = cX / 2
...@@ -85,20 +120,26 @@ export default function useImagePreview() { ...@@ -85,20 +120,26 @@ export default function useImagePreview() {
if (y < 0) { if (y < 0) {
y = 10 y = 10
} }
div.style.left = x + 'px' previewDiv.style.left = x + 'px'
div.style.top = y + 'px' previewDiv.style.top = y + 'px'
} }
show.value = true show.value = true
currentTarget = ev.currentTarget as HTMLElement currentTarget = ev.currentTarget as HTMLElement
if (mousemoveHandler) {
window.addEventListener('mousemove', mousemoveHandler) window.addEventListener('mousemove', mousemoveHandler)
} }
} }
}
const mouseleaveImg = () => { const mouseleaveImg = () => {
if (show.value === false) return // 在 mouseleave 时也要清理事件监听器
div.style.display = 'none' cleanup()
show.value = false
} }
// 组件卸载时清理(防止内存泄漏)
onUnmounted(() => {
cleanup()
})
return { mouseoverImg, mouseleaveImg } return { mouseoverImg, mouseleaveImg }
} }
...@@ -355,6 +355,14 @@ const formConfig = computed<IFormConfig[]>(() => [ ...@@ -355,6 +355,14 @@ const formConfig = computed<IFormConfig[]>(() => [
type: 'switch', type: 'switch',
label: '是否默认', label: '是否默认',
}, },
{
prop: 'creditCode',
type: 'input',
label: '社会信用代码',
attrs: {
placeholder: '请输入社会信用代码',
},
},
]) ])
async function getCountryList() { async function getCountryList() {
......
...@@ -17,8 +17,24 @@ ...@@ -17,8 +17,24 @@
<div class="box">批次数量:{{ item.arrangeMax }}</div> <div class="box">批次数量:{{ item.arrangeMax }}</div>
<div class="box">每天{{ getTime(item) }}开始自动排单</div> <div class="box">每天{{ getTime(item) }}开始自动排单</div>
</div> </div>
<div class="action"> <div class="action">
<div style="display: flex; align-items: center; gap: 2px">
<div>状态:</div>
<el-switch
v-model="item.status"
active-value="ACTIVE"
inactive-value="INACTIVE"
active-text="开"
inactive-text="关"
inline-prompt
class="ml-2"
style="
--el-switch-on-color: #42b983;
--el-switch-off-color: #f56c6c;
"
@change="(val:string) => ruleSwitch(item.id, val)"
/>
</div>
<el-icon <el-icon
size="24" size="24"
title="编辑" title="编辑"
...@@ -180,6 +196,22 @@ ...@@ -180,6 +196,22 @@
" "
/> />
</div> </div>
<div style="display: flex; align-items: center">
<div style="color: #606266; margin-right: 10px">状态:</div>
<el-switch
v-model="editForm.status"
active-value="ACTIVE"
inactive-value="INACTIVE"
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 v-if="editForm.isAuto">
<div label="排版类型:"> <div label="排版类型:">
<el-radio-group v-model="editForm.type"> <el-radio-group v-model="editForm.type">
...@@ -215,6 +247,7 @@ import { ...@@ -215,6 +247,7 @@ import {
usArrangeRuleApi, usArrangeRuleApi,
getByFactoryNoApi, getByFactoryNoApi,
updateRuleByIdApi, updateRuleByIdApi,
ruleSwitchApi,
} from '@/api/podUsSchedulingRules.ts' } from '@/api/podUsSchedulingRules.ts'
import { Plus, Minus } from '@element-plus/icons-vue' import { Plus, Minus } from '@element-plus/icons-vue'
...@@ -223,7 +256,7 @@ import CustomizeForm from '@/components/CustomizeForm.tsx' ...@@ -223,7 +256,7 @@ import CustomizeForm from '@/components/CustomizeForm.tsx'
import { Edit } from '@element-plus/icons-vue' import { Edit } from '@element-plus/icons-vue'
import { debounce } from 'lodash-es' import { debounce } from 'lodash-es'
const editForm = ref<BaseForm>({ isAuto: false }) const editForm = ref<BaseForm>({ isAuto: false, status: 'ACTIVE' })
const tableData = ref([]) const tableData = ref([])
import userUserStore from '@/store/user' import userUserStore from '@/store/user'
const userStore = userUserStore() const userStore = userUserStore()
...@@ -311,6 +344,7 @@ interface BaseForm { ...@@ -311,6 +344,7 @@ interface BaseForm {
hour?: string | number hour?: string | number
minute?: string | number minute?: string | number
second?: string | number second?: string | number
status?: string
} }
type ParamKey = `param${number}` type ParamKey = `param${number}`
type ParamValue = string | number | boolean | null | undefined type ParamValue = string | number | boolean | null | undefined
...@@ -433,6 +467,26 @@ const changeSwitch = () => { ...@@ -433,6 +467,26 @@ const changeSwitch = () => {
editForm.value.templateWidth = undefined editForm.value.templateWidth = undefined
} }
const ruleSwitch = async (id: number | undefined, status: string) => {
if (!id) {
return
}
try {
const res = await ruleSwitchApi({
id,
status,
})
ElMessage({
message: res.message,
type: 'success',
offset: window.innerHeight / 2,
})
getList()
} catch (e) {
console.error(e)
}
}
const padTimeUnit = (value: string | number): string => { const padTimeUnit = (value: string | number): string => {
const num = Number(value) const num = Number(value)
// 若转换后是 NaN(无效值),或原始值不存在(null/undefined/''),均返回 '00' // 若转换后是 NaN(无效值),或原始值不存在(null/undefined/''),均返回 '00'
...@@ -513,6 +567,9 @@ const getTime = (item: BaseForm) => { ...@@ -513,6 +567,9 @@ const getTime = (item: BaseForm) => {
} }
.action { .action {
display: flex;
align-items: center;
gap: 100px;
.iconView { .iconView {
} }
} }
......
...@@ -87,6 +87,14 @@ ...@@ -87,6 +87,14 @@
style="width: 160px" style="width: 160px"
/> />
</ElFormItem> </ElFormItem>
<ElFormItem style="margin-right: 10px" label="备注">
<ElInput
v-model.trim="searchForm.remark"
clearable
placeholder="请输入备注"
style="width: 160px"
/>
</ElFormItem>
<ElFormItem> <ElFormItem>
<ElButton type="primary" @click="search" ref="searchBtnRef" <ElButton type="primary" @click="search" ref="searchBtnRef"
>查询</ElButton >查询</ElButton
...@@ -244,6 +252,20 @@ ...@@ -244,6 +252,20 @@
align="center" align="center"
></ElTableColumn> ></ElTableColumn>
<ElTableColumn <ElTableColumn
label="创建时间"
show-overflow-tooltip
prop="createTime"
header-align="center"
align="center"
></ElTableColumn>
<ElTableColumn
label="审核时间"
show-overflow-tooltip
prop="auditTime"
header-align="center"
align="center"
></ElTableColumn>
<ElTableColumn
label="备注" label="备注"
show-overflow-tooltip show-overflow-tooltip
prop="remark" prop="remark"
......
...@@ -184,6 +184,14 @@ ...@@ -184,6 +184,14 @@
align="center" align="center"
></ElTableColumn> ></ElTableColumn>
<ElTableColumn <ElTableColumn
label="备货单号"
show-overflow-tooltip
prop="sourceOn"
width="130"
header-align="center"
align="center"
></ElTableColumn>
<ElTableColumn
label="单据状态" label="单据状态"
width="130" width="130"
prop="billStatusTxt" prop="billStatusTxt"
......
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