no message

main
HUOJIN\92525 2025-12-19 18:06:39 +08:00
parent 7b2f3fe311
commit d308c16400
23 changed files with 1113 additions and 251 deletions

View File

@ -14,7 +14,7 @@
@focus="handleAsyncFocus" @focus="handleAsyncFocus"
@search="loadData" @search="loadData"
@change="handleAsyncChange" @change="handleAsyncChange"
@popupScroll="handlePopupScroll" @popup-scroll="handlePopupScroll"
:mode="multiple?'multiple':''" :mode="multiple?'multiple':''"
@select="handleSelect" @select="handleSelect"
@deselect="handleDeSelect" @deselect="handleDeSelect"

View File

@ -161,7 +161,6 @@
} }
async function setModalHeight(option?) { async function setModalHeight(option?) {
console.log("---------性能监控--------setModalHeight----------")
const options = option || {}; const options = option || {};
const source = options.source; const source = options.source;
const callBack = options.callBack; const callBack = options.callBack;

View File

@ -1,10 +1,11 @@
import { defHttp } from '/@/utils/http/axios'; import { defHttp } from '/@/utils/http/axios';
import { useMessage } from "/@/hooks/web/useMessage"; import { useMessage } from '/@/hooks/web/useMessage';
const { createConfirm } = useMessage(); const { createConfirm } = useMessage();
enum Api { enum Api {
list = '/base/item/list', list = '/base/item/list',
queryById = '/base/item/queryById',
save = '/base/item/add', save = '/base/item/add',
edit = '/base/item/edit', edit = '/base/item/edit',
deleteOne = '/base/item/delete', deleteOne = '/base/item/delete',
@ -30,6 +31,12 @@ export const getImportUrl = Api.importExcel;
*/ */
export const list = (params) => defHttp.get({ url: Api.list, params }); export const list = (params) => defHttp.get({ url: Api.list, params });
/**
* id
* @param params
*/
export const queryById = (params) => defHttp.get({ url: Api.queryById, params });
/** /**
* *
* @param params * @param params
@ -39,7 +46,7 @@ export const deleteOne = (params,handleSuccess) => {
return defHttp.delete({ url: Api.deleteOne, params }, { joinParamsToUrl: true }).then(() => { return defHttp.delete({ url: Api.deleteOne, params }, { joinParamsToUrl: true }).then(() => {
handleSuccess(); handleSuccess();
}); });
} };
/** /**
* *
@ -54,12 +61,20 @@ export const batchDelete = (params, handleSuccess) => {
okText: '确认', okText: '确认',
cancelText: '取消', cancelText: '取消',
onOk: () => { onOk: () => {
return defHttp.delete({url: Api.deleteBatch, data: params}, {joinParamsToUrl: true}).then(() => { return defHttp
.delete(
{
url: Api.deleteBatch,
data: params,
},
{ joinParamsToUrl: true }
)
.then(() => {
handleSuccess(); handleSuccess();
}); });
} },
}); });
} };
/** /**
* *
@ -67,6 +82,6 @@ export const batchDelete = (params, handleSuccess) => {
* @param isUpdate * @param isUpdate
*/ */
export const saveOrUpdate = (params, isUpdate) => { export const saveOrUpdate = (params, isUpdate) => {
let url = isUpdate ? Api.edit : Api.save; const url = isUpdate ? Api.edit : Api.save;
return defHttp.post({ url: url, params }, { isTransformResponse: false }); return defHttp.post({ url: url, params }, { isTransformResponse: false });
} };

View File

@ -0,0 +1,416 @@
<!-- 物料选择 -->
<template>
<a-select
v-model:value="selectedValue"
showSearch
:placeholder="placeholder"
:loading="loading"
:allowClear="true"
:filterOption="filterOption"
:notFoundContent="notFoundContent"
:mode="multiple ? 'multiple' : 'default'"
@change="handleChange"
@search="handleSearch"
@focus="handleFocus"
@popup-scroll="handlePopupScroll"
:getPopupContainer="getParentContainer"
v-bind="attrs"
>
<template #notFoundContent>
<a-spin v-if="loading" size="small" />
<span v-else></span>
</template>
<a-select-option v-for="option in Options" :key="option.id" :value="getOptionValue(option)">
{{ getOptionLabel(option) }}
</a-select-option>
</a-select>
</template>
<script lang="ts">
import { defineComponent, ref, watch, computed, onMounted } from 'vue';
import { useAttrs } from '/@/hooks/core/useAttrs';
import { propTypes } from '/@/utils/propTypes';
import { defHttp } from '/@/utils/http/axios';
import { queryById } from '@/views/base/item/Item.api';
import { setPopContainer } from '/@/utils';
import { debounce } from 'lodash-es';
//
interface Item {
id: string;
itemCode: string;
itemName: string;
}
//
interface ResponseData {
records: Item[];
total: number;
size: number;
current: number;
page: number;
}
export default defineComponent({
name: 'ItemSelect',
inheritAttrs: false,
props: {
// v-model
value: propTypes.oneOfType([propTypes.string, propTypes.array, propTypes.object]),
//
placeholder: propTypes.string.def('请选择物料'),
//
multiple: propTypes.bool.def(false),
//
async: propTypes.bool.def(true),
//
pageSize: propTypes.number.def(10),
//
popContainer: propTypes.string,
//
getPopupContainer: {
type: Function,
default: (node: HTMLElement) => node?.parentNode,
},
// change
immediateChange: propTypes.bool.def(false),
// : 'id'() | 'object' |
returnValue: propTypes.string.def('id'),
//
izActive: propTypes.number.def(1),
},
emits: ['change', 'update:value', 'optionsLoaded'],
setup(props, { emit }) {
const Options = ref<Item[]>([]);
const loading = ref<boolean>(false);
const allItems = ref<Item[]>([]);
const attrs = useAttrs({ excludeDefaultKeys: false });
//
const pageNo = ref(1);
const isHasData = ref(true);
const scrollLoading = ref(false);
const searchKeyword = ref('');
//
const selectedValue = ref<string | string[] | undefined>(undefined);
//
const notFoundContent = computed(() => {
return loading.value ? undefined : null;
});
/**
* 获取选项显示文本 - 始终显示完整格式
*/
function getOptionLabel(option: Item) {
return `${option.itemCode} - ${option.itemName}`;
}
/**
* 获取选项值 - 根据returnValue确定实际存储的值
*/
function getOptionValue(option: Item) {
if (props.returnValue === 'object') {
return option.id; // object使idchange
} else if (props.returnValue === 'id') {
return option.id;
} else {
return option[props.returnValue as keyof Item] as string;
}
}
/**
* 获取弹出层容器
*/
function getParentContainer(node: HTMLElement) {
if (props.popContainer) {
return setPopContainer(node, props.popContainer);
} else {
if (typeof props.getPopupContainer === 'function') {
return props.getPopupContainer(node);
} else {
return node?.parentNode;
}
}
}
/**
* 过滤选项 - 禁用前端过滤使用后端搜索
*/
function filterOption(_input: string, _option: any) {
return true; //
}
/**
* 确保物料唯一性的辅助函数
*/
function ensureUnique(items: Item[], newItem: Item): Item[] {
// id
const filtered = items.filter(item => item.id !== newItem.id);
return [newItem, ...filtered];
}
/**
* 获取物料数据
*/
const queryData = async (page = 1, keyword = '', isSearch = false) => {
try {
loading.value = true;
const res = await defHttp.get<ResponseData>({
url: '/base/item/list',
params: {
pageSize: props.pageSize,
pageNo: page,
keyword: keyword,
izActive: props.izActive,
},
});
const records = res.records || [];
if (page === 1 || isSearch) {
//
allItems.value = records;
Options.value = records;
} else {
//
const newRecords = records.filter(record =>
!allItems.value.some(item => item.id === record.id)
);
allItems.value = [...allItems.value, ...newRecords];
Options.value = [...Options.value, ...newRecords];
}
//
isHasData.value = records.length >= props.pageSize;
emit('optionsLoaded', allItems.value);
} catch (error) {
if (page === 1) {
allItems.value = [];
Options.value = [];
}
} finally {
loading.value = false;
scrollLoading.value = false;
}
};
async function queryDataById(value: string) {
try {
const res = await queryById({ id: value });
if (res) {
//
const item = Array.isArray(res) ? res[0] : res;
if (item) {
// 使ensureUnique
allItems.value = ensureUnique(allItems.value, item);
Options.value = ensureUnique(Options.value, item);
}
return item;
}
} catch (error) {
console.error('查询物料失败:', error);
}
return null;
}
/**
* 根据选项值找到对应的选项对象
*/
function findOptionByValue(value: string): Item | undefined {
if (props.returnValue === 'object' || props.returnValue === 'id') {
return allItems.value.find((item) => item.id === value);
} else {
return allItems.value.find((item) => item[props.returnValue as keyof Item] === value);
}
}
/**
* 获取需要返回的值
*/
function getReturnValue(value: string | string[]) {
if (!value) {
return props.multiple ? [] : undefined;
}
//
if (props.returnValue === 'object') {
if (Array.isArray(value)) {
return value.map((v) => findOptionByValue(v)).filter(Boolean);
} else {
return findOptionByValue(value);
}
}
// ID
else if (props.returnValue === 'id') {
return value;
}
//
else {
if (Array.isArray(value)) {
return value.map((v) => {
const option = findOptionByValue(v);
return option ? option[props.returnValue as keyof Item] : v;
});
} else {
const option = findOptionByValue(value);
return option ? option[props.returnValue as keyof Item] : value;
}
}
}
/**
* 搜索处理防抖
*/
const handleSearch = debounce(function (value: string) {
searchKeyword.value = value;
pageNo.value = 1;
isHasData.value = true;
// API
queryData(1, value, true);
}, 300);
/**
* 处理焦点事件
*/
function handleFocus() {
//
if (allItems.value.length === 0 && props.async) {
pageNo.value = 1;
isHasData.value = true;
queryData(1, '');
}
attrs.onFocus?.();
}
/**
* 处理值变化
*/
function handleChange(value: string | string[]) {
selectedValue.value = value;
//
const returnValue = getReturnValue(value);
emit('update:value', returnValue);
emit('change', returnValue);
}
/**
* 滚动加载处理
*/
function handlePopupScroll(e: Event) {
const target = e.target as HTMLElement;
const { scrollTop, scrollHeight, clientHeight } = target;
if (!scrollLoading.value && isHasData.value && scrollTop + clientHeight >= scrollHeight - 10) {
scrollLoading.value = true;
pageNo.value++;
queryData(pageNo.value, searchKeyword.value)
.finally(() => {
scrollLoading.value = false;
})
.catch(() => {
pageNo.value--;
});
}
}
/**
* 根据选中值初始化显示文本
*/
const initSelectValue = async () => {
if (!props.value) {
selectedValue.value = props.multiple ? [] : undefined;
return;
}
//
if (props.async && allItems.value.length === 0) {
await queryData();
}
// returnValue
let valueIds: string[] = [];
if (props.returnValue === 'object') {
// value
if (Array.isArray(props.value)) {
valueIds = props.value.map((item: any) => item.id);
selectedValue.value = valueIds;
} else {
valueIds = [(props.value as any).id];
selectedValue.value = valueIds[0];
}
} else if (props.returnValue === 'id') {
if (Array.isArray(props.value)) {
valueIds = props.value as string[];
selectedValue.value = valueIds;
} else {
valueIds = [props.value as string];
selectedValue.value = valueIds[0];
}
} else {
// 使
selectedValue.value = props.value as string | string[];
// ID
if (Array.isArray(props.value)) {
valueIds = props.value.map((v) => {
const option = allItems.value.find((item) => item[props.returnValue as keyof Item] === v);
return option ? option.id : v;
});
} else {
const option = allItems.value.find((item) => item[props.returnValue as keyof Item] === props.value);
valueIds = option ? [option.id] : [props.value as string];
}
}
//
const missingIds = valueIds.filter((id) => !allItems.value.some((item) => item.id === id));
if (missingIds.length > 0) {
// ID
const queryPromises = missingIds.map((id) => queryDataById(id));
await Promise.all(queryPromises);
}
};
// value
watch(
() => props.value,
() => {
initSelectValue();
},
{ immediate: true }
);
//
onMounted(() => {
if (!props.async) {
queryData();
}
});
return {
attrs,
Options,
loading,
selectedValue,
notFoundContent,
getParentContainer,
filterOption,
handleChange,
handleSearch,
handleFocus,
getOptionLabel,
getOptionValue,
handlePopupScroll,
};
},
});
</script>
<style lang="less" scoped></style>

View File

@ -5,6 +5,7 @@ const { createConfirm } = useMessage();
enum Api { enum Api {
list = '/base/point/list', list = '/base/point/list',
queryById='/base/point/queryById',
save='/base/point/add', save='/base/point/add',
edit='/base/point/edit', edit='/base/point/edit',
deleteOne = '/base/point/delete', deleteOne = '/base/point/delete',
@ -30,6 +31,13 @@ export const getImportUrl = Api.importExcel;
*/ */
export const list = (params) => defHttp.get({ url: Api.list, params }); export const list = (params) => defHttp.get({ url: Api.list, params });
/**
* id
* @param params
*/
export const queryById = (params) => defHttp.get({ url: Api.queryById, params });
/** /**
* *
* @param params * @param params
@ -67,6 +75,6 @@ export const batchDelete = (params, handleSuccess) => {
* @param isUpdate * @param isUpdate
*/ */
export const saveOrUpdate = (params, isUpdate) => { export const saveOrUpdate = (params, isUpdate) => {
let url = isUpdate ? Api.edit : Api.save; const url = isUpdate ? Api.edit : Api.save;
return defHttp.post({ url: url, params }, { isTransformResponse: false }); return defHttp.post({ url: url, params }, { isTransformResponse: false });
} }

View File

@ -92,8 +92,6 @@
import { columns } from './Point.data'; import { columns } from './Point.data';
import { list, deleteOne, batchDelete, saveOrUpdate, getImportUrl, getExportUrl } from './Point.api'; import { list, deleteOne, batchDelete, saveOrUpdate, getImportUrl, getExportUrl } from './Point.api';
import PointModal from './components/PointModal.vue'; import PointModal from './components/PointModal.vue';
import { useUserStore } from '/@/store/modules/user';
import { useMessage } from '/@/hooks/web/useMessage';
import { getDateByPicker } from '/@/utils'; import { getDateByPicker } from '/@/utils';
import { JInputTypeEnum } from '@/enums/cpteEnum'; import { JInputTypeEnum } from '@/enums/cpteEnum';
import JInput from '../../../components/Form/src/jeecg/components/JInput.vue'; import JInput from '../../../components/Form/src/jeecg/components/JInput.vue';
@ -106,8 +104,6 @@
const queryParam = reactive<any>({}); const queryParam = reactive<any>({});
const toggleSearchStatus = ref<boolean>(false); const toggleSearchStatus = ref<boolean>(false);
const registerModal = ref(); const registerModal = ref();
const userStore = useUserStore();
const { createMessage } = useMessage();
// //
const enhancedColumns = columns.map((col) => { const enhancedColumns = columns.map((col) => {
@ -135,7 +131,7 @@
}); });
//table //table
const { prefixCls, tableContext, onExportXls, onImportXls } = useListPage({ const { tableContext, onExportXls, onImportXls } = useListPage({
tableProps: { tableProps: {
title: '库位', title: '库位',
api: list, api: list,
@ -169,8 +165,7 @@
success: handleSuccess, success: handleSuccess,
}, },
}); });
const [registerTable, { reload, collapseAll, updateTableDataRecord, findTableDataRecord, getDataSource }, { rowSelection, selectedRowKeys }] = const [registerTable, { reload }, { rowSelection, selectedRowKeys }] = tableContext;
tableContext;
const labelCol = reactive({ const labelCol = reactive({
xs: 24, xs: 24,
sm: 4, sm: 4,

View File

@ -1,4 +1,4 @@
<!-- 库位下拉选择--> <!-- 库位选择 -->
<template> <template>
<a-select <a-select
v-model:value="selectedValue" v-model:value="selectedValue"
@ -12,7 +12,7 @@
@change="handleChange" @change="handleChange"
@search="handleSearch" @search="handleSearch"
@focus="handleFocus" @focus="handleFocus"
@popupScroll="handlePopupScroll" @popup-scroll="handlePopupScroll"
:getPopupContainer="getParentContainer" :getPopupContainer="getParentContainer"
v-bind="attrs" v-bind="attrs"
> >
@ -20,7 +20,7 @@
<a-spin v-if="loading" size="small" /> <a-spin v-if="loading" size="small" />
<span v-else></span> <span v-else></span>
</template> </template>
<a-select-option v-for="option in PointOptions" :key="option.id" :value="getOptionValue(option)"> <a-select-option v-for="option in Options" :key="option.id" :value="getOptionValue(option)">
{{ getOptionLabel(option) }} {{ getOptionLabel(option) }}
</a-select-option> </a-select-option>
</a-select> </a-select>
@ -31,6 +31,7 @@
import { useAttrs } from '/@/hooks/core/useAttrs'; import { useAttrs } from '/@/hooks/core/useAttrs';
import { propTypes } from '/@/utils/propTypes'; import { propTypes } from '/@/utils/propTypes';
import { defHttp } from '/@/utils/http/axios'; import { defHttp } from '/@/utils/http/axios';
import { queryById } from '@/views/base/point/Point.api';
import { setPopContainer } from '/@/utils'; import { setPopContainer } from '/@/utils';
import { debounce } from 'lodash-es'; import { debounce } from 'lodash-es';
@ -63,7 +64,7 @@
// //
async: propTypes.bool.def(true), async: propTypes.bool.def(true),
// //
pageSize: propTypes.number.def(20), pageSize: propTypes.number.def(10),
// //
popContainer: propTypes.string, popContainer: propTypes.string,
// //
@ -80,7 +81,7 @@
}, },
emits: ['change', 'update:value', 'optionsLoaded'], emits: ['change', 'update:value', 'optionsLoaded'],
setup(props, { emit }) { setup(props, { emit }) {
const PointOptions = ref<Point[]>([]); const Options = ref<Point[]>([]);
const loading = ref<boolean>(false); const loading = ref<boolean>(false);
const allPoints = ref<Point[]>([]); const allPoints = ref<Point[]>([]);
const attrs = useAttrs({ excludeDefaultKeys: false }); const attrs = useAttrs({ excludeDefaultKeys: false });
@ -141,10 +142,19 @@
return true; // return true; //
} }
/**
* 确保库位唯一性的辅助函数
*/
function ensureUnique(Points: Point[], newPoint: Point): Point[] {
// id
const filtered = Points.filter(Point => Point.id !== newPoint.id);
return [newPoint, ...filtered];
}
/** /**
* 获取库位数据 * 获取库位数据
*/ */
const fetchPoints = async (page = 1, keyword = '', isSearch = false) => { const queryData = async (page = 1, keyword = '', isSearch = false) => {
try { try {
loading.value = true; loading.value = true;
@ -158,29 +168,29 @@
}, },
}); });
console.log('获取库位数据成功:', res);
const records = res.records || []; const records = res.records || [];
if (page === 1 || isSearch) { if (page === 1 || isSearch) {
// //
allPoints.value = records; allPoints.value = records;
PointOptions.value = records; Options.value = records;
} else { } else {
// //
allPoints.value = [...allPoints.value, ...records]; const newRecords = records.filter(record =>
PointOptions.value = [...PointOptions.value, ...records]; !allPoints.value.some(Point => Point.id === record.id)
);
allPoints.value = [...allPoints.value, ...newRecords];
Options.value = [...Options.value, ...newRecords];
} }
// //
isHasData.value = records.length >= props.pageSize; isHasData.value = records.length >= props.pageSize;
console.log('是否还有更多数据:', records.length);
emit('optionsLoaded', allPoints.value); emit('optionsLoaded', allPoints.value);
} catch (error) { } catch (error) {
console.error('获取库位数据失败:', error);
if (page === 1) { if (page === 1) {
allPoints.value = []; allPoints.value = [];
PointOptions.value = []; Options.value = [];
} }
} finally { } finally {
loading.value = false; loading.value = false;
@ -188,14 +198,33 @@
} }
}; };
async function queryDataById(value: string) {
try {
const res = await queryById({ id: value });
if (res) {
//
const Point = Array.isArray(res) ? res[0] : res;
if (Point) {
// 使ensureUnique
allPoints.value = ensureUnique(allPoints.value, Point);
Options.value = ensureUnique(Options.value, Point);
}
return Point;
}
} catch (error) {
console.error('查询库位失败:', error);
}
return null;
}
/** /**
* 根据选项值找到对应的选项对象 * 根据选项值找到对应的选项对象
*/ */
function findOptionByValue(value: string): Point | undefined { function findOptionByValue(value: string): Point | undefined {
if (props.returnValue === 'object' || props.returnValue === 'id') { if (props.returnValue === 'object' || props.returnValue === 'id') {
return allPoints.value.find((item) => item.id === value); return allPoints.value.find((Point) => Point.id === value);
} else { } else {
return allPoints.value.find((item) => item[props.returnValue as keyof Point] === value); return allPoints.value.find((Point) => Point[props.returnValue as keyof Point] === value);
} }
} }
@ -242,7 +271,7 @@
isHasData.value = true; isHasData.value = true;
// API // API
fetchPoints(1, value, true); queryData(1, value, true);
}, 300); }, 300);
/** /**
@ -253,7 +282,7 @@
if (allPoints.value.length === 0 && props.async) { if (allPoints.value.length === 0 && props.async) {
pageNo.value = 1; pageNo.value = 1;
isHasData.value = true; isHasData.value = true;
fetchPoints(1, ''); queryData(1, '');
} }
attrs.onFocus?.(); attrs.onFocus?.();
} }
@ -266,7 +295,6 @@
// //
const returnValue = getReturnValue(value); const returnValue = getReturnValue(value);
console.log('值变化:', returnValue);
emit('update:value', returnValue); emit('update:value', returnValue);
emit('change', returnValue); emit('change', returnValue);
} }
@ -279,11 +307,10 @@
const { scrollTop, scrollHeight, clientHeight } = target; const { scrollTop, scrollHeight, clientHeight } = target;
if (!scrollLoading.value && isHasData.value && scrollTop + clientHeight >= scrollHeight - 10) { if (!scrollLoading.value && isHasData.value && scrollTop + clientHeight >= scrollHeight - 10) {
console.log('滚动加载更多');
scrollLoading.value = true; scrollLoading.value = true;
pageNo.value++; pageNo.value++;
fetchPoints(pageNo.value, searchKeyword.value) queryData(pageNo.value, searchKeyword.value)
.finally(() => { .finally(() => {
scrollLoading.value = false; scrollLoading.value = false;
}) })
@ -304,22 +331,50 @@
// //
if (props.async && allPoints.value.length === 0) { if (props.async && allPoints.value.length === 0) {
await fetchPoints(); await queryData();
} }
// returnValue // returnValue
let valueIds: string[] = [];
if (props.returnValue === 'object') { if (props.returnValue === 'object') {
// value // value
if (Array.isArray(props.value)) { if (Array.isArray(props.value)) {
selectedValue.value = props.value.map((item: any) => item.id); valueIds = props.value.map((Point: any) => Point.id);
selectedValue.value = valueIds;
} else { } else {
selectedValue.value = (props.value as any).id; valueIds = [(props.value as any).id];
selectedValue.value = valueIds[0];
} }
} else if (props.returnValue === 'id') { } else if (props.returnValue === 'id') {
selectedValue.value = props.value as string | string[]; if (Array.isArray(props.value)) {
valueIds = props.value as string[];
selectedValue.value = valueIds;
} else {
valueIds = [props.value as string];
selectedValue.value = valueIds[0];
}
} else { } else {
// 使 // 使
selectedValue.value = props.value as string | string[]; selectedValue.value = props.value as string | string[];
// ID
if (Array.isArray(props.value)) {
valueIds = props.value.map((v) => {
const option = allPoints.value.find((Point) => Point[props.returnValue as keyof Point] === v);
return option ? option.id : v;
});
} else {
const option = allPoints.value.find((Point) => Point[props.returnValue as keyof Point] === props.value);
valueIds = option ? [option.id] : [props.value as string];
}
}
//
const missingIds = valueIds.filter((id) => !allPoints.value.some((Point) => Point.id === id));
if (missingIds.length > 0) {
// ID
const queryPromises = missingIds.map((id) => queryDataById(id));
await Promise.all(queryPromises);
} }
}; };
@ -335,13 +390,13 @@
// //
onMounted(() => { onMounted(() => {
if (!props.async) { if (!props.async) {
fetchPoints(); queryData();
} }
}); });
return { return {
attrs, attrs,
PointOptions, Options,
loading, loading,
selectedValue, selectedValue,
notFoundContent, notFoundContent,

View File

@ -5,6 +5,7 @@ const { createConfirm } = useMessage();
enum Api { enum Api {
list = '/base/stock/list', list = '/base/stock/list',
queryById='/base/stock/queryById',
save='/base/stock/add', save='/base/stock/add',
edit='/base/stock/edit', edit='/base/stock/edit',
deleteOne = '/base/stock/delete', deleteOne = '/base/stock/delete',
@ -30,6 +31,12 @@ export const getImportUrl = Api.importExcel;
*/ */
export const list = (params) => defHttp.get({ url: Api.list, params }); export const list = (params) => defHttp.get({ url: Api.list, params });
/**
* id
* @param params
*/
export const queryById = (params) => defHttp.get({ url: Api.queryById, params });
/** /**
* *
* @param params * @param params

View File

@ -13,12 +13,7 @@
<a-col :lg="6"> <a-col :lg="6">
<a-form-item name="pointId"> <a-form-item name="pointId">
<template #label><span title="库位">库位</span></template> <template #label><span title="库位">库位</span></template>
<JSearchSelect <PointSelect v-model:value="queryParam.pointId" placeholder="请选择库位" />
v-model:value="queryParam.pointId"
placeholder="请选择库位"
dict="base_point where iz_active=1 and del_flag=0 ,point_code,id"
allowClear
/>
</a-form-item> </a-form-item>
</a-col> </a-col>
<a-col :lg="6"> <a-col :lg="6">
@ -99,7 +94,7 @@
import { JDictSelectTag, JSearchSelect } from '@/components/Form'; import { JDictSelectTag, JSearchSelect } from '@/components/Form';
import { JInputTypeEnum } from '@/enums/cpteEnum'; import { JInputTypeEnum } from '@/enums/cpteEnum';
import JInput from '../../../components/Form/src/jeecg/components/JInput.vue'; import JInput from '../../../components/Form/src/jeecg/components/JInput.vue';
import PointSelect from '@/views/base/point/components/PointSelect.vue';
const fieldPickers = reactive({}); const fieldPickers = reactive({});
const formRef = ref(); const formRef = ref();

View File

@ -27,12 +27,7 @@
</a-col> </a-col>
<a-col :span="24"> <a-col :span="24">
<a-form-item label="库位" v-bind="validateInfos.pointId" id="StockForm-pointId" name="pointId"> <a-form-item label="库位" v-bind="validateInfos.pointId" id="StockForm-pointId" name="pointId">
<JSearchSelect <PointSelect v-model:value="formData.pointId" />
v-model:value="formData.pointId"
placeholder="请选择库位"
dict="base_point where iz_active=1 and del_flag=0 ,point_code,id"
allowClear
/>
</a-form-item> </a-form-item>
</a-col> </a-col>
<a-col :span="24"> <a-col :span="24">
@ -60,8 +55,9 @@
import { Form } from 'ant-design-vue'; import { Form } from 'ant-design-vue';
import JFormContainer from '/@/components/Form/src/container/JFormContainer.vue'; import JFormContainer from '/@/components/Form/src/container/JFormContainer.vue';
import JSwitch from '@/components/Form/src/jeecg/components/JSwitch.vue'; import JSwitch from '@/components/Form/src/jeecg/components/JSwitch.vue';
import { JDictSelectTag, JSearchSelect } from '@/components/Form'; import { JDictSelectTag } from '@/components/Form';
import { getTenantId } from '@/utils/auth'; import { getTenantId } from '@/utils/auth';
import PointSelect from '@/views/base/point/components/PointSelect.vue';
const props = defineProps({ const props = defineProps({
formDisabled: { type: Boolean, default: false }, formDisabled: { type: Boolean, default: false },

View File

@ -0,0 +1,415 @@
<!-- 容器选择 -->
<template>
<a-select
v-model:value="selectedValue"
showSearch
:placeholder="placeholder"
:loading="loading"
:allowClear="true"
:filterOption="filterOption"
:notFoundContent="notFoundContent"
:mode="multiple ? 'multiple' : 'default'"
@change="handleChange"
@search="handleSearch"
@focus="handleFocus"
@popup-scroll="handlePopupScroll"
:getPopupContainer="getParentContainer"
v-bind="attrs"
>
<template #notFoundContent>
<a-spin v-if="loading" size="small" />
<span v-else></span>
</template>
<a-select-option v-for="option in Options" :key="option.id" :value="getOptionValue(option)">
{{ getOptionLabel(option) }}
</a-select-option>
</a-select>
</template>
<script lang="ts">
import { defineComponent, ref, watch, computed, onMounted } from 'vue';
import { useAttrs } from '/@/hooks/core/useAttrs';
import { propTypes } from '/@/utils/propTypes';
import { defHttp } from '/@/utils/http/axios';
import { queryById } from '@/views/base/stock/Stock.api';
import { setPopContainer } from '/@/utils';
import { debounce } from 'lodash-es';
//
interface Stock {
id: string;
stockCode: string;
}
//
interface ResponseData {
records: Stock[];
total: number;
size: number;
current: number;
page: number;
}
export default defineComponent({
name: 'StockSelect',
inheritAttrs: false,
props: {
// v-model
value: propTypes.oneOfType([propTypes.string, propTypes.array, propTypes.object]),
//
placeholder: propTypes.string.def('请选择容器'),
//
multiple: propTypes.bool.def(false),
//
async: propTypes.bool.def(true),
//
pageSize: propTypes.number.def(10),
//
popContainer: propTypes.string,
//
getPopupContainer: {
type: Function,
default: (node: HTMLElement) => node?.parentNode,
},
// change
immediateChange: propTypes.bool.def(false),
// : 'id'() | 'object' |
returnValue: propTypes.string.def('id'),
//
izActive: propTypes.number.def(1),
},
emits: ['change', 'update:value', 'optionsLoaded'],
setup(props, { emit }) {
const Options = ref<Stock[]>([]);
const loading = ref<boolean>(false);
const allStocks = ref<Stock[]>([]);
const attrs = useAttrs({ excludeDefaultKeys: false });
//
const pageNo = ref(1);
const isHasData = ref(true);
const scrollLoading = ref(false);
const searchKeyword = ref('');
//
const selectedValue = ref<string | string[] | undefined>(undefined);
//
const notFoundContent = computed(() => {
return loading.value ? undefined : null;
});
/**
* 获取选项显示文本 - 始终显示完整格式
*/
function getOptionLabel(option: Stock) {
return `${option.stockCode}`;
}
/**
* 获取选项值 - 根据returnValue确定实际存储的值
*/
function getOptionValue(option: Stock) {
if (props.returnValue === 'object') {
return option.id; // object使idchange
} else if (props.returnValue === 'id') {
return option.id;
} else {
return option[props.returnValue as keyof Stock] as string;
}
}
/**
* 获取弹出层容器
*/
function getParentContainer(node: HTMLElement) {
if (props.popContainer) {
return setPopContainer(node, props.popContainer);
} else {
if (typeof props.getPopupContainer === 'function') {
return props.getPopupContainer(node);
} else {
return node?.parentNode;
}
}
}
/**
* 过滤选项 - 禁用前端过滤使用后端搜索
*/
function filterOption(_input: string, _option: any) {
return true; //
}
/**
* 确保容器唯一性的辅助函数
*/
function ensureUnique(Stocks: Stock[], newStock: Stock): Stock[] {
// id
const filtered = Stocks.filter(Stock => Stock.id !== newStock.id);
return [newStock, ...filtered];
}
/**
* 获取容器数据
*/
const queryData = async (page = 1, keyword = '', isSearch = false) => {
try {
loading.value = true;
const res = await defHttp.get<ResponseData>({
url: '/base/stock/list',
params: {
pageSize: props.pageSize,
pageNo: page,
keyword: keyword,
izActive: props.izActive,
},
});
const records = res.records || [];
if (page === 1 || isSearch) {
//
allStocks.value = records;
Options.value = records;
} else {
//
const newRecords = records.filter(record =>
!allStocks.value.some(Stock => Stock.id === record.id)
);
allStocks.value = [...allStocks.value, ...newRecords];
Options.value = [...Options.value, ...newRecords];
}
//
isHasData.value = records.length >= props.pageSize;
emit('optionsLoaded', allStocks.value);
} catch (error) {
if (page === 1) {
allStocks.value = [];
Options.value = [];
}
} finally {
loading.value = false;
scrollLoading.value = false;
}
};
async function queryDataById(value: string) {
try {
const res = await queryById({ id: value });
if (res) {
//
const Stock = Array.isArray(res) ? res[0] : res;
if (Stock) {
// 使ensureUnique
allStocks.value = ensureUnique(allStocks.value, Stock);
Options.value = ensureUnique(Options.value, Stock);
}
return Stock;
}
} catch (error) {
console.error('查询容器失败:', error);
}
return null;
}
/**
* 根据选项值找到对应的选项对象
*/
function findOptionByValue(value: string): Stock | undefined {
if (props.returnValue === 'object' || props.returnValue === 'id') {
return allStocks.value.find((Stock) => Stock.id === value);
} else {
return allStocks.value.find((Stock) => Stock[props.returnValue as keyof Stock] === value);
}
}
/**
* 获取需要返回的值
*/
function getReturnValue(value: string | string[]) {
if (!value) {
return props.multiple ? [] : undefined;
}
//
if (props.returnValue === 'object') {
if (Array.isArray(value)) {
return value.map((v) => findOptionByValue(v)).filter(Boolean);
} else {
return findOptionByValue(value);
}
}
// ID
else if (props.returnValue === 'id') {
return value;
}
//
else {
if (Array.isArray(value)) {
return value.map((v) => {
const option = findOptionByValue(v);
return option ? option[props.returnValue as keyof Stock] : v;
});
} else {
const option = findOptionByValue(value);
return option ? option[props.returnValue as keyof Stock] : value;
}
}
}
/**
* 搜索处理防抖
*/
const handleSearch = debounce(function (value: string) {
searchKeyword.value = value;
pageNo.value = 1;
isHasData.value = true;
// API
queryData(1, value, true);
}, 300);
/**
* 处理焦点事件
*/
function handleFocus() {
//
if (allStocks.value.length === 0 && props.async) {
pageNo.value = 1;
isHasData.value = true;
queryData(1, '');
}
attrs.onFocus?.();
}
/**
* 处理值变化
*/
function handleChange(value: string | string[]) {
selectedValue.value = value;
//
const returnValue = getReturnValue(value);
emit('update:value', returnValue);
emit('change', returnValue);
}
/**
* 滚动加载处理
*/
function handlePopupScroll(e: Event) {
const target = e.target as HTMLElement;
const { scrollTop, scrollHeight, clientHeight } = target;
if (!scrollLoading.value && isHasData.value && scrollTop + clientHeight >= scrollHeight - 10) {
scrollLoading.value = true;
pageNo.value++;
queryData(pageNo.value, searchKeyword.value)
.finally(() => {
scrollLoading.value = false;
})
.catch(() => {
pageNo.value--;
});
}
}
/**
* 根据选中值初始化显示文本
*/
const initSelectValue = async () => {
if (!props.value) {
selectedValue.value = props.multiple ? [] : undefined;
return;
}
//
if (props.async && allStocks.value.length === 0) {
await queryData();
}
// returnValue
let valueIds: string[] = [];
if (props.returnValue === 'object') {
// value
if (Array.isArray(props.value)) {
valueIds = props.value.map((Stock: any) => Stock.id);
selectedValue.value = valueIds;
} else {
valueIds = [(props.value as any).id];
selectedValue.value = valueIds[0];
}
} else if (props.returnValue === 'id') {
if (Array.isArray(props.value)) {
valueIds = props.value as string[];
selectedValue.value = valueIds;
} else {
valueIds = [props.value as string];
selectedValue.value = valueIds[0];
}
} else {
// 使
selectedValue.value = props.value as string | string[];
// ID
if (Array.isArray(props.value)) {
valueIds = props.value.map((v) => {
const option = allStocks.value.find((Stock) => Stock[props.returnValue as keyof Stock] === v);
return option ? option.id : v;
});
} else {
const option = allStocks.value.find((Stock) => Stock[props.returnValue as keyof Stock] === props.value);
valueIds = option ? [option.id] : [props.value as string];
}
}
//
const missingIds = valueIds.filter((id) => !allStocks.value.some((Stock) => Stock.id === id));
if (missingIds.length > 0) {
// ID
const queryPromises = missingIds.map((id) => queryDataById(id));
await Promise.all(queryPromises);
}
};
// value
watch(
() => props.value,
() => {
initSelectValue();
},
{ immediate: true }
);
//
onMounted(() => {
if (!props.async) {
queryData();
}
});
return {
attrs,
Options,
loading,
selectedValue,
notFoundContent,
getParentContainer,
filterOption,
handleChange,
handleSearch,
handleFocus,
getOptionLabel,
getOptionValue,
handlePopupScroll,
};
},
});
</script>
<style lang="less" scoped></style>

