入库流程对接完毕

This commit is contained in:
zx
2026-04-03 08:38:34 +08:00
parent dde27ed688
commit ce6d5e5d6e
42 changed files with 3028 additions and 936 deletions

24
api/getRepository.js Normal file
View File

@@ -0,0 +1,24 @@
// mockWarehousing.js
// 模拟数据
const mockWarehousingData = [
{ id: 101, deptName: "Y00002", deptId: "WSA0197", remark: "备注" },
{ id: 102, deptName: "Y00003", deptId: "WSA0198", remark: "" },
{ id: 103, deptName: "Y00004", deptId: "WSA0199", remark: "备注" },
{ id: 104, deptName: "Y00005", deptId: "WSA0200", remark: "" },
];
// Mock 接口(和你原接口格式一致)
const getRepository = (params) => {
return new Promise((resolve) => {
setTimeout(() => {
resolve({
code: 200,
msg: "获取成功",
data: mockWarehousingData,
});
}, 200);
});
};
export default getRepository;

View File

@@ -1,5 +1,6 @@
//存放主站域名 //存放主站域名
const BASE_URL = 'http://192.168.1.5:8082' // 正式环境接口 // const BASE_URL = 'http://192.168.1.5:8082' // w
const BASE_URL = 'http://192.168.1.9:8082' // 正式环境接口
// const BASE_URL = 'http://47.100.212.83:18088' // 正式环境接口 // const BASE_URL = 'http://47.100.212.83:18088' // 正式环境接口
// const BASE_URL = 'http://192.168.1.9:8088' // 测试环境接口 // const BASE_URL = 'http://192.168.1.9:8088' // 测试环境接口
// const BASE_URL = 'http://192.168.1.5:8088' // 测试环境接口 // const BASE_URL = 'http://192.168.1.5:8088' // 测试环境接口

37
api/stockIn.js Normal file
View File

@@ -0,0 +1,37 @@
import { request } from './request'
const api ='/worn/inboundBill/'
// 新建:入库单
const addStockIn = (params) => {
return request(api+'add', params, 'POST')
}
// 列表
const stockList = (params) => {
return request(api+'list', params, 'get')
}
// 详情
const stockInDetail = (params) => {
return request('/worn/item/list', params, 'get')
}
// 编辑
const stockInUpdate = (params) => {
return request('/worn/item/update', params, 'post')
}
// 入库
const inboundFinish = (params) => {
return request('/worn/inboundBill/inboundFinish', params, 'post')
}
// 作废
const inboundBillVoid = (params) => {
return request('/worn/inboundBill/void', params, 'post')
}
export {
addStockIn,
stockList,
stockInDetail,
stockInUpdate,
inboundFinish,
inboundBillVoid
}

8
api/system.js Normal file
View File

@@ -0,0 +1,8 @@
import { request } from "./request";
// 根据当前用户获取仓库信息
const getWarehousingInfo = (params) => {
return request(`/system/user/myDeptChildren`, params, "get");
};
export { getWarehousingInfo };

View File

