出入库导入导出修改、物资盘点显示和导出修改、添加点踩理由显示、
property-only-app
zxf 1 month ago
parent d56c5394e8
commit e3aaa6c161

@ -38,6 +38,7 @@
},
"dependencies": {
"@babel/parser": "^7.9.6",
"@element-plus/icons-vue": "^2.3.2",
"@riophae/vue-treeselect": "0.4.0",
"async-validator": "^1.11.2",
"axios": "^0.24.0",

@ -42,9 +42,8 @@ export function ckbillcargoDetailApi(id) {
*/
export function ckbillcargoDeleteApi(ids) {
return request({
url: `autogencode/ckbillcargo/delete`,
method: 'POST',
data: ids
url: `autogencode/ckbillcargo/` + ids.join(','),
method: 'DELETE'
})
}
@ -99,6 +98,15 @@ export function selectTotalWt(query) {
})
}
// 查询出库合计(数量和金额)
export function selectTotal(query) {
return request({
url: 'autogencode/ckbillcargo/selectTotal',
method: 'get',
params: query
})
}
export function recallOutBill(id) {
return request({
url: 'autogencode/ckbillcargo/recallOutBill/' + id,

@ -61,3 +61,15 @@ export function ckstockchangeListApi(params) {
})
}
/**
* 物资盘点表查询
* @param data 查询条件
*/
export function inventoryListApi(data) {
return request({
url: `autogencode/ckstockchange/inventory`,
method: 'POST',
data
})
}

@ -144,3 +144,42 @@ export function pmdailymenuListByAll(params) {
})
}
/**
* pmdailymenu导出
* @param params
*/
export function pmdailymenuExportApi(params) {
return request({
url: `autogencode/pmdailymenu/export`,
method: 'GET',
params,
responseType: 'blob'
})
}
/**
* pmdailymenu导入
* @param formData
*/
export function pmdailymenuImportApi(formData) {
return request({
url: `autogencode/pmdailymenu/import`,
method: 'POST',
data: formData,
headers: {
'Content-Type': 'multipart/form-data'
}
})
}
/**
* pmdailymenu导入模板下载
*/
export function pmdailymenuImportTemplateApi() {
return request({
url: `autogencode/pmdailymenu/import/template`,
method: 'GET',
responseType: 'blob'
})
}

@ -65,14 +65,18 @@ export function pmdailymenudtllikeListApi(params) {
* 对菜单明细进行点赞/点踩
* @param menuDtlId 菜单明细ID
* @param likeType 评价类型1-点赞2-点踩
* @param remark 备注可选
* @param uid 手机端用户ID可选
*/
export function pmdailymenudtllikeLikeApi(menuDtlId, likeType) {
export function pmdailymenudtllikeLikeApi(menuDtlId, likeType, remark, uid) {
return request({
url: `autogencode/pmdailymenudtllike/like`,
method: 'POST',
params: {
menuDtlId,
likeType
likeType,
remark,
uid
}
})
}

@ -73,3 +73,15 @@ export function pmproductclassTreeSelect(params) {
})
}
/**
* 获取商品类别列表
* @param params
*/
export function listProductClass(params) {
return request({
url: `autogencode/pmproductclass/list`,
method: 'GET',
params
})
}

