新增 产品信息导入,稿件下载功能

This commit is contained in:
yf 2025-01-04 22:41:06 +08:00
parent 22888ef18c
commit c2285e42e4
16 changed files with 340 additions and 7 deletions

View File

@ -1,8 +1,9 @@
package cn.hangtag.module.oms.controller.admin.productinfo;
import cn.hangtag.framework.mybatis.build.QueryFilterInfo;
import io.swagger.v3.oas.annotations.Parameters;
import lombok.AllArgsConstructor;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import org.springframework.validation.annotation.Validated;
import org.springframework.security.access.prepost.PreAuthorize;
import io.swagger.v3.oas.annotations.tags.Tag;
@ -28,15 +29,17 @@ import static cn.hangtag.framework.apilog.core.enums.OperateTypeEnum.*;
import cn.hangtag.module.oms.controller.admin.productinfo.vo.*;
import cn.hangtag.module.oms.dal.dataobject.productinfo.ProductInfoDO;
import cn.hangtag.module.oms.service.productinfo.ProductInfoService;
import org.springframework.web.multipart.MultipartFile;
@Tag(name = "管理后台 - 产品资料 ")
@RestController
@RequestMapping("/oms/product-info")
@Validated
@AllArgsConstructor
public class ProductInfoController {
@Resource
private ProductInfoService productInfoService;
private final ProductInfoService productInfoService;
@PostMapping("/create")
@Operation(summary = "创建产品资料 ")
@ -88,6 +91,22 @@ public class ProductInfoController {
return success(BeanUtils.toBean(pageResult, ProductInfoRespVO.class));
}
@GetMapping("/excel-template")
@Operation(summary = "产品信息导入模板")
public void importTemplate(HttpServletResponse response) throws IOException {
// 输出
ExcelUtils.write(response, "产品信息导入模板.xls", "产品信息", ProductInfoExcelVO.class, null);
}
@PostMapping("/import")
@Operation(summary = "导入生产制单")
@Parameters({ @Parameter(name = "file", description = "Excel 文件", required = true),
})
public CommonResult<String> importExcel(@RequestParam("file") MultipartFile file) throws Exception {
List<ProductInfoExcelVO> list = ExcelUtils.read(file, ProductInfoExcelVO.class);
String res = productInfoService.importExcel(list);
return success(res);
}
@GetMapping("/export-excel")
@Operation(summary = "导出产品资料 Excel")
@PreAuthorize("@ss.hasPermission('oms:product-info:export')")

View File

@ -0,0 +1,32 @@
package cn.hangtag.module.oms.controller.admin.productinfo.vo;
import com.alibaba.excel.annotation.ExcelProperty;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
import java.time.LocalDateTime;
/**
* 产品信息导入 Excel VO
*/
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
@Accessors(chain = false) // 设置 chain = false避免数据导入有问题
public class ProductInfoExcelVO {
@ExcelProperty("产品编码")
private String code;
@ExcelProperty("产品名称")
private String name;
@ExcelProperty("品牌名称")
private String brandName;
}

View File

@ -78,4 +78,6 @@ public interface BrandService {
* @return {@link Integer }
*/
Integer updateProductCount(Long id);
BrandDO getBrandByName(String brandName);
}

View File

@ -3,6 +3,7 @@ package cn.hangtag.module.oms.service.brand;
import cn.hangtag.framework.common.exception.ServiceException;
import cn.hangtag.framework.common.exception.enums.GlobalErrorCodeConstants;
import cn.hangtag.framework.common.util.FuncUtil;
import cn.hangtag.framework.common.util.cache.CacheUtils;
import cn.hangtag.framework.mybatis.core.dataobject.BaseDO;
import cn.hangtag.module.oms.base.dal.dataobject.producttype.ProductTypeDO;
import cn.hangtag.module.oms.dal.dataobject.customerbrand.CustomerBrandDO;
@ -14,6 +15,8 @@ import cn.hangtag.module.oms.serialnumber.CodingRulesUtils;
import cn.hangtag.module.system.dal.dataobject.permission.MenuDO;
import cn.hangtag.module.system.dal.dataobject.permission.RoleMenuDO;
import cn.hangtag.module.system.dal.mysql.permission.RoleMenuMapper;
import cn.hutool.cache.CacheUtil;
import cn.hutool.cache.impl.FIFOCache;
import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import lombok.AllArgsConstructor;
@ -159,6 +162,26 @@ public class BrandServiceImpl implements BrandService {
}
return convertSet(customerBrandMapper.selectListByCustomerId(customerIds), CustomerBrandDO::getBrandId);
}
private final FIFOCache<String, BrandDO> brandDOS = CacheUtil.newFIFOCache(100,1000*60*60*10);
@Override
public BrandDO getBrandByName(String brandName) {
if (FuncUtil.isNotEmpty(brandName)) {
BrandDO brandDO = brandDOS.get(brandName);
if (FuncUtil.isNotEmpty(brandDO)) {
return brandDO;
}
LambdaQueryWrapper<BrandDO> lambdaQueryWrapper = new LambdaQueryWrapper<>();
lambdaQueryWrapper.eq(BrandDO::getName, brandName);
lambdaQueryWrapper.eq(BrandDO::getDeleted, false);
List<BrandDO> dos = brandMapper.selectList(lambdaQueryWrapper);
if (FuncUtil.isNotEmpty(dos)) {
brandDOS.put(brandName, dos.get(0));
return brandDO;
}
}
return null;
}
private void checkCode(Long id, String code) {

View File

@ -9,6 +9,7 @@ import cn.hangtag.module.oms.dal.dataobject.productinfo.ProductInfoDO;
import cn.hangtag.framework.common.pojo.PageResult;
import java.math.BigDecimal;
import java.util.List;
/**
* 产品资料 Service 接口
@ -72,4 +73,6 @@ public interface ProductInfoService {
* @return {@link BigDecimal }
*/
BigDecimal queryPriceByProductId(Long productId, String currency);
String importExcel(List<ProductInfoExcelVO> list);
}