@@ -21,9 +21,12 @@ const delUniqueCode = (params) => {
}; };
// 详情:唯一码 // 详情:唯一码
const detailUniqueCode = (params) => { const detailUniqueCode = (params) => {
console.log(params,'params');
return request(`/unique/code/${params.id}`, params, 'GET'); return request(`/unique/code/${params.id}`, params, 'GET');
}; };
// 详情:通过唯一码获取物料信息
const getMaterialUnique = (params) => {
return request(`/unique/code/materialInfo/${params.code}`, params, 'GET');
};
export { export {
getMaterial, getMaterial,
@@ -32,4 +35,5 @@ export {
delUniqueCode, delUniqueCode,
detailUniqueCode, detailUniqueCode,
editUniqueCode, editUniqueCode,
getMaterialUnique
}; };

View File

@@ -1,60 +0,0 @@
<!-- components/BarcodeGenerator.vue -->
<template>
<div class="barcode-container">
<canvas v-if="type === 'canvas'" ref="barcodeRef"></canvas>
<svg v-else ref="barcodeRef"></svg>
</div>
</template>
<script setup>
import { ref, watch, onMounted, defineProps, defineEmits } from 'vue';
import JsBarcode from 'jsbarcode';
// 定义Props外部传参
const props = defineProps({
value: { // 条形码内容
type: String,
required: true
},
format: { // 条形码格式
type: String,
default: 'CODE128'
},
type: { // 渲染类型
type: String,
default: 'canvas',
validator: (val) => ['canvas', 'svg'].includes(val)
},
options: { // 自定义配置
type: Object,
default: () => ({
width: 2,
height: 100,
displayValue: true
})
}
})
// 定义DOM引用
const barcodeRef = ref(null)
// 生成条形码
const generate = () => {
if (!barcodeRef.value) return
// 合并默认配置和外部配置
const finalOptions = { ...props.options, format: props.format }
// 清空CanvasSVG无需清空
if (props.type === 'canvas') {
const ctx = barcodeRef.value.getContext('2d')
ctx.clearRect(0, 0, barcodeRef.value.width, barcodeRef.value.height)
}
// 生成条形码
JsBarcode(barcodeRef.value, props.value, finalOptions)
}
// 挂载后初始化
onMounted(generate)
// 监听props变化自动更新
watch([() => props.value, () => props.format, () => props.options], generate, { deep: true })
</script>

24
package-lock.json generated
View File

@@ -6,7 +6,10 @@
"": { "": {
"dependencies": { "dependencies": {
"@climblee/uv-ui": "^1.1.20", "@climblee/uv-ui": "^1.1.20",
"dayjs": "^1.11.20",
"jsbarcode": "^3.12.3", "jsbarcode": "^3.12.3",
"lodash": "^4.17.23",
"numeral": "^2.0.6",
"z-paging": "^2.8.8" "z-paging": "^2.8.8"
}, },
"devDependencies": { "devDependencies": {
@@ -817,6 +820,12 @@
"dev": true, "dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/dayjs": {
"version": "1.11.20",
"resolved": "https://registry.npmmirror.com/dayjs/-/dayjs-1.11.20.tgz",
"integrity": "sha512-YbwwqR/uYpeoP4pu043q+LTDLFBLApUP6VxRihdfNTqu4ubqMlGDLd6ErXhEgsyvY0K6nCs7nggYumAN+9uEuQ==",
"license": "MIT"
},
"node_modules/detect-libc": { "node_modules/detect-libc": {
"version": "2.1.2", "version": "2.1.2",
"resolved": "https://registry.npmmirror.com/detect-libc/-/detect-libc-2.1.2.tgz", "resolved": "https://registry.npmmirror.com/detect-libc/-/detect-libc-2.1.2.tgz",
@@ -1107,6 +1116,12 @@
"node": ">=8.9.0" "node": ">=8.9.0"
} }
}, },
"node_modules/lodash": {
"version": "4.17.23",
"resolved": "https://registry.npmmirror.com/lodash/-/lodash-4.17.23.tgz",
"integrity": "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==",
"license": "MIT"
},
"node_modules/merge-stream": { "node_modules/merge-stream": {
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmmirror.com/merge-stream/-/merge-stream-2.0.0.tgz", "resolved": "https://registry.npmmirror.com/merge-stream/-/merge-stream-2.0.0.tgz",
@@ -1159,6 +1174,15 @@
"dev": true, "dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/numeral": {
"version": "2.0.6",
"resolved": "https://registry.npmmirror.com/numeral/-/numeral-2.0.6.tgz",
"integrity": "sha512-qaKRmtYPZ5qdw4jWJD6bxEf1FJEqllJrwxCLIm0sQU/A7v2/czigzOb+C2uSiFsa9lBUzeH7M1oK+Q+OLxL3kA==",
"license": "MIT",
"engines": {
"node": "*"
}
},
"node_modules/picocolors": { "node_modules/picocolors": {
"version": "1.1.1", "version": "1.1.1",
"resolved": "https://registry.npmmirror.com/picocolors/-/picocolors-1.1.1.tgz", "resolved": "https://registry.npmmirror.com/picocolors/-/picocolors-1.1.1.tgz",

View File

@@ -1,7 +1,10 @@
{ {
"dependencies": { "dependencies": {
"@climblee/uv-ui": "^1.1.20", "@climblee/uv-ui": "^1.1.20",
"dayjs": "^1.11.20",
"jsbarcode": "^3.12.3", "jsbarcode": "^3.12.3",
"lodash": "^4.17.23",
"numeral": "^2.0.6",
"z-paging": "^2.8.8" "z-paging": "^2.8.8"
}, },
"devDependencies": { "devDependencies": {

View File

@@ -32,22 +32,117 @@
"path": "pages/my/index", "path": "pages/my/index",
"style": { "navigationBarTitleText": "我的" } "style": { "navigationBarTitleText": "我的" }
}, },
// 仓储 - 唯一码
{ {
"path": "pages/uniqueCode/issueUniqueCode/index", "path": "pages/warehousing/uniqueCode/issueUniqueCode/index",
"style": { "navigationBarTitleText": "唯一码发放" } "style": { "navigationBarTitleText": "唯一码发放" }
}, },
{ {
"path": "pages/uniqueCode/issueUniqueCode/materialSelection", "path": "pages/warehousing/uniqueCode/issueUniqueCode/materialSelection",
"style": { "navigationBarTitleText": "物料选择" } "style": { "navigationBarTitleText": "物料选择" }
}, },
{ {
"path": "pages/uniqueCode/myUniqueCode/index", "path": "pages/warehousing/uniqueCode/myUniqueCode/index",
"style": { "navigationBarTitleText": "唯一码" } "style": { "navigationBarTitleText": "唯一码" }
}, },
{ {
"path": "pages/uniqueCode/myUniqueCode/detail", "path": "pages/warehousing/uniqueCode/myUniqueCode/detail",
"style": { "navigationBarTitleText": "唯一码详情" } "style": {
"navigationBarTitleText": "唯一码详情",
"navigationStyle": "custom"
}
},
// {
// "path": "pages/uniqueCode/bindRfid",
// "style": { "navigationBarTitleText": "绑定RFID" }
// },
// ========== 库存信息模块 ==========
// {
// "path": "pages/inventory/checkUniqueCode",
// "style": { "navigationBarTitleText": "唯一码盘点" }
// },
// {
// "path": "pages/inventory/age",
// "style": { "navigationBarTitleText": "库龄查看" }
// },
// {
// "path": "pages/inventory/alert",
// "style": { "navigationBarTitleText": "库存预警" }
// },
// ========== 报表模块 ==========
// {
// "path": "pages/report/daily",
// "style": { "navigationBarTitleText": "库存日报" }
// },
// {
// "path": "pages/report/monthly",
// "style": { "navigationBarTitleText": "库存月报" }
// },
// {
// "path": "pages/report/company",
// "style": { "navigationBarTitleText": "公司库存报表" }
// },
// {
// "path": "pages/report/warehouse",
// "style": { "navigationBarTitleText": "仓库库存报表" }
// },
// ========== 申报单模块 ==========
// {
// "path": "pages/declaration/materialQuery",
// "style": { "navigationBarTitleText": "物资查询" }
// },
// {
// "path": "pages/declaration/create",
// "style": { "navigationBarTitleText": "申报单开单" }
// },
// {
// "path": "pages/declaration/my",
// "style": { "navigationBarTitleText": "我的申报单" }
// },
// ========== 入库单模块 ==========
{
"path": "pages/warehousing/stockIn/create",
"style": { "navigationBarTitleText": "入库单开单" }
},
{
"path": "pages/warehousing/stockIn/my",
"style": { "navigationBarTitleText": "我的入库单" }
},
{
"path": "pages/warehousing/stockIn/components/detail",
"style": { "navigationBarTitleText": "详情", "navigationStyle": "custom" }
},
{
"path": "pages/warehousing/stockIn/putaway",
"style": { "navigationBarTitleText": "入库单入库" }
},
{
"path": "pages/warehousing/toChooseList",
"style": { "navigationBarTitleText": "请选择" }
},
{
"path": "pages/warehousing/stockIn/components/inbound",
"style": { "navigationBarTitleText": "入库" }
} }
// ========== 出库单模块 ==========
// {
// "path": "pages/stockOut/create",
// "style": { "navigationBarTitleText": "出库单开单" }
// },
// {
// "path": "pages/stockOut/my",
// "style": { "navigationBarTitleText": "我的出库单" }
// },
// {
// "path": "pages/stockOut/warehousing",
// "style": { "navigationBarTitleText": "出库单出库" }
// }
], ],
"globalStyle": { "globalStyle": {
"navigationBarTextStyle": "black", "navigationBarTextStyle": "black",
@@ -55,18 +150,17 @@
"navigationBarBackgroundColor": "#F5F5F5", "navigationBarBackgroundColor": "#F5F5F5",
"backgroundColor": "#F5F5F5" "backgroundColor": "#F5F5F5"
}, },
// 底部 tabBar 核心配置
"tabBar": { "tabBar": {
"color": "#666", // 未选中文字颜色 "color": "#666",
"selectedColor": "#007AFF", // 选中文字颜色 "selectedColor": "#007AFF",
"backgroundColor": "#fff", // 导航栏背景色 "backgroundColor": "#fff",
"borderStyle": "black", // 上边框样式black/white "borderStyle": "black",
"height": "50px", // 导航栏高度App 端建议 50px 左右) "height": "50px",
"list": [ "list": [
{ {
"pagePath": "pages/index/index", // 必须和 pages 中的路径一致 "pagePath": "pages/index/index",
"text": "首页", // 底部文字 "text": "首页",
"iconPath": "static/icon/home.png", // 未选中图标(建议 28x28px "iconPath": "static/icon/home.png",
"selectedIconPath": "static/icon/home-active.png" "selectedIconPath": "static/icon/home-active.png"
}, },
{ {

View File

@@ -0,0 +1,236 @@
<!-- 仓库/存储区选择 -->
<template>
<view class="content">
<view class="topSearch">
<uni-easyinput type="text" v-model="queryParams.keyword" prefixIcon="search" :inputBorder="false"
@iconClick="getList" placeholder="请输入搜索内容" />
</view>
<!-- 仓库/存储列表 -->
<z-paging ref="pagingRef" class="containerBox" v-model="infoList" @query="getList">
<uni-list class="listBox">
<uni-list-item v-for="item in infoList" :key="item.id" clickable @click="onChecked(item)">
<template v-slot:body>
<view style="display: flex; flex-direction: column; width: 100%;" v-if="isWarehousing">
<view class="line title">
<p>仓库名称</p>
<p>{{ item.deptName }}</p>
</view>
<view class="line content">
<p>仓库编码</p>
<p>{{ item.deptId }}</p>
</view>
<!-- todo 以下两个字段pc未维护暂时注释 -->
<!-- <view class=" line content">
<p>仓库类型</p>
<p></p>
</view>
<view class="line content">
<p>仓库位置</p>
<p></p>
</view> -->
<view class="line content">
<p>备注</p>
<p>
<span v-if="item?.remark"></span>
<span v-else class="empty">未填写</span>
</p>
</view>
</view>
<view v-else style="display: flex; flex-direction: column;width: 100%;">
<view class="line title">
<p>{{ item.deptName }} {{ item.deptId }}</p>
</view>
<view class="line content">
<p>
<span v-if="item?.remark">{{ item?.remark }}</span>
<span v-else class="empty">未填写</span>
</p>
</view>
</view>
</template>
</uni-list-item>
</uni-list>
</z-paging>
</view>
</template>
<script setup>
import { ref, toRefs } from 'vue';
import { onShow } from "@dcloudio/uni-app";
import { getWarehousingInfo } from '../../api/system';
import getRepository from "@/api/getRepository";
import { objectToQuery } from '../until';
const props = defineProps({
// 是否为仓库查询
isWarehousing: {
type: Boolean,
default: true,
required: true
},
// 返回出库开单/入库开单
backStr: {
type: String,
default: true,
required: true
},
pathParams: {
type: Object,
default: null,
required: false
}
})
const { isWarehousing, backStr, pathParams } = toRefs(props)
// 数据:查询关键字
const queryParams = ref({
keyword: ''
})
// 绑定:加载屏
const pagingRef = ref(null)
// 数据:仓库/存储区
const infoList = ref([])
// 列表判断:获取仓库列表/存储库列表
const getList = () => {
if (isWarehousing.value) {
// 仓库列表
getWarehousing()
} else {
// 存储库列表
getRepositoryInfo()
}
}
// 列表:仓库列表
const getWarehousing = () => {
const userInfo = uni.getStorageSync('app_user')
const params = {
userId: `${userInfo.userId}`,
keyword: queryParams.value.keyword
}
getWarehousingInfo(params).then(res => {
res.data.forEach(e => {
e.showMore = false;
});
pagingRef.value.complete(res.data)
}).catch(res => {
pagingRef.value.complete(false)
})
}
// 列表:存储区列表
const getRepositoryInfo = async () => {
getRepository({ pageNum: 1, pageSize: 10 }).then(res => {
res.data.forEach(e => {
e.showMore = false;
});
pagingRef.value.complete(res.data)
}).catch(res => {
pagingRef.value.complete(false)
})
}
// 返回路径
const toBack = (back) => {
let url = ''
switch (back) {
case 'stockIn':
// 入库单开单
url = '/pages/warehousing/stockIn/create'
break;
case 'stockOut':
// 出库单开单
url = ''
break;
}
let params = pathParams.value
delete params.backStr
delete params.flag
const query = objectToQuery(params)
uni.navigateTo({
url: `${url}${query}`
})
}
// 选择:仓库/存储区
const onChecked = (val) => {
const flag = isWarehousing.value ? 'app_warehousing' : 'app_storageArea'
let infoChecked = uni.getStorageSync(flag);
if (!Array.isArray(infoChecked)) {
infoChecked = [];
}
if (val) {
infoChecked = [{ ...val }]
uni.setStorageSync(flag, infoChecked);
}
// 返回list
toBack(backStr.value)
}
onShow(() => {
pagingRef.value?.reload?.()
})
</script>
<style scoped lang="scss">
.topSearch {
position: fixed;
top: 0;
left: 0;
right: 0;
z-index: 999;
padding: 4rpx;
background-color: #fff;
border-bottom: 1px solid #eee;
box-sizing: border-box;
}
.containerBox {
padding-top: 44px;
width: 100%;
height: 100%;
box-sizing: border-box;
.zp-l-text-rpx {
font-size: 12px;
}
}
::v-deep.listBox {
.uni-list {
background-color: #F8F8FF;
}
.uni-list-item {
margin-bottom: 1rpx;
}
.uni-list-item__container {
padding: 0;
}
.line {
display: flex;
justify-content: space-between;
align-items: center;
border-bottom: 1px solid #eee;
padding: 12rpx;
}
.title {
font-weight: 600;
font-size: 14px;
}
.content {
font-weight: 500;
font-size: 13px;
}
.empty {
color: #D3D3D3;
}
}
</style>

View File

@@ -0,0 +1,88 @@
<template>
<view>
<view>
<view class="detailInfo mb-2">
<view class="line title">
<p> {{ typeName + '单号' }}{{ detailInfo?.billNo }}</p>
<span :style="getColor(detailInfo?.billType)">
{{ getBillType(detailInfo?.billType,detailInfo?.status) }}
</span>
</view>
<p class="line content">仓库
<span> {{ detailInfo?.warehouseName }}</span>
</p>
<p class="line content">存储区
<span> {{ detailInfo?.areaName }}</span>
</p>
<p class="line content">开单时间
<span>{{ formatDate(detailInfo?.createTime) }}</span>
</p>
<p class="line content" v-if="detailInfo?.inboundTime">{{ typeName + '时间' }}
<span>{{ formatDate(detailInfo?.inboundTime) }}</span>
</p>
</view>
<view class="detailInfo mb-6">
<p class="line content">
<span class="grey" style="font-weight: 600;">详细备注</span>
<span>{{ detailInfo?.billRemark || '-' }}</span>
</p>
</view>
</view>
<!-- 物料列表 -只读 -->
<view>
<material-list ref="materialRef" isEdit="3" :extendData="{ billType: detailInfo?.billType }"
:pathParams="{ billNo: detailInfo?.billNo }" backStr="stockInDetail"
:formData="{ material: detailInfo.itemList }" />
</view>
</view>
</template>
<script setup>
import { getBillType, formatDate, getColor } from '../until'
import { computed, toRefs } from 'vue';
import MaterialList from './MaterialList.vue'
const props = defineProps({
// 查询列表类型 stockIn:入库 stockOut出库
type: {
type: String,
default: '',
required: true
},
detailInfo: {
type: Object,
default: {},
required: true
}
})
const { type, detailInfo } = toRefs(props)
const typeName = computed(() => type.value == 'stockIn' ? '入库单' : '出库单')
console.log(detailInfo, 'detailInfo');
</script>
<style scoped lang="scss">
.line {
display: flex;
background-color: #fff;
align-items: center;
padding: 6rpx 20rpx;
}
.title {
display: flex;
justify-content: space-between;
font-weight: 600;
font-size: 13px;
}
.content {
font-weight: 500;
font-size: 13px;
}
</style>

View File

@@ -0,0 +1,97 @@
<template>
<view class="step-container">
<view class="step-item" :class="{ done: index < current, active: index == current }"
v-for="(item, index) in stepList" :key="index">
<!-- 节点圆圈 -->
<view class="step-circle">{{ index + 1 }}</view>
<!-- 文字 -->
<view class="step-text">{{ item }}</view>
<!-- 连接线最后一个不显示 -->
<view class="step-line" v-if="index !== stepList.length - 1"></view>
</view>
</view>
</template>
<script setup>
import { ref } from 'vue'
// 步骤列表
const stepList = ref(['创建', '审核', '绑定', '完成'])
// 当前进度0开始
const current = ref(2)
</script>
<style scoped>
/* 整体容器 */
.step-container {
display: flex;
padding: 30rpx;
background: #fff;
border-radius: 16rpx;
}
/* 单个步骤 */
.step-item {
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
position: relative;
}
/* 节点圆圈 */
.step-circle {
width: 40rpx;
height: 40rpx;
border-radius: 50%;
background: #cdd4e0;
color: #fff;
display: flex;
align-items: center;
justify-content: center;
font-size: 24rpx;
z-index: 10;
}
/* 文字 */
.step-text {
margin-top: 12rpx;
font-size: 24rpx;
color: #999;
}
/* 连接线 */
.step-line {
position: absolute;
top: 20rpx;
left: 50%;
width: 100%;
height: 4rpx;
background: #cdd4e0;
z-index: 1;
}
/* ============= 状态样式 ============= */
/* 已完成 */
.step-item.done .step-circle {
background: #07c160;
}
.step-item.done .step-line {
background: #07c160;
}
.step-item.done .step-text {
color: #07c160;
}
/* 当前激活 */
.step-item.active .step-circle {
background: #465bfd;
}
.step-item.active .step-text {
color: #465bfd;
font-weight: bold;
}
</style>

View File

@@ -0,0 +1,281 @@
<template>
<!-- 物料列表 - 添加物料 -->
<view class="materialList">
<view class="remarkLine">
<text>物料列表</text>
<uv-button type="primary" v-if="isEdit == 1" :plain="true" size="small" text="添加物料"
@click="toMaterial"></uv-button>
<view v-if="isEdit == 5" class="flex align-center justify-between">
<span class="f-12 mr-16 grey" style="font-size: 12px;" v-if="num > 0">
剩余<span style="color: red;">
{{ num }}
</span>种物料未生成唯一码
</span>
<uv-button type="primary" :plain="true" size="small" text="快速生成唯一码"
@click="toGenerateUniqueCodeQuickly"></uv-button>
</view>
<!-- <p class="btn-link" v-if="isEdit == 2" @click="toEdit">修改</p> -->
</view>
<!-- 物料列表-->
<view class="material-list-container">
<uni-forms ref="materialFormRef" :modelValue="formData">
<view v-for="(item, idx) in formData.material" :key="item.id || idx" class="material-box"
@longpress="() => handleItemLongPress(item, index)">
<view class="material-item" v-if="`${item?.isDelete}` === '0'">
<!-- 标题 -->
<view class="title mb-2">
{{ item.materialName }} ({{ item.materialCode }})
</view>
<!-- 副标题简称/型号/规格/类型 -->
<view class="subTitle mb-2">
<span v-if="item.materialShortName">{{ item.materialShortName }} / </span>
<span v-if="item.model">{{ item.model }} / </span>
<span v-if="item.specification">{{ item.specification }} / </span>
<span v-if="item.typeName">{{ item.typeName }}</span>
</view>
<!-- uniqueCode -->
<view class="uniqueCode mb-2">
<span class="f-12 " v-if="item.uniqueCode">唯一码{{ item?.uniqueCode }}</span>
</view>
<!-- 数量 + 单位 -->
<view class="tag-row mb-2">
<span class="tag-error">{{ getTypeParentNames(item.typeParentNames) }}</span>
<view class="quantity-form">
<uni-forms-item :name="`material[${idx}].quantity`" label-width="0">
<uni-easyinput :disabled="!edit || isEdit == '5'"
v-model="formData.material[idx].quantity" type="number" :clearable="false"
placeholder="数量" />
</uni-forms-item>
<text class="unit">{{ item.unitName }}</text>
</view>
</view>
<!-- 库存状态 -->
<view class="flex justify-between mb-2 " v-if="isEdit == 3">
<p style="font-size: 13px;">
已入库
<span style="color:green">{{ getStockQuantity(item, 'complete') }}</span>
</p>
<p style="font-size: 13px;">
剩余
<span style="color:red">{{ getStockQuantity(item, 'remaining') }}</span>
</p>
</view>
<!-- 备注 -->
<view class="remark-row">
<uni-forms-item :name="`material[${idx}].remark`" label="备注:">
<uni-easyinput :disabled="!edit" v-model="formData.material[idx].remark" type="text"
:clearable="false" :inputBorder="false" placeholder="请填写备注信息" />
</uni-forms-item>
</view>
</view>
</view>
<!-- 空数据提示 -->
<view class="empty" v-if="!formData.material?.length">
暂无物料请添加物料
</view>
</uni-forms>
</view>
<!-- 选择物料 -->
<uv-modal ref="modalRef" class="chooseModal" :title="none" :showConfirmButton="false" :showCancelButton="false">
<p class="title">请选择称重来源</p>
<p class="link" @click="toMaterialSelection('0')">无线液位计</p>
<p class="link" @click="toMaterialSelection('1')">手动输入</p>
</uv-modal>
<!-- 删除弹窗 -->
<view>
<uni-popup ref="alertDialog" type="center">
<uni-popup-dialog type="error" v-if="isDialog" cancelText="取消" confirmText="确认" title="是否需要删除该唯一码?"
@confirm="dialogConfirm" @close="dialogClose"></uni-popup-dialog>
</uni-popup>
</view>
</view>
</template>
<script setup>
import { computed, onMounted, ref, toRefs } from 'vue';
import { assign, findIndex } from 'lodash';
import { getStockQuantity, objectToQuery } from '../until';
import { getTypeParentNames } from '../warehousing/uniqueCode/until';
import { onLoad, onShow } from "@dcloudio/uni-app";
const props = defineProps({
formData: { type: Object, default: () => ({ material: [], remark: '' }), required: true },//物料信息
isEdit: { type: Boolean, default: true, required: true }, //编辑1 / 物料只读2 / 入库单只读3 /入库生成唯一码编辑4 /5数量不可编辑 备注可编辑
backStr: { type: String, default: '' }, //选择物料返回页面的标识
pathParams: { type: Object, default: {} }, //只读时传入对应的code、id
extendData: { type: Object, default: {} }, //其他额外参数
})
const { formData, isEdit, backStr, pathParams, extendData } = toRefs(props)
console.log(extendData, 'extendData==>');
//是否为编辑状态
const edit = computed(() => {
if (`${isEdit.value}` === '1') return '1';
else if (`${isEdit.value}` === '4') return '4';
else return false;
})
// 删除:数据
const delItem = ref(null)
// 删除:开关
const alertDialog = ref(null)
// 删除:开关
const isDialog = ref(false)
const selectMaterialList = computed(() => extendData.value?.selectMaterialList)
const num = computed(() => selectMaterialList.value?.length || 0)
// 弹窗开关:入库单开单
const modalRef = ref()
// 按钮:添加物料
const toMaterial = () => {
if (backStr.value == 'stockIn') { // 入库单开单 - 添加物料可选择手输入/无线液位计
modalRef.value.open()
} else {
const path = objectToQuery({ ...pathParams.value, back: backStr?.value })
// 唯一码 - 编辑/新增
let url = '/pages/warehousing/uniqueCode/issueUniqueCode/materialSelection';
const value = uni.getStorageSync('app_material');
uni.setStorageSync('app_material', [{ ...value[0], uniqueCodeRemark: formData.value?.remark }]); // 将最新的唯一码备注写入缓存
uni.navigateTo({
url: `${url}${path}`
})
}
}
// 添加物料弹窗: 入库单入库 时可选择手动/无线液
const toMaterialSelection = (type) => {
// 0无线液 1手动输入
let url = ''
const path = objectToQuery({ ...pathParams.value, back: backStr.value })
if (type === '1') {
url = '/pages/warehousing/uniqueCode/issueUniqueCode/materialSelection'
}
uni.navigateTo({
url: `${url}${path}`
})
}
// 快速生成唯一码 -
const toGenerateUniqueCodeQuickly = () => {
const path = objectToQuery({ ...pathParams.value, back: 'inbound' })
uni.navigateTo({
url: `/pages/warehousing/uniqueCode/issueUniqueCode/index${path}`
})
}
// 删除弹窗:显示 长按事件
const handleItemLongPress = (val) => {
alertDialog.value.open()
isDialog.value = true
delItem.value = val
}
// 删除弹窗:关闭
const dialogClose = () => {
alertDialog.value.close()
isDialog.value = false
}
// 删除弹窗:确认
const dialogConfirm = () => {
const idx = findIndex(formData.value.material, (i) => `${i.id}` === `${delItem.value.id}`)
assign(formData.value.material[idx], { isDelete: '1' });
uni.setStorageSync('app_material', formData.value.material)
}
const getMaterialList = () => {
const params = formData.value?.material?.map((i) => ({
...i,
material: {
//? 这里的判断是为了区分 =详情带出的物料以及新增的物料 详情带出的物料有id以及materialId 新增物料id为物料id materialId无值
materialId: i.materialId ? i.materialId : i.id,
id: i.materialId ? i.id : null,
remark: i.remark,
quantity: i.quantity,
unitId: i.unitId,
isDelete: i?.isDelete,
uniqueCode: i?.uniqueCode
},
}))
return params
}
defineExpose({
getMaterialList
})
</script>
<style scoped lang="scss">
.remarkLine {
display: flex;
align-items: center;
background-color: #fff;
justify-content: space-between;
padding: 12px 15px;
margin-bottom: 2rpx;
text {
font-weight: 700;
font-size: 13px;
}
.btn-link {
font-weight: 500;
color: #3c9cff;
}
span {
font-size: 14px;
}
p {
font-weight: 700;
font-size: 14px;
}
}
::v-deep .uv-button {
height: 30px;
// line-height: 60rpx;
font-size: 24rpx;
}
.material-item {
padding: 12px 15px;
}
.empty {
text-align: center;
padding: 60rpx 0;
color: #999;
font-size: 26rpx;
}
::v-deep.chooseModal {
.uv-modal__content {
display: block;
padding-top: 24rpx !important;
padding: 48rpx;
.title {
font-size: 16px;
margin: 16rpx 0;
}
.link {
color: #999;
margin-top: 24rpx;
font-size: 14px;
}
}
}
</style>

View File

@@ -0,0 +1,107 @@
<template>
<!-- 状态栏占位 -->
<view class="nav-placeholder" :style="{ height: navHeight + 'px' }"></view>
<view class="navbar" :style="{ paddingTop: statusBarHeight + 'px' }">
<view class="left" @click="goBack">
<uni-icons type="left" size="18" />
</view>
<!-- 中间标题 -->
<view class="title">{{ title }}</view>
<!-- 右侧父组件传入插槽 -->
<view class="right">
<slot name="right"></slot>
</view>
</view>
<view class="content">
</view>
</template>
<script setup>
import { ref, onMounted, toRefs } from 'vue'
import { onLoad, onShow } from '@dcloudio/uni-app';
const props = defineProps({
title: { type: String, default: '' },
backUrl: { type: String, default: '' },
})
const statusBarHeight = ref(0)
const navHeight = ref(0)
const back = ref('')
const { backUrl, title } = toRefs(props)
onMounted(() => {
const res = uni.getSystemInfoSync()
statusBarHeight.value = res.statusBarHeight
navHeight.value = res.statusBarHeight + 44
})
// 接收id 且修改标题
onLoad(() => {
const pages = getCurrentPages()
if (pages.length >= 2) {
const prevPage = pages?.[pages.length - 2]
back.value = backUrl.value ? backUrl.value : prevPage.route
console.log(back.value, prevPage, pages, ' back.value');
}
})
// 返回
const goBack = () => {
console.log(back.value, back.value);
if (back.value) {
uni.navigateTo({
url: `/${back.value}`,
});
} else {
uni.navigateBack()
}
}
</script>
<style scoped lang="scss">
.navbar {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 70px;
background: #fff;
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 20rpx;
box-sizing: border-box;
z-index: 999;
// margin-bottom: 8px;
}
.left {
display: flex;
align-items: center;
}
.back-txt {
margin-left: 6rpx;
}
.title {
font-size: 14px;
font-weight: bold;
}
.right {
min-width: 60rpx;
display: flex;
align-items: center;
justify-content: flex-end;
}
.nav-placeholder {
width: 100%;
}
</style>

View File

@@ -0,0 +1,186 @@
<!-- 查询列表 - 使用我的出库单/我的入库单 -->
<template>
<view class="content">
<view class="topSearch">
<uni-easyinput type="text" v-model="queryParams.keyword" prefixIcon="search" :inputBorder="false"
@iconClick="getList" placeholder="请输入搜索内容" />
</view>
<z-paging ref="pagingRef" class="containerBox" v-model="list" @query="getList">
<uni-list class="listBox">
<uni-list-item v-for="item in list" :key="item.id" clickable @click="onDetail(item)">
<template v-slot:body>
<view style="display: flex; flex-direction: column; width: 100%;">
<view class="line title">
<p> {{ typeName + '编号' }}:{{ item.billNo }}</p>
<span :style="getColor(item?.billType)">
{{ getBillType(item?.billType, item?.status) }}
</span>
</view>
<p class="line content">{{ typeName + '类型' }}
<span> {{ getOrderType(item.status) }}</span>
</p>
<p class="line content">库位
<span> {{ item.areaName }}</span>
</p>
<p class="line content">开单时间
<span>{{ item.createTime }}</span>
</p>
</view>
</template>
</uni-list-item>
</uni-list>
</z-paging>
</view>
</template>
<script setup>
import _, { includes } from 'lodash';
import { computed, toRefs, ref } from 'vue';
import { onShow } from "@dcloudio/uni-app";
import { stockList } from '../../api/stockIn';
import { getBillType, getColor } from '../until';
const props = defineProps({
// 查询列表类型 stockIn:入库 stockIn-putAway入库单入库 stockOut出库
type: {
type: String,
default: '',
required: true
},
// 查询条件
query: {
type: Object,
default: {},
required: true
},
})
const { type, query } = toRefs(props)
const pagingRef = ref(null)
// 数据:列表
const list = ref([])
const typeName = computed(() => includes(type.value, 'stockIn') ? '入库单' : '出库单')
const getOrderType = () => {
if (includes(type.value, 'stockIn')) {
return '入库单入库'
} else {
return '出库单出库'
}
}
// 数据:关键字
const queryParams = ref({
keyword: ''
})
onShow(() => {
pagingRef.value?.reload?.()
})
// 点击:查看详情
const onDetail = (val) => {
if (_.includes(type.value, 'stockIn')) {
// pages/stockIn/detail
uni.navigateTo({
url: `/pages/warehousing/stockIn/components/detail?billNo=${val.billNo}&type=${type.value}`,
});
}
}
// 数据:获取列表
const getList = (pageNo, pageSize) => {
queryParams.value.pageNum = pageNo
if (_.includes(type.value, 'stockIn')) {
stockList({ ...queryParams?.value, ...query?.value }).then(res => {
res.rows.forEach(e => {
e.showMore = false;
});
pagingRef.value.complete(res.rows)
}).catch(res => {
pagingRef.value.complete(false)
})
} else if (type.value == 'stockOut') {
}
}
</script>
<style scoped lang="scss">
.topSearch {
position: fixed;
top: 0;
left: 0;
right: 0;
z-index: 999;
padding: 4rpx;
background-color: #fff;
border-bottom: 1px solid #eee;
box-sizing: border-box;
}
.containerBox {
padding-top: 44px;
width: 100%;
height: 100%;
box-sizing: border-box;
.zp-l-text-rpx {
font-size: 12px;
}
}
::v-deep .listBox {
width: 100%;
height: 100%;
box-sizing: border-box;
background-color: #F8F8FF;
.zp-l-text-rpx {
font-size: 12px;
}
.uni-list {
background-color: #F8F8FF;
}
.uni-list-item {
margin-bottom: 4rpx;
}
.uni-list-item__container {
padding: 0;
}
.line {
display: flex;
// justify-content: space-between;
align-items: center;
// border-bottom: 1px solid #eee;
padding: 12rpx 20rpx;
}
.title {
display: flex;
justify-content: space-between;
font-weight: 600;
font-size: 13px;
}
.content {
font-weight: 500;
font-size: 13px;
}
.empty {
color: #D3D3D3;
}
span {
color: #696969;
}
}
</style>

View File

@@ -0,0 +1,144 @@
<!-- 获取仓库数据 -->
<template>
<view class="content">
<uv-form ref="formRef" class="form" :model="formData" label-width="150rpx">
<!-- 仓库 -->
<uv-form-item label="仓库" prop="warehouse">
<u-cell @click="onClick('warehouse')">
<view slot="title" class="u-slot-title">
<text class="u-cell-text" v-if="formData?.warehousing?.[0]?.deptName">
{{ formData?.warehousing?.[0].deptName }}
</text>
<text class="placeholder" v-else> 请选择仓库</text>
<uni-icons type="right" size="10" />
</view>
</u-cell>
</uv-form-item>
<!-- 存储区 -->
<uv-form-item label="存储区" prop="storageArea">
<u-cell @click="onClick('storageArea')">
<view slot="title" class="u-slot-title">
<text class="u-cell-text" v-if="formData?.storageArea?.[0]?.deptName">
{{ formData?.storageArea?.[0].deptName }}
</text>
<text class="placeholder" v-else> 请选择存储区</text>
<uni-icons type="right" size="10" />
</view>
</u-cell>
</uv-form-item>
<!-- 备注 -->
<uv-form-item label="备注">
<uv-input v-model="formData.remark" placeholder="请输入备注" align="right" border="none" />
</uv-form-item>
</uv-form>
</view>
</template>
<script setup>
import { ref, toRefs } from 'vue';
import { objectToQuery } from '../until';
import { onShow } from "@dcloudio/uni-app";
const props = defineProps({
// 数据:仓库信息
warehouseInfo: {
type: Object,
default: null,
required: false
},
// 数据:路径参数
pathParams: {
type: Object,
default: null,
required: false
}
})
const { warehouseInfo, pathParams } = toRefs(props)
// 表单数据
const formData = ref({ remark: '', warehousing: '', storageArea: '' })
// 赋值:更新列表值
const setFormData = (d) => {
if (!d) return;
formData.value = d
}
// 渲染:更新列表值
onShow(() => {
setFormData(warehouseInfo.value)
})
// 点击事件:选择仓库/选择存储区
const onClick = (type) => {
uni.setStorageSync('app_billRemark', formData.value.remark)
let query = { ...pathParams.value, backStr: 'stockIn' }
if (type == 'warehouse') {
query = objectToQuery({ ...query, flag: 'warehousing' })
} else if (type == 'storageArea') {
query = objectToQuery({ ...query, flag: 'storageArea' })
}
uni.navigateTo({
url: `/pages/warehousing/toChooseList${query}`,
});
}
//暴露:向父组件暴露 - 暂时没用
defineExpose({
getWarehousingInfo: () => {
return { ...formData.value }
}
})
</script>
<style lang="scss" scoped>
::v-deep .form {
// background-color: #fff;
padding: 0 0 12rpx 0;
.uv-input__content {
.uv-input__content__field-wrapper__field {
text-align: right !important;
}
}
.uv-form-item__body {
background-color: #fff;
padding: 12rpx;
margin-bottom: 2rpx;
align-items: center;
}
.uv-form-item__body__left__content__label {
font-size: 14px;
}
.uv-line {
border-bottom: 0 !important;
}
.uv-form-item__body__right__content__slot {
justify-content: flex-end;
align-items: center;
}
.uv-cell__body {
padding: 0
}
.uni-icons {
display: block;
}
.u-slot-title {
display: flex;
align-items: center;
}
}
.placeholder {
color: rgb(192, 196, 204);
margin-right: 10rpx;
}
</style>

View File

@@ -1,3 +1,4 @@
<!-- 底部导航栏首页 -->
<template> <template>
<view class="content"> <view class="content">
首页 首页

View File

@@ -1,3 +1,4 @@
<!-- 底部导航栏智能 -->
<template> <template>
<view class="content"> <view class="content">
智能 智能

View File

@@ -32,7 +32,8 @@
<script setup> <script setup>
import { ref } from "vue"; import { ref } from "vue";
import { onLoad } from "@dcloudio/uni-app"; import {removeStorage} from '../until';
import { userLogin, getCodeImg, getInfo } from "@/api/login" import { userLogin, getCodeImg, getInfo } from "@/api/login"
const formModel = ref({ const formModel = ref({
@@ -96,6 +97,7 @@ const submit = () => {
uni.setStorageSync('app_user', user.user) uni.setStorageSync('app_user', user.user)
uni.setStorageSync('app_roles', user.roles) uni.setStorageSync('app_roles', user.roles)
uni.setStorageSync('app_permissions', user.permissions) uni.setStorageSync('app_permissions', user.permissions)
removeStorage()
uni.switchTab({ uni.switchTab({
url: "/pages/index/index" url: "/pages/index/index"
}) })

View File

@@ -1,3 +1,4 @@
<!-- 底部导航栏我的 -->
<template> <template>
<view class="content"> <view class="content">
@@ -17,7 +18,7 @@
</uni-list> </uni-list>
</view> </view>
<uni-popup ref="popupRef" type="center" background-color="#fff" borderRadius="8px"> <uni-popup ref="popupRef" type="center" background-color="#fff" borderRadius="8px">
<view class="popup-wrap" style="width: 400rpx; "> <view class="popup-wrap" style="width: 200rpx; ">
<view class="popup-body"> <view class="popup-body">
<p>确定退出登录吗</p> <p>确定退出登录吗</p>
<view class="popup-btn"> <view class="popup-btn">
@@ -128,7 +129,7 @@ getUserInfo();
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
.uv-button-wrapper{ .uv-button-wrapper{
width: 48%; width: 40%;
margin-left: 8rpx; margin-left: 8rpx;
} }

View File

@@ -1,242 +0,0 @@
<!-- 唯一码发放页面 -->
<template>
<view class="content">
<view class="remarkLine">
<span>备注</span>
<uni-easyinput type="text" v-model="formData.remark" :inputBorder="false" placeholder="请输入备注" />
</view>
<!-- 物料列表 - 添加物料 -->
<view class="remarkLine">
<p>物料列表</p>
<uv-button type="primary" :plain="true" size="small" text="添加物料" @click="toMaterial"></uv-button>
</view>
<view>
<uni-forms ref="materialFormRef" :modelValue="formData.material" class="form">
<view v-for="(item, idx) in materialList" :key="item.id" class="material-card"
style="padding: 24rpx; margin-bottom: 16rpx;">
<view style="display: flex; flex-direction: column; width: 100%;">
<!-- 标题行 -->
<view class="title">{{ item.materialName }} ({{ item.materialCode }})</view>
<!--小标题行 简称/型号/规格/类型 -->
<view class="subTitle">
<span v-if="item.materialShortName">{{ item.materialShortName }} / </span>
<span v-if="item.model">{{ item.model }} / </span>
<span v-if="item.specification">{{ item.specification }} / </span>
<span v-if="item.typeName">{{ item.typeName }} </span>
</view>
<!-- 数量行 -->
<view class="tag">
<uni-tag :text="getTypeParentNames(item.typeParentNames)" type="error" />
<p class="tag-form">
<uni-forms-item :name="`material.${idx}.quantity`">
<uni-easyinput v-model="formData.material[idx].quantity" type="number"
:clearable="false" />
</uni-forms-item>
<span style=" margin-left: 8rpx; width: 16px;">{{ item.unitName }}</span>
</p>
</view>
<!-- 备注行 -->
<view class="remark">
<uni-forms-item :name="`material.${idx}.remark`" label="备注:">
<uni-easyinput type="text" v-model="formData.material[idx].remark" :clearable="false"
:inputBorder="false" placeholder="请填写备注信息" />
</uni-forms-item>
</view>
</view>
</view>
</uni-forms>
</view>
<button class="bottom-btn" type="primary" @click="handleSubmit">提交</button>
</view>
</template>
<script setup>
import { ref } from 'vue';
import { getTypeParentNames } from '../until';
import { addUniqueCode, editUniqueCode } from '@/api/uniqueCode'
import { onLoad, onShow } from '@dcloudio/uni-app';
const formData = ref({
remark: '',
material: []
})
// 物料列表
const materialList = ref([])
const materialFormRef = ref([])
// 路径参数
const pathParams = ref('')
// 选择物料列表
const toMaterial = () => {
uni.navigateTo({
url: '/pages/uniqueCode/issueUniqueCode/materialSelection'
});
}
// 接收路径参数 - 修改标题
onLoad((options) => {
pathParams.value = options
})
onShow(() => {
if (pathParams.value.code) {
uni.setNavigationBarTitle({ title: pathParams.value.code })
}
})
// 创建/编辑 唯一码
const handleSubmit = () => {
materialFormRef.value.validate().then(res => {
let params = {
...formData.value,
material: {
materialId: formData.value.material[0].id,
remark: formData.value.material[0].remark,
quantity: formData.value.material[0].quantity,
unitId: formData.value.material[0].unitId,
}
}
if (pathParams.value.id) {
params.id = pathParams.value.id;
params.material.id = pathParams.value.materialId;
params.material.materialId = formData.value.material[0].materialId;
editUniqueCode(params).then((response) => {
if (response.code == 200) {
uni.showToast({
title: '唯一码编辑成功',
mask: true,
icon: 'success',
})
uni.navigateTo({
url: `/pages/uniqueCode/myUniqueCode/detail?id=${pathParams.value.id}&code=${pathParams.value.code}`,
});
}
})
} else {
addUniqueCode(params).then((response) => {
if (response.code == 200) {
uni.showToast({
title: '唯一码创建成功',
mask: true,
icon: 'success',
})
uni.switchTab({
url: "/pages/warehousing/index"
})
}
})
}
})
}
// 获取缓存内的物料列表
const getMaterialList = () => {
uni.getStorage({
key: 'app_material',
success: ({ data }) => {
materialList.value = data
formData.value.material = data
formData.value.remark = data?.[0]?.uniqueCodeRemark
console.log(formData);
},
fail: (error) => { }
})
}
getMaterialList();
</script>
<style scoped lang="scss">
.remarkLine {
display: flex;
align-items: center;
background-color: #fff;
justify-content: space-between;
padding: 8rpx 24rpx;
margin-bottom: 8rpx;
min-height: 40rpx;
span {
font-size: 14px;
}
p {
font-weight: 700;
font-size: 14px;
}
}
::v-deep .uv-button {
height: 20px;
}
::v-deep .form {
.title {
color: #424242;
}
.tag {
.uni-tag {
display: flex;
align-items: center;
height: calc(25px - 6px);
}
.tag-form {
display: flex;
align-items: center;
}
.uni-forms-item {
margin-bottom: 0;
align-items: center;
.uni-easyinput__content,
.uni-easyinput__content-input {
height: 25px;
width: 80px;
}
}
}
.remark {
.uni-forms-item {
margin-bottom: 0;
margin-top: 16rpx;
display: flex;
align-items: center;
.uni-forms-item__label {
color: #212121;
height: 40rpx;
}
.uni-easyinput__content-input {
height: 40rpx;
.uni-input-wrapper {
text-align: right;
}
}
.uni-input-placeholder {
text-align: right;
font-size: 13px;
}
}
}
}
</style>

View File

@@ -1,121 +0,0 @@
<!-- 物料选择 -->
<template>
<view class="content">
<view class="topSearch">
<uni-easyinput type="text" v-model="queryParams.keyword" prefixIcon="search" :inputBorder="false"
@iconClick="getMaterialList" placeholder="请输入搜索内容" />
</view>
<!-- 物料列表 -->
<z-paging ref="pagingRef" class="containerBox" v-model="materialList" @query="getMaterialList">
<uni-list>
<uni-list-item v-for="item in materialList" :key="item.id" clickable class="material-card"
@click="onMaterial(item)">
<template v-slot:body>
<p style="display: flex; flex-direction: column; width: 100%;">
<p class="title">{{ item.materialName }} ({{ item.materialCode }})</p>
<p class="subTitle">
<!-- 简称/型号/规格/类型 -->
<span v-if="item.materialShortName">{{ item.materialShortName }} / </span>
<span v-if="item.model">{{ item.model }} / </span>
<span v-if="item.specification">{{ item.specification }} / </span>
<span v-if="item.typeName">{{ item.typeName }} </span>
</p>
<p class="tag">
<uni-tag :text="getTypeParentNames(item.typeParentNames)" type="error" />
<p>单位{{ item.unitName }}</p>
</p>
</p>
</template>
</uni-list-item>
</uni-list>
</z-paging>
</view>
</template>
<script setup>
import { ref } from 'vue';
import { getMaterial } from '@/api/uniqueCode'
import { onShow } from "@dcloudio/uni-app";
import { getTypeParentNames } from '../until';
const queryParams = ref({
keyword:''
})
const pagingRef = ref(null)
const materialList = ref([])
// 获取列表
const getMaterialList = () => {
getMaterial(queryParams.value).then(res => {
res.rows.forEach(e => {
e.showMore = false;
});
pagingRef.value.complete(res.rows)
}).catch(res => {
pagingRef.value.complete(false)
})
}
onShow(() => {
pagingRef.value?.reload?.()
})
// 搜索物料
const handleSearch = () => {
}
// 选择物料
const onMaterial = (val) => {
let materialChecked = uni.getStorageSync('app_material');
if (!Array.isArray(materialChecked)) {
materialChecked = [];
}
const idx = materialChecked?.findIndex((i) => i.id === val.id)
if (idx != -1) {
//! 现在仅允许添加一条物料打印一个唯一码 但保留可添加多个
// uni.showToast({
// title: '该物料已添加,请勿重复添加!',
// mask: true,
// icon: 'none',
// })
} else {
if (val) {
// materialChecked = [...materialChecked, { ...val, quantity: 1 }];
materialChecked = [{ ...val, quantity: 1 }]
uni.setStorageSync('app_material', materialChecked);
}
}
uni.navigateTo({
url: '/pages/uniqueCode/issueUniqueCode/index'
});
}
</script>
<style scoped lang="scss">
.topSearch {
position: fixed;
top: 0;
left: 0;
right: 0;
z-index: 999;
padding: 10px;
background-color: #fff;
border-bottom: 1px solid #eee;
box-sizing: border-box;
}
.containerBox {
// padding: 0 12rpx;
padding-top: 60px;
width: 100%;
height: 100%;
box-sizing: border-box;
.zp-l-text-rpx {
font-size: 12px;
}
}
</style>

View File

@@ -1,97 +0,0 @@
<template>
<view class="top">
<img style="width: 80rpx; height: 80rpx;" src="../../../static//logo/logo.jpg" />
<svg id="barcodeSvg" ref="barcodeRef"></svg>
</view>
<view class="content mt-16">
<p>物料
<span>{{ material?.materialName }}</span>
<span v-if="material?.quantity">x {{ material?.quantity }}</span>
</p>
<p>描述
<span v-if="material?.description">{{ material?.description || '-' }}</span>
<span v-else>
<span v-if="material?.materialShortName">{{ material?.materialShortName }} / </span>
<span v-if="material?.model">{{ material?.model }} / </span>
<span v-if="material?.specification">{{ material?.specification }} / </span>
<span v-if="material?.typeName">{{ material?.typeName }} </span>
</span>
</p>
<view class="top">
<p>备注
<!-- 简称/型号/规格/物料类型 -->
<span>{{ material?.qrCodeMark || '' }}</span>
</p>
<img :src="qrCode" class="qrcode" />
</view>
</view>
</template>
<script setup>
import { onMounted, computed } from 'vue';
import JsBarcode from 'jsbarcode'
const props = defineProps({
detail: {
type: Object,
default: null,
}
})
// 条形码容器
console.log(props.detail, 'props.detail');
// 二维码
const qrCode = computed(() => props.detail?.[0]?.qrCode);
// 物料数据
const material = computed(() => props.detail?.[0]);
// 条形码
const barcodeValue = computed(() => props.detail?.[0]?.code);
const generateBarcode = () => {
const svg = document.getElementById('barcodeSvg')
// SVG渲染直接调用JsBarcode无需清空会自动替换内容
JsBarcode(svg, barcodeValue.value, {
format: 'CODE128', // 常用的CODE128格式支持任意字符
width: 3,
height: 80,
displayValue: false,
margin: 10 // 条形码四周留白
})
}
onMounted(generateBarcode)
</script>
<style scoped lang="scss">
.qrcode {
width: 120rpx;
height: 120rpx;
}
.top {
display: flex;
align-items: center;
justify-content: space-between;
}
.content {
p {
font-weight: 600;
font-size: 14px;
}
span {
font-weight: 400;
font-size: 14px;
}
}
</style>

View File

@@ -1,292 +0,0 @@
<!-- 唯一码详情 -->
<template>
<uv-skeletons :loading="loading" :skeleton="skeleton">
<view class=" content">
<!-- 唯一码展示 -->
<uni-list>
<uni-list-item v-for="item in uniqueCodeList" :key="item.id" clickable class="material-card">
<template v-slot:body>
<p style="display: flex; flex-direction: column; width: 100%;">
<!-- 编码状态行 -->
<view class="flex-center">
<p class="title">编号{{ item.code }}</p>
<p class="status">{{ getStatusName(item.status) }}</p>
</view>
<!-- 内容行 -->
<view class="text ">
<p>RFID
<span v-if="item.rfidCode">{{ item.remark }}</span>
<span v-else style="color: #999;">未绑定RFID</span>
</p>
<p>生成时间
<span>{{ item?.createTime }}</span>
</p>
<!-- 备注行-->
<view class="flex-center qrCode">
<p>备注
<span v-if="item.remark">{{ item.remark }}</span>
<span v-else style="color: #999;">未填写</span>
</p>
<view @click="toViewQrCode">
<uv-image src="../../../static/qrcode.png" width="40rpx" height="40rpx"
style="margin-top:10rpx" />
</view>
</view>
</view>
</p>
</template>
</uni-list-item>
</uni-list>
<!-- 物料列表 -->
<view class="remarkLine">
<p>物料列表</p>
<p class="btn-link" @click="toEdit">修改</p>
</view>
<view>
<uni-forms ref="materialFormRef" :modelValue="materialList" class="form">
<view v-for="(item, idx) in materialList" :key="item.id" class="material-card"
style="padding: 24rpx; margin-bottom: 16rpx;">
<view style="display: flex; flex-direction: column; width: 100%;">
<!-- 标题行 -->
<view class="title">{{ item.materialName }} ({{ item.materialCode }})</view>
<!--小标题行 简称/型号/规格/类型 -->
<view class="subTitle">
<span v-if="item.materialShortName">{{ item.materialShortName }} / </span>
<span v-if="item.model">{{ item.model }} / </span>
<span v-if="item.specification">{{ item.specification }} / </span>
<span v-if="item.typeName">{{ item.typeName }} </span>
</view>
<!-- 数量行 -->
<view class="tag">
<uni-tag :text="getTypeParentNames(item.typeParentNames) || ''" type="error" />
<p class="tag-form">
<uni-forms-item :name="`material.${idx}.quantity`">
<uni-easyinput v-model="materialList[idx].quantity" :disabled="true"
type="number" :clearable="false" />
</uni-forms-item>
<span style=" margin-left: 8rpx; width: 16px;">{{ item.unitName }}</span>
</p>
</view>
<!-- 备注行 -->
<view class="remark">
<uni-forms-item :name="`material.${idx}.remark`" label="备注:">
<uni-easyinput type="text" v-model="materialList[idx].remark"
:disabled="true" :clearable="false" :inputBorder="false"
placeholder="请填写备注信息" />
</uni-forms-item>
</view>
</view>
</view>
</uni-forms>
</view>
<!-- 底部按钮 -->
<view class="bottom">
<uv-button type="primary" text="唯一码打印"></uv-button>
<uv-button type="success" text="溯源" @click="toTraceability"></uv-button>
</view>
</view>
</uv-skeletons>
<uv-modal ref="modalRef" :title="none" class="qrCodeModal" width="480rpx" :showConfirmButton="false">
<qr-code-modal :detail="materialList" />
</uv-modal>
</template>
<script setup>
import { ref } from 'vue';
import { onLoad, onShow } from '@dcloudio/uni-app';
import { detailUniqueCode } from '@/api/uniqueCode'
import { getTypeParentNames, getStatusName, getDetail } from '../until';
import QrCodeModal from './QrCodeModal.vue';
const modalRef = ref()
// 路径参数
const pathParams = ref('')
// 物料列表数组
const materialList = ref([])
// 唯一码数组
const uniqueCodeList = ref([])
// 详情数据
const detail = ref([])
// 骨架屏开关
const loading = ref(true)
// 骨架屏样式
const skeleton = [{
type: 'line',
num: 3,
gap: '20rpx',
style: ['width: 200rpx;marginBottom: 50rpx;marginTop:50rpx;padding:24rpx', 'height: 100rpx;', 'width: 500rpx;'],
}]
const toViewQrCode = () => {
modalRef.value.open()
}
// 详情:唯一码
const getDetailInfo = () => {
detailUniqueCode({ id: pathParams.value.id }).then((res) => {
if (res.code == 200) {
// ! 写成数组原因 为了适应可多个物料生成唯一码
materialList.value = [{ ...res.data.material, ...getDetail(res.data.material.wornMaterial), qrCode: res.data.qrCode, code: res.data.code, qrCodeMark: res.data.remark }]
uniqueCodeList.value = [res.data]
} else {
materialList.value = []
uniqueCodeList.value = []
uni.showToast({ title: '暂无数据', icon: 'none' })
}
loading.value = false
})
}
// 修改:唯一码的物料信息
const toEdit = () => {
uni.setStorageSync('app_material', [{ ...materialList.value?.[0], uniqueCodeRemark: uniqueCodeList.value?.[0].remark }]);
uni.navigateTo({
url: `/pages/uniqueCode/issueUniqueCode/index?code=${pathParams.value.code}&id=${pathParams.value.id}&materialId=${materialList.value?.[0].id}`,
});
}
// 溯源
const toTraceability = () => {
}
// 接收id 且修改标题
onLoad((options) => {
if (!options.id) {
loading.value = false
uni.showToast({ title: '参数错误,无唯一码ID', icon: 'none' })
return
}
pathParams.value = options
getDetailInfo()
})
onShow(() => {
uni.setNavigationBarTitle({ title: pathParams.value.code })
})
</script>
<style scoped lang="scss">
.remarkLine {
display: flex;
align-items: center;
background-color: #fff;
justify-content: space-between;
padding: 8rpx 24rpx;
margin-bottom: 4rpx;
min-height: 40rpx;
margin-top: 16rpx;
span {
font-size: 14px;
}
p {
font-weight: 700;
font-size: 14px;
}
.btn-link {
font-weight: 500;
color: #3c9cff;
}
}
::v-deep .form {
.title {
color: #424242;
}
.tag {
.uni-tag {
display: flex;
align-items: center;
height: calc(25px - 6px);
}
.tag-form {
display: flex;
align-items: center;
}
.uni-forms-item {
margin-bottom: 0;
align-items: center;
.uni-easyinput__content,
.uni-easyinput__content-input {
height: 25px;
width: 80px;
}
}
}
.remark {
.uni-forms-item {
margin-bottom: 0;
margin-top: 16rpx;
display: flex;
align-items: center;
.is-disabled {
background-color: #fff !important;
}
.uni-forms-item__label {
color: #212121;
height: 40rpx;
}
.uni-easyinput__content-input {
height: 40rpx;
.uni-input-wrapper {
text-align: right;
}
}
.uni-input-placeholder {
text-align: right;
font-size: 13px;
}
}
}
.uni-input-input {
color: #616161
}
}
// 底部按钮
.bottom {
position: fixed;
bottom: 0;
height: 60rpx;
font-size: 14px;
display: flex;
align-items: center;
justify-content: center;
width: 100%;
.uv-button-wrapper {
width: 50%;
border-radius: 0;
}
}
::v-deep.qrCodeModal {
.uv-modal__content {
display: block;
padding: 24rpx;
}
}
</style>

123
pages/until.js Normal file
View File

@@ -0,0 +1,123 @@
import dayjs from "dayjs";
import numeral from "numeral";
// 订单状态
// 0=入库申请,1=入库成功2=出库申请3=出库成功4=作废
const billTypeMenu = [
{
value: "0",
label: "入库单已提交",
},
{
value: "1",
label: "已完成",
},
{
value: "2",
label: "出库单已提交",
},
{
value: "3",
label: "已完成",
},
{
value: "4",
label: "已取消",
},
];
// 订单状态为0时 再查一次
const billStatueMenu = [
{
value: "0",
label: "入库单已提交",
},
{
value: "1",
label: "全部入库",
},
{
value: "2",
label: "物料部分入库",
},
];
// 物料状态
const materialType = [
{
value: "0",
label: "未提交",
},
{
value: "1",
label: "已提交",
},
];
// 入/出库单状态的label
export const getBillType = (val, status) => {
if (val === "0") {
return billStatueMenu.find((i) => i.value == `${status}`)?.label;
}
return billTypeMenu.find((i) => i.value == `${val}`)?.label;
};
// 获取出入库单的状态以及tag颜色
export const getColor = (val) => {
if (val == "0" || val == "2") {
return "color:#B8B8B8;font-weight:500;font-size:12px;";
} else if (val == "1" || val == "3") {
return "color:green;font-weight:500;font-size:12px;";
} else {
return "color:red;font-weight:500;font-size:12px;";
}
};
// 时间格式化
export const formatDate = (t, fmt = "YYYY-MM-DD HH:mm:ss") =>
t ? dayjs(t).format(fmt) : "-";
// 数字格式化
export const formatNum = (num, fmt = "0.00") =>
num ? numeral(num).format(fmt) : "-";
// 入库单/出库单:获取已入库、出库数量
// item:此列数据 type已入库、剩余数量
export const getStockQuantity = (item, type) => {
const num = item.quantity;
const status = item.status;
// 未入库
if (status == "0") {
if (type == "complete") {
return "0.000";
} else {
return formatNum(num, "0.00");
}
} else {
if (type == "complete") {
return formatNum(num, "0.000");
} else {
return "0.00";
}
}
};
// 清楚缓存 - 将所有需要清楚的缓存写入此处不要写登录信息
export const removeStorage = () => {
uni.removeStorageSync("app_warehousing");
uni.removeStorageSync("app_storageArea");
uni.removeStorageSync("app_billRemark");
uni.removeStorageSync("app_material");
uni.removeStorageSync("app_material_select_list");
};
// 对象转 URL 查询字符串 ?a=1&b=2
export const objectToQuery = (params) => {
if (!params || Object.keys(params).length === 0) return "";
const query = Object.entries(params)
.map(([key, value]) => `${key}=${encodeURIComponent(value)}`)
.join("&");
return "?" + query;
};

View File

@@ -0,0 +1,147 @@
<template>
<!-- 自定义导航栏 -->
<navigation :title="pathParams.billNo">
<template #right>
<view class="right-btn flex align-center justify-center ">
<uv-image @click="onPrinter" src="../../../../static/printer.png" width="20px" height="20px"
style="margin-top:10rpx" />
<uv-image @click="onEdit" src="../../../../static/edit.png" class="ml-24" width="20px" height="20px"
style="margin-top:10rpx" />
</view>
</template>
</Navigation>
<!-- 详情信息 -->
<detail-info type="stockIn" :detailInfo="detailInfo" />
<view v-if="detailInfo?.billType == '0'"
:class="getBillType(detailInfo?.billType, detailInfo?.status) != '物料部分入库' ? 'bottom' : 'bottom1'">
<uv-button type="error" v-if="getBillType(detailInfo?.billType, detailInfo?.status) != '物料部分入库'" text="作废"
@click="toObsolete"></uv-button>
<uv-button type="primary" text="入库单入库" @click="toPutAway"></uv-button>
</view>
</template>
<script setup>
import _ from 'lodash';
import { ref, computed } from 'vue';
import { onLoad } from "@dcloudio/uni-app";
import { objectToQuery, getBillType } from '../../../until';
import { stockInDetail, inboundBillVoid } from '../../../../api/stockIn';
import DetailInfo from '../../../components/DetailInfo.vue';
import Navigation from '../../../components/Navigation.vue';
// 数据:路径参数
const pathParams = ref('')
// 数据:详情
const detailInfo = ref('')
// 数据:类型 stockIn:入库 stockIn-putAway入库单入库 stockOut出库
const type = computed(() => pathParams.value.type)
// 方法:获取详情 - 将isDelete写入
const getDetailInfo = () => {
stockInDetail({ billNo: pathParams.value.billNo }).then((res) => {
detailInfo.value = res.data
})
}
// 打印
const onPrinter = () => {
}
const toObsolete = () => {
const params = {
billNo: pathParams.value?.billNo,
billType: '5'
}
inboundBillVoid(params).then((res) => {
// /pages/warehousing/stockIn/my
uni.navigateTo({
url: `/pages/warehousing/stockIn/my`
});
})
}
const setDetailInfoToStorage = (type) => {
uni.setStorageSync('app_warehousing', [{
deptName: detailInfo.value?.warehouseName,
deptCode: detailInfo.value?.warehouseCode,
}])
uni.setStorageSync('app_storageArea', [{
deptName: detailInfo.value?.areaName,
deptCode: detailInfo.value?.areaCode,
}])
if (type) {
const material = _.filter(detailInfo.value?.itemList, (i) => i?.uniqueCode && i.status == '0')
const materialSelectList = _.filter(detailInfo.value?.itemList, (i) => !i?.uniqueCode && i.status == '0')
uni.setStorageSync('app_material', material)
uni.setStorageSync('app_material_select_list', materialSelectList)
} else {
uni.setStorageSync('app_material', detailInfo.value?.itemList)
}
uni.setStorageSync('app_billRemark', detailInfo.value?.billRemark,)
}
// 按钮:入库单入库
const toPutAway = () => {
setDetailInfoToStorage('1')
uni.navigateTo({
url: `/pages/warehousing/stockIn/components/inbound?billNo=${pathParams.value.billNo}`
});
}
// 编辑
const onEdit = () => {
setDetailInfoToStorage()
const query = objectToQuery(pathParams.value)
let url = `/pages/warehousing/stockIn/create${query}`
uni.navigateTo({
url: url
});
}
// 接收路径参数billNo
onLoad((options) => {
if (!options.billNo) {
uni.showToast({ title: '参数错误,无唯一码ID', icon: 'none' })
return
}
pathParams.value = options
getDetailInfo()
})
</script>
<style scoped lang="scss">
/* 底部按钮 */
// 底部按钮
.bottom {
position: fixed;
bottom: 0;
height: 60rpx;
font-size: 14px;
display: flex;
align-items: center;
justify-content: center;
width: 100%;
.uv-button-wrapper {
width: 50%;
border-radius: 0;
}
}
.bottom1 {
position: fixed;
bottom: 0;
height: 60rpx;
font-size: 14px;
display: flex;
align-items: center;
// justify-content: center;
width: 100%;
.uv-button-wrapper {
width: 100%;
border-radius: 0;
}
}
</style>

View File

@@ -0,0 +1,158 @@
<!--点击入库单入库后的入库页面 -->
<template>
<view class="page">
<!-- 仓库信息 - 仓库存储区 -->
<warehousing-info ref="warehousingInfoRef" :warehouseInfo="warehouseInfo" :pathParams="pathParams" />
<!-- 物料列表 - 添加物料 -->
<material-list ref="materialRef" :formData="formData" isEdit="5" backStr="stockIn" :pathParams="pathParams"
:extendData="{ selectMaterialList }" />
<!-- 底部操作栏 -->
<view class="bottom">
<uv-button type="primary" @click="submitForm">入库</uv-button>
</view>
</view>
</template>
<script setup>
import _ from 'lodash';
import { ref } from 'vue';
import { onLoad, onShow } from "@dcloudio/uni-app";
import { inboundFinish, stockInDetail,inboundBillVoid } from '@/api/stockIn';
import MaterialList from '../../../components/MaterialList.vue';
import WarehousingInfo from '../../../components/WarehousingInfo.vue';
import { onMounted } from 'vue';
// 数据:路径参数
const pathParams = ref('')
// 标志:是否为编辑
const isEdit = ref('')
// 标志:区分页面 进入页面的状态 null新建 stockIn:入库编辑 stockIn-putAway入库单入库编辑
const flag = ref('')
// ref物料绑定
const materialRef = ref([])
// ref仓库信息绑定
const warehousingInfoRef = ref([])
const selectMaterialList = ref([])
const detailInfo = ref('')
// 数据:物料列表
const formData = ref([{ remark: '', material: [] }])
// 数据:仓库信息
const warehouseInfo = ref({ warehousing: {}, storageArea: {}, remark: '' })
// 数据:获取缓存信息
const getMaterialList = () => {
// 获取仓库信息
const warehouse = uni.getStorageSync('app_warehousing');
warehouseInfo.value.warehousing = warehouse
// 获取存储区信息
const storageArea = uni.getStorageSync('app_storageArea');
warehouseInfo.value.storageArea = storageArea
// 获取物料信息 - 有uniqueCode
const material = uni.getStorageSync('app_material');
formData.value.material = material
// 获取物料信息 - 无uniqueCode
const selectMaterial = uni.getStorageSync('app_material_select_list');
selectMaterialList.value = selectMaterial
// 获取入库单备注
const billRemark = uni.getStorageSync('app_billRemark');
warehouseInfo.value.remark = billRemark
console.log('获取缓存', material, selectMaterial);
}
// 方法:获取详情 - 更新物料得值 获取唯一码后回来
const getDetailInfo = () => {
stockInDetail({ billNo: pathParams.value.billNo }).then((res) => {
setDetailInfoToStorage(res.data?.itemList)
})
}
// 方法:存入缓存 - 更新物料得值 获取唯一码后回来
const setDetailInfoToStorage = (val) => {
// 过滤出有uniqueCode得值
const material = _.filter(val, (i) => i?.uniqueCode && i.status == '0')
const materialSelectList = _.filter(val, (i) => !i?.uniqueCode && i.status == '0')
uni.setStorageSync('app_material', material)
uni.setStorageSync('app_material_select_list', materialSelectList)
getMaterialList()
}
// 入库:提交表单
const submitForm = () => {
const info = warehousingInfoRef.value.getWarehousingInfo()
const materialInfo = materialRef.value.getMaterialList()
// 过滤出已删除的物料
const _materialInfo = _.filter(materialInfo, { isDelete: '0' })
let params = {
billType: '1',//0入库申请 1入库成功 2出库申请 3出库成功 4入库单已作废 5出库单已作废
billNo: pathParams.value.billNo,
warehouseCode: info.warehousing?.[0].deptCode || info.warehousing?.[0].deptId,
warehouseName: info.warehousing?.[0].deptName,
areaCode: info.storageArea?.[0].deptCode || info.storageArea?.[0].deptId,
areaName: info.storageArea?.[0].deptName,
remark: info.remark,
billRemark: info.remark,
itemList: _.map(_materialInfo, (i) => ({ ...i.material }))
}
inboundFinish(params).then((res) => {
// stockIn
// /pages/warehousing/stockIn/components/detail
uni.navigateTo({
url: `/pages/warehousing/stockIn/components/detail?billNo=${pathParams.value.billNo}&type=stockIn`
});
console.log(res, '入库单入库');
})
}
// 数据:路径参数
onLoad((options) => {
console.log(options, 'options', _.includes(options?.type, 'stockIn'));
if (!options.billNo) {
uni.showToast({ title: '参数错误,无唯一码ID', icon: 'none' })
return
}
pathParams.value = options
getDetailInfo()
flag.value = options.type
isEdit.value = _.includes(options?.type, 'stockIn')
})
// 修改标题若有billNo传入 修改标题
onShow(() => {
if (pathParams.value.billNo) {
uni.setNavigationBarTitle({ title: pathParams.value.billNo })
}
})
onMounted(() => {
getMaterialList()
})
</script>
<style lang="scss" scoped>
.page {
background: #f5f5f5;
min-height: 100vh;
}
/* 底部按钮 */
::v-deep .bottom {
position: fixed;
bottom: 0;
height: 60rpx;
font-size: 14px;
display: flex;
align-items: center;
justify-content: center;
width: 100%;
.uv-button-wrapper {
width: 100%;
border-radius: 0;
}
}
</style>

View File

@@ -0,0 +1,206 @@
<template>
<view class="page">
<!-- 仓库信息 - 仓库存储区 -->
<warehousing-info ref="warehousingInfoRef" :warehouseInfo="warehouseInfo" :pathParams="pathParams" />
<!-- 物料列表 - 添加物料 -->
<material-list ref="materialRef" :formData="formData" isEdit="1" backStr="stockIn" :pathParams="pathParams" />
<!-- 底部操作栏 -->
<view class="bottom">
<uv-button @click="scanCode">扫码添加</uv-button>
<uv-button type="primary" @click="submitForm">提交</uv-button>
</view>
</view>
</template>
<script setup>
import _ from 'lodash';
import { ref } from 'vue';
import { objectToQuery } from '../../until';
import { onLoad, onShow } from "@dcloudio/uni-app";
import { addStockIn, stockInUpdate } from '@/api/stockIn';
import { getMaterialUnique } from '@/api/uniqueCode';
import MaterialList from '../../components/MaterialList.vue';
import WarehousingInfo from '../../components/WarehousingInfo.vue';
// 数据:路径参数
const pathParams = ref('')
// 标志:是否为编辑
const isEdit = ref('')
// 标志:区分页面 进入页面的状态 null新建 stockIn:入库编辑 stockIn-putAway入库单入库编辑
const flag = ref('')
// ref物料绑定
const materialRef = ref([])
// ref仓库信息绑定
const warehousingInfoRef = ref([])
// 数据:物料列表
const formData = ref([{ remark: '', material: [] }])
// 数据:仓库信息
const warehouseInfo = ref({ warehousing: {}, storageArea: {}, remark: '' })
// 数据:获取缓存信息
const getMaterialList = () => {
// 获取仓库信息
const warehouse = uni.getStorageSync('app_warehousing');
warehouseInfo.value.warehousing = warehouse
// 获取存储区信息
const storageArea = uni.getStorageSync('app_storageArea');
warehouseInfo.value.storageArea = storageArea
// 获取物料信息
const material = uni.getStorageSync('app_material');
formData.value.material = material
// 获取入库单备注
const billRemark = uni.getStorageSync('app_billRemark');
warehouseInfo.value.remark = billRemark
}
getMaterialList();
const scanResult = ref('')
// 扫码添加
const scanCode = () => {
// 先校验是否支持扫码
uni.scanCode({
scanType: ['qrCode', 'barCode'], // 支持二维码 + 条形码
success: (res) => {
scanResult.value = res.result;
if (res.result) {
getMaterialUnique({ code: res.result }).then((response) => {
console.log('物料内容:', response);
if (`${response.code}` === '200') {
const material = uni.getStorageSync('app_material');
// 处理物料信息 合并
const materialInfo = _.filter(material, { isDelete: '0' });
console.log('materialInfo', materialInfo);
const materialId = response.data?.[0]?.materialId;
console.log('materialId', materialId);
const idx = _.findIndex(materialInfo, (i) => i.materialId ? i.materialId == materialId : i.id == materialId);
console.log('已选中是否有重复数据', idx);
if (idx != -1) {
uni.showToast({
title: '该物料已添加,请勿重复添加!',
mask: true,
icon: 'none',
})
} else {
let _material = [...materialInfo, { ...response.data[0],uniqueCode:res.result }]
console.log('新的物料数组', _material);
formData.value.material = _material
uni.setStorageSync('app_material', formData.value.material)
}
} else {
console.log(response.code, 'response.code');
}
})
}
},
fail: () => {
uni.showToast({ title: '扫码失败', icon: 'none' });
}
});
};
// 提交表单
const submitForm = () => {
const info = warehousingInfoRef.value.getWarehousingInfo()
const materialInfo = materialRef.value.getMaterialList()
let params = {
warehouseCode: info.warehousing?.[0].deptCode || info.warehousing?.[0].deptId,
warehouseName: info.warehousing?.[0].deptName,
areaCode: info.storageArea?.[0].deptCode || info.storageArea?.[0].deptId,
areaName: info.storageArea?.[0].deptName,
remark: info.remark,
billRemark: info.remark,
itemList: _.map(materialInfo, (i) => ({ ...i.material }))
}
console.log(params, isEdit, !isEdit, info, materialInfo, '入参params');
// 新建
if (!isEdit.value) {
addStockIn(params).then((res) => {
if (res.code == 200) {
uni.showToast({
title: '入库单创建成功',
mask: true,
icon: 'success',
})
uni.navigateTo({
url: `/pages/warehousing/stockIn/my`,
});
}
})
} else if (isEdit.value) {
// 编辑
params.billNo = pathParams.value?.billNo
params.billId = pathParams.value?.billId
const query = objectToQuery(pathParams.value)
stockInUpdate(params).then((res) => {
if (res.code == 200) {
uni.showToast({
title: '入库单编辑成功',
mask: true,
icon: 'success',
})
let url = `/pages/warehousing/stockIn/components/detail${query}`
uni.navigateTo({
url: url,
});
}
})
}
}
// 数据:路径参数
onLoad((options) => {
console.log(options, 'options', _.includes(options?.type, 'stockIn'));
pathParams.value = options
flag.value = options.type
isEdit.value = _.includes(options?.type, 'stockIn')
})
// 修改标题若有billNo传入 修改标题
onShow(() => {
if (pathParams.value.billNo) {
uni.setNavigationBarTitle({ title: pathParams.value.billNo })
}
})
</script>
<style lang="scss" scoped>
.page {
background: #f5f5f5;
min-height: 100vh;
}
/* 底部按钮 */
::v-deep .bottom {
position: fixed;
bottom: 0;
height: 60rpx;
font-size: 14px;
display: flex;
align-items: center;
justify-content: center;
width: 100%;
.uv-button-wrapper {
width: 50%;
border-radius: 0;
}
.uv-button--info {
background-color: #07c160;
color: #fff;
}
}
</style>

View File

@@ -0,0 +1,9 @@
<template>
<search-list type="stockIn" :onDetail="onDetail" />
</template>
<script setup>
import SearchList from '../../components/SearchList.vue';
</script>

View File

@@ -0,0 +1,10 @@
<!-- putAway入库单入库 -->
<template>
<search-list type="stockIn-putAway" :onDetail="onDetail" :query="{ billType: '0' }" />
</template>
<script setup>
import SearchList from '../../components/SearchList.vue';
</script>

View File

@@ -1,3 +1,4 @@
<!-- 底部导航栏仓库 -->
<template> <template>
<view class="content"> <view class="content">
<uni-card v-for="(item, index) in menuList" :key="item.id" :title="item.title" :isFull="true" <uni-card v-for="(item, index) in menuList" :key="item.id" :title="item.title" :isFull="true"
@@ -12,7 +13,7 @@
<script setup> <script setup>
import { onShow } from '@dcloudio/uni-app'; import { onShow } from '@dcloudio/uni-app';
import {removeStorage} from '../until';
const menuList = [ const menuList = [
{ {
id: 'inventory', id: 'inventory',
@@ -125,7 +126,11 @@ const menuList = [
icon: '../../static/stockInOrOut/stockInCreateOrder.png', icon: '../../static/stockInOrOut/stockInCreateOrder.png',
title: '入库单开单', title: '入库单开单',
click: () => { click: () => {
removeStorage()
console.log('入库单开单') console.log('入库单开单')
uni.navigateTo({
url: '/pages/warehousing/stockIn/create'
});
} }
}, },
{ {
@@ -134,6 +139,10 @@ const menuList = [
title: '我的入库单', title: '我的入库单',
click: () => { click: () => {
console.log('我的入库单') console.log('我的入库单')
removeStorage()
uni.navigateTo({
url: '/pages/warehousing/stockIn/my'
});
} }
}, },
{ {
@@ -142,6 +151,9 @@ const menuList = [
title: '入库单入库', title: '入库单入库',
click: () => { click: () => {
console.log('入库单入库') console.log('入库单入库')
uni.navigateTo({
url: '/pages/warehousing/stockIn/putaway'
});
} }
}, },
@@ -188,9 +200,9 @@ const menuList = [
title: '唯一码发放', title: '唯一码发放',
click: () => { click: () => {
console.log('唯一码发放') console.log('唯一码发放')
uni.setStorageSync('app_material', []); uni.removeStorage('app_material')
uni.navigateTo({ uni.navigateTo({
url: '/pages/uniqueCode/issueUniqueCode/index' url: '/pages/warehousing/uniqueCode/issueUniqueCode/index'
}); });
} }
}, },
@@ -201,7 +213,7 @@ const menuList = [
click: () => { click: () => {
console.log('唯一码') console.log('唯一码')
uni.navigateTo({ uni.navigateTo({
url: '/pages/uniqueCode/myUniqueCode/index' url: '/pages/warehousing/uniqueCode/myUniqueCode/index'
}); });
} }
}, },
@@ -216,40 +228,39 @@ const menuList = [
}, },
] ]
}, },
{ // {
id: 'transportCheckIn', // id: 'transportCheckIn',
title: '运输打卡', // title: '运输打卡',
menuItem: [ // menuItem: [
{ // {
id: "transportCheckIn_transportCheckIn", // id: "transportCheckIn_transportCheckIn",
icon: '../../static/transportCheckIn/transportCheckIn.png', // icon: '../../static/transportCheckIn/transportCheckIn.png',
title: '运输打卡', // title: '运输打卡',
click: () => { // click: () => {
console.log('运输打卡') // console.log('运输打卡')
} // }
}, // },
{ // {
id: "transportCheckIn_myTransportCheckIn", // id: "transportCheckIn_myTransportCheckIn",
icon: '../../static/transportCheckIn/myTransportCheckIn.png', // icon: '../../static/transportCheckIn/myTransportCheckIn.png',
title: '我的运输打卡', // title: '我的运输打卡',
click: () => { // click: () => {
console.log('我的运输打卡') // console.log('我的运输打卡')
} // }
}, // },
] // ]
}, // },
] ]
onShow(() => { onShow(() => {
uni.removeStorage({ uni.removeStorage({
key: 'app_material', key: 'app_material',
success: (result) => {}, success: (result) => { },
fail: (error) => {} fail: (error) => { }
}) })
}) })
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
/* 覆盖 uni-card 根容器样式 */
::v-deep .custom-card.uni-card { ::v-deep .custom-card.uni-card {
margin-bottom: 6rpx !important; margin-bottom: 6rpx !important;
border-color: #F8F8FF !important; border-color: #F8F8FF !important;

View File

@@ -0,0 +1,18 @@
<!-- 仓库信息选择 -->
<template>
<choose-list :isWarehousing="isWarehousing" :backStr="pathParams.backStr" :pathParams="pathParams" />
</template>
<script setup>
import { computed, ref } from 'vue'
import { onLoad } from "@dcloudio/uni-app";
import ChooseList from '../components/ChooseList.vue'
// 路径参数
const pathParams = ref('')
const isWarehousing = computed(() => pathParams.value.flag == 'warehousing')
// 接收路径参数
onLoad((options) => {
pathParams.value = options
})
</script>

View File

@@ -0,0 +1,205 @@
<!-- 唯一码发放页面 -->
<template>
<view class="content">
<view class="remarkLine">
<span>备注</span>
<uni-easyinput type="text" v-model="formData.remark" :inputBorder="false" placeholder="请输入备注" />
</view>
<!-- 物料列表 - 添加物料 -->
<material-list ref="materialRef" :formData="formData" :remark="formData.remark" isEdit="1" :backStr="backStr"
:pathParams="pathParams" />
<button class="bottom-btn" type="primary" @click="handleSubmit">提交</button>
</view>
</template>
<script setup>
import { ref, onMounted } from 'vue';
import { onLoad, onShow } from '@dcloudio/uni-app';
import { addUniqueCode, editUniqueCode } from '@/api/uniqueCode';
import MaterialList from '../../../components/MaterialList.vue';
import { computed } from 'vue';
import _ from 'lodash';
// 数据:路径参数
const pathParams = ref('')
// 数据:物料、备注
const formData = ref({ remark: '', material: [] })
// ref:绑定物料列表
const materialRef = ref([])
const backStr = computed(() => pathParams.value.back == 'inbound' ? `issueUniqueCode_inbound` : 'issueUniqueCode')
// 接收路径参数 - 修改标题- 携带code、id、materialId
onLoad((options) => {
pathParams.value = options
})
onShow(() => {
if (pathParams.value.title) {
uni.setNavigationBarTitle({ title: pathParams.value.title })
}
if (pathParams.value.back == 'inbound') {
uni.setNavigationBarTitle({ title: '快速生成唯一码' })
}
})
onMounted(() => {
// 快速生成唯一码时 已选择的物料列表初始为空 点击添加物料 去选择
if (pathParams.value.back != 'inbound') {
getMaterialList();
}
})
// 创建/编辑 唯一码
const handleSubmit = () => {
// 物料最新编辑数据
const material = materialRef.value.getMaterialList()?.[0]
const params = {
remark: formData.value.remark,
material: { ...material.material, unitId: '23' }
}
if (pathParams.value.back == `issueUniqueCode_inbound`) {
params.billNo = pathParams.value.billNo
}
if (pathParams.value.id) {
params.id = pathParams.value.id; //唯一码id
params.material.id = formData.value.material?.[0]?.uniqueCodeMaterialId; //唯一码物料id
params.material.materialId = formData.value.material?.[0]?.materialId; //物料id
editUniqueCode(params).then((response) => {
if (response.code == 200) {
uni.showToast({
title: '唯一码编辑成功',
mask: true,
icon: 'success',
})
uni.navigateTo({
url: `/pages/warehousing/uniqueCode/myUniqueCode/detail?id=${pathParams.value.id}&title=${pathParams.value.title}`,
});
}
})
} else {
addUniqueCode(params).then((response) => {
if (response.code == 200) {
uni.showToast({
title: '唯一码创建成功',
mask: true,
icon: 'success',
})
if (_.includes(pathParams.value?.back, 'inbound')) {
// /pages/warehousing/uniqueCode/myUniqueCode/detail?id=38&code=600038
uni.navigateTo({
url: `/pages/warehousing/stockIn/components/inbound?billNo=${pathParams.value.billNo}`,
});
} else {
uni.switchTab({
url: "/pages/warehousing/index"
})
}
// /pages/warehousing/stockIn/components/inbound?billNo=WR1775032495153
}
})
}
}
// 获取缓存内的物料列表
const getMaterialList = () => {
const value = uni.getStorageSync('app_material');
if (value) {
formData.value.material = value
if (value?.[0]?.uniqueCodeRemark) {
formData.value.remark = value?.[0]?.uniqueCodeRemark
}
}
}
</script>
<style scoped lang="scss">
.remarkLine {
display: flex;
align-items: center;
background-color: #fff;
justify-content: space-between;
padding: 8rpx 24rpx;
margin-bottom: 8rpx;
min-height: 40rpx;
span {
font-size: 14px;
}
p {
font-weight: 700;
font-size: 14px;
}
}
::v-deep .uv-button {
height: 20px;
}
::v-deep .form {
.title {
color: #424242;
}
.tag {
.uni-tag {
display: flex;
align-items: center;
height: calc(25px - 6px);
}
.tag-form {
display: flex;
align-items: center;
}
.uni-forms-item {
margin-bottom: 0;
align-items: center;
.uni-easyinput__content,
.uni-easyinput__content-input {
height: 25px;
width: 80px;
}
}
}
.remark {
.uni-forms-item {
margin-bottom: 0;
margin-top: 16rpx;
display: flex;
align-items: center;
.uni-forms-item__label {
color: #212121;
height: 40rpx;
}
.uni-easyinput__content-input {
height: 40rpx;
.uni-input-wrapper {
text-align: right;
}
}
.uni-input-placeholder {
text-align: right;
font-size: 13px;
}
}
}
}
</style>

View File

@@ -0,0 +1,218 @@
<!-- 物料选择 -->
<template>
<view class="content">
<view class="topSearch" v-if="!backUrl.includes('inbound')">
<uni-easyinput type="text" v-model="queryParams.keyword" prefixIcon="search" :inputBorder="false"
@iconClick="getMaterialList" placeholder="请输入搜索内容" />
</view>
<!-- 物料列表 -->
<z-paging ref="pagingRef" class="containerBox" v-model="materialList" @query="getMaterialList"
v-if="!backUrl.includes('inbound')">
<uni-list>
<uni-list-item v-for="item in materialList" :key="item.id" clickable class="material-card"
@click="onMaterial(item)">
<template v-slot:body>
<p style="display: flex; flex-direction: column; width: 100%;">
<p class="title">{{ item.materialName }} ({{ item.materialCode }})</p>
<p class="subTitle">
<!-- 简称/型号/规格/类型 -->
<span v-if="item.materialShortName">{{ item.materialShortName }} / </span>
<span v-if="item.model">{{ item.model }} / </span>
<span v-if="item.specification">{{ item.specification }} / </span>
<span v-if="item.typeName">{{ item.typeName }} </span>
</p>
<p class="tag">
<uni-tag :text="getTypeParentNames(item.typeParentNames)" type="error" />
<p>单位{{ item.unitName }}</p>
</p>
</p>
</template>
</uni-list-item>
</uni-list>
</z-paging>
<view v-if="backUrl.includes('inbound')">
<uni-list>
<uni-list-item v-for="item in materialList" :key="item.id" clickable class="material-card"
@click="onMaterial(item)">
<template v-slot:body>
<p style="display: flex; flex-direction: column; width: 100%;">
<p class="title">{{ item.materialName }} ({{ item.materialCode }})</p>
<p class="subTitle">
<!-- 简称/型号/规格/类型 -->
<span v-if="item.materialShortName">{{ item.materialShortName }} / </span>
<span v-if="item.model">{{ item.model }} / </span>
<span v-if="item.specification">{{ item.specification }} / </span>
<span v-if="item.typeName">{{ item.typeName }} </span>
</p>
<p class="tag">
<uni-tag :text="getTypeParentNames(item.typeParentNames)" type="error" />
<p>单位{{ item.unitName }}</p>
</p>
</p>
</template>
</uni-list-item>
</uni-list>
</view>
</view>
</template>
<script setup>
import _ from 'lodash';
import { computed, ref } from 'vue';
import { getTypeParentNames, } from '../until';
import { objectToQuery } from '../../../until';
import { getMaterial } from '@/api/uniqueCode'
import { onShow, onLoad } from "@dcloudio/uni-app";
// ref:下拉加载
const pagingRef = ref(null)
// 数据:查询参数
const queryParams = ref({
keyword: ''
})
// 数据:物料
const materialList = ref([])
// 数据:路径参数
const pathParams = ref('')
// 数据:返回路径标识 stockIn 入库单开单 stockInDetail入库单编辑 issueUniqueCode唯一码 issueUniqueCode_inbound入库生成唯一码
const backUrl = computed(() => pathParams?.value?.back)
// 方法:获取列表
const getMaterialList = () => {
getMaterial(queryParams.value).then(res => {
res.rows.forEach(e => {
e.showMore = false;
});
pagingRef.value.complete(res.rows)
}).catch(res => {
pagingRef.value.complete(false)
})
}
// 方法:获取列表 - 入库生成唯一码
const getMaterialListInbound = () => {
const list = uni.getStorageSync('app_material_select_list')
console.log('list',list);
materialList.value=list
}
onShow(() => {
pagingRef.value?.reload?.()
})
onLoad((options) => {
pathParams.value = options
getMaterialListInbound()
})
// 方法:获取缓存物料信息
const getStorageMaterial = (val) => {
// 唯一码编辑进入 数量默认1\唯一码物料表id\物料id
if (pathParams.value.id) {
const value = uni.getStorageSync('app_material');
if (value && value?.length > 0) {
return [{ ...val, quantity: 1, uniqueCodeMaterialId: value?.[0]?.id, uniqueCodeRemark: value?.[0]?.uniqueCodeRemark, materialId: val.id, isDelete: '0' }]
}
}
return [{ ...val, quantity: 1 }]
}
// 方法:返回路径
const toBack = (back) => {
const path = objectToQuery({ ...pathParams.value })
let url = ''
switch (back) {
case 'stockIn':
// 入库单开单添加物料进入
url = `/pages/warehousing/stockIn/create`
break
case 'issueUniqueCode':
case 'issueUniqueCode_inbound':
// 唯一码发放/入库生成唯一码
url = `/pages/warehousing/uniqueCode/issueUniqueCode/index`
break
case 'stockInDetail':
//我的入库单/入库单入库
url = `/pages/warehousing/stockIn/components/detail`
break
}
uni.navigateTo({
url: `${url}${path}`
})
}
// 方法:选择物料
const onMaterial = (val) => {
let materialChecked = uni.getStorageSync('app_material');
if (!Array.isArray(materialChecked)) {
materialChecked = [];
}
console.log(materialChecked, 'materialChecked==>');
const item = _.find(materialChecked, (i) => i.materialId ? i.materialId === val.id : i.id === val.id)
console.log(item, 'item==>');
const idx = _.findIndex(materialChecked, (i) => i.materialId ? i.materialId === val.id : i.id === val.id)
console.log(idx, 'idx==>', idx != -1 && item?.isDelete == '0', backUrl.value);
if (idx != -1 && item?.isDelete == '0') { // isDelete为0表示未删除 1为已删除数据不在对比范围内
console.log(backUrl.value == 'stockIn', '11111111111111111111111111');
if (backUrl.value == 'stockIn') {
console.log(111111111111);
uni.showToast({
title: '该物料已添加,请勿重复添加!',
mask: true,
icon: 'none',
})
}
//! 现在仅允许添加一条物料打印一个唯一码 但保留可添加多个
// uni.showToast({
// title: '该物料已添加,请勿重复添加!',
// mask: true,
// icon: 'none',
// })
toBack(backUrl.value)
} else {
console.log('执行到这里');
if (val) {
if (backUrl.value == 'stockIn') {
materialChecked = [...materialChecked, { ...val, quantity: 1, isDelete: '0' }];
} else {
materialChecked = getStorageMaterial(val)
}
uni.setStorageSync('app_material', materialChecked);
}
toBack(backUrl.value)
}
}
</script>
<style scoped lang="scss">
.topSearch {
position: fixed;
top: 0;
left: 0;
right: 0;
z-index: 999;
padding: 10px;
background-color: #fff;
border-bottom: 1px solid #eee;
box-sizing: border-box;
}
.containerBox {
// padding: 0 12rpx;
padding-top: 60px;
width: 100%;
height: 100%;
box-sizing: border-box;
.zp-l-text-rpx {
font-size: 12px;
}
}
</style>

View File

@@ -0,0 +1,112 @@
<template>
<view style="display: block;">
<view class="top">
<uv-image src="../../../../static//logo/logo.jpg" width="40px" height="40px" style="margin-top:10rpx" />
<!-- 唯一兼容 iPad 的写法 2D Canvas -->
<canvas id="barcode" type="2d" class="barcode-canvas"></canvas>
</view>
<view class="content mt-16">
<p>物料
<span>{{ material?.materialName }}</span>
<span v-if="material?.quantity">x {{ material?.quantity }}</span>
</p>
<p>描述
<span v-if="material?.description">{{ material?.description || '-' }}</span>
<span v-else>
<span v-if="material?.materialShortName">{{ material?.materialShortName }} / </span>
<span v-if="material?.model">{{ material?.model }} / </span>
<span v-if="material?.specification">{{ material?.specification }} / </span>
<span v-if="material?.typeName">{{ material?.typeName }} </span>
</span>
</p>
<view class="top">
<p>备注
<span>{{ material?.uniqueCodeRemark || '' }}</span>
</p>
<uv-image :src="qrCode" width="80rpx" height="80rpx" />
</view>
</view>
</view>
</template>
<script setup>
import { onMounted, computed, watch } from 'vue';
import JsBarcode from 'jsbarcode';
const props = defineProps({
detail: { type: Object, default: null }
});
const qrCode = computed(() => props.detail?.material?.[0]?.qrCode);
const material = computed(() => props.detail?.material?.[0]);
const barcodeValue = computed(() => props.detail?.material?.[0]?.code || '');
// iPad 兼容核心:不用 document只用 uni 官方 API
const drawBarcode = () => {
if (!barcodeValue.value) return;
const query = uni.createSelectorQuery();
query.select('#barcode')
.fields({ node: true, size: true })
.exec((res) => {
const canvas = res[0]?.node;
if (!canvas) return;
const ctx = canvas.getContext('2d');
ctx.clearRect(0, 0, canvas.width, canvas.height);
// 渲染条形码
JsBarcode(canvas, barcodeValue.value, {
format: 'CODE128',
width: 2,
height: 40,
displayValue: false,
margin: 10,
});
});
};
// 延迟执行,保证 iPad 能加载
onMounted(() => {
setTimeout(drawBarcode, 400);
});
// 数据变化重新绘制
watch(
() => props.detail,
() => setTimeout(drawBarcode, 400),
{ deep: true }
);
</script>
<style scoped lang="scss">
.top {
display: flex;
align-items: center;
justify-content: space-between;
}
.content {
// display: flex;
p {
font-weight: 600;
font-size: 14px;
}
span {
font-weight: 400;
font-size: 14px;
}
}
/* 必须给宽度高度iPad 才会显示 */
.barcode-canvas {
width: 40%;
height: 20px;
}
</style>

View File

@@ -0,0 +1,184 @@
<!-- 唯一码详情 -->
<template>
<!-- 自定义导航栏 -->
<navigation :title="pathParams.code ? pathParams.code : '唯一码发放'">
<template #right>
<view class="right-btn flex align-center justify-center ">
<uv-image @click="toEdit" src="../../../../static/edit.png" class="ml-24" width="20px" height="20px"
style="margin-top:10rpx" />
</view>
</template>
</Navigation>
<uv-skeletons :loading="loading" :skeleton="skeleton">
<view class=" content">
<!-- 唯一码展示 -->
<uni-list>
<uni-list-item v-for="item in uniqueCodeList" :key="item.id" clickable class="material-card">
<template v-slot:body>
<p style="display: flex; flex-direction: column; width: 100%;">
<!-- 编码状态行 -->
<view class="flex-center">
<p class="title">编号{{ item.code }}</p>
<p class="status">{{ getStatusName(item.status) }}</p>
</view>
<!-- 内容行 -->
<view class="text">
<p>RFID
<span v-if="item.rfidCode">{{ item.remark }}</span>
<span v-else style="color: #999;">未绑定RFID</span>
</p>
<p>生成时间
<span>{{ item?.createTime }}</span>
</p>
<!-- 备注行-->
<view class="flex-center qrCode">
<p>备注
<span v-if="item.remark">{{ item.remark }}</span>
<span v-else style="color: #999;">未填写</span>
</p>
<view @click="toViewQrCode">
<uv-image src="../../../../static/qrcode.png" width="40rpx" height="40rpx"
style="margin-top:10rpx" />
</view>
</view>
</view>
</p>
</template>
</uni-list-item>
</uni-list>
<!-- 物料列表 -只读 -->
<view>
<material-list ref="materialRef" :formData="materialList" isEdit="2" />
</view>
<!-- 底部按钮 -->
<view class="bottom">
<uv-button type="primary" text="唯一码打印"></uv-button>
<uv-button type="success" text="溯源" @click="toTraceability"></uv-button>
</view>
</view>
</uv-skeletons>
<uv-modal ref="modalRef" :title="none" class="qrCodeModal" width="480rpx" :showConfirmButton="false">
<qr-code-modal :detail="materialList" />
</uv-modal>
</template>
<script setup>
import { ref } from 'vue';
import { onLoad, onShow } from '@dcloudio/uni-app';
import { detailUniqueCode } from '@/api/uniqueCode'
import { getStatusName, getDetail } from '../until';
import QrCodeModal from './QrCodeModal.vue';
import MaterialList from '../../../components/MaterialList.vue'
import Navigation from '../../../components/Navigation.vue'
const modalRef = ref()
// 路径参数
const pathParams = ref('')
// 物料列表数组
const materialList = ref({
material: {},
remark: ''
})
// 唯一码数组
const uniqueCodeList = ref([])
// 详情数据
const detail = ref([])
// 骨架屏开关
const loading = ref(true)
// 骨架屏样式
const skeleton = [{
type: 'line',
num: 3,
gap: '20rpx',
style: ['width: 200rpx;marginBottom: 50rpx;marginTop:50rpx;padding:24rpx', 'height: 100rpx;', 'width: 500rpx;'],
}]
const toViewQrCode = () => {
modalRef.value.open()
}
// 详情:唯一码
const getDetailInfo = () => {
detailUniqueCode({ id: pathParams.value.id }).then((res) => {
if (res.code == 200) {
// ! 写成数组原因 为了适应可多个物料生成唯一码
// 将物料写入信息以及物料详细信息解构出来、以及qrCode、唯一码对应的物料code、qrCodeMark仅用于二维码页面的备注展示
materialList.value.material = [{
...res.data.material,
...getDetail(res.data.material.wornMaterial),
qrCode: res.data.qrCode,
code: res.data.code, //唯一码code
uniqueCodeMaterialId: res.data.material.id, //唯一码物料id
uniqueCodeRemark: res.data.remark, //唯一码备注
}]
// 唯一码备注
materialList.value.remark = res.data.remark
uniqueCodeList.value = [res.data]
console.log('详情数据唯一码materialList', materialList);
// 更新缓存物料值
uni.setStorageSync('app_material', materialList.value.material);
} else {
materialList.value = []
uniqueCodeList.value = []
uni.showToast({ title: '暂无数据', icon: 'none' })
}
loading.value = false
})
}
// 溯源
const toTraceability = () => {
}
// 修改:唯一码的物料信息 - 携带title和唯一码id
const toEdit = () => {
uni.navigateTo({
url: `/pages/warehousing/uniqueCode/issueUniqueCode/index?title=${pathParams.value.code}&id=${pathParams.value.id}`,
});
}
// 接收id 且修改标题
onLoad((options) => {
if (!options.id) {
loading.value = false
uni.showToast({ title: '参数错误,无唯一码ID', icon: 'none' })
return
}
pathParams.value = options
getDetailInfo()
})
onShow(() => {
uni.setNavigationBarTitle({ title: pathParams.value.code })
})
</script>
<style scoped lang="scss">
// 底部按钮
.bottom {
position: fixed;
bottom: 0;
height: 60rpx;
font-size: 14px;
display: flex;
align-items: center;
justify-content: center;
width: 100%;
.uv-button-wrapper {
width: 50%;
border-radius: 0;
}
}
::v-deep.qrCodeModal {
.uv-modal__content {
display: block !important;
padding: 24rpx;
}
}
</style>

View File

@@ -34,7 +34,7 @@
<span v-else style="color: #999;">未填写</span> <span v-else style="color: #999;">未填写</span>
</p> </p>
<view @click.stop="getDetailInfo(item)"> <view @click.stop="getDetailInfo(item)">
<uv-image src="../../../static/qrcode.png" width="40rpx" height="40rpx" <uv-image src="../../../../static/qrcode.png" width="40rpx" height="40rpx"
style="margin-top:10rpx" /> style="margin-top:10rpx" />
</view> </view>
</view> </view>
@@ -52,7 +52,7 @@
</uni-popup> </uni-popup>
</view> </view>
<!-- 唯一码图形显示弹窗 --> <!-- 唯一码图形显示弹窗 -->
<uv-modal ref="modalRef" :title="none" class="qrCodeModal":showConfirmButton="false"> <uv-modal ref="modalRef" :title="none" class="qrCodeModal" :showConfirmButton="false">
<qr-code-modal :detail="materialList" /> <qr-code-modal :detail="materialList" />
</uv-modal> </uv-modal>
</view> </view>
@@ -79,9 +79,13 @@ const isDialog = ref(false)
// //
const uniqueCodeList = ref([]) const uniqueCodeList = ref([])
// //
const materialList = ref([]) const materialList = ref({
material: []
})
// //
const getMaterialList = () => { const getMaterialList = (pageNo, pageSize) => {
queryParams.value.pageNum = pageNo
console.log(pageNo, pageSize, queryParams.value)
getUniqueCodeList(queryParams.value).then(res => { getUniqueCodeList(queryParams.value).then(res => {
res.rows.forEach(e => { res.rows.forEach(e => {
e.showMore = false; e.showMore = false;
@@ -99,7 +103,7 @@ onShow(() => {
// //
const toDetail = (val) => { const toDetail = (val) => {
uni.navigateTo({ uni.navigateTo({
url: `/pages/uniqueCode/myUniqueCode/detail?id=${val.id}&code=${val.code}`, url: `/pages/warehousing/uniqueCode/myUniqueCode/detail?id=${val.id}&code=${val.code}`,
}); });
} }
@@ -118,7 +122,6 @@ const dialogClose = () => {
const dialogConfirm = () => { const dialogConfirm = () => {
console.log(delItem.value.id, 'delItem.value.id'); console.log(delItem.value.id, 'delItem.value.id');
delUniqueCode({ ids: [delItem.value.id] }).then(res => { delUniqueCode({ ids: [delItem.value.id] }).then(res => {
console.log(res, '11');
dialogClose() dialogClose()
}) })
} }
@@ -126,7 +129,7 @@ const dialogConfirm = () => {
const getDetailInfo = (row) => { const getDetailInfo = (row) => {
detailUniqueCode({ id: row.id }).then((res) => { detailUniqueCode({ id: row.id }).then((res) => {
if (res.code == 200) { if (res.code == 200) {
materialList.value = [{ ...res.data.material, ...getDetail(res.data.material.wornMaterial), qrCode: res.data.qrCode, code: res.data.code,qrCodeMark:res.data.remark }] materialList.value.material = [{ ...res.data.material, ...getDetail(res.data.material.wornMaterial), qrCode: res.data.qrCode, code: res.data.code, uniqueCodeRemark: res.data.remark }]
setTimeout(() => { setTimeout(() => {
modalRef.value.open() modalRef.value.open()
}, 300) }, 300)
@@ -159,6 +162,7 @@ const getDetailInfo = (row) => {
font-size: 12px; font-size: 12px;
} }
} }
::v-deep.qrCodeModal { ::v-deep.qrCodeModal {
.uv-modal__content { .uv-modal__content {
display: block; display: block;

View File

@@ -1,8 +1,8 @@
export const getTypeParentNames = (val) => { export const getTypeParentNames = (val) => {
if (val) { if (val) {
let value = val.replace("/", ","); let value = val.replace(/\//g, ",");
const arr = `${value}`.split(","); const arr = `${value}`.split(",");
return arr[arr?.length - 1]; return arr[0];
} }
}; };
@@ -49,6 +49,7 @@ export const getDetail = (info) => {
description: info.description, description: info.description,
weight: info.weight, weight: info.weight,
kgFactor: info.kgFactor, kgFactor: info.kgFactor,
materialId:info.id,//物料id
} }

BIN
static/edit.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

BIN
static/printer.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

View File

@@ -1,81 +1,225 @@
.material-card { .material-card {
background-color: #fff; background-color: #fff;
// padding: 24rpx; // padding: 24rpx;
// margin: 8rpx 0 0 0; // margin: 8rpx 0 0 0;
.title { .title {
font-size: 14px; font-size: 14px;
font-weight: 600; font-weight: 600;
} }
.subTitle { .subTitle {
font-size: 12px; font-size: 12px;
color: #999; color: #999;
margin-top: 14rpx; margin-top: 8rpx;
} }
.tag { .tag {
margin-top: 14rpx; margin-top: 8rpx;
display: flex;
justify-content: space-between;
align-items: center;
p {
font-size: 14px;
}
.uni-tag {
padding: 2px 4px;
}
}
.remark{
height: 100%;
}
.text{
height: 100%;
p{
font-size: 14px;
margin-top: 16rpx;
}
span{
color: #616161;
}
}
.qrCode{
}
.status{
font-size: 12px;
color: #616161;
}
}
.flex-center{
display: flex; display: flex;
justify-content: space-between;
align-items: center; align-items: center;
justify-content: space-between;
p {
font-size: 14px;
}
.uni-tag {
padding: 2px 4px;
}
}
.remark {
height: 100%;
}
.text {
height: 100%;
p {
font-size: 14px;
margin-top: 8rpx;
}
span {
color: #616161;
}
}
.status {
font-size: 12px;
color: #616161;
}
}
.flex-center {
display: flex;
align-items: center;
justify-content: space-between;
} }
// 底部按钮 // 底部按钮
.bottom-btn { .bottom-btn {
position: fixed; position: fixed;
bottom: 0; bottom: 0;
width: 100%; width: 100%;
border-radius: 0; border-radius: 0;
background-color: #409EFF !important; background-color: #409eff !important;
border-color: #409EFF !important; border-color: #409eff !important;
height: 60rpx; height: 60rpx;
font-size: 14px; font-size: 14px;
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
} }
.mt-8{ .mt-8 {
margin-top: 8rpx; margin-top: 8rpx;
} }
.mt-16{ .mb-16 {
margin-top: 16rpx; margin-bottom: 16rpx;
}
.mb-4 {
margin-bottom: 4rpx;
}
.mb-6 {
margin-bottom: 6rpx;
}
.mb-2 {
margin-bottom: 2px;
}
.mt-16 {
margin-top: 16rpx;
}
.mr-8 {
margin-right: 8rpx;
}
.mr-16 {
margin-right: 16rpx;
}
.ml-8 {
margin-left: 8rpx;
}
.ml-16 {
margin-left: 16rpx;
}
.ml-24 {
margin-left: 24rpx;
}
.flex {
display: flex;
}
.align-center {
align-items: center;
}
.justify-center {
justify-content: center;
}
.justify-between {
justify-content: space-between;
}
.f-12 {
font-size: 12px;
}
.grey {
color: #696969;
}
.tag-error {
padding: 4px 6px;
border-radius: 4px;
display: inline-block;
height: 20px;
color: #fff;
background-color: #e43d33;
font-size: 10px;
line-height: 20px;
}
.material-box {
background-color: #fff;
// 标题
.title {
font-size: 14px;
font-weight: 600;
}
// 副标题
.subTitle {
font-size: 12px;
color: #999;
}
.uniqueCode {
font-size: 12px;
}
// 标签
.tag-row {
display: flex;
justify-content: space-between;
align-items: center;
p {
font-size: 14px;
}
}
.quantity-form {
display: flex;
align-items: center;
.uni-forms-item {
margin-bottom: 0;
align-items: center;
}
.uni-easyinput__content-input{
height: 25px;
width: 30px;
}
.unit {
margin-left: 8rpx;
font-size: 12px;
width: 20px;
}
}
.remark-row {
.uni-forms-item {
margin-bottom: 0;
margin-top: 8rpx;
}
.uni-forms-item__label {
height: 25px;
color: #333;
}
.uni-easyinput,
.uni-easyinput__content,
.uni-input-input {
height: 25px;
}
.is-disabled {
background-color: #fff !important;
}
.uni-input-input:disabled {
color: #333;
}
.uni-easyinput__content-input {
.uni-input-wrapper {
text-align: right;
}
}
.uni-input-placeholder {
text-align: right;
}
}
.text {
height: 100%;
p {
font-size: 14px;
margin-top: 8rpx;
}
span {
color: #616161;
}
}
.status {
font-size: 12px;
color: #616161;
}
} }

View File

@@ -341,6 +341,11 @@ commander@^2.20.0:
resolved "https://registry.npmmirror.com/commander/-/commander-2.20.3.tgz" resolved "https://registry.npmmirror.com/commander/-/commander-2.20.3.tgz"
integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==
dayjs@^1.11.20:
version "1.11.20"
resolved "https://registry.npmmirror.com/dayjs/-/dayjs-1.11.20.tgz"
integrity sha512-YbwwqR/uYpeoP4pu043q+LTDLFBLApUP6VxRihdfNTqu4ubqMlGDLd6ErXhEgsyvY0K6nCs7nggYumAN+9uEuQ==
detect-libc@^2.0.3: detect-libc@^2.0.3:
version "2.1.2" version "2.1.2"
resolved "https://registry.npmmirror.com/detect-libc/-/detect-libc-2.1.2.tgz" resolved "https://registry.npmmirror.com/detect-libc/-/detect-libc-2.1.2.tgz"
@@ -504,6 +509,11 @@ loader-utils@^2.0.0:
emojis-list "^3.0.0" emojis-list "^3.0.0"
json5 "^2.1.2" json5 "^2.1.2"
lodash@^4.17.23:
version "4.17.23"
resolved "https://registry.npmmirror.com/lodash/-/lodash-4.17.23.tgz"
integrity sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==
merge-stream@^2.0.0: merge-stream@^2.0.0:
version "2.0.0" version "2.0.0"
resolved "https://registry.npmmirror.com/merge-stream/-/merge-stream-2.0.0.tgz" resolved "https://registry.npmmirror.com/merge-stream/-/merge-stream-2.0.0.tgz"
@@ -536,6 +546,11 @@ node-releases@^2.0.27:
resolved "https://registry.npmmirror.com/node-releases/-/node-releases-2.0.36.tgz" resolved "https://registry.npmmirror.com/node-releases/-/node-releases-2.0.36.tgz"
integrity sha512-TdC8FSgHz8Mwtw9g5L4gR/Sh9XhSP/0DEkQxfEFXOpiul5IiHgHan2VhYYb6agDSfp4KuvltmGApc8HMgUrIkA== integrity sha512-TdC8FSgHz8Mwtw9g5L4gR/Sh9XhSP/0DEkQxfEFXOpiul5IiHgHan2VhYYb6agDSfp4KuvltmGApc8HMgUrIkA==
numeral@^2.0.6:
version "2.0.6"
resolved "https://registry.npmmirror.com/numeral/-/numeral-2.0.6.tgz"
integrity sha512-qaKRmtYPZ5qdw4jWJD6bxEf1FJEqllJrwxCLIm0sQU/A7v2/czigzOb+C2uSiFsa9lBUzeH7M1oK+Q+OLxL3kA==
picocolors@^1.1.1: picocolors@^1.1.1:
version "1.1.1" version "1.1.1"
resolved "https://registry.npmmirror.com/picocolors/-/picocolors-1.1.1.tgz" resolved "https://registry.npmmirror.com/picocolors/-/picocolors-1.1.1.tgz"