@ -63,7 +63,8 @@ export function adminAdd(pram) {
status: pram.status,
phone: pram.phone,
depts: pram.depts,
posts: pram.posts
posts: pram.posts,
isDefaultReceiver: pram.isDefaultReceiver
}
return request({
url: '/admin/system/admin/save',
@ -84,7 +85,8 @@ export function adminUpdate(pram) {
isDel: pram.isDel,
phone: pram.phone,
depts: pram.depts,
posts: pram.posts
posts: pram.posts,
isDefaultReceiver: pram.isDefaultReceiver
}
return request({
url: '/admin/system/admin/update',
@ -204,4 +206,4 @@ export function updateIsSmsApi(params) {
method: 'get',
params
})
}
}

@ -1,64 +1,57 @@
/**
* v-dialogDrag 弹窗拖拽
* Copyright (c) 2019 ruoyi
*/
* v-dialogDrag 弹窗拖拽终极稳定版
* 解决初始居中 + 按下闪烁偏移错误飞到右下角
*/
export default {
bind(el, binding, vnode, oldVnode) {
bind(el, binding) {
const value = binding.value
if (value == false) return
// 获取拖拽内容头部
const dialogHeaderEl = el.querySelector('.el-dialog__header');
const dragDom = el.querySelector('.el-dialog');
dialogHeaderEl.style.cursor = 'move';
// 获取原有属性 ie dom元素.currentStyle 火狐谷歌 window.getComputedStyle(dom元素, null);
const sty = dragDom.currentStyle || window.getComputedStyle(dragDom, null);
dragDom.style.position = 'absolute';
dragDom.style.marginTop = 0;
let width = dragDom.style.width;
if (width.includes('%')) {
width = +document.body.clientWidth * (+width.replace(/\%/g, '') / 100);
} else {
width = +width.replace(/\px/g, '');
}
dragDom.style.left = `${(document.body.clientWidth - width) / 2}px`;
// 鼠标按下事件
dialogHeaderEl.onmousedown = (e) => {
// 鼠标按下,计算当前元素距离可视区的距离 (鼠标点击位置距离可视窗口的距离)
const disX = e.clientX - dialogHeaderEl.offsetLeft;
const disY = e.clientY - dialogHeaderEl.offsetTop;
if (value === false) return
// 获取到的值带px 正则匹配替换
let styL, styT;
const dialogHeaderEl = el.querySelector('.el-dialog__header')
const dragDom = el.querySelector('.el-dialog')
// 注意在ie中 第一次获取到的值为组件自带50% 移动之后赋值为px
if (sty.left.includes('%')) {
styL = +document.body.clientWidth * (+sty.left.replace(/\%/g, '') / 100);
styT = +document.body.clientHeight * (+sty.top.replace(/\%/g, '') / 100);
} else {
styL = +sty.left.replace(/\px/g, '');
styT = +sty.top.replace(/\px/g, '');
};
if (!dialogHeaderEl || !dragDom) return
// 鼠标拖拽事件
document.onmousemove = function (e) {
// 通过事件委托,计算移动的距离 (开始拖拽至结束拖拽的距离)
const l = e.clientX - disX;
const t = e.clientY - disY;
dialogHeaderEl.style.cursor = 'move'
let finallyL = l + styL
let finallyT = t + styT
// 初始化:绝对居中
dragDom.style.position = 'fixed'
dragDom.style.margin = '0'
dragDom.style.left = '50%'
dragDom.style.top = '50%'
dragDom.style.transform = 'translate(-50%, -50%)'
// 移动当前元素
dragDom.style.left = `${finallyL}px`;
dragDom.style.top = `${finallyT}px`;
};
document.onmouseup = function (e) {
document.onmousemove = null;
document.onmouseup = null;
};
// 鼠标按下
dialogHeaderEl.onmousedown = (e) => {
e.preventDefault()
// 先拿到当前弹窗真实的 left / top去掉 translate 影响)
const currLeft = dragDom.getBoundingClientRect().left
const currTop = dragDom.getBoundingClientRect().top
// 按下瞬间立刻清除居中,重新设置真实 left top
dragDom.style.transform = 'none'
dragDom.style.left = currLeft + 'px'
dragDom.style.top = currTop + 'px'
// 计算正确偏移(这是唯一不飞的算法)
const disX = e.clientX - currLeft
const disY = e.clientY - currTop
// 移动
document.onmousemove = (e) => {
let left = e.clientX - disX
let top = e.clientY - disY
dragDom.style.left = left + 'px'
dragDom.style.top = top + 'px'
}
// 抬起
document.onmouseup = () => {
document.onmousemove = null
document.onmouseup = null
}
}
}
};
}

@ -170,11 +170,11 @@ export default {
},
getList() {
this.list = [];
if (!this.custId) return;
this.loading = true;
listCkstock({
custId: this.custId,
custId: this.custId || '',
gdsSeqno: this.searchGdsSeqno,
isShow: false, // 0
}).then(res => {
this.loading = false;
this.list = res || [];

@ -206,17 +206,31 @@
</template>
</el-table-column>
<el-table-column label="单价" prop="unitPrice">
<template slot-scope="scope">
<div class="nomal-text">{{ scope.row.unitPrice || 0 }}</div>
</template>
</el-table-column>
<el-table-column label="领用数量" prop="cargoWt">
<template slot-scope="scope">
<el-form-item class="margin-none" :prop="'ckBillCargos.' + scope.$index + '.cargoWt'"
:rules="rules.cargoWt">
<div v-if="tableReadonly" class="nomal-text">{{ scope.row.cargoWt }}</div>
<el-input-number style="width: 100%" controls-position="right" v-else v-model="scope.row.cargoWt" :min="0"
:precision="2" :max="getMaxData(scope.row).cargoWt" placeholder="输入数量" />
:precision="2" :max="getMaxData(scope.row).cargoWt" placeholder="输入数量" @change="onCargoWtChange(scope.row)" />
</el-form-item>
</template>
</el-table-column>
<el-table-column label="金额" prop="cargoValue">
<template slot-scope="scope">
<div v-if="tableReadonly" class="nomal-text">{{ scope.row && getCargoValue(scope.row) }}</div>
<el-input v-else v-model.number="scope.row.cargoValue" type="number" style="width: 100%" placeholder="输入金额"
:max="getMaxCargoValue(scope.row)" @input="onCargoValueInput(scope.row, $event)" />
</template>
</el-table-column>
</el-table>
</el-form>
@ -323,9 +337,7 @@ export default {
billNumber: [
{ required: true, message: '单据编号不能为空', trigger: 'blur' }
],
custId: [
{ required: true, message: '供应商不能为空', trigger: 'blur' }
],
custId: [],
cargoName: [
{ required: true, message: '商品名称不能为空', trigger: 'blur' }
],
@ -407,11 +419,34 @@ export default {
// this.getVesselList();
},
methods: {
// getCargoValue(row){
// const value = row.unitPrice * row.cargoWt;
// row.cargoValue = isNaN(value) ? 0 : value.toFixed(4);
// return row.cargoValue;
// },
getCargoValue(row){
if (!row) return 0;
const value = (row.unitPrice || 0) * (row.cargoWt || 0);
row.cargoValue = isNaN(value) ? 0 : value.toFixed(4);
return row.cargoValue;
},
//
onCargoWtChange(row) {
if (row) {
const value = (row.unitPrice || 0) * (row.cargoWt || 0);
row.cargoValue = isNaN(value) ? 0 : value.toFixed(4);
}
},
// ×
getMaxCargoValue(row) {
if (!row) return Infinity;
const maxWt = this.getMaxData(row).cargoWt;
const value = (row.unitPrice || 0) * maxWt;
return isNaN(value) ? Infinity : value;
},
//
onCargoValueInput(row, event) {
const value = parseFloat(event);
const maxValue = this.getMaxCargoValue(row);
if (!isNaN(value) && value > maxValue) {
row.cargoValue = maxValue.toFixed(4);
}
},
init() {
this.getCustList();
this.getMenuList();
@ -838,10 +873,6 @@ export default {
});
},
addStockOut() {
if (!this.form.custId) {
this.$message('请先选择供应商');
return;
}
this.open = true;
},
//
@ -908,11 +939,9 @@ export default {
},
//
getStockList() {
if (!this.form.custId) {
return;
}
listCkstock({
custId: this.form.custId,
custId: this.form.custId || '',
isShow: false, // 0
}).then(res => {
this.stockList = res || [];
}).catch(e => {

@ -3,13 +3,40 @@
<!-- 搜索部分 -->
<el-header height="auto" style="padding: 10px 0; background-color: #fff; border-bottom: 1px solid #eaeaea;">
<SearchBlock v-model="billQuery" v-show="showSearch" size="mini" :menus="queryOption" @change="handleQuery"></SearchBlock>
<el-col :span="1.5">
<el-button
type="info"
plain
icon="el-icon-upload2"
size="small"
@click="handleImport"
>导入
</el-button>
</el-col>
<el-col :span="1.5">
<el-button
type="warning"
plain
icon="el-icon-download"
size="small"
@click="handleExport"
>导出
</el-button>
</el-col>
</el-header>
<el-container>
<!-- 左侧单据部分 -->
<el-aside width="300px" style="border-right: 1px solid #eaeaea; padding: 10px;max-height: 90vh;">
<h3 style="margin: 0 0 10px 0; font-size: 16px; font-weight: 500;">单据列表</h3>
<BillList ref="billList" billType="2" :params="billQuery" :showAuditStatus="true" @rowClick="billClick"></BillList>
<el-aside :width="isCollapse ? '60px' : '300px'"
style="border-right: 1px solid #eaeaea; padding: 10px;max-height: 90vh; transition: width 0.3s; position: relative;">
<div class="collapse-btn" @click="isCollapse = !isCollapse"
style="position: absolute; right: 10px; top: 10px; background: #fff; border: 1px solid #eaeaea; padding: 3px 6px; cursor: pointer; z-index: 10; border-radius: 2px;">
<span v-if="isCollapse"></span>
<span v-else></span>
</div>
<h3 v-if="!isCollapse" style="margin: 0 0 10px 0; font-size: 16px; font-weight: 500; padding-right: 30px;">
单据列表</h3>
<BillList v-if="!isCollapse" ref="billList" billType="2" :params="billQuery" :showAuditStatus="true" @rowClick="billClick"></BillList>
</el-aside>
<!-- 右侧商品部分 -->
@ -120,6 +147,8 @@
</el-table-column>
<el-table-column label="商品编号" align="center" prop="hsCode" sortable='custom' min-width="100" />
<el-table-column label="商品数量" align="center" prop="cargoWt" sortable='custom' min-width="100" />
<el-table-column label="单价" align="center" prop="unitPrice" sortable='custom' min-width="100" />
<el-table-column label="金额" align="center" prop="cargoValue" sortable='custom' min-width="120" />
<el-table-column label="规格类型品质" show-overflow-tooltip align="center" prop="cargoSpec" sortable='custom' min-width="200" />
<el-table-column label="有效期至" align="center" prop="expiryDate" min-width="180" sortable='custom'>
</el-table-column>
@ -128,7 +157,7 @@
<el-table-column label="单据编号" align="center" prop="billNumber" sortable='custom' min-width="100" />
<el-table-column label="源单据" align="center" prop="outBillNumber" sortable='custom' min-width="100" />
<el-table-column label="操作" fixed="right" align="center" class-name="small-padding fixed-width" width="120px">
<el-table-column label="操作" fixed="right" align="center" class-name="small-padding fixed-width" width="180px">
<template slot-scope="scope">
<el-button size="mini" type="text" icon="el-icon-check" @click="showDetail(scope.row)"
v-hasPermi="['jxc:ckcargo:edit']">查看
@ -136,6 +165,9 @@
<el-button size="mini" type="text" v-if="scope.row.billStatus=='0'&&scope.row.sourceType=='1'&&scope.row.cancelStatus!='1'" icon="el-icon-edit" @click="handleUpdate(scope.row)"
v-hasPermi="['jxc:ckcargo:edit']">修改
</el-button>
<el-button size="mini" type="text" v-if="scope.row.billStatus=='0'&&scope.row.sourceType=='1'&&scope.row.cancelStatus!='1'" icon="el-icon-delete" @click="handleDelete(scope.row)"
v-hasPermi="['jxc:ckcargo:remove']">删除
</el-button>
</template>
</el-table-column>
</el-table>
@ -148,6 +180,38 @@
<StockOut ref="stockOut" :readonly="readonly" :modifyData="modifyData" :billInfo="selectedBill" @close="handleClose"
@refresh="refreshList"></StockOut>
</el-dialog>
<!-- 导入对话框 -->
<el-dialog :title="upload.title" :visible.sync="upload.open" width="400px" append-to-body>
<el-upload
ref="upload"
:limit="1"
accept=".xlsx, .xls"
:headers="upload.headers"
:action="''"
:disabled="upload.isUploading"
:on-progress="handleFileUploadProgress"
:on-success="handleFileSuccess"
:auto-upload="false"
:http-request="uploadFile"
drag
>
<i class="el-icon-upload"></i>
<div class="el-upload__text">将文件拖到此处<em>点击上传</em></div>
<div class="el-upload__tip text-center" slot="tip">
<div class="el-upload__tip" slot="tip">
<el-checkbox v-model="upload.updateSupport"/>
是否更新已经存在的数据
</div>
<span>仅允许导入xlsxlsx格式文件</span>
<el-link type="primary" :underline="false" style="font-size:12px;vertical-align: baseline;" @click="importTemplate"></el-link>
</div>
</el-upload>
<div slot="footer" class="dialog-footer">
<el-button type="primary" @click="submitFileForm"> </el-button>
<el-button @click="upload.open = false"> </el-button>
</div>
</el-dialog>
</el-main>
</el-container>
</el-container>
@ -172,6 +236,7 @@ import { ckbillDeleteApi as delCkbill, auditBill, cancelAuditBill, rejectBill} f
import SearchBlock from '@/components/SearchBlock';
// import { listVessel } from "@/api/jxc/vessel";
import { pmdailymenuListByAll } from "@/api/pmdailymenu.js";
import request from '@/utils/request';
export default {
name: 'Ckoutbound',
@ -341,6 +406,25 @@ export default {
readonly: false,
//
form: {},
//
upload: {
//
open: false,
//
title: "出库信息导入",
//
isUploading: false,
//
updateSupport: 0,
//
headers: {
Authorization: ""
},
//
fileList: []
},
//
isCollapse: false,
}
},
created() {
@ -358,20 +442,37 @@ export default {
// });
// },
//
billClick(row) {
const isChange = this.selectedBill.id !== row.id;
this.selectedBill = row || {};
this.queryParams.billNumber = this.selectedBill.billNumber;
//
if (isChange) {
billClick(row, isAutoRestore = false) {
if (row && Object.keys(row).length > 0) {
if (isAutoRestore) {
// selectedBill
this.selectedBill = row || {};
this.queryParams.pageNum = 1;
this.queryParams.billNumber = this.selectedBill.billNumber;
//
this.getList();
} else {
const isChange = this.selectedBill.id !== row.id;
this.selectedBill = row || {};
this.queryParams.billNumber = this.selectedBill.billNumber;
//
if (isChange) {
this.queryParams.pageNum = 1;
}
if (this.queryParams.billNumber) {
this.getList();
} else {
this.ckcargoList = [];
}
}
} else if (!row || Object.keys(row).length === 0) {
//
this.selectedBill = {};
this.queryParams.pageNum = 1;
}
if (this.queryParams.billNumber) {
this.queryParams.billNumber = null;
//
this.getList();
} else {
this.ckcargoList = [];
}
},
handleClose(show) {
if (!show) {
@ -455,6 +556,9 @@ export default {
this.handleQuery()
},
backBill() {
// ID
const selectedBillId = this.selectedBill.id;
const billNumber = this.selectedBill.billNumber;
this.$confirm(`确定撤销当前单据吗?`, '温馨提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
@ -467,14 +571,27 @@ export default {
message: '撤销成功!',
type: 'success'
});
// selectedBill使
this.selectedBill.billStatus = '0';
this.loading = false;
//
this.refreshList();
//
this.$nextTick(() => {
if (billNumber) {
this.queryParams.billNumber = billNumber;
this.getList();
}
});
}).catch(e => {
this.loading = false;
});
});
},
confirm() {
// ID
const selectedBillId = this.selectedBill.id;
const billNumber = this.selectedBill.billNumber;
//
if (this.selectedBill.autoOut === '1') {
this.$confirm(`当前单据设置为自动出库,确认后将会自动生成出库单据。\n确定提交当前单据吗`, '温馨提示', {
@ -489,8 +606,18 @@ export default {
message: '提交成功!',
type: 'success'
});
// selectedBill使
this.selectedBill.billStatus = '1';
this.loading = false;
//
this.refreshList();
//
this.$nextTick(() => {
if (billNumber) {
this.queryParams.billNumber = billNumber;
this.getList();
}
});
}).catch(e => {
this.loading = false;
});
@ -508,8 +635,18 @@ export default {
message: '提交成功!',
type: 'success'
});
// selectedBill使
this.selectedBill.billStatus = '1';
this.loading = false;
//
this.refreshList();
//
this.$nextTick(() => {
if (billNumber) {
this.queryParams.billNumber = billNumber;
this.getList();
}
});
}).catch(e => {
this.loading = false;
});
@ -616,7 +753,13 @@ export default {
this.$modal.confirm('是否确认删除"' + this.selectedBill.billNumber+ '"的领用单据?').then(() => {
return delCkbill(ids);
}).then(() => {
//
this.selectedBill = {};
this.queryParams.billNumber = null;
//
this.refreshList();
//
this.getList();
this.$modal.msgSuccess("删除成功");
}).catch(() => {});
}
@ -700,6 +843,76 @@ export default {
this.download('jxc/ckcargo/export', {
...this.queryParams
}, `仓库领用管理_${new Date().getTime()}.xlsx`)
},
/** 导入按钮操作 */
handleImport() {
this.upload.title = "出库信息导入";
this.upload.open = true;
},
/** 下载模板操作 */
importTemplate() {
// API
request({
url: '/autogencode/ckbill/downloadOutTemplate',
method: 'get',
responseType: 'blob'
}).then(blob => {
// 使blob
const url = window.URL.createObjectURL(blob);
const link = document.createElement('a');
link.href = url;
link.setAttribute('download', `出库导入模板_${new Date().getTime()}.xlsx`);
document.body.appendChild(link);
link.click();
//
document.body.removeChild(link);
window.URL.revokeObjectURL(url);
}).catch(error => {
console.error('下载模板失败:', error);
this.$modal.msgError('下载模板失败,请重试');
});
},
//
handleFileUploadProgress(event, file, fileList) {
this.upload.isUploading = true;
},
//
handleFileSuccess(response, file, fileList) {
this.upload.open = false;
this.upload.isUploading = false;
this.$refs.upload.clearFiles();
this.$alert("<div style='overflow: auto;overflow-x: hidden;max-height: 70vh;padding: 10px 20px 0;" + response.msg + "</div>", "导入结果", {dangerouslyUseHTMLString: true});
this.getList();
},
/** 提交上传文件 */
submitFileForm() {
this.$refs.upload.submit();
},
/** 自定义上传方法 */
uploadFile(file) {
const formData = new FormData();
formData.append('file', file.file);
formData.append('updateSupport', this.upload.updateSupport);
// API
request({
url: '/autogencode/ckbill/importOutData',
method: 'post',
data: formData,
headers: {
'Content-Type': 'multipart/form-data'
}
}).then(response => {
this.$modal.msgSuccess('导入成功');
this.upload.open = false;
this.upload.fileList = [];
//
this.refreshList();
}).catch(error => {
this.$modal.msgError('导入失败:' + (error.response?.data?.message || '未知错误'));
}).finally(() => {
this.upload.isUploading = false;
});
}
}
}

@ -0,0 +1,663 @@
<template>
<div class="app-container">
<div v-if="show==0">
<!-- 查询工具栏 -->
<div class="search-toolbar mb8">
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" label-width="100px">
<el-form-item label="时间" prop="stockDate">
<el-date-picker
v-model="queryTime"
type="daterange"
range-separator="—"
start-placeholder="开始日期"
end-placeholder="结束日期"
format="yyyy-MM-dd"
value-format="yyyy-MM-dd"
/>
</el-form-item>
<el-form-item label="物品类别" prop="cargoCategory">
<el-select v-model="queryParams.cargoCategory" placeholder="请选择物品类别" clearable multiple>
<el-option label="全部" value=""/>
<el-option v-for="classItem in productClassList" :key="classItem.id" :label="classItem.className" :value="classItem.id"/>
</el-select>
</el-form-item>
<el-form-item label="供应商" prop="custId">
<el-select v-model="queryParams.custId" placeholder="请选择供应商" clearable>
<el-option label="全部" value=""/>
<el-option v-for="cust in custList" :key="cust.id" :label="cust.custName" :value="cust.id"/>
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery"></el-button>
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery"></el-button>
<el-button type="warning" icon="el-icon-download" size="mini" @click="handleInventoryExport"></el-button>
</el-form-item>
</el-form>
</div>
<!-- 物资盘点表标题区 -->
<div class="inventory-header mb8">
<div class="header-info">
<div class="supplier-info">供应商: {{ displayCustName }}</div>
<div class="date-info">日期: {{ displayDateRange }}</div>
<div class="title-info">标题: 物资盘点表</div>
</div>
</div>
<!-- 明细表格区域 -->
<el-table v-loading="loading" highlight-current-row :data="ckstockList" @sort-change="handleSortChange" border>
<el-table-column label="序号" width="80" align="center">
<template slot-scope="{ $index, row }">
<span v-if="!isTotalRow(row)">{{ $index + 1 }}</span>
</template>
</el-table-column>
<el-table-column label="品名及规格" align="center" min-width="200">
<template slot-scope="{ row }">
<div :style="{ fontWeight: isTotalRow(row) ? 'bold' : 'normal' }">{{ row.cargoName }}</div>
<div v-if="row.cargoSpec && !isTotalRow(row)" class="spec-info">{{ row.cargoSpec }}</div>
</template>
</el-table-column>
<el-table-column label="上期结存" align="center">
<el-table-column label="单位" align="center" prop="unit">
<template slot-scope="{ row }">
<span v-if="!isTotalRow(row)">{{ row.unit || '-' }}</span>
</template>
</el-table-column>
<el-table-column label="数量" align="center" prop="lastCargoNum">
<template slot-scope="{ row }">
<span v-if="!isTotalRow(row)">{{ row.lastCargoNum }}</span>
</template>
</el-table-column>
<el-table-column label="金额" align="center" prop="lastCargoValue"/>
</el-table-column>
<el-table-column label="本期入库" align="center">
<el-table-column label="单位" align="center" prop="unit">
<template slot-scope="{ row }">
<span v-if="!isTotalRow(row)">{{ row.unit || '-' }}</span>
</template>
</el-table-column>
<el-table-column label="数量" align="center" prop="inCargoNum">
<template slot-scope="{ row }">
<span v-if="!isTotalRow(row)">{{ row.inCargoNum }}</span>
</template>
</el-table-column>
<el-table-column label="金额" align="center" prop="inCargoValue"/>
</el-table-column>
<el-table-column label="本期出库" align="center">
<el-table-column label="单位" align="center" prop="unit">
<template slot-scope="{ row }">
<span v-if="!isTotalRow(row)">{{ row.unit || '-' }}</span>
</template>
</el-table-column>
<el-table-column label="数量" align="center" prop="outCargoNum">
<template slot-scope="{ row }">
<span v-if="!isTotalRow(row)">{{ row.outCargoNum }}</span>
</template>
</el-table-column>
<el-table-column label="金额" align="center" prop="outCargoValue"/>
</el-table-column>
<el-table-column label="累计结存" align="center">
<el-table-column label="单位" align="center" prop="unit">
<template slot-scope="{ row }">
<span v-if="!isTotalRow(row)">{{ row.unit || '-' }}</span>
</template>
</el-table-column>
<el-table-column label="数量" align="center" prop="cargoNum">
<template slot-scope="{ row }">
<span v-if="!isTotalRow(row)">{{ row.cargoNum }}</span>
</template>
</el-table-column>
<el-table-column label="金额" align="center" prop="cargoValue"/>
</el-table-column>
</el-table>
<!-- 物资盘点表不需要分页因为需要显示全部数据和合计 -->
</div>
<div v-if="show == 1 ">
<div>
<el-button @click="cancel" style="right: 3%;position:absolute;"> </el-button>
<br>
<br>
<el-table v-loading="loading" highlight-current-row :data="ckstockDetailList" @sort-change="handleSortChange"
@selection-change="handleSelectionChange" border>
<!-- <el-table-column type="selection" width="55" align="center"/>-->
<el-table-column label="单据信息" align="center">
<el-table-column label="入库日期" align="center" prop="stockDate" width="180" sortable='custom'>
<template slot-scope="scope">
<span>{{ parseTime(scope.row.stockDate, '{y}-{m}-{d} {h}:{i}:{s}') }}</span>
</template>
</el-table-column>
<el-table-column label="单据编号" align="center" prop="billNumber" sortable='custom' width="100"/>
</el-table-column>
<el-table-column label="商品信息" align="center">
<el-table-column label="仓库名称" align="center" prop="stockName" sortable='custom' width="100"/>
<el-table-column label="供应商名称" align="center" prop="custName" sortable='custom' width="100"/>
<el-table-column label="商品名称" align="center" prop="cargoName" sortable='custom' width="100"/>
<el-table-column label="商品编号" align="center" prop="hsCode" sortable='custom' width="100"/>
<el-table-column label="商品价值" align="center" prop="cargoValue" sortable='custom' width="100"/>
<el-table-column label="规格类型品质" show-overflow-tooltip align="center" prop="cargoSpec" sortable='custom'
min-width="200"/>
<el-table-column label="商品数量" align="center" prop="cargoWt" sortable='custom' width="130"/>
</el-table-column>
<el-table-column label="有效期至" align="center" prop="expiryDate" width="180" sortable='custom'>
<template slot-scope="scope">
<span>{{ parseTime(scope.row.expiryDate, '{y}-{m}-{d} {h}:{i}:{s}') }}</span>
</template>
</el-table-column>
</el-table>
<pagination
:total="detailTotal"
:page.sync="detailParams.pageNum"
:limit.sync="detailParams.pageSize"
@pagination="handleUpdate"
/>
</div>
</div>
</div>
</template>
<script>
import {
newList,
getCkstock,
// delCkstock,
// addCkstock,
// updateCkstock,
newListCkstock,
ckcargostockListApi as pageListCkstock
} from '@/api/ckcargostock';
import {listStock} from "@/api/cmstock";
import {listCust, cmcustListByAll} from "@/api/cmcust";
import {inventoryListApi} from "@/api/ckstockchange";
import {pmproductclassTreeSelect} from "@/api/pmproductclass";
// import {listVessel,} from "@/api/jxc/vessel";
// import {listBmvoyage} from "@/api/jxc/bmvoyage";
import {datePickerOpts} from "@/utils";
import {listWarehouse} from "@/api/cmwarehouse";
import SearchBlock from '@/components/SearchBlock';
export default {
name: "Ckstock",
components: {
SearchBlock,
},
dicts: ['sys_business_nature', 'sys_trade_nature', 'sys_origin_country'],
data() {
return {
queryOption: [
{
label: '商品名称',
prop: 'cargoName',
type: 'text',
},
{
label: '商品编码',
prop: 'hsCode',
type: 'text',
},
{
label: '供应商',
prop: 'custName',
type: 'text',
},
],
activeName: ['1', '2', '3'],
show: 0,
tableData: [{
id: 3,
cargoName: '铁',
stockName: '1号库场',
custName: '王晓敏',
cargoNum: '1519 ',
cargoWt: '11',
cargoVol: '120',
}, {
id: 31,
cargoName: '铁',
stockName: '1号库场',
custName: '王晓敏',
cargoNum: '519 ',
cargoWt: '10',
cargoVol: '100',
},
],
//
warehouses: [],
//
loading: true,
//
ids: [],
//
names: [],
//
single: true,
//
multiple: true,
//
showSearch: true,
//
total: 0,
//
queryTime: [],
// newListCkstockList
newListCkstockList: [],
//
bmvoyageList: [],
//
vesselList: [],
//
custList: [],
//
cargoList: [],
//
stockList: [],
//
productClassList: [],
//
ckstockList: [],
ckstockDetailList: [],
detailTotal: 0,
//
title: "",
//
open: false,
//
queryParams: {
pageNum: 1,
pageSize: 10,
stockDate: null,
billNumber: null,
voyageId: null,
voyageNo: null,
vesselId: null,
vesselName: null,
cargoId: null,
hsCode: null,
cargoName: null,
billNo: null,
cargoSpec: null,
originCountry: null,
custId: null,
custName: null,
opType: null,
tradType: null,
cargoNum: null,
cargoWt: null,
cargoVol: null,
stockId: null,
stockCode: null,
stockName: null,
cargoValue: null,
customsNo: null,
//
cargoCategory: null,
listNumber: null,
cargoNumber: null,
itemNumber: null,
bookNumber: null,
expiryDate: null,
},
//
detailParams: {
pageNum: 1,
pageSize: 10,
cargoId: null,
},
//
form: {},
//
rules: {
stockId: [
{required: true, message: "库场ID不能为空", trigger: "blur"}
],
stockDate: [
{required: true, message: "入库日期不能为空", trigger: "blur"}
],
billNumber: [
{required: true, message: "单据编号不能为空", trigger: "blur"}
],
voyageNo: [
{required: true, message: "航次号不能为空", trigger: "blur"}
],
vesselName: [
{required: true, message: "船舶名称不能为空", trigger: "blur"}
],
cargoName: [
{required: true, message: "商品名称不能为空", trigger: "blur"}
],
billNo: [
{required: true, message: "提运单号不能为空", trigger: "blur"}
],
custName: [
{required: true, message: "供应商不能为空", trigger: "blur"}
],
opType: [
{required: true, message: "业务性质不能为空", trigger: "blur"}
],
tradType: [
{required: true, message: "贸易性质不能为空", trigger: "blur"}
],
stockName: [
{required: true, message: "库场名称不能为空", trigger: "blur"}
],
}
};
},
created() {
//
const now = new Date();
const dayOfWeek = now.getDay() || 7;
const monday = new Date(now);
monday.setDate(now.getDate() - dayOfWeek + 1);
const sunday = new Date(now);
sunday.setDate(monday.getDate() + 6);
this.queryTime = [
monday.getFullYear() + '-' + String(monday.getMonth() + 1).padStart(2, '0') + '-' + String(monday.getDate()).padStart(2, '0'),
sunday.getFullYear() + '-' + String(sunday.getMonth() + 1).padStart(2, '0') + '-' + String(sunday.getDate()).padStart(2, '0')
];
this.getList();
this.getCustList();
this.getProductClassList();
// this.getVesselList();
// this.getBmvoyage();
this.getNewListCkstock();
listWarehouse({}).then(response => {
this.warehouses = response.data;
});
},
computed: {
//
displayCustName() {
if (!this.queryParams.custId) {
return '全部';
}
const cust = this.custList.find(c => c.id === this.queryParams.custId);
return cust ? cust.custName : '未知';
},
//
displayDateRange() {
if (this.queryTime && this.queryTime.length === 2) {
return this.queryTime[0] + ' 至 ' + this.queryTime[1];
}
return new Date().toISOString().split('T')[0];
}
},
methods: {
getNewListCkstock() {
let param = {};
newListCkstock(param).then(response => {
this.newListCkstockList = response.data;
}).catch(e => {
});
},
//
// setBmvoyageDetail(voyageId) {
//
// this.bmvoyageList.forEach(ele => {
// if (ele.id == voyageId) {
// this.form.voyageNo = ele.voyageNo;
// this.form.vesselName = ele.vesselName;
// this.form.deptId = ele.deptId;
// this.form.vesselId = ele.vesselId;
// this.form.netTon = ele.netTon;
// this.form.opType = ele.opType;
//
//
// }
// })
// },
getBmvoyage() {
let param = {};
listBmvoyage(param).then(response => {
this.bmvoyageList = response.data;
}).catch(e => {
});
},
isTotalRow(row) {
return row.cargoName === '合计';
},
getCustList() {
cmcustListByAll({}).then(response => {
this.custList = response || [];
}).catch(e => {
console.error('获取供应商列表失败:', e);
this.custList = [];
});
},
getProductClassList() {
pmproductclassTreeSelect({}).then(response => {
//
this.productClassList = response || [];
}).catch(e => {
console.error('获取物品类别列表失败:', e);
this.productClassList = [];
});
},
//
setStockDetail(stockId) {
this.stockList.forEach(ele => {
if (ele.id == stockId) {
this.form.stockName = ele.stockName;
this.form.stockCode = ele.stockCode;
}
})
},
/** 查询仓库库存列表 */
getList() {
this.loading = true;
const params = {
startDate: this.queryTime && this.queryTime.length > 0 ? this.queryTime[0] : null,
endDate: this.queryTime && this.queryTime.length > 1 ? this.queryTime[1] : null,
custId: this.queryParams.custId || null,
cargoCategory: this.queryParams.cargoCategory
};
inventoryListApi(params).then(response => {
this.ckstockList = response.list || [];
this.total = response.total || 0;
this.loading = false;
}).catch(e => {
this.loading = false;
});
},
//
cancel() {
this.show = 0;
this.reset();
},
//
reset() {
this.form = {
id: null,
stockDate: null,
billNumber: null,
voyageId: null,
voyageNo: null,
vesselId: null,
vesselName: null,
cargoId: null,
hsCode: null,
cargoName: null,
billNo: null,
cargoSpec: null,
originCountry: null,
custId: null,
custName: null,
opType: null,
tradType: null,
cargoNum: null,
cargoWt: null,
cargoVol: null,
stockId: null,
stockCode: null,
stockName: null,
remark: null,
cargoValue: null,
customsNo: null,
listNumber: null,
cargoNumber: null,
itemNumber: null,
bookNumber: null,
expiryDate: null,
};
this.resetForm("form");
},
//
handleSortChange(col) {
this.$sortBy(col, this.queryParams);
this.getList();
},
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.pageNum = 1;
this.getList();
},
/** 重置按钮操作 */
resetQuery() {
this.resetForm("queryForm");
this.handleQuery();
},
//
handleSelectionChange(selection) {
this.ids = selection.map(item => item.id);
this.names = selection.map(item => item.id);
this.single = selection.length !== 1;
this.multiple = !selection.length;
},
/** 新增按钮操作 */
handleAdd() {
this.reset();
this.open = true;
this.title = "添加仓库库存";
},
/** 详情按钮操作 */
handleUpdate(row) {
if (row) {
this.detailParams.cargoId = row.cargoId,
this.detailParams.custId = row.custId
}
pageListCkstock(this.detailParams).then(response => {
this.ckstockDetailList = response.list;
this.detailTotal = response.total;
this.show = 1;
}).catch(e => {
});
},
/** 提交按钮 */
// submitForm() {
// this.$refs["form"].validate(valid => {
// if (valid) {
// if (this.form.id != null) {
// updateCkstock(this.form).then(response => {
// this.$modal.msgSuccess("");
// this.open = false;
// this.getList();
// });
// } else {
// addCkstock(this.form).then(response => {
// this.$modal.msgSuccess("");
// this.open = false;
// this.getList();
// });
// }
// }
// });
// },
/** 删除按钮操作 */
// handleDelete(row) {
// const ids = row.id || this.ids;
// const names = row.id || this.names;
// this.$modal.confirm('"' + row.billNumber + '"').then(function () {
// return delCkstock(ids);
// }).then(() => {
// this.getList();
// this.$modal.msgSuccess("");
// }).catch(() => {
// });
// },
/** 导出按钮操作 */
handleExport() {
this.download('jxc/ckstock/export', {
...this.queryParams
}, `库场商品查询_${new Date().getTime()}.xlsx`)
},
/** 物资盘点表导出操作 */
handleInventoryExport() {
this.download('autogencode/ckstockchange/export-inventory', {
startDate: this.queryTime && this.queryTime.length > 0 ? this.queryTime[0] : null,
endDate: this.queryTime && this.queryTime.length > 1 ? this.queryTime[1] : null,
custId: this.queryParams.custId || null,
cargoCategory: this.queryParams.cargoCategory
}, `物资盘点表_${new Date().getTime()}.xlsx`)
}
}
};
</script>
<style scoped>
.search-toolbar {
background-color: #f5f7fa;
padding: 10px;
border-radius: 4px;
margin-bottom: 16px;
}
.inventory-header {
background-color: #f5f7fa;
padding: 10px;
border-radius: 4px;
margin-bottom: 16px;
}
.header-info {
display: flex;
justify-content: space-between;
align-items: center;
}
.supplier-info {
font-weight: bold;
}
.date-info {
font-weight: bold;
}
.title-info {
font-weight: bold;
font-size: 16px;
}
.spec-info {
font-size: 12px;
color: #666;
margin-top: 4px;
}
</style>

@ -55,29 +55,37 @@
<el-table v-loading="loading" highlight-current-row :data="ckstockList" @sort-change="handleSortChange"
@selection-change="handleSelectionChange" border>
<el-table-column type="selection" width="55" align="center"/>
<!-- <el-table-column label="商品自然序号" align="center" prop="cargoNumber" sortable='custom'/>-->
<el-table-column label="商品编码" align="center" prop="hsCode" sortable='custom'/>
<el-table-column label="仓库" align="center" prop="stockName" sortable='custom'/>
<el-table-column label="分类代码" align="center" prop="classCode" sortable='custom'/>
<el-table-column label="商品类别" align="center" prop="className" sortable='custom'/>
<el-table-column label="商品名称" align="center" prop="cargoName" sortable='custom'/>
<el-table-column label="供应商" align="center" prop="custName" sortable='custom'/>
<el-table-column label="数量" align="center" prop="cargoWt" sortable='custom'/>
<!-- <el-table-column label="盘点日期" align="center" prop="updateTime" sortable='custom'/>-->
<el-table-column label="商品编号" align="center" prop="hsCode" sortable='custom'/>
<el-table-column label="规格型号" align="center" prop="cargoSpec" sortable='custom' show-overflow-tooltip/>
<el-table-column label="单位" align="center" prop="unit" sortable='custom'>
<template slot-scope="scope">
<dict-tag :options="dict.type.bm_measuring_unit" :value="scope.row.unit"/>
</template>
</el-table-column>
<el-table-column label="入库日期" align="center" prop="stockDate" sortable='custom'>
<template slot-scope="{ row }">
{{ parseTime(row.stockDate, '{y}-{m}-{d}') }}
</template>
</el-table-column>
<el-table-column label="操作" fixed="right" align="center" class-name="small-padding fixed-width" width="120px">
<template slot-scope="scope">
<el-button
size="mini"
type="text"
icon="el-icon-edit"
@click="handleUpdate(scope.row)"
v-hasPermi="['jxc:ckstock:edit']"
>库存明细
</el-button>
</template>
</el-table-column>
<el-table-column label="数量" align="center" prop="cargoWt" sortable='custom'/>
<el-table-column label="单价" align="center" prop="unitPrice" sortable='custom'/>
<el-table-column label="金额" align="center" prop="cargoValue" sortable='custom'/>
<!-- <el-table-column label="操作" fixed="right" align="center" class-name="small-padding fixed-width" width="120px">-->
<!-- <template slot-scope="scope">-->
<!-- <el-button-->
<!-- size="mini"-->
<!-- type="text"-->
<!-- icon="el-icon-edit"-->
<!-- @click="handleUpdate(scope.row)"-->
<!-- v-hasPermi="['jxc:ckstock:edit']"-->
<!-- >库存明细-->
<!-- </el-button>-->
<!-- </template>-->
<!-- </el-table-column>-->
</el-table>
<pagination
:total="total"
@ -146,9 +154,8 @@ import {
newListCkstock,
ckcargostockListApi as pageListCkstock
} from '@/api/ckcargostock';
import {listStock} from "@/api/cmstock";
import {listCust} from "@/api/cmcust";
import {listCargo} from "@/api/cmcustproduct";
import {pmproductclassTreeSelect} from "@/api/pmproductclass";
// import {listVessel,} from "@/api/jxc/vessel";
// import {listBmvoyage} from "@/api/jxc/bmvoyage";
import {datePickerOpts} from "@/utils";
@ -161,10 +168,26 @@ export default {
components: {
SearchBlock,
},
dicts: ['sys_business_nature', 'sys_trade_nature', 'sys_origin_country'],
dicts: ['sys_business_nature', 'sys_trade_nature', 'sys_origin_country','bm_measuring_unit'],
data() {
return {
queryOption: [
{
label: '仓库名称',
prop: 'stockId',
type: 'select',
getOptions: () => this.warehouses,
optionsProp: 'id',
optionsLabel: 'stockName',
},
{
label: '商品类别',
prop: 'classId',
type: 'select',
getOptions: () => this.productClassList,
optionsProp: 'id',
optionsLabel: 'className',
},
{
label: '商品名称',
prop: 'cargoName',
@ -209,6 +232,8 @@ export default {
//
warehouses: [],
//
productClassList: [],
//
loading: true,
//
@ -329,16 +354,13 @@ export default {
},
created() {
this.getList();
this.getStockList();
this.getCustList();
this.getCargoList();
this.getVesselList();
this.getBmvoyage();
this.getNewListCkstock();
listWarehouse({}).then(response => {
this.warehouses = response.data;
this.warehouses = response;
});
this.getProductClassList();
},
methods: {
getNewListCkstock() {
@ -365,34 +387,6 @@ export default {
// }
// })
// },
getBmvoyage() {
let param = {};
listBmvoyage(param).then(response => {
this.bmvoyageList = response.data;
}).catch(e => {
});
},
//
setVesselDetail(vesselId) {
console.log(vesselId);
this.vesselList.forEach(ele => {
if (ele.id == vesselId) {
console.log("aa:", ele.vesselName);
this.form.vesselName = ele.vesselName;
}
})
},
getVesselList() {
let param = {};
listVessel(param).then(response => {
this.vesselList = response.data;
}).catch(e => {
});
},
//
setCargoDetail(cargoId) {
@ -407,14 +401,6 @@ export default {
})
},
getCargoList() {
let param = {};
listCargo(param).then(response => {
this.cargoList = response.data;
}).catch(e => {
});
},
//
setCustDetail(custId) {
this.custList.forEach(ele => {
@ -443,11 +429,13 @@ export default {
})
},
getStockList() {
let param = {};
listStock(param).then(response => {
this.stockList = response.data;
getProductClassList() {
pmproductclassTreeSelect({}).then(response => {
//
this.productClassList = response || [];
}).catch(e => {
console.error('获取物品类别列表失败:', e);
this.productClassList = [];
});
},

@ -86,7 +86,11 @@
</el-table-column>
<el-table-column label='商品单价' prop='unitPrice' >
<template slot-scope="scope">
<div class="nomal-text">{{ scope.row.unitPrice }}</div>
<div v-if="tableReadonly" class="nomal-text">{{ scope.row.unitPrice }}</div>
<el-form-item v-else prop="unitPrice" class="margin-none">
<el-input-number v-model="scope.row.unitPrice" placeholder="请输入商品单价" style="width: 100%" :precision="2"
:min="0" controls-position="right" @change="updateCargoValue(scope.row)"/>
</el-form-item>
</template>
</el-table-column>
<el-table-column label='商品数量' prop='cargoWt' >
@ -94,7 +98,16 @@
<div v-if="tableReadonly" class="nomal-text">{{ scope.row.cargoWt }}</div>
<el-form-item v-else prop="cargoWt" class="margin-none">
<el-input-number v-model="scope.row.cargoWt" placeholder="请输入商品数量" style="width: 100%" :precision="2"
:min="0" controls-position="right" />
:min="0" controls-position="right" @change="updateCargoValue(scope.row)"/>
</el-form-item>
</template>
</el-table-column>
<el-table-column label='金额' prop='cargoValue' >
<template slot-scope="scope">
<div v-if="tableReadonly" class="nomal-text">{{ scope.row.cargoValue }}</div>
<el-form-item v-else prop="cargoValue" class="margin-none">
<el-input-number v-model="scope.row.cargoValue" placeholder="请输入金额" style="width: 100%" :precision="2"
:min="0" controls-position="right" @change="markManualCargoValue(scope.row)"/>
</el-form-item>
</template>
</el-table-column>
@ -216,7 +229,7 @@
label="单位"
>
<template slot-scope="scope">
<DictTag :options="dict.type.sys_measurement_unit" :value="scope.row.unit" />
<DictTag :options="dict.type.bm_measuring_unit" :value="scope.row.unit" />
</template>
</el-table-column>
<el-table-column
@ -301,7 +314,7 @@ export default {
props: {
},
dicts: ['bm_measuring_unit', 'sys_measurement_unit', 'sys_origin_country', 'sys_curr_type', 'pm_order_status'],
dicts: ['bm_measuring_unit', 'bm_measuring_unit', 'sys_origin_country', 'sys_curr_type', 'pm_order_status'],
computed: {
//
tableReadonly() {
@ -679,6 +692,7 @@ export default {
cargoWt: 0,
cargoVol: 0,
cargoValue: 0,
manualCargoValue: false,
stockCode: '',
shelfId: '',
locationId: '',
@ -747,6 +761,7 @@ export default {
form.cargoSpec = ele.spec || '';
form.unit = ele.unit || '';
form.unitPrice = ele.costPrice || 0;
form.manualCargoValue = false;
//
let allAutoOut = true;
//
@ -1054,6 +1069,7 @@ export default {
cargoWt: detail.orderQuantity || detail.quantity || 0,
cargoVol: 0,
cargoValue: (detail.orderQuantity || detail.quantity || 0) * (detail.orderPrice || detail.price || 0),
manualCargoValue: false,
stockCode: '',
shelfId: '',
locationId: '',
@ -1093,6 +1109,17 @@ export default {
this.$message.warning('该订单没有计划明细')
}
},
//
updateCargoValue(row) {
//
if (row.unitPrice && row.cargoWt && !row.manualCargoValue) {
row.cargoValue = row.unitPrice * row.cargoWt;
}
},
//
markManualCargoValue(row) {
row.manualCargoValue = true;
},
//
open(options) {
const { type, data } = options;

@ -77,6 +77,11 @@ export default {
showAutoOut: {
type: Boolean,
default: false
},
//
autoSelectFirst: {
type: Boolean,
default: false
}
},
data() {
@ -102,8 +107,18 @@ export default {
},
methods: {
rowClick(e) {
this.selectItem = e;
this.$emit('rowClick', e);
//
if (this.selectItem.id === e.id) {
//
this.selectItem = {};
this.$emit('rowClick', {});
this.$refs.table.setCurrentRow(null);
} else {
//
this.selectItem = e;
this.$emit('rowClick', e);
this.$refs.table.setCurrentRow(e);
}
},
getList() {
this.loading = true;
@ -122,6 +137,8 @@ export default {
requestParams.endDate = requestParams.dateEnd;
delete requestParams.dateEnd;
}
// ID
const currentSelectedId = this.selectItem.id;
stockPageListCkbill(requestParams).then(response => {
this.list = response.list || [];
@ -129,18 +146,32 @@ export default {
this.loading = false;
if (this.list.length) {
let currentItem = this.list[0];
if (this.selectItem.id) {
if (currentSelectedId) {
//
const findIndex = this.list.findIndex(val => val.id === this.selectItem.id);
const findIndex = this.list.findIndex(val => val.id === currentSelectedId);
if (findIndex > -1) {
currentItem = this.list[findIndex];
const currentItem = this.list[findIndex];
// selectItemrowClickbillClick
this.selectItem = currentItem;
//
this.$refs.table.setCurrentRow(currentItem);
//
this.$emit('rowClick', currentItem, true);
return;
}
}
this.rowClick(currentItem);
this.$refs.table.setCurrentRow(currentItem);
} else {
this.rowClick({});
if (this.autoSelectFirst) {
//
const currentItem = this.list[0];
this.selectItem = currentItem;
this.$refs.table.setCurrentRow(currentItem);
this.$emit('rowClick', currentItem, true);
}
} else if (!this.list.length) {
this.selectItem = {};
this.$refs.table.setCurrentRow(null);
this.$emit('rowClick', {}, true);
}
}).catch(e => {
this.loading = false;

File diff suppressed because it is too large Load Diff

@ -1,107 +1,62 @@
<template>
<div class="app-container">
<SearchBlock v-model="queryParams" v-show="showSearch" size="mini" :menus="queryOption" @change="handleQuery"></SearchBlock>
<!-- <el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="100px">
<el-form-item label="单据编号" prop="billNumber">
<el-input
v-model="queryParams.billNumber"
placeholder="请输入单据编号"
clearable
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<el-form-item label="航次号" prop="voyageNo">
<el-input
v-model="queryParams.voyageNo"
placeholder="请输入航次号"
clearable
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="100px">
<!-- 商品类别 -->
<el-form-item label="商品类别" prop="classIds">
<el-select v-model="queryParams.classIds" placeholder="请选择商品类别" clearable multiple>
<el-option v-for="item in productClassList" :key="item.id" :label="item.className" :value="item.id"/>
</el-select>
</el-form-item>
<!-- 供应商 -->
<el-form-item label="供应商" prop="custId">
<el-select v-model="queryParams.custId" placeholder="请选择供应商" clearable>
<el-option v-for="item in custList" :key="item.id" :label="item.custName" :value="item.id"/>
</el-select>
</el-form-item>
<!-- 入库时间 -->
<el-form-item label="入库时间" prop="billDate">
<el-date-picker
v-model="queryTime"
type="daterange"
range-separator="—"
start-placeholder="开始日期"
end-placeholder="结束日期"
format="yyyy-MM-dd"
value-format="yyyy-MM-dd"
/>
</el-form-item>
<!-- 单据编号 -->
<el-form-item label="单据编号" prop="billNumber">
<el-input
v-model="queryParams.billNumber"
placeholder="请输入单据编号"
clearable
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<!-- 商品名称 -->
<el-form-item label="商品名称" prop="cargoName">
<el-input
v-model="queryParams.cargoName"
placeholder="请输入商品名称"
clearable
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<!-- 仓库名称 -->
<el-form-item label="仓库名称" prop="stockCode" class="search-item">
<el-select v-model="queryParams.stockCode" filterable placeholder="请选择">
<el-option v-for="dict in warehouses" :key="dict.stockCode" :label="dict.stockName" :value="dict.stockCode">
</el-option>
</el-select>
</el-form-item>
<el-form-item label="船舶名称" prop="vesselName">
<el-input
v-model="queryParams.vesselName"
placeholder="请输入船舶名称"
clearable
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<el-form-item label="商品名称" prop="cargoName">
<el-input
v-model="queryParams.cargoName"
placeholder="请输入商品名称"
clearable
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<el-form-item label="仓库名称" prop="stockCode" class="search-item">
<el-select v-model="queryParams.stockCode" filterable placeholder="请选择">
<el-option v-for="dict in warehouses" :key="dict.stockCode" :label="dict.stockName" :value="dict.stockCode">
</el-option>
</el-select>
</el-form-item>
<el-form-item label="仓位名称" prop="stockName" class="search-item">
<el-select v-model="queryParams.stockName" filterable placeholder="请选择">
<el-option v-for="dict in stockList" :key="dict.stockName" :label="dict.stockName" :value="dict.stockName">
</el-option>
</el-select>
</el-form-item>
<el-form-item label="报关单号" prop="customsNo">
<el-input
v-model="queryParams.customsNo"
placeholder="请输入报关单号"
clearable
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<el-form-item label="核注清单号" prop="listNumber" >
<el-input
v-model="queryParams.listNumber"
placeholder="请输入核注清单号"
clearable
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<el-form-item label="项号" prop="bookNumber" >
<el-input
v-model="queryParams.bookNumber"
placeholder="请输入项号"
clearable
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<el-form-item label="单据日期" prop="queryTime">
<el-date-picker clearable
v-model="queryTime"
type="daterange"
:picker-options="pickerOptions"
value-format="yyyy-MM-dd"
end-placeholder="结束日期"
range-separator="-" start-placeholder="开始日期"
placeholder="请选择单据日期">
</el-date-picker>
</el-form-item>
<el-form-item label="有效期至" prop="queryTime">
<el-date-picker clearable
v-model="queryTimeTwo"
size="small"
:picker-options="pickerOptions"
end-placeholder="结束日期"
range-separator="-" start-placeholder="开始日期" type="daterange"
value-format="yyyy-MM-dd"
placeholder="请选择有效期至">
</el-date-picker>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery"></el-button>
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery"></el-button>
</el-form-item>
</el-form> -->
<!-- 操作按钮 -->
<el-form-item>
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery"></el-button>
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery"></el-button>
</el-form-item>
</el-form>
<el-row :gutter="10" class="mb8">
@ -119,36 +74,76 @@
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
<el-table v-loading="loading" highlight-current-row :data="ckcargoList" @sort-change="handleSortChange" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="单据编号" align="center" prop="billNumber" sortable='custom' width="100"/>
<el-table-column label="单据日期" align="center" prop="billDate" width="180" sortable='custom'>
<el-table v-loading="loading" highlight-current-row :data="displayList" @sort-change="handleSortChange" @selection-change="handleSelectionChange">
<el-table-column label="序号" width="80" align="center">
<template slot-scope="{ $index, row }">
<span v-if="!row.isTotalRow">{{ (queryParams.pageNum - 1) * queryParams.pageSize + $index + 1 }}</span>
</template>
</el-table-column>
<el-table-column label="单据编号" align="center" prop="billNumber" sortable='custom' width="100">
<template slot-scope="{ row }">
<span v-if="row.isTotalRow" style="font-weight: bold;">{{ row.billNumber }}</span>
<span v-else>{{ row.billNumber }}</span>
</template>
</el-table-column>
<el-table-column label="仓库" align="center" prop="stockCode" sortable='custom' width="120">
<template slot-scope="scope">
<span>{{ parseTime(scope.row.billDate, '{y}-{m}-{d} {h}:{i}:{s}') }}</span>
<span v-if="!scope.row.isTotalRow">{{scope.row.stockName}}</span>
</template>
</el-table-column>
<el-table-column label="仓库名称" align="center" prop="stockCode" sortable='custom' width="120">
<el-table-column label="入库时间" align="center" prop="billDate" width="180" sortable='custom'>
<template slot-scope="scope">
{{scope.row.stockName}}/{{scope.row.shelfName}}/{{scope.row.locationName}}
<span v-if="!scope.row.isTotalRow">{{ parseTime(scope.row.billDate, '{y}-{m}-{d}') }}</span>
</template>
</el-table-column>
<el-table-column label="商品编码" align="center" prop="hsCode" sortable='custom' width="100">
<template slot-scope="{ row }">
<span v-if="!row.isTotalRow">{{ row.hsCode }}</span>
</template>
</el-table-column>
<el-table-column label="商品名称" align="center" prop="cargoName" sortable='custom' width="100">
<template slot-scope="{ row }">
<span v-if="row.isTotalRow">{{ row.cargoName }}</span>
<span v-else>{{ row.cargoName }}</span>
</template>
</el-table-column>
<el-table-column label="仓位名称" align="center" prop="stockName" sortable='custom' width="100" v-if="false"/>
<el-table-column label="供应商" align="center" prop="custName" sortable='custom' width="100"/>
<el-table-column label="商品编号" align="center" prop="hsCode" sortable='custom' width="100"/>
<el-table-column label="商品名称" align="center" prop="cargoName" sortable='custom' width="100"/>
<el-table-column label="入库件数" align="center" prop="cargoWt" sortable='custom' width="130"/>
<el-table-column label="规格类型品质" show-overflow-tooltip align="center" prop="cargoSpec" sortable='custom' min-width="200"/>
<!-- <el-table-column label="船舶信息" align="center">-->
<!-- <el-table-column label="航次号" align="center" prop="voyageNo" sortable='custom' width="100"/>-->
<!-- <el-table-column label="船舶名称" align="center" prop="vesselName" sortable='custom' width="100"/>-->
<!-- </el-table-column>-->
<el-table-column label="有效期至" align="center" prop="expiryDate" width="180" sortable='custom'>
<el-table-column label="商品类别" align="center" prop="className" sortable='custom' width="100">
<template slot-scope="{ row }">
<span v-if="!row.isTotalRow">{{ row.className }}</span>
</template>
</el-table-column>
<el-table-column label="规格" show-overflow-tooltip align="center" prop="cargoSpec" sortable='custom' min-width="200">
<template slot-scope="{ row }">
<span v-if="!row.isTotalRow">{{ row.cargoSpec }}</span>
</template>
</el-table-column>
<el-table-column label="单位" align="center" prop="unit" sortable='custom' width="80">
<template slot-scope="scope">
<span>{{ parseTime(scope.row.expiryDate, '{y}-{m}-{d} {h}:{i}:{s}') }}</span>
<dict-tag v-if="!scope.row.isTotalRow" :options="dict.type.bm_measuring_unit" :value="scope.row.unit" />
</template>
</el-table-column>
<el-table-column label="数量" align="center" prop="cargoWt" sortable='custom' width="100">
<template slot-scope="{ row }">
<span v-if="row.isTotalRow" style="font-weight: bold;">{{ row.cargoWt }}</span>
<span v-else>{{ row.cargoWt }}</span>
</template>
</el-table-column>
<el-table-column label="单价" align="center" prop="unitPrice" sortable='custom' width="100">
<template slot-scope="{ row }">
<span v-if="!row.isTotalRow">{{ row.unitPrice }}</span>
</template>
</el-table-column>
<el-table-column label="金额" align="center" prop="cargoValue" sortable='custom' width="100">
<template slot-scope="{ row }">
<span v-if="row.isTotalRow" style="font-weight: bold;">{{ row.cargoValue }}</span>
<span v-else>{{ row.cargoValue }}</span>
</template>
</el-table-column>
<el-table-column label="供应商" align="center" prop="custName" sortable='custom' width="100">
<template slot-scope="{ row }">
<span v-if="!row.isTotalRow">{{ row.custName }}</span>
</template>
</el-table-column>
</el-table>
<!-- <el-row>-->
<!-- <el-col :span="12" class="text-right" style=" margin-top: 10px; margin-left: 10px">-->
@ -167,80 +162,26 @@
</template>
<script>
import { ckbillcargoListApi as pageListCkcargo, ckbillcargoDetailApi as getCkcargo, ckbillcargoDeleteApi as delCkcargo, ckbillcargoCreateApi as addCkcargo, ckbillcargoUpdateApi as updateCkcargo,selectTotalWt } from "@/api/ckbillcargo";
import { ckbillcargoListApi as pageListCkcargo, ckbillcargoDetailApi as getCkcargo, ckbillcargoDeleteApi as delCkcargo, ckbillcargoCreateApi as addCkcargo, ckbillcargoUpdateApi as updateCkcargo,selectTotalWt, selectTotal } from "@/api/ckbillcargo";
// import { listStock } from "@/api/jxc/stock";
import { cmcustListByAll as listCust } from "@/api/cmcust";
import { cmcustproductListByAll as listCargo } from "@/api/cmcustproduct";
// import {listVessel,} from "@/api/jxc/vessel";
// import {listBmvoyage} from "@/api/jxc/bmvoyage";
import {listWarehouse} from "@/api/cmwarehouse";
import { listWarehouse } from "@/api/cmwarehouse";
import { cmshelfListByAll as listShelf } from "@/api/cmshelf";
import { datePickerOpts } from "@/utils";
import SearchBlock from '@/components/SearchBlock';
import { pmproductclassTreeSelect } from "@/api/pmproductclass";
import WarehouseName from '@/components/WarehouseName';
export default {
name: "Instock",
components: {
SearchBlock,
WarehouseName,
},
dicts: ['sys_business_nature', 'sys_trade_nature','sys_inbound_outbound_type','sys_origin_country'],
dicts: ['sys_business_nature', 'sys_trade_nature','sys_inbound_outbound_type','sys_origin_country','bm_measuring_unit'],
data() {
return {
//
queryOption: [
{
label: '仓库名称',
prop: 'stockCode',
type: 'select',
getOptions: () => this.warehouses,
optionsProp: 'stockCode',
optionsLabel: 'stockName',
},
{
label: '有效期至',
prop: 'excTime',
type: 'daterange',
options: datePickerOpts()
},
{
label: '单据编号',
prop: 'billNumber',
type: 'text',
},
{
label: '商品编号',
prop: 'hsCode',
type: 'text',
},
{
label: '商品名称',
prop: 'cargoName',
type: 'text',
},
{
label: '货架名称',
prop: 'shelfName',
type: 'select',
getOptions: () => this.shelfList,
optionsProp: 'shelfName',
optionsLabel: 'shelfName',
},
{
label: '单据日期',
prop: 'queryTime',
type: 'daterange',
options: datePickerOpts()
},
{
label: '供应商',
prop: 'custName',
type: 'text',
},
],
activeName: ['1', '2', '3'],
pickerOptions: {
shortcuts: [{
@ -270,6 +211,11 @@ export default {
}]
},
totalWeight: 0,
//
totalData: {
totalWt: 0,
totalValue: 0
},
//
loading: true,
//
@ -302,6 +248,8 @@ export default {
stockList: [],
//
shelfList: [],
//
productClassList: [],
//
ckcargoList: [],
//
@ -396,12 +344,36 @@ export default {
this.getCargoList();
this.getWarehouseList();
this.getShelfList();
this.getProductClassList();
// listWarehouse({}).then(response => {
// console.log(response)
// this.warehouses = response.list;
// });
},
computed: {
//
isLastPage() {
if (this.total === 0) return false;
const totalPages = Math.ceil(this.total / this.queryParams.pageSize);
return this.queryParams.pageNum >= totalPages;
},
//
displayList() {
if (this.isLastPage && this.ckcargoList.length > 0) {
//
const totalRow = {
isTotalRow: true,
billNumber: '',
cargoName: '合计',
cargoWt: this.totalData.totalWt || 0,
cargoValue: this.totalData.totalValue || 0
};
return [...this.ckcargoList, totalRow];
}
return this.ckcargoList;
}
},
methods: {
getWarehouseList() {
let param = {};
@ -481,7 +453,7 @@ export default {
getCustList() {
let param = {};
listCust(param).then(response => {
this.custList = response.data;
this.custList = response;
}).catch(e => {
});
},
@ -511,36 +483,85 @@ export default {
this.shelfList = [];
});
},
getProductClassList() {
pmproductclassTreeSelect({}).then(response => {
//
this.productClassList = response || [];
}).catch(e => {
console.error('获取商品类别列表失败:', e);
this.productClassList = [];
});
},
/** 查询仓库入库管理列表 */
getList() {
this.loading = true;
this.queryParams.inoutType = "1";
if(this.queryParams.queryTime){
this.queryParams.beginDate=this.queryParams.queryTime[0];
this.queryParams.endDate=this.queryParams.queryTime[1];
this.queryParams.queryTime = [];
// 使 queryTime
if(this.queryTime && this.queryTime.length === 2){
this.queryParams.beginDate=this.queryTime[0];
this.queryParams.endDate=this.queryTime[1];
}else{
this.queryParams.beginDate=null;
this.queryParams.endDate=null;
}
if(this.queryParams.excTime){
this.queryParams.beginDateTwo=this.queryParams.excTime[0];
this.queryParams.endDateTwo=this.queryParams.excTime[1];
this.queryParams.excTime = [];
// 使 excTime
if(this.excTime && this.excTime.length === 2){
this.queryParams.beginDateTwo=this.excTime[0];
this.queryParams.endDateTwo=this.excTime[1];
}else{
this.queryParams.beginDateTwo=null;
this.queryParams.endDateTwo=null;
}
pageListCkcargo(this.queryParams).then(response => {
this.ckcargoList = response.list;
this.total = response.total;
//
const requestParams = { ...this.queryParams };
//
if (Array.isArray(requestParams.classIds) && requestParams.classIds.length > 0) {
requestParams.classIds = requestParams.classIds.join(',');
}
pageListCkcargo(requestParams).then(response => {
// response
if (response && (response.list || response.data)) {
this.ckcargoList = response.list || response.data.list || [];
this.total = response.total || response.data.total || 0;
} else {
this.ckcargoList = [];
this.total = 0;
}
this.loading = false;
//
this.getTotalData(requestParams);
}).catch(e => {
console.error('获取入库列表失败:', e);
this.ckcargoList = [];
this.total = 0;
this.loading = false;
});
selectTotalWt(this.queryParams).then(response => {
this.totalWeight = response;
},
/** 查询合计数据 */
getTotalData(params) {
//
const requestParams = { ...params };
//
if (requestParams && Array.isArray(requestParams.classIds) && requestParams.classIds.length > 0) {
requestParams.classIds = requestParams.classIds.join(',');
}
selectTotal(requestParams).then(response => {
if (response) {
this.totalData.totalWt = response.totalWt !== undefined ? response.totalWt : 0;
this.totalData.totalValue = response.totalValue !== undefined ? response.totalValue : 0;
} else {
this.totalData.totalWt = 0;
this.totalData.totalValue = 0;
}
}).catch(e => {
console.error('获取合计数据失败:', e);
this.totalData.totalWt = 0;
this.totalData.totalValue = 0;
});
},
//
@ -669,9 +690,15 @@ export default {
},
/** 导出按钮操作 */
handleExport() {
this.download('jxc/ckcargo/export', {
...this.queryParams
}, `仓库入库管理_${new Date().getTime()}.xlsx`)
//
const requestParams = { ...this.queryParams };
//
if (Array.isArray(requestParams.classIds) && requestParams.classIds.length > 0) {
requestParams.classIds = requestParams.classIds.join(',');
}
//
requestParams.inoutType = "1";
this.download('autogencode/ckbillcargo/export-instock', requestParams, `仓库入库管理_${new Date().getTime()}.xlsx`)
}
}
};

@ -0,0 +1,705 @@
<template>
<div class="app-container">
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="100px">
<!-- 商品类别 -->
<el-form-item label="商品类别" prop="classIds">
<el-select v-model="queryParams.classIds" placeholder="请选择商品类别" clearable multiple>
<el-option v-for="item in productClassList" :key="item.id" :label="item.className" :value="item.id"/>
</el-select>
</el-form-item>
<!-- 供应商 -->
<el-form-item label="供应商" prop="custId">
<el-select v-model="queryParams.custId" placeholder="请选择供应商" clearable>
<el-option v-for="item in custList" :key="item.id" :label="item.custName" :value="item.id"/>
</el-select>
</el-form-item>
<!-- 出库时间 -->
<el-form-item label="出库时间" prop="billDate">
<el-date-picker
v-model="queryTime"
type="daterange"
range-separator="—"
start-placeholder="开始日期"
end-placeholder="结束日期"
format="yyyy-MM-dd"
value-format="yyyy-MM-dd"
/>
</el-form-item>
<!-- 单据编号 -->
<el-form-item label="单据编号" prop="billNumber">
<el-input
v-model="queryParams.billNumber"
placeholder="请输入单据编号"
clearable
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<!-- 仓库名称 -->
<el-form-item label="仓库名称" prop="stockCode" class="search-item">
<el-select v-model="queryParams.stockCode" filterable placeholder="请选择">
<el-option v-for="dict in warehouses" :key="dict.stockCode" :label="dict.stockName" :value="dict.stockCode">
</el-option>
</el-select>
</el-form-item>
<!-- 操作按钮 -->
<el-form-item>
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery"></el-button>
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery"></el-button>
</el-form-item>
</el-form>
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button
type="warning"
plain
icon="el-icon-download"
size="mini"
@click="handleExport"
v-hasPermi="['jxc:ckcargo:export']"
>导出</el-button>
</el-col>
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
<el-table v-loading="loading" highlight-current-row :data="displayList" @sort-change="handleSortChange" @selection-change="handleSelectionChange">
<el-table-column label="序号" width="80" align="center">
<template slot-scope="{ $index, row }">
<span v-if="!row.isTotalRow">{{ (queryParams.pageNum - 1) * queryParams.pageSize + $index + 1 }}</span>
</template>
</el-table-column>
<el-table-column label="单据编号" align="center" prop="billNumber" sortable='custom' width="100">
<template slot-scope="{ row }">
<span v-if="row.isTotalRow" style="font-weight: bold;">{{ row.billNumber }}</span>
<span v-else>{{ row.billNumber }}</span>
</template>
</el-table-column>
<el-table-column label="仓库" align="center" prop="stockCode" sortable='custom' width="120">
<template slot-scope="scope">
<span v-if="!scope.row.isTotalRow">{{scope.row.stockName}}</span>
</template>
</el-table-column>
<el-table-column label="出库时间" align="center" prop="billDate" width="180" sortable='custom'>
<template slot-scope="scope">
<span v-if="!scope.row.isTotalRow">{{ parseTime(scope.row.billDate, '{y}-{m}-{d} {h}:{i}:{s}') }}</span>
</template>
</el-table-column>
<el-table-column label="商品编码" align="center" prop="hsCode" sortable='custom' width="100">
<template slot-scope="{ row }">
<span v-if="!row.isTotalRow">{{ row.hsCode }}</span>
</template>
</el-table-column>
<el-table-column label="商品名称" align="center" prop="cargoName" sortable='custom' width="100">
<template slot-scope="{ row }">
<span v-if="row.isTotalRow">{{ row.cargoName }}</span>
<span v-else>{{ row.cargoName }}</span>
</template>
</el-table-column>
<el-table-column label="批次号" align="center" prop="outBillNumber" sortable='custom' width="100">
<template slot-scope="{ row }">
<span v-if="!row.isTotalRow">{{ row.outBillNumber }}</span>
</template>
</el-table-column>
<el-table-column label="商品类别" align="center" prop="className" sortable='custom' width="100">
<template slot-scope="{ row }">
<span v-if="!row.isTotalRow">{{ row.className }}</span>
</template>
</el-table-column>
<el-table-column label="规格" show-overflow-tooltip align="center" prop="cargoSpec" sortable='custom' min-width="200">
<template slot-scope="{ row }">
<span v-if="!row.isTotalRow">{{ row.cargoSpec }}</span>
</template>
</el-table-column>
<el-table-column label="单位" align="center" prop="unit" sortable='custom' width="80">
<template slot-scope="scope">
<dict-tag v-if="!scope.row.isTotalRow" :options="dict.type.bm_measuring_unit" :value="scope.row.unit" />
</template>
</el-table-column>
<el-table-column label="数量" align="center" prop="cargoWt" sortable='custom' width="100">
<template slot-scope="{ row }">
<span v-if="row.isTotalRow" style="font-weight: bold;">{{ row.cargoWt }}</span>
<span v-else>{{ row.cargoWt }}</span>
</template>
</el-table-column>
<el-table-column label="单价" align="center" prop="unitPrice" sortable='custom' width="100">
<template slot-scope="{ row }">
<span v-if="!row.isTotalRow">{{ row.unitPrice }}</span>
</template>
</el-table-column>
<el-table-column label="金额" align="center" prop="cargoValue" sortable='custom' width="100">
<template slot-scope="{ row }">
<span v-if="row.isTotalRow" style="font-weight: bold;">{{ row.cargoValue }}</span>
<span v-else>{{ row.cargoValue }}</span>
</template>
</el-table-column>
<el-table-column label="供应商" align="center" prop="custName" sortable='custom' width="100">
<template slot-scope="{ row }">
<span v-if="!row.isTotalRow">{{ row.custName }}</span>
</template>
</el-table-column>
<el-table-column label="领用部门" align="center" prop="receiveDeptName" sortable='custom' width="100">
<template slot-scope="{ row }">
<span v-if="!row.isTotalRow">{{ row.receiveDeptName }}</span>
</template>
</el-table-column>
<el-table-column label="领用人" align="center" prop="receiverName" sortable='custom' width="100">
<template slot-scope="{ row }">
<span v-if="!row.isTotalRow">{{ row.receiverName }}</span>
</template>
</el-table-column>
</el-table>
<pagination
:total="total"
:page.sync="queryParams.pageNum"
:limit.sync="queryParams.pageSize"
@pagination="getList"
/>
</div>
</template>
<script>
import { ckbillcargoListApi as pageListCkcargo, ckbillcargoDetailApi as getCkcargo, ckbillcargoDeleteApi as delCkcargo, ckbillcargoCreateApi as addCkcargo, ckbillcargoUpdateApi as updateCkcargo,selectTotalWt, selectTotal } from "@/api/ckbillcargo";
// import { listStock } from "@/api/jxc/stock";
import { cmcustListByAll as listCust } from "@/api/cmcust";
import { cmcustproductListByAll as listCargo } from "@/api/cmcustproduct";
// import {listVessel,} from "@/api/jxc/vessel";
// import {listBmvoyage} from "@/api/jxc/bmvoyage";
import { listWarehouse } from "@/api/cmwarehouse";
import { cmshelfListByAll as listShelf } from "@/api/cmshelf";
import { pmproductclassTreeSelect } from "@/api/pmproductclass";
import WarehouseName from '@/components/WarehouseName';
export default {
name: "Instock",
components: {
WarehouseName,
},
dicts: ['sys_business_nature', 'sys_trade_nature','sys_inbound_outbound_type','sys_origin_country','bm_measuring_unit'],
data() {
return {
activeName: ['1', '2', '3'],
pickerOptions: {
shortcuts: [{
text: '最近一周',
onClick(picker) {
const end = new Date();
const start = new Date();
start.setTime(start.getTime() - 3600 * 1000 * 24 * 7);
picker.$emit('pick', [start, end]);
}
}, {
text: '最近一个月',
onClick(picker) {
const end = new Date();
const start = new Date();
start.setTime(start.getTime() - 3600 * 1000 * 24 * 30);
picker.$emit('pick', [start, end]);
}
}, {
text: '最近三个月',
onClick(picker) {
const end = new Date();
const start = new Date();
start.setTime(start.getTime() - 3600 * 1000 * 24 * 90);
picker.$emit('pick', [start, end]);
}
}]
},
totalWeight: 0,
//
totalData: {
totalWt: 0,
totalValue: 0
},
//
loading: true,
//
ids: [],
//
names: [],
//
single: true,
//
multiple: true,
//
showSearch: true,
//
total: 0,
//
queryTimeTwo: [],
//
queryTime: [],
//
warehouses: [],
//
bmvoyageList: [],
//
vesselList: [],
//
custList: [],
//
cargoList: [],
//
stockList: [],
//
shelfList: [],
//
productClassList: [],
//
ckcargoList: [],
//
title: "",
//
open: false,
//
queryParams: {
pageNum: 1,
pageSize: 10,
billId: null,
billDate: null,
billNumber: null,
inoutType: "2",
voyageId: null,
voyageNo: null,
vesselId: null,
vesselName: null,
cargoId: null,
hsCode: null,
cargoName: null,
billNo: null,
cargoSpec: null,
originCountry: null,
custId: null,
custName: null,
opType: null,
tradType: null,
cargoNum: null,
cargoWt: null,
cargoVol: null,
stockId: null,
stockCode: null,
stockName: null,
shelfName: null,
cargoValue:null,
customsNo:null,
listNumber:null,
beginDate: null,
endDate: null,
beginDateTwo: null,
endDateTwo: null,
bookNumber: null,
},
//
form: {},
//
rules: {
stockId: [
{ required: true, message: "库场ID不能为空", trigger: "blur" }
],
billDate: [
{ required: true, message: "单据日期不能为空", trigger: "blur" }
],
billNumber: [
{ required: true, message: "单据编号不能为空", trigger: "blur" }
],
inoutType: [
{ required: true, message: "出入库类型不能为空", trigger: "blur" }
],
voyageNo: [
{ required: true, message: "航次号不能为空", trigger: "blur" }
],
vesselName: [
{ required: true, message: "船舶名称不能为空", trigger: "blur" }
],
cargoName: [
{ required: true, message: "商品名称不能为空", trigger: "blur" }
],
cargoWt: [
{ required: true, message: "商品重量不能为空", trigger: "blur" }
],
cargoNum: [
{ required: true, message: "商品件数不能为空", trigger: "blur" }
],
cargoVol: [
{ required: true, message: "商品体积不能为空", trigger: "blur" }
],
stockName: [
{ required: true, message: "库场名称不能为空", trigger: "blur" }
],
custName: [
{ required: true, message: "供应商不能为空", trigger: "blur" }
],
}
};
},
created() {
this.getCustList();
this.getList();
// this.getStockList();
this.getCargoList();
this.getWarehouseList();
this.getShelfList();
this.getProductClassList();
// listWarehouse({}).then(response => {
// console.log(response)
// this.warehouses = response.list;
// });
},
computed: {
//
isLastPage() {
if (this.total === 0) return false;
const totalPages = Math.ceil(this.total / this.queryParams.pageSize);
const result = this.queryParams.pageNum >= totalPages;
console.log('isLastPage:', result, 'pageNum:', this.queryParams.pageNum, 'totalPages:', totalPages, 'total:', this.total);
return result;
},
//
displayList() {
console.log('totalData:', this.totalData);
console.log('ckcargoList length:', this.ckcargoList.length);
if (this.isLastPage && this.ckcargoList.length > 0) {
//
const totalRow = {
isTotalRow: true,
billNumber: '',
cargoName: '合计',
cargoWt: this.totalData.totalWt || 0,
cargoValue: this.totalData.totalValue || 0
};
console.log('Adding total row:', totalRow);
return [...this.ckcargoList, totalRow];
}
return this.ckcargoList;
}
},
methods: {
getWarehouseList() {
let param = {};
listWarehouse(param).then(response => {
this.warehouses = response || [];
}).catch(e => {
this.warehouses = [];
});
},
//
// setBmvoyageDetail(voyageId) {
// this.bmvoyageList.forEach(ele => {
// if (ele.id == voyageId) {
// this.form.voyageNo = ele.voyageNo;
// this.form.vesselName = ele.vesselName;
// // this.form.berthName = ele.berthName;
// this.form.deptId = ele.deptId;
// this.form.vesselId = ele.vesselId;
// this.form.netTon = ele.netTon;
// this.form.opType = ele.opType;
// }
// })
// },
// getBmvoyage() {
// let param = {};
// listBmvoyage(param).then(response => {
// this.bmvoyageList = response.data;
// }).catch(e => {
// });
// },
//
// setVesselDetail(vesselId) {
// this.vesselList.forEach(ele => {
// if (ele.id == vesselId) {
// this.form.vesselName = ele.vesselName;
//
// }
// })
// },
// getVesselList() {
// let param = {};
// listVessel(param).then(response => {
// this.vesselList = response.data;
// }).catch(e => {
// });
// },
//
getCargoList() {
let param = {};
listCargo(param).then(response => {
this.cargoList = response.data;
}).catch(e => {
});
},
getCustList() {
let param = {};
listCust(param).then(response => {
this.custList = response || [];
}).catch(e => {
});
},
//
setStockDetail(stockId) {
this.stockList.forEach(ele => {
if (ele.id == stockId) {
// this.form.stockId = ele.stockId;
this.form.stockName = ele.stockName;
this.form.stockCode = ele.stockCode;
}
})
},
// getStockList() {
// let param = {};
// listStock(param).then(response => {
// this.stockList = response.data;
// }).catch(e => {
// });
// },
//
getShelfList() {
let param = {};
listShelf(param).then(response => {
this.shelfList = response;
}).catch(e => {
this.shelfList = [];
});
},
getProductClassList() {
pmproductclassTreeSelect({}).then(response => {
//
this.productClassList = response || [];
}).catch(e => {
console.error('获取商品类别列表失败:', e);
this.productClassList = [];
});
},
/** 查询仓库出库管理列表 */
getList() {
this.loading = true;
//
const queryParamsCopy = {
...this.queryParams,
inoutType: "2",
beginDate: this.queryTime ? this.queryTime[0] : null,
endDate: this.queryTime ? this.queryTime[1] : null,
beginDateTwo: this.excTime ? this.excTime[0] : null,
endDateTwo: this.excTime ? this.excTime[1] : null
};
this.queryParams.inoutType = "2";
if(this.queryTime){
this.queryParams.beginDate=this.queryTime[0];
this.queryParams.endDate=this.queryTime[1];
}else{
this.queryParams.beginDate=null;
this.queryParams.endDate=null;
}
if(this.excTime){
this.queryParams.beginDateTwo=this.excTime[0];
this.queryParams.endDateTwo=this.excTime[1];
}else{
this.queryParams.beginDateTwo=null;
this.queryParams.endDateTwo=null;
}
//
const requestParams = { ...this.queryParams };
//
if (Array.isArray(requestParams.classIds) && requestParams.classIds.length > 0) {
requestParams.classIds = requestParams.classIds.join(',');
}
pageListCkcargo(requestParams).then(response => {
// response
if (response && (response.list || response.data)) {
this.ckcargoList = response.list || response.data.list || [];
this.total = response.total || response.data.total || 0;
} else {
this.ckcargoList = [];
this.total = 0;
}
this.loading = false;
//
this.getTotalData(queryParamsCopy);
}).catch(e => {
console.error('获取入库列表失败:', e);
this.ckcargoList = [];
this.total = 0;
this.loading = false;
});
},
/** 查询合计数据 */
getTotalData(params) {
//
const requestParams = { ...params };
//
if (requestParams && Array.isArray(requestParams.classIds) && requestParams.classIds.length > 0) {
requestParams.classIds = requestParams.classIds.join(',');
}
selectTotal(requestParams).then(response => {
console.log('合计数据:', response);
if (response) {
this.totalData.totalWt = response.totalWt !== undefined ? response.totalWt : 0;
this.totalData.totalValue = response.totalValue !== undefined ? response.totalValue : 0;
} else {
this.totalData.totalWt = 0;
this.totalData.totalValue = 0;
}
}).catch(e => {
console.error('获取合计数据失败:', e);
this.totalData.totalWt = 0;
this.totalData.totalValue = 0;
});
},
//
cancel() {
this.open = false;
this.reset();
},
//
reset() {
this.form = {
id: null,
billId: null,
billDate: null,
billNumber: null,
inoutType: "2",
voyageId: null,
voyageNo: null,
vesselId: null,
vesselName: null,
cargoId: null,
hsCode: null,
cargoName: null,
billNo: null,
cargoSpec: null,
originCountry: null,
custId: null,
custName: null,
opType: null,
tradType: null,
cargoNum: null,
cargoWt: null,
cargoVol: null,
stockId: null,
stockCode: null,
stockName: null,
remark: null,
cargoValue:null,
customsNo:null,
listNumber:null,
bookNumber:null,
};
this.resetForm("form");
},
//
handleSortChange(col) {
this.$sortBy(col, this.queryParams);
this.getList();
},
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.pageNum = 1;
if (this.queryParams.pageSize === undefined) {
this.queryParams.pageSize = 10;
}
this.getList();
},
/** 重置按钮操作 */
resetQuery() {
this.resetForm("queryForm");
this.queryTime = [];
this.queryTimeTwo = [];
this.queryParams = {
...this.queryParams,
pageNum: 1,
pageSize: 10
};
this.handleQuery();
},
//
handleSelectionChange(selection) {
this.ids = selection.map(item => item.id);
this.names = selection.map(item => item.id);
this.single = selection.length!==1;
this.multiple = !selection.length;
},
/** 新增按钮操作 */
handleAdd() {
this.reset();
this.form.inoutType="出库";
this.open = true;
this.title = "添加仓库出库管理";
},
/** 修改按钮操作 */
handleUpdate(row) {
this.reset();
const id = row.id || this.ids;
getCkcargo(id).then(response => {
this.form = response.data;
this.open = true;
this.title = "修改仓库出库管理";
});
},
/** 提交按钮 */
submitForm() {
this.$refs["form"].validate(valid => {
if (valid) {
if (this.form.id != null) {
updateCkcargo(this.form).then(response => {
this.$modal.msgSuccess("修改成功");
this.open = false;
this.getList();
});
} else {
addCkcargo(this.form).then(response => {
this.$modal.msgSuccess("新增成功");
this.open = false;
this.getList();
});
}
}
});
},
/** 删除按钮操作 */
handleDelete(row) {
const ids = row.id || this.ids;
const names = row.id || this.names;
this.$modal.confirm('是否确认删除"' + row.billNumber + '"的数据项?').then(function() {
return delCkcargo(ids);
}).then(() => {
this.getList();
this.$modal.msgSuccess("删除成功");
}).catch(() => {});
},
/** 导出按钮操作 */
handleExport() {
const params = { ...this.queryParams };
//
if (this.queryTime && this.queryTime.length === 2) {
params.beginDate = this.queryTime[0];
params.endDate = this.queryTime[1];
}
// classIds
if (params.classIds && Array.isArray(params.classIds) && params.classIds.length > 0) {
params.classIds = params.classIds.join(',');
}
this.download('autogencode/ckbillcargo/export', params, `仓库出库管理_${new Date().getTime()}.xlsx`)
}
}
};
</script>

@ -3,7 +3,9 @@
<el-dialog
:title="!dataForm.id ? '添加' : '修改'"
:close-on-click-modal="false"
:visible.sync="visible">
:visible.sync="visible"
v-dialogDrag
>
<!-- 新增和修改表单 -->
<el-form :model="dataForm" :rules="dataRule" ref="dataForm" @keyup.enter.native="dataSubmit()" label-width="120px">
<el-row :gutter="20">
@ -27,7 +29,7 @@
<el-col :span="12">
<el-form-item label="联系方式" prop="contactWay">
<el-input v-model="dataForm.contactWay" placeholder="联系方式"></el-input>
<el-button type="primary" size="small" style="margin-left: 10px;" @click="generateAccount"></el-button>
<!-- <el-button type="primary" size="small" style="margin-left: 10px;" @click="generateAccount"></el-button>-->
</el-form-item>
</el-col>
</el-row>

@ -3,7 +3,8 @@
<el-dialog
:title="!dataForm.id ? '添加' : '修改'"
:close-on-click-modal="false"
:visible.sync="visible">
:visible.sync="visible"
v-dialogDrag>
<!-- 新增和修改表单 -->
<el-form :model="dataForm" :rules="dataRule" ref="dataForm" @keyup.enter.native="dataSubmit()" label-width="110px">
<el-row :gutter="20">
@ -52,7 +53,7 @@
<el-col :span="12">
<el-form-item label="单位" prop="unit">
<el-select v-model="dataForm.unit" placeholder="选择单位" style="width: 100%" filterable>
<el-option v-for="dict in dict.type.sys_measurement_unit" :key="dict.value" :label="dict.label" :value="dict.value"></el-option>
<el-option v-for="dict in dict.type.bm_measuring_unit" :key="dict.value" :label="dict.label" :value="dict.value"></el-option>
</el-select>
</el-form-item>
</el-col>
@ -126,7 +127,7 @@
import Treeselect from "@riophae/vue-treeselect";
import "@riophae/vue-treeselect/dist/vue-treeselect.css";
export default {
dicts: ['goods_status', 'is_show','sys_measurement_unit'],
dicts: ['goods_status', 'is_show','bm_measuring_unit'],
components: { Treeselect },
data () {
return {
@ -144,25 +145,16 @@
salePrice: 0 ,
inDate: '' ,
expiryDate: '' ,
status: '' ,
status: '1' ,
isShow: '' ,
remark: '' ,
},
classTreeData: [],
supplierList: [],
dataRule: {
custId: [
{ required: true, message: '供应商 为必填项', trigger: 'blur' }
],
goodsCode: [
{ required: true, message: '商品编号 为必填项', trigger: 'blur' }
],
goodsName: [
{ required: true, message: '采购商品名称 为必填项', trigger: 'blur' }
],
status: [
{ required: true, message: '状态 为必填项', trigger: 'blur' }
],
}
}

@ -111,7 +111,7 @@
align="center"
label="单位">
<template slot-scope="scope">
<dict-tag :options="dict.type.sys_measurement_unit" :value="scope.row.unit"/>
<dict-tag :options="dict.type.bm_measuring_unit" :value="scope.row.unit"/>
</template>
</el-table-column>
<el-table-column
@ -208,7 +208,7 @@
import * as classApi from '@/api/pmproductclass.js'
import * as supplierApi from '@/api/cmcust.js'
export default {
dicts: ['goods_status', 'is_show','sys_measurement_unit'],
dicts: ['goods_status', 'is_show','bm_measuring_unit'],
data () {
return {
dataForm: {

@ -111,7 +111,7 @@
align="center"
label="单位">
<template slot-scope="scope">
<dict-tag :options="dict.type.sys_measurement_unit" :value="scope.row.unit"/>
<dict-tag :options="dict.type.bm_measuring_unit" :value="scope.row.unit"/>
</template>
</el-table-column>
<el-table-column
@ -208,7 +208,7 @@
import * as classApi from '@/api/pmproductclass.js'
import * as supplierApi from '@/api/cmcust.js'
export default {
dicts: ['goods_status', 'is_show','sys_measurement_unit'],
dicts: ['goods_status', 'is_show','bm_measuring_unit'],
data () {
return {
dataForm: {

@ -4,7 +4,9 @@
:title="!dataForm.id ? '添加' : '修改'"
:close-on-click-modal="false"
width="500px"
:visible.sync="visible">
:visible.sync="visible"
v-dialogDrag
>
<!-- 新增和修改表单 -->
<el-form :model="dataForm" :rules="dataRule" ref="dataForm" @keyup.enter.native="dataSubmit()" label-width="100px">
<el-form-item label="序号" prop="itemNo">

@ -4,7 +4,9 @@
:title="!dataForm.id ? '添加' : '修改'"
:close-on-click-modal="false"
:append-to-body="true"
:visible.sync="visible">
:visible.sync="visible"
v-dialogDrag
>
<!-- 新增和修改表单 -->
<el-form :model="dataForm" :rules="dataRule" ref="dataForm" @keyup.enter.native="dataSubmit()" label-width="80px">
<el-form-item label="货位代码" prop="storageLocationCode">

@ -4,7 +4,8 @@
:title="!dataForm.id ? '添加' : '修改'"
:close-on-click-modal="false"
:append-to-body="true"
:visible.sync="visible">
:visible.sync="visible"
v-dialogDrag>
<!-- 新增和修改表单 -->
<el-form :model="dataForm" :rules="dataRule" ref="dataForm" @keyup.enter.native="dataSubmit()" label-width="120px">
<el-form-item label="仓库代码" prop="stockCode">

@ -132,7 +132,7 @@
<el-form-item label="单位" prop="unit">
<el-select v-model="dataForm.unit" placeholder="请选择单位" clearable filterable style="width: 100%" :disabled="isViewMode">
<el-option
v-for="dict in dict.type.sys_measurement_unit"
v-for="dict in dict.type.bm_measuring_unit"
:key="dict.value"
:label="dict.label"
:value="dict.value"
@ -311,7 +311,7 @@
import * as ownerApi from '@/api/pmowner.js'
import * as tenantApi from '@/api/pmtenant.js'
export default {
dicts: ['sys_curr_type','sys_measurement_unit','biz_type','hx_status','charge_status','pay_method','ie_type','cust_type'],
dicts: ['sys_curr_type','bm_measuring_unit','biz_type','hx_status','charge_status','pay_method','ie_type','cust_type'],
data () {
return {
visible: false,

@ -124,7 +124,7 @@
align="center"
label="单位">
<template slot-scope="scope">
<dict-tag :options="dict.type.sys_measurement_unit" :value="scope.row.unit"/>
<dict-tag :options="dict.type.bm_measuring_unit" :value="scope.row.unit"/>
</template>
</el-table-column>
<el-table-column
@ -235,7 +235,7 @@
import AddOrUpdate from './fmbillcharge-add-and-update'
import * as api from '@/api/fmbillcharge.js'
export default {
dicts: ['sys_curr_type','sys_measurement_unit','biz_type','hx_status','charge_status','pay_method','ie_type','cust_type'],
dicts: ['sys_curr_type','bm_measuring_unit','biz_type','hx_status','charge_status','pay_method','ie_type','cust_type'],
data () {
return {
dataForm: {

@ -108,7 +108,7 @@
</el-table-column>
<el-table-column label="单位" align="center" prop="unit" sortable='custom'>
<template slot-scope="scope">
<dict-tag :options="dict.type.sys_measurement_unit" :value="scope.row.unit"/>
<dict-tag :options="dict.type.bm_measuring_unit" :value="scope.row.unit"/>
</template>
</el-table-column>
<el-table-column label="数量" align="center" prop="chargeNum" sortable='custom'/>

@ -11,7 +11,7 @@
<el-table-column prop="spec" header-align="center" align="center" label="规格"></el-table-column>
<el-table-column prop="unit" header-align="center" align="center" label="单位">
<template slot-scope="scope">
<DictTag :options="dict.type.sys_measurement_unit" :value="scope.row.unit"/>
<DictTag :options="dict.type.bm_measuring_unit" :value="scope.row.unit"/>
</template>
</el-table-column>
<el-table-column prop="demandQuantity" header-align="center" align="center" label="需求采购数量"></el-table-column>
@ -40,7 +40,7 @@
import * as detailApi from '@/api/pmcanteendemanddetail.js'
import DictTag from '@/components/DictTag'
export default {
dicts: ['sys_measurement_unit'],
dicts: ['bm_measuring_unit'],
components: {
DictTag
},

@ -4,6 +4,7 @@
:title="!dataForm.id ? '添加' : '修改'"
:close-on-click-modal="false"
:visible.sync="visible"
v-dialogDrag
width="80%">
<!-- 新增和修改表单 -->
<el-form :model="dataForm" :rules="dataRule" ref="dataForm" @keyup.enter.native="dataSubmit()" label-width="120px">
@ -59,7 +60,7 @@
<el-table-column prop="spec" header-align="center" align="center" label="规格"></el-table-column>
<el-table-column prop="unit" header-align="center" align="center" label="单位">
<template slot-scope="scope">
<DictTag :options="dict.type.sys_measurement_unit" :value="scope.row.unit"/>
<DictTag :options="dict.type.bm_measuring_unit" :value="scope.row.unit"/>
</template>
</el-table-column>
<el-table-column prop="demandQuantity" header-align="center" align="center" label="需求采购数量">
@ -166,7 +167,7 @@
import * as cmcustproductApi from '@/api/cmcustproduct.js'
import DictTag from '@/components/DictTag'
export default {
dicts: ['owner_type', 'demand_status', 'sys_measurement_unit'],
dicts: ['owner_type', 'demand_status', 'bm_measuring_unit'],
components: {
DictTag
},

@ -4,7 +4,9 @@
:title="!dataForm.id ? '添加' : '修改'"
:close-on-click-modal="false"
:append-to-body="true"
:visible.sync="visible">
:visible.sync="visible"
v-dialogDrag
>
<!-- 新增和修改表单 -->
<el-form :model="dataForm" :rules="dataRule" ref="dataForm" @keyup.enter.native="dataSubmit()" label-width="120px">
<el-form-item label="计划采购id" prop="planId" style="display: none;">

@ -14,7 +14,7 @@
<el-table-column prop="spec" header-align="center" align="center" label="规格"></el-table-column>
<el-table-column prop="unit" header-align="center" align="center" label="单位">
<template slot-scope="scope">
<DictTag :options="dict.type.sys_measurement_unit" :value="scope.row.unit" />
<DictTag :options="dict.type.bm_measuring_unit" :value="scope.row.unit" />
</template>
</el-table-column>
<el-table-column prop="orderQuantity" header-align="center" align="center" label="订单数量" style="width: 100px">
@ -101,7 +101,7 @@ import * as detailApi from '@/api/pmcanteenplanorderrel.js'
import * as orderApi from '@/api/pmcanteenpurchaseorder.js'
import DictTag from '@/components/DictTag'
export default {
dicts: ['sys_measurement_unit'],
dicts: ['bm_measuring_unit'],
components: {
DictTag
},

@ -122,7 +122,7 @@
align="center"
label="单位">
<template slot-scope="scope">
<DictTag :options="dict.type.sys_measurement_unit" :value="scope.row.unit" />
<DictTag :options="dict.type.bm_measuring_unit" :value="scope.row.unit" />
</template>
</el-table-column>
<el-table-column
@ -280,7 +280,7 @@
align="center"
label="单位">
<template slot-scope="scope">
<DictTag :options="dict.type.sys_measurement_unit" :value="scope.row.unit" />
<DictTag :options="dict.type.bm_measuring_unit" :value="scope.row.unit" />
</template>
</el-table-column>
<el-table-column
@ -386,7 +386,7 @@
align="center"
label="单位">
<template slot-scope="scope">
<DictTag :options="dict.type.sys_measurement_unit" :value="scope.row.unit" />
<DictTag :options="dict.type.bm_measuring_unit" :value="scope.row.unit" />
</template>
</el-table-column>
<el-table-column
@ -454,7 +454,7 @@
import * as cmcustproductApi from '@/api/cmcustproduct.js'
import DictTag from '@/components/DictTag'
export default {
dicts: ['sys_measurement_unit', 'pm_order_status'],
dicts: ['bm_measuring_unit', 'pm_order_status'],
data () {
return {
visible: false,

@ -16,7 +16,7 @@
<el-table-column prop="spec" header-align="center" align="center" label="规格"></el-table-column>
<el-table-column prop="unit" header-align="center" align="center" label="单位">
<template slot-scope="scope">
<DictTag :options="dict.type.sys_measurement_unit" :value="scope.row.unit"/>
<DictTag :options="dict.type.bm_measuring_unit" :value="scope.row.unit"/>
</template>
</el-table-column>
<el-table-column prop="planQuantity" header-align="center" align="center" label="计划采购数量" style="width: 100px">
@ -104,7 +104,7 @@ import * as detailApi from '@/api/pmcanteenpurchasedetail.js'
import DetailAddOrUpdate from '../detail/pmcanteenpurchasedetail-add-and-update.vue'
import DictTag from '@/components/DictTag'
export default {
dicts: ['sys_measurement_unit'],
dicts: ['bm_measuring_unit'],
components: {
DetailAddOrUpdate,
DictTag

@ -250,7 +250,7 @@
import * as demandApi from '@/api/pmcanteendemand.js'
import DictTag from '@/components/DictTag'
export default {
dicts: ['plan_status', 'demand_status', 'sys_measurement_unit'],
dicts: ['plan_status', 'demand_status', 'bm_measuring_unit'],
data () {
return {
dataForm: {

@ -139,7 +139,7 @@
align="center"
label="单位">
<template slot-scope="scope">
<dict-tag :options="dict.type.sys_measurement_unit" :value="scope.row.unit"/>
<dict-tag :options="dict.type.bm_measuring_unit" :value="scope.row.unit"/>
</template>
</el-table-column>
<el-table-column
@ -299,7 +299,7 @@
align="center"
label="单位">
<template slot-scope="scope">
<dict-tag :options="dict.type.sys_measurement_unit" :value="scope.row.unit"/>
<dict-tag :options="dict.type.bm_measuring_unit" :value="scope.row.unit"/>
</template>
</el-table-column>
<el-table-column
@ -395,7 +395,7 @@
align="center"
label="单位">
<template slot-scope="scope">
<DictTag :options="dict.type.sys_measurement_unit" :value="scope.row.unit" />
<DictTag :options="dict.type.bm_measuring_unit" :value="scope.row.unit" />
</template>
</el-table-column>
<el-table-column
@ -479,7 +479,7 @@
default: false
}
},
dicts: ['demand_status', 'plan_status', 'sys_measurement_unit'],
dicts: ['demand_status', 'plan_status', 'bm_measuring_unit'],
data () {
return {
isAddOperation: false, //

@ -56,7 +56,49 @@
{{ rankingType === 'like' ? scope.row.likeCount : scope.row.dislikeCount }}
</template>
</el-table-column>
<el-table-column header-align="center" align="center" label="操作" width="120" v-if="rankingType === 'dislike'">
<template slot-scope="scope">
<el-button type="primary" size="mini" @click="viewDislikeRecords(scope.row.id, scope.row.itemName)">查看点踩记录</el-button>
</template>
</el-table-column>
</el-table>
<!-- 点踩记录弹窗 -->
<el-dialog
:title="'点踩记录 - ' + dialogTitle"
:visible.sync="dialogVisible"
width="800px"
>
<el-table
:data="dislikeRecords"
border
v-loading="recordsLoading"
style="width: 100%;"
>
<el-table-column type="index" label="序号" width="80" align="center"></el-table-column>
<el-table-column header-align="center" align="center" label="用户">
<template slot-scope="scope">
{{ scope.row.userName || `用户${scope.row.userId}` }}
</template>
</el-table-column>
<el-table-column prop="menuDate" header-align="center" align="center" label="点踩日期">
<template slot-scope="scope">
{{ formatDateString(scope.row.menuDate) }}
</template>
</el-table-column>
<el-table-column prop="remark" header-align="center" align="center" label="意见"></el-table-column>
</el-table>
<el-pagination
hide-on-single-page
@size-change="recordsSizeChangeHandle"
@current-change="recordsCurrentChangeHandle"
:current-page="recordsPageIndex"
:page-sizes="[10, 20, 50, 100]"
:page-size="recordsPageSize"
:total="recordsTotal"
layout="total, sizes, prev, pager, next, jumper">
</el-pagination>
</el-dialog>
<el-pagination
hide-on-single-page
@size-change="sizeChangeHandle"
@ -91,7 +133,16 @@ export default {
pageIndex: 1,
pageSize: 10,
totalPage: 0,
dataListLoading: false
dataListLoading: false,
//
dialogVisible: false,
dialogTitle: '',
currentMenuDtlId: null,
dislikeRecords: [],
recordsPageIndex: 1,
recordsPageSize: 10,
recordsTotal: 0,
recordsLoading: false
}
},
mounted() {
@ -168,6 +219,79 @@ export default {
viewDetail(id) {
//
this.$message.info('查看菜品详情:' + id)
},
//
viewDislikeRecords(menuDtlId, itemName) {
this.currentMenuDtlId = menuDtlId
this.dialogTitle = itemName
this.recordsPageIndex = 1
this.getDislikeRecords()
this.dialogVisible = true
},
//
getDislikeRecords() {
this.recordsLoading = true
const params = {
page: this.recordsPageIndex,
limit: this.recordsPageSize,
menuDtlId: this.currentMenuDtlId,
likeType: '2' // 2
}
//
if (this.searchForm.dateRange && this.searchForm.dateRange.length === 2) {
params.startDate = this.formatDate(this.searchForm.dateRange[0])
params.endDate = this.formatDate(this.searchForm.dateRange[1])
}
// API
likeApi.pmdailymenudtllikeListApi(params).then((res) => {
this.recordsLoading = false
if (res && 'list' in res) {
this.dislikeRecords = res.list || []
this.recordsTotal = res.total || 0
} else {
this.dislikeRecords = []
this.recordsTotal = 0
}
}).catch((error) => {
this.recordsLoading = false
this.dislikeRecords = []
this.recordsTotal = 0
console.error(error)
})
},
//
recordsSizeChangeHandle(val) {
this.recordsPageSize = val
this.recordsPageIndex = 1
this.getDislikeRecords()
},
recordsCurrentChangeHandle(val) {
this.recordsPageIndex = val
this.getDislikeRecords()
},
//
formatDateString(dateString) {
if (!dateString) return ''
//
const date = new Date(dateString)
if (isNaN(date.getTime())) return dateString
const year = date.getFullYear()
const month = String(date.getMonth() + 1).padStart(2, '0')
const day = String(date.getDate()).padStart(2, '0')
const hours = String(date.getHours()).padStart(2, '0')
const minutes = String(date.getMinutes()).padStart(2, '0')
const seconds = String(date.getSeconds()).padStart(2, '0')
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`
},
//
getUserInfo(userId) {
if (!userId) return ''
//
// API
// ID
return `用户${userId}`
}
}
}

@ -4,7 +4,9 @@
:title="!dataForm.id ? '添加' : '修改'"
:close-on-click-modal="false"
:append-to-body="true"
:visible.sync="visible">
:visible.sync="visible"
v-dialogDrag
>
<!-- 新增和修改表单 -->
<el-form :model="dataForm" :rules="dataRule" ref="dataForm" @keyup.enter.native="dataSubmit()" label-width="80px">
<el-form-item label="菜品名称" prop="itemName">

@ -39,6 +39,8 @@
<el-button @click="resetForm()"></el-button>
<el-button v-hasPermi="['autogencode:pmdailymenu:save']" type="primary" @click="addOrUpdateHandle()"></el-button>
<el-button v-hasPermi="['autogencode:pmdailymenu:delete']" type="danger" @click="deleteHandle()" :disabled="dataListSelections.length <= 0"></el-button>
<el-button type="warning" @click="exportHandle()"></el-button>
<el-button type="info" @click="handleImport"></el-button>
</el-form-item>
</el-form>
<el-table
@ -130,6 +132,40 @@
<add-or-update v-if="addOrUpdateVisible" ref="addOrUpdate" @refreshDataList="getDataList"></add-or-update>
<!-- 菜单明细弹窗 -->
<menu-dtl-add-or-update v-if="menuDtlAddOrUpdateVisible" ref="menuDtlAddOrUpdate" @refreshDataList="handleDtlRefresh"></menu-dtl-add-or-update>
<!-- 导入对话框 -->
<el-dialog :title="upload.title" :visible.sync="upload.open" width="400px" append-to-body>
<el-upload
ref="upload"
:limit="1"
accept=".xlsx, .xls"
:headers="upload.headers"
:action="''"
:disabled="upload.isUploading"
:on-progress="handleFileUploadProgress"
:on-success="handleFileSuccess"
:auto-upload="false"
:http-request="uploadFile"
drag
>
<i class="el-icon-upload"></i>
<div class="el-upload__text">将文件拖到此处<em>点击上传</em></div>
<div class="el-upload__tip text-center" slot="tip">
<div class="el-upload__tip" slot="tip">
<el-checkbox v-model="upload.updateSupport"/>
是否更新已经存在的数据
</div>
<span>仅允许导入xlsxlsx格式文件</span>
<el-link type="primary" :underline="false" style="font-size:12px;vertical-align: baseline;"
@click="importTemplate">下载模板
</el-link>
</div>
</el-upload>
<div slot="footer" class="dialog-footer">
<el-button type="primary" @click="submitFileForm"> </el-button>
<el-button @click="upload.open = false"> </el-button>
</div>
</el-dialog>
</div>
</template>
@ -156,11 +192,26 @@
pageSize: 10,
totalPage: 0,
dataListLoading: false,
dataListSelections: [],
dataListSelections: [],
addOrUpdateVisible: false,
menuDtlAddOrUpdateVisible: false,
refreshKey: 0,
expandedRowKeys: [] // ID
expandedRowKeys: [], // ID
//
upload: {
//
open: false,
//
title: "",
//
isUploading: false,
//
updateSupport: 0,
//
headers: {Authorization: "Bearer " + sessionStorage.getItem('token')},
//
url: process.env.VUE_APP_BASE_API + "/api/autogencode/pmdailymenu/import"
}
}
},
components: {
@ -326,6 +377,92 @@
})
})
},
//
exportHandle () {
//
const params = {}
//
if (this.dataForm.menuDate) params.menuDate = this.dataForm.menuDate;
if (this.dataForm.canteenName) params.canteenName = this.dataForm.canteenName;
if (this.dataForm.mealType) params.mealType = this.dataForm.mealType;
if (this.dataForm.status) params.status = this.dataForm.status;
if (this.dataForm.remark) params.remark = this.dataForm.remark;
// API
api.pmdailymenuExportApi(params).then(res => {
//
const url = window.URL.createObjectURL(new Blob([res]))
const link = document.createElement('a')
link.style.display = 'none'
link.href = url
link.setAttribute('download', `菜单导出_${new Date().getTime()}.xlsx`)
document.body.appendChild(link)
link.click()
document.body.removeChild(link)
this.$message.success('导出成功')
}).catch(e => {
this.$message.error('导出失败')
})
},
//
handleImport() {
this.upload.title = "菜单信息导入"
this.upload.open = true
},
//
importTemplate() {
// API
api.pmdailymenuImportTemplateApi().then(blob => {
// 使blob
const url = window.URL.createObjectURL(blob)
const link = document.createElement('a')
link.href = url
link.setAttribute('download', `菜单导入模板_${new Date().getTime()}.xlsx`)
document.body.appendChild(link)
link.click()
//
document.body.removeChild(link)
window.URL.revokeObjectURL(url)
}).catch(error => {
console.error('下载模板失败:', error)
this.$message.error('下载模板失败,请重试')
})
},
//
handleFileUploadProgress(event, file, fileList) {
this.upload.isUploading = true
},
//
handleFileSuccess(response, file, fileList) {
this.upload.open = false
this.upload.isUploading = false
this.$refs.upload.clearFiles()
this.$alert("<div style='overflow: auto;overflow-x: hidden;max-height: 70vh;padding: 10px 20px 0;'>" + response.msg + "</div>", "导入结果", {dangerouslyUseHTMLString: true})
this.getDataList()
},
//
submitFileForm() {
this.$refs.upload.submit()
},
//
uploadFile(file) {
const formData = new FormData()
formData.append('file', file.file)
formData.append('updateSupport', this.upload.updateSupport)
api.pmdailymenuImportApi(formData).then(response => {
this.$message.success('导入成功')
this.upload.open = false
this.upload.fileList = []
//
this.getDataList()
}).catch(error => {
this.$message.error('导入失败:' + (error.response?.data?.message || '未知错误'))
}).finally(() => {
this.upload.isUploading = false
})
},
}
}
</script>

@ -4,7 +4,9 @@
:title="!dataForm.id ? '添加' : '修改'"
:close-on-click-modal="false"
:append-to-body="true"
:visible.sync="visible">
:visible.sync="visible"
v-dialogDrag
>
<!-- 新增和修改表单 -->
<el-form :model="dataForm" :rules="dataRule" ref="dataForm" @keyup.enter.native="dataSubmit()" label-width="80px">
<el-row>

@ -186,7 +186,7 @@ export default {
costAmount: '' ,
completePhoto: '' ,
ownerConfirm: '否' ,
status: '0' ,
status: '1' ,
files: []
},

@ -144,11 +144,12 @@
<el-table-column
header-align="center"
align="center"
width="150"
width="200"
label="操作">
<template slot-scope="scope">
<el-button type="text" size="small" @click="addOrUpdateHandle(scope.row.id)">{{ '' }}</el-button>
<el-button v-hasPermi="['autogencode:pmmaintenanceorder:update']" v-if="scope.row.status !== '99'" type="text" size="small" @click="completeOrder(scope.row.id)">{{ '' }}</el-button>
<el-button v-hasPermi="['autogencode:pmmaintenanceorder:update']" v-if="scope.row.status === '1'" type="text" size="small" @click="submitOrder(scope.row.id)">{{ '' }}</el-button>
<el-button v-hasPermi="['autogencode:pmmaintenanceorder:update']" v-if="scope.row.status !== '99' && scope.row.status !== '1'" type="text" size="small" @click="completeOrder(scope.row.id)">{{ '' }}</el-button>
<el-button v-hasPermi="['autogencode:pmmaintenanceorder:update']" v-if="scope.row.status === '99'" type="text" size="small" @click="revokeOrder(scope.row.id)">{{ '' }}</el-button>
<el-button v-hasPermi="['autogencode:pmmaintenanceorder:delete']" type="text" size="small" @click="deleteHandle(scope.row.id)" style="color: #f56c6c;"></el-button>
</template>
@ -397,6 +398,27 @@ import DictTag from '@/components/DictTag'
}).catch(() => {
})
},
// 稿
submitOrder (id) {
this.$confirm('确定要提交订单吗?提交后状态将变为待处理', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
// API
api.pmmaintenanceorderUpdateStatusAndRemarkApi({
id: id,
status: '0', // 0
remark: ''
}).then(res => {
this.$message.success('提交成功')
this.getDataList()
}).catch(() => {
this.$message.error('提交失败')
})
}).catch(() => {
})
},
}
}
</script>

@ -256,7 +256,7 @@ export default {
reportChannel: '',
reportTime: '',
urgencyLevel: '',
status: '0',
status: '1',
remark: '',
files: [], //
afterProcessFiles: [] //
@ -632,7 +632,7 @@ export default {
}.bind(this))
} else {
//
this.setFileUploadPermissions('0'); //
this.setFileUploadPermissions('1'); // 稿
//
this.faultPhotoFiles = []
this.afterProcessFiles = []

@ -5,7 +5,8 @@
:close-on-click-modal="false"
:append-to-body="true"
:visible.sync="visible"
width="600px">
width="600px"
v-dialogDrag>
<!-- 新增和修改表单 -->
<el-form :model="dataForm" :rules="dataRule" ref="dataForm" @keyup.enter.native="dataSubmit()" label-width="100px">
<!-- 父分类选择 -->

@ -454,7 +454,7 @@ export default {
assigneContext: this.form.handleContent,
dispatchNote: this.form.dispatchNote,
expectedCompleteTime: this.form.expectedCompleteTime,
status: '0',
status: '1',
assignTime: this.getCurrentTime(),
files: []
};

@ -62,6 +62,12 @@ public class AdminLoginController {
public CommonResult<SystemAdminResponse> getAdminInfo() {
return CommonResult.success(adminLoginService.getInfoByToken());
}
@ApiOperation(value="根据uid获取用户详情")
@GetMapping(value = "/getAdminInfoByUid")
public CommonResult<SystemAdminResponse> getAdminInfoByUid(@RequestParam Long uid) {
return CommonResult.success(adminLoginService.getInfoByUid(uid));
}
/**
*

@ -46,7 +46,6 @@ public class SystemAdminController {
* @param systemAdminRequest
* @param pageParamRequest
*/
@PreAuthorize("hasAuthority('admin:system:admin:list')")
@ApiOperation(value = "分页列表")
@RequestMapping(value = "/list", method = RequestMethod.GET)
@ResponseBody

@ -48,4 +48,9 @@ public interface AdminLoginService {
* Token
*/
SystemAdminResponse getInfoByToken();
/**
* uid
*/
SystemAdminResponse getInfoByUid(Long uid);
}

@ -2,6 +2,7 @@ package com.zbkj.admin.service.impl;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.zbkj.admin.filter.TokenComponent;
import com.zbkj.admin.service.AdminLoginService;
import com.zbkj.admin.service.ValidateCodeService;
@ -11,6 +12,8 @@ import com.zbkj.common.exception.CrmebException;
import com.zbkj.common.model.system.SystemAdmin;
import com.zbkj.common.model.system.SystemMenu;
import com.zbkj.common.model.system.SystemPermissions;
import com.zbkj.common.model.system.SystemRole;
import com.zbkj.common.model.user.User;
import com.zbkj.common.request.SystemAdminLoginRequest;
import com.zbkj.common.response.MenusResponse;
import com.zbkj.common.response.SystemAdminResponse;
@ -19,10 +22,8 @@ import com.zbkj.common.response.SystemLoginResponse;
import com.zbkj.common.utils.SecurityUtil;
import com.zbkj.common.vo.LoginUserVo;
import com.zbkj.common.vo.MenuTree;
import com.zbkj.service.service.SystemAdminService;
import com.zbkj.service.service.SystemConfigService;
import com.zbkj.service.service.SystemGroupDataService;
import com.zbkj.service.service.SystemMenuService;
import com.zbkj.service.dao.SystemMenuDao;
import com.zbkj.service.service.*;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationManager;
@ -33,6 +34,7 @@ import org.springframework.security.core.AuthenticationException;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@ -75,6 +77,16 @@ public class AdminLoginServiceImpl implements AdminLoginService {
@Autowired
private SystemMenuService systemMenuService;
@Autowired
private SystemMenuDao systemMenuDao;
@Autowired
private SystemRoleService systemRoleService;
@Autowired
private UserService userService;
/**
* PC
*/
@ -184,4 +196,69 @@ public class AdminLoginServiceImpl implements AdminLoginService {
systemAdminResponse.setPermissionsList(permList);
return systemAdminResponse;
}
/**
* uid
*/
@Override
public SystemAdminResponse getInfoByUid(Long uid) {
// 先查询sysUser然后用其中的adminId查询对应的管理员
User user = userService.getById(uid);
if (user == null) {
throw new CrmebException("用户不存在");
}
Integer adminId = user.getAdminid();
if (adminId == null) {
throw new CrmebException("用户不是管理员");
}
SystemAdmin systemAdmin = systemAdminService.getById(adminId);
if (systemAdmin == null) {
throw new CrmebException("管理员不存在");
}
SystemAdminResponse systemAdminResponse = new SystemAdminResponse();
BeanUtils.copyProperties(systemAdmin, systemAdminResponse);
List<String> roleList = Stream.of(systemAdmin.getRoles().split(",")).collect(Collectors.toList());
List<String> permList = CollUtil.newArrayList();
if (roleList.contains("1")) {
permList.add("*:*:*");
} else {
// 获取用户权限
permList = getPermissionsByAdminId(systemAdmin.getId());
}
systemAdminResponse.setPermissionsList(permList);
return systemAdminResponse;
}
/**
* ID
* @param adminId ID
* @return
*/
private List<String> getPermissionsByAdminId(Integer adminId) {
SystemAdmin systemAdmin = systemAdminService.getById(adminId);
if (systemAdmin == null || systemAdmin.getIsDel()) {
throw new CrmebException("管理员不存在");
}
// 获取角色列表
List<String> roleList = new ArrayList<>();
if (StrUtil.isNotBlank(systemAdmin.getRoles())) {
roleList = Stream.of(systemAdmin.getRoles().split(",")).collect(Collectors.toList());
}
// 如果是超管,返回所有权限
if (roleList.contains("1")) {
return CollUtil.newArrayList("*:*:*");
}
// 获取用户权限
List<SystemMenu> menuList =systemMenuDao.getMenusByUserIdAndPhoto(adminId);
menuList = menuList.stream().filter(e -> StrUtil.isNotEmpty(e.getPerms())).collect(Collectors.toList());
List<String> permissions = menuList.stream().map(SystemMenu::getPerms).collect(Collectors.toList());
return permissions;
}
}

@ -24,6 +24,12 @@ public class QuickOutBillRequest {
@ApiModelProperty(value = "商品列表")
private List<QuickOutBillItem> items;
/**
* id
*/
@ApiModelProperty(value = "用户id")
private Long uid;
/**
*
*/
@ -42,6 +48,12 @@ public class QuickOutBillRequest {
@ApiModelProperty(value = "数量")
private BigDecimal quantity;
/**
* id
*/
@ApiModelProperty(value = "入库单据id")
private Long ckCargoStockId;
}
}

@ -0,0 +1,117 @@
package com.zbkj.common.response;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import com.alibaba.excel.annotation.ExcelIgnore;
import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.annotation.write.style.ColumnWidth;
import java.math.BigDecimal;
/**
*
*/
@Data
public class InventoryResponse {
/**
*
*/
@ApiModelProperty(value = "序号")
@ExcelProperty(value = "序号", index = 0)
@ColumnWidth(10)
private Integer index;
/**
*
*/
@ApiModelProperty(value = "商品名称")
@ExcelIgnore
private String cargoName;
/**
*
*/
@ApiModelProperty(value = "规格型号")
@ExcelIgnore
private String cargoSpec;
/**
*
*/
@ApiModelProperty(value = "品名及规格")
@ExcelProperty(value = "品名及规格", index = 1)
@ColumnWidth(30)
private String cargoNameWithSpec;
/**
*
*/
@ApiModelProperty(value = "上期结存数量")
@ExcelProperty(value = {"上期结存", "数量"}, index = 2)
@ColumnWidth(15)
private BigDecimal lastCargoNum;
/**
*
*/
@ApiModelProperty(value = "上期结存金额")
@ExcelProperty(value = {"上期结存", "金额"}, index = 3)
@ColumnWidth(15)
private BigDecimal lastCargoValue;
/**
*
*/
@ApiModelProperty(value = "本期入库数量")
@ExcelProperty(value = {"本期入库", "数量"}, index = 4)
@ColumnWidth(15)
private BigDecimal inCargoNum;
/**
*
*/
@ApiModelProperty(value = "本期入库金额")
@ExcelProperty(value = {"本期入库", "金额"}, index = 5)
@ColumnWidth(15)
private BigDecimal inCargoValue;
/**
*
*/
@ApiModelProperty(value = "本期出库数量")
@ExcelProperty(value = {"本期出库", "数量"}, index = 6)
@ColumnWidth(15)
private BigDecimal outCargoNum;
/**
*
*/
@ApiModelProperty(value = "本期出库金额")
@ExcelProperty(value = {"本期出库", "金额"}, index = 7)
@ColumnWidth(15)
private BigDecimal outCargoValue;
/**
*
*/
@ApiModelProperty(value = "累计结存数量")
@ExcelProperty(value = {"累计结存", "数量"}, index = 8)
@ColumnWidth(15)
private BigDecimal cargoNum;
/**
*
*/
@ApiModelProperty(value = "累计结存金额")
@ExcelProperty(value = {"累计结存", "金额"}, index = 9)
@ColumnWidth(15)
private BigDecimal cargoValue;
/**
*
*/
@ApiModelProperty(value = "单位")
@ExcelIgnore
private String unit;
}

@ -4,6 +4,7 @@ import java.math.BigDecimal;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
@ -31,6 +32,12 @@ import org.springframework.security.access.prepost.PreAuthorize;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.zbkj.modules.autogencode.entity.CkCargoStock;
import com.zbkj.modules.autogencode.service.CkCargoStockService;
import com.zbkj.modules.autogencode.entity.CmWarehouse;
import com.zbkj.modules.autogencode.entity.CmCustProduct;
import com.zbkj.modules.autogencode.entity.PmProductClass;
import com.zbkj.modules.autogencode.service.CmWarehouseService;
import com.zbkj.modules.autogencode.service.CmCustProductService;
import com.zbkj.modules.autogencode.service.PmProductClassService;
@ -53,6 +60,15 @@ public class CkCargoStockController {
@Autowired
private CkBillStockService ckBillStockService;
@Autowired
private CmWarehouseService cmWarehouseService;
@Autowired
private CmCustProductService cmCustProductService;
@Autowired
private PmProductClassService pmProductClassService;
/**
*
*
@ -397,6 +413,7 @@ public class CkCargoStockController {
"hs_code",
"cargo_number",
"stock_date",
"stock_id",
"sum(cargo_num) as cargo_num",
"sum(cargo_wt) as cargo_wt",
"sum(cargo_vol) as cargo_vol"
@ -441,7 +458,8 @@ public class CkCargoStockController {
"origin_country",
"hs_code",
"cargo_number",
"stock_date"
"stock_date",
"stock_id"
);
// 排序处理
@ -473,6 +491,34 @@ public class CkCargoStockController {
Page<CkCargoStock> page = new Page<>(pageParamRequest.getPage(), pageParamRequest.getLimit());
Page<CkCargoStock> resultPage = ckCargoStockService.page(page, queryWrapper);
// 补充仓库、分类、单位信息
for (CkCargoStock stock : resultPage.getRecords()) {
// 仓库信息
if (stock.getStockId() != null) {
CmWarehouse warehouse = cmWarehouseService.getById(stock.getStockId());
if (warehouse != null) {
stock.setStockCode(warehouse.getStockCode());
stock.setStockName(warehouse.getStockName());
}
}
// 根据cargoId查询CmCustProduct获取分类、单位等信息
if (stock.getCargoId() != null) {
CmCustProduct product = cmCustProductService.getById(stock.getCargoId());
if (product != null) {
stock.setClassId(product.getClassId());
stock.setUnit(product.getUnit());
// 根据classId查询物资类别代码
if (product.getClassId() != null) {
PmProductClass productClass = pmProductClassService.getById(product.getClassId());
if (productClass != null) {
stock.setClassCode(productClass.getClassCode());
stock.setClassName(productClass.getClassName());
}
}
}
}
}
CommonPage<CkCargoStock> commonPage = new CommonPage<>();
commonPage.setPage((int) resultPage.getCurrent());
commonPage.setLimit((int) resultPage.getSize());
@ -502,9 +548,10 @@ public class CkCargoStockController {
CkCargoStock::getRemark, CkCargoStock::getCreateDept, CkCargoStock::getCreateBy, CkCargoStock::getCreateTime, CkCargoStock::getUpdateBy,
CkCargoStock::getUpdateTime, CkCargoStock::getDelFlag);
condition(queryWrapper, ckCargoStock);
if(ckCargoStock.getCustId()!=null){
// 过滤掉重量为0的商品除非明确指定要显示
if (null == ckCargoStock.getIsShow() || !ckCargoStock.getIsShow()) {
queryWrapper.and(qw -> {
qw.gt(CkCargoStock::getCargoWt, "0")
qw.gt(CkCargoStock::getCargoWt, 0)
;
});
}
@ -574,6 +621,11 @@ public class CkCargoStockController {
return;
}
// 仓库ID
if (request.getStockId() != null) {
queryWrapper.eq("stock_id", request.getStockId());
}
// 商品ID
if (request.getCargoId() != null) {
queryWrapper.eq("cargo_id", request.getCargoId());
@ -614,6 +666,21 @@ public class CkCargoStockController {
queryWrapper.eq("cargo_number", request.getCargoNumber());
}
// 商品类别ID
if (request.getClassId() != null) {
// 通过子查询获取对应类别的商品ID
LambdaQueryWrapper<CmCustProduct> productQueryWrapper = new LambdaQueryWrapper<>();
productQueryWrapper.eq(CmCustProduct::getClassId, request.getClassId());
List<CmCustProduct> products = cmCustProductService.list(productQueryWrapper);
if (!products.isEmpty()) {
List<Long> cargoIds = products.stream().map(CmCustProduct::getId).collect(java.util.stream.Collectors.toList());
queryWrapper.in("cargo_id", cargoIds);
} else {
// 如果没有找到对应类别的商品,返回空结果
queryWrapper.eq("cargo_id", -1);
}
}
// 入库日期
if (request.getStockDate() != null) {
queryWrapper.eq("stock_date", request.getStockDate());

@ -1,12 +1,15 @@
package com.zbkj.modules.autogencode.controller;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.zbkj.common.request.PageParamRequest;
import com.zbkj.common.request.InventoryRequest;
import com.zbkj.common.response.CommonResult;
import com.zbkj.common.response.InventoryResponse;
import com.zbkj.common.page.CommonPage;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
@ -270,4 +273,30 @@ public class CkStockChangeController {
return CommonResult.failed();
}
/**
*
*/
@ApiOperation(value = "查询物资盘点表数据")
@RequestMapping(value = "/inventory", method = RequestMethod.POST)
public CommonResult<CommonPage<InventoryResponse>> getInventoryList(@RequestBody InventoryRequest request) {
List<InventoryResponse> list = ckStockChangeService.getInventoryList(request);
CommonPage<InventoryResponse> page = new CommonPage<>();
page.setList(list);
page.setTotal(Long.valueOf(list.size()));
return CommonResult.success(page);
}
/**
*
*/
@ApiOperation(value = "导出物资盘点表数据")
@RequestMapping(value = "/export-inventory", method = RequestMethod.GET)
public void exportInventory(InventoryRequest request) {
ckStockChangeService.exportInventory(request);
}
}

@ -218,6 +218,8 @@ public class CmCustController {
return CommonResult.success(cmCustList);
}
/**
*
*/

@ -356,7 +356,7 @@ public class CmCustProductController {
String unit = cmCustProduct.getUnit();
String translatedUnit = unit;
List<SysDictData> dictDataList = sysDictDataService.list(new LambdaQueryWrapper<SysDictData>()
.eq(SysDictData::getDictType, "sys_measurement_unit")
.eq(SysDictData::getDictType, "bm_measuring_unit")
.eq(SysDictData::getDictValue, unit)
.eq(SysDictData::getStatus, "0"));
if (!dictDataList.isEmpty()) {

@ -1,26 +1,39 @@
package com.zbkj.modules.autogencode.controller;
import java.util.*;
import java.io.IOException;
import java.io.OutputStream;
import java.math.BigDecimal;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.zbkj.common.request.PageParamRequest;
import com.zbkj.common.response.CommonResult;
import com.zbkj.common.page.CommonPage;
import org.apache.poi.hssf.util.HSSFColor;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.*;
import org.apache.poi.hssf.usermodel.*;
import org.apache.poi.ss.util.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import io.swagger.annotations.ApiOperation;
import org.springframework.security.access.prepost.PreAuthorize;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.multipart.MultipartFile;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.ss.usermodel.DateUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.zbkj.modules.autogencode.entity.PmDailyMenu;
import com.zbkj.modules.autogencode.entity.PmDailyMenuDtl;
import com.zbkj.modules.autogencode.entity.SysDictData;
import com.zbkj.modules.autogencode.service.PmDailyMenuService;
import com.zbkj.modules.autogencode.service.PmDailyMenuDtlService;
import com.zbkj.modules.autogencode.service.SysDictDataService;
@ -36,6 +49,9 @@ public class PmDailyMenuController {
@Autowired
private PmDailyMenuDtlService pmDailyMenuDtlService;
@Autowired
private SysDictDataService sysDictDataService;
@ -200,6 +216,15 @@ public class PmDailyMenuController {
*/
@RequestMapping(value = "/delete", method = RequestMethod.POST)
public CommonResult<String> delete(@RequestBody Long[] ids){
// 检查是否有关联的PmDailyMenuDtl
for (Long id : ids) {
LambdaQueryWrapper<PmDailyMenuDtl> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(PmDailyMenuDtl::getMenuId, id);
if (pmDailyMenuDtlService.count(queryWrapper) > 0) {
return CommonResult.failed("菜单下存在关联的菜单明细,无法删除");
}
}
if (pmDailyMenuService.removeByIds(Arrays.asList(ids))) {
return CommonResult.success();
}
@ -400,4 +425,693 @@ public class PmDailyMenuController {
}
}
/**
*
*/
@ApiOperation(value = "导出菜单")
@RequestMapping(value = "/export", method = RequestMethod.GET)
public void export(PmDailyMenu request, HttpServletResponse response) throws IOException {
// 构建查询条件
LambdaQueryWrapper<PmDailyMenu> queryWrapper = new LambdaQueryWrapper();
condition(queryWrapper, request);
queryWrapper.orderByDesc(PmDailyMenu::getMenuDate);
queryWrapper.orderByDesc(PmDailyMenu::getCanteenName);
queryWrapper.orderByDesc(PmDailyMenu::getMealType);
// 查询菜单数据
List<PmDailyMenu> menus = pmDailyMenuService.list(queryWrapper);
// 为每个菜单查询明细
for (PmDailyMenu menu : menus) {
LambdaQueryWrapper<PmDailyMenuDtl> dtlQueryWrapper = new LambdaQueryWrapper<>();
dtlQueryWrapper.eq(PmDailyMenuDtl::getMenuId, menu.getId());
List<PmDailyMenuDtl> menuDtls = pmDailyMenuDtlService.list(dtlQueryWrapper);
menu.setPmDailyMenuDtls(menuDtls);
}
// 创建 Excel 工作簿
Workbook workbook = new XSSFWorkbook();
// 为每个菜单创建一个 sheet
for (PmDailyMenu menu : menus) {
// 创建 sheet名称为 日期-食堂-餐别
java.text.SimpleDateFormat sdf = new java.text.SimpleDateFormat("yyyy-MM-dd");
String dateStr = sdf.format(menu.getMenuDate());
String sheetName = dateStr + "-" + menu.getCanteenName() + "-" + getMealTypeLabel(menu.getMealType());
// 移除无效字符
sheetName = sheetName.replaceAll("[:\\/\\?\\*\\[\\]]", "");
// 限制 sheet 名称长度
if (sheetName.length() > 31) {
sheetName = sheetName.substring(0, 31);
}
Sheet sheet = workbook.createSheet(sheetName);
// 设置列宽
sheet.setColumnWidth(0, 5000);
sheet.setColumnWidth(1, 3000);
sheet.setColumnWidth(2, 5000);
sheet.setColumnWidth(3, 3000);
sheet.setColumnWidth(4, 5000);
sheet.setColumnWidth(5, 3000);
sheet.setColumnWidth(6, 5000);
sheet.setColumnWidth(7, 3000);
// 创建标题行
Row titleRow = sheet.createRow(0);
titleRow.setHeightInPoints(40);
// 为所有列创建单元格并应用背景色
for (int i = 0; i <= 7; i++) {
Cell cell = titleRow.createCell(i);
if (i == 0) {
cell.setCellValue(getWeekday(menu.getMenuDate()) + "" + getMealTypeLabel(menu.getMealType()) + "");
}
cell.setCellStyle(getTitleCellStyle(workbook));
}
sheet.addMergedRegion(new org.apache.poi.ss.util.CellRangeAddress(0, 0, 0, 7));
// 创建表头
Row headerRow = sheet.createRow(1);
headerRow.setHeightInPoints(30);
// 为所有列创建单元格并应用背景色
for (int i = 0; i <= 7; i++) {
Cell cell = headerRow.createCell(i);
cell.setCellStyle(getHeaderCellStyle(workbook));
}
// 设置表头内容并合并单元格
Cell cell0 = headerRow.getCell(0);
cell0.setCellValue("主荤菜");
sheet.addMergedRegion(new org.apache.poi.ss.util.CellRangeAddress(1, 1, 0, 1));
Cell cell2 = headerRow.getCell(2);
cell2.setCellValue("半荤菜");
sheet.addMergedRegion(new org.apache.poi.ss.util.CellRangeAddress(1, 1, 2, 3));
Cell cell4 = headerRow.getCell(4);
cell4.setCellValue("素菜");
sheet.addMergedRegion(new org.apache.poi.ss.util.CellRangeAddress(1, 1, 4, 5));
Cell cell6 = headerRow.getCell(6);
cell6.setCellValue("炖罐");
sheet.addMergedRegion(new org.apache.poi.ss.util.CellRangeAddress(1, 1, 6, 7));
// 分类菜品
Map<String, List<PmDailyMenuDtl>> dishMap = new HashMap<>();
dishMap.put("主荤菜", new ArrayList<>());
dishMap.put("半荤菜", new ArrayList<>());
dishMap.put("素菜", new ArrayList<>());
dishMap.put("炖罐", new ArrayList<>());
dishMap.put("免费主食", new ArrayList<>());
dishMap.put("付费主食", new ArrayList<>());
dishMap.put("打包/西点", new ArrayList<>());
dishMap.put("打包/中点", new ArrayList<>());
if (menu.getPmDailyMenuDtls() != null) {
for (PmDailyMenuDtl dtl : menu.getPmDailyMenuDtls()) {
String itemType = dtl.getItemType();
if ("ZHC".equals(itemType)) {
dishMap.get("主荤菜").add(dtl);
} else if ("BHC".equals(itemType)) {
dishMap.get("半荤菜").add(dtl);
} else if ("SC".equals(itemType)) {
dishMap.get("素菜").add(dtl);
} else if ("DG".equals(itemType)) {
dishMap.get("炖罐").add(dtl);
} else if ("MFZS".equals(itemType)) {
dishMap.get("免费主食").add(dtl);
} else if ("FFZS".equals(itemType)) {
dishMap.get("付费主食").add(dtl);
} else if ("DBXD".equals(itemType)) {
dishMap.get("打包/西点").add(dtl);
} else if ("DBZD".equals(itemType)) {
dishMap.get("打包/中点").add(dtl);
}
}
}
// 填充数据
int rowIndex = 2;
int maxRows = Math.max(
Math.max(dishMap.get("主荤菜").size(), dishMap.get("半荤菜").size()),
Math.max(dishMap.get("素菜").size(), dishMap.get("炖罐").size())
);
for (int i = 0; i < maxRows; i++) {
Row dataRow = sheet.createRow(rowIndex++);
dataRow.setHeightInPoints(25);
// 为所有列创建单元格并应用背景色
for (int j = 0; j <= 7; j++) {
Cell cell = dataRow.createCell(j);
cell.setCellStyle(getDataCellStyle(workbook));
}
// 主荤菜
if (i < dishMap.get("主荤菜").size()) {
PmDailyMenuDtl dtl = dishMap.get("主荤菜").get(i);
Cell mainCell0 = dataRow.getCell(0);
mainCell0.setCellValue(dtl.getItemName());
Cell mainCell1 = dataRow.getCell(1);
mainCell1.setCellValue(dtl.getItemPrice().doubleValue());
}
// 半荤菜
if (i < dishMap.get("半荤菜").size()) {
PmDailyMenuDtl dtl = dishMap.get("半荤菜").get(i);
Cell halfCell2 = dataRow.getCell(2);
halfCell2.setCellValue(dtl.getItemName());
Cell halfCell3 = dataRow.getCell(3);
halfCell3.setCellValue(dtl.getItemPrice().doubleValue());
}
// 素菜
if (i < dishMap.get("素菜").size()) {
PmDailyMenuDtl dtl = dishMap.get("素菜").get(i);
Cell vegCell4 = dataRow.getCell(4);
vegCell4.setCellValue(dtl.getItemName());
Cell vegCell5 = dataRow.getCell(5);
vegCell5.setCellValue(dtl.getItemPrice().doubleValue());
}
// 炖罐
if (i < dishMap.get("炖罐").size()) {
PmDailyMenuDtl dtl = dishMap.get("炖罐").get(i);
Cell stewCell6 = dataRow.getCell(6);
stewCell6.setCellValue(dtl.getItemName());
Cell stewCell7 = dataRow.getCell(7);
stewCell7.setCellValue(dtl.getItemPrice().doubleValue());
} else if (dishMap.get("炖罐").isEmpty()) {
// 只有当没有炖罐菜品时才显示"以实际供应为准"
Cell stewCell6 = dataRow.getCell(6);
stewCell6.setCellValue("以实际供应为准");
}
}
// 添加主食和免费项目
// 免费主食
if (!dishMap.get("免费主食").isEmpty()) {
Row freeRow = sheet.createRow(rowIndex++);
freeRow.setHeightInPoints(25);
// 为所有列创建单元格并应用背景色
for (int i = 0; i <= 7; i++) {
Cell cell = freeRow.createCell(i);
if (i == 0) {
StringBuilder freeItems = new StringBuilder("免费:");
List<PmDailyMenuDtl> freeStapleList = dishMap.get("免费主食");
for (int j = 0; j < freeStapleList.size(); j++) {
PmDailyMenuDtl dtl = freeStapleList.get(j);
freeItems.append(dtl.getItemName());
if (j < freeStapleList.size() - 1) {
freeItems.append(" ");
}
}
cell.setCellValue(freeItems.toString());
}
cell.setCellStyle(getContentCellStyle(workbook));
}
sheet.addMergedRegion(new org.apache.poi.ss.util.CellRangeAddress(rowIndex-1, rowIndex-1, 0, 7));
}
// 付费主食
if (!dishMap.get("付费主食").isEmpty()) {
Row paidRow = sheet.createRow(rowIndex++);
paidRow.setHeightInPoints(25);
// 为所有列创建单元格并应用背景色
for (int i = 0; i <= 7; i++) {
Cell cell = paidRow.createCell(i);
if (i == 0) {
StringBuilder paidItems = new StringBuilder("付费主食:");
List<PmDailyMenuDtl> paidStapleList = dishMap.get("付费主食");
for (int j = 0; j < paidStapleList.size(); j++) {
PmDailyMenuDtl dtl = paidStapleList.get(j);
paidItems.append(dtl.getItemName());
paidItems.append(" ").append(dtl.getItemPrice().doubleValue());
if (j < paidStapleList.size() - 1) {
paidItems.append(" ");
}
}
cell.setCellValue(paidItems.toString());
}
cell.setCellStyle(getContentCellStyle(workbook));
}
sheet.addMergedRegion(new org.apache.poi.ss.util.CellRangeAddress(rowIndex-1, rowIndex-1, 0, 7));
}
// 打包品种
List<PmDailyMenuDtl> westernList = dishMap.get("打包/西点");
List<PmDailyMenuDtl> chineseList = dishMap.get("打包/中点");
if (!westernList.isEmpty() || !chineseList.isEmpty()) {
Row packRow = sheet.createRow(rowIndex++);
packRow.setHeightInPoints(25);
// 为所有列创建单元格并应用背景色
for (int i = 0; i <= 7; i++) {
Cell cell = packRow.createCell(i);
if (i == 0) {
StringBuilder packItems = new StringBuilder("打包品种:");
// 西点
if (!westernList.isEmpty()) {
packItems.append("西点: ");
for (int j = 0; j < westernList.size(); j++) {
PmDailyMenuDtl dtl = westernList.get(j);
packItems.append(dtl.getItemName());
packItems.append(" ").append(dtl.getItemPrice().doubleValue());
if (j < westernList.size() - 1) {
packItems.append(" ");
}
}
}
// 中点
if (!chineseList.isEmpty()) {
if (!westernList.isEmpty()) {
packItems.append(" ");
}
packItems.append("中点: ");
for (int j = 0; j < chineseList.size(); j++) {
PmDailyMenuDtl dtl = chineseList.get(j);
packItems.append(dtl.getItemName());
packItems.append(" ").append(dtl.getItemPrice().doubleValue());
if (j < chineseList.size() - 1) {
packItems.append(" ");
}
}
}
cell.setCellValue(packItems.toString());
}
cell.setCellStyle(getContentCellStyle(workbook));
}
sheet.addMergedRegion(new org.apache.poi.ss.util.CellRangeAddress(rowIndex-1, rowIndex-1, 0, 7));
}
Row remarkRow = sheet.createRow(rowIndex++);
remarkRow.setHeightInPoints(25);
// 为所有列创建单元格并应用背景色,避免右下角出现缺口
for (int i = 0; i <= 7; i++) {
Cell cell = remarkRow.createCell(i);
if (i == 0) {
cell.setCellValue("(注:当日菜品以实物为准)");
}
cell.setCellStyle(getContentCellStyle(workbook));
}
sheet.addMergedRegion(new org.apache.poi.ss.util.CellRangeAddress(rowIndex-1, rowIndex-1, 0, 7));
}
// 设置响应头
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
response.setHeader("Content-Disposition", "attachment; filename=menu_export_" + new Date().getTime() + ".xlsx");
// 输出文件
OutputStream outputStream = response.getOutputStream();
workbook.write(outputStream);
outputStream.flush();
outputStream.close();
workbook.close();
}
/**
*
*/
private String getWeekday(Date date) {
String[] weekdays = {"星期日", "星期一", "星期二", "星期三", "星期四", "星期五", "星期六"};
Calendar calendar = Calendar.getInstance();
calendar.setTime(date);
int day = calendar.get(Calendar.DAY_OF_WEEK) - 1;
return weekdays[day];
}
/**
* #4A80B9
* @param style
* @param workbook 簿
*/
private void setSteelBlueBackground(CellStyle style, Workbook workbook) {
if (workbook instanceof XSSFWorkbook) {
// POI 3.17 版本的 XSSFColor 构造方式
XSSFColor customColor = new XSSFColor(new java.awt.Color(74, 128, 185));
XSSFCellStyle xssfStyle = (XSSFCellStyle) style;
xssfStyle.setFillForegroundColor(customColor);
} else if (workbook instanceof HSSFWorkbook) {
// POI 3.17 版本的 HSSFColor 设置方式
HSSFWorkbook hssfWorkbook = (HSSFWorkbook) workbook;
HSSFPalette palette = hssfWorkbook.getCustomPalette();
HSSFColor customColor = palette.findColor((byte) 74, (byte) 128, (byte) 185);
if (customColor == null) {
customColor = palette.addColor((byte) 74, (byte) 128, (byte) 185);
}
HSSFCellStyle hssfStyle = (HSSFCellStyle) style;
hssfStyle.setFillForegroundColor(customColor.getIndex());
} else {
// 其他类型工作簿使用内置颜色
style.setFillForegroundColor(IndexedColors.LIGHT_BLUE.getIndex());
}
style.setFillPattern(FillPatternType.SOLID_FOREGROUND);
}
/**
*
*/
private CellStyle getTitleCellStyle(Workbook workbook) {
CellStyle style = workbook.createCellStyle();
style.setAlignment(HorizontalAlignment.CENTER);
style.setVerticalAlignment(VerticalAlignment.CENTER);
// 添加边框
style.setBorderTop(BorderStyle.THIN);
style.setBorderBottom(BorderStyle.THIN);
style.setBorderLeft(BorderStyle.THIN);
style.setBorderRight(BorderStyle.THIN);
Font font = workbook.createFont();
font.setFontName("宋体");
font.setFontHeightInPoints((short) 16);
font.setBold(true);
style.setFont(font);
// 使用钢蓝色 #4A80B9
setSteelBlueBackground(style, workbook);
font.setColor(IndexedColors.WHITE.getIndex());
return style;
}
/**
*
*/
private CellStyle getHeaderCellStyle(Workbook workbook) {
CellStyle style = workbook.createCellStyle();
style.setAlignment(HorizontalAlignment.CENTER);
style.setVerticalAlignment(VerticalAlignment.CENTER);
// 添加边框
style.setBorderTop(BorderStyle.THIN);
style.setBorderBottom(BorderStyle.THIN);
style.setBorderLeft(BorderStyle.THIN);
style.setBorderRight(BorderStyle.THIN);
Font font = workbook.createFont();
font.setFontName("宋体");
font.setFontHeightInPoints((short) 12);
font.setBold(true);
style.setFont(font);
// 使用钢蓝色 #4A80B9
setSteelBlueBackground(style, workbook);
font.setColor(IndexedColors.WHITE.getIndex());
return style;
}
/**
*
*/
private CellStyle getContentCellStyle(Workbook workbook) {
CellStyle style = workbook.createCellStyle();
style.setAlignment(HorizontalAlignment.LEFT);
style.setVerticalAlignment(VerticalAlignment.CENTER);
// 添加边框
style.setBorderTop(BorderStyle.THIN);
style.setBorderBottom(BorderStyle.THIN);
style.setBorderLeft(BorderStyle.THIN);
style.setBorderRight(BorderStyle.THIN);
Font font = workbook.createFont();
font.setFontName("宋体");
font.setFontHeightInPoints((short) 11);
style.setFont(font);
// 使用钢蓝色 #4A80B9
setSteelBlueBackground(style, workbook);
font.setColor(IndexedColors.WHITE.getIndex());
return style;
}
/**
*
*/
private CellStyle getDataCellStyle(Workbook workbook) {
CellStyle style = workbook.createCellStyle();
style.setAlignment(HorizontalAlignment.LEFT);
style.setVerticalAlignment(VerticalAlignment.CENTER);
// 添加边框
style.setBorderTop(BorderStyle.THIN);
style.setBorderBottom(BorderStyle.THIN);
style.setBorderLeft(BorderStyle.THIN);
style.setBorderRight(BorderStyle.THIN);
Font font = workbook.createFont();
font.setFontName("宋体");
font.setFontHeightInPoints((short) 11);
style.setFont(font);
// 使用钢蓝色 #4A80B9
setSteelBlueBackground(style, workbook);
font.setColor(IndexedColors.WHITE.getIndex());
return style;
}
/**
*
* @param file Excel
*/
@PostMapping("/import")
public ResponseEntity<CommonResult<String>> importMenu(@RequestParam("file") MultipartFile file) {
try {
// 验证文件
if (file == null || file.isEmpty()) {
return ResponseEntity.ok(CommonResult.failed("文件不能为空"));
}
// 读取Excel文件
Workbook workbook = WorkbookFactory.create(file.getInputStream());
Sheet sheet = workbook.getSheetAt(0);
// 开始处理数据
int successCount = 0;
int errorCount = 0;
// 从第二行开始读取数据(第一行是表头)
for (int rowNum = 1; rowNum <= sheet.getLastRowNum(); rowNum++) {
Row row = sheet.getRow(rowNum);
if (row == null) {
continue;
}
try {
// 读取数据
String menuDate = getCellValue(row.getCell(0));
String canteenName = getCellValue(row.getCell(1));
String mealType = getCellValue(row.getCell(2));
String itemName = getCellValue(row.getCell(3));
String itemType = getCellValue(row.getCell(4));
String itemPriceStr = getCellValue(row.getCell(5));
// 翻译菜品类型(中文名称转字典键值)
itemType = translateItemType(itemType);
// 翻译餐别(中文名称转字典键值)
mealType = translateMealType(mealType);
// 翻译食堂名称(中文名称转字典键值)
canteenName = translateCanteenName(canteenName);
// 验证必填字段
if (StringUtils.isEmpty(menuDate) || StringUtils.isEmpty(canteenName) ||
StringUtils.isEmpty(mealType) || StringUtils.isEmpty(itemName) ||
StringUtils.isEmpty(itemType) || StringUtils.isEmpty(itemPriceStr)) {
errorCount++;
continue;
}
// 解析价格
BigDecimal itemPrice = new BigDecimal(itemPriceStr);
// 查找或创建菜单
PmDailyMenu menu = pmDailyMenuService.findByDateAndCanteenAndMealType(menuDate, canteenName, mealType);
if (menu == null) {
menu = new PmDailyMenu();
menu.setMenuDate(new java.text.SimpleDateFormat("yyyy-MM-dd").parse(menuDate));
menu.setCanteenName(canteenName);
menu.setMealType(mealType);
menu.setStatus("0"); // 未发布
pmDailyMenuService.save(menu);
}
// 创建菜品明细
PmDailyMenuDtl dtl = new PmDailyMenuDtl();
dtl.setMenuId(menu.getId());
dtl.setItemName(itemName);
dtl.setItemType(itemType);
dtl.setItemPrice(itemPrice);
pmDailyMenuDtlService.save(dtl);
successCount++;
} catch (Exception e) {
errorCount++;
}
}
workbook.close();
return ResponseEntity.ok(CommonResult.success("导入成功,成功:" + successCount + ",失败:" + errorCount));
} catch (Exception e) {
return ResponseEntity.ok(CommonResult.failed("导入失败:" + e.getMessage()));
}
}
/**
*
*/
private String getCellValue(Cell cell) {
if (cell == null) {
return "";
}
switch (cell.getCellTypeEnum()) {
case STRING:
return cell.getStringCellValue().trim();
case NUMERIC:
if (DateUtil.isCellDateFormatted(cell)) {
return new java.text.SimpleDateFormat("yyyy-MM-dd").format(cell.getDateCellValue());
} else {
return String.valueOf(cell.getNumericCellValue());
}
case BOOLEAN:
return String.valueOf(cell.getBooleanCellValue());
case FORMULA:
try {
return String.valueOf(cell.getNumericCellValue());
} catch (Exception e) {
return cell.getStringCellValue();
}
default:
return "";
}
}
/**
*
*/
private String translateItemType(String itemType) {
if (StringUtils.isEmpty(itemType)) {
return itemType;
}
// 从系统字典获取菜品类型映射
List<SysDictData> dictDataList = sysDictDataService.getByDictType("item_type");
Map<String, String> itemTypeMap = new HashMap<>();
for (SysDictData dictData : dictDataList) {
itemTypeMap.put(dictData.getDictLabel(), dictData.getDictValue());
}
// 如果是中文名称,返回对应的字典键值
if (itemTypeMap.containsKey(itemType)) {
return itemTypeMap.get(itemType);
}
// 否则返回原值(可能已经是字典键值)
return itemType;
}
/**
*
*/
private String translateMealType(String mealType) {
if (StringUtils.isEmpty(mealType)) {
return mealType;
}
// 从系统字典获取餐别映射
List<SysDictData> dictDataList = sysDictDataService.getByDictType("meal_type");
Map<String, String> mealTypeMap = new HashMap<>();
for (SysDictData dictData : dictDataList) {
mealTypeMap.put(dictData.getDictLabel(), dictData.getDictValue());
}
// 如果是中文名称,返回对应的字典键值
if (mealTypeMap.containsKey(mealType)) {
return mealTypeMap.get(mealType);
}
// 否则返回原值(可能已经是字典键值)
return mealType;
}
/**
*
*/
private String translateCanteenName(String canteenName) {
if (StringUtils.isEmpty(canteenName)) {
return canteenName;
}
// 从系统字典获取食堂名称映射
List<SysDictData> dictDataList = sysDictDataService.getByDictType("canteen_name");
Map<String, String> canteenNameMap = new HashMap<>();
for (SysDictData dictData : dictDataList) {
canteenNameMap.put(dictData.getDictLabel(), dictData.getDictValue());
}
// 如果是中文名称,返回对应的字典键值
if (canteenNameMap.containsKey(canteenName)) {
return canteenNameMap.get(canteenName);
}
// 没有对应的情况下,取第一个字典值
if (!dictDataList.isEmpty()) {
return dictDataList.get(0).getDictValue();
}
// 否则返回原值
return canteenName;
}
/**
*
* @param response
*/
@GetMapping("/import/template")
public void importTemplate(HttpServletResponse response) {
try {
// 创建Excel工作簿
Workbook workbook = new XSSFWorkbook();
Sheet sheet = workbook.createSheet("菜单导入模板");
// 创建表头
Row headerRow = sheet.createRow(0);
String[] headers = {"日期", "食堂名称", "餐别", "菜品名称", "菜品类型", "菜品价格"};
for (int i = 0; i < headers.length; i++) {
Cell cell = headerRow.createCell(i);
cell.setCellValue(headers[i]);
// 设置表头样式
CellStyle style = workbook.createCellStyle();
Font font = workbook.createFont();
font.setFontName("宋体");
font.setFontHeightInPoints((short) 12);
font.setBold(true);
style.setFont(font);
style.setAlignment(HorizontalAlignment.CENTER);
style.setVerticalAlignment(VerticalAlignment.CENTER);
// 添加边框
style.setBorderTop(BorderStyle.THIN);
style.setBorderBottom(BorderStyle.THIN);
style.setBorderLeft(BorderStyle.THIN);
style.setBorderRight(BorderStyle.THIN);
cell.setCellStyle(style);
}
// 设置列宽
sheet.setColumnWidth(0, 15 * 256);
sheet.setColumnWidth(1, 15 * 256);
sheet.setColumnWidth(2, 10 * 256);
sheet.setColumnWidth(3, 20 * 256);
sheet.setColumnWidth(4, 15 * 256);
sheet.setColumnWidth(5, 10 * 256);
// 设置响应头
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
response.setHeader("Content-Disposition", "attachment; filename=菜单导入模板.xlsx");
// 输出文件
OutputStream outputStream = response.getOutputStream();
workbook.write(outputStream);
outputStream.flush();
outputStream.close();
workbook.close();
} catch (Exception e) {
}
}
}

@ -18,6 +18,7 @@ import com.zbkj.modules.autogencode.entity.PmDailyMenu;
import com.zbkj.modules.autogencode.service.PmDailyMenuService;
import com.zbkj.modules.autogencode.entity.PmDailyMenuDtl;
import com.zbkj.modules.autogencode.service.PmDailyMenuDtlService;
import com.zbkj.service.service.SystemAdminService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
@ -48,7 +49,8 @@ public class PmDailyMenuDtlLikeController {
@Autowired
private PmDailyMenuService pmDailyMenuService;
@Autowired
private SystemAdminService systemAdminService;
/**
@ -134,6 +136,26 @@ public class PmDailyMenuDtlLikeController {
condition(queryWrapper, request);
CommonPage<PmDailyMenuDtlLike> page = CommonPage.restPage(pmDailyMenuDtlLikeService.pageList(queryWrapper, pageParamRequest));
// 这里进行翻译是通过systemAdminService进行翻译
if (page != null && page.getList() != null) {
for (PmDailyMenuDtlLike item : page.getList()) {
if (item.getUserId() != null) {
try {
// 通过systemAdminService根据userId获取用户信息
// 假设SystemAdmin实体有getName()方法获取用户名称
com.zbkj.common.model.system.SystemAdmin admin = systemAdminService.getById(item.getUserId());
if (admin != null) {
// 将用户名称设置到PmDailyMenuDtlLike的某个字段中
// 假设PmDailyMenuDtlLike有setUserName方法
item.setUserName(admin.getRealName());
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
return CommonResult.success(page);
}
@ -186,10 +208,11 @@ public class PmDailyMenuDtlLikeController {
* @param menuDtlId ID
* @param likeType 1-2-
* @param uid ID使ID使ID
* @param remark
*/
@ApiOperation(value = "对菜单明细进行点赞/点踩")
@RequestMapping(value = "/like", method = RequestMethod.POST)
public CommonResult<String> likeOrDislike(@RequestParam Long menuDtlId, @RequestParam String likeType, @RequestParam(required = false) Long uid) {
public CommonResult<String> likeOrDislike(@RequestParam Long menuDtlId, @RequestParam String likeType, @RequestParam(required = false) Long uid, @RequestParam(required = false) String remark) {
try {
// 获取当前登录用户ID
LoginUserVo loginUserVo = SecurityUtil.getLoginUserVo();
@ -247,6 +270,7 @@ public class PmDailyMenuDtlLikeController {
like.setUserId(userId);
like.setMenuDate(menuDate);
like.setLikeType(likeType);
like.setRemark(remark);
like.setDelFlag("0");
like.setCreateBy(userId);
like.setCreateTime(new Date());

@ -201,6 +201,7 @@ public class PmMaintenanceDispatchController {
// 应用搜索条件
condition(queryWrapper, request);
queryWrapper.orderByDesc(PmMaintenanceDispatch::getAssignTime);
CommonPage<PmMaintenanceDispatch> page = CommonPage.restPage(pmMaintenanceDispatchService.pageList(queryWrapper, pageParamRequest));
@ -233,6 +234,8 @@ public class PmMaintenanceDispatchController {
// 部门ID格式错误忽略
}
}
// 设置文件信息
setDispatchFile(item);
}
return CommonResult.success(page);

@ -17,10 +17,19 @@ import com.zbkj.common.response.CommonResult;
import com.zbkj.common.page.CommonPage;
import com.zbkj.common.utils.SecurityUtil;
import com.zbkj.common.vo.LoginUserVo;
import com.zbkj.common.constants.Constants;
import com.zbkj.common.constants.UserConstants;
import com.zbkj.common.utils.DateUtil;
import com.zbkj.service.service.SysDeptService;
import com.zbkj.service.service.SystemAdminService;
import com.zbkj.service.service.UserService;
import com.zbkj.common.model.user.User;
import com.zbkj.common.constants.NotifyConstants;
import com.zbkj.service.service.TemplateMessageService;
import com.zbkj.service.service.SystemNotificationService;
import com.zbkj.service.service.UserTokenService;
import com.zbkj.common.model.system.SystemNotification;
import com.zbkj.common.model.user.UserToken;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
@ -86,6 +95,12 @@ public class PmMaintenanceOrderController {
private PmTenantHouseRelService pmTenantHouseRelService;
@Autowired
private SysDeptService sysDeptService;
@Autowired
private TemplateMessageService templateMessageService;
@Autowired
private SystemNotificationService systemNotificationService;
@Autowired
private UserTokenService userTokenService;
/**
@ -489,9 +504,9 @@ public class PmMaintenanceOrderController {
}
// 办结时检查处理后文件
if ("99".equals(pmMaintenanceOrder.getStatus()) && (pmMaintenanceOrder.getAfterProcessFiles() == null || pmMaintenanceOrder.getAfterProcessFiles().isEmpty())) {
return CommonResult.failed("办结时必须上传修复凭证");
}
// if ("99".equals(pmMaintenanceOrder.getStatus()) && (pmMaintenanceOrder.getAfterProcessFiles() == null || pmMaintenanceOrder.getAfterProcessFiles().isEmpty())) {
// return CommonResult.failed("办结时必须上传修复凭证");
// }
if (pmMaintenanceOrderService.save(pmMaintenanceOrder)) {
// 获取保存后的订单ID
@ -507,6 +522,9 @@ public class PmMaintenanceOrderController {
return CommonResult.failed();
}
// 写一个方法, 这里进行推送,首先查出对应的角色,然后对应的用户
/**
*
*/
@ -518,9 +536,14 @@ public class PmMaintenanceOrderController {
}
// 办结时检查处理后文件
if ("99".equals(pmMaintenanceOrder.getStatus()) && (pmMaintenanceOrder.getAfterProcessFiles() == null || pmMaintenanceOrder.getAfterProcessFiles().isEmpty())) {
return CommonResult.failed("办结时必须上传修复凭证");
}
// if ("99".equals(pmMaintenanceOrder.getStatus()) && (pmMaintenanceOrder.getAfterProcessFiles() == null || pmMaintenanceOrder.getAfterProcessFiles().isEmpty())) {
// return CommonResult.failed("办结时必须上传修复凭证");
// }
// 记录原状态
PmMaintenanceOrder oldOrder = pmMaintenanceOrderService.getById(pmMaintenanceOrder.getId());
String oldStatus = oldOrder != null ? oldOrder.getStatus() : null;
String newStatus = pmMaintenanceOrder.getStatus();
if (pmMaintenanceOrderService.updateById(pmMaintenanceOrder)) {
// 获取订单ID
@ -530,6 +553,11 @@ public class PmMaintenanceOrderController {
// 处理报修派单记录列表
handleDispatchRecords(orderId, dispatchList);
// 当从草稿(1)修改为待处理(0)时,发送推送通知
if ("1".equals(oldStatus) && "0".equals(newStatus)) {
sendMaintenanceNotification(pmMaintenanceOrder);
}
return CommonResult.success();
}
@ -575,11 +603,53 @@ public class PmMaintenanceOrderController {
}
}
/**
*
* @param orderId ID
* @param dispatchList
*
* @param pmMaintenanceOrder
*/
private void sendMaintenanceNotification(PmMaintenanceOrder pmMaintenanceOrder) {
// 发送用户通知
SystemNotification userNotification = systemNotificationService.getByMark(NotifyConstants.MAINTENANCE_NOTICE_MARK);
if (userNotification != null && userNotification.getIsWechat().equals(1)) {
// 获取用户的微信openid
UserToken userToken = userTokenService.getTokenByUserId(Math.toIntExact(pmMaintenanceOrder.getUid()), UserConstants.USER_TOKEN_TYPE_WECHAT);
if (userToken != null) {
// 构建通知内容
HashMap<String, String> temMap = new HashMap<>();
temMap.put(Constants.WE_CHAT_TEMP_KEY_FIRST, "您的报修申请已提交成功!");
temMap.put("keyword1", pmMaintenanceOrder.getOrderNo());
temMap.put("keyword2", pmMaintenanceOrder.getFaultType());
temMap.put("keyword3", pmMaintenanceOrder.getUrgencyLevel());
temMap.put("keyword4", DateUtil.nowDateTimeStr());
temMap.put(Constants.WE_CHAT_TEMP_KEY_END, "我们将尽快处理您的报修申请,请耐心等待。");
// 发送微信模板消息
templateMessageService.pushTemplateMessage(userNotification.getWechatId(), temMap, userToken.getToken());
}
}
// 发送管理员通知
SystemNotification adminNotification = systemNotificationService.getByMark(NotifyConstants.MAINTENANCE_NOTICE_ADMIN_MARK);
if (adminNotification != null && adminNotification.getIsWechat().equals(1)) {
// 这里可以获取管理员的openid假设系统有默认管理员或者根据部门获取
// 暂时使用用户的openid作为示例
UserToken userToken = userTokenService.getTokenByUserId(Math.toIntExact(pmMaintenanceOrder.getUid()), UserConstants.USER_TOKEN_TYPE_WECHAT);
if (userToken != null) {
// 构建通知内容
HashMap<String, String> temMap = new HashMap<>();
temMap.put(Constants.WE_CHAT_TEMP_KEY_FIRST, "收到新的报修申请!");
temMap.put("keyword1", pmMaintenanceOrder.getOrderNo());
temMap.put("keyword2", pmMaintenanceOrder.getFaultType());
temMap.put("keyword3", pmMaintenanceOrder.getUrgencyLevel());
temMap.put("keyword4", DateUtil.nowDateTimeStr());
temMap.put(Constants.WE_CHAT_TEMP_KEY_END, "请及时处理该报修申请。");
// 发送微信模板消息
templateMessageService.pushTemplateMessage(adminNotification.getWechatId(), temMap, userToken.getToken());
}
}
}
private void handleDispatchRecords(Long orderId, List<PmMaintenanceDispatch> dispatchList) {
// 处理报修派单记录列表
if (dispatchList != null && !dispatchList.isEmpty()) {
@ -637,16 +707,23 @@ public class PmMaintenanceOrderController {
// 这里需要从前端传递修复照片,或者在前端进行检查
// 由于此接口主要用于状态和备注的快速修改,建议在前端进行修复照片的检查
// 如果前端没有传递修复照片,则返回错误
if (params.get("afterProcessFiles") == null) {
return CommonResult.failed("办结时必须上传修复凭证");
}
// if (params.get("afterProcessFiles") == null) {
// return CommonResult.failed("办结时必须上传修复凭证");
// }
}
// 记录原状态
String oldStatus = pmMaintenanceOrder.getStatus();
// 只修改状态和备注
pmMaintenanceOrder.setStatus(status);
pmMaintenanceOrder.setRemark(remark);
if (pmMaintenanceOrderService.updateById(pmMaintenanceOrder)) {
// 当从草稿(1)修改为待处理(0)时,发送推送通知
if ("1".equals(oldStatus) && "0".equals(status)) {
sendMaintenanceNotification(pmMaintenanceOrder);
}
return CommonResult.success();
}
return CommonResult.failed();

@ -206,6 +206,8 @@ public class CkBillCargo implements Serializable {
*
*/
@ApiModelProperty(value = "有效期至")
@JsonFormat(pattern = "yyyy-MM-dd")
@DateTimeFormat(pattern = "yyyy-MM-dd")
private Date expiryDate;
/**
*
@ -428,6 +430,37 @@ public class CkBillCargo implements Serializable {
@TableField(exist = false)
private String receiverName;
/**
* ID
*/
@ApiModelProperty(value = "分类ID")
@TableField(exist = false)
private Long classId;
/**
*
*/
@ApiModelProperty(value = "分类名称")
@TableField(exist = false)
private String className;
/**
*
*/
@ApiModelProperty(value = "分类代码")
@TableField(exist = false)
private String classCode;
@TableField(exist = false)
private String unitName;
/**
* ID
*/
@ApiModelProperty(value = "分类ID列表逗号分隔")
@TableField(exist = false)
private String classIds;
}

@ -210,6 +210,35 @@ public class CkCargoStock implements Serializable {
@ApiModelProperty(value = "货位ID")
@TableField(exist = false)
private Long locationId;
/**
* ID
*/
@ApiModelProperty(value = "分类ID")
@TableField(exist = false)
private Long classId;
/**
*
*/
@ApiModelProperty(value = "分类名称")
@TableField(exist = false)
private String className;
/**
*
*/
@ApiModelProperty(value = "类别代码")
@TableField(exist = false)
private String classCode;
/**
*
*/
@ApiModelProperty(value = "单位")
@TableField(exist = false)
private String unit;
/**
*
*/

@ -1,5 +1,6 @@
package com.zbkj.modules.autogencode.entity;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import io.swagger.annotations.ApiModelProperty;
@ -46,6 +47,18 @@ public class CkStockChange implements Serializable {
*/
@ApiModelProperty(value = "货品名称")
private String cargoName;
/**
*
*/
@ApiModelProperty(value = "物品类别")
@TableField(exist = false)
private String cargoCategory;
/**
*
*/
@ApiModelProperty(value = "规格型号")
@TableField(exist = false)
private String cargoSpec;
/**
*
*/

@ -114,4 +114,17 @@ public class PmDailyMenuDtlLike implements Serializable {
@TableField(exist = false)
private Integer totalCount;
/**
*
*/
@ApiModelProperty(value = "备注")
private String remark;
/**
*
*/
@ApiModelProperty(value = "用户名称")
@TableField(exist = false)
private String userName;
}

@ -0,0 +1,23 @@
package com.zbkj.modules.autogencode.entity.vo;
import lombok.Data;
import java.math.BigDecimal;
@Data
public class ImportItemVO {
private String orderNo; // 单据编号 XSDD-20260403-0424
private String orderDate; // 2026-04-03
private String customer; // 福州市八方物业管理有限公司
private String address; // 港口集团食堂
private String seq; // 序号
private String goodsName; // 商品名称
private String spec; // 规格型号
private String unit; // 单位
private BigDecimal qty; // 数量
private BigDecimal price; // 单价
private BigDecimal amount; // 金额
private String remark; // 备注
}

@ -3,6 +3,8 @@ package com.zbkj.modules.autogencode.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.zbkj.modules.autogencode.entity.CkStockChange;
import com.zbkj.common.request.PageParamRequest;
import com.zbkj.common.request.InventoryRequest;
import com.zbkj.common.response.InventoryResponse;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
@ -21,5 +23,18 @@ public interface CkStockChangeService extends IService<CkStockChange> {
* @return
*/
List<CkStockChange> pageList(LambdaQueryWrapper<CkStockChange> queryWrapper, PageParamRequest pageParamRequest);
/**
*
* @param request
* @return
*/
List<InventoryResponse> getInventoryList(InventoryRequest request);
/**
*
* @param request
*/
void exportInventory(InventoryRequest request);
}

@ -21,5 +21,14 @@ public interface PmDailyMenuService extends IService<PmDailyMenu> {
* @return
*/
List<PmDailyMenu> pageList(LambdaQueryWrapper<PmDailyMenu> queryWrapper, PageParamRequest pageParamRequest);
/**
*
* @param menuDate
* @param canteenName
* @param mealType
* @return
*/
PmDailyMenu findByDateAndCanteenAndMealType(String menuDate, String canteenName, String mealType);
}

@ -21,5 +21,12 @@ public interface SysDictDataService extends IService<SysDictData> {
* @return
*/
List<SysDictData> pageList(LambdaQueryWrapper<SysDictData> queryWrapper, PageParamRequest pageParamRequest);
/**
*
* @param dictType
* @return
*/
List<SysDictData> getByDictType(String dictType);
}

@ -6,11 +6,16 @@ import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.zbkj.common.model.system.SysDept;
import com.zbkj.common.model.system.SystemAdmin;
import com.zbkj.common.response.CommonResult;
import com.zbkj.common.utils.SecurityUtil;
import com.zbkj.common.vo.LoginUserVo;
import com.zbkj.modules.autogencode.entity.*;
import com.zbkj.modules.autogencode.service.*;
import com.zbkj.modules.autogencode.entity.PmProductClass;
import com.zbkj.service.service.SysDeptService;
import com.zbkj.service.service.SystemAdminService;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateUtils;
import org.springframework.beans.BeanUtils;
@ -55,6 +60,9 @@ public class CkBillServiceImpl extends ServiceImpl<CkBillDao, CkBill> implements
@Resource
private CmWarehouseService cmWarehouseService;
@Resource
private PmProductClassService pmProductClassService;
@Resource
private CmShelfService cmShelfService;
@ -76,6 +84,12 @@ public class CkBillServiceImpl extends ServiceImpl<CkBillDao, CkBill> implements
@Resource
private CkBillNumberService ckBillNumberService;
@Resource
private SysDeptService sysDeptService;
@Resource
private SystemAdminService systemAdminService;
/**
*
*/
@ -175,6 +189,11 @@ public class CkBillServiceImpl extends ServiceImpl<CkBillDao, CkBill> implements
stockWrapper.eq(CkCargoStock::getStockId,ckBillStock.getStockId());
stockWrapper.eq(CkCargoStock::getBillNumber,ckBillStock.getBillNumber());
CkCargoStock ckCargoStock = ckCargoStockService.getOne(stockWrapper);
// 检查库存记录是否存在
if (ckCargoStock == null) {
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();//手动回滚事务
throw new RuntimeException("库存记录不存在");
}
ckCargoStock.setCargoNum(ckCargoStock.getCargoNum().add(ckBillStock.getCargoNum()));
ckCargoStock.setCargoWt(ckCargoStock.getCargoWt().add(ckBillStock.getCargoWt()));
ckCargoStock.setCargoVol(ckCargoStock.getCargoVol().add(ckBillStock.getCargoVol()));
@ -199,6 +218,73 @@ public class CkBillServiceImpl extends ServiceImpl<CkBillDao, CkBill> implements
wapper.eq(CkBillCargo::getBillId,ckBill.getId());
//单据明细
List<CkBillCargo> ckBillCargos = ckBillCargoService.list(wapper);
// 如果autoOut为1准备生成出库单据
CkBill outBill = null;
List<CkBillCargo> outCargoList = null;
if ("1".equals(ckBill.getAutoOut())) {
// 生成一个出库单据
outBill = new CkBill();
outBill.setBillNumber(ckBillNumberService.getBillNumber("CK"));
outBill.setBillDate(new Date());
outBill.setBillType("2"); // 出库
outBill.setStatus("1");
outBill.setBillStatus("0");
outBill.setSourceType("1");
outBill.setAuditStatus("0");
outBill.setCancelStatus("0");
// 使用第一个入库明细的货主信息
if (ckBillCargos != null && !ckBillCargos.isEmpty()) {
CkBillCargo firstCargo = ckBillCargos.get(0);
outBill.setCustId(firstCargo.getCustId());
outBill.setCustName(firstCargo.getCustName());
}
outBill.setVoyageNo(ckBill.getVoyageNo());
outBill.setVesselName(ckBill.getVesselName());
outBill.setVesselId(ckBill.getVesselId());
outBill.setAutoOut("0"); // 出库单据不需要自动出库
outBill.setSubmitStatus(false); // 设置提交状态为false
// 设置领用部门和领用人信息(从原单据中获取)
outBill.setReceiverDeptId(ckBill.getReceiverDeptId());
outBill.setReceiverId(ckBill.getReceiverId());
// 如果原单据没有设置领用部门和领用人,从部门表和用户表中获取默认值
if (outBill.getReceiverDeptId() == null || outBill.getReceiverId() == null) {
// 从部门表中获取默认领用部门
if (outBill.getReceiverDeptId() == null && sysDeptService != null) {
try {
// 查询isReceiveDept为1的部门作为默认领用部门
LambdaQueryWrapper<SysDept> deptWrapper = new LambdaQueryWrapper<>();
deptWrapper.eq(SysDept::getIsReceiveDept, "1");
List<SysDept> deptList = sysDeptService.list(deptWrapper);
if (deptList != null && !deptList.isEmpty()) {
outBill.setReceiverDeptId(deptList.get(0).getDeptId());
}
} catch (Exception e) {
e.printStackTrace();
}
}
// 从用户表中获取默认领用人
if (outBill.getReceiverId() == null && systemAdminService != null) {
try {
// 查询isDefaultReceiver为1的用户作为默认领用人
LambdaQueryWrapper<SystemAdmin> userWrapper = new LambdaQueryWrapper<>();
userWrapper.eq(SystemAdmin::getIsDefaultReceiver, "1");
List<SystemAdmin> userList = systemAdminService.list(userWrapper);
if (userList != null && !userList.isEmpty()) {
outBill.setReceiverId(Long.valueOf(userList.get(0).getId()));
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
// 创建出库明细列表
outCargoList = new ArrayList<>();
}
for (int i = 0; i < ckBillCargos.size(); i++) {
CkBillCargo ckBillCargo = ckBillCargos.get(i);
ckBillCargo.setBillStatus("1");
@ -233,51 +319,63 @@ public class CkBillServiceImpl extends ServiceImpl<CkBillDao, CkBill> implements
saveChange(ckBillCargo,ckBillStock);
});
// 如果autoOut为1自动生成出库单据 直接用outCargo这个方法
if ("1".equals(ckBill.getAutoOut())) {
// 如果autoOut为1为每个库存记录创建出库明细
if ("1".equals(ckBill.getAutoOut()) && outCargoList != null) {
// 为每个库存记录创建一个出库明细
for (CkBillStock ckBillStock : ckBillStocks) {
// 生成出库单据
CkBill outBill = new CkBill();
outBill.setBillNumber(ckBillNumberService.getBillNumber("CK"));
outBill.setBillDate(new Date());
outBill.setBillType("2"); // 出库
outBill.setStatus("1");
outBill.setBillStatus("0");
outBill.setSourceType("1");
outBill.setAuditStatus("0");
outBill.setCancelStatus("0");
outBill.setCustId(ckBillCargo.getCustId());
outBill.setCustName(ckBillCargo.getCustName());
outBill.setVoyageNo(ckBill.getVoyageNo());
outBill.setVesselName(ckBill.getVesselName());
outBill.setVesselId(ckBill.getVesselId());
outBill.setAutoOut("0"); // 出库单据不需要自动出库
outBill.setSubmitStatus(false); // 设置提交状态为false
// 检查仓库和分类是否需要自动出库
boolean needAutoOut = false;
// 创建出库明细
CkBillCargo outCargo = new CkBillCargo();
BeanUtils.copyProperties(ckBillCargo, outCargo, "id", "billId", "billStatus", "inoutType");
outCargo.setBillStatus("0");
outCargo.setInoutType("2"); // 出库
outCargo.setSourceType("1");
outCargo.setOutStockId(ckBillStock.getStockId());
outCargo.setOutBillNumber(ckBillStock.getBillNumber());
// 设置数量字段,从库存记录中获取
outCargo.setCargoNum(ckBillStock.getCargoNum());
outCargo.setCargoWt(ckBillStock.getCargoWt());
outCargo.setCargoVol(ckBillStock.getCargoVol());
outCargo.setCargoValue(ckBillStock.getCargoValue());
outCargo.setVoyageNo(ckBill.getVoyageNo());
outCargo.setVesselName(ckBill.getVesselName());
outCargo.setVesselId(ckBill.getVesselId());
// 检查仓库是否需要自动出库
CmWarehouse warehouse = cmWarehouseService.getById(ckBillStock.getStockId());
if (warehouse != null && "1".equals(warehouse.getAutoOut())) {
needAutoOut = true;
}
// 设置出库单据的明细列表
List<CkBillCargo> outCargoList = new ArrayList<>();
outCargoList.add(outCargo);
outBill.setCkBillCargos(outCargoList);
// 检查分类是否需要自动出库
if (!needAutoOut) {
// 首先通过cargoId查询CmCustProduct获取分类ID
if (ckBillCargo.getCargoId() != null) {
CmCustProduct cmCustProduct = cmCustProductService.getById(ckBillCargo.getCargoId());
if (cmCustProduct != null && cmCustProduct.getClassId() != null) {
// 然后通过分类ID查询PmProductClass获取autoOut字段
Long classId = cmCustProduct.getClassId();
try {
if (pmProductClassService != null) {
PmProductClass productClass = pmProductClassService.getById(classId);
if (productClass != null && "1".equals(productClass.getAutoOut())) {
needAutoOut = true;
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
// 调用outCargo方法处理出库
this.outCargo(outBill);
// 只要仓库或分类中有一个需要自动出库,就生成出库明细
if (needAutoOut) {
// 创建出库明细
CkBillCargo outCargo = new CkBillCargo();
BeanUtils.copyProperties(ckBillCargo, outCargo, "id", "billId", "billStatus", "inoutType");
outCargo.setBillStatus("0");
outCargo.setInoutType("2"); // 出库
outCargo.setSourceType("1");
outCargo.setOutStockId(ckBillStock.getStockId());
outCargo.setOutBillNumber(ckBillStock.getBillNumber());
// 设置数量字段,从库存记录中获取
outCargo.setCargoNum(ckBillStock.getCargoNum());
outCargo.setCargoWt(ckBillStock.getCargoWt());
outCargo.setCargoVol(ckBillStock.getCargoVol());
outCargo.setCargoValue(ckBillStock.getCargoValue());
outCargo.setVoyageNo(ckBill.getVoyageNo());
outCargo.setVesselName(ckBill.getVesselName());
outCargo.setVesselId(ckBill.getVesselId());
// 添加到明细列表
outCargoList.add(outCargo);
}
}
}
}else{
@ -285,11 +383,15 @@ public class CkBillServiceImpl extends ServiceImpl<CkBillDao, CkBill> implements
for (int j = 0; j < ckBillStocks.size(); j++) {
CkBillStock ckBillStock = ckBillStocks.get(j);
LambdaQueryWrapper<CkCargoStock> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(CkCargoStock::getCustId,ckBillCargo.getCustId());
wrapper.eq(CkCargoStock::getCargoId,ckBillCargo.getCargoId());
wrapper.eq(CkCargoStock::getStockId,ckBillStock.getStockId());
wrapper.eq(CkCargoStock::getBillNumber,ckBillStock.getBillNumber());
CkCargoStock ckCargoStock = ckCargoStockService.getOne(wrapper);
// 检查库存记录是否存在
if (ckCargoStock == null) {
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();//手动回滚事务
return CommonResult.failed("库存记录不存在");
}
Boolean flag = false;
if(ckCargoStock.getCargoNum().compareTo(ckBillStock.getCargoNum())>=0){
flag = true;
@ -321,6 +423,29 @@ public class CkBillServiceImpl extends ServiceImpl<CkBillDao, CkBill> implements
}
}
}
// 如果autoOut为1调用outCargo方法处理出库
if ("1".equals(ckBill.getAutoOut()) && outBill != null && outCargoList != null && !outCargoList.isEmpty()) {
// 设置出库单据的明细列表
outBill.setCkBillCargos(outCargoList);
// 调用outCargo方法处理出库
this.outCargo(outBill);
// 自动确认出库单据
try {
// 直接调用warehouseWork方法确认单据跳过审核步骤
// 由于是自动生成的单据,我们直接设置为已审核状态
outBill.setAuditStatus("1");
this.updateById(outBill);
// 调用warehouseWork方法确认单据
this.warehouseWork(outBill, false);
} catch (Exception e) {
e.printStackTrace();
// 确认失败不影响主流程
}
}
this.updateById(ckBill);
ckBillCargoService.updateBatchById(ckBillCargos);
return CommonResult.success();
@ -757,3 +882,6 @@ public class CkBillServiceImpl extends ServiceImpl<CkBillDao, CkBill> implements
return warehouseWork(this.getById(id),true);
}
}

@ -5,18 +5,41 @@ import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.zbkj.modules.autogencode.entity.SysDictData;
import com.zbkj.modules.autogencode.service.SysDictDataService;
import org.apache.poi.hssf.usermodel.HSSFSheet;
import org.springframework.stereotype.Service;
import com.zbkj.modules.autogencode.dao.CkStockChangeDao;
import com.zbkj.modules.autogencode.entity.CkStockChange;
import com.zbkj.modules.autogencode.dao.CmCustDao;
import com.zbkj.modules.autogencode.entity.CmCust;
import com.zbkj.modules.autogencode.service.CkStockChangeService;
import com.zbkj.common.page.CommonPage;
import com.zbkj.common.request.PageParamRequest;
import com.zbkj.common.request.InventoryRequest;
import com.zbkj.common.response.InventoryResponse;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.URLEncoder;
import java.util.Map;
import java.util.Arrays;
import java.util.List;
import java.util.ArrayList;
import java.math.BigDecimal;
import java.util.HashMap;
import java.util.stream.Collectors;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.CellRangeAddress;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import java.util.Objects;
import com.zbkj.modules.autogencode.entity.CmCustProduct;
import com.zbkj.modules.autogencode.dao.CmCustProductDao;
@Service("ckStockChangeService")
@ -26,7 +49,16 @@ public class CkStockChangeServiceImpl extends ServiceImpl<CkStockChangeDao, CkSt
@Resource
private CkStockChangeDao dao;
@Resource
private CmCustProductDao cmCustProductDao;
@Resource
private SysDictDataService sysDictDataService;
@Resource
private CmCustDao cmCustDao;
/**
*
*/
@ -37,6 +69,590 @@ public class CkStockChangeServiceImpl extends ServiceImpl<CkStockChangeDao, CkSt
return dao.selectList(queryWrapper);
}
/**
*
* after_wtafter_value
* changeTypechangeType
* changeTypechangeType
*
*/
@Override
public List<InventoryResponse> getInventoryList(InventoryRequest request) {
List<InventoryResponse> result = new ArrayList<>();
// 构建查询条件
LambdaQueryWrapper<CkStockChange> wrapper = new LambdaQueryWrapper<>();
if (request.getCustId() != null) {
wrapper.eq(CkStockChange::getCustId, request.getCustId());
}
if (request.getCargoId() != null) {
wrapper.eq(CkStockChange::getCargoId, request.getCargoId());
}
if (request.getCargoName() != null) {
wrapper.eq(CkStockChange::getCargoName, request.getCargoName());
}
if (request.getStockId() != null) {
wrapper.eq(CkStockChange::getStockId, request.getStockId());
}
if (request.getStockName() != null) {
wrapper.eq(CkStockChange::getStockName, request.getStockName());
}
// 按时间升序排序,便于取最后一条记录
wrapper.orderByAsc(CkStockChange::getChangeDate);
// 查询所有库存变动记录
List<CkStockChange> allChanges = dao.selectList(wrapper);
// 1. 同bill_id + cargoId去重取每组最后一条记录
Map<String, CkStockChange> uniqueByBillAndCargo = new HashMap<>();
for (CkStockChange change : allChanges) {
String key = change.getBillId() + "_" + change.getCargoId();
// 由于已经按时间升序排序,后面的记录会覆盖前面的,最终保留最后一条
uniqueByBillAndCargo.put(key, change);
}
List<CkStockChange> uniqueChanges = new ArrayList<>(uniqueByBillAndCargo.values());
// 2. 按 cargoName + hsCode + custId + stockId 分组
Map<String, List<CkStockChange>> groupedByProduct = uniqueChanges.stream()
.collect(Collectors.groupingBy(change ->
change.getCargoName() + "_" + change.getHsCode() + "_" + change.getCustId() + "_" + change.getStockId()));
// 3. 处理物品类别过滤
if (request.getCargoCategory() != null && !request.getCargoCategory().isEmpty()) {
// 获取所有商品ID
List<Long> cargoIds = uniqueChanges.stream()
.map(CkStockChange::getCargoId)
.filter(Objects::nonNull)
.distinct()
.collect(Collectors.toList());
if (!cargoIds.isEmpty()) {
// 查询商品分类信息
List<CmCustProduct> products = cmCustProductDao.selectList(new LambdaQueryWrapper<CmCustProduct>()
.in(CmCustProduct::getId, cargoIds));
// 构建商品ID到分类ID的映射
Map<Long, Long> cargoClassMap = products.stream()
.collect(Collectors.toMap(CmCustProduct::getId, CmCustProduct::getClassId, (a, b) -> b));
// 构建商品ID到单位的映射
Map<Long, String> cargoUnitMap = products.stream()
.collect(Collectors.toMap(CmCustProduct::getId, CmCustProduct::getUnit, (a, b) -> b));
// 过滤分组,只保留符合分类条件的商品
Map<String, List<CkStockChange>> filteredGroups = new HashMap<>();
for (Map.Entry<String, List<CkStockChange>> entry : groupedByProduct.entrySet()) {
List<CkStockChange> changes = entry.getValue();
if (!changes.isEmpty()) {
CkStockChange firstChange = changes.get(0);
Long classId = cargoClassMap.get(firstChange.getCargoId());
if (classId != null && request.getCargoCategory().contains(classId.toString())) {
filteredGroups.put(entry.getKey(), entry.getValue());
}
}
}
groupedByProduct = filteredGroups;
} else {
// 没有商品ID清空分组
groupedByProduct.clear();
}
}
// 合计数据
BigDecimal totalLastCargoNum = BigDecimal.ZERO;
BigDecimal totalLastCargoValue = BigDecimal.ZERO;
BigDecimal totalInCargoNum = BigDecimal.ZERO;
BigDecimal totalInCargoValue = BigDecimal.ZERO;
BigDecimal totalOutCargoNum = BigDecimal.ZERO;
BigDecimal totalOutCargoValue = BigDecimal.ZERO;
BigDecimal totalCargoNum = BigDecimal.ZERO;
BigDecimal totalCargoValue = BigDecimal.ZERO;
// 查询所有商品的单位信息(通过字典翻译)
Map<Long, String> cargoUnitMap = new HashMap<>();
List<Long> allCargoIds = groupedByProduct.values().stream()
.flatMap(list -> list.stream())
.map(CkStockChange::getCargoId)
.filter(Objects::nonNull)
.distinct()
.collect(Collectors.toList());
if (!allCargoIds.isEmpty()) {
List<CmCustProduct> products = cmCustProductDao.selectList(new LambdaQueryWrapper<CmCustProduct>()
.in(CmCustProduct::getId, allCargoIds));
// 通过字典表 bm_measuring_unit 翻译单位
List<SysDictData> unitDictList = sysDictDataService.list(new LambdaQueryWrapper<SysDictData>()
.eq(SysDictData::getDictType, "bm_measuring_unit")
.eq(SysDictData::getStatus, "0"));
Map<String, String> unitTranslationMap = unitDictList.stream()
.collect(Collectors.toMap(SysDictData::getDictValue, SysDictData::getDictLabel, (a, b) -> b));
// 设置翻译后的单位
for (CmCustProduct product : products) {
String unitCode = product.getUnit();
String translatedUnit = unitTranslationMap.getOrDefault(unitCode, unitCode != null ? unitCode : "吨");
cargoUnitMap.put(product.getId(), translatedUnit);
}
}
// 遍历每个商品组
int index = 1;
for (Map.Entry<String, List<CkStockChange>> entry : groupedByProduct.entrySet()) {
List<CkStockChange> productChanges = entry.getValue();
InventoryResponse response = new InventoryResponse();
// 获取商品基本信息(使用第一个记录的数据)
CkStockChange firstChange = productChanges.get(0);
response.setIndex(index++);
response.setCargoName(firstChange.getCargoName());
response.setCargoSpec(firstChange.getCargoSpec());
// 设置单位
response.setUnit(cargoUnitMap.getOrDefault(firstChange.getCargoId(), "吨"));
// 构建品名及规格
String cargoNameWithSpec = firstChange.getCargoName();
if (firstChange.getCargoSpec() != null && !firstChange.getCargoSpec().isEmpty()) {
cargoNameWithSpec += "\n" + firstChange.getCargoSpec();
}
response.setCargoNameWithSpec(cargoNameWithSpec);
// 上期结存变量
BigDecimal lastCargoNum = BigDecimal.ZERO;
BigDecimal lastCargoValue = BigDecimal.ZERO;
// 本期入库变量
BigDecimal inCargoNum = BigDecimal.ZERO;
BigDecimal inCargoValue = BigDecimal.ZERO;
// 本期出库变量
BigDecimal outCargoNum = BigDecimal.ZERO;
BigDecimal outCargoValue = BigDecimal.ZERO;
// 遍历变动记录,计算上期结存和本期入库出库
for (CkStockChange change : productChanges) {
if (request.getStartDate() != null) {
if (change.getChangeDate().before(request.getStartDate())) {
// 上期变动记录,用于计算上期结存
if ("1".equals(change.getChangeType())) {
// 上期入库
lastCargoNum = lastCargoNum.add(change.getChangeWt() != null ? change.getChangeWt() : BigDecimal.ZERO);
lastCargoValue = lastCargoValue.add(change.getChangeValue() != null ? change.getChangeValue() : BigDecimal.ZERO);
} else if ("2".equals(change.getChangeType())) {
// 上期出库
lastCargoNum = lastCargoNum.subtract(change.getChangeWt() != null ? change.getChangeWt() : BigDecimal.ZERO);
lastCargoValue = lastCargoValue.subtract(change.getChangeValue() != null ? change.getChangeValue() : BigDecimal.ZERO);
}
} else if (request.getEndDate() != null && !change.getChangeDate().after(request.getEndDate())) {
// 本期变动记录
if ("1".equals(change.getChangeType())) {
// 本期入库
inCargoNum = inCargoNum.add(change.getChangeWt() != null ? change.getChangeWt() : BigDecimal.ZERO);
inCargoValue = inCargoValue.add(change.getChangeValue() != null ? change.getChangeValue() : BigDecimal.ZERO);
} else if ("2".equals(change.getChangeType())) {
// 本期出库
outCargoNum = outCargoNum.add(change.getChangeWt() != null ? change.getChangeWt() : BigDecimal.ZERO);
outCargoValue = outCargoValue.add(change.getChangeValue() != null ? change.getChangeValue() : BigDecimal.ZERO);
}
}
}
}
// 确保结存不为负数
if (lastCargoNum.compareTo(BigDecimal.ZERO) < 0) {
lastCargoNum = BigDecimal.ZERO;
}
if (lastCargoValue.compareTo(BigDecimal.ZERO) < 0) {
lastCargoValue = BigDecimal.ZERO;
}
// 设置响应数据
response.setLastCargoNum(lastCargoNum);
response.setLastCargoValue(lastCargoValue);
response.setInCargoNum(inCargoNum);
response.setInCargoValue(inCargoValue);
response.setOutCargoNum(outCargoNum);
response.setOutCargoValue(outCargoValue);
// 累计结存 = 上期结存 + 本期入库 - 本期出库
BigDecimal totalNum = lastCargoNum.add(inCargoNum).subtract(outCargoNum);
BigDecimal totalValue = lastCargoValue.add(inCargoValue).subtract(outCargoValue);
response.setCargoNum(totalNum);
response.setCargoValue(totalValue);
// 累加合计
totalLastCargoNum = totalLastCargoNum.add(lastCargoNum);
totalLastCargoValue = totalLastCargoValue.add(lastCargoValue);
totalInCargoNum = totalInCargoNum.add(inCargoNum);
totalInCargoValue = totalInCargoValue.add(inCargoValue);
totalOutCargoNum = totalOutCargoNum.add(outCargoNum);
totalOutCargoValue = totalOutCargoValue.add(outCargoValue);
totalCargoNum = totalCargoNum.add(totalNum);
totalCargoValue = totalCargoValue.add(totalValue);
result.add(response);
}
// 添加合计行
if (!result.isEmpty()) {
InventoryResponse totalResponse = new InventoryResponse();
totalResponse.setCargoName("合计");
totalResponse.setCargoNameWithSpec("合计");
totalResponse.setLastCargoNum(totalLastCargoNum);
totalResponse.setLastCargoValue(totalLastCargoValue);
totalResponse.setInCargoNum(totalInCargoNum);
totalResponse.setInCargoValue(totalInCargoValue);
totalResponse.setOutCargoNum(totalOutCargoNum);
totalResponse.setOutCargoValue(totalOutCargoValue);
totalResponse.setCargoNum(totalCargoNum);
totalResponse.setCargoValue(totalCargoValue);
result.add(totalResponse);
}
return result;
}
// 标题样式加粗、居中、14号
private CellStyle getTitleStyle(Workbook wb) {
CellStyle style = wb.createCellStyle();
Font font = wb.createFont();
font.setFontName("微软雅黑");
font.setFontHeightInPoints((short) 14);
font.setBold(true);
style.setFont(font);
style.setAlignment(HorizontalAlignment.CENTER);
style.setVerticalAlignment(VerticalAlignment.CENTER);
return style;
}
// 表头样式加粗、居中、11号
private CellStyle getHeaderStyle(Workbook wb) {
CellStyle style = wb.createCellStyle();
Font font = wb.createFont();
font.setFontName("微软雅黑");
font.setFontHeightInPoints((short) 11);
font.setBold(true);
style.setFont(font);
style.setAlignment(HorizontalAlignment.CENTER);
style.setVerticalAlignment(VerticalAlignment.CENTER);
style.setBorderTop(BorderStyle.THIN);
style.setBorderBottom(BorderStyle.THIN);
style.setBorderLeft(BorderStyle.THIN);
style.setBorderRight(BorderStyle.THIN);
return style;
}
// 数据样式:居中、细边框
private CellStyle getDataStyle(Workbook wb) {
CellStyle style = wb.createCellStyle();
Font font = wb.createFont();
font.setFontName("微软雅黑");
font.setFontHeightInPoints((short) 10);
style.setFont(font);
style.setAlignment(HorizontalAlignment.CENTER);
style.setVerticalAlignment(VerticalAlignment.CENTER);
style.setBorderTop(BorderStyle.THIN);
style.setBorderBottom(BorderStyle.THIN);
style.setBorderLeft(BorderStyle.THIN);
style.setBorderRight(BorderStyle.THIN);
return style;
}
// 合计金额样式:右对齐、加粗、细边框
private CellStyle getTotalValueStyle(Workbook wb) {
CellStyle style = wb.createCellStyle();
Font font = wb.createFont();
font.setFontName("微软雅黑");
font.setFontHeightInPoints((short) 11);
font.setBold(true);
style.setFont(font);
style.setAlignment(HorizontalAlignment.RIGHT);
style.setVerticalAlignment(VerticalAlignment.CENTER);
style.setBorderTop(BorderStyle.THIN);
style.setBorderBottom(BorderStyle.THIN);
style.setBorderLeft(BorderStyle.THIN);
style.setBorderRight(BorderStyle.THIN);
return style;
}
// 底部签字样式
private CellStyle getSignStyle(Workbook wb) {
CellStyle style = wb.createCellStyle();
Font font = wb.createFont();
font.setFontName("微软雅黑");
font.setFontHeightInPoints((short) 10);
style.setFont(font);
return style;
}
@Override
public void exportInventory(InventoryRequest request) {
// 获取盘点数据
List<InventoryResponse> inventoryList = getInventoryList(request);
// 获取响应对象
HttpServletResponse response = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getResponse();
if (response == null) {
return;
}
try {
// 1. 创建工作簿
HSSFWorkbook wb = new HSSFWorkbook();
HSSFSheet sheet = wb.createSheet("物资盘点表");
// 2. 行高设置
sheet.createRow(0).setHeight((short) 600); // 标题
sheet.createRow(1).setHeight((short) 350); // 类别/供应商/日期
sheet.createRow(2).setHeight((short) 380); // 表头1
sheet.createRow(3).setHeight((short) 380); // 表头2
// ====================== 第1行标题 ======================
Row titleRow = sheet.getRow(0);
Cell titleCell = titleRow.createCell(0);
titleCell.setCellValue("物资盘点表");
titleCell.setCellStyle(getTitleStyle(wb));
// 合并 A1:N1
sheet.addMergedRegion(new CellRangeAddress(0, 0, 0, 13));
// ====================== 第2行类别、供应商、日期 ======================
Row infoRow = sheet.getRow(1);
infoRow.createCell(0).setCellValue("类别:所有类别");
// 根据custId查询供应商名称
String supplierName = "";
if (request.getCustId() != null) {
CmCust cmCust = cmCustDao.selectById(request.getCustId());
if (cmCust != null) {
supplierName = cmCust.getCustName();
}
}
infoRow.createCell(6).setCellValue("供应商:" + supplierName);
// 使用传参中的结束日期
String endDateStr = "";
if (request.getEndDate() != null) {
endDateStr = new java.text.SimpleDateFormat("yyyy-MM-dd").format(request.getEndDate());
}
infoRow.createCell(11).setCellValue("日期:" + endDateStr);
// ====================== 第3-4行双行表头 ======================
Row headerRow1 = sheet.getRow(2);
Row headerRow2 = sheet.getRow(3);
CellStyle hs = getHeaderStyle(wb);
// 双行表头结构(严格对齐你的表)
String[] h1 = {
"序号", "品名及规格", "上期结存", "", "", "本期入库", "", "", "本期出库", "", "", "累计结存", "", ""
};
String[] h2 = {
"", "", "单位", "数量", "金额", "单位", "数量", "金额", "单位", "数量", "金额", "单位", "数量", "金额"
};
for (int i = 0; i < h1.length; i++) {
Cell c1 = headerRow1.createCell(i);
Cell c2 = headerRow2.createCell(i);
c1.setCellValue(h1[i]);
c2.setCellValue(h2[i]);
c1.setCellStyle(hs);
c2.setCellStyle(hs);
}
// 合并表头(上期/入库/出库/结存)
sheet.addMergedRegion(new CellRangeAddress(2, 2, 2, 4)); // 上期
sheet.addMergedRegion(new CellRangeAddress(2, 2, 5, 7)); // 入库
sheet.addMergedRegion(new CellRangeAddress(2, 2, 8, 10)); // 出库
sheet.addMergedRegion(new CellRangeAddress(2, 2, 11, 13)); // 结存
// ====================== 数据行 ======================
CellStyle ds = getDataStyle(wb);
int dataRowIndex = 4;
for (int i = 0; i < inventoryList.size(); i++) {
InventoryResponse item = inventoryList.get(i);
Row dataRow = sheet.createRow(dataRowIndex);
dataRow.setHeight((short) 300);
// 判断是否为合计行(最后一行)
boolean isTotalRow = (i == inventoryList.size() - 1) && "合计".equals(item.getCargoNameWithSpec());
CellStyle currentStyle = isTotalRow ? hs : ds;
if (!isTotalRow) {
// 数据行:显示完整内容
// 序号
Cell indexCell = dataRow.createCell(0);
indexCell.setCellValue(item.getIndex() != null ? item.getIndex() : 0);
indexCell.setCellStyle(currentStyle);
// 品名及规格
Cell nameCell = dataRow.createCell(1);
nameCell.setCellValue(item.getCargoNameWithSpec() != null ? item.getCargoNameWithSpec() : "");
nameCell.setCellStyle(currentStyle);
// 上期结存
String unit = item.getUnit() != null ? item.getUnit() : "吨";
Cell lastUnitCell = dataRow.createCell(2);
lastUnitCell.setCellValue(unit);
lastUnitCell.setCellStyle(currentStyle);
Cell lastNumCell = dataRow.createCell(3);
BigDecimal lastNum = item.getLastCargoNum() != null ? item.getLastCargoNum() : BigDecimal.ZERO;
lastNumCell.setCellValue(lastNum.doubleValue());
lastNumCell.setCellStyle(currentStyle);
Cell lastValueCell = dataRow.createCell(4);
BigDecimal lastValue = item.getLastCargoValue() != null ? item.getLastCargoValue() : BigDecimal.ZERO;
lastValueCell.setCellValue(lastValue.doubleValue());
lastValueCell.setCellStyle(currentStyle);
// 本期入库
Cell inUnitCell = dataRow.createCell(5);
inUnitCell.setCellValue(unit);
inUnitCell.setCellStyle(currentStyle);
Cell inNumCell = dataRow.createCell(6);
BigDecimal inNum = item.getInCargoNum() != null ? item.getInCargoNum() : BigDecimal.ZERO;
inNumCell.setCellValue(inNum.doubleValue());
inNumCell.setCellStyle(currentStyle);
Cell inValueCell = dataRow.createCell(7);
BigDecimal inValue = item.getInCargoValue() != null ? item.getInCargoValue() : BigDecimal.ZERO;
inValueCell.setCellValue(inValue.doubleValue());
inValueCell.setCellStyle(currentStyle);
// 本期出库
Cell outUnitCell = dataRow.createCell(8);
outUnitCell.setCellValue(unit);
outUnitCell.setCellStyle(currentStyle);
Cell outNumCell = dataRow.createCell(9);
BigDecimal outNum = item.getOutCargoNum() != null ? item.getOutCargoNum() : BigDecimal.ZERO;
outNumCell.setCellValue(outNum.doubleValue());
outNumCell.setCellStyle(currentStyle);
Cell outValueCell = dataRow.createCell(10);
BigDecimal outValue = item.getOutCargoValue() != null ? item.getOutCargoValue() : BigDecimal.ZERO;
outValueCell.setCellValue(outValue.doubleValue());
outValueCell.setCellStyle(currentStyle);
// 累计结存
Cell cargoUnitCell = dataRow.createCell(11);
cargoUnitCell.setCellValue(unit);
cargoUnitCell.setCellStyle(currentStyle);
Cell cargoNumCell = dataRow.createCell(12);
BigDecimal cargoNum = item.getCargoNum() != null ? item.getCargoNum() : BigDecimal.ZERO;
cargoNumCell.setCellValue(cargoNum.doubleValue());
cargoNumCell.setCellStyle(currentStyle);
Cell cargoValueCell = dataRow.createCell(13);
BigDecimal cargoValue = item.getCargoValue() != null ? item.getCargoValue() : BigDecimal.ZERO;
cargoValueCell.setCellValue(cargoValue.doubleValue());
cargoValueCell.setCellStyle(currentStyle);
dataRowIndex++;
} else {
// 合计行:每个大列内部合并单位、数量和金额列,只显示金额(右对齐)
CellStyle totalValueStyle = getTotalValueStyle(wb);
// 序号(空)
Cell indexCell = dataRow.createCell(0);
indexCell.setCellStyle(currentStyle);
// 品名及规格:显示"合计"
Cell nameCell = dataRow.createCell(1);
nameCell.setCellValue("合计");
nameCell.setCellStyle(currentStyle);
// 上期结存:合并单位(2)、数量(3)和金额(4)列,只显示金额(右对齐)
for (int col = 2; col <= 4; col++) {
Cell cell = dataRow.createCell(col);
cell.setCellStyle(totalValueStyle);
}
sheet.addMergedRegion(new CellRangeAddress(dataRowIndex, dataRowIndex, 2, 4));
Cell lastValueCell = dataRow.getCell(2);
BigDecimal lastValue = item.getLastCargoValue() != null ? item.getLastCargoValue() : BigDecimal.ZERO;
lastValueCell.setCellValue(lastValue.doubleValue());
// 本期入库:合并单位(5)、数量(6)和金额(7)列,只显示金额(右对齐)
for (int col = 5; col <= 7; col++) {
Cell cell = dataRow.createCell(col);
cell.setCellStyle(totalValueStyle);
}
sheet.addMergedRegion(new CellRangeAddress(dataRowIndex, dataRowIndex, 5, 7));
Cell inValueCell = dataRow.getCell(5);
BigDecimal inValue = item.getInCargoValue() != null ? item.getInCargoValue() : BigDecimal.ZERO;
inValueCell.setCellValue(inValue.doubleValue());
// 本期出库:合并单位(8)、数量(9)和金额(10)列,只显示金额(右对齐)
for (int col = 8; col <= 10; col++) {
Cell cell = dataRow.createCell(col);
cell.setCellStyle(totalValueStyle);
}
sheet.addMergedRegion(new CellRangeAddress(dataRowIndex, dataRowIndex, 8, 10));
Cell outValueCell = dataRow.getCell(8);
BigDecimal outValue = item.getOutCargoValue() != null ? item.getOutCargoValue() : BigDecimal.ZERO;
outValueCell.setCellValue(outValue.doubleValue());
// 累计结存:合并单位(11)、数量(12)和金额(13)列,只显示金额(右对齐)
for (int col = 11; col <= 13; col++) {
Cell cell = dataRow.createCell(col);
cell.setCellStyle(totalValueStyle);
}
sheet.addMergedRegion(new CellRangeAddress(dataRowIndex, dataRowIndex, 11, 13));
Cell cargoValueCell = dataRow.getCell(11);
BigDecimal cargoValue = item.getCargoValue() != null ? item.getCargoValue() : BigDecimal.ZERO;
cargoValueCell.setCellValue(cargoValue.doubleValue());
dataRowIndex++;
}
}
// ====================== 底部签字 ======================
Row signRow = sheet.createRow(dataRowIndex++);
signRow.setHeight((short) 300);
CellStyle ss = getSignStyle(wb);
// 审核人合并列0-4
sheet.addMergedRegion(new CellRangeAddress(dataRowIndex - 1, dataRowIndex - 1, 0, 4));
Cell auditorCell = signRow.createCell(0);
auditorCell.setCellValue("审核人:");
auditorCell.setCellStyle(ss);
// 仓管员合并列5-9
sheet.addMergedRegion(new CellRangeAddress(dataRowIndex - 1, dataRowIndex - 1, 5, 9));
Cell warehouseCell = signRow.createCell(5);
warehouseCell.setCellValue("仓管员:");
warehouseCell.setCellStyle(ss);
// 领料人合并列10-13
sheet.addMergedRegion(new CellRangeAddress(dataRowIndex - 1, dataRowIndex - 1, 10, 13));
Cell requesterCell = signRow.createCell(10);
requesterCell.setCellValue("领料人:");
requesterCell.setCellStyle(ss);
// 设置所有单元格样式
for (int i = 0; i < 14; i++) {
Cell c = signRow.getCell(i);
if (c == null) {
c = signRow.createCell(i);
}
c.setCellStyle(ss);
}
// ====================== 列宽 ======================
int[] widths = {6, 24, 6, 10, 10, 6, 10, 10, 6, 10, 10, 6, 10, 10};
for (int i = 0; i < widths.length; i++) {
sheet.setColumnWidth(i, widths[i] * 256);
}
// ====================== 输出 ======================
response.setContentType("application/vnd.ms-excel");
String fileName = URLEncoder.encode("物资盘点表", "UTF-8").replaceAll("\\+", "%");
response.setHeader("Content-Disposition", "attachment;filename*=UTF-8''" + fileName + ".xls");
wb.write(response.getOutputStream());
wb.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}

@ -17,6 +17,7 @@ import javax.annotation.Resource;
import java.util.Map;
import java.util.Arrays;
import java.util.List;
import java.util.Date;
@Service("pmDailyMenuService")
@ -38,5 +39,20 @@ public class PmDailyMenuServiceImpl extends ServiceImpl<PmDailyMenuDao, PmDailyM
return dao.selectList(queryWrapper);
}
@Override
public PmDailyMenu findByDateAndCanteenAndMealType(String menuDate, String canteenName, String mealType) {
LambdaQueryWrapper<PmDailyMenu> queryWrapper = new LambdaQueryWrapper<>();
try {
// 解析日期字符串为 Date 对象
Date date = new java.text.SimpleDateFormat("yyyy-MM-dd").parse(menuDate);
queryWrapper.eq(PmDailyMenu::getMenuDate, date);
} catch (Exception e) {
// 如果日期解析失败,返回 null
return null;
}
queryWrapper.eq(PmDailyMenu::getCanteenName, canteenName);
queryWrapper.eq(PmDailyMenu::getMealType, mealType);
return dao.selectOne(queryWrapper);
}
}

@ -38,5 +38,11 @@ public class SysDictDataServiceImpl extends ServiceImpl<SysDictDataDao, SysDictD
return dao.selectList(queryWrapper);
}
@Override
public List<SysDictData> getByDictType(String dictType) {
LambdaQueryWrapper<SysDictData> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(SysDictData::getDictType, dictType);
return dao.selectList(queryWrapper);
}
}

@ -39,4 +39,9 @@ public class NotifyConstants {
public static final String PLACE_AN_ORDER_ADMIN_MARK = "adminPlaceAnOrder";
/** 用户发起退款(管理员)标记 */
public static final String APPLY_ORDER_REFUND_ADMIN_MARK = "adminApplyOrderRefund";
/** 报修通知标记 */
public static final String MAINTENANCE_NOTICE_MARK = "maintenanceNotice";
/** 报修通知(管理员)标记 */
public static final String MAINTENANCE_NOTICE_ADMIN_MARK = "adminMaintenanceNotice";
}

@ -0,0 +1,82 @@
package com.zbkj.common.model.wechat;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import java.io.Serializable;
import java.util.Date;
/**
*
* +----------------------------------------------------------------------
* | CRMEB [ CRMEB ]
* +----------------------------------------------------------------------
* | Copyright (c) 2016~2022 https://www.crmeb.com All rights reserved.
* +----------------------------------------------------------------------
* | Licensed CRMEBCRMEB
* +----------------------------------------------------------------------
* | Author: CRMEB Team <admin@crmeb.com>
* +----------------------------------------------------------------------
*/
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@TableName("eb_wechat_fans")
@ApiModel(value="WechatFans对象", description="公众号关注用户表")
public class WechatFans implements Serializable {
private static final long serialVersionUID=1L;
@TableId(value = "id", type = IdType.AUTO)
private Integer id;
@ApiModelProperty(value = "公众号openid")
private String openId;
@ApiModelProperty(value = "unionid")
private String unionId;
@ApiModelProperty(value = "用户昵称")
private String nickname;
@ApiModelProperty(value = "性别:0未知1-男2-女")
private Integer sex;
@ApiModelProperty(value = "用户个人资料填写的省份")
private String province;
@ApiModelProperty(value = "普通用户个人资料填写的城市")
private String city;
@ApiModelProperty(value = "国家如中国为CN")
private String country;
@ApiModelProperty(value = "用户头像")
private String headimgurl;
@ApiModelProperty(value = "用户是否关注0-未关注1-已关注")
private Integer subscribe;
@ApiModelProperty(value = "关注时间")
private Date subscribeTime;
@ApiModelProperty(value = "备注")
private String remark;
@ApiModelProperty(value = "分组ID")
private Integer groupId;
@ApiModelProperty(value = "创建时间")
private Date createTime;
@ApiModelProperty(value = "更新时间")
private Date updateTime;
}

@ -25,4 +25,14 @@ public class PageParamRequest {
@ApiModelProperty(value = "每页数量", example = Constants.DEFAULT_LIMIT + "")
private int limit = Constants.DEFAULT_LIMIT;
// 兼容pageNum参数
public void setPageNum(int pageNum) {
this.page = pageNum;
}
// 兼容pageSize参数
public void setPageSize(int pageSize) {
this.limit = pageSize;
}
}

@ -59,4 +59,7 @@ public class RegisterThirdUserRequest implements Serializable {
@ApiModelProperty(value = "用户openId")
private String openId;
@ApiModelProperty(value = "unionId")
private String unionId;
}

@ -0,0 +1,61 @@
package com.zbkj.common.request;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
*
*/
@Data
@ApiModel(value = "SubscribeMessageRequest", description = "微信一次性订阅消息请求")
public class SubscribeMessageRequest {
@ApiModelProperty(value = "接收消息的用户openid", required = true)
private String touser;
@ApiModelProperty(value = "订阅消息模板ID", required = true)
private String templateId;
@ApiModelProperty(value = "点击消息跳转链接(需ICP备案)")
private String url;
@ApiModelProperty(value = "跳小程序配置")
private MiniProgram miniProgram;
@ApiModelProperty(value = "订阅场景值", required = true)
private String scene;
@ApiModelProperty(value = "订阅通知消息标题(15字以内)", required = true)
private String title;
@ApiModelProperty(value = "消息内容", required = true)
private MessageData data;
@Data
@ApiModel(value = "MiniProgram", description = "跳小程序配置")
public static class MiniProgram {
@ApiModelProperty(value = "小程序appid", required = true)
private String appid;
@ApiModelProperty(value = "小程序页面路径", required = true)
private String pagepath;
}
@Data
@ApiModel(value = "MessageData", description = "消息内容")
public static class MessageData {
@ApiModelProperty(value = "内容信息")
private Content content;
}
@Data
@ApiModel(value = "Content", description = "内容信息")
public static class Content {
@ApiModelProperty(value = "消息文本(200字内)")
private String value;
@ApiModelProperty(value = "字体颜色")
private String color;
}
}

@ -0,0 +1,26 @@
package com.zbkj.common.response;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
*
*/
@Data
@ApiModel(value = "SubscribeMessageResponse", description = "微信订阅消息发送响应")
public class SubscribeMessageResponse {
@ApiModelProperty(value = "错误码")
private Integer errcode;
@ApiModelProperty(value = "错误信息")
private String errmsg;
/**
*
*/
public boolean isSuccess() {
return errcode == null || errcode == 0;
}
}

@ -175,7 +175,9 @@ public class FrontTokenComponent {
"api/front/index/get/version",
"api/front/image/domain",
"api/front/product/leaderboard",
"api/autogencode/sysdictdata/types"
"api/autogencode/sysdictdata/types",
"api/front/bindingWx",
"api/front/unbindWx"
};
return ArrayUtils.contains(routerList, uri);

@ -7,6 +7,7 @@ import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
@ -26,6 +27,7 @@ import springfox.documentation.swagger2.annotations.EnableSwagger2;
@EnableSwagger2
@Configuration
@EnableTransactionManagement
@EnableScheduling //开启定时任务
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class) //去掉数据源
@ComponentScan(basePackages = {"com.zbkj", "com.zbkj.front"})
@MapperScan(basePackages = {"com.zbkj.**.dao"})

@ -9,6 +9,7 @@ import com.zbkj.common.response.LoginResponse;
import com.zbkj.common.response.WeChatJsSdkConfigResponse;
import com.zbkj.front.service.UserCenterService;
import com.zbkj.service.service.SystemNotificationService;
import com.zbkj.service.service.WechatFansService;
import com.zbkj.service.service.WechatNewService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
@ -51,6 +52,9 @@ public class WeChatController {
@Autowired
private SystemNotificationService systemNotificationService;
@Autowired
private WechatFansService wechatFansService;
/**
* code
*/
@ -113,7 +117,50 @@ public class WeChatController {
public CommonResult<List<TemplateMessage>> programMyTempList(@RequestParam(name = "type") String type){
return CommonResult.success(systemNotificationService.getMiniTempList(type));
}
}
/**
*
* API
*/
@ApiOperation(value = "同步公众号关注用户列表")
@RequestMapping(value = "/fans/sync", method = RequestMethod.POST)
public CommonResult<Integer> syncFans(){
Integer count = wechatFansService.syncFans();
return CommonResult.success(count, "同步成功");
}
/**
*
* unionIdopenId
*/
@ApiOperation(value = "发送一次性订阅消息")
@RequestMapping(value = "/subscribe/message/send", method = RequestMethod.POST)
@ApiImplicitParams({
@ApiImplicitParam(name = "unionId", value = "用户unionId", dataType = "String", required = true),
@ApiImplicitParam(name = "templateId", value = "订阅消息模板ID", dataType = "String", required = true),
@ApiImplicitParam(name = "scene", value = "订阅场景值", dataType = "String", required = true),
@ApiImplicitParam(name = "title", value = "消息标题(15字以内)", dataType = "String", required = true),
@ApiImplicitParam(name = "content", value = "消息内容(200字以内)", dataType = "String", required = true),
@ApiImplicitParam(name = "url", value = "跳转链接(需ICP备案)", dataType = "String")
})
public CommonResult<Boolean> sendSubscribeMessage(@RequestParam(value = "unionId") String unionId,
@RequestParam(value = "templateId") String templateId,
@RequestParam(value = "scene") String scene,
@RequestParam(value = "title") String title,
@RequestParam(value = "content") String content,
@RequestParam(value = "url", required = false) String url){
// 通过unionId获取公众号openId
String openId = wechatFansService.getOpenIdByUnionId(unionId);
if (openId == null) {
return CommonResult.failed("未找到该用户的公众号openId");
}
// 发送订阅消息
boolean success = wechatFansService.sendSubscribeMessage(openId, templateId, scene, title, content, url);
if (success) {
return CommonResult.success(true, "发送成功");
} else {
return CommonResult.failed("发送失败");
}
}
}

@ -547,6 +547,7 @@ public class UserCenterServiceImpl extends ServiceImpl<UserDao, User> implements
request.setType(Constants.USER_LOGIN_TYPE_PROGRAM);
request.setOpenId(response.getOpenId());
request.setUnionId(response.getUnionId());
String key = SecureUtil.md5(response.getOpenId());
redisUtil.set(key, JSONObject.toJSONString(request), (long) (60 * 2), TimeUnit.MINUTES);
loginResponse.setType("register");

@ -28,4 +28,6 @@ public interface SystemMenuDao extends BaseMapper<SystemMenu> {
* @return List
*/
List<SystemMenu> getMenusByUserId(Integer userId);
List<SystemMenu> getMenusByUserIdAndPhoto(Integer userId);
}

@ -0,0 +1,21 @@
package com.zbkj.service.dao;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.zbkj.common.model.wechat.WechatFans;
import org.apache.ibatis.annotations.Mapper;
/**
* Mapper
* +----------------------------------------------------------------------
* | CRMEB [ CRMEB ]
* +----------------------------------------------------------------------
* | Copyright (c) 2016~2022 https://www.crmeb.com All rights reserved.
* +----------------------------------------------------------------------
* | Licensed CRMEBCRMEB
* +----------------------------------------------------------------------
* | Author: CRMEB Team <admin@crmeb.com>
* +----------------------------------------------------------------------
*/
@Mapper
public interface WechatFansDao extends BaseMapper<WechatFans> {
}

@ -104,4 +104,11 @@ public interface SystemMenuService extends IService<SystemMenu> {
* @return List
*/
List<SystemMenu> getMenusByUserId(Integer userId);
/**
* id
* @param userId id
* @return List
*/
List<SystemMenu> getMenusByUserIdAndPhoto(Integer userId);
}

@ -0,0 +1,60 @@
package com.zbkj.service.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.zbkj.common.model.wechat.WechatFans;
import java.util.List;
/**
*
* +----------------------------------------------------------------------
* | CRMEB [ CRMEB ]
* +----------------------------------------------------------------------
* | Copyright (c) 2016~2022 https://www.crmeb.com All rights reserved.
* +----------------------------------------------------------------------
* | Licensed CRMEBCRMEB
* +----------------------------------------------------------------------
* | Author: CRMEB Team <admin@crmeb.com>
* +----------------------------------------------------------------------
*/
public interface WechatFansService extends IService<WechatFans> {
/**
*
* @return
*/
Integer syncFans();
/**
* openId
* @param openId openId
* @return WechatFans
*/
WechatFans getByOpenId(String openId);
/**
*
* @param list
* @return
*/
boolean batchSaveOrUpdate(List<WechatFans> list);
/**
*
* @param openId openid
* @param templateId ID
* @param scene
* @param title
* @param content
* @param url
* @return
*/
boolean sendSubscribeMessage(String openId, String templateId, String scene, String title, String content, String url);
/**
* unionIdopenId
* @param unionId unionId
* @return openId
*/
String getOpenIdByUnionId(String unionId);
}

@ -30,6 +30,7 @@ import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@Service
@ -572,6 +573,7 @@ public class SystemAdminServiceImpl extends ServiceImpl<SystemAdminDao, SystemAd
lqw.eq(SystemAdmin::getIsDel, false);
return dao.selectOne(lqw);
}
}

@ -266,6 +266,16 @@ public class SystemMenuServiceImpl extends ServiceImpl<SystemMenuDao, SystemMenu
return dao.getMenusByUserId(userId);
}
/**
* id
* @param userId id
* @return List
*/
@Override
public List<SystemMenu> getMenusByUserIdAndPhoto(Integer userId) {
return dao.getMenusByUserIdAndPhoto(userId);
}
/**
* id
* @param pid id

@ -1095,6 +1095,7 @@ public class UserServiceImpl extends ServiceImpl<UserDao, User> implements UserS
user.setSpreadTime(DateUtil.nowDateTime());
user.setSex(Integer.parseInt(thirdUserRequest.getSex()));
user.setAddres(thirdUserRequest.getCountry() + "," + thirdUserRequest.getProvince() + "," + thirdUserRequest.getCity());
user.setUnionId(thirdUserRequest.getUnionId());
return user;
}

@ -0,0 +1,270 @@
package com.zbkj.service.service.impl;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ObjectUtil;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.zbkj.common.model.wechat.WechatFans;
import com.zbkj.common.utils.RestTemplateUtil;
import com.zbkj.service.dao.WechatFansDao;
import com.zbkj.service.service.WechatFansService;
import com.zbkj.service.service.WechatNewService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
/**
*
* +----------------------------------------------------------------------
* | CRMEB [ CRMEB ]
* +----------------------------------------------------------------------
* | Copyright (c) 2016~2022 https://www.crmeb.com All rights reserved.
* +----------------------------------------------------------------------
* | Licensed CRMEBCRMEB
* +----------------------------------------------------------------------
* | Author: CRMEB Team <admin@crmeb.com>
* +----------------------------------------------------------------------
*/
@Service
public class WechatFansServiceImpl extends ServiceImpl<WechatFansDao, WechatFans> implements WechatFansService {
private static final Logger logger = LoggerFactory.getLogger(WechatFansServiceImpl.class);
@Autowired
private WechatNewService wechatNewService;
@Autowired
private RestTemplateUtil restTemplateUtil;
/**
* URL
*/
private static final String GET_FANS_URL = "https://api.weixin.qq.com/cgi-bin/user/get?access_token=%s&next_openid=%s";
/**
* URL
*/
private static final String BATCH_GET_USER_INFO_URL = "https://api.weixin.qq.com/cgi-bin/user/info/batchget?access_token=%s";
/**
* URL
*/
private static final String SEND_SUBSCRIBE_MESSAGE_URL = "https://api.weixin.qq.com/cgi-bin/message/template/subscribe?access_token=%s";
@Override
@Transactional(rollbackFor = Exception.class)
public Integer syncFans() {
int totalCount = 0;
String nextOpenId = "";
String accessToken = wechatNewService.getPublicAccessToken();
try {
do {
String url = String.format(GET_FANS_URL, accessToken, nextOpenId);
String result = restTemplateUtil.getLink(url);
JSONObject jsonObject = JSONObject.parseObject(result);
if (jsonObject.containsKey("errcode") && jsonObject.getInteger("errcode") != 0) {
logger.error("获取公众号关注用户列表失败: {}", jsonObject.getString("errmsg"));
break;
}
Integer total = jsonObject.getInteger("total");
Integer count = jsonObject.getInteger("count");
nextOpenId = jsonObject.getString("next_openid");
if (count != null && count > 0) {
JSONObject data = jsonObject.getJSONObject("data");
if (data != null) {
JSONArray openIdArray = data.getJSONArray("openid");
if (openIdArray != null && !openIdArray.isEmpty()) {
List<WechatFans> fansList = batchGetUserInfo(accessToken, openIdArray);
if (!fansList.isEmpty()) {
batchSaveOrUpdate(fansList);
totalCount += fansList.size();
}
}
}
}
if (count == null || count == 0 || ObjectUtil.isEmpty(nextOpenId)) {
break;
}
} while (true);
logger.info("公众号关注用户同步完成,共同步 {} 个用户", totalCount);
} catch (Exception e) {
logger.error("同步公众号关注用户失败", e);
}
return totalCount;
}
private List<WechatFans> batchGetUserInfo(String accessToken, JSONArray openIdArray) {
List<WechatFans> fansList = new ArrayList<>();
try {
for (int i = 0; i < openIdArray.size(); i += 100) {
int end = Math.min(i + 100, openIdArray.size());
JSONArray batchArray = new JSONArray();
for (int j = i; j < end; j++) {
JSONObject item = new JSONObject();
item.put("openid", openIdArray.getString(j));
item.put("lang", "zh_CN");
batchArray.add(item);
}
JSONObject requestBody = new JSONObject();
requestBody.put("user_list", batchArray);
String url = String.format(BATCH_GET_USER_INFO_URL, accessToken);
String result = restTemplateUtil.postStringData(url, requestBody.toJSONString());
JSONObject jsonObject = JSONObject.parseObject(result);
if (jsonObject.containsKey("errcode") && jsonObject.getInteger("errcode") != 0) {
logger.error("批量获取用户信息失败: {}", jsonObject.getString("errmsg"));
continue;
}
JSONArray userInfoList = jsonObject.getJSONArray("user_info_list");
if (userInfoList != null && !userInfoList.isEmpty()) {
for (int j = 0; j < userInfoList.size(); j++) {
JSONObject userInfo = userInfoList.getJSONObject(j);
WechatFans fans = convertToWechatFans(userInfo);
fansList.add(fans);
}
}
}
} catch (Exception e) {
logger.error("批量获取用户信息异常", e);
}
return fansList;
}
private WechatFans convertToWechatFans(JSONObject userInfo) {
WechatFans fans = new WechatFans();
fans.setOpenId(userInfo.getString("openid"));
fans.setUnionId(userInfo.getString("unionid"));
fans.setNickname(userInfo.getString("nickname"));
fans.setSex(userInfo.getInteger("sex"));
fans.setProvince(userInfo.getString("province"));
fans.setCity(userInfo.getString("city"));
fans.setCountry(userInfo.getString("country"));
fans.setHeadimgurl(userInfo.getString("headimgurl"));
fans.setSubscribe(userInfo.getInteger("subscribe"));
Long subscribeTime = userInfo.getLong("subscribe_time");
if (subscribeTime != null) {
fans.setSubscribeTime(new Date(subscribeTime * 1000L));
}
fans.setRemark(userInfo.getString("remark"));
fans.setGroupId(userInfo.getInteger("groupid"));
fans.setCreateTime(DateUtil.date());
fans.setUpdateTime(DateUtil.date());
return fans;
}
@Override
public WechatFans getByOpenId(String openId) {
LambdaQueryWrapper<WechatFans> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(WechatFans::getOpenId, openId);
return getOne(wrapper);
}
@Override
@Transactional(rollbackFor = Exception.class)
public boolean batchSaveOrUpdate(List<WechatFans> list) {
if (list == null || list.isEmpty()) {
return true;
}
for (WechatFans fans : list) {
WechatFans existing = getByOpenId(fans.getOpenId());
if (existing != null) {
fans.setId(existing.getId());
fans.setCreateTime(existing.getCreateTime());
}
saveOrUpdate(fans);
}
return true;
}
/**
*
* @Scheduled(cron = "0 0 2 * * ?") 2
*/
// @Scheduled(cron = "0 * * * * ?")
public void scheduledSyncFans() {
logger.info("========== 开始定时同步公众号关注用户 ==========");
try {
Integer count = syncFans();
logger.info("定时同步公众号关注用户完成,同步 {} 个用户", count);
} catch (Exception e) {
logger.error("定时同步公众号关注用户失败", e);
}
logger.info("========== 定时同步公众号关注用户结束 ==========");
}
@Override
public boolean sendSubscribeMessage(String openId, String templateId, String scene, String title, String content, String url) {
try {
String accessToken = wechatNewService.getPublicAccessToken();
JSONObject requestBody = new JSONObject();
requestBody.put("touser", openId);
requestBody.put("template_id", templateId);
requestBody.put("scene", scene);
requestBody.put("title", title);
if (url != null && !url.isEmpty()) {
requestBody.put("url", url);
}
JSONObject data = new JSONObject();
JSONObject contentObj = new JSONObject();
contentObj.put("value", content);
contentObj.put("color", "#173177");
data.put("content", contentObj);
requestBody.put("data", data);
String requestUrl = String.format(SEND_SUBSCRIBE_MESSAGE_URL, accessToken);
String result = restTemplateUtil.postStringData(requestUrl, requestBody.toJSONString());
JSONObject jsonObject = JSONObject.parseObject(result);
Integer errcode = jsonObject.getInteger("errcode");
if (errcode == null || errcode == 0) {
logger.info("发送一次性订阅消息成功openId: {}", openId);
return true;
} else {
logger.error("发送一次性订阅消息失败openId: {}, error: {}", openId, jsonObject.getString("errmsg"));
return false;
}
} catch (Exception e) {
logger.error("发送一次性订阅消息异常", e);
return false;
}
}
@Override
public String getOpenIdByUnionId(String unionId) {
LambdaQueryWrapper<WechatFans> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(WechatFans::getUnionId, unionId);
WechatFans fans = getOne(wrapper);
return fans != null ? fans.getOpenId() : null;
}
}

@ -37,14 +37,14 @@ public class WeiXinUtil {
// 日期格式
private static SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
// 微信小程序appid
private static String appid="wx72fabe1da6e6fb82" ;
private static String appid="wxacb083448570908a" ;
// 微信小程序appsecret
private static String appsecret="24479b73fc113f01048e7236378c6ce8";
private static String appsecret="b9289b898e0bc822827fa8f65dfb71bd";
// 微信公众号appid
private static String gzhappid="wxe8539d3b62c9a06c";
private static String gzhappid="wx49d7d3aba9479a85";
// 微信公众号appsecret
private static String gzhappsecret="84f607283501d0177ca7595f709c20ca";
private static String gzhappsecret="d4aaa97c1a169cede51f3dbb6c44c574";
// 微信开放号appid
private static String openAppid = "";

@ -24,4 +24,15 @@
ORDER BY m.sort ASC, m.id ASC
</select>
<select id="getMenusByUserIdAndPhoto" resultType="com.zbkj.common.model.system.SystemMenu" parameterType="Integer" >
SELECT m.*
FROM eb_system_menu m
right join eb_system_role_menu rm on rm.menu_id = m.id
right join eb_system_role r on rm.rid = r.id
right join eb_system_admin a on FIND_IN_SET(r.id, a.roles)
where m.is_delte = 0 and r.`status` = 1 and m.is_show = 1 and a.id = #{userId}
GROUP BY m.id
ORDER BY m.sort ASC, m.id ASC
</select>
</mapper>

Loading…
Cancel
Save