View File

@ -272,4 +272,38 @@ public class ProductInfoServiceImpl implements ProductInfoService {
return new BigDecimal(0);
}
@Override
public String importExcel(List<ProductInfoExcelVO> list) {
List<ProductInfoDO> newList = new ArrayList<>();
for (int i = 0; i < list.size(); i++) {
ProductInfoExcelVO excelVO = list.get(i);
ProductInfoDO productInfo = new ProductInfoDO();
String code = excelVO.getCode();
if(FuncUtil.isNotEmpty(code)){
try {
checkCode(productInfo.getId(),code);
}catch (Exception e){
return ""+(i+1)+"行编码重复:"+code;
}
}else {
code = productInfo.getCode();
}
productInfo.setName(excelVO.getName());
String brandName = excelVO.getBrandName();
productInfo.setCode(code);
productInfo.setTemplateType("2");
if(FuncUtil.isNotEmpty(brandName)){
BrandDO brandDO = brandService.getBrandByName(brandName);
if(FuncUtil.isNotEmpty(brandDO)){
productInfo.setBrandId(brandDO.getId());
}
}
newList.add(productInfo);
}
productInfoMapper.insertBatch(newList);
return "";
}
}

View File

@ -54,6 +54,9 @@ export const ProductInfoApi = {
// 导出产品资料 Excel
exportProductInfo: async (params) => {
return await request.download({ url: `/oms/product-info/export-excel`, params })
return await request.download({ url: `/oms/product-info/export-excel`, params ,timeout: 1000*60 * 20})
},
downloadTemplate: async()=> {
return await request.download({ url: `/oms/product-info/excel-template` })
}
}

View File