View File

@ -31,7 +31,7 @@
<main class="dashboard-card"> <main class="dashboard-card">
<section class="scan-section"> <section class="scan-section">
<div class="section-header"> <div class="section-header">
<span class="label-cn">当前扫描</span> <span class="label-cn">当前托盘</span>
<span class="label-en">CURRENT SCAN</span> <span class="label-en">CURRENT SCAN</span>
</div> </div>
<div class="scan-content"> <div class="scan-content">
@ -123,7 +123,7 @@
}); });
async function queryData() { async function queryData() {
const conveyorLine = 'CKJBK01'; const conveyorLine = 'ST102';
const res = await showConveyorLine(conveyorLine); const res = await showConveyorLine(conveyorLine);
scanData.value = { scanData.value = {
stockCode: res.stockCode, stockCode: res.stockCode,

View File

@ -1,4 +1,5 @@
import { BasicColumn } from '/@/components/Table'; import { BasicColumn } from '/@/components/Table';
import { render } from '@/utils/common/renderUtils';
//列表数据 //列表数据
export const columns: BasicColumn[] = [ export const columns: BasicColumn[] = [
{ {
@ -36,7 +37,19 @@ export const columns: BasicColumn[] = [
align: 'center', align: 'center',
dataIndex: 'status_dictText', dataIndex: 'status_dictText',
width: 100, width: 100,
customRender: ({ text }) => {
const statusColorMap = {
: 'red',
: 'green',
: 'orange',
: 'cyan',
: 'blue',
: 'purple',
: 'pink',
};
const color = statusColorMap[text] || 'red';
return render.renderTag(text, color);
},
}, },
{ {
title: '外部仓库', title: '外部仓库',

View File

@ -7,34 +7,19 @@
<a-col :lg="6"> <a-col :lg="6">
<a-form-item name="itemId"> <a-form-item name="itemId">
<template #label><span title="物料">物料</span></template> <template #label><span title="物料">物料</span></template>
<JSearchSelect <ItemSelect v-model:value="queryParam.itemId" />
v-model:value="queryParam.itemId"
placeholder="请选择物料"
dict="base_item where iz_active=1 and del_flag=0 ,item_code,id"
allowClear
/>
</a-form-item> </a-form-item>
</a-col> </a-col>
<a-col :lg="6"> <a-col :lg="6">
<a-form-item name="pointId"> <a-form-item name="pointId">
<template #label><span title="库位">库位</span></template> <template #label><span title="库位">库位</span></template>
<JSearchSelect <PointSelect v-model:value="queryParam.pointId" />
v-model:value="queryParam.pointId"
placeholder="请选择库位"
dict="base_point where iz_active=1 and del_flag=0 ,point_code,id"
allowClear
/>
</a-form-item> </a-form-item>
</a-col> </a-col>
<a-col :lg="6"> <a-col :lg="6">
<a-form-item name="stockId"> <a-form-item name="stockId">
<template #label><span title="容器">容器</span></template> <template #label><span title="容器">容器</span></template>
<JSearchSelect <StockSelect v-model:value="queryParam.stockId" />
v-model:value="queryParam.stockId"
placeholder="请选择容器"
dict="base_stock where iz_active=1 and del_flag=0 ,stock_code,id"
allowClear
/>
</a-form-item> </a-form-item>
</a-col> </a-col>
<a-col :xl="6" :lg="7" :md="8" :sm="24"> <a-col :xl="6" :lg="7" :md="8" :sm="24">
@ -52,7 +37,7 @@
</a-row> </a-row>
<a-row :gutter="24"> <a-row :gutter="24">
<a-col :lg="6"> <a-col :lg="6">
<a-form-item name="status"> <a-form-item name="status" v-if="toggleSearchStatus">
<template #label><span title="库存状态">库存状态</span></template> <template #label><span title="库存状态">库存状态</span></template>
<JDictSelectTag v-model:value="queryParam.status" placeholder="请选择" dictCode="inventory_status" allowClear /> <JDictSelectTag v-model:value="queryParam.status" placeholder="请选择" dictCode="inventory_status" allowClear />
</a-form-item> </a-form-item>
@ -108,6 +93,9 @@
import { useMessage } from '/@/hooks/web/useMessage'; import { useMessage } from '/@/hooks/web/useMessage';
import { getDateByPicker } from '/@/utils'; import { getDateByPicker } from '/@/utils';
import { JDictSelectTag, JSearchSelect } from '@/components/Form'; import { JDictSelectTag, JSearchSelect } from '@/components/Form';
import ItemSelect from '@/views/base/item/components/ItemSelect.vue';
import PointSelect from '@/views/base/point/components/PointSelect.vue';
import StockSelect from '@/views/base/stock/components/StockSelect.vue';
const fieldPickers = reactive({}); const fieldPickers = reactive({});

View File

@ -6,32 +6,17 @@
<a-row class="form-row" :gutter="24"> <a-row class="form-row" :gutter="24">
<a-col :span="8"> <a-col :span="8">
<a-form-item label="物料" v-bind="validateInfos.itemId" id="InventoryForm-itemId" name="itemId"> <a-form-item label="物料" v-bind="validateInfos.itemId" id="InventoryForm-itemId" name="itemId">
<JSearchSelect <ItemSelect v-model:value="formData.itemId" />
v-model:value="formData.itemId"
placeholder="请选择物料"
dict="base_item where iz_active=1 and del_flag=0 ,item_code,id"
allowClear
/>
</a-form-item> </a-form-item>
</a-col> </a-col>
<a-col :span="8"> <a-col :span="8">
<a-form-item label="库位" v-bind="validateInfos.pointId" id="InventoryForm-pointId" name="pointId"> <a-form-item label="库位" v-bind="validateInfos.pointId" id="InventoryForm-pointId" name="pointId">
<JSearchSelect <PointSelect v-model:value="formData.pointId" />
v-model:value="formData.pointId"
placeholder="请选择库位"
dict="base_point where iz_active=1 and del_flag=0 ,point_code,id"
allowClear
/>
</a-form-item> </a-form-item>
</a-col> </a-col>
<a-col :span="8"> <a-col :span="8">
<a-form-item label="容器" v-bind="validateInfos.stockId" id="InventoryForm-stockId" name="stockId"> <a-form-item label="容器" v-bind="validateInfos.stockId" id="InventoryForm-stockId" name="stockId">
<JSearchSelect <StockSelect v-model:value="formData.stockId" />
v-model:value="formData.stockId"
placeholder="请选择容器"
dict="base_stock where iz_active=1 and del_flag=0 ,stock_code,id"
allowClear
/>
</a-form-item> </a-form-item>
</a-col> </a-col>
</a-row> </a-row>
@ -41,9 +26,32 @@
<a-input-number v-model:value="formData.quantity" placeholder="请输入数量" style="width: 100%" /> <a-input-number v-model:value="formData.quantity" placeholder="请输入数量" style="width: 100%" />
</a-form-item> </a-form-item>
</a-col> </a-col>
<a-col :span="8">
<a-form-item label="占用数" v-bind="validateInfos.quantity" id="InventoryForm-queuedQty" name="queuedQty">
<a-input-number v-model:value="formData.queuedQty" placeholder="请输入占用数" style="width: 100%" />
</a-form-item>
</a-col>
<a-col :span="8"> <a-col :span="8">
<a-form-item label="库存状态" v-bind="validateInfos.status" id="InventoryForm-status" name="status"> <a-form-item label="库存状态" v-bind="validateInfos.status" id="InventoryForm-status" name="status">
<JDictSelectTag v-model:value="formData.status" placeholder="请选择" dictCode="inventory_status" string-to-number="true" allowClear /> <JDictSelectTag
v-model:value="formData.status"
placeholder="请选择"
dictCode="inventory_status"
:string-to-number="true"
allowClear
/>
</a-form-item>
</a-col>
</a-row>
<a-row class="form-row" :gutter="24">
<a-col :span="8">
<a-form-item label="项目号" v-bind="validateInfos.project" id="InventoryForm-project" name="project">
<a-input v-model:value="formData.project" placeholder="请输入项目号" allow-clear></a-input>
</a-form-item>
</a-col>
<a-col :span="8">
<a-form-item label="任务号" v-bind="validateInfos.taskNo" id="InventoryForm-taskNo" name="taskNo">
<a-input v-model:value="formData.taskNo" placeholder="请输入任务号" allow-clear></a-input>
</a-form-item> </a-form-item>
</a-col> </a-col>
<a-col :span="8"> <a-col :span="8">
@ -63,17 +71,6 @@
<a-input v-model:value="formData.propC3" placeholder="请输入外部库存状态" allow-clear></a-input> <a-input v-model:value="formData.propC3" placeholder="请输入外部库存状态" allow-clear></a-input>
</a-form-item> </a-form-item>
</a-col> </a-col>
<a-col :span="8">
<a-form-item label="生产日期" v-bind="validateInfos.propD1" id="InventoryForm-propD1" name="propD1">
<a-date-picker
placeholder="请选择生产日期"
v-model:value="formData.propD1"
value-format="YYYY-MM-DD"
style="width: 100%"
allow-clear
/>
</a-form-item>
</a-col>
</a-row> </a-row>
<a-row class="form-row" :gutter="24"> <a-row class="form-row" :gutter="24">
<a-col :span="24"> <a-col :span="24">
@ -103,7 +100,10 @@
import { saveOrUpdate } from '../Inventory.api'; import { saveOrUpdate } from '../Inventory.api';
import { Form } from 'ant-design-vue'; import { Form } from 'ant-design-vue';
import JFormContainer from '/@/components/Form/src/container/JFormContainer.vue'; import JFormContainer from '/@/components/Form/src/container/JFormContainer.vue';
import { JDictSelectTag, JSearchSelect } from '@/components/Form'; import { JDictSelectTag } from '@/components/Form';
import ItemSelect from '@/views/base/item/components/ItemSelect.vue';
import PointSelect from '@/views/base/point/components/PointSelect.vue';
import StockSelect from '@/views/base/stock/components/StockSelect.vue';
const props = defineProps({ const props = defineProps({
formDisabled: { type: Boolean, default: false }, formDisabled: { type: Boolean, default: false },
@ -117,17 +117,17 @@
let tenantId = getTenantId(); let tenantId = getTenantId();
const formData = reactive<Record<string, any>>({ const formData = reactive<Record<string, any>>({
id: '', id: '',
itemId: undefined, itemId: '',
pointId: undefined, pointId: '',
stockId: undefined, stockId: '',
quantity: '', quantity: '',
queuedQty: 0, queuedQty: 0,
whCode: '', whCode: '',
project: '',
taskNo: '',
propC1: '', propC1: '',
propC2: '',
propC3: '', propC3: '',
status: '', status: 1,
propD1: '',
description: '', description: '',
tenantId: tenantId, tenantId: tenantId,
}); });

View File

@ -1,4 +1,14 @@
<template><j-modal :title="title" :maxHeight="500" :width="800" :visible="visible" @ok="handleOk" :okButtonProps="{ class: { 'jee-hidden': disableSubmit } }" @cancel="handleCancel" cancelText="关闭"> <template>
<j-modal
:title="title"
:maxHeight="600"
:width="900"
:visible="visible"
@ok="handleOk"
:okButtonProps="{ class: { 'jee-hidden': disableSubmit } }"
@cancel="handleCancel"
cancelText="关闭"
>
<InventoryForm ref="registerForm" @ok="submitCallback" :formDisabled="disableSubmit" :formBpm="false"></InventoryForm> <InventoryForm ref="registerForm" @ok="submitCallback" :formDisabled="disableSubmit" :formBpm="false"></InventoryForm>
<template #footer> <template #footer>
<a-button @click="handleCancel"></a-button> <a-button @click="handleCancel"></a-button>
@ -9,10 +19,9 @@
<script lang="ts" setup> <script lang="ts" setup>
import { ref, nextTick, defineExpose } from 'vue'; import { ref, nextTick, defineExpose } from 'vue';
import InventoryForm from './InventoryForm.vue' import InventoryForm from './InventoryForm.vue';
import JModal from '/@/components/Modal/src/JModal/JModal.vue'; import JModal from '/@/components/Modal/src/JModal/JModal.vue';
import { useMessage } from '/@/hooks/web/useMessage';
const { createMessage } = useMessage();
const title = ref<string>(''); const title = ref<string>('');
const visible = ref<boolean>(false); const visible = ref<boolean>(false);
const disableSubmit = ref<boolean>(false); const disableSubmit = ref<boolean>(false);
@ -63,6 +72,7 @@
function handleCancel() { function handleCancel() {
visible.value = false; visible.value = false;
} }
defineExpose({ defineExpose({
add, add,
edit, edit,

View File

@ -60,35 +60,5 @@ export const columns: BasicColumn[] = [
align: 'center', align: 'center',
dataIndex: 'afterAllocatedQty', dataIndex: 'afterAllocatedQty',
width: 120, width: 120,
}, }
{
title: '项目号',
align: 'center',
dataIndex: 'project',
width: 100,
},
{
title: '任务号',
align: 'center',
dataIndex: 'taskNo',
width: 100,
},
{
title: '批次号',
align: 'center',
dataIndex: 'propC1',
width: 100,
},
{
title: '外部库存状态',
align: 'center',
dataIndex: 'propC3',
width: 100,
},
{
title: '备注',
align: 'center',
dataIndex: 'description',
ellipsis: true,
},
]; ];

View File

@ -7,34 +7,19 @@
<a-col :lg="6"> <a-col :lg="6">
<a-form-item name="itemId"> <a-form-item name="itemId">
<template #label><span title="物料">物料</span></template> <template #label><span title="物料">物料</span></template>
<JSearchSelect <ItemSelect v-model:value="queryParam.itemId" />
v-model:value="queryParam.itemId"
placeholder="请选择物料"
dict="base_item where iz_active=1 and del_flag=0 ,item_code,id"
allowClear
/>
</a-form-item> </a-form-item>
</a-col> </a-col>
<a-col :lg="6"> <a-col :lg="6">
<a-form-item name="pointId"> <a-form-item name="pointId">
<template #label><span title="库位">库位</span></template> <template #label><span title="库位">库位</span></template>
<JSearchSelect <PointSelect v-model:value="queryParam.pointId" />
v-model:value="queryParam.pointId"
placeholder="请选择库位"
dict="base_point where iz_active=1 and del_flag=0 ,point_code,id"
allowClear
/>
</a-form-item> </a-form-item>
</a-col> </a-col>
<a-col :lg="6"> <a-col :lg="6">
<a-form-item name="stockId"> <a-form-item name="stockId">
<template #label><span title="容器">容器</span></template> <template #label><span title="容器">容器</span></template>
<JSearchSelect <StockSelect v-model:value="queryParam.stockId" />
v-model:value="queryParam.stockId"
placeholder="请选择容器"
dict="base_stock where iz_active=1 and del_flag=0 ,stock_code,id"
allowClear
/>
</a-form-item> </a-form-item>
</a-col> </a-col>
<a-col :xl="6" :lg="7" :md="8" :sm="24"> <a-col :xl="6" :lg="7" :md="8" :sm="24">
@ -52,7 +37,7 @@
</a-row> </a-row>
<a-row :gutter="24"> <a-row :gutter="24">
<a-col :lg="6"> <a-col :lg="6">
<a-form-item name="logType"> <a-form-item name="logType" v-if="toggleSearchStatus">
<template #label><span title="日志类型">日志类型</span></template> <template #label><span title="日志类型">日志类型</span></template>
<JDictSelectTag v-model:value="queryParam.logType" placeholder="请选择" dictCode="inventory_log_type" allowClear /> <JDictSelectTag v-model:value="queryParam.logType" placeholder="请选择" dictCode="inventory_log_type" allowClear />
</a-form-item> </a-form-item>
@ -112,10 +97,11 @@
import { columns } from './InventoryLog.data'; import { columns } from './InventoryLog.data';
import { list, deleteOne, batchDelete, getImportUrl, getExportUrl } from './InventoryLog.api'; import { list, deleteOne, batchDelete, getImportUrl, getExportUrl } from './InventoryLog.api';
import InventoryLogModal from './components/InventoryLogModal.vue'; import InventoryLogModal from './components/InventoryLogModal.vue';
import { useUserStore } from '/@/store/modules/user';
import { useMessage } from '/@/hooks/web/useMessage';
import { getDateByPicker } from '/@/utils'; import { getDateByPicker } from '/@/utils';
import { JDictSelectTag, JSearchSelect } from '@/components/Form'; import { JDictSelectTag } from '@/components/Form';
import ItemSelect from '@/views/base/item/components/ItemSelect.vue';
import PointSelect from '@/views/base/point/components/PointSelect.vue';
import StockSelect from '@/views/base/stock/components/StockSelect.vue';
const fieldPickers = reactive({}); const fieldPickers = reactive({});
@ -123,10 +109,8 @@
const queryParam = reactive<any>({}); const queryParam = reactive<any>({});
const toggleSearchStatus = ref<boolean>(false); const toggleSearchStatus = ref<boolean>(false);
const registerModal = ref(); const registerModal = ref();
const userStore = useUserStore();
const { createMessage } = useMessage();
//table //table
const { prefixCls, tableContext, onExportXls, onImportXls } = useListPage({ const { tableContext, onExportXls, onImportXls } = useListPage({
tableProps: { tableProps: {
title: '库存日志', title: '库存日志',
api: list, api: list,
@ -161,8 +145,7 @@
success: handleSuccess, success: handleSuccess,
}, },
}); });
const [registerTable, { reload, collapseAll, updateTableDataRecord, findTableDataRecord, getDataSource }, { rowSelection, selectedRowKeys }] = const [registerTable, { reload }, { rowSelection, selectedRowKeys }] = tableContext;
tableContext;
const labelCol = reactive({ const labelCol = reactive({
xs: 24, xs: 24,
sm: 4, sm: 4,

View File

@ -27,13 +27,14 @@ export const columns: BasicColumn[] = [
dataIndex: 'status_dictText', dataIndex: 'status_dictText',
width: '80px', width: '80px',
customRender: ({ text }) => { customRender: ({ text }) => {
//入库状态:1.已创建;2.部分收货;3.收货完成;4.已关闭,5.已取消 //入库状态:1.已创建;2.部分收货;3.收货完成;4.已关闭,5.已取消、6.已扫描
const statusColorMap = { const statusColorMap = {
: 'orange', : 'orange',
: 'blue', : 'blue',
: 'green', : 'green',
: 'green', : 'green',
: 'red', : 'red',
: 'purple',
}; };
const color = statusColorMap[text] || 'red'; const color = statusColorMap[text] || 'red';
return render.renderTag(text, color); return render.renderTag(text, color);
@ -74,8 +75,8 @@ export const columns: BasicColumn[] = [
align: 'center', align: 'center',
dataIndex: 'resMessage', dataIndex: 'resMessage',
customRender: ({ text }) => { customRender: ({ text }) => {
return render.renderTip(text) return render.renderTip(text);
} },
}, },
{ {
title: '订单日期', title: '订单日期',
@ -181,7 +182,6 @@ export const asnDetailColumns: JVxeColumn[] = [
3: '收货完成', 3: '收货完成',
4: '已关闭', 4: '已关闭',
5: '已取消', 5: '已取消',
}; };
// 状态颜色映射 // 状态颜色映射
const statusColorMap = { const statusColorMap = {

View File

@ -4,12 +4,6 @@
<div class="jeecg-basic-table-form-container"> <div class="jeecg-basic-table-form-container">
<a-form ref="formRef" @keyup.enter.native="reload" :model="queryParam" :label-col="labelCol" :wrapper-col="wrapperCol"> <a-form ref="formRef" @keyup.enter.native="reload" :model="queryParam" :label-col="labelCol" :wrapper-col="wrapperCol">
<a-row :gutter="24"> <a-row :gutter="24">
<a-col :lg="6">
<a-form-item name="orderNo">
<template #label><span title="系统单号">系统单号</span></template>
<JInput v-model:value="queryParam.orderNo" :placeholder="'请输入系统单号'" :type="JInputTypeEnum.JINPUT_RIGHT_LIKE" />
</a-form-item>
</a-col>
<a-col :lg="6"> <a-col :lg="6">
<a-form-item name="no"> <a-form-item name="no">
<template #label><span title="任务号">任务号</span></template> <template #label><span title="任务号">任务号</span></template>
@ -22,6 +16,12 @@
<JDictSelectTag v-model:value="queryParam.orderType" placeholder="请选择" dictCode="asn_order_type" allowClear /> <JDictSelectTag v-model:value="queryParam.orderType" placeholder="请选择" dictCode="asn_order_type" allowClear />
</a-form-item> </a-form-item>
</a-col> </a-col>
<a-col :lg="6">
<a-form-item name="orderDate">
<template #label><span title="订单日期">订单日期</span></template>
<JRangeDate v-model:value="queryParam.orderDate" />
</a-form-item>
</a-col>
<a-col :xl="6" :lg="7" :md="8" :sm="24"> <a-col :xl="6" :lg="7" :md="8" :sm="24">
<span style="float: left; overflow: hidden" class="table-page-search-submitButtons"> <span style="float: left; overflow: hidden" class="table-page-search-submitButtons">
<a-col :lg="6"> <a-col :lg="6">
@ -36,18 +36,13 @@
</a-col> </a-col>
</a-row> </a-row>
<a-row :gutter="24"> <a-row :gutter="24">
<a-col :lg="6"> <!-- <a-col :lg="6">
<a-form-item name="status"> <a-form-item name="status">
<template #label><span title="状态">状态</span></template> <template #label><span title="状态">状态</span></template>
<JDictSelectTag v-model:value="queryParam.status" placeholder="请选择" dictCode="asn_status" allowClear /> <JDictSelectTag v-model:value="queryParam.status" placeholder="请选择" dictCode="asn_status" allowClear />
</a-form-item> </a-form-item>
</a-col> </a-col>-->
<a-col :lg="6">
<a-form-item name="orderDate">
<template #label><span title="订单日期">订单日期</span></template>
<JRangeDate v-model:value="queryParam.orderDate" />
</a-form-item>
</a-col>
</a-row> </a-row>
</a-form> </a-form>
</div> </div>

View File

@ -221,24 +221,37 @@ export const taskColumns: JVxeColumn[] = [
{ {
title: '物料', title: '物料',
key: 'itemId', key: 'itemId',
type: JVxeTypes.normal, type: JVxeTypes.selectDictSearch,
disabled: true,
async: true, // 异步搜索,默认为 true async: true, // 异步搜索,默认为 true
//查询状态启用、未删除的物料 dict: 'base_item,item_code,id',
dict: 'base_item where iz_active=1 and del_flag=0,item_code,id',
tipsContent: '请搜索物料',
}, },
{ {
title: '单位', title: '原库位',
key: 'unit', key: 'fromPointId',
type: JVxeTypes.normal, type: JVxeTypes.selectDictSearch,
dictCode: 'package_unit', disabled: true,
placeholder: '请选择${title}', async: true, // 异步搜索,默认为 true
defaultValue: '托', dict: 'base_point,point_code,id',
},
{
title: '目标库位',
key: 'toPointId',
type: JVxeTypes.selectDictSearch,
disabled: true,
async: true, // 异步搜索,默认为 true
dict: 'base_point,point_code,id',
},
{
title: '分配数量',
key: 'planQty',
type: JVxeTypes.normal,
defaultValue: '0',
disabled: true,
}, },
{ {
title: '拣货数量', title: '拣货数量',
key: 'planQty', key: 'moveQty',
type: JVxeTypes.normal, type: JVxeTypes.normal,
defaultValue: '0', defaultValue: '0',
disabled: true, disabled: true,
@ -247,6 +260,5 @@ export const taskColumns: JVxeColumn[] = [
title: '返回报文', title: '返回报文',
key: 'resMessage', key: 'resMessage',
type: JVxeTypes.normal, type: JVxeTypes.normal,
width: '200px', }
},
]; ];

View File

@ -4,12 +4,6 @@
<div class="jeecg-basic-table-form-container"> <div class="jeecg-basic-table-form-container">
<a-form ref="formRef" @keyup.enter.native="reload" :model="queryParam" :label-col="labelCol" :wrapper-col="wrapperCol"> <a-form ref="formRef" @keyup.enter.native="reload" :model="queryParam" :label-col="labelCol" :wrapper-col="wrapperCol">
<a-row :gutter="24"> <a-row :gutter="24">
<a-col :lg="6">
<a-form-item name="orderNo">
<template #label><span title="系统单号">系统单号</span></template>
<JInput v-model:value="queryParam.orderNo" :placeholder="'请输入系统单号'" :type="JInputTypeEnum.JINPUT_RIGHT_LIKE" />
</a-form-item>
</a-col>
<a-col :lg="6"> <a-col :lg="6">
<a-form-item name="no"> <a-form-item name="no">
<template #label><span title="任务号">任务号</span></template> <template #label><span title="任务号">任务号</span></template>
@ -22,6 +16,12 @@
<JDictSelectTag v-model:value="queryParam.orderType" placeholder="请选择" dictCode="pick_order_type" allowClear /> <JDictSelectTag v-model:value="queryParam.orderType" placeholder="请选择" dictCode="pick_order_type" allowClear />
</a-form-item> </a-form-item>
</a-col> </a-col>
<a-col :lg="6">
<a-form-item name="orderDate">
<template #label><span title="订单日期">订单日期</span></template>
<JRangeDate v-model:value="queryParam.orderDate" />
</a-form-item>
</a-col>
<a-col :xl="6" :lg="7" :md="8" :sm="24"> <a-col :xl="6" :lg="7" :md="8" :sm="24">
<span style="float: left; overflow: hidden" class="table-page-search-submitButtons"> <span style="float: left; overflow: hidden" class="table-page-search-submitButtons">
<a-col :lg="6"> <a-col :lg="6">
@ -39,13 +39,7 @@
<a-col :lg="6"> <a-col :lg="6">
<a-form-item name="status"> <a-form-item name="status">
<template #label><span title="状态">状态</span></template> <template #label><span title="状态">状态</span></template>
<JDictSelectTag v-model:value="queryParam.status" placeholder="请选择" dictCode="pick_status" allowClear /> <JDictSelectTag v-model:value="queryParam.status" placeholder="请选择" dictCode="pick_status" mode="multiple" allowClear />
</a-form-item>
</a-col>
<a-col :lg="6">
<a-form-item name="orderDate">
<template #label><span title="订单日期">订单日期</span></template>
<JRangeDate v-model:value="queryParam.orderDate" />
</a-form-item> </a-form-item>
</a-col> </a-col>
</a-row> </a-row>
@ -116,7 +110,6 @@
import { list, deleteOne, batchDelete, getImportUrl, getExportUrl, allocatePick, cancelAllocate } from './Pick.api'; import { list, deleteOne, batchDelete, getImportUrl, getExportUrl, allocatePick, cancelAllocate } from './Pick.api';
import { useMessage } from '/@/hooks/web/useMessage'; import { useMessage } from '/@/hooks/web/useMessage';
import { getDateByPicker } from '/@/utils'; import { getDateByPicker } from '/@/utils';
import { useUserStore } from '/@/store/modules/user';
import { JInputTypeEnum } from '@/enums/cpteEnum'; import { JInputTypeEnum } from '@/enums/cpteEnum';
import { JInput, JDictSelectTag } from '@/components/Form'; import { JInput, JDictSelectTag } from '@/components/Form';
import JRangeDate from '@/components/Form/src/jeecg/components/JRangeDate.vue'; import JRangeDate from '@/components/Form/src/jeecg/components/JRangeDate.vue';
@ -126,13 +119,11 @@
const fieldPickers = reactive({}); const fieldPickers = reactive({});
const formRef = ref(); const formRef = ref();
const queryParam = reactive<any>({}); const queryParam = reactive<any>({});
const checkedKeys = ref<Array<string | number>>([]);
//model //model
const [registerModal, { openModal }] = useModal(); const [registerModal, { openModal }] = useModal();
const userStore = useUserStore();
const { createMessage } = useMessage(); const { createMessage } = useMessage();
//table //table
const { prefixCls, tableContext, onExportXls, onImportXls } = useListPage({ const { tableContext, onExportXls, onImportXls } = useListPage({
tableProps: { tableProps: {
title: '出库单', title: '出库单',
api: list, api: list,
@ -212,11 +203,10 @@
await allocatePick(selectedRowKeys.value, handleSuccess); await allocatePick(selectedRowKeys.value, handleSuccess);
} catch (e) { } catch (e) {
console.error('分配失败:', e); console.error('分配失败:', e);
handleSuccess() handleSuccess();
} finally { } finally {
// //
allocate_loading.value = false; allocate_loading.value = false;
} }
} }
@ -243,7 +233,7 @@
await cancelAllocate(selectedRowKeys.value, handleSuccess); await cancelAllocate(selectedRowKeys.value, handleSuccess);
} catch (e) { } catch (e) {
console.error('取消失败:', e); console.error('取消失败:', e);
handleSuccess() handleSuccess();
} finally { } finally {
// //
cancel_loading.value = false; cancel_loading.value = false;

View File

@ -73,8 +73,8 @@
<a-tab-pane tab="出库明细" key="pickDetail" :forceRender="true"> <a-tab-pane tab="出库明细" key="pickDetail" :forceRender="true">
<j-vxe-table <j-vxe-table
:keep-source="true" :keep-source="true"
resizable
ref="pickDetailTableRef" ref="pickDetailTableRef"
resizable
:loading="pickDetailTable.loading" :loading="pickDetailTable.loading"
:columns="pickDetailTable.columns" :columns="pickDetailTable.columns"
:dataSource="pickDetailTable.dataSource" :dataSource="pickDetailTable.dataSource"
@ -107,7 +107,7 @@
<script lang="ts"> <script lang="ts">
import { defineComponent, ref, reactive, computed, toRaw } from 'vue'; import { defineComponent, ref, reactive, computed, toRaw } from 'vue';
import { useValidateAntFormAndTable } from '/@/hooks/system/useJvxeMethods'; import { useValidateAntFormAndTable } from '/@/hooks/system/useJvxeMethods';
import { queryPickDetailListByMainId, queryDataById, saveOrUpdate } from '../Pick.api'; import { queryPickDetailListByMainId, queryDataById, saveOrUpdate,queryTaskByMainId } from '../Pick.api';
import { JVxeTable } from '/@/components/jeecg/JVxeTable'; import { JVxeTable } from '/@/components/jeecg/JVxeTable';
import { pickDetailColumns, taskColumns } from '../Pick.data'; import { pickDetailColumns, taskColumns } from '../Pick.data';
import JFormContainer from '/@/components/Form/src/container/JFormContainer.vue'; import JFormContainer from '/@/components/Form/src/container/JFormContainer.vue';