新增耗材入库-ai批量入库
This commit is contained in:
@@ -149,6 +149,38 @@ const AuthAPI = {
|
|||||||
method: "post",
|
method: "post",
|
||||||
data,
|
data,
|
||||||
});
|
});
|
||||||
|
},
|
||||||
|
// 入库单识别
|
||||||
|
stockOcr(data: any) {
|
||||||
|
return request<any, Responseres>({
|
||||||
|
url: `${baseURL}/stock/ocr`,
|
||||||
|
method: "post",
|
||||||
|
data,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
// ocr识别结果
|
||||||
|
ocrResult(params: any) {
|
||||||
|
return request<any, Responseres>({
|
||||||
|
url: `${baseURL}/stock/ocrResult`,
|
||||||
|
method: "get",
|
||||||
|
params,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
// 耗材入库
|
||||||
|
stockIn(data: any) {
|
||||||
|
return request<any, Responseres>({
|
||||||
|
url: `${baseURL}/stock/in`,
|
||||||
|
method: "POST",
|
||||||
|
data,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
// 供应商-列表
|
||||||
|
vendorList(params: any) {
|
||||||
|
return request<any, Responseres>({
|
||||||
|
url: `${baseURL}/vendor/list`,
|
||||||
|
method: "get",
|
||||||
|
params,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,17 +1,8 @@
|
|||||||
<!-- 单图上传组件 -->
|
<!-- 单图上传组件 -->
|
||||||
<template>
|
<template>
|
||||||
<el-upload
|
<el-upload v-model="modelValue" class="single-upload" list-type="picture-card" :show-file-list="false"
|
||||||
v-model="modelValue"
|
:accept="props.accept" :before-upload="handleBeforeUpload" :http-request="handleUpload" :on-success="onSuccess"
|
||||||
class="single-upload"
|
:on-error="onError" multiple :before-remove="emits('clear')">
|
||||||
list-type="picture-card"
|
|
||||||
:show-file-list="false"
|
|
||||||
:accept="props.accept"
|
|
||||||
:before-upload="handleBeforeUpload"
|
|
||||||
:http-request="handleUpload"
|
|
||||||
:on-success="onSuccess"
|
|
||||||
:on-error="onError"
|
|
||||||
multiple
|
|
||||||
>
|
|
||||||
<template #default>
|
<template #default>
|
||||||
<el-image v-if="modelValue" :src="modelValue" />
|
<el-image v-if="modelValue" :src="modelValue" />
|
||||||
<el-icon v-if="modelValue" class="single-upload__delete-btn" @click.stop="handleDelete">
|
<el-icon v-if="modelValue" class="single-upload__delete-btn" @click.stop="handleDelete">
|
||||||
@@ -120,6 +111,7 @@ function handleBeforeUpload(file: UploadRawFile) {
|
|||||||
*/
|
*/
|
||||||
function handleUpload(options: UploadRequestOptions) {
|
function handleUpload(options: UploadRequestOptions) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
|
emits("upload", options);
|
||||||
const file = options.file;
|
const file = options.file;
|
||||||
|
|
||||||
const formData = new FormData();
|
const formData = new FormData();
|
||||||
@@ -147,7 +139,7 @@ function handleDelete() {
|
|||||||
modelValue.value = "";
|
modelValue.value = "";
|
||||||
}
|
}
|
||||||
|
|
||||||
const emits = defineEmits(["onSuccess"]);
|
const emits = defineEmits(["onSuccess", 'upload', 'clear']);
|
||||||
/**
|
/**
|
||||||
* 上传成功回调
|
* 上传成功回调
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -681,13 +681,15 @@ export default {
|
|||||||
shopId: this.shopInfo.id
|
shopId: this.shopInfo.id
|
||||||
});
|
});
|
||||||
this.trade = res;
|
this.trade = res;
|
||||||
this.tradeLoading = false;
|
|
||||||
this.tradeSale = res.sale;
|
this.tradeSale = res.sale;
|
||||||
this.tradeVip = res.vip;
|
this.tradeVip = res.vip;
|
||||||
this.tradeCount = res.count;
|
this.tradeCount = res.count;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error);
|
console.log(error);
|
||||||
}
|
}
|
||||||
|
setTimeout(() => {
|
||||||
|
this.tradeLoading = false;
|
||||||
|
}, 500);
|
||||||
},
|
},
|
||||||
lineChartTypeChange(i) {
|
lineChartTypeChange(i) {
|
||||||
this.lineChartType = i;
|
this.lineChartType = i;
|
||||||
|
|||||||
@@ -8,9 +8,8 @@
|
|||||||
<el-form :model="query" inline label-position="left">
|
<el-form :model="query" inline label-position="left">
|
||||||
<template v-if="orderType == 2">
|
<template v-if="orderType == 2">
|
||||||
<el-form-item>
|
<el-form-item>
|
||||||
<el-input placeholder="商品名称" v-model="query.productName" />
|
<el-input placeholder="商品名称" v-model="query.productName" clearable />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
|
||||||
<el-form-item v-if="isHeadShop == 1 && loginType == 0">
|
<el-form-item v-if="isHeadShop == 1 && loginType == 0">
|
||||||
<el-select v-model="shopId" placeholder="选择分店" style="width: 200px; margin-right: 10px"
|
<el-select v-model="shopId" placeholder="选择分店" style="width: 200px; margin-right: 10px"
|
||||||
@change="getCategory">
|
@change="getCategory">
|
||||||
@@ -35,12 +34,12 @@
|
|||||||
<el-radio-button value="custom">自定义</el-radio-button>
|
<el-radio-button value="custom">自定义</el-radio-button>
|
||||||
</el-radio-group>
|
</el-radio-group>
|
||||||
<el-date-picker class="u-m-l-10" v-model="query.createdAt" type="daterange" range-separator="至"
|
<el-date-picker class="u-m-l-10" v-model="query.createdAt" type="daterange" range-separator="至"
|
||||||
start-placeholder="开始日期" end-placeholder="结束日期" value-format="YYYY-MM-DD HH:mm:ss"
|
start-placeholder="开始日期" end-placeholder="结束日期" value-format="YYYY-MM-DD" :disabled-date="disabledDate"
|
||||||
v-if="timeValue == 'custom'"></el-date-picker>
|
v-if="timeValue == 'custom'"></el-date-picker>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item>
|
<el-form-item>
|
||||||
<el-button type="primary" @click="getTableData">查询</el-button>
|
<el-button type="primary" icon="Search" :loading="tableData.loading" @click="getTableData">查询</el-button>
|
||||||
<el-button @click="resetHandle">重置</el-button>
|
<el-button icon="Refresh" :loading="tableData.loading" @click="resetHandle">重置</el-button>
|
||||||
<el-button icon="download" v-loading="downloadLoading" @click="downloadHandle">
|
<el-button icon="download" v-loading="downloadLoading" @click="downloadHandle">
|
||||||
<span v-if="!downloadLoading">导出Excel</span>
|
<span v-if="!downloadLoading">导出Excel</span>
|
||||||
<span v-else>下载中...</span>
|
<span v-else>下载中...</span>
|
||||||
@@ -205,7 +204,7 @@ export default {
|
|||||||
orderType: "2",
|
orderType: "2",
|
||||||
categorys: [],
|
categorys: [],
|
||||||
query: {
|
query: {
|
||||||
createdAt: [],
|
createdAt: [dayjs().format("YYYY-MM-DD"), dayjs().format("YYYY-MM-DD")],
|
||||||
productName: "",
|
productName: "",
|
||||||
prodCategoryId: "",
|
prodCategoryId: "",
|
||||||
},
|
},
|
||||||
@@ -225,6 +224,12 @@ export default {
|
|||||||
isHeadShop: JSON.parse(localStorage.getItem("userInfo")).isHeadShop,
|
isHeadShop: JSON.parse(localStorage.getItem("userInfo")).isHeadShop,
|
||||||
loginType: localStorage.getItem("loginType"),
|
loginType: localStorage.getItem("loginType"),
|
||||||
shopInfo: JSON.parse(localStorage.getItem("userInfo")),
|
shopInfo: JSON.parse(localStorage.getItem("userInfo")),
|
||||||
|
disabledDate: (time) => {
|
||||||
|
// dayjs().startOf('day'):获取今天的 00:00:00
|
||||||
|
// dayjs(time):将原生 Date 转为 dayjs 对象
|
||||||
|
// isAfter:判断目标日期是否在今天之后
|
||||||
|
return dayjs(time).isAfter(dayjs().startOf('day'));
|
||||||
|
}
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
filters: {
|
filters: {
|
||||||
@@ -268,9 +273,9 @@ export default {
|
|||||||
// 获取订单汇总
|
// 获取订单汇总
|
||||||
async daycount() {
|
async daycount() {
|
||||||
try {
|
try {
|
||||||
if (this.query.createdAt[1]) {
|
// if (this.query.createdAt[1]) {
|
||||||
this.query.createdAt.splice(1, 1, this.query.createdAt[1].replace("00:00:00", "23:59:59"))
|
// this.query.createdAt.splice(1, 1, this.query.createdAt[1].replace("00:00:00", "23:59:59"))
|
||||||
}
|
// }
|
||||||
const res = await saleSummaryApi.count({
|
const res = await saleSummaryApi.count({
|
||||||
beginDate: this.query.createdAt[0],
|
beginDate: this.query.createdAt[0],
|
||||||
endDate: this.query.createdAt[1],
|
endDate: this.query.createdAt[1],
|
||||||
@@ -289,9 +294,9 @@ export default {
|
|||||||
async downloadHandle() {
|
async downloadHandle() {
|
||||||
try {
|
try {
|
||||||
this.downloadLoading = true;
|
this.downloadLoading = true;
|
||||||
if (this.query.createdAt[1]) {
|
// if (this.query.createdAt[1]) {
|
||||||
this.query.createdAt.splice(1, 1, this.query.createdAt[1].replace("00:00:00", "23:59:59"))
|
// this.query.createdAt.splice(1, 1, this.query.createdAt[1].replace("00:00:00", "23:59:59"))
|
||||||
}
|
// }
|
||||||
const file = await saleSummaryApi.export({
|
const file = await saleSummaryApi.export({
|
||||||
beginDate: this.query.createdAt[0],
|
beginDate: this.query.createdAt[0],
|
||||||
endDate: this.query.createdAt[1],
|
endDate: this.query.createdAt[1],
|
||||||
@@ -307,7 +312,7 @@ export default {
|
|||||||
},
|
},
|
||||||
// 重置查询
|
// 重置查询
|
||||||
resetHandle() {
|
resetHandle() {
|
||||||
this.timeValue = "";
|
this.timeValue = 'today';
|
||||||
this.query = { ...this.resetQuery };
|
this.query = { ...this.resetQuery };
|
||||||
this.page = 1;
|
this.page = 1;
|
||||||
this.getTableData();
|
this.getTableData();
|
||||||
@@ -327,9 +332,9 @@ export default {
|
|||||||
this.tableData.loading = true;
|
this.tableData.loading = true;
|
||||||
try {
|
try {
|
||||||
this.daycount();
|
this.daycount();
|
||||||
if (this.query.createdAt[1]) {
|
// if (this.query.createdAt[1]) {
|
||||||
this.query.createdAt.splice(1, 1, this.query.createdAt[1].replace("00:00:00", "23:59:59"))
|
// this.query.createdAt.splice(1, 1, this.query.createdAt[1].replace("00:00:00", "23:59:59"))
|
||||||
}
|
// }
|
||||||
const res = await saleSummaryApi.page({
|
const res = await saleSummaryApi.page({
|
||||||
page: this.tableData.page,
|
page: this.tableData.page,
|
||||||
size: this.tableData.size,
|
size: this.tableData.size,
|
||||||
@@ -347,11 +352,17 @@ export default {
|
|||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error);
|
console.log(error);
|
||||||
}
|
}
|
||||||
this.tableData.loading = false;
|
setTimeout(() => {
|
||||||
|
this.tableData.loading = false;
|
||||||
|
}, 500);
|
||||||
},
|
},
|
||||||
// 切换时间
|
// 切换时间
|
||||||
timeChange(e) {
|
timeChange(e) {
|
||||||
this.query.createdAt = formatDateRange(e)
|
if (e !== 'custom') {
|
||||||
|
this.query.createdAt = formatDateRange(e)
|
||||||
|
} else {
|
||||||
|
this.query.createdAt = []
|
||||||
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -7,12 +7,8 @@
|
|||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="耗材分类" prop="consGroupId">
|
<el-form-item label="耗材分类" prop="consGroupId">
|
||||||
<el-select v-model="form.consGroupId" placeholder="请选择耗材分类" style="width: 200px">
|
<el-select v-model="form.consGroupId" placeholder="请选择耗材分类" style="width: 200px">
|
||||||
<el-option
|
<el-option v-for="option in consGroups" :key="option.conTypeId" :label="option.label"
|
||||||
v-for="option in consGroups"
|
:value="option.id"></el-option>
|
||||||
:key="option.conTypeId"
|
|
||||||
:label="option.label"
|
|
||||||
:value="option.id"
|
|
||||||
></el-option>
|
|
||||||
</el-select>
|
</el-select>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="耗材价格" prop="price">
|
<el-form-item label="耗材价格" prop="price">
|
||||||
@@ -24,18 +20,11 @@
|
|||||||
<el-form-item label="状态">
|
<el-form-item label="状态">
|
||||||
<el-switch v-model="form.status" :active-value="1" :inactive-value="0"></el-switch>
|
<el-switch v-model="form.status" :active-value="1" :inactive-value="0"></el-switch>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
|
||||||
<el-form-item label="单位" prop="conUnit">
|
<el-form-item label="单位" prop="conUnit">
|
||||||
<el-input v-model="form.conUnit" placeholder="请输入单位"></el-input>
|
<el-input v-model="form.conUnit" placeholder="请输入单位"></el-input>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-alert
|
<el-alert class="u-m-t-10 u-m-b-10" title="提示" description="换算值为第二单位*第二单位转换数量=第一单位" type="warning" show-icon
|
||||||
class="u-m-t-10 u-m-b-10"
|
:closable="false"></el-alert>
|
||||||
title="提示"
|
|
||||||
description="换算值为第二单位*第二单位转换数量=第一单位"
|
|
||||||
type="warning"
|
|
||||||
show-icon
|
|
||||||
:closable="false"
|
|
||||||
></el-alert>
|
|
||||||
<el-form-item label="预警值">
|
<el-form-item label="预警值">
|
||||||
<el-input-number v-model="form.conWarning" placeholder="请输入耗材预警值"></el-input-number>
|
<el-input-number v-model="form.conWarning" placeholder="请输入耗材预警值"></el-input-number>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
@@ -46,10 +35,7 @@
|
|||||||
<el-input v-model="form.conUnitTwo" placeholder="请输入第二单位"></el-input>
|
<el-input v-model="form.conUnitTwo" placeholder="请输入第二单位"></el-input>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="第二单位转换数量" prop="conUnitTwoConvert">
|
<el-form-item label="第二单位转换数量" prop="conUnitTwoConvert">
|
||||||
<el-input-number
|
<el-input-number v-model="form.conUnitTwoConvert" placeholder="第二单位转换数量"></el-input-number>
|
||||||
v-model="form.conUnitTwoConvert"
|
|
||||||
placeholder="第二单位转换数量"
|
|
||||||
></el-input-number>
|
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="默认入库单位" prop="defaultUnit">
|
<el-form-item label="默认入库单位" prop="defaultUnit">
|
||||||
<el-input v-model="form.defaultUnit" placeholder="请输入默认入库单位"></el-input>
|
<el-input v-model="form.defaultUnit" placeholder="请输入默认入库单位"></el-input>
|
||||||
|
|||||||
393
src/views/inventory/consumables/components/aiEntryDialog.vue
Normal file
393
src/views/inventory/consumables/components/aiEntryDialog.vue
Normal file
@@ -0,0 +1,393 @@
|
|||||||
|
<!-- AI入库 -->
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<el-dialog title="批量入库" width="1000px" v-model="visible" @close="dialogCloseHandle">
|
||||||
|
<div class="content">
|
||||||
|
<div class="title_wrap">
|
||||||
|
<span class="t">第一步:上传图片</span>
|
||||||
|
</div>
|
||||||
|
<div class="upload_wrap" v-loading="uploadLoading">
|
||||||
|
<div class="upload_btn">
|
||||||
|
<SingleImageUpload v-model="imgUrl" @upload="uploadLoading = true" @on-success="uploadSuccess"
|
||||||
|
@clear="resId = null" />
|
||||||
|
</div>
|
||||||
|
<div class="btn">
|
||||||
|
<el-button type="primary" :disabled="resId === null" @click="startCheckOcrRes">开始解析</el-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="title_wrap">
|
||||||
|
<span class="t">第二步:编辑信息</span>
|
||||||
|
</div>
|
||||||
|
<div class="table" v-if="form.bodyList">
|
||||||
|
<el-form :model="form" label-width="120px" label-position="top">
|
||||||
|
<el-form-item label="供应商信息">
|
||||||
|
<el-select v-model="form.vendorId" style="width: 200px;" :disabled="stockInStatus">
|
||||||
|
<el-option v-for="item in vendorList" :key="item.id" :label="item.name" :value="item.id" />
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="耗材信息">
|
||||||
|
<div class="header_wrap">
|
||||||
|
<div class="item">
|
||||||
|
<span class="tb">共{{ form.bodyList.length }}种耗材,</span>
|
||||||
|
<span class="tb">金额合计:¥{{multiplyAndFormat(form.bodyList.reduce((sum, item) => sum +
|
||||||
|
(item.purchasePrice || 0) *
|
||||||
|
(item.inOutNumber || 0), 0), 1)}}</span>
|
||||||
|
</div>
|
||||||
|
<div class="item">
|
||||||
|
<el-button type="primary" @click="consumableListRef.show(form.bodyList)"
|
||||||
|
v-if="!stockInStatus">选择耗材</el-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<el-table :data="form.bodyList" border stripe width="100%" v-loading="confirmLoading">
|
||||||
|
<el-table-column prop="conName" label="耗材名称">
|
||||||
|
<template v-slot="scope">
|
||||||
|
<el-input v-model="scope.row.conName" :maxlength="8" :disabled="stockInStatus"></el-input>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="purchasePrice" label="单价">
|
||||||
|
<template v-slot="scope">
|
||||||
|
<el-input v-model="scope.row.purchasePrice" :maxlength="8"
|
||||||
|
@input="e => scope.row.purchasePrice = filterNumberInput(e)" :disabled="stockInStatus"></el-input>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="unitName" label="单位">
|
||||||
|
<template v-slot="scope">
|
||||||
|
<el-input v-model="scope.row.unitName" :maxlength="8" :disabled="stockInStatus"></el-input>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="inOutNumber" label="数量">
|
||||||
|
<template v-slot="scope">
|
||||||
|
<el-input v-model="scope.row.inOutNumber" @input="e => scope.row.inOutNumber = filterNumberInput(e)"
|
||||||
|
:disabled="stockInStatus"></el-input>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="subTotal" label="小计">
|
||||||
|
<template v-slot="scope">
|
||||||
|
{{ multiplyAndFormat(scope.row.purchasePrice || 0, scope.row.inOutNumber || 0) }}
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="状态" width="120">
|
||||||
|
<template v-slot="scope">
|
||||||
|
<div v-if="stockInStatus">
|
||||||
|
<el-text type="success" v-if="scope.row.conId">入库成功</el-text>
|
||||||
|
<div class="column" v-else>
|
||||||
|
<div>
|
||||||
|
<el-text type="danger">入库失败</el-text>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<el-text type="info" size="small">失败原因:{{ scope.row.failReason }}</el-text>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="操作" width="80">
|
||||||
|
<template v-slot="scope">
|
||||||
|
<el-button type="danger" link @click="form.bodyList.splice(scope.$index, 1)"
|
||||||
|
v-if="!stockInStatus">删除</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
<div class="result_wrap" v-if="stockInStatus">
|
||||||
|
上传完成 共{{ form.bodyList.length }}条数据,其中成功
|
||||||
|
<el-text type="success">{{form.bodyList.filter(item => item.conId).length}} </el-text> 条,失败 <el-text
|
||||||
|
type="danger">{{form.bodyList.filter(item => !item.conId).length}} </el-text>条
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="tips" v-else>
|
||||||
|
<el-text type="danger">请解析后使用</el-text>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<template #footer>
|
||||||
|
<div class="dialog-footer" v-if="form.bodyList && !stockInStatus">
|
||||||
|
<el-button @click="visible = false">取消</el-button>
|
||||||
|
<el-button type="primary" :loading="confirmLoading" @click="submitHandle">提交</el-button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
|
<el-dialog title="扫描结果查询中..." width="500px" v-model="checkLoading" :show-close="false"
|
||||||
|
:close-on-click-modal="false">
|
||||||
|
<div class="loading_wrap" ref="loadingWrap" v-loading="checkLoading" :element-loading-text="loadingText"></div>
|
||||||
|
<template #footer>
|
||||||
|
<div class="dialog-footer">
|
||||||
|
<el-button type="danger" @click="closeCheckOcrHandle">取消查询</el-button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
|
<consumableList ref="consumableListRef" @success="consumableListRes" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ref, computed, watch, nextTick } from "vue";
|
||||||
|
import SingleImageUpload from "@/components/Upload/SingleImageUpload.vue"
|
||||||
|
import productApi from "@/api/product/index";
|
||||||
|
import { ElMessage } from "element-plus";
|
||||||
|
import { filterNumberInput, multiplyAndFormat } from '@/utils'
|
||||||
|
import consumableList from "../../operation_in/components/consumableList.vue";
|
||||||
|
|
||||||
|
const consumableListRef = ref(null);
|
||||||
|
|
||||||
|
function consumableListRes(e) {
|
||||||
|
console.log(e);
|
||||||
|
if (e.length) {
|
||||||
|
let arr = e.map(item => ({
|
||||||
|
id: item.id,
|
||||||
|
conId: item.id,
|
||||||
|
conName: item.conName,
|
||||||
|
purchasePrice: item.price,
|
||||||
|
unitName: item.conUnit,
|
||||||
|
inOutNumber: 1,
|
||||||
|
}));
|
||||||
|
form.value.bodyList.push(...arr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const emits = defineEmits(['update']);
|
||||||
|
|
||||||
|
const visible = ref(false);
|
||||||
|
|
||||||
|
const show = () => {
|
||||||
|
visible.value = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
const dialogCloseHandle = () => {
|
||||||
|
emits('update');
|
||||||
|
resId.value = null;
|
||||||
|
form.value = {};
|
||||||
|
stockInStatus.value = false;
|
||||||
|
imgUrl.value = "";
|
||||||
|
};
|
||||||
|
|
||||||
|
// 上传图片
|
||||||
|
let resId = ref(null);
|
||||||
|
const uploadLoading = ref(false);
|
||||||
|
const imgUrl = ref("");
|
||||||
|
async function uploadSuccess(url) {
|
||||||
|
try {
|
||||||
|
imgUrl.value = url;
|
||||||
|
uploadLoading.value = true;
|
||||||
|
resId.value = await productApi.stockOcr({ url: imgUrl.value });
|
||||||
|
} catch (error) {
|
||||||
|
console.error('上传图片失败', error);
|
||||||
|
}
|
||||||
|
setTimeout(() => {
|
||||||
|
uploadLoading.value = false;
|
||||||
|
}, 500);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询OCR结果
|
||||||
|
const form = ref({})
|
||||||
|
function startCheckOcrRes() {
|
||||||
|
stockInStatus.value = false;
|
||||||
|
startQueryInterval()
|
||||||
|
.then((res) => {
|
||||||
|
console.log('查询成功', JSON.stringify(res));
|
||||||
|
form.value = res;
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
console.error('查询失败', error);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 启动查询方法,每5秒查询一次,五分钟后超时停止查询
|
||||||
|
const checkLoading = ref(false);
|
||||||
|
const speed = 10000; // 查询间隔时间,单位毫秒
|
||||||
|
const timeout = 300000; // 超时时间,单位毫秒
|
||||||
|
// const timeout = 15000; // 超时时间,单位毫秒
|
||||||
|
const interval = ref(null);
|
||||||
|
const checkNumber = ref(0);
|
||||||
|
const loadingText = computed(() => `每${speed / 1000}秒查询1次,已查询:${checkNumber.value}次`);
|
||||||
|
const loadingWrap = ref(null);
|
||||||
|
|
||||||
|
function updateOverlayText() {
|
||||||
|
nextTick(() => {
|
||||||
|
try {
|
||||||
|
const el = loadingWrap.value && loadingWrap.value.querySelector && loadingWrap.value.querySelector('.el-loading-text');
|
||||||
|
if (el) el.textContent = loadingText.value;
|
||||||
|
} catch (e) {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
watch(loadingText, () => {
|
||||||
|
if (checkLoading.value) updateOverlayText();
|
||||||
|
});
|
||||||
|
|
||||||
|
watch(checkLoading, (val) => {
|
||||||
|
if (val) updateOverlayText();
|
||||||
|
});
|
||||||
|
function startQueryInterval() {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
if (!resId.value) {
|
||||||
|
reject(new Error('无效的 resId,无法查询'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 重置计数并显示 loading
|
||||||
|
checkNumber.value = 0;
|
||||||
|
checkLoading.value = true;
|
||||||
|
updateOverlayText();
|
||||||
|
|
||||||
|
const startTime = Date.now();
|
||||||
|
interval.value = null;
|
||||||
|
|
||||||
|
async function checkOnce() {
|
||||||
|
try {
|
||||||
|
checkNumber.value++;
|
||||||
|
console.log('ocr checkNumber:', checkNumber.value);
|
||||||
|
checkLoading.value = true;
|
||||||
|
const res = await productApi.ocrResult({ id: resId.value });
|
||||||
|
if (res && res.batchNo) {
|
||||||
|
ElMessage.success('查询成功');
|
||||||
|
checkLoading.value = false;
|
||||||
|
clearInterval(interval.value);
|
||||||
|
resolve(res);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果还未超时,则继续等待下一轮查询
|
||||||
|
if (Date.now() - startTime >= timeout) {
|
||||||
|
checkLoading.value = false;
|
||||||
|
ElMessage.error('查询超时');
|
||||||
|
clearInterval(interval.value);
|
||||||
|
reject(new Error('查询超时'));
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
ElMessage.error('查询失败');
|
||||||
|
checkLoading.value = false;
|
||||||
|
clearInterval(interval.value);
|
||||||
|
reject(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 立即执行一次查询,然后按间隔继续查询
|
||||||
|
checkOnce();
|
||||||
|
interval.value = setInterval(checkOnce, speed);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 取消查询
|
||||||
|
function closeCheckOcrHandle() {
|
||||||
|
checkLoading.value = false;
|
||||||
|
clearInterval(interval.value);
|
||||||
|
// 清空 overlay 文本(如果存在)以避免残留描述
|
||||||
|
nextTick(() => {
|
||||||
|
try {
|
||||||
|
const el = loadingWrap.value && loadingWrap.value.querySelector && loadingWrap.value.querySelector('.el-loading-text');
|
||||||
|
if (el) el.textContent = '';
|
||||||
|
} catch (e) { }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 供应商列表
|
||||||
|
const vendorList = ref([]);
|
||||||
|
async function vendorListAjax() {
|
||||||
|
try {
|
||||||
|
vendorList.value = await productApi.vendorList();
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 最终提交
|
||||||
|
const confirmLoading = ref(false);
|
||||||
|
const stockInStatus = ref(false);
|
||||||
|
async function submitHandle() {
|
||||||
|
try {
|
||||||
|
confirmLoading.value = true;
|
||||||
|
const res = await productApi.stockIn(form.value)
|
||||||
|
form.value = res;
|
||||||
|
stockInStatus.value = true;
|
||||||
|
ElMessage.success('提交成功');
|
||||||
|
} catch (error) {
|
||||||
|
console.error('提交失败', error);
|
||||||
|
}
|
||||||
|
setTimeout(() => {
|
||||||
|
confirmLoading.value = false;
|
||||||
|
// visible.value = false;
|
||||||
|
}, 500);
|
||||||
|
}
|
||||||
|
|
||||||
|
defineExpose({
|
||||||
|
show,
|
||||||
|
});
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
vendorListAjax();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.content {
|
||||||
|
.title_wrap {
|
||||||
|
background-color: #F8F8F8;
|
||||||
|
height: 54px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 0 42px;
|
||||||
|
|
||||||
|
.t {
|
||||||
|
font-size: 16px;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.upload_wrap {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 14px 0;
|
||||||
|
|
||||||
|
.upload_btn {
|
||||||
|
width: 152px;
|
||||||
|
height: 152px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading_wrap {
|
||||||
|
height: 200px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table {
|
||||||
|
width: 100%;
|
||||||
|
padding-top: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.result_wrap {
|
||||||
|
padding: 14px 0;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tips {
|
||||||
|
padding: 14px 0;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.column {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: flex-start;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header_wrap {
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding-bottom: 14px;
|
||||||
|
|
||||||
|
.tb {
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -82,6 +82,11 @@ const contentConfig: IContentConfig = {
|
|||||||
name: "reportinglosses",
|
name: "reportinglosses",
|
||||||
auth: "",
|
auth: "",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
text: "批量入库",
|
||||||
|
name: "batchWarehousing",
|
||||||
|
auth: "",
|
||||||
|
},
|
||||||
],
|
],
|
||||||
defaultToolbar: ["refresh", "filter", "search"],
|
defaultToolbar: ["refresh", "filter", "search"],
|
||||||
cols: [
|
cols: [
|
||||||
|
|||||||
@@ -83,10 +83,14 @@
|
|||||||
<add-haocai ref="refAddHaocai" @refresh="refresh" />
|
<add-haocai ref="refAddHaocai" @refresh="refresh" />
|
||||||
<!-- 耗材盘点 -->
|
<!-- 耗材盘点 -->
|
||||||
<addConsTakin ref="refAddHaocaiTakin" @success="refresh" />
|
<addConsTakin ref="refAddHaocaiTakin" @success="refresh" />
|
||||||
|
<!-- AI入库 -->
|
||||||
|
<ai-entry-dialog ref="aiEntryDialogRef" @update="
|
||||||
|
contentRef?.fetchPageData()" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import aiEntryDialog from "./components/aiEntryDialog.vue";
|
||||||
import addHaocai from "./components/add-haocai.vue";
|
import addHaocai from "./components/add-haocai.vue";
|
||||||
import dataTongji from "./components/DataStatistics.vue";
|
import dataTongji from "./components/DataStatistics.vue";
|
||||||
import addConsTakin from "./components/addConsTakin.vue";
|
import addConsTakin from "./components/addConsTakin.vue";
|
||||||
@@ -101,6 +105,12 @@ import searchConfig from "./config/search";
|
|||||||
import { returnOptionsLabel } from "./config/config";
|
import { returnOptionsLabel } from "./config/config";
|
||||||
import { isSyncStatus } from "@/utils/index";
|
import { isSyncStatus } from "@/utils/index";
|
||||||
|
|
||||||
|
const aiEntryDialogRef = ref<any>(null);
|
||||||
|
|
||||||
|
function showAiEntryDialog() {
|
||||||
|
aiEntryDialogRef.value?.show?.();
|
||||||
|
}
|
||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const {
|
const {
|
||||||
searchRef,
|
searchRef,
|
||||||
@@ -164,7 +174,7 @@ watch(
|
|||||||
);
|
);
|
||||||
|
|
||||||
//耗材盘点
|
//耗材盘点
|
||||||
const refAddHaocaiTakin = ref();
|
const refAddHaocaiTakin = ref<any>(null);
|
||||||
function refAddHaocaiTakinShow(item: any, type: string) {
|
function refAddHaocaiTakinShow(item: any, type: string) {
|
||||||
console.log(item);
|
console.log(item);
|
||||||
if (type === "manual-in") {
|
if (type === "manual-in") {
|
||||||
@@ -172,14 +182,14 @@ function refAddHaocaiTakinShow(item: any, type: string) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (type === "delete") {
|
if (type === "delete") {
|
||||||
refAddHaocaiTakin.value.show(item, type);
|
refAddHaocaiTakin.value?.show?.(item, type);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (type === "consumables") {
|
if (type === "consumables") {
|
||||||
refAddHaocaiTakin.value.show(item, type);
|
refAddHaocaiTakin.value?.show?.(item, type);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
refAddHaocaiTakin.value.show(item);
|
refAddHaocaiTakin.value?.show?.(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
function refresh() {
|
function refresh() {
|
||||||
@@ -187,9 +197,9 @@ function refresh() {
|
|||||||
contentRef.value?.fetchPageData();
|
contentRef.value?.fetchPageData();
|
||||||
getTongji(undefined);
|
getTongji(undefined);
|
||||||
}
|
}
|
||||||
const refAddHaocai = ref();
|
const refAddHaocai = ref<any>(null);
|
||||||
function refAddHaocaiOpen(item: any) {
|
function refAddHaocaiOpen(item: any) {
|
||||||
refAddHaocai.value.open(item);
|
refAddHaocai.value?.open?.(item);
|
||||||
}
|
}
|
||||||
// 新增
|
// 新增
|
||||||
async function handleAddClick() {
|
async function handleAddClick() {
|
||||||
@@ -234,6 +244,10 @@ async function handleToolbarClick(name: string) {
|
|||||||
router.push({ path: "/inventory/libraryrecords", query: { inOutItem: name } });
|
router.push({ path: "/inventory/libraryrecords", query: { inOutItem: name } });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (name === "batchWarehousing") {
|
||||||
|
showAiEntryDialog();
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// 其他操作列
|
// 其他操作列
|
||||||
async function handleOperatClick(data: IOperatData) {
|
async function handleOperatClick(data: IOperatData) {
|
||||||
|
|||||||
@@ -35,6 +35,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
|
import _ from "lodash";
|
||||||
import CouponLists from "./coup-lists.vue";
|
import CouponLists from "./coup-lists.vue";
|
||||||
|
|
||||||
import { ref, toRaw } from "vue";
|
import { ref, toRaw } from "vue";
|
||||||
@@ -132,7 +133,7 @@ function open(data, index) {
|
|||||||
console.log("data", data);
|
console.log("data", data);
|
||||||
console.log("index", index);
|
console.log("index", index);
|
||||||
if (data && data.name) {
|
if (data && data.name) {
|
||||||
form.value = { ...data };
|
form.value = _.cloneDeep(toRaw(data));
|
||||||
isedit.value = true;
|
isedit.value = true;
|
||||||
dataIndex = index;
|
dataIndex = index;
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
Reference in New Issue
Block a user