93242dd899ff4a7e40b8304660cb1eac01871db6..09009377b44b914effba2538421dbc09b1218af6
2025-12-26 skyouc
库位初始化优化
090093 对比 | 目录
2025-12-25 skyouc
出库添加一键完结功能
c45c8e 对比 | 目录
2025-12-25 skyouc
no message
db67c5 对比 | 目录
12个文件已修改
235 ■■■■ 已修改文件
rsf-admin/src/i18n/en.js 5 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/i18n/zh.js 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/page/orders/asnOrder/AsnOrderList.jsx 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/page/orders/outStock/OutOrderList.jsx 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/page/system/menu/MenuList.jsx 87 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/page/warehouseAreas/WarehouseAreasCreate.jsx 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/com/vincent/rsf/server/manager/controller/LocController.java 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/com/vincent/rsf/server/manager/controller/OutStockController.java 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/com/vincent/rsf/server/manager/controller/WarehouseController.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/com/vincent/rsf/server/manager/service/OutStockService.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/com/vincent/rsf/server/manager/service/impl/LocServiceImpl.java 55 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/com/vincent/rsf/server/manager/service/impl/OutStockServiceImpl.java 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/i18n/en.js
@@ -217,7 +217,7 @@
        locDeadReport: 'Locs Dead Report',
        stockStatistic: 'Stock Statistic',
        statisticCount: 'Statistic Count',
        preparation: "备料单",
    },
    table: {
        field: {
@@ -1364,6 +1364,9 @@
        selectWave: 'Select Wave Rule',
    },
    placeholder: {
        warehouseAreasCode: "use for warehouse areas code prefix",
    },
    request: {
        error: {
            stock: "Insufficient inventory to deliver !!",
rsf-admin/src/i18n/zh.js
@@ -1381,6 +1381,9 @@
        modiftySite: '修改库口',
        selectWave: '波次规则',
    },
    placeholder: {
        warehouseAreasCode: "用于库位编码前缀占位符",
    },
    request: {
        error: {
            stock: "库存不足,无法提交!!",
rsf-admin/src/page/orders/asnOrder/AsnOrderList.jsx
@@ -406,8 +406,6 @@
    // </Button> : )
    record.exceStatus === 1 ? <ConfirmButton label={"toolbar.complete"} color="secondary" startIcon={<TaskIcon />} onConfirm={requestComplete} /> : <></>
  )
}
const CloseButton = () => {
rsf-admin/src/page/orders/outStock/OutOrderList.jsx
@@ -55,6 +55,7 @@
import PublicIcon from '@mui/icons-material/Public';
import SelectMatnrModal from "./SelectMatnrModal";
import EditIcon from '@mui/icons-material/Edit';
import TaskIcon from '@mui/icons-material/Task';
import OutOrderPreview from "./OutOrderPreview";
import AddIcon from '@mui/icons-material/Add';
import OutStockPublic from "./OutStockPublic";
@@ -195,6 +196,7 @@
            <MyButton setCreateDialog={setManualDialog} setmodalType={setmodalType} />
            <EditButton label="toolbar.detail" icon={(<DetailsIcon />)}></EditButton>
            <CancelButton />
            <CompleteButton />
            <PublicButton setDrawerVal={setDrawerVal} drawerVal={drawerVal} setSelect={setSelect} />
          </WrapperField>
        </StyledDatagrid>
@@ -251,6 +253,29 @@
  );
}
//完成单据
const CompleteButton = () => {
  const record = useRecordContext();
  const notify = useNotify();
  const refresh = useRefresh();
  const requestComplete = async () => {
    const { data: { code, data, msg } } = await request.get(`/outStock/complete/${record.id}`);
    if (code === 200) {
      notify(msg);
      refresh()
    } else {
      notify(msg);
    }
  }
  return (
    // record.exceStatus === 1 && (record.anfme === record.qty ? <Button onClick={requestComplete} label={"toolbar.complete"} color="secondary">
    //   <TaskIcon />
    // </Button> : )
    record.exceStatus != 15 ? <ConfirmButton label={"toolbar.complete"} color="secondary" startIcon={<TaskIcon />} onConfirm={requestComplete} /> : <></>
  )
}
const MyButton = ({ setCreateDialog, setmodalType }) => {
  const record = useRecordContext();
  const handleEditClick = (btn) => {
rsf-admin/src/page/system/menu/MenuList.jsx
@@ -127,6 +127,8 @@
    textOverflow: 'ellipsis',
    whiteSpace: 'nowrap',
    maxWidth: 600,
    // 确保所有单元格有基本的内边距
    padding: '8px 16px',
}));
const TreeTableRow = (props) => {
@@ -139,15 +141,41 @@
    const isOpen = openNodes[row.id] || false;
    // 更明显的透明度渐变
    const getOpacity = (currentDepth) => {
        // 第一级:100%,第二级:75%,第三级:50%,第四级:40%
        const opacities = [1, 0.9, 0.8, 0.7];
        return opacities[currentDepth] || 0.4;
    };
    const opacity = getOpacity(depth);
    return (
        <React.Fragment>
            <StyledTableRow hover tabIndex={-1} key={row.id}>
                <StyledTableCell sx={{ padding: 0 }}>
                    {row.children && (
            <StyledTableRow
                hover
                tabIndex={-1}
                key={row.id}
                sx={{
                    opacity: depth > 0 ? opacity : 1,
                    // 添加背景色渐变增强效果
                    backgroundColor: depth > 0 ? `rgba(0, 0, 0, ${0.02 * (3 - depth)})` : 'inherit',
                }}
            >
                <StyledTableCell sx={{
                    padding: 0,
                    width: 60,
                    // 进一步减小缩进距离到12px
                    paddingLeft: depth * 4
                }}>
                    {row.children && row.children.length > 0 && (
                        <IconButton
                            aria-label="expand row"
                            size="small"
                            onClick={() => toggleNode(row.id)}
                            sx={{
                                opacity: depth > 0 ? opacity : 1,
                            }}
                        >
                            {isOpen ? <KeyboardArrowDownIcon /> : <KeyboardArrowRightIcon />}
                        </IconButton>
@@ -164,10 +192,19 @@
                                key={column.id}
                                align={column.align || 'left'}
                                style={{
                                    paddingLeft: idx === 0 && (depth * 16 + 16),
                                    opacity: column.id === 'icon' && .6
                                    // 名称列也使用12px缩进
                                    paddingLeft: idx === 0 ? (depth * 24 + 16) : 16,
                                    // opacity: column.id === 'icon' && .6
                                }}
                                onClick={() => toggleNode(row.id)}
                                onClick={() => column.id === 'name' && toggleNode(row.id)}
                                sx={{
                                    opacity: column.id === 'icon' ? 0.6 : (depth > 0 ? opacity : 1),
                                    fontWeight: 400,
                                    // 使用字体大小或颜色来区分层级
                                    fontSize: depth === 0 ? '0.95rem' : '0.9rem',
                                    // 或者使用不同的颜色
                                    color: depth === 0 ? 'text.primary' : `rgba(0, 0, 0, ${opacity})`,
                                }}
                            >
                                {column.format ? column.format(value) : value}
                            </StyledTableCell>
@@ -176,18 +213,30 @@
                })}
                <StyledTableCell>
                    <Tooltip title="Edit">
                        <IconButton onClick={() => onEdit(row)}>
                        <IconButton
                            onClick={() => onEdit(row)}
                            sx={{
                                opacity: depth > 0 ? opacity : 1,
                            }}
                            size="small"
                        >
                            <Edit />
                        </IconButton>
                    </Tooltip>
                    <Tooltip title="Delete">
                        <IconButton onClick={() => onDelete(row)}>
                        <IconButton
                            onClick={() => onDelete(row)}
                            sx={{
                                opacity: depth > 0 ? opacity : 1,
                            }}
                            size="small"
                        >
                            <Delete />
                        </IconButton>
                    </Tooltip>
                </StyledTableCell>
            </StyledTableRow>
            {row.children && isOpen && (
            {row.children && row.children.length > 0 && isOpen && (
                row.children.map((child) => (
                    <TreeTableRow
                        key={child.id}
@@ -217,6 +266,14 @@
    const [editRecord, setEditRecord] = React.useState(null);
    const [openNodes, setOpenNodes] = React.useState({});
    const [expandAll, setExpandAll] = React.useState(false);
    const notifyState = React.useRef({ last: '', at: 0 });
    const pushNotify = React.useCallback((type, msg) => {
        const text = typeof msg === 'string' ? msg : (msg || '');
        const now = Date.now();
        if (notifyState.current.last === text && now - notifyState.current.at < 1500) return;
        notifyState.current = { last: text, at: now };
        notify(text, { type, messageArgs: { _: text } });
    }, [notify]);
    const http = async () => {
        const res = await request.post(RESOURCE + '/tree', {
@@ -225,7 +282,8 @@
        if (res?.data?.code === 200) {
            setTreeData(res.data.data);
        } else {
            notify(res.data.msg);
            const msg = translate('ra.notification.http_error', { _: res?.data?.msg || 'Request failed' });
            pushNotify('warning', msg);
        }
    }
@@ -255,10 +313,12 @@
                {
                    onSuccess: () => {
                        handleRefresh();
                        notify('Department deleted successfully', { type: 'info', messageArgs: { _: 'Department deleted successfully' } });
                        const msg = translate('ra.message.delete_success', { _: 'Deleted successfully' });
                        pushNotify('success', msg);
                    },
                    onError: (error) => {
                        notify(`Error: ${error.message}`, { type: 'warning', messageArgs: { _: `Error: ${error.message}` } });
                        const msg = translate('ra.notification.http_error', { _: error?.message || 'Network error' });
                        pushNotify('error', msg);
                    },
                }
            );
@@ -270,6 +330,7 @@
            const newExpandAll = !prevExpandAll;
            const newOpenNodes = {};
            const updateOpenNodes = (nodes) => {
                if (!nodes) return;
                nodes.forEach(node => {
                    newOpenNodes[node.id] = newExpandAll;
                    if (node.children) {
@@ -392,4 +453,4 @@
    );
};
export default MenuList;
export default MenuList;
rsf-admin/src/page/warehouseAreas/WarehouseAreasCreate.jsx
@@ -103,11 +103,12 @@
                    source="code"
                    validate={[required()]}
                    parse={(v) => v}
                    placeholder={translate("placeholder.warehouseAreasCode")}
                  />
                  <AutocompleteInput
                    choices={dicts}
                    optionText="label"
                    label="table.field.asnOrder.type"
                    label="table.field.warehouseAreas.type"
                    source="type"
                    optionValue="value"
                    parse={v => v}
rsf-server/src/main/java/com/vincent/rsf/server/manager/controller/LocController.java
@@ -1,8 +1,6 @@
package com.vincent.rsf.server.manager.controller;
import com.alibaba.fastjson.JSONArray;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.vincent.rsf.framework.common.Cools;
import com.vincent.rsf.framework.common.R;
@@ -19,7 +17,6 @@
import com.vincent.rsf.server.manager.service.LocService;
import com.vincent.rsf.server.system.controller.BaseController;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiModelProperty;
import io.swagger.annotations.ApiOperation;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
@@ -29,7 +26,6 @@
import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;
import javax.validation.constraints.NotNull;
import java.util.*;
import java.util.stream.Collectors;
@@ -127,10 +123,13 @@
    public R update(@RequestBody Loc loc) {
        loc.setUpdateBy(getLoginUserId());
        String join = StringUtils.join(loc.getTypeIds(), ",");
        loc.setType(join);
        if (Objects.isNull(loc.getTypeIds())) {
            throw new CoolException("库位类型不能为空!!");
        if (!Objects.isNull(loc.getTypeIds())) {
            loc.setType(join);
        }
        if (Objects.isNull(loc.getTypeIds()) && !Objects.isNull(loc.getType())) {
            loc.setTypeIds(Arrays.asList(Long.valueOf(loc.getType())));
        }
        if (!locService.updateById(loc)) {
            return R.error("Update Fail");
        }
@@ -209,9 +208,6 @@
        }
        if (Objects.isNull(param.getTypeIds()) && param.getTypeIds().isEmpty()) {
            return R.error("库位类型不能为空!!");
        }
        if (Objects.isNull(param.getWarehouseId())) {
            return R.error("仓库ID不能为空!!");
        }
        if (Objects.isNull(param.getAreaId())) {
            return R.error("库区ID不能为空!!");
rsf-server/src/main/java/com/vincent/rsf/server/manager/controller/OutStockController.java
@@ -256,6 +256,17 @@
        return outStockService.saveOutStock(params, getLoginUserId());
    }
    @GetMapping("/outStock/complete/{id}")
    @ApiOperation("完成出库单")
    @PreAuthorize("hasAuthority('manager:outStock:update')")
    public R complete(@PathVariable Long id) {
        if (Objects.isNull(id)) {
            return R.error("参数不能为空!!");
        }
        return outStockService.completeOutOrderById(id);
    }
    @ApiOperation("单据信息修改")
    @PostMapping("/outStock/items/update")
    @PreAuthorize("hasAuthority('manager:outStock:update')")
rsf-server/src/main/java/com/vincent/rsf/server/manager/controller/WarehouseController.java
@@ -104,18 +104,17 @@
        Warehouse warehouse1 = warehouseService.getById(warehouse.getId());
        if (!warehouse.getName().equals(warehouse1.getName())) {
            List<Warehouse> areasList = warehouseService.list(new LambdaQueryWrapper<Warehouse>().eq(Warehouse::getName, warehouse1.getName()));
            List<Warehouse> areasList = warehouseService.list(new LambdaQueryWrapper<Warehouse>().eq(Warehouse::getName, warehouse.getName()));
            if (!areasList.isEmpty()) {
                throw new CoolException("仓库名已存在!!");
            }
        }
        if (!warehouse.getCode().equals(warehouse1.getCode())) {
            List<Warehouse> areasList = warehouseService.list(new LambdaQueryWrapper<Warehouse>().eq(Warehouse::getCode, warehouse1.getCode()));
            List<Warehouse> areasList = warehouseService.list(new LambdaQueryWrapper<Warehouse>().eq(Warehouse::getCode, warehouse.getCode()));
            if (!areasList.isEmpty()) {
                throw new CoolException("仓库编码已存在!!");
            }
        }
        if (!warehouseService.updateById(warehouse)) {
            return R.error("Update Fail");
rsf-server/src/main/java/com/vincent/rsf/server/manager/service/OutStockService.java
@@ -32,4 +32,6 @@
    R cancelOutOrderByItems(List<WkOrderItem> orderItems);
    R completeOutOrderById(Long id);
}
rsf-server/src/main/java/com/vincent/rsf/server/manager/service/impl/LocServiceImpl.java
@@ -60,7 +60,7 @@
        String join = loc.getType();
        if (!Objects.isNull(loc.getTypeIds()) && !loc.getTypeIds().isEmpty()) {
             join = StringUtils.join(loc.getTypeIds(), ",");
            join = StringUtils.join(loc.getTypeIds(), ",");
        }
        boolean update = this.update(new LambdaUpdateWrapper<Loc>()
                .in(Loc::getId, locs.getId())
@@ -85,25 +85,29 @@
    @Override
    @Transactional(rollbackFor = Exception.class)
    public R initLocs(LocMastInitParam param, Long loginUserId) {
        //清空表
//        this.baseMapper.deleteAll();
        //清空当前库区库位
        List<Loc> locs = this.list(new LambdaQueryWrapper<Loc>().eq(Loc::getWarehouseId, param.getWarehouseId()).eq(Loc::getAreaId, param.getAreaId()));
        if (!locs.isEmpty()) {
            if (!this.remove(new LambdaQueryWrapper<Loc>().eq(Loc::getWarehouseId, param.getWarehouseId()).eq(Loc::getAreaId, param.getAreaId()))) {
                throw new CoolException("初始化失败:数据删除失败!!");
            }
        }
        WarehouseAreas warehouseAreas = warehouseAreasService.getOne(new LambdaQueryWrapper<WarehouseAreas>()
                .eq(WarehouseAreas::getWarehouseId, param.getWarehouseId())
                .eq(WarehouseAreas::getId, param.getAreaId()));
                .eq(!Objects.isNull(param.getWarehouseId()), WarehouseAreas::getWarehouseId, param.getWarehouseId())
                .eq(!Objects.isNull(param.getAreaId()), WarehouseAreas::getId, param.getAreaId()));
        if (Objects.isNull(warehouseAreas)) {
            throw new CoolException("库区不存在!!");
        }
        // 清空表
        // this.baseMapper.deleteAll();
        // 清空当前库区库位
        List<Loc> locs = this.list(new LambdaQueryWrapper<Loc>().eq(Loc::getWarehouseId, warehouseAreas.getWarehouseId())
                .eq(Loc::getAreaId, warehouseAreas.getId()));
        if (!locs.isEmpty()) {
            if (!this.remove(new LambdaQueryWrapper<Loc>().eq(Loc::getWarehouseId, warehouseAreas.getWarehouseId())
                    .eq(Loc::getAreaId, warehouseAreas.getId()))) {
                throw new CoolException("初始化失败:数据删除失败!!");
            }
        }
        String join = StringUtils.join(param.getTypeIds(), ",");
        String[] split = warehouseAreas.getCode().split("\\.");
        if (split.length == 0){
        if (split.length == 0) {
            throw new CoolException("库区编码错误!!");
        }
        StringBuilder locStar = new StringBuilder();
@@ -112,27 +116,28 @@
        }
        List<Loc> list = new ArrayList<>();
        Integer chanl = 0; //默认第一巷道
        Integer chanl = 0; // 默认第一巷道
        for (int r = param.getStartRow(); r <= param.getEndRow(); r++) {
            try{
                Shelves shelves = new Shelves(param.getEndRow() - param.getStartRow() + 1, param.getChannel(),param.getStartRow());
                for (List<Integer> node : shelves.nodes){
            try {
                Shelves shelves = new Shelves(param.getEndRow() - param.getStartRow() + 1, param.getChannel(),
                        param.getStartRow());
                for (List<Integer> node : shelves.nodes) {
                    if (node.contains(r)) {
                        if (!Cools.isEmpty(param.getStartChannel()) && param.getStartChannel() > 0){
                        if (!Cools.isEmpty(param.getStartChannel()) && param.getStartChannel() > 0) {
                            chanl = shelves.nodes.indexOf(node) + param.getStartChannel();
                        }else {
                        } else {
                            chanl = shelves.nodes.indexOf(node) + 1;
                        }
                        break;
                    }
                }
            }catch (Exception e){
            } catch (Exception e) {
                throw new CoolException("解析巷道号失败");
            }
            for (int b = param.getStartBay(); b <= param.getEndBay(); b++) {
                for (int l = param.getStartLev(); l <= param.getEndLev(); l++) {
                    // 获取库位号
                    String locNo = locStar +  String.format("%d", r) + String.format("-%d", b) + String.format("-%d", l);
                    String locNo = locStar + String.format("%d", r) + String.format("-%d", b) + String.format("-%d", l);
                    Loc loc = new Loc();
                    loc.setCode(locNo)
                            .setUseStatus("O")
@@ -169,7 +174,7 @@
    @Override
    public boolean saveType(Loc loc) {
        List<LocTypeRela> relas =  new ArrayList<>();
        List<LocTypeRela> relas = new ArrayList<>();
        loc.getTypeIds().forEach(id -> {
            LocTypeRela typeRela = new LocTypeRela();
            typeRela.setTypeId(id)
@@ -185,7 +190,7 @@
    @Override
    public boolean updateLoc(Loc loc) {
        locTypeRelaMapper.delete(new LambdaQueryWrapper<LocTypeRela>().eq(LocTypeRela::getLocId, loc.getId()));
        List<LocTypeRela> relas =  new ArrayList<>();
        List<LocTypeRela> relas = new ArrayList<>();
        loc.getTypeIds().forEach(id -> {
            LocTypeRela typeRela = new LocTypeRela();
            typeRela.setTypeId(id)
@@ -206,7 +211,8 @@
        if (Objects.isNull(locs.getLocAreaId())) {
            throw new CoolException("修改库位信息不能为空!!");
        }
        List<LocArea> locAreas = locAreaService.list(new LambdaQueryWrapper<LocArea>().eq(LocArea::getId, locs.getLocAreaId()));
        List<LocArea> locAreas = locAreaService
                .list(new LambdaQueryWrapper<LocArea>().eq(LocArea::getId, locs.getLocAreaId()));
        if (locAreas.isEmpty()) {
            throw new CoolException("库位分区不存在!!");
        }
@@ -226,6 +232,7 @@
    /**
     * 获取库位使用情况
     *
     * @return
     */
    @Override
rsf-server/src/main/java/com/vincent/rsf/server/manager/service/impl/OutStockServiceImpl.java
@@ -728,4 +728,25 @@
        return waveItems;
    }
    /**
     * @param id
     * @return
     * @author Ryan
     * @description 完成出库单
     * @time 2025/4/25 10:07
     */
    @Override
    public R completeOutOrderById(Long id) {
        WkOrder order = this.getById(id);
        if (Objects.isNull(order)) {
            return R.error("出库单不存在!!");
        }
        order.setExceStatus(AsnExceStatus.OUT_STOCK_STATUS_TASK_DONE.val);
        if (!this.updateById(order)) {
            throw new CoolException("完成出库单失败!!");
        }
         return R.ok("完成出库单成功!!");
    }
}