货道关联商品
需求
对智能售货机内部的货道进行商品摆放的管理
此功能涉及四个后端接口
-
查询设备类型(已完成)
-
查询货道列表(待完成)
-
查询商品列表(已完成)
-
货道关联商品(待完成)
货道对话框
① 从资料中复制货道api请求js文件到api/manage目录下
② 从资料中复制货道的视图组件到views/manage/vm目录下
具体代码如下:
idenx.scss
@import '@/assets/styles/variables.module.scss';
:deep(.el-dialog) {
.vm-config-channel-dialog-wrapper,
.vm-select-sku-dialog-wrapper {
.scrollbar {
width: 814px;
height: 384px;
margin: 0 auto;
.el-scrollbar__wrap {
scroll-behavior: smooth;
}
.el-row {
flex-wrap: nowrap;
}
.el-col-12 {
width: 50%;
flex: 0;
}
.el-scrollbar__bar.is-horizontal {
display: none;
}
}
}
}
.vm-config-channel-dialog-wrapper,
.vm-select-sku-dialog-wrapper {
position: relative;
width: 847px;
margin: 0 auto;
.channel-basic {
display: flex;
align-items: center;
width: 847px;
height: 56px;
margin-bottom: 16px;
background: $--color-function3;
.vm-row {
margin-left: 43px;
}
.vm-col {
margin-left: 55px;
}
.channel-max-capacity {
flex: 1;
margin-left: 54px;
}
.business-top10 {
margin-right: 22px;
}
}
.space {
margin-bottom: 20px;
}
// TODO: 样式和vm-select-sku-dialog冗余了
.arrow {
position: absolute;
top: 50%;
width: 50px !important;
height: 50px !important;
color: $--color-black;
cursor: pointer;
}
.disabled {
color: $--border-color-base;
cursor: auto;
}
.arrow-left {
left: -45px;
}
.arrow-right {
right: -45px;
}
}
.vm-select-sku-dialog-wrapper {
width: 750px;
.scrollbar {
width: 750px;
height: auto;
.el-row {
display: flex;
flex-wrap: wrap;
}
:deep(.el-scrollbar__bar.is-horizontal) {
display: none;
}
}
.sku {
position: relative;
width: 134px;
height: 134px;
padding-top: 16px;
background-color: #f6f7fb;
-webkit-box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.06);
box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.06);
border-radius: 4px;
text-align: center;
cursor: pointer;
.selected {
position: absolute;
top: 0;
left: 0;
}
.img {
display: inline-block;
width: 83px;
height: 84px;
margin-bottom: 5px;
object-fit: contain;
}
}
.el-col-5 {
width: 20%;
flex: 0;
}
.el-col-24 {
flex: none;
margin-right: 16px;
}
}
channel.js
import request from '@/utils/request';
// 查询货道列表
export function getGoodsList(innerCode) {
return request({
url: '/manage/channel/list/' + innerCode,
method: 'get',
});
}
// 查询设备类型
export function getGoodsType(typeId) {
return request({
url: '/manage/vmType/' + typeId,
method: 'get',
});
}
// 提交获取的货道
export function channelConfig(data) {
return request({
url: '/manage/channel/config',
method: 'put',
data: data,
});
}
components:下的两个ChannelDialog.vue===》ChannelDialogItem.vue
ChannelDialog.vue
<template>
<!-- 货道弹层 -->
<el-dialog
width="940px"
title="货道设置"
v-model="visible"
:close-on-click-modal="false"
:close-on-press-escape="false"
@open="handleGoodOpen"
@close="handleGoodcClose"
>
<div class="vm-config-channel-dialog-wrapper">
<div class="channel-basic">
<span class="vm-row">货道行数:{{ vmType.vmRow }}</span>
<span class="vm-col">货道列数:{{ vmType.vmCol }}</span>
<span class="channel-max-capacity"
>货道容量(个):{{ vmType.channelMaxCapacity }}</span
>
</div>
<el-scrollbar ref="scroll" v-loading="listLoading" class="scrollbar">
<el-row
v-for="vmRowIndex in vmType.vmRow"
:key="vmRowIndex"
type="flex"
:gutter="16"
class="space"
>
<el-col
v-for="vmColIndex in vmType.vmCol"
:key="vmColIndex"
:span="vmType.vmCol <= 5 ? 5 : 12"
>
<ChannelDialogItem
:current-index="computedCurrentIndex(vmRowIndex, vmColIndex)"
:channel="channels[computedCurrentIndex(vmRowIndex, vmColIndex)]"
@openSetSkuDialog="openSetSkuDialog"
@openRemoveSkuDialog="openRemoveSkuDialog"
>
</ChannelDialogItem>
</el-col>
</el-row>
</el-scrollbar>
<el-icon
v-if="vmType.vmCol > 5"
class="arrow arrow-left"
:class="scrollStatus === 'LEFT' ? 'disabled' : ''"
@click="handleClickPrevButton"
><ArrowLeft
/></el-icon>
<el-icon
v-if="vmType.vmCol > 5"
class="arrow arrow-right"
:class="scrollStatus === 'RIGHT' ? 'disabled' : ''"
@click="handleClickNextButton"
><ArrowRight
/></el-icon>
</div>
<div class="dialog-footer">
<el-button
type="primary"
class="el-button--primary1"
@click="handleClick"
>
确认
</el-button>
</div>
<!-- 商品选择 -->
<el-dialog
width="858px"
title="选择商品"
v-model="skuDialogVisible"
:close-on-click-modal="false"
:close-on-press-escape="false"
append-to-body
@open="handleListOpen"
@close="handleListClose"
>
<div class="vm-select-sku-dialog-wrapper">
<!-- 搜索区 -->
<el-form
ref="form"
class="search"
:model="listQuery"
:label-width="formLabelWidth"
>
<el-form-item label="商品名称:">
<el-row type="flex" justify="space-between">
<el-col>
<el-input
v-model="listQuery.skuName"
placeholder="请输入"
clearable
class="sku-name"
@input="resetPageIndex"
/>
</el-col>
<el-col>
<el-button
type="primary"
class="el-button--primary1"
@click="handleListOpen"
>
<el-icon><Search /></el-icon> 查询
</el-button>
</el-col>
</el-row>
</el-form-item>
</el-form>
<el-scrollbar
ref="scroll2"
v-loading="listSkuLoading"
class="scrollbar"
>
<el-row v-loading="listSkuLoading" :gutter="20">
<el-col
v-for="(item, index) in listSkuData.rows"
:key="index"
:span="5"
>
<div class="item">
<!-- TODO: 只有一行的时候考虑 -->
<div
class="sku"
:class="index < 5 ? 'space' : ''"
@click="handleCurrentChange(index)"
>
<img
v-show="currentRow.skuId === item.skuId"
class="selected"
src="@/assets/vm/selected.png"
/>
<img class="img" :src="item.skuImage" />
<div class="name" :title="item.skuName">
{{ item.skuName }}
</div>
</div>
</div>
</el-col>
</el-row>
</el-scrollbar>
<el-icon
v-if="pageCount > 1"
class="arrow arrow-left"
:class="pageCount === 1 ? 'disabled' : ''"
@click="handleClickPrev"
><ArrowLeft
/></el-icon>
<el-icon
v-if="pageCount > 1"
class="arrow arrow-right"
:class="listQuery.pageIndex === pageCount ? 'disabled' : ''"
@click="handleClickNext"
><ArrowRight
/></el-icon>
</div>
<div class="dialog-footer">
<el-button
type="primary"
class="el-button--primary1"
@click="handleSelectClick"
>
确认
</el-button>
</div>
</el-dialog>
<!-- end -->
</el-dialog>
<!-- end -->
</template>
<script setup>
import { require } from '@/utils/validate';
const { proxy } = getCurrentInstance();
// 滚动插件
import { ElScrollbar } from 'element-plus';
// 接口
import {
getGoodsList,
getGoodsType,
channelConfig,
} from '@/api/manage/channel';
import { listSku } from '@/api/manage/sku';
// 内部组件
import ChannelDialogItem from './ChannelDialogItem.vue';
import { watch } from 'vue';
// 获取父组件参数
const props = defineProps({
// 弹层隐藏显示
goodVisible: {
type: Boolean,
default: false,
},
// 触发的货道信息
goodData: {
type: Object,
default: () => {},
},
});
// 获取父组件的方法
const emit = defineEmits(['handleCloseGood']);
// ******定义变量******
const visible = ref(false); //货道弹层显示隐藏
const scrollStatus = ref('LEFT');
const listLoading = ref(false);
const vmType = ref({}); //获取货道基本信息
const channels = ref({}); //货道数据
const scroll = ref(null); //滚动条ref
// 监听货道弹层显示/隐藏
watch(
() => props.goodVisible,
(val) => {
visible.value = val;
}
);
// ******定义方法******
// 获取货道基本信息
const handleGoodOpen = () => {
getVmType();
channelList();
};
// 获取货道基本信息
const getVmType = async () => {
const { data } = await getGoodsType(props.goodData.vmTypeId);
vmType.value = data;
};
// 获取货道列表
const channelList = async () => {
listLoading.value = true;
const { data } = await getGoodsList(props.goodData.innerCode);
channels.value = data;
listLoading.value = false;
};
const computedCurrentIndex = (vmRowIndex, vmColIndex) => {
return (vmRowIndex - 1) * vmType.value.vmCol + vmColIndex - 1;
};
// 关闭货道弹窗
const handleGoodcClose = () => {
visible.value = false
emit('handleCloseGood');
};
const handleClickPrevButton = () => {
scroll.value.wrapRef.scrollLeft = 0;
scrollStatus.value = 'LEFT';
};
const handleClickNextButton = () => {
scroll.value.wrapRef.scrollLeft = scroll.value.wrapRef.scrollWidth;
scrollStatus.value = 'RIGHT';
};
const currentIndex = ref(0);
const channelCode = ref('');
const skuDialogVisible = ref(false); //添加商品弹层
// 删除选中的商品
const openRemoveSkuDialog = (index, code) => {
currentIndex.value = index;
channelCode.value = code;
channels.value[currentIndex.value].skuId = '0';
channels.value[currentIndex.value].sku = undefined;
};
// 添加商品
const listQuery = ref({
pageIndex: 1,
pageSize: 10,
}); //搜索商品
const listSkuLoading = ref(false); //商品列表loading
const listSkuData = ref({}); //商品数据
const currentRow = ref({});
const pageCount = ref(0); //总页数
const channelModelView = ref({});
// 商品弹层列表
const handleListOpen = async () => {
listSkuLoading.value = true;
listQuery.value.skuName = listQuery.value.skuName || undefined;
const data = await listSku(listQuery.value);
listSkuData.value = data;
pageCount.value = Math.ceil(data.total / 10);
listSkuLoading.value = false;
};
// 打开商品选择弹层
const openSetSkuDialog = (index, code) => {
currentIndex.value = index;
channelCode.value = code;
skuDialogVisible.value = true;
};
// 关闭商品详情
const handleListClose = () => {
skuDialogVisible.value = false;
};
// 商品上一页
const handleClickPrev = () => {
if (listQuery.value.pageIndex === 1) {
return;
}
listQuery.value.pageIndex--;
handleListOpen();
};
// 商品下一页
const handleClickNext = () => {
if (listQuery.value.pageIndex === pageCount.value) {
return;
}
listQuery.value.pageIndex++;
handleListOpen();
};
// 搜索
const resetPageIndex = () => {
listQuery.value.pageIndex = 1;
handleListOpen();
};
// 商品选择
const handleCurrentChange = (i) => {
// TODO:点击取消选中功能
currentRow.value = listSkuData.value.rows[i];
};
// 确认商品选择
const handleSelectClick = (sku) => {
handleListClose();
channels.value[currentIndex.value].skuId = currentRow.value.skuId;
channels.value[currentIndex.value].sku = {
skuName: currentRow.value.skuName,
skuImage: currentRow.value.skuImage,
};
};
// 确认货道提交
const handleClick = async () => {
channelModelView.value.innerCode = props.goodData.innerCode;
channelModelView.value.channelList = channels.value.map((item) => {
return {
innerCode: props.goodData.innerCode,
channelCode: item.channelCode,
skuId: item.skuId,
};
});
const res = await channelConfig(channelModelView.value);
if (res.code === 200) {
proxy.$modal.msgSuccess('操作成功');
visible.value = false
emit('handleCloseGood');
}
};
</script>
// <style lang="scss" scoped src="../index.scss"></style>
ChannelDialogItem.vue
<template>
<div v-if="channel" class="item">
<div class="code">
{{ channel.channelCode }}
</div>
<div class="sku">
<img
class="img"
:src="channel.sku&&channel.sku.skuImage
? channel.sku.skuImage
: require('@/assets/vm/default_sku.png')"
/>
<div class="name" :title="channel.sku ? channel.sku.skuName : '暂无商品'">
{{ channel.sku ? channel.sku.skuName : '暂无商品' }}
</div>
</div>
<div>
<el-button
type="text"
class="el-button--primary-text"
@click="handleSetClick"
>
添加
</el-button>
<el-button
type="text"
class="el-button--danger-text"
:disabled="!channel.sku ? true : false"
@click="handleRemoveClick"
>
删除
</el-button>
</div>
</div>
</template>
<script setup>
import { require } from '@/utils/validate';
const props = defineProps({
currentIndex: {
type: Number,
default: 0,
},
channel: {
type: Object,
default: () => {},
},
});
const emit = defineEmits(['openSetSkuDialog','openRemoveSkuDialog']);
// 添加商品
const handleSetClick = () => {
emit('openSetSkuDialog', props.currentIndex, props.channel.channelCode);
};
// 删除产品
const handleRemoveClick = () => {
emit('openRemoveSkuDialog', props.currentIndex, props.channel.channelCode);
};
</script>
<style scoped lang="scss">
@import '@/assets/styles/variables.module.scss';
.item {
position: relative;
width: 150px;
height: 180px;
background: $base-menu-light-background;
box-shadow: 0px 2px 4px 0px rgba(0, 0, 0, 0.06);
border-radius: 4px;
text-align: center;
.code {
position: absolute;
top: 10px;
left: 0;
width: 43px;
height: 23px;
line-height: 23px;
background: #829bed;
border-radius: 0px 10px 10px 0px;
font-size: 12px;
color: $base-menu-light-background;
}
.sku {
height: 135px;
padding-top: 16px;
background-color: #f6f7fb;
border-radius: 4px;
.img {
display: inline-block;
width: 84px;
height: 78px;
margin-bottom: 10px;
object-fit: contain;
}
.name {
padding: 0 16px;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
}
}
</style>
③ 修改设备index.vue
vm下德index
<el-button link type="primary" @click="handleGoods(scope.row)" v-hasPermi="['manage:vm:edit']">货道</el-button>
<!-- 货道组件 -->
<ChannelDialog :goodVisible="goodVisible" :goodData="goodData" @handleCloseGood="handleCloseGood"></ChannelDialog>
<!-- end -->
<script setup name="Vm">
// ********************货道********************
// 货道组件
import ChannelDialog from './components/ChannelDialog.vue';
const goodVisible = ref(false); //货道弹层显示隐藏
const goodData = ref({}); //货道信息用来拿取 vmTypeId和innerCode
// 打开货道弹层
const handleGoods = (row) => {
goodVisible.value = true;
goodData.value = row;
};
// 关闭货道弹层
const handleCloseGood = () => {
goodVisible.value = false;
};
// ********************货道end********************
</script>
<style lang="scss" scoped src="./index.scss"></style>
查询货道列表
ChannelVo
@Data
public class ChannelVo extends Channel {
// 商品
private Sku sku;
}
ChannelMapper和xml
/**
* 根据售货机编号查询货道列表
*
* @param innerCode
* @return ChannelVo集合
*/
List<ChannelVo> selectChannelVoListByInnerCode(String innerCode);
<resultMap type="ChannelVo" id="ChannelVoResult">
<result property="id" column="id" />
<result property="channelCode" column="channel_code" />
<result property="skuId" column="sku_id" />
<result property="vmId" column="vm_id" />
<result property="innerCode" column="inner_code" />
<result property="maxCapacity" column="max_capacity" />
<result property="currentCapacity" column="current_capacity" />
<result property="lastSupplyTime" column="last_supply_time" />
<result property="createTime" column="create_time" />
<result property="updateTime" column="update_time" />
<association property="sku" javaType="Sku" column="sku_id" select="com.dkd.manage.mapper.SkuMapper.selectSkuBySkuId"/>
</resultMap>
<select id="selectChannelVoListByInnerCode" resultMap="ChannelVoResult">
<include refid="selectChannelVo"/>
where inner_code = #{innerCode}
</select>
IChannelService接口和实现
/**
* 根据售货机编号查询货道列表
*
* @param innerCode
* @return ChannelVo集合
*/
List<ChannelVo> selectChannelVoListByInnerCode(String innerCode);
/**
* 根据售货机编号查询货道列表
*
* @param innerCode
* @return ChannelVo集合
*/
@Override
public List<ChannelVo> selectChannelVoListByInnerCode(String innerCode) {
return channelMapper.selectChannelVoListByInnerCode(innerCode);
}
ChannelController
/**
* 根据售货机编号查询货道列表
*/
@PreAuthorize("@ss.hasPermi('manage:channel:list')")
@GetMapping("/list/{innerCode}")
public AjaxResult lisetByInnerCode(@PathVariable("innerCode") String innerCode) {
List<ChannelVo> voList = channelService.selectChannelVoListByInnerCode(innerCode);
return success(voList);
}
货道关联商品
ChannelSkuDto
// 某个货道对应的sku信息
@Data
public class ChannelSkuDto {
private String innerCode;
private String channelCode;
private Long skuId;
}
ChannelConfigDto
// 售货机货道配置
@Data
public class ChannelConfigDto {
// 售货机编号
private String innerCode;
// 货道配置
private List<ChannelSkuDto> channelList;
}
ChannelMapper和xml
/**
* 根据售货机编号和货道编号查询货道信息
* @param innerCode
* @param channelCode
* @return 售货机货道
*/
@Select("select * from tb_channel where inner_code =#{innerCode} and channel_code=#{channelCode}")
Channel getChannelInfo(@Param("innerCode") String innerCode, @Param("channelCode") String channelCode);
/**
* 批量修改货道
* @param list
* @return 结果
*/
int batchUpdateChannel(List<Channel> list);
<update id="batchUpdateChannel" parameterType="java.util.List">
<foreach item="channel" collection="list" separator=";">
UPDATE tb_channel
<set>
<if test="channel.channelCode != null and channel.channelCode != ''">channel_code = #{channel.channelCode},</if>
<if test="channel.skuId != null">sku_id = #{channel.skuId},</if>
<if test="channel.vmId != null">vm_id = #{channel.vmId},</if>
<if test="channel.innerCode != null and channel.innerCode != ''">inner_code = #{channel.innerCode},</if>
<if test="channel.maxCapacity != null">max_capacity = #{channel.maxCapacity},</if>
<if test="channel.currentCapacity != null">current_capacity = #{channel.currentCapacity},</if>
<if test="channel.lastSupplyTime != null">last_supply_time = #{channel.lastSupplyTime},</if>
<if test="channel.createTime != null">create_time = #{channel.createTime},</if>
<if test="channel.updateTime != null">update_time = #{channel.updateTime},</if>
</set>
WHERE id = #{channel.id}
</foreach>
</update>
application-druid.yml
允许mybatis框架在单个请求中发送多个sql语句
IChannelService接口和实现
/**
* 货道关联商品
* @param channelConfigDto
* @return 结果
*/
int setChannel(ChannelConfigDto channelConfigDto);
/**
* 货道关联商品
* @param channelConfigDto
* @return 结果
*/
@Override
public int setChannel(ChannelConfigDto channelConfigDto) {
//1. dto转po
List<Channel> list = channelConfigDto.getChannelList().stream().map(c -> {
// 根据售货机编号和货道编号查询货道
Channel channel = channelMapper.getChannelInfo(c.getInnerCode(), c.getChannelCode());
if (channel != null) {
// 货道更新skuId
channel.setSkuId(c.getSkuId());
// 货道更新时间
channel.setUpdateTime(DateUtils.getNowDate());
}
return channel;
}).collect(Collectors.toList());
//2. 批量修改货道
return channelMapper.batchUpdateChannel(list);
}
ChannelController
/**
* 货道关联商品
*/
@PreAuthorize("@ss.hasPermi('manage:channel:edit')")
@Log(title = "售货机货道", businessType = BusinessType.UPDATE)
@PutMapping("/config")
public AjaxResult setChannel(@RequestBody ChannelConfigDto channelConfigDto) {
return toAjax(channelService.setChannel(channelConfigDto));
}