@ -670,6 +670,7 @@ export default {
"viewOrder": "viewOrder",
"backHome": "BackHome",
"tipsDataChange": "DataChange Click Update Button",
"downloadSuccess": "downloadSuccess",
},
"productDialogList": {
"title": "Product List",

View File

@ -666,6 +666,7 @@ export default {
viewOrder: "查看订单",
backHome: "返回首页",
tipsDataChange: "数据已更新,请点击更新按钮",
downloadSuccess: "下载成功",
},
productDialogList:{
title: '产品列表',

View File

@ -0,0 +1,138 @@
<template>
<Dialog v-model="dialogVisible" :title="props.title" :width="props.width">
<el-upload
ref="uploadRef"
v-model:file-list="fileList"
:action="importUrl"
:auto-upload="false"
:disabled="formLoading"
:headers="uploadHeaders"
:limit="1"
:on-error="submitFormError"
:on-exceed="handleExceed"
:on-success="submitFormSuccess"
accept=".xlsx, .xls"
drag
>
<Icon icon="ep:upload" />
<div class="el-upload__text">将文件拖到此处<em>点击上传</em></div>
<template #tip>
<div class="el-upload__tip text-center">
<span>支持导入 xlsxlsx 格式文件</span>
<el-link
:underline="false"
style="font-size: 12px; vertical-align: baseline"
type="primary"
@click="importTemplate"
>
下载模板
</el-link>
</div>
</template>
</el-upload>
<template #footer>
<el-button :disabled="formLoading" type="primary" @click="submitForm">导入</el-button>
<el-button @click="dialogVisible = false">关闭</el-button>
</template>
</Dialog>
</template>
<script lang="ts" setup>
import { getAccessToken, getTenantId } from '@/utils/auth'
import { computed } from 'vue';
defineOptions({ name: 'ProductInfoExcelImport' })
const message = useMessage() //
const dialogVisible = ref(false) //
const formLoading = ref(false) //
const uploadRef = ref()
const uploadHeaders = ref() // Header
const fileList = ref([]) //
const props = defineProps({
title: {
type: String,
default: '产品导入'
},
width: {
type: String,
default: '50vw'
},
uploadUrl: {
type: String,
default: '/oms/product-info/import'
}
})
const importUrl = computed(() => {
return `${import.meta.env.VITE_BASE_URL}${import.meta.env.VITE_API_URL}${props.uploadUrl}`;
});
/** 打开弹窗 */
const open = () => {
dialogVisible.value = true
fileList.value = []
resetForm()
}
/** 提交表单 */
const submitForm = async () => {
if (fileList.value.length == 0) {
message.error('请上传文件')
return
}
//
uploadHeaders.value = {
Authorization: 'Bearer ' + getAccessToken(),
'tenant-id': getTenantId()
}
formLoading.value = true
uploadRef.value!.submit()
}
/** 文件上传成功 */
const emits = defineEmits(['success','downloadTemplate'])
const submitFormSuccess = (response: any) => {
if (response.code !== 0) {
message.error(response.msg)
formLoading.value = false
return
}
//
emits('success',response.data)
}
const close = () => {
resetForm();
formLoading.value = false
dialogVisible.value = false
}
/** 上传错误提示 */
const submitFormError = (): void => {
message.error('上传失败,请您重新上传!')
formLoading.value = false
}
/** 重置表单 */
const resetForm = async (): Promise<void> => {
//
formLoading.value = false
await nextTick()
uploadRef.value?.clearFiles()
}
/** 文件数超出提示 */
const handleExceed = (): void => {
message.error('最多只能上传一个文件!')
}
/** 下载模板操作 */
const importTemplate = async () => {
emits('downloadTemplate')
}
defineExpose({ open,close,resetForm }) // open
</script>

View File

@ -102,7 +102,19 @@
<Icon icon="ep:download" class="mr-5px"/>
导出
</el-button>
<div class="ml-2" v-hasPermi="['oms:product-info:create']">
<el-button
type="primary"
plain
@click="handleImport"
>
<Icon icon="ep:upload" class="mr-5px"/>
excel导入
</el-button>
<ProductInfoExcelImport ref="excelImportRef" @success="importSuccess" @download-template="downloadTemplate"/>
</div>
</el-form-item>
</el-form>
</ContentWrap>
@ -200,6 +212,7 @@ const designPreviewDialogRef = ref()
const loading = ref(true) //
const list = ref<ProductInfoVO[]>([]) //
const total = ref(0) //
const excelImportRef = ref();
const queryParams = reactive({
pageNo: 1,
pageSize: 20,
@ -313,8 +326,25 @@ onMounted(() => {
openForm('create')
}
}
getList();
init();
})
const importSuccess = (res)=>{
if(!res){
message.success('导入成功')
excelImportRef.value.close()
getList();
}else {
message.error(res)
excelImportRef.value.resetForm()
}
}
const downloadTemplate = ()=>{
ProductInfoApi.downloadTemplate().then(res => {
download.excel(res, '产品导入模板.xlsx')
})
}
const handleImport = () => {
excelImportRef.value.open()
}
</script>

View File

@ -747,8 +747,8 @@ const toSVGData = (callback: (url: string) => void, options?: {
}
}) => {
let widthScale = 20 // options && options.widthScale ? options.widthScale : 5
let heightScale = 20 // options && options.heightScale ? options.heightScale : 5
let widthScale = 60 // options && options.widthScale ? options.widthScale : 5
let heightScale = 60 // options && options.heightScale ? options.heightScale : 5
graph.toSVG((dataUri) => {
that.previewUrl = dataUri;
callback(that.previewUrl)

View File

@ -670,6 +670,7 @@ export default {
"viewOrder": "viewOrder",
"backHome": "BackHome",
"tipsDataChange": "DataChange Click Update Button",
"downloadSuccess": "downloadSuccess",
},
"productDialogList": {

View File

@ -666,6 +666,7 @@ export default {
viewOrder: "查看订单",
backHome: "返回首页",
tipsDataChange: "数据已更新,请点击更新按钮",
downloadSuccess: "下载成功",
},
productDialogList:{
title: '产品列表',

View File

@ -117,6 +117,7 @@ const getValue = (item:any) => {
}
return arr.join(";")
}
const formData = ref({})
watch(() => props.productInfo, (newVal) => {
console.log("newVal",newVal)
@ -182,11 +183,50 @@ const change = () => {
emit("change", formData.value)
}
const imagePreview = (imgUrl: string) => {
addDownloadImg(imgUrl,new Date().getTime())
createImageViewer({
zIndex: 9999999,
urlList: [imgUrl]
})
}
const addDownloadImg = (url: string,name = 'img') => {
setTimeout(() => {
let wrapper = document.getElementsByClassName("el-image-viewer__btn el-image-viewer__actions");
if (wrapper.length > 0) {
let downImg = document.createElement("i");
downImg.style.fontSize = "20px";
downImg.style.padding = "4px";
downImg.style.color = "#fff";
downImg.setAttribute("class", "icon-lk_down");
//
wrapper[0].appendChild(downImg);
//
downImg.addEventListener("click", () => {
//
const link = document.createElement("a");
useMessage().success(t("createOrder.downloadSuccess"))
// 使 fetch
fetch(url)
.then((res) => res.blob())
.then((blob) => {
const blobUrl = URL.createObjectURL(blob); // blob URL
link.href = blobUrl; //
link.download = name+".jpg"; //
link.click(); //
URL.revokeObjectURL(blobUrl); // blob URL
})
.catch((err) => {
console.error("下载失败:", err);
});
});
}
}, 100);
};
defineExpose({
init
})

View File

@ -0,0 +1,5 @@
-- 销售订单sku备注
ALTER TABLE oms_sale_order_sku
ADD COLUMN `remark` varchar(1024) DEFAULT NULL COMMENT '备注信息' AFTER `main_color`;