Commit 4a56b804 by wuqian

Merge branch 'zzq_bill_order' of http://47.122.114.111:9999/qinjianhui/factory_front into wq

parents 7b08addd 92e8663c
......@@ -9,8 +9,10 @@ declare module 'vue' {
export interface GlobalComponents {
CommonCard: typeof import('./src/components/CommonCard.vue')['default']
ElButton: typeof import('element-plus/es')['ElButton']
ElCard: typeof import('element-plus/es')['ElCard']
ElCarousel: typeof import('element-plus/es')['ElCarousel']
ElCarouselItem: typeof import('element-plus/es')['ElCarouselItem']
ElCascader: typeof import('element-plus/es')['ElCascader']
ElCheckbox: typeof import('element-plus/es')['ElCheckbox']
ElCol: typeof import('element-plus/es')['ElCol']
ElConfigProvider: typeof import('element-plus/es')['ElConfigProvider']
......@@ -24,7 +26,9 @@ declare module 'vue' {
ElForm: typeof import('element-plus/es')['ElForm']
ElFormItem: typeof import('element-plus/es')['ElFormItem']
ElIcon: typeof import('element-plus/es')['ElIcon']
ElImage: typeof import('element-plus/es')['ElImage']
ElInput: typeof import('element-plus/es')['ElInput']
ElInputNumber: typeof import('element-plus/es')['ElInputNumber']
ElMenu: typeof import('element-plus/es')['ElMenu']
ElMenuItem: typeof import('element-plus/es')['ElMenuItem']
ElOption: typeof import('element-plus/es')['ElOption']
......
import axios from '@/api/axios.ts'
import { BasePaginationData, BaseRespData } from '@/types/api'
import {
warehouseSearchForm,
InterWarehousePage,
InterWarehouseDetail,
InterWarehouseTree,
InterskuList,
InterWarehouseList,
ILocation,
InRecordEditForm
} from '@/types/api/warehouse'
export interface factoryWarehouseInfo {
pageSize: number
currentPage: number
total?: number
}
export interface UpdateDefaulted {
id?: number
defaulted: number
}
export interface warehouseInfo {
id?: number
name: string
code: string
sort: string
defaulted: number
factoryId?: number
factoryCode?: string
remarks: string
}
interface WarehouseWarningData {
id: number
factoryId: number
warehouseId: number
warehouseName: string
warehouseLocationId: number
locationName: string
warehouseSku: string
skuName: string
unit: string
occupyInventory: number
freezeInventory: number
inventory: number
upperLimit: number
floorLimit: number
productNumber: string
itemSort: number
dataVersion: number
status: number
createTime: string
updateTime: string
}
export function getFactoryWarehouseInfo(data: factoryWarehouseInfo) {
return axios.post<never, BasePaginationData<warehouseInfo>>(
'factoryWarehouseInfo/list_page',
data,
)
}
export function createWarehouseApi(data: warehouseInfo) {
return axios.post<never, BaseRespData<never>>(
'/factoryWarehouseInfo/add',
data,
)
}
export function createWarehouseInventoryApi(data: WarehouseWarningData) {
return axios.post<never, BaseRespData<never>>(
'/factoryWarehouseInventory/add',
data,
)
}
export function updateWarehouseApi(data: warehouseInfo | UpdateDefaulted) {
return axios.post<never, BaseRespData<never>>(
'/factoryWarehouseInfo/update',
data,
)
}
export function updateWarehouseInventoryApi(data: WarehouseWarningData) {
return axios.post<never, BaseRespData<never>>(
'/factoryWarehouseInventory/update',
data,
)
}
export function deleteWarehouseApi(ids: string) {
return axios.get<never, BaseRespData<never>>('/factoryWarehouseInfo/delete', {
params: { ids },
})
}
export function deleteWarehouseInventory(ids: string) {
return axios.get<never, BaseRespData<never>>(
'/factoryWarehouseInventory/delete',
{
params: { ids },
},
)
}
// 入库单
export function warehouseInRecordListPage(
data: warehouseSearchForm,
currentPage: number,
pageSize: number,
) {
return axios.post<never, BasePaginationData<InterWarehousePage>>(
'/factory/warehouseInRecord/list_page',
{
...data,
currentPage,
pageSize,
},
)
}
export function addInRecordApi(form: InRecordEditForm) {
return axios.post<never, BaseRespData<never>>('factory/warehouseInRecord/add', {
...form,
})
}
export function updateInRecordApi(form: InRecordEditForm) {
return axios.post<never, BaseRespData<never>>('factory/warehouseInRecord/update', {
...form,
})
}
export function getWarehouseInRecordDetail(id: number) {
return axios.get<never, BaseRespData<never>>(
'factory/warehouseInRecord/get',
{
params: {
id,
},
},
)
}
export function getBySku(warehouseId: number, sku: string | null) {
return axios.get<never, BaseRespData<InterskuList[]>>(
'customProductItem/getBySku',
{
params: {
warehouseId,
sku,
},
},
)
}
export function getByWareHouseIdAndCode(wareHouseId: number, code: string | null) {
return axios.get<never, BaseRespData<ILocation[]>>(
'factoryWarehouseLocation/getByWareHouseIdAndCode',
{
params: {
wareHouseId,
code,
},
},
)
}
export function getWarehouseStatusTree() {
return axios.get<never, BaseRespData<InterWarehouseTree[]>>(
'factory/warehouseInRecord/status_tree',
)
}
export function getWarehouseListApi() {
return axios.get<never, BaseRespData<InterWarehouseList[]>>(
'factoryWarehouseInfo/getAll',
)
}
<template>
<div v-loading="loading" class="enhanced-upload">
<div class="import-content">
<div>
<div class="upload-area" @click="triggerFileInput">
<el-icon class="el-icon--upload"><UploadFilled /></el-icon>
<div class="el-upload__text">
将文件拖到此处,或<span style="color: #409eff">点击上传</span>
</div>
</div>
<input
ref="fileInputRef"
type="file"
accept=".xls,.xlsx"
style="display: none"
@change="onFileChange"
/>
<div class="custom-tip">
<span>请上传Excel文件(.xls 或 .xlsx)</span>
</div>
</div>
<div v-if="fileList.length > 0">
<div
v-for="(item, idx) in fileList"
:key="idx"
class="custom-file-item"
>
<el-icon class="file-icon"><Document /></el-icon>
<div class="file-info">
<div
class="file-name"
:title="`点击下载 ${item.path} 文件`"
@click="downloadFile(item.path)"
>
{{ item?.filename || SetFileName(item.path) }}
</div>
</div>
<el-icon class="delete-icon" @click="removeFile(idx)"
><Close
/></el-icon>
</div>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { ref, watch, defineProps, defineEmits, computed } from 'vue'
import { UploadFilled, Document, Close } from '@element-plus/icons-vue'
import { ElMessage } from 'element-plus'
import { uploadFileApi } from '@/api/common'
interface FileItem {
path: string
filename?: string
}
const props = defineProps<{
modelValue: string
}>()
const emit = defineEmits(['update:modelValue'])
const fileList = ref<FileItem[]>([])
const fileInputRef = ref<HTMLInputElement | null>(null)
const loading = ref(false)
const value = computed({
get() {
return props.modelValue
},
set(val: string) {
emit('update:modelValue', val)
},
})
watch(
value,
() => {
let file: FileItem[] = []
if (!Array.isArray(value)) {
if (value.value) {
file.push({ path: value.value })
} else {
file = []
}
}
fileList.value = file
},
{ immediate: true },
)
const triggerFileInput = () => {
if (loading.value) return
fileInputRef.value?.click()
}
const downloadFile = (path: string) => {
window.open(path)
}
const SetFileName = (path: string) => {
if (!path) return ''
const i = path.lastIndexOf('/')
if (i === -1) return path
return path.substring(i + 1)
}
const onFileChange = async (e: Event) => {
const files = (e.target as HTMLInputElement).files
if (!files) return
const file = files[0]
// 校验类型
const isValidType = [
'application/vnd.ms-excel',
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
].includes(file.type)
if (!isValidType) {
ElMessage.error('文件类型错误,请上传Excel文件')
return
}
// 校验大小
const isSizeValid = file.size / 1024 / 1024 < 50
if (!isSizeValid) {
ElMessage.error('文件大小不能超过50MB')
return
}
await fileUpload(file)
// 清空input
if (fileInputRef.value) fileInputRef.value.value = ''
}
const fileUpload = async (file: File) => {
const formData = new FormData()
const filename = file.name
formData.append('file', file)
formData.append('businessType', 'product')
loading.value = true
try {
const res = await uploadFileApi(formData)
fileList.value = [{ path: res.message ?? '', filename }]
value.value = res.message
ElMessage.success('导入成功')
} catch (e) {
ElMessage.error('上传失败')
} finally {
loading.value = false
}
}
const removeFile = (idx: number) => {
fileList.value.splice(idx, 1)
value.value = ''
}
</script>
<style scoped lang="scss">
.enhanced-upload {
.import-content {
padding: 20px 0;
}
.custom-file-item {
display: flex;
align-items: center;
padding: 12px;
border: 1px solid #e8e8e8;
border-radius: 4px;
margin-top: 8px;
background: #fff;
.file-icon {
font-size: 24px;
color: #52c41a;
margin-right: 12px;
}
.file-info {
flex-grow: 1;
.file-name {
color: #333;
margin-bottom: 4px;
}
.file-name:hover {
text-decoration: underline;
color: blue;
}
}
.delete-icon {
color: #f56c6c;
font-size: 20px;
cursor: pointer;
margin-left: 10px;
&:hover {
color: #ff0000;
}
}
}
.upload-area {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
border: 1px dashed #d9d9d9;
border-radius: 6px;
padding: 40px 20px;
// background: #fafafa;
background: var(--el-fill-color-blank);
cursor: pointer;
&:hover {
border-color: var(--el-color-primary);
}
.el-icon--upload {
font-size: 70px;
color: #a8abb2;
margin-bottom: 12px;
}
.el-upload__text {
color: #666;
}
}
.custom-tip {
display: flex;
align-items: center;
color: #666;
margin-top: 12px;
i {
margin-right: 6px;
}
}
}
</style>
......@@ -86,10 +86,10 @@ const onChange = async (e: Event) => {
const files = (e.target as HTMLInputElement).files
if (!files) return
const file = files[0]
const isLtTenM = file.size / 1024 / 1024 < 200;
const isLtTenM = file.size / 1024 / 1024 < 200
if (!isLtTenM) {
ElMessage.error("上传文件大小不能超过 200MB!");
return isLtTenM;
ElMessage.error('上传文件大小不能超过 200MB!')
return isLtTenM
}
if (props.imageMode) {
imageUpload(file)
......
......@@ -16,12 +16,17 @@ import { getToken } from '@/api/axios'
import UserPage from '@/views/UserPage.vue'
import DeliveryNotePage from '@/views/DeliveryNotePage.vue'
import AccountStatementNote from '@/views/AccountStatementNote.vue'
import Product from '@/views/product/index.vue'
import PodBillOrder from '@/views/podBillOrder/index.vue'
import TypeseetingManagement from '@/views/typesetting/TypesettingManagement.vue'
import PodOrderList from '@/views/order/pod/index.vue'
import PodDeliveryNoteList from '@/views/order/pod/deliveryOrderList.vue'
import PodUsOrderList from '@/views/order/podUs/index.vue'
import WarehouseManage from '@/views/warehouse/manage.vue'
import WarehouseWarning from '@/views/warehouse/warning.vue'
import receiptDoc from '@/views/warehouse/receiptDoc.vue'
import issueDoc from '@/views/warehouse/issueDoc.vue'
const router = createRouter({
history: createWebHistory(),
routes: [
......@@ -38,6 +43,13 @@ const router = createRouter({
component: Dashboard,
},
{
path: '/product',
meta: {
title: '商品',
},
component: Product,
},
{
path: '/order/list',
meta: {
title: '定制订单',
......@@ -92,7 +104,8 @@ const router = createRouter({
title: '定制对账单',
},
component: AccountStatementNote,
},{
},
{
path: '/account/pod-bill-order',
meta: {
title: 'POD对账单',
......@@ -106,6 +119,34 @@ const router = createRouter({
},
component: TypeseetingManagement,
},
{
path: '/warehouse/manage',
meta: {
title: '仓库管理',
},
component: WarehouseManage,
},
{
path: '/warehouse/receipt-doc',
meta: {
title: '入库单',
},
component: receiptDoc,
},
{
path: '/warehouse/issue-doc',
meta: {
title: '出库单',
},
component: issueDoc,
},
{
path: '/warehouse/warning',
meta: {
title: '仓库预警',
},
component: WarehouseWarning,
},
],
},
// 登录
......
......@@ -12,6 +12,43 @@ const menu: MenuItem[] = [
label: '概览',
},
{
index: '/product',
id: 11,
label: '商品',
},
{
index: '13',
id: 13,
label: '库存',
children: [
{
index: '/warehouse/manage',
id: 121,
label: '仓库管理',
},
{
index: '/order/list',
id: 122,
label: '库位管理',
},
{
index: '/warehouse/receipt-doc',
id: 123,
label: '入库单',
},
{
index: '/warehouse/issue-doc',
id: 124,
label: '出库单',
},
{
index: '/warehouse/warning',
id: 125,
label: '仓库预警',
},
],
},
{
index: '1',
id: 2,
label: '订单',
......
export interface warehouseSearchForm {
status?: string
batchNumber?: string
createTimeEnd?: string
createTimeStart?: string
orderNumber?: string
sku?: string
warehouseId?: number
}
// 主表列表ts,看新增时是不是一样
export interface InterWarehousePage {
id: number
factoryId?: number
factoryCode?: string
warehouseId?: number
warehouseName?: string
inNo?: string
shipmentNumber?: string
skuAmount?: string
total?: number
totalPrice?: number
billStatus?: string
remark?: string
productList?: InterProductList[]
}
export interface InterWarehouseDetail {
id?: string
inId?: string
productNo?: string
warehouseSku?: string
skuImage?: string
skuName?: string
buyStored?: string
totalPrice?: number
rejectsAmount?: string
rejectsReason?: string
remark?: string
factoryId?: number
createTime?: string
updateTime?: string
}
export interface InterWarehouseTree {
id: number
code: string
name: string
countQuantity: string | null
children: null
leaf: boolean
}
export interface InterProductList {
createTime?: string
factoryId?: number
id?: number
inId?: number
productNo?: string | null //货号
buyStored?: number | null //入库数量
rejectsAmount?: number
rejectsReason?: string
remark?: string
skuImage?: string
skuName?: string
costPrice?: number | null
totalPrice?: number | null
updateTime?: string
warehouseSku?: string
locationId?: number | null
locationCode?: string | null
}
export interface InterskuList {
id?: number
productId?: number
sku?: string
skuName?: string
image?: string
factoryPrice?: number
productNo?: string | null //货号
locationId?: number | null
locationCode?: string | null
}
export interface InterWarehouseList {
id: number
name: string
code?: string
sort?: number
defaulted?: number
factoryId?: number
factoryCode?: string
remarks?: string | null
}
export interface ILocation {
id?: number
locationId?: number | null
locationCode?: string | null
}
export interface InRecordEditForm {
id: number
factoryId?: number
factoryCode?: string
warehouseId?: number
warehouseName?: string
inNo?: string
shipmentNumber?: string
skuAmount?: string
total?: number
totalPrice?: number
billStatus?: string
remark?: string
productList?: InterProductList[]
}
......@@ -321,7 +321,7 @@
: ''
"
class="item-image"
@click="handleChangeImages(item, cardItem)"
@click.stop="handleChangeImages(item, cardItem)"
>
<img :src="item?.image" height="28" />
</div>
......
<template>
<div class="h100">
<div class="wraper" @mouseout="closeShow">
<div class="image-box" :title="data.name">
<el-image
style="width: 100%; height: 100%"
:src="imageUrl"
fit="cover"
/>
<div
v-if="isShowHomeSku === 'bottom'"
class="app-btn-bottom"
style="width: 100%"
>
<transition>
<div
class="more_wrap"
style="max-width: 100%; overflow-x: auto; overflow-y: hidden"
>
<slot name="operations" />
</div>
</transition>
</div>
</div>
<div class="content">
<div class="colors_main">
<div class="colors_wrap">
<ul class="colors">
<li v-for="(val, index) in data.colorImageList" :key="index">
<img
style="display: block"
width="30"
loading="lazy"
height="30"
:src="val"
alt=""
@mouseenter.stop="setCurrentImg(val)"
/>
</li>
</ul>
</div>
</div>
<!-- <slot name="skuid" v-if="$slots.skuid"></slot> -->
<slot name="content">
<div
style="
display: flex;
align-items: center;
position: relative;
/* height: 24px; */
"
>
<slot name="product-name"></slot>
<slot name="price" />
<slot name="synthesizeStatus"></slot>
<slot name="level-time"></slot>
</div>
</slot>
<slot name="otherContent"></slot>
</div>
<slot name="extra" />
</div>
</div>
</template>
<script lang="ts" setup>
import {computed} from 'vue'
const props = defineProps({
// 控制一些元素显示与否
isStore: {
type: Boolean,
},
width: {
type: Number,
default: 300,
},
hasWidth: {
type: Boolean,
default: false,
},
isFormat: {
type: Boolean,
default: false,
},
smallWidth: {
type: Number,
default: 45,
},
isShowTag: {
type: Boolean,
default: false,
},
isShowHomeSku: {
type: [String, Boolean],
validator: (v) => v === "top" || v === "bottom" || !v,
},
isSkuImageList: {
type: Boolean,
},
isShowSkuId: {
type: Boolean,
default: true,
},
isShowTitle: {
type: Boolean,
default: false,
},
isShowLevel: {
type: Boolean,
default: true,
},
isShowStatus: {
type: Boolean,
default: true,
},
isShowSku: {
type: Boolean,
default: true,
},
isShowSales: {
type: Boolean,
default: true,
},
showSelectable: {
type: Boolean,
default: true,
},
openly: {
type: Number,
default: 1,
},
showOpenly: {
type: Boolean,
default: false,
},
proportion: {
type: Number,
default: 125,
},
data: {
type: Object,
default: () => ({ colorImageList: [] }),
},
selectIds: {
type: Array,
default: () => [],
},
activeFn: {
type: Function,
default: () => {},
},
index: {
type: Number,
default: null,
},
searchForm: {
type: Object,
default: () => {},
},
operational: {
type: Boolean,
default: true,
},
customTagList: {
type: Array,
default: () => [],
},
isCustom: {
type: Boolean,
default: () => false,
},
});
const emits = defineEmits(["setCurrentImg"]);
const imageUrl = computed(() => {
return props.data.img_url;
});
const setCurrentImg = (val: string) => {
if (!val) return;
emits("setCurrentImg", val);
};
const closeShow = () => {
if (!props.data.colorImageList.length) return;
emits("setCurrentImg", props.data.colorImageList[0]);
};
// edit(item) {
// this.$emit('showEdit', item)
// }
</script>
<style lang="scss" scoped>
.wraper {
position: relative;
border-radius: 12px 0px 0px 0px;
border: 1px solid #efefef;
background: #eeeeee;
overflow: hidden;
}
.wraper:hover {
border: 1px solid #4168ff;
}
.addtag {
display: none;
}
.wraper:hover .addtag {
display: block;
}
.sales_wrap {
padding-top: 5px;
height: 24px;
display: flex;
align-items: center;
}
.sales_wrap svg {
margin-bottom: 5px;
}
.sales {
flex: 1;
height: 24px;
margin-left: 5px;
line-height: 24px;
box-sizing: border-box;
background-position: left center;
background-size: contain;
background-repeat: no-repeat;
display: inline-block;
font-size: 14px;
vertical-align: bottom;
padding-top: 1px;
font-weight: bold;
text-align: center;
}
.high {
color: red;
font-weight: bold;
color: #444444;
background: linear-gradient(0deg, rgb(255, 255, 255) 0%, #dddddd 100%);
border-radius: 4px 4px 0 0;
position: relative;
}
.high::before {
position: absolute;
top: -11px;
right: 0;
left: 0;
text-align: center;
line-height: 20px;
font-size: 12px;
font-weight: normal;
}
.today.high::before {
content: "today";
}
.seven.high::before {
content: "seven";
}
.thirty.high::before {
content: "thirty";
}
.history.high::before {
content: "history";
}
.active-icon {
position: relative;
display: block;
width: 16px;
height: 16px;
border: 1px solid #cccccc;
box-shadow: 0px 2px 3px 0px rgba(0, 0, 0, 0.4) inset;
border-radius: 8px;
background: #fff;
}
.active-icon.active {
background: #4168ff;
position: relative;
border-color: #4168ff;
box-shadow: none;
font-size: 18px;
top: -1px;
left: -1px;
}
.active-icon.active::after {
position: absolute;
content: "";
top: 0px;
left: 4px;
width: 4px;
height: 8px;
border-width: 2px;
border-style: solid;
border-color: transparent #fff #fff transparent;
transform: rotate(45deg) scale(0.8);
}
.colors_main {
height: 40px;
display: flex;
align-items: flex-start;
justify-content: space-between;
}
.colors_wrap {
height: 40px;
flex: 1;
overflow-x: auto;
overflow-y: hidden;
}
.colors {
white-space: nowrap;
display: flex;
}
.colors_wrap::-webkit-scrollbar {
height: 5px;
width: 5px;
}
.colors li {
border-right: 1px solid #fff;
width: 30px;
min-width: 30px;
height: 30px;
background: #ddd;
background-size: cover;
background-repeat: no-repeat;
background-position: center;
cursor: pointer;
margin-right: 5px;
border-radius: 5px;
overflow: hidden;
border: 1px solid #ddd;
box-sizing: border-box;
}
.colors li img {
object-fit: contain;
object-position: center;
}
.star {
display: inline-block;
width: 80px;
color: red;
padding-left: 2px;
}
.star span {
display: inline-block;
height: 14px;
width: 14px;
margin-right: 2px;
background-position: center;
background-size: contain;
// background-image: url('../../assets/icon/star-no.svg');
}
// .star span.active {
// background-image: url('../../assets/icon/star.svg');
// }
// .moneyall {
// background-image: url('../../assets/images/moneyall.png');
// }
// .moneyone {
// background-image: url('../../assets/images/moneyone.png');
// }
// .rmb {
// background-image: url('../../assets/images/rmb.png');
// }
.version {
display: block;
width: 18px;
height: 18px;
line-height: 14px;
font-size: 12px;
color: #fff;
padding: 0 0 3px 2px;
text-align: center;
box-sizing: border-box;
// background: no-repeat url('../../assets/images/supply/no-version.png');
}
.repeat {
display: inline-block;
height: 24px;
width: 24px;
border-radius: 12px;
font-size: 12px;
background: red;
line-height: 22px;
color: #fff;
text-align: center;
}
// .version.main {
// background-image: url('../../assets/images/supply/version.png');
// }
// .dollar {
// background-image: url('../../assets/images/supply/money.png');
// }
// .eidt_title {
// background-image: url('../../assets/images/edit001.png');
// }
// .edit_online {
// background-image: url('../../assets/images/plane.png');
// }
.scroll {
overflow: auto;
padding-top: 10px;
user-select: none;
border: 1px solid #ddd;
}
.empty.elements {
border: none;
}
.select-number {
position: absolute;
left: 5px;
top: 10px;
}
.left_mark {
position: absolute;
top: 5px;
left: 5px;
}
.right_check {
position: absolute;
top: 5px;
right: 5px;
display: flex;
}
.right_check > * {
display: block;
margin-bottom: 5px;
}
.app-btn-top {
position: relative;
height: 30px;
}
.app-btn-top > .more_wrap {
position: absolute;
right: 5px;
}
.image-box {
position: relative;
padding-top: 100%;
width: 100%;
.el-image {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
.app-btn-bottom {
position: absolute;
bottom: 5px;
left: 0;
}
}
.log_btn {
height: 22px;
display: inline-block;
background: #ffffff;
margin-right: 5px;
padding: 0 8px;
font-size: 12px;
vertical-align: middle;
box-shadow: 0px 2px 2px 0px rgba(0, 0, 0, 0.2);
border-radius: 13px;
text-align: center;
cursor: pointer;
}
.edit {
vertical-align: middle;
}
.content {
padding: 5px 10px;
background: linear-gradient(
90deg,
rgba(228, 228, 228, 0.4) 0%,
rgba(255, 255, 255, 1) 100%
);
}
.wraper:hover .content {
background: linear-gradient(
90deg,
rgba(26, 125, 255, 0.404) 0%,
rgba(64, 158, 255, 0) 100%
);
}
.footer_wrap {
display: flex;
align-items: center;
padding: 5px 0px 0;
}
.more_wrap {
display: none;
height: 35px;
/* //display: flex; */
align-items: center;
}
.wraper:hover .more_wrap {
display: flex;
}
.more_wrap :deep(> img) {
margin-left: 6px;
cursor: pointer;
}
.more_wrap :deep(> img:hover) {
transform: scale(1.3);
}
.price {
color: rgba(181, 124, 91, 1);
font-size: 14px;
font-weight: bold;
}
.new {
display: block;
width: 43px;
height: 16px;
line-height: 14px;
background: #8fc31f;
text-align: center;
border-radius: 8px;
font-size: 12px;
font-family: Alibaba PuHuiTi;
font-weight: 400;
color: #ffffff;
}
.bottom-home-sku {
cursor: pointer;
display: flex;
align-items: center;
padding: 2px 5px 2px 0;
background: rgba(255, 255, 255, 0.5);
border-radius: 5px;
margin-right: 6px;
position: relative;
z-index: 1;
}
.bottom-home-sku span,
.top-home-sku span {
font-size: 14px;
vertical-align: top;
margin-left: 5px;
color: #222;
}
.more_wrap :deep(> .delete:hover),
.more_wrap :deep(> .download:hover) {
transform: scale(1.3);
}
.delete,
.download {
display: inline-block;
width: 20px;
height: 20px;
background: rgba(255, 0, 0, 0.678);
padding: 0 4px;
border-radius: 10px;
box-sizing: border-box;
}
.delete svg,
.download svg {
margin-top: -1px;
width: 12px;
height: 12px;
}
.product {
right: 66px;
background: #4168ff;
color: #ffffff;
padding: 0 8px 8px 8px;
border-radius: 18px;
}
.variants {
display: flex;
padding: 5px 0;
}
.variants + .variants {
border-top: 1px solid #ddd;
}
.variants .title {
width: 120px;
min-width: 110px;
color: #303133;
font-size: 14px;
font-weight: bold;
line-height: 20px;
}
.variants li {
display: inline-block;
position: relative;
width: 96px;
padding: 0 6px;
margin-left: 6px;
box-sizing: border-box;
border: 1px solid #dcdfe6;
background: #fff;
border-radius: 4px;
overflow: hidden;
white-space: nowrap;
font-weight: 500;
font-size: 12px;
line-height: 24px;
color: #606266;
cursor: pointer;
text-overflow: ellipsis;
padding-right: 10px;
}
.variants li.active {
border: 1px solid blue;
}
.variants li i {
position: absolute;
top: 7px;
right: 5px;
}
.value {
display: inline-block;
width: 150px;
text-align: left;
color: #000;
font-weight: bold;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.wraper:hover .store::after {
display: none;
}
.img_footer_cont {
position: absolute;
display: none;
right: 0;
bottom: 0;
left: 0;
padding-top: 50px;
background-image: linear-gradient(
to top,
rgba(255, 255, 255, 0.9),
rgba(255, 255, 255, 0)
);
}
.img_footer_cont .title {
min-height: 24px;
line-height: 24px;
font-size: 12px;
color: #222;
padding: 5px 8px 0;
}
.wraper:hover .img_footer_cont {
display: block;
}
.top-home-sku {
position: absolute;
bottom: 5px;
left: 5px;
z-index: 888;
display: flex;
align-items: center;
padding: 2px 5px;
background: hsla(0, 0%, 100%, 0.5);
border-radius: 5px;
}
.top-home-sku > span {
width: auto;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
display: inline-block;
}
.openly {
top: 5px;
z-index: 3;
right: 5px;
position: absolute;
}
</style>
<script setup lang="ts">
import { reactive, ref } from 'vue'
import cardWrapper from './cardWrapper.vue'
const queryParams = reactive({
name: "",
sku: "",
diy_sku: "",
print_type: "",
category_id: "",
});
const pager = reactive({
page:1,
size:100,
loading: false ,
count: 0,
lists: [] ,
extend: {}
})
const setCurrentImg = (item: any, val: string) => {
item.img_url = val;
};
const categoryTree = ref([])
const resetPage = () => {
}
</script>
<template>
<div class="product">
<el-card>
<el-form
ref="formRef"
class="searchForm"
label-width="80px"
:model="queryParams"
:inline="true"
>
<el-form-item label="商品名称">
<el-input
v-model="queryParams.name"
clearable
maxlength="40"
style="width: 150px"
show-word-limit
placeholder="请输入商品名称"
/>
</el-form-item>
<el-form-item label="SKU">
<el-input
v-model="queryParams.sku"
clearable
style="width: 150px"
placeholder="请输入sku"
/>
</el-form-item>
<el-form-item label="印花类型">
<el-select
v-model="queryParams.print_type"
style="width: 150px"
clearable
placeholder="请选择"
>
<el-option
v-for="(item, index) in ['满印', '局部印']"
:key="index"
:label="item"
:value="index"
></el-option>
</el-select>
</el-form-item>
<el-form-item label="商品分类">
<el-cascader
ref="categoryCascader"
v-model="queryParams.category_id"
:options="categoryTree"
clearable
style="width: 150px"
:props="{
label: 'name',
value: 'id',
emitPath: false,
}"
:show-all-levels="false"
></el-cascader>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="resetPage">查询</el-button>
</el-form-item>
</el-form>
</el-card>
<div class="product-list">
<el-card
style="flex: 1; flex-shrink: 0; overflow: hidden"
class="!border-none"
shadow="never"
>
<div
v-loading="pager.loading"
class="eltable"
element-loading-text="加载中..."
>
<div v-if="pager.lists.length > 0" id="shopline" class="card-mode">
<div
v-for="(item, index) in pager.lists"
:key="index"
class="card-item"
>
<card-wrapper
:data="item"
:index="index"
is-show-home-sku="bottom"
:is-sku-image-list="true"
:has-width="true"
:is-show-sku="false"
:is-show-sales="false"
:is-custom="true"
@set-current-img="(val) => setCurrentImg(item, val)"
>
<template #operations>
<!-- <img-->
<!-- title="操作日志"-->
<!-- width="24"-->
<!-- height="24"-->
<!-- src="@/assets/images/log.png"-->
<!-- alt=""-->
<!-- @click.stop="viewTheLog(item.id)"-->
<!-- />-->
<img
title="编辑"
width="24"
height="24"
src="@/assets/images/edit.png"
alt=""
@click.stop="editInfo(item, 'edit')"
/>
</template>
<template #price>
<span
style="margin: 0 5px"
class="price"
:title="'建议零售价' + item.sales_price"
>
{{ item.sales_price ? "¥" : ""
}}{{ item.sales_price }}&nbsp;
</span>
</template>
<template #level-time>
<div
class="create-time"
:title="`上架时间:${item.create_time}`"
>
<span>{{ item.create_time }}</span>
</div>
</template>
<template #synthesizeStatus>
<div title="印花状态" style="position: absolute; right: 0">
<el-tag :type="item.print_type == 0 ? '' : 'warning'">
{{ item.print_type == 0 ? "满印" : "局部印" }}
</el-tag>
</div>
</template>
<template #otherContent>
<div class="sds-keyid">
<div class="product-id" @click.stop="copy(item.id)">
<img
:title="'复制' + item.id"
width="20"
src="@/assets/images/id.png"
alt=""
/>
<span :title="item.id">
{{ item.id }}
</span>
</div>
<div class="product-sku" @click.stop="copy(item.sku)">
<span :title="item.sku">
{{ item.sku }}
</span>
</div>
</div>
</template>
</card-wrapper>
</div>
</div>
<div v-else class="empty">暂无数据</div>
</div>
<div class="flex justify-center mt-4">
<div
style="
display: flex;
align-items: center;
margin-left: 20px;
width: max-content;
"
>
<span>一行展示</span>
<el-input
v-model="rowNumber"
type="number"
style="width: 80px; margin: 0 10px"
placeholder="请输入"
clearable
@change="changeRowNumber"
/>
<span></span>
</div>
&emsp;&emsp;&emsp;
<pagination v-model="pager" @change="getLists" />
</div>
</el-card>
</div>
</div>
</template>
<style scoped lang="scss">
.product{
display: flex;
flex-direction: column;
height: 100%;
}
.searchForm{
::v-deep(.el-form-item){
margin-bottom: 0;
}
}
.justify-center{
display: flex;
justify-content: center;
}
.product-list{
flex: 1;
margin-top: 10px;
flex-shrink: 0;
::v-deep(.el-card){
height: 100%;
.el-card__body{
display: flex;
height: 100%;
flex-direction: column;
.eltable{
flex: 1;
flex-shrink: 0;
overflow: auto;
display: flex;
align-items: center;
justify-content: center;
}
}
}
}
</style>
<template>
<div class="page card h-100 flex-gap-10 overflow-hidden flex">
<div class="left">
<ElTree
ref="treeRef"
default-expand-all
:expand-on-click-node="false"
:default-expanded-keys="[]"
:highlight-current="true"
node-key="code"
:data="treeData"
:props="{ children: 'children', label: 'remark' }"
@node-click="nodeClick"
>
<template #default="{ data }">
<div class="tree-node">
<div class="tree-node-label">{{ data.remark }}</div>
<div v-if="data.count || data.count === 0" class="tree-node-count">
{{ `(${data.count})` }}
</div>
</div>
</template>
</ElTree>
</div>
<div class="right">
<div class="delivery-note-page flex-column card h-100 overflow-hidden">
<splitDiv size="50">
<template #top>
<div class="header-filter-form">
<ElForm :model="searchForm" inline>
<ElFormItem label="账期">
<el-date-picker
v-model="dateRange"
:default-time="[
new Date(0, 0, 0, 0, 0, 0),
new Date(0, 0, 0, 23, 59, 59),
]"
type="datetimerange"
start-placeholder="开始时间"
end-placeholder="结束时间"
clearable
style="width: 260px"
format="YYYY-MM-DD HH:mm:ss"
value-format="YYYY-MM-DD HH:mm:ss"
/>
</ElFormItem>
<ElFormItem label="对账单号">
<ElInput
v-model="searchForm.recNumber"
clearable
placeholder="对账单号"
style="width: 160px"
/>
</ElFormItem>
<ElFormItem label="发货单号">
<ElInput
v-model="searchForm.billNumber"
clearable
placeholder="发货单号"
style="width: 160px"
/>
</ElFormItem>
<ElFormItem style="margin-right: 10px" label="订单号">
<ElInput
v-model="searchForm.orderNumber"
clearable
placeholder="订单号"
style="width: 160px"
/>
</ElFormItem>
<ElFormItem>
<ElButton type="primary" @click="search">查询</ElButton>
</ElFormItem>
<ElFormItem>
<ElButton @click="resetSearchForm">重置</ElButton>
</ElFormItem>
</ElForm>
</div>
<div class="btn-list">
<el-button
v-if="nodeId === 10"
type="primary"
@click="confirmOrder"
>
确认对账单
</el-button>
<el-button
v-if="nodeId === 10"
type="warning"
@click="rejectOrder"
>
驳回
</el-button>
<el-button type="success" @click="exportExcel"> 导出 </el-button>
<el-button
v-if="nodeId === 30"
type="danger"
@click="auditOrder('archive')"
>
归档
</el-button>
</div>
<div
class="delivery-note-content flex-1 flex-column overflow-hidden"
>
<div class="delivery-note-list flex-1 overflow-hidden">
<ElTable
ref="singleTableRef"
highlight-current-row
:data="tableData"
show-summary
:summary-method="getSummaries"
default-expand-all
size="small"
style="width: 100%; height: 100%"
border
@current-change="rowClick"
@selection-change="handleSelectionChange"
>
<ElTableColumn
type="selection"
width="70"
header-align="center"
align="center"
></ElTableColumn>
<ElTableColumn
type="index"
label="序号"
width="60"
fixed="left"
header-align="center"
align="center"
></ElTableColumn>
<ElTableColumn
label="编码"
show-overflow-tooltip
prop="name"
width="200"
header-align="center"
align="center"
></ElTableColumn>
<!-- <ElTableColumn
label="客户"
show-overflow-tooltip
prop="user_mark"
width="200"
header-align="center"
align="center"
></ElTableColumn> -->
<ElTableColumn
label="对账单号"
show-overflow-tooltip
prop="rec_number"
min-width="200"
header-align="center"
align="center"
></ElTableColumn>
<!-- <ElTableColumn
label="订单号"
show-overflow-tooltip
prop="order_number"
width="200"
header-align="center"
align="center"
></ElTableColumn> -->
<!-- <ElTableColumn
label="ERP总价格"
show-overflow-tooltip
width="130"
prop="erp_total_amount"
header-align="center"
align="center"
></ElTableColumn> -->
<el-table-column
label="账期"
header-align="center"
align="center"
min-width="340"
>
<template #default="scope">
{{ scope.row.start_time }} - {{ scope.row.end_time }}
</template>
</el-table-column>
<el-table-column
label="商品总价格(¥)"
header-align="center"
prop="product_total_amount"
width="130"
align="center"
show-overflow-tooltip
>
</el-table-column>
<!-- <el-table-column
label="ERP总价格"
header-align="center"
prop="erp_total_amount"
width="120"
align="center"
show-overflow-tooltip
>
</el-table-column> -->
<el-table-column
label="物流总价(¥)"
header-align="center"
prop="carriage_total_amount"
width="130"
align="center"
show-overflow-tooltip
></el-table-column>
<el-table-column
label="应付金额(¥)"
header-align="center"
width="130"
align="center"
show-overflow-tooltip
>
<template #default="{ row }">
<span>{{
row.carriage_total_amount + row.product_total_amount
}}</span>
</template>
</el-table-column>
<el-table-column
label="实付金额(¥)"
header-align="center"
prop="actual_amount"
width="130"
align="center"
show-overflow-tooltip
></el-table-column>
<el-table-column
label="水单"
header-align="center"
prop="water_list"
width="100"
align="center"
show-overflow-tooltip
>
<template #default="{ row }">
<ImageView :src="row.water_list" />
</template>
</el-table-column>
<ElTableColumn
label="发货数量(件)"
show-overflow-tooltip
width="100"
prop="carriage_total_amount"
header-align="center"
align="center"
></ElTableColumn>
<ElTableColumn
label="总发货数量(件)"
show-overflow-tooltip
prop="num"
width="130"
header-align="center"
align="center"
></ElTableColumn>
<ElTableColumn
label="质检通过数量(件)"
show-overflow-tooltip
prop="pass_num"
width="130"
header-align="center"
align="center"
></ElTableColumn>
<!-- <ElTableColumn
label="ERP状态"
show-overflow-tooltip
width="130"
prop="erp_status"
header-align="center"
align="center"
></ElTableColumn> -->
<ElTableColumn
label="质检未通过数量(件)"
show-overflow-tooltip
width="140"
prop="not_pass_num"
header-align="center"
align="center"
></ElTableColumn>
<ElTableColumn
label="创建时间"
show-overflow-tooltip
width="200"
prop="create_time"
header-align="center"
align="center"
></ElTableColumn>
</ElTable>
</div>
<ElPagination
v-model:current-page="currentPage"
v-model:page-size="pageSize"
:page-sizes="[100, 200, 300, 400, 500]"
background
layout="total, sizes, prev, pager, next, jumper"
:total="total"
style="margin: 10px auto 0; text-align: right"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
></ElPagination>
</div>
</template>
<template #bottom>
<el-tabs v-model="tabsValue" @tab-click="tabsClick">
<el-tab-pane name="0" label="发货单详情">
<el-form :model="detailForm" inline>
<el-form-item label="发货单号">
<el-input
v-model="detailForm.billNumber"
placeholder="请输入发货单号"
clearable
style="width: 130px"
/>
</el-form-item>
<el-form-item label="订单号">
<el-input
v-model="detailForm.orderNumber"
placeholder="请输入订单号"
clearable
style="width: 130px"
/>
</el-form-item>
<el-form-item label="生产单号">
<el-input
v-model="detailForm.subOrderNumber"
placeholder="请输入订单号"
clearable
style="width: 130px"
/>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="searchDetail"
>查询</el-button
>
</el-form-item>
</el-form>
<div class="table-wrap">
<ElTable size="small" :data="detailList" height="100%" border>
<ElTableColumn
show-overflow-tooltip
width="60"
align="center"
label="序号"
type="index"
/>
<ElTableColumn
show-overflow-tooltip
align="center"
label="单号"
prop="factory_order_number"
/>
<!-- <ElTableColumn
show-overflow-tooltip
align="center"
label="ERP单号"
prop="erp_order_number"
/> -->
<ElTableColumn
show-overflow-tooltip
align="center"
label="发货单号"
prop="bill_number"
/>
<ElTableColumn
show-overflow-tooltip
align="center"
label="发货数(件)"
prop="num"
/>
<ElTableColumn
show-overflow-tooltip
align="center"
label="质检通过(件)"
prop="pass_num"
/>
<ElTableColumn
show-overflow-tooltip
align="center"
label="质检不通过(件)"
prop="not_pass_num"
/>
<!-- <ElTableColumn
show-overflow-tooltip
align="center"
label="ERP价格"
prop="erp_price"
/> -->
<ElTableColumn
show-overflow-tooltip
align="center"
label="成本价(¥)"
prop="price"
/>
<ElTableColumn
show-overflow-tooltip
align="center"
label="物流价格(¥)"
prop="carriage_amount"
/>
<ElTableColumn
show-overflow-tooltip
align="center"
label="发货时间"
prop="shipment_time"
/>
<ElTableColumn
width="100"
align="center"
header-align="center"
label="操作"
>
<template #default="{ row }">
<ElButton type="primary" link @click="onDetail(row)"
>查看详情
</ElButton>
</template>
</ElTableColumn>
</ElTable>
</div>
<ElPagination
v-model:current-page="detailPager.page"
v-model:page-size="detailPager.rows"
:page-sizes="[100, 200, 300, 400, 500]"
background
layout="total, sizes, prev, pager, next, jumper"
:total="detailPager.total"
style="margin: 10px auto 0; text-align: right"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
></ElPagination>
</el-tab-pane>
<el-tab-pane name="1" label="操作日志">
<ul
style="
color: #333;
font-size: 12px;
height: 100%;
overflow: auto;
"
>
<li
v-for="(item, index) in logList"
:key="index"
style="display: flex"
>
<span style="display: inline-block">
{{ item.create_time }}
</span>
<span style="margin: 0 5px 0 20px">{{
item.employee_account
}}</span>
<span style="display: inline-block">{{
item.description
}}</span>
</li>
</ul>
</el-tab-pane>
</el-tabs>
</template>
</splitDiv>
</div>
</div>
</div>
<ElDrawer
v-model="shipmentOrderDetailDrawerVisible"
title="发货单详情"
size="50%"
>
<shipmentOrderDetailInfo :detail="shipmentOrderDetail" />
</ElDrawer>
<ElDialog
v-model="confirmOrderVisible"
title="确认对账单"
width="500px"
:close-on-click-modal="false"
>
<ElForm ref="auditFormRef" :model="auditForm" label-width="80px">
<ElFormItem
label="意见"
prop="pass"
:rules="[{ required: true, message: '请选择' }]"
>
<ElRadioGroup v-model="auditForm.pass">
<ElRadio :label="1">通过</ElRadio>
<ElRadio :label="0">不通过</ElRadio>
</ElRadioGroup>
</ElFormItem>
<ElFormItem
label="审核意见"
prop="description"
:rules="
auditForm.pass === 0
? [{ required: true, message: '请输入审核意见' }]
: []
"
>
<ElInput v-model="auditForm.description" type="textarea" />
</ElFormItem>
</ElForm>
<template #footer>
<span class="dialog-footer">
<el-button @click="confirmOrderVisible = false">取消</el-button>
<el-button type="primary" @click="submitConfirmOrder">确认</el-button>
</span>
</template>
</ElDialog>
</template>
<script setup lang="ts">
import { ElMessage, ElRadioGroup, ElTree, TableColumnCtx } from 'element-plus'
import splitDiv from '@/components/splitDiv/splitDiv.vue'
import { ElTable } from 'element-plus'
import usePageList from '@/utils/hooks/usePageList'
import { useValue } from '@/utils/hooks/useValue'
import {
customJomallReconciliation,
getReconciliationAmount,
getShipmentDetailsById,
auditOrderApi,
getShipmentOrderDetailById,
confirmOrderApi,
rejectOrderApi,
getLogListApi,
exportExcelApi,
} from '@/api/order'
import { ref, onMounted, watch, nextTick } from 'vue'
import 'element-plus/dist/index.css'
import {
CountStatus,
AccountStatementNote,
ItemList,
LogList,
AccountStatementNoteSearchForm,
} from '@/types/api/billOrder.ts'
import { DetailForm, ShipmentOrderDetailData } from '@/types/api/deliveryNote'
import shipmentOrderDetailInfo from '@/components/ShipmentOrderDetail.vue'
import ImageView from '@/components/ImageView.vue'
import { showConfirm } from '@/utils/ui'
// import { getUserMarkList } from '@/api/auth.ts'
interface Tree {
remark?: string
count?: number
code?: string
children?: Tree[]
}
interface SummaryMethodProps<T = AccountStatementNote> {
columns: TableColumnCtx<T>[]
data: T[]
}
// const nameSpaceList = ref<string[]>([])
const treeData = ref<CountStatus[]>()
const [searchForm, resetSearchForm] = useValue<AccountStatementNoteSearchForm>(
{},
)
const dateRange = ref<string[]>([])
const selections = ref<AccountStatementNote[]>([])
const detailList = ref<ItemList[]>([])
const tabsValue = ref<string>('0')
const singleTableRef = ref<InstanceType<typeof ElTable>>()
const currentRow = ref<AccountStatementNote | null>(null)
const logList = ref<LogList[]>([])
const nodeId = ref<number | string>(10)
const treeRef = ref<InstanceType<typeof ElTree>>()
const {
currentPage,
pageSize,
total,
data: tableData,
refresh: search,
onCurrentPageChange: handleCurrentChange,
onPageSizeChange: handleSizeChange,
} = usePageList({
query: (page, pageSize) =>
customJomallReconciliation(
{
...searchForm.value,
status: nodeId.value === -1 ? null : nodeId.value,
start_time: dateRange.value && dateRange.value[0],
end_time: dateRange.value && dateRange.value[1],
},
page,
pageSize,
).then((res) => res.data) as never,
})
onMounted(() => {
// getNameSpaceList()
})
// const dialogVisible = ref<boolean>(false)
// const getNameSpaceList = async () => {
// try {
// const res = await getUserMarkList()
// nameSpaceList.value = res.data
// } catch (e) {
// // showError(e)
// }
// }
const getTreeNum = async () => {
try {
const res = await getReconciliationAmount()
res.data = [{ code: -1, remark: '全部', children: res.data }]
treeData.value = res.data
await nextTick(() => {
treeRef.value!.setCurrentKey(nodeId.value, true)
})
} catch (e) {
console.error(e)
}
}
const rowClick = (row: AccountStatementNote) => {
if (!row) {
currentRow.value = null
}
currentRow.value = row
tabsClick()
}
const tabsClick = async () => {
if (!currentRow.value) {
detailList.value = []
logList.value = []
return
}
await nextTick()
if (tabsValue.value === '0') {
searchDetail()
} else {
getLogList()
}
}
const handleSelectionChange = (v: AccountStatementNote[]) => {
selections.value = v
}
const getSummaries = (param: SummaryMethodProps) => {
const { columns, data } = param
const sums: string[] = []
columns.forEach(
(column: TableColumnCtx<AccountStatementNote>, index: number) => {
if (index === 0) {
sums[index] = '合计'
return
}
const values = data.map((item: AccountStatementNote) => {
return Number(item[column.property as keyof AccountStatementNote])
})
if (!values.every((value: number) => !isNaN(value))) {
return
}
sums[index] = values
.reduce((prev: number, curr: number) => {
const value = Number(curr)
if (!isNaN(value)) {
return prev + curr
} else {
return prev
}
}, 0)
.toString()
},
)
return sums
}
const confirmOrderVisible = ref<boolean>(false)
const auditForm = ref({
pass: 1,
description: '',
})
const auditFormRef = ref()
const auditOrder = (key: string) => {
let url = ''
let text = ''
switch (key) {
case 'pay':
url = 'reconciliation/payment'
text = '确认付款'
break
case 'archive':
url = 'reconciliation/archiving'
text = '确认归档'
break
}
if (selections.value.length === 0) {
return ElMessage.warning('请选择要操作的数据')
}
ElMessageBox.confirm(`${text}对账单?`, '重要提示', {
confirmButtonText: '确定',
type: 'warning',
}).then(async () => {
const ids = selections.value.map((el) => el.id).join(',')
await auditOrderApi(url, ids)
ElMessage.success('操作成功')
search()
await getTreeNum()
})
}
const rejectOrder = () => {
if (selections.value.length === 0) {
return ElMessage.warning('请选择要操作的数据')
}
ElMessageBox.prompt('请输入驳回原因', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
inputPattern: /.+/,
customClass: 'reject',
inputErrorMessage: '请输入驳回原因',
inputPlaceholder: '驳回原因',
}).then(async ({ value }) => {
const ids = selections.value.map((el) => el.id).join(',')
try {
await rejectOrderApi({ ids: ids, description: value, pass: 0 })
ElMessage.success('操作成功')
search()
await getTreeNum()
} catch (e) {
console.error(e)
}
})
}
const confirmOrder = async () => {
if (selections.value.length === 0) {
return ElMessage.warning('请选择要操作的数据')
}
try {
await showConfirm('是否确认对账单?', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
})
} catch {
return
}
const ids = selections.value.map((el) => el.id).join(',')
try {
await confirmOrderApi({
ids,
})
confirmOrderVisible.value = false
ElMessage.success('操作成功')
search()
await getTreeNum()
} catch (e) {
console.error(e)
}
// confirmOrderVisible.value = true
// auditForm.value = {
// pass: 1,
// description: '',
// }
// await nextTick()
// auditFormRef.value!.clearValidate()
}
const submitConfirmOrder = async () => {
try {
await auditFormRef.value?.validate()
} catch {
return
}
const ids = selections.value.map((el) => el.id).join(',')
await confirmOrderApi({
...auditForm.value,
pass: auditForm.value.pass === 0 ? 0 : undefined,
description:
auditForm.value.description === ''
? undefined
: auditForm.value.description,
ids,
})
confirmOrderVisible.value = false
ElMessage.success('操作成功')
search()
await getTreeNum()
if (singleTableRef.value) {
singleTableRef.value!.setCurrentRow(currentRow.value)
}
}
watch(
() => tableData.value,
() => {
if (tableData.value && tableData.value.length > 0) {
if (singleTableRef.value) {
singleTableRef.value!.setCurrentRow(tableData.value[0])
currentRow.value = (tableData.value as never)[0]
}
} else {
detailList.value = []
}
},
{ immediate: true },
)
const nodeClick = (data: Tree) => {
nodeId.value = data.code ?? ''
search()
}
const shipmentOrderDetailDrawerVisible = ref(false)
const shipmentOrderDetail = ref({} as ShipmentOrderDetailData)
const onDetail = async (row: ItemList) => {
try {
const res = await getShipmentOrderDetailById(row.shipment_id)
shipmentOrderDetail.value = res.data
shipmentOrderDetailDrawerVisible.value = true
} catch (e) {
console.error(e)
}
}
const detailForm = ref({} as DetailForm)
const detailPager = ref({
page: 1,
rows: 100,
total: 0,
})
const searchDetail = async () => {
try {
const res = await getShipmentDetailsById({
...detailForm.value,
page: detailPager.value.page,
rows: detailPager.value.rows,
infoId: currentRow.value?.id,
})
detailList.value = res.data.records || []
detailPager.value.total = res.data.total
// logList.value = res.data.logList || []
} catch (e) {
console.error(e)
}
}
const getLogList = async () => {
try {
const res = await getLogListApi(currentRow.value?.id)
logList.value = res.data
// logList.value = res.data.logList || []
} catch (e) {
console.error(e)
}
}
const exportExcel = async () => {
if (selections.value.length === 0) {
return ElMessage.warning('请选择要操作的数据')
}
const ids = selections.value.map((el) => el.id).join(',')
try {
const res = await exportExcelApi(ids)
window.open(res.data)
} catch (e) {
console.error(e)
}
}
onMounted(() => {
getTreeNum()
})
</script>
<style lang="scss" scoped>
.header-filter-form {
:deep(.el-form-item) {
margin-right: 14px;
margin-bottom: 10px;
}
}
$border: solid 1px #ddd;
.send-order-list {
display: grid;
grid-template-columns: 2fr 1fr;
border-left: $border;
border-top: $border;
}
.send-order-column {
padding: 10px 16px;
line-height: 1.5;
}
.send-order-header {
font-weight: bold;
text-align: center;
background-color: #f8f8f9;
}
.send-order-product-item {
display: flex;
justify-content: space-between;
gap: 20px;
&:not(:first-child) {
border-top: $border;
padding: 10px 0;
}
.send-order-prop-list {
flex: 1;
}
}
.send-order-product-image {
width: 100px;
}
.dialog-footer {
text-align: center;
}
.delivery-note-list {
:deep(.vertical-align-top) {
vertical-align: top;
}
}
.left {
width: 160px;
:deep(.el-tree-node__content) {
height: 30px;
line-height: 30px;
}
:deep(.el-tree-node__label) {
font-size: 13px;
cursor: pointer;
display: inline-block;
width: 100%;
color: black !important;
padding: 3px 7px;
}
:deep(.el-tree-node__expand-icon) {
display: none;
}
:deep(.is-current) {
.tree-node-label,
.tree-node-count {
background-color: #ecf5ff;
color: #409eff !important;
}
.el-tree-node__children {
.tree-node-label,
.tree-node-count {
background-color: transparent !important;
color: black !important;
}
}
}
}
.tree-node {
display: flex;
color: #333;
font-weight: 500;
}
.right {
flex: 1;
flex-shrink: 0;
background: white;
overflow: hidden;
}
::v-deep(.el-tree-node__label) {
font-size: 13px;
color: rgb(96, 98, 102);
cursor: pointer;
}
::v-deep(.el-tree-node__expand-icon) {
display: none;
}
::v-deep(.el-tree-node__label) {
display: inline-block;
width: 100%;
color: black !important;
padding: 3px 7px;
}
::v-deep(.is-current) {
.el-tree-node__label {
background-color: #ecf5ff;
color: #409eff !important;
}
.el-tree-node__children {
.el-tree-node__label {
background-color: transparent !important;
color: black !important;
}
}
}
::v-deep(.splitpanes__pane) {
display: flex;
flex-direction: column;
}
::v-deep(.splitpanes--horizontal > .splitpanes__splitter) {
min-height: 5px;
margin-top: 10px;
}
.draw-line {
width: 100%;
height: 5px;
background: #eff3f6;
}
.btn-list {
margin-bottom: 10px;
}
::v-deep(.el-tree-node) {
cursor: pointer;
margin-bottom: 5px;
}
::v-deep(.el-tree-node__label) {
font-size: 14px;
}
::v-deep(.el-tabs) {
display: flex;
flex-direction: column;
height: 100%;
.el-tabs__content {
flex: 1;
flex-shrink: 0;
overflow: hidden;
.el-tab-pane {
height: 100%;
display: flex;
flex-direction: column;
overflow: hidden;
}
}
}
.delivery-note-page {
:deep(#top) {
height: 100%;
}
}
</style>
<script setup lang="ts">
import {
getFactoryWarehouseInfo,
factoryWarehouseInfo,
warehouseInfo,
createWarehouseApi,
updateWarehouseApi, deleteWarehouseApi,
} from '@/api/warehouse.ts'
import { nextTick, ref } from 'vue'
const selections = ref<warehouseInfo[]>([])
const formRef = ref()
const createData = ref({
title: '',
show: false,
isEdit: false,
form: {
name: '',
code: '',
sort: '',
defaulted: 1,
remarks: '',
},
})
const rules = {
name: [
{ required: true, message: '请输入仓库名称', trigger: 'blur' },
],
code: [
{ required: true, message: '请输入仓库编码', trigger: 'blur' },
],
defaulted: [
{ required: true, message: '请选择是否默认仓库', trigger: 'change' },
],
}
const leftData = ref<warehouseInfo[]>([])
const pagination = ref<factoryWarehouseInfo>({
pageSize: 50,
currentPage: 1,
total: 0,
})
async function getData() {
const res = await getFactoryWarehouseInfo({
...pagination.value,
})
leftData.value = res.data.records
pagination.value.total = res.data.total
}
const handleSelectionChange = (data: warehouseInfo[]) => {
selections.value = data
}
const handleSizeChange = (pageSize: number) => {
pagination.value.pageSize = pageSize
getData()
}
const handleCurrentChange = (currentPage: number) => {
pagination.value.currentPage = currentPage
getData()
}
const handleConfirm = async () => {
await formRef.value?.validate()
if (!createData.value.isEdit) {
await createWarehouseApi(createData.value.form)
} else {
await updateWarehouseApi(createData.value.form)
}
createData.value.show = false
ElMessage.success('操作成功')
await getData()
}
const handleBatchDelete = async () => {
if (!selections.value.length) {
return ElMessage.warning('请选择要删除的数据')
}
await ElMessageBox.confirm('确定要删除吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
})
const str = selections.value.map((el) => el.id).join(',')
await deleteWarehouseApi(str)
ElMessage.success('删除成功')
await getData()
}
const createWarehouse = () => {
createData.value.show = true
createData.value.isEdit = false
createData.value.title = '新增仓库'
createData.value.form = {
name: '',
code: '',
sort: '',
defaulted: 1,
remarks: '',
}
nextTick(() => {
formRef.value?.clearValidate()
})
}
const updateDefaulted = async (item: warehouseInfo) => {
await updateWarehouseApi({
id: item.id,
defaulted: item.defaulted,
})
}
const updateWarehouse = (item: warehouseInfo) => {
createData.value.show = true
createData.value.isEdit = true
createData.value.title = '修改仓库'
createData.value.form = { ...item }
nextTick(() => {
formRef.value?.clearValidate()
})
}
getData()
</script>
<template>
<el-card style="height: 100%">
<div class="manage">
<div class="header">
<el-button type="success" @click="createWarehouse">新增仓库</el-button>
<el-button type="danger" @click="handleBatchDelete">删除</el-button>
</div>
<div class="table-flex">
<div class="left-table">
<div class="table-container">
<el-table height="100%" :data="leftData" border @selection-change="handleSelectionChange">
<el-table-column type="selection" />
<el-table-column type="index" label="序号" width="60" />
<el-table-column label="仓库名称" prop="name" align="center" />
<el-table-column label="仓库类型" prop="name" align="center" />
<el-table-column label="仓库编码" prop="code" align="center" />
<el-table-column label="仓库地址" prop="name" align="center" />
<el-table-column label="备注" prop="remarks" align="center" />
<el-table-column label="默认仓库" prop="defaulted" align="center">
<template #default="{row}">
<el-switch
v-model="row.defaulted" :active-value="1" :inactive-value="0"
@click="updateDefaulted(row)"></el-switch>
</template>
</el-table-column>
<el-table-column label="操作" align="center">
<template #default="{row}">
<el-button type="primary" @click="updateWarehouse(row)">编辑</el-button>
</template>
</el-table-column>
</el-table>
</div>
<div class="pagination">
<el-pagination
v-model:current-page="pagination.currentPage"
v-model:page-size="pagination.pageSize"
:page-sizes="[50, 100, 150, 200]"
layout="total, sizes, prev, pager, next, jumper"
:total="pagination.total"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
/>
</div>
</div>
</div>
<el-dialog v-model="createData.show" :title="createData.title">
<el-form ref="formRef" label-width="110px" :rules="rules" :model="createData.form">
<el-form-item label="仓库名称" prop="name">
<el-input v-model="createData.form.name" clearable placeholder="请输入仓库名称"></el-input>
</el-form-item>
<el-form-item label="仓库编码" prop="code">
<el-input v-model="createData.form.code" clearable placeholder="请输入仓库编码"></el-input>
</el-form-item>
<el-form-item label="仓库序号" prop="sort">
<el-input-number v-model="createData.form.sort"></el-input-number>
</el-form-item>
<el-form-item label="是否默认仓库" prop="defaulted">
<el-switch v-model="createData.form.defaulted" :active-value="1" :inactive-value="0"></el-switch>
</el-form-item>
<el-form-item label="备注" prop="remarks">
<el-input
v-model="createData.form.remarks" type="textarea" :rows="4" clearable
placeholder="请输入备注"></el-input>
</el-form-item>
</el-form>
<template #footer>
<el-button @click="createData.show=false">取消</el-button>
<el-button type="primary" @click="handleConfirm">确定</el-button>
</template>
</el-dialog>
</div>
</el-card>
</template>
<style scoped lang="scss">
.el-card {
::v-deep(.el-card__body) {
height: 100%;
}
}
.manage {
height: 100%;
display: flex;
flex-direction: column;
.header {
margin-bottom: 10px;
}
.table-flex {
flex: 1;
flex-shrink: 0;
overflow: hidden;
display: flex;
}
.right-table {
flex: 1;
margin-left: 10px;
flex-shrink: 0;
}
.left-table {
height: 100%;
display: flex;
width: 100%;
flex-direction: column;
.pagination {
display: flex;
margin-top: 10px;
justify-content: center;
}
.table-container {
flex: 1;
flex-shrink: 0;
overflow: hidden;
}
}
}
</style>
<template>
<div class="page card h-100 flex-gap-10 overflow-hidden flex">
<div class="left">
<ElTree
ref="treeRef"
default-expand-all
:expand-on-click-node="false"
:default-expanded-keys="[]"
:highlight-current="true"
node-key="code"
:data="treeData"
:props="{ children: 'children', label: 'name' }"
@node-click="nodeClick"
>
<template #default="{ data }">
<div class="tree-node">
<div class="tree-node-label">{{ data.name }}</div>
<div
v-if="data.countQuantity || data.countQuantity === 0"
class="tree-node-count"
>
{{ `(${data.countQuantity})` }}
</div>
</div>
</template>
</ElTree>
</div>
<div class="right">
<div class="delivery-note-page flex-column card h-100 overflow-hidden">
<splitDiv size="50">
<template #top>
<div class="header-filter-form">
<ElForm :model="searchForm" inline>
<ElFormItem label="采购仓库">
<ElSelect
v-model="searchForm.warehouseId"
clearable
placeholder="请选择仓库"
style="width: 160px"
>
<ElOption
v-for="item in warehouseList"
:key="item.id"
:label="item.name"
:value="item.id"
></ElOption>
</ElSelect>
</ElFormItem>
<ElFormItem label="入库时间">
<el-date-picker
v-model="tradingTime"
:shortcuts="pickerOptions.shortcuts"
:default-time="[
new Date(0, 0, 0, 0, 0, 0),
new Date(0, 0, 0, 23, 59, 59),
]"
type="datetimerange"
start-placeholder="开始时间"
end-placeholder="结束时间"
clearable
style="width: 260px"
format="YYYY-MM-DD HH:mm:ss"
value-format="YYYY-MM-DD HH:mm:ss"
/>
</ElFormItem>
<ElFormItem label="单号">
<ElInput
v-model="searchForm.orderNumber"
clearable
placeholder="请输入入库单号"
style="width: 160px"
/>
</ElFormItem>
<ElFormItem label="交货单号">
<ElInput
v-model="searchForm.batchNumber"
clearable
placeholder="请输入交货单号"
style="width: 160px"
/>
</ElFormItem>
<ElFormItem style="margin-right: 10px" label="SKU">
<ElInput
v-model="searchForm.sku"
clearable
placeholder="请输入SKU"
style="width: 160px"
/>
</ElFormItem>
<ElFormItem>
<ElButton type="primary" @click="search">查询</ElButton>
</ElFormItem>
<ElFormItem>
<ElButton @click="resetSearchForm">重置</ElButton>
</ElFormItem>
</ElForm>
</div>
<div class="btn-list">
<el-button type="primary" @click="addDialog(1)"> 新增 </el-button>
<el-button type="danger" @click="rejectOrder"> 驳回 </el-button>
<el-button type="danger" @click="rejectOrder"> 删除 </el-button>
<el-button type="warning" @click="importExcel"> 导入 </el-button>
<el-button type="success" @click="exportExcel"> 导出 </el-button>
<el-button type="primary" @click="exportExcel">
打印SKU标签
</el-button>
<el-button type="warning" @click="exportExcel"> 审核 </el-button>
<el-button type="success" @click="exportExcel">
提交审核
</el-button>
<el-button type="danger" @click="rejectOrder"> 作废 </el-button>
<el-button type="success" @click="auditOrder('archive')">
归档
</el-button>
</div>
<div
class="delivery-note-content flex-1 flex-column overflow-hidden"
>
<div class="delivery-note-list flex-1 overflow-hidden">
<ElTable
ref="singleTableRef"
highlight-current-row
:data="tableData"
default-expand-all
size="small"
style="width: 100%; height: 100%"
border
@current-change="rowClick"
@selection-change="handleSelectionChange"
>
<ElTableColumn
type="selection"
width="70"
header-align="center"
align="center"
></ElTableColumn>
<ElTableColumn
type="index"
label="序号"
width="60"
fixed="left"
header-align="center"
align="center"
></ElTableColumn>
<ElTableColumn
label="入库单号"
show-overflow-tooltip
prop="inNo"
width="110"
header-align="center"
align="center"
></ElTableColumn>
<ElTableColumn
label="单据状态"
show-overflow-tooltip
prop="billStatus"
width="80"
header-align="center"
align="center"
></ElTableColumn>
<ElTableColumn
label="仓库名称"
show-overflow-tooltip
prop="warehouseName"
min-width="100"
header-align="center"
align="center"
></ElTableColumn>
<ElTableColumn
label="工厂编号"
show-overflow-tooltip
prop="factoryCode"
min-width="100"
header-align="center"
align="center"
></ElTableColumn>
<ElTableColumn
label="物流单号"
show-overflow-tooltip
prop="shipmentNumber"
width="200"
header-align="center"
align="center"
></ElTableColumn>
<ElTableColumn
label="总金额(¥)"
show-overflow-tooltip
width="120"
prop="totalPrice"
header-align="center"
align="center"
></ElTableColumn>
<ElTableColumn
label="SKU数量"
header-align="center"
prop="skuAmount"
width="130"
align="center"
show-overflow-tooltip
>
</ElTableColumn>
<ElTableColumn
label="总数量"
header-align="center"
prop="total"
width="120"
align="center"
show-overflow-tooltip
>
</ElTableColumn>
<ElTableColumn
label="备注"
show-overflow-tooltip
width="240"
prop="remark"
header-align="center"
align="center"
></ElTableColumn>
<ElTableColumn
width="100"
align="center"
header-align="center"
label="操作"
>
<template #default="{ row }">
<ElButton type="primary" link @click="addDialog(2, row)"
>编辑
</ElButton>
</template>
</ElTableColumn>
</ElTable>
</div>
<ElPagination
v-model:current-page="currentPage"
v-model:page-size="pageSize"
:page-sizes="[100, 200, 300, 400, 500]"
background
layout="total, sizes, prev, pager, next, jumper"
:total="total"
style="margin: 10px auto 0; text-align: right"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
></ElPagination>
</div>
</template>
<template #bottom>
<el-tabs v-model="tabsValue" @tab-click="tabsClick">
<el-tab-pane name="0" label="入库商品">
<div class="table-wrap">
<ElTable size="small" :data="detailList" height="100%" border>
<ElTableColumn
show-overflow-tooltip
width="60"
align="center"
label="序号"
type="index"
/>
<ElTableColumn
show-overflow-tooltip
align="center"
label="SKU图片"
prop="factory_order_number"
>
<template #default="{ row }">
<ImageView
:src="row.skuImage"
width="80px"
height="80px"
/>
</template>
</ElTableColumn>
<ElTableColumn
show-overflow-tooltip
align="center"
label="商品SKU"
prop="warehouseSku"
/>
<ElTableColumn
show-overflow-tooltip
align="center"
label="SKU名称"
prop="skuName"
/>
<ElTableColumn
show-overflow-tooltip
align="center"
label="入库数量"
prop="buyStored"
/>
<ElTableColumn
show-overflow-tooltip
align="center"
label="总成本(¥)"
prop="buyStored"
/>
<ElTableColumn
show-overflow-tooltip
align="center"
label="不良品数量"
prop="rejectsAmount"
/>
<ElTableColumn
show-overflow-tooltip
align="center"
label="不良品原因"
prop="rejectsAeason"
/>
<ElTableColumn
show-overflow-tooltip
align="center"
width="240"
label="备注"
prop="remark"
/>
<ElTableColumn
show-overflow-tooltip
align="center"
label="创建时间"
prop="createTime"
/>
<ElTableColumn
show-overflow-tooltip
align="center"
label="更新时间"
prop="updateTime"
/>
</ElTable>
</div>
<ElPagination
v-model:current-page="detailPager.page"
v-model:page-size="detailPager.rows"
:page-sizes="[100, 200, 300, 400, 500]"
background
layout="total, sizes, prev, pager, next, jumper"
:total="detailPager.total"
style="margin: 10px auto 0; text-align: right"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
></ElPagination>
</el-tab-pane>
<!-- <el-tab-pane name="1" label="操作日志">
<ul
style="
color: #333;
font-size: 12px;
height: 100%;
overflow: auto;
"
>
<li
v-for="(item, index) in logList"
:key="index"
style="display: flex"
>
<span style="display: inline-block">
{{ item.createdTime }}
</span>
<span style="display: inline-block">{{
item.description
}}</span>
</li>
</ul>
</el-tab-pane>
<el-tab-pane name="2" label="物流轨迹">
<ul
v-if="logisticsData.length > 0"
style="
color: #333;
font-size: 12px;
height: 100%;
overflow: auto;
"
>
<li v-for="(item, index) in logisticsData" :key="index">
<span class="log-desc">
{{ item.time + ' ' + item.context }}
</span>
</li>
</ul>
<div v-else class="empty">{{ $t('暂无数据') }}</div>
</el-tab-pane> -->
</el-tabs>
</template>
</splitDiv>
</div>
</div>
</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 v-model="importedFileUrl" />
</div>
</div>
<template #footer>
<span class="dialog-footer">
<el-button @click="importDialogVisible = false">取消</el-button>
<el-button type="primary" @click="submitConfirmOrder">确认</el-button>
</span>
</template>
</ElDialog>
<ElDialog
v-model="newDialogVisible"
:title="formId ? '编辑' : '新增'"
width="80%"
:close-on-click-modal="false"
>
<div class="dialog-form">
<ElForm
ref="editFormRef"
:model="editForm"
:rules="rules"
inline
label-width="90px"
>
<ElFormItem label="入库单号" prop="account">
<ElInput v-model="editForm.inNo" clearable disabled />
</ElFormItem>
<ElFormItem label="仓库" prop="warehouseId" required>
<ElSelect
v-model="editForm.warehouseId"
clearable
placeholder="请选择仓库"
style="width: 160px"
>
<ElOption
v-for="item in warehouseList"
:key="item.id"
:label="item.name"
:value="item.id"
></ElOption>
</ElSelect>
</ElFormItem>
<ElFormItem label="工厂:" prop="factoryCode">
<span>{{ editForm.factoryCode }}</span>
</ElFormItem>
<ElFormItem label="备注" prop="remark" style="width: 45%">
<ElInput
v-model="editForm.remark"
placeholder="请输入备注"
clearable
/>
</ElFormItem>
</ElForm>
<ElTable
size="small"
:data="otherPurchaseData"
height="500px"
border
@selection-change="productSelectionChange"
>
<ElTableColumn
type="selection"
width="70"
header-align="center"
align="center"
></ElTableColumn>
<ElTableColumn
show-overflow-tooltip
align="center"
width="100"
label="SKU图片"
prop="skuImage"
>
<template #default="{ row }">
<ImageView :src="row.skuImage" width="40px" height="40px" />
</template>
</ElTableColumn>
<ElTableColumn
show-overflow-tooltip
align="center"
label="商品SKU"
prop="warehouseSku"
/>
<ElTableColumn
show-overflow-tooltip
align="center"
label="SKU名称"
prop="skuName"
/>
<ElTableColumn align="center" label="入库数量" prop="buyStored">
<template #default="{ row }">
<el-input
v-model.number="row.buyStored"
@input="setCostPrice(row)"
placeholder="入库数量"
style="width: 120px"
clearable
size="small"
></el-input>
</template>
</ElTableColumn>
<ElTableColumn
width="100"
align="center"
label="成本价(¥)"
prop="costPrice"
/>
<ElTableColumn
align="center"
width="100"
label="总成本(¥)"
prop="totalPrice"
/>
<ElTableColumn
show-overflow-tooltip
align="center"
label="不良品数量"
prop="rejectsAmount"
/>
<ElTableColumn
show-overflow-tooltip
align="center"
label="不良品原因"
prop="rejectsAeason"
/>
<ElTableColumn align="center" label="库位" prop="loacationCode">
<template #default="{ row }">
<!-- +后有就正常展示,没有则通过搜索接口自己添加 -->
<span v-if="row.loacationCode">{{ row.loacationCode }}</span>
<ElSelect
v-else
v-model="row.locationId"
clearable
placeholder="请输入库位"
style="width: 120px"
filterable
remote
:remote-method="(query) => handleLocationSearch(query, row)"
:loading="locationLoading"
@change="(val) => handleLocationChange(val, row)"
>
<ElOption
v-for="item in locationList"
:key="item.locationId"
:label="item.locationCode"
:value="item.locationId"
></ElOption>
</ElSelect>
</template>
</ElTableColumn>
<ElTableColumn
show-overflow-tooltip
align="center"
width="240"
label="备注"
prop="remark"
/>
</ElTable>
</div>
<template #footer>
<div class="product-dialog-footer">
<div>
<el-input
v-model="selectSku"
placeholder="商品SKU"
style="width: 200px; margin: 0 10px"
clearable
size="small"
></el-input>
<el-popover placement="top-start" width="900" trigger="click">
<div v-if="skuData.length > 0" style="height: 50vh">
<ElTable size="small" :data="skuData" height="100%" border>
<ElTableColumn
show-overflow-tooltip
width="60"
align="center"
label="序号"
type="index"
/>
<ElTableColumn
show-overflow-tooltip
align="center"
label="SKU图片"
width="100"
prop="image"
>
<template #default="{ row }">
<ImageView :src="row.image" width="40px" height="40px" />
</template>
</ElTableColumn>
<ElTableColumn
show-overflow-tooltip
align="center"
label="商品SKU"
width="200"
prop="skuName"
/>
<ElTableColumn
show-overflow-tooltip
align="center"
width="200"
label="SKU名称"
prop="sku"
/>
<ElTableColumn
show-overflow-tooltip
align="center"
label="成本价(¥)"
width="100"
prop="factoryPrice"
/>
<ElTableColumn
show-overflow-tooltip
align="center"
label="库位"
prop="loacationCode"
/>
<ElTableColumn
width="100"
align="center"
header-align="center"
label="操作"
>
<template #default="{ row }">
<el-icon :size="32" color="#67C23A"
><CirclePlusFilled @click="skudblclick(row)"
/></el-icon>
</template>
</ElTableColumn>
</ElTable>
</div>
<template #reference>
<el-button
type="primary"
size="small"
style="width: 90px"
@click="selectbySku()"
>
查询
</el-button>
</template>
</el-popover>
<el-button
style="margin-left: 6px"
type="success"
size="small"
@click="addPurchase"
>
批量新增
</el-button>
<el-button
type="danger"
style="margin-left: 10px"
size="small"
@click="deleteOtherWarehousing()"
>
删除
</el-button>
<el-button
type="primary"
style="margin-left: 10px"
size="small"
@click="importData"
>
导入
</el-button>
</div>
<div>
<el-button
size="small"
style="margin-left: 10px"
@click="newDialogVisible = false"
>
取消
</el-button>
<el-button type="primary" size="small" @click="addOtherCurrency()">
保存
</el-button>
</div>
</div>
</template>
</ElDialog>
</template>
<script setup lang="ts">
import { ElMessage, ElRadioGroup, ElTree, TableColumnCtx } from 'element-plus'
import { Download, CirclePlusFilled } from '@element-plus/icons-vue'
import splitDiv from '@/components/splitDiv/splitDiv.vue'
import { ElTable } from 'element-plus'
import usePageList from '@/utils/hooks/usePageList'
import { useValue } from '@/utils/hooks/useValue'
import {
auditOrderApi,
rejectOrderApi,
getLogListApi,
exportExcelApi,
} from '@/api/order'
import {
getWarehouseStatusTree,
warehouseInRecordListPage,
getWarehouseInRecordDetail,
getBySku,
getWarehouseListApi,
getByWareHouseIdAndCode,
addInRecordApi,
updateInRecordApi,
} from '@/api/warehouse'
import BigNumber from 'bignumber.js'
import { ref, onMounted, watch, nextTick } from 'vue'
import 'element-plus/dist/index.css'
import { LogList } from '@/types/api/billOrder.ts'
import {
warehouseSearchForm,
InterWarehousePage,
InterWarehouseDetail,
InterWarehouseTree,
InterProductList,
InterskuList,
InterWarehouseList,
ILocation,
} from '@/types/api/warehouse'
import ImageView from '@/components/ImageView.vue'
import { showConfirm } from '@/utils/ui'
import UploadExcel from '@/components/UploadExcel.vue'
import { debounce } from 'lodash-es'
// import UploadExcel from '@/components/INgoVopy.vue'
const warehouseList = ref<InterWarehouseList[]>([])
const pickerOptions = {
shortcuts: [
{
text: '今日',
value: () => {
const start = new Date(new Date(getStartTime()).getTime())
const end = new Date()
return [start, end]
},
},
{
text: '昨天',
value: () => {
const start = new Date()
const end = new Date(new Date(getStartTime()).getTime() - 1)
start.setTime(end.getTime() - 3600 * 1000 * 24 * 1 + 1)
return [start, end]
},
},
{
text: '最近7天',
value: () => {
const end = new Date()
const start = new Date(getStartTime())
start.setTime(start.getTime() - 3600 * 1000 * 24 * 6)
return [start, end]
},
},
{
text: '最近14天',
value: () => {
const end = new Date()
const start = new Date(getStartTime())
start.setTime(start.getTime() - 3600 * 1000 * 24 * 13)
return [start, end]
},
},
{
text: '最近30天',
value: () => {
const end = new Date()
const start = new Date(getStartTime())
start.setTime(start.getTime() - 3600 * 1000 * 24 * 29)
return [start, end]
},
},
{
text: '本星期',
value: () => {
const end = new Date()
const start = new Date()
const nowDay = new Date().getDay() - 1
start.setTime(
new Date(getStartTime()).getTime() - 3600 * 1000 * 24 * nowDay,
)
return [start, end]
},
},
{
text: '上星期',
value: () => {
const end = new Date()
const start = new Date()
const nowDay = new Date().getDay() - 1
end.setTime(
new Date(getStartTime()).getTime() - 3600 * 1000 * 24 * nowDay - 1,
)
start.setTime(end.getTime() - 3600 * 1000 * 24 * 7 + 1)
return [start, end]
},
},
{
text: '这个月',
value: () => {
const end = new Date()
const start = new Date()
const nowDate = new Date().getDate() - 1
start.setTime(
new Date(getStartTime()).getTime() - 3600 * 1000 * 24 * nowDate,
)
return [start, end]
},
},
{
text: '上个月',
value: () => {
const date = new Date()
let year = date.getFullYear()
let month = date.getMonth()
const end = new Date(
new Date(`${year}-${month + 1}-1 00:00:00`).getTime() - 1,
)
if (month === 0) {
month = 12
year = year - 1
}
const start = new Date(
new Date(`${year}-${month}-1 00:00:00`).getTime(),
)
return [start, end]
},
},
{
text: '历史',
value: () => {
return ['', '']
},
},
],
}
function getStartTime() {
const date = new Date()
const year = date.getFullYear()
const month = date.getMonth() + 1
const day = date.getDate()
return `${year}-${month}-${day} 00:00:00`
}
interface Tree {
remark?: string
count?: number
code?: string
children?: Tree[]
}
const selectSku = ref('')
const treeData = ref<InterWarehouseTree[]>()
const [searchForm, resetSearchForm] = useValue<warehouseSearchForm>({})
const tradingTime = ref<string[]>([])
const selections = ref<InterWarehousePage[]>([])
const detailList = ref<InterWarehouseDetail[]>([])
const tabsValue = ref<string>('0')
const singleTableRef = ref<InstanceType<typeof ElTable>>()
const currentRow = ref<InterWarehousePage | null>(null)
const logList = ref<LogList[]>([])
const rules = {
warehouseId: [{ required: true, message: '请选择仓库', trigger: 'change' }],
}
const nodeId = ref<number | string>(10)
const treeRef = ref<InstanceType<typeof ElTree>>()
const {
currentPage,
pageSize,
total,
data: tableData,
refresh: search,
onCurrentPageChange: handleCurrentChange,
onPageSizeChange: handleSizeChange,
} = usePageList({
query: (page, pageSize) =>
warehouseInRecordListPage(
{
...searchForm.value,
// status: nodeId.value === -1 ? null : nodeId.value,
createTimeStart: tradingTime.value && tradingTime.value[0],
createTimeEnd: tradingTime.value && tradingTime.value[1],
},
page,
pageSize,
).then((res) => res.data) as never,
})
const setCostPrice = (item: InterProductList) => {
if (!item.costPrice) {
ElMessage.warning('商品成本价为空,请完善商品成本价')
return
}
const amount = new BigNumber(item.buyStored)
.multipliedBy(item.costPrice)
.toFixed(2)
item.totalPrice = Number(amount)
}
const getTreeNum = async () => {
try {
const res = await getWarehouseStatusTree()
res.data = [{ code: -1, name: '全部', children: res.data }]
treeData.value = res.data
await nextTick(() => {
treeRef.value!.setCurrentKey(nodeId.value, true)
})
} catch (e) {
console.error(e)
}
}
const getWarehouseList = async () => {
try {
const res = await getWarehouseListApi()
warehouseList.value = res.data
await nextTick(() => {
treeRef.value!.setCurrentKey(nodeId.value, true)
})
} catch (e) {
console.error(e)
}
}
const rowClick = (row: InterWarehousePage) => {
if (!row) {
currentRow.value = null
}
currentRow.value = row
tabsClick()
}
const skuData = ref<InterskuList[]>([])
const selectbySku = async () => {
if (!editForm.value.warehouseId) return ElMessage.error('请选择仓库')
try {
const res = await getBySku(editForm.value.warehouseId, selectSku.value)
skuData.value = res.data || []
fetchLocationList('') //获取该仓库下的所有库位
} catch (e) {
console.error(e)
}
}
const skudblclick = (val: InterskuList) => {
// 使用可选链和空值合并运算符处理可能的null值
const {
locationCode = '',
factoryPrice = 0,
productNo = '',
sku = '',
skuName = '',
image = '',
locationId = '',
} = val || {}
otherPurchaseData.value = [
...otherPurchaseData.value,
{
skuImage: image,
warehouseSku: sku,
skuName,
productNo,
locationCode: locationCode ?? '', // 确保空值处理
locationId: locationId ?? '', // 确保空值处理
costPrice: factoryPrice, ////添加时成本价字段是啥?要提交吗?
buyStored: null,
totalPrice: null,
},
]
// 使用filter代替forEach+splice,时间复杂度从O(n^2)降到O(n)
const skuSet = new Set(
otherPurchaseData.value.map((item: InterProductList) => item.warehouseSku),
)
skuData.value = skuData.value.filter(
(item: InterskuList) => !skuSet.has(item.sku),
)
}
const tabsClick = async () => {
if (!currentRow.value) {
detailList.value = []
logList.value = []
return
}
await nextTick()
if (tabsValue.value === '0') {
searchDetail()
} else if (tabsValue.value === '1') {
getLogList()
} else {
getLogisticsData()
}
}
const [editForm, resetEditForm] = useValue({
inNo: '',
warehouseId: '',
remark: '',
factoryCode: '',
factoryId: '',
})
const newDialogVisible = ref(false)
const editFormRef = ref()
const editForm2 = ref({})
const formId = ref<number | undefined>(undefined)
const otherPurchaseData = ref<InterProductList[]>([])
const addDialog = async (i: number, v: InterWarehousePage) => {
if (i === 2) {
if (v) formId.value = v.id
// getProduct(v.id)
if (!formId.value) return ElMessage('请勾选至少一条记录')
// v.inWarehouseId = v.warehouseId
// v.inType = v.type
// v.handleUserId = v.makerUserId
editForm.value = JSON.parse(JSON.stringify(v))
} else {
await nextTick()
editForm.value = JSON.parse(JSON.stringify(editForm2.value))
resetEditForm()
const userJson = localStorage.getItem('user')
if (userJson) {
try {
const userData = JSON.parse(userJson)
editForm.value.factoryCode = userData.factoryCode || ''
editForm.value.factoryId = userData.factoryId || 0
} catch {
// ignore
}
}
otherPurchaseData.value = []
formId.value = undefined
}
newDialogVisible.value = true
}
// const getProduct = (id: number) => {
// let url = `otherInWarehouseProductDetails/getProductList?otherOutWarehouseId=${id}`
// get(url).then((res) => {
// if (res.code == 200) {
// otherPurchaseData.value = res.data;
// productData.value = res.data;
// } else {
// this.$alert(res.message, this.$t('错误提示'), {
// dangerouslyUseHTMLString: true,
// })
// }
// })
// }
const handleSelectionChange = (v: InterWarehousePage[]) => {
selections.value = v
}
const productSelection = ref<InterProductList[]>([])
const productSelectionChange = (v: InterProductList[]) => {
productSelection.value = v
}
const auditOrder = (key: string) => {
let url = ''
let text = ''
switch (key) {
case 'pay':
url = 'reconciliation/payment'
text = '确认付款'
break
case 'archive':
url = 'reconciliation/archiving'
text = '确认归档'
break
}
if (selections.value.length === 0) {
return ElMessage.warning('请选择要操作的数据')
}
ElMessageBox.confirm(`${text}对账单?`, '重要提示', {
confirmButtonText: '确定',
type: 'warning',
}).then(async () => {
const ids = selections.value.map((el) => el.id).join(',')
await auditOrderApi(url, ids)
ElMessage.success('操作成功')
search()
await getTreeNum()
})
}
const rejectOrder = () => {
if (selections.value.length === 0) {
return ElMessage.warning('请选择要操作的数据')
}
ElMessageBox.prompt('请输入驳回原因', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
inputPattern: /.+/,
customClass: 'reject',
inputErrorMessage: '请输入驳回原因',
inputPlaceholder: '驳回原因',
}).then(async ({ value }) => {
const ids = selections.value.map((el) => el.id).join(',')
try {
await rejectOrderApi({ ids: ids, description: value, pass: 0 })
ElMessage.success('操作成功')
search()
await getTreeNum()
} catch (e) {
console.error(e)
}
})
}
watch(
() => tableData.value,
() => {
if (tableData.value && tableData.value.length > 0) {
if (singleTableRef.value) {
singleTableRef.value!.setCurrentRow(tableData.value[0])
currentRow.value = (tableData.value as never)[0]
}
} else {
detailList.value = []
}
},
{ immediate: true },
)
const addOtherCurrency = async () => {
try {
await editFormRef.value?.validate()
} catch {
return
}
const arr = otherPurchaseData.value
if (arr.length === 0) {
ElMessage.error('请至少选择一条数据')
return
}
for (let i = 0; i < arr.length; i++) {
if (!arr[i].buyStored) {
ElMessage.error('请输入入库数量')
return
}
if (!arr[i].locationId) {
ElMessage.error('请选择库位')
return
}
}
// 看新增后要不要打印标签
// try {
// if (!editId.value) {
// await addUserApi({
// ...editForm.value,
// supperMark: Number(editForm.value.supperMark),
// status: Number(editForm.value.status),
// })
// } else {
// await updateUserApi({
// ...editForm.value,
// supperMark: Number(editForm.value.supperMark),
// status: Number(editForm.value.status),
// })
// }
// ElMessage({
// message: '保存成功',
// type: 'success',
// offset: window.innerHeight / 2,
// })
// newDialogVisible.value = false
// search()
// } catch (e) {
// return
// }
if (!formId.value) {
addSection()
} else {
upSection()
}
}
const addSection = async () => {
const params = { ...editForm.value }
params.productList = otherPurchaseData.value
try {
await addInRecordApi(params)
ElMessage.success('保存成功')
newDialogVisible.value = false
search()
await getTreeNum()
} catch (e) {
console.error(e)
}
// post(url, params).then((res: any) => {
// if (res.code === 200) {
// otherDialogVisible.value = false;
// ElConfirm('保存成功,是否打印标签?', '提示', {
// confirmButtonText: '确定',
// cancelButtonText: '取消',
// type: 'warning',
// })
// .then(() => {
// if (res.message) {
// getPrintData(res.message, true);
// } else {
// ElMessage.warning('入口单号为空,无法打印');
// }
// })
// .catch((e: any) => {
// console.error(e);
// });
// getList();
// getStatusAmount();
// } else {
// ElAlert(res.message, '错误提示', {
// dangerouslyUseHTMLString: true,
// });
// }
// });
}
const upSection = async () => {
const params = { ...editForm.value }
params.productList = otherPurchaseData.value
try {
await updateInRecordApi(params)
newDialogVisible.value = false
ElMessage.success('修改成功')
search()
await getTreeNum()
} catch (e) {
console.error(e)
}
}
const addPurchase = async () => {}
const deleteOtherWarehousing = async () => {}
const importData = async () => {
if (!editForm.value.warehouseId) return ElMessage.error('请选择仓库')
}
const nodeClick = (data: Tree) => {
nodeId.value = data.code ?? ''
search()
}
const detailPager = ref({
page: 1,
rows: 100,
total: 0,
})
const searchDetail = async () => {
try {
const res = await getWarehouseInRecordDetail(currentRow.value?.id)
detailList.value = res.data.records || []
detailPager.value.total = res.data.total
} catch (e) {
console.error(e)
}
}
const getLogList = async () => {
try {
const res = await getLogListApi(currentRow.value?.id)
logList.value = res.data
} catch (e) {
console.error(e)
}
}
const logisticsData = ref([])
const getLogisticsData = async () => {
// try {
// const res = await getLogisticsDataApi(currentRow.value?.id)
// logisticsData.value = res.data
// } catch (e) {
// console.error(e)
// }
}
const importDialogVisible = ref(false)
const importedFileUrl = ref('')
const importExcel = () => {
importDialogVisible.value = true
}
const downloadTemplate = () => {
// TODO: 实现下载模板功能
window.open('/api/template/download')
}
const exportExcel = async () => {
if (selections.value.length === 0) {
return ElMessage.warning('请选择要操作的数据')
}
const ids = selections.value.map((el) => el.id).join(',')
try {
const res = await exportExcelApi(ids)
window.open(res.data)
} catch (e) {
console.error(e)
}
}
const locationList = ref<ILocation[]>([])
const locationLoading = ref(false)
const fetchLocationList = async (query: string) => {
// if (!query) {
// locationList.value = []
// return
// }
locationLoading.value = true
try {
const res = await getByWareHouseIdAndCode(editForm.value.warehouseId, query)
const result = res.data || []
locationList.value = result.map((item: ILocation) => {
return {
locationId: item.id,
locationCode: item.locationCode,
}
})
} catch (e) {
locationList.value = []
} finally {
locationLoading.value = false
}
}
// 输入2秒后再调用接口(节流)
const handleLocationSearch = debounce(fetchLocationList, 2000)
const handleLocationChange = (val: number, row: InterProductList) => {
const found = locationList.value.find(
(item: InterProductList) => item.locationId === val,
)
row.locationCode = found ? found.locationCode : ''
}
onMounted(() => {
getTreeNum()
getWarehouseList()
})
</script>
<style lang="scss" scoped>
.dialog-footer {
display: inline-block;
width: 500px;
text-align: center;
}
.header-filter-form {
:deep(.el-form-item) {
margin-right: 14px;
margin-bottom: 10px;
}
}
.product-dialog-footer {
display: flex;
justify-content: space-between;
margin: 8px 0;
}
$border: solid 1px #ddd;
.send-order-list {
display: grid;
grid-template-columns: 2fr 1fr;
border-left: $border;
border-top: $border;
}
.send-order-column {
padding: 10px 16px;
line-height: 1.5;
}
.send-order-header {
font-weight: bold;
text-align: center;
background-color: #f8f8f9;
}
.send-order-product-item {
display: flex;
justify-content: space-between;
gap: 20px;
&:not(:first-child) {
border-top: $border;
padding: 10px 0;
}
.send-order-prop-list {
flex: 1;
}
}
.send-order-product-image {
width: 100px;
}
.delivery-note-list {
:deep(.vertical-align-top) {
vertical-align: top;
}
}
.left {
width: 160px;
:deep(.el-tree-node__content) {
height: 30px;
line-height: 30px;
}
:deep(.el-tree-node__label) {
font-size: 13px;
cursor: pointer;
display: inline-block;
width: 100%;
color: black !important;
padding: 3px 7px;
}
:deep(.el-tree-node__expand-icon) {
display: none;
}
:deep(.is-current) {
.tree-node-label,
.tree-node-count {
background-color: #ecf5ff;
color: #409eff !important;
}
.el-tree-node__children {
.tree-node-label,
.tree-node-count {
background-color: transparent !important;
color: black !important;
}
}
}
}
.tree-node {
display: flex;
color: #333;
font-weight: 500;
}
.right {
flex: 1;
flex-shrink: 0;
background: white;
overflow: hidden;
}
::v-deep(.el-tree-node__label) {
font-size: 13px;
color: rgb(96, 98, 102);
cursor: pointer;
}
::v-deep(.el-tree-node__expand-icon) {
display: none;
}
::v-deep(.el-tree-node__label) {
display: inline-block;
width: 100%;
color: black !important;
padding: 3px 7px;
}
::v-deep(.is-current) {
.el-tree-node__label {
background-color: #ecf5ff;
color: #409eff !important;
}
.el-tree-node__children {
.el-tree-node__label {
background-color: transparent !important;
color: black !important;
}
}
}
::v-deep(.splitpanes__pane) {
display: flex;
flex-direction: column;
}
::v-deep(.splitpanes--horizontal > .splitpanes__splitter) {
min-height: 5px;
margin-top: 10px;
}
.draw-line {
width: 100%;
height: 5px;
background: #eff3f6;
}
.btn-list {
margin-bottom: 10px;
}
::v-deep(.el-tree-node) {
cursor: pointer;
margin-bottom: 5px;
}
::v-deep(.el-tree-node__label) {
font-size: 14px;
}
::v-deep(.el-tabs) {
display: flex;
flex-direction: column;
height: 100%;
.el-tabs__content {
flex: 1;
flex-shrink: 0;
overflow: hidden;
.el-tab-pane {
height: 100%;
display: flex;
flex-direction: column;
overflow: hidden;
}
}
}
.delivery-note-page {
:deep(#top) {
height: 100%;
}
}
.import-dialog {
.import-header {
display: flex;
justify-content: flex-end;
}
.import-content {
padding: 20px 0;
}
}
.import-success {
text-align: center;
padding: 20px 0;
.success-icon {
font-size: 48px;
margin-bottom: 16px;
}
.success-text {
font-size: 16px;
color: #67c23a;
margin-bottom: 16px;
}
.file-link {
a {
color: #409eff;
text-decoration: none;
&:hover {
text-decoration: underline;
}
}
}
}
</style>
<script setup lang="ts">
import {
getFactoryWarehouseInfo,
factoryWarehouseInfo,
warehouseInfo,
updateWarehouseApi,
deleteWarehouseInventory,
createWarehouseInventoryApi,
updateWarehouseInventoryApi,
} from '@/api/warehouse.ts'
import { nextTick, ref } from 'vue'
const selections = ref<warehouseInfo[]>([])
const formRef = ref()
const createData = ref({
title: '',
show: false,
isEdit: false,
form: {
name: '',
code: '',
sort: '',
defaulted: 1,
remarks: '',
},
})
const rules = {
name: [
{ required: true, message: '请输入仓库名称', trigger: 'blur' },
],
code: [
{ required: true, message: '请输入仓库编码', trigger: 'blur' },
],
defaulted: [
{ required: true, message: '请选择是否默认仓库', trigger: 'change' },
],
}
const leftData = ref<warehouseInfo[]>([])
const pagination = ref<factoryWarehouseInfo>({
pageSize: 50,
currentPage: 1,
total: 0,
})
async function getData() {
const res = await getFactoryWarehouseInfo({
...pagination.value,
})
leftData.value = res.data.records
pagination.value.total = res.data.total
}
const handleSelectionChange = (data: warehouseInfo[]) => {
selections.value = data
}
const handleSizeChange = (pageSize: number) => {
pagination.value.pageSize = pageSize
getData()
}
const handleCurrentChange = (currentPage: number) => {
pagination.value.currentPage = currentPage
getData()
}
const handleConfirm = async () => {
await formRef.value?.validate()
if (!createData.value.isEdit) {
await createWarehouseInventoryApi(createData.value.form)
} else {
await updateWarehouseInventoryApi(createData.value.form)
}
createData.value.show = false
ElMessage.success('操作成功')
await getData()
}
const handleBatchDelete = async () => {
if (!selections.value.length) {
return ElMessage.warning('请选择要删除的数据')
}
await ElMessageBox.confirm('确定要删除吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
})
const str = selections.value.map((el) => el.id).join(',')
await deleteWarehouseInventory(str)
ElMessage.success('删除成功')
await getData()
}
const createWarehouse = () => {
createData.value.show = true
createData.value.isEdit = false
createData.value.title = '新增仓库'
createData.value.form = {
name: '',
code: '',
sort: '',
defaulted: 1,
remarks: '',
}
nextTick(() => {
formRef.value?.clearValidate()
})
}
const updateDefaulted = async (item: warehouseInfo) => {
await updateWarehouseApi({
id: item.id,
defaulted: item.defaulted,
})
}
const updateWarehouse = (item: warehouseInfo) => {
createData.value.show = true
createData.value.isEdit = true
createData.value.title = '修改仓库'
createData.value.form = { ...item }
nextTick(() => {
formRef.value?.clearValidate()
})
}
getData()
</script>
<template>
<el-card style="height: 100%">
<div class="manage">
<div class="header">
<el-button type="success" @click="createWarehouse">新增仓库</el-button>
<el-button type="danger" @click="handleBatchDelete">删除</el-button>
</div>
<div class="table-flex">
<div class="left-table">
<div class="table-container">
<el-table height="100%" :data="leftData" border @selection-change="handleSelectionChange">
<el-table-column type="selection" />
<el-table-column type="index" label="序号" width="60" />
<el-table-column label="仓库名称" prop="name" align="center" />
<el-table-column label="仓库类型" prop="name" align="center" />
<el-table-column label="仓库编码" prop="code" align="center" />
<el-table-column label="仓库地址" prop="name" align="center" />
<el-table-column label="备注" prop="remarks" align="center" />
<el-table-column label="默认仓库" prop="defaulted" align="center">
<template #default="{row}">
<el-switch
v-model="row.defaulted" :active-value="1" :inactive-value="0"
@click="updateDefaulted(row)"></el-switch>
</template>
</el-table-column>
<el-table-column label="操作" align="center">
<template #default="{row}">
<el-button type="primary" @click="updateWarehouse(row)">编辑</el-button>
</template>
</el-table-column>
</el-table>
</div>
<div class="pagination">
<el-pagination
v-model:current-page="pagination.currentPage"
v-model:page-size="pagination.pageSize"
:page-sizes="[50, 100, 150, 200]"
layout="total, sizes, prev, pager, next, jumper"
:total="pagination.total"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
/>
</div>
</div>
</div>
<el-dialog v-model="createData.show" :title="createData.title">
<el-form ref="formRef" label-width="110px" :rules="rules" :model="createData.form">
<el-form-item label="仓库名称" prop="name">
<el-input v-model="createData.form.name" clearable placeholder="请输入仓库名称"></el-input>
</el-form-item>
<el-form-item label="仓库编码" prop="code">
<el-input v-model="createData.form.code" clearable placeholder="请输入仓库编码"></el-input>
</el-form-item>
<el-form-item label="仓库序号" prop="sort">
<el-input-number v-model="createData.form.sort"></el-input-number>
</el-form-item>
<el-form-item label="是否默认仓库" prop="defaulted">
<el-switch v-model="createData.form.defaulted" :active-value="1" :inactive-value="0"></el-switch>
</el-form-item>
<el-form-item label="备注" prop="remarks">
<el-input
v-model="createData.form.remarks" type="textarea" :rows="4" clearable
placeholder="请输入备注"></el-input>
</el-form-item>
</el-form>
<template #footer>
<el-button @click="createData.show=false">取消</el-button>
<el-button type="primary" @click="handleConfirm">确定</el-button>
</template>
</el-dialog>
</div>
</el-card>
</template>
<style scoped lang="scss">
.el-card {
::v-deep(.el-card__body) {
height: 100%;
}
}
.manage {
height: 100%;
display: flex;
flex-direction: column;
.header {
margin-bottom: 10px;
}
.table-flex {
flex: 1;
flex-shrink: 0;
overflow: hidden;
display: flex;
}
.right-table {
flex: 1;
margin-left: 10px;
flex-shrink: 0;
}
.left-table {
height: 100%;
display: flex;
width: 100%;
flex-direction: column;
.pagination {
display: flex;
margin-top: 10px;
justify-content: center;
}
.table-container {
flex: 1;
flex-shrink: 0;
overflow: hidden;
}
}
}
</style>
......@@ -7,6 +7,15 @@ import vue from '@vitejs/plugin-vue'
// https://vitejs.dev/config/
export default defineConfig({
base: '/',
server: {
port: 9803,
host: true,
proxy: {
"/api": {
target: "http://10.168.31.230:8060"
}
}
},
plugins: [
vue(),
AutoImport({
......
// vite.config.ts
import { defineConfig } from "file:///C:/work/factory_front/node_modules/vite/dist/node/index.js";
import AutoImport from "file:///C:/work/factory_front/node_modules/unplugin-auto-import/dist/vite.js";
import Components from "file:///C:/work/factory_front/node_modules/unplugin-vue-components/dist/vite.js";
import { ElementPlusResolver } from "file:///C:/work/factory_front/node_modules/unplugin-vue-components/dist/resolvers.js";
import vue from "file:///C:/work/factory_front/node_modules/@vitejs/plugin-vue/dist/index.mjs";
var vite_config_default = defineConfig({
base: "./",
server: {
port: 9803,
host: true,
proxy: {
"/api": {
target: "http://192.168.31.40:8060"
}
}
},
plugins: [
vue(),
AutoImport({
resolvers: [ElementPlusResolver()]
}),
Components({
resolvers: [ElementPlusResolver()]
})
],
resolve: {
alias: { "@": "/src" }
}
});
export {
vite_config_default as default
};
//# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsidml0ZS5jb25maWcudHMiXSwKICAic291cmNlc0NvbnRlbnQiOiBbImNvbnN0IF9fdml0ZV9pbmplY3RlZF9vcmlnaW5hbF9kaXJuYW1lID0gXCJDOlxcXFx3b3JrXFxcXGZhY3RvcnlfZnJvbnRcIjtjb25zdCBfX3ZpdGVfaW5qZWN0ZWRfb3JpZ2luYWxfZmlsZW5hbWUgPSBcIkM6XFxcXHdvcmtcXFxcZmFjdG9yeV9mcm9udFxcXFx2aXRlLmNvbmZpZy50c1wiO2NvbnN0IF9fdml0ZV9pbmplY3RlZF9vcmlnaW5hbF9pbXBvcnRfbWV0YV91cmwgPSBcImZpbGU6Ly8vQzovd29yay9mYWN0b3J5X2Zyb250L3ZpdGUuY29uZmlnLnRzXCI7aW1wb3J0IHsgZGVmaW5lQ29uZmlnIH0gZnJvbSAndml0ZSdcbmltcG9ydCBBdXRvSW1wb3J0IGZyb20gJ3VucGx1Z2luLWF1dG8taW1wb3J0L3ZpdGUnXG5pbXBvcnQgQ29tcG9uZW50cyBmcm9tICd1bnBsdWdpbi12dWUtY29tcG9uZW50cy92aXRlJ1xuaW1wb3J0IHsgRWxlbWVudFBsdXNSZXNvbHZlciB9IGZyb20gJ3VucGx1Z2luLXZ1ZS1jb21wb25lbnRzL3Jlc29sdmVycydcbmltcG9ydCB2dWUgZnJvbSAnQHZpdGVqcy9wbHVnaW4tdnVlJ1xuXG4vLyBodHRwczovL3ZpdGVqcy5kZXYvY29uZmlnL1xuZXhwb3J0IGRlZmF1bHQgZGVmaW5lQ29uZmlnKHtcbiAgYmFzZTogJy4vJyxcbiAgc2VydmVyOiB7XG4gICAgcG9ydDogOTgwMyxcbiAgICBob3N0OiB0cnVlLFxuICAgIHByb3h5OiB7XG4gICAgICAnL2FwaSc6IHtcbiAgICAgICAgdGFyZ2V0OiAnaHR0cDovLzE5Mi4xNjguMzEuNDA6ODA2MCcsXG4gICAgICB9LFxuICAgIH0sXG4gIH0sXG4gIHBsdWdpbnM6IFtcbiAgICB2dWUoKSxcbiAgICBBdXRvSW1wb3J0KHtcbiAgICAgIHJlc29sdmVyczogW0VsZW1lbnRQbHVzUmVzb2x2ZXIoKV0sXG4gICAgfSksXG4gICAgQ29tcG9uZW50cyh7XG4gICAgICByZXNvbHZlcnM6IFtFbGVtZW50UGx1c1Jlc29sdmVyKCldLFxuICAgIH0pLFxuICBdLFxuICByZXNvbHZlOiB7XG4gICAgYWxpYXM6IHsgJ0AnOiAnL3NyYycgfSxcbiAgfSxcbn0pXG4iXSwKICAibWFwcGluZ3MiOiAiO0FBQXVQLFNBQVMsb0JBQW9CO0FBQ3BSLE9BQU8sZ0JBQWdCO0FBQ3ZCLE9BQU8sZ0JBQWdCO0FBQ3ZCLFNBQVMsMkJBQTJCO0FBQ3BDLE9BQU8sU0FBUztBQUdoQixJQUFPLHNCQUFRLGFBQWE7QUFBQSxFQUMxQixNQUFNO0FBQUEsRUFDTixRQUFRO0FBQUEsSUFDTixNQUFNO0FBQUEsSUFDTixNQUFNO0FBQUEsSUFDTixPQUFPO0FBQUEsTUFDTCxRQUFRO0FBQUEsUUFDTixRQUFRO0FBQUEsTUFDVjtBQUFBLElBQ0Y7QUFBQSxFQUNGO0FBQUEsRUFDQSxTQUFTO0FBQUEsSUFDUCxJQUFJO0FBQUEsSUFDSixXQUFXO0FBQUEsTUFDVCxXQUFXLENBQUMsb0JBQW9CLENBQUM7QUFBQSxJQUNuQyxDQUFDO0FBQUEsSUFDRCxXQUFXO0FBQUEsTUFDVCxXQUFXLENBQUMsb0JBQW9CLENBQUM7QUFBQSxJQUNuQyxDQUFDO0FBQUEsRUFDSDtBQUFBLEVBQ0EsU0FBUztBQUFBLElBQ1AsT0FBTyxFQUFFLEtBQUssT0FBTztBQUFBLEVBQ3ZCO0FBQ0YsQ0FBQzsiLAogICJuYW1lcyI6IFtdCn0K
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