优化 下单修改为手机号码非必填,

优化 下单步骤产品列表添加关键字搜索
优化 后台订单金额调整为保留4位小数位
This commit is contained in:
yf 2025-07-09 23:01:27 +08:00
parent 766b427270
commit f68e17ea0e
10 changed files with 296 additions and 107 deletions

View File

@ -63,4 +63,38 @@ public class KeywordSearchHandler {
return handler;
}
/**
* 产品信息 name 关键搜索处理
* @return
*/
@Bean("productInfoName")
public AbsBaseKeywordSearchHandler productInfoNameHandler() {
AbsBaseKeywordSearchHandler handler = new AbsBaseKeywordSearchHandler() {
@Override
public List<KeywordResultVO> search(String keyword, long maxCount,String filter) {
Page page = new Page();
page.setSize(maxCount);
LambdaQueryWrapper<ProductInfoDO> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.select(ProductInfoDO::getName, ProductInfoDO::getBrandId,BaseDO::getDeleted);
if(FuncUtil.isNotEmpty(keyword)){
queryWrapper.like(ProductInfoDO::getName, keyword);
}
if(FuncUtil.isNotEmpty(filter)){
queryWrapper.eq(ProductInfoDO::getBrandId, filter);
}
queryWrapper.eq(BaseDO::getDeleted,false);
queryWrapper.groupBy(ProductInfoDO::getName,BaseDO::getDeleted,ProductInfoDO::getBrandId);
Page<ProductInfoDO> page1 = productInfoMapper.selectPage(page, queryWrapper);
List<ProductInfoDO> records = page1.getRecords();
List<KeywordResultVO> res = new ArrayList<>();
for (ProductInfoDO record : records) {
res.add(new KeywordResultVO(record.getName(), record.getName()));
}
return res;
}
};
return handler;
}
}

View File

@ -5,11 +5,11 @@
<div style="max-height: 90vh; overflow:auto;" v-loading="loading">
<span style="color: #ff0000;font-size: 22px; padding-left: 4px">
*{{ t('designInfo.tipsInfo')}}
*{{ t('designInfo.tipsInfo') }}
</span>
<div style="padding-bottom: 4px">
<el-checkbox style="margin: 8px" v-model="that.reView">
{{ t('designInfo.titleSyncUpdateDesign')}}
{{ t('designInfo.titleSyncUpdateDesign') }}
</el-checkbox>
<el-button
v-show="!that.reView"
@ -53,7 +53,8 @@
<el-form-item label="Style" v-show="that.draftDesignList.length > 1">
<div class="flex ml-3">
<div>
<el-select class="min-w-100" v-model="that.draftDesignId" @change="changeType">
<el-select class="min-w-100" v-model="that.draftDesignId"
@change="changeType">
<el-option
v-for="(item) in that.draftDesignList"
:key="item.id"
@ -205,7 +206,8 @@
:key="index" style="display: flex">
<div class="img-box">
<div style="display: flex;width: 380px;align-items: center" :class="{ 'error_tip': errorItem(tmp.key,index) }">
<div style="display: flex;width: 380px;align-items: center"
:class="{ 'error_tip': errorItem(tmp.key,index) }">
<el-image
style="width: 64px; height: 64px"
fit="contain"
@ -305,7 +307,7 @@
<script lang="ts" setup name="DynamicPropConfig">
// @ts-nocheck
import {ElButton, ElLoading, ElSelect, ElSelectV2} from "element-plus";
import {ElButton, ElLoading, ElSelect, ElSelectV2} from "element-plus";
import {reactive, watch} from 'vue'
import {useMessage} from "@/hooks/web/useMessage";
import {DraftDesignDataApi} from "@/api/oms/draftdesigndata";
@ -322,7 +324,7 @@ import {useI18n} from "@/hooks/web/useI18n";
const {t} = useI18n()
const svgSize= computed(() => {
const svgSize = computed(() => {
return {
width: that.svgWidth * 10,
height: that.svgHeight * 10,
@ -392,7 +394,7 @@ const imageSize = computed(() => {
if (that.sizeInfo) {
return {
// @ts-ignore
width: that.sizeInfo.width, height: that.sizeInfo.height
width: that.sizeInfo.width, height: that.sizeInfo.height
}
}
return {
@ -588,6 +590,34 @@ const washingInfoListByType = (type, index = -1) => {
const getIngredientInfoListByType = (type) => {
return that.ingredientInfoList.filter(item => item.type === type)
}
/**
* 等待 图片加载完成
* @param container
*/
async function waitAllImagesLoaded(container) {
const imgs = Array.from(container.querySelectorAll('img'));
for (const img of imgs) {
await waitImageLoad(img);
console.log(`图片加载完成:${img.src} 宽度: ${img.naturalWidth}px高度: ${img.naturalHeight}px`);
}
}
function waitImageLoad(img) {
return new Promise((resolve, reject) => {
if (img.complete && img.naturalWidth > 0) {
resolve();
} else {
img.onload = () => resolve();
img.onerror = () =>{
useMessage().notifyError(t('designInfo.tipsUploadError'))
that.pageLoading.close();
reject( new Error('图片加载失败: ' + img.src) );
}
}
});
}
const uploadFile = async (fileName) => {
return new Promise((resolve, reject) => {
let svgElement = document.getElementById(that.svgId);
@ -596,48 +626,69 @@ const uploadFile = async (fileName) => {
console.log("svgElement?.innerHTML",)
// Blob SVG
saveMaxSvg(svgElement,fileName);
// saveMaxSvg(svgElement,fileName);
// //
// const link = document.createElement('a');
// link.href = URL.createObjectURL(blob);
// link.download = 'graph.html'; //
// //
// link.click();
domtoimage.toBlob(svgElement, {
width: that.svgWidth + 10,
height: that.svgHeight + 10,
type: 'image/png',
scale:4,
quality: 1
}).then((blob) => {
const file = new File([blob], fileName, {type: 'image/png'});
FileApi.updateFile({file: file})
.then((res) => {
if (res.code === 0) {
resolve({
...res,
filename: fileName
})
} else {
reject(res)
}
}).catch((res) => {
reject(res)
waitAllImagesLoaded(svgElement).then(async d => {
that.pageLoading.close();
that.pageLoading = ElLoading.service({
lock: true,
text: t('designInfo.tipsDataTransmitting')+'90%',
background: 'rgba(0,0,0,0.5)'
})
}).catch(function (error) {
useMessage().notifyError(t('designInfo.tipsUploadError'))
console.error('转换或上传过程中出现错误:', error);
});
domtoimage.toBlob(svgElement, {
width: that.svgWidth + 10,
height: that.svgHeight + 10,
type: 'image/png',
scale: 4,
quality: 1
}).then((blob) => {
that.pageLoading.close();
that.pageLoading = ElLoading.service({
lock: true,
text: t('designInfo.tipsDataTransmitting')+'98%',
background: 'rgba(0,0,0,0.5)'
})
const file = new File([blob], fileName, {type: 'image/png'});
FileApi.updateFile({file: file})
.then((res) => {
if (res.code === 0) {
resolve({
...res,
filename: fileName
})
} else {
reject(res)
}
}).catch((res) => {
reject(res)
})
}).catch(function (error) {
useMessage().notifyError(t('designInfo.tipsUploadError'))
that.pageLoading.close();
console.error('转换或上传过程中出现错误:', error);
});
})
// 1111
// 22222
});
}
const saveMaxSvg = (svgElement,fileName)=>{
const saveMaxSvg = (svgElement, fileName) => {
const htmlStr = getHtml(`${svgElement?.parentElement?.innerHTML}`);
const blob = new Blob([htmlStr], { type: 'text/html' });
const file = new File([blob], fileName+".html", {type: 'text/html'});
const blob = new Blob([htmlStr], {type: 'text/html'});
const file = new File([blob], fileName + ".html", {type: 'text/html'});
FileApi.updateFile({file: file})
.then((res) => {
console.log("res"+res)
console.log("res" + res)
if (res.code === 0) {
that.sourceFile = res.data
} else {
@ -1102,10 +1153,10 @@ const checkPropInfo = (info) => {
const showLabel1 = newInfo[key].dataInfo[0].showLabel
for (let j = 0; j < infos.length; j++) {
console.log("newInfo[key].canInput",showLabel1,newInfo[key])
if( newInfo[key].canInput){
console.log("newInfo[key].canInput", showLabel1, newInfo[key])
if (newInfo[key].canInput) {
infos[j].label = showLabel1
}else{
} else {
outerLoop: for (let i = 0; i < infoList.length; i++) {
const row1 = infoList[i];
for (let k = 0; k < row1.langMapping.length; k++) {
@ -1328,7 +1379,7 @@ const changeType = () => {
const getPropInfo = () => {
//
return new Promise((resolve) => {
if( that.changeCount > 0){
if (that.changeCount > 0) {
useMessage().warning(t('designInfo.tipsDataChange'));
return;
}
@ -1360,7 +1411,7 @@ const getPropInfo = () => {
that.svgHeight = dom.height;
that.svgWidth = dom.width;
console.log("that.svgHeight",that.svgHeight,that.svgWidth)
console.log("that.svgHeight", that.svgHeight, that.svgWidth)
document.querySelector(`#${that.svgId} svg`).style.left = `${svgDom.left - dom.left}px`;
document.querySelector(`#${that.svgId} svg`).style.top = `${svgDom.top - dom.top}px`;
// that.pageLoading.close()

View File

@ -11,7 +11,7 @@
<ContentWrap>
<Search
:expand="true"
:expandField="['productTypeId','name']"
:expandField="['productTypeId','name','customerGroupId']"
:schema="allSchemas.searchSchema"
:is-col="false"
ref="searchRef"
@ -21,11 +21,40 @@
<!-- 新增等操作按钮 -->
<template #productTypeId="{data}">
<div>
<el-select class="w-full min-w-[200px]" v-model="data.productTypeId" clearable placeholder="Please select the product type">
<el-option v-for="item in that.typeList" :key="item.id" :label="item.label" :value="item.id"/>
<el-select class="w-full min-w-[200px]" v-model="data.productTypeId" clearable
placeholder="Please select the product type">
<el-option
v-for="item in that.typeList" :key="item.id" :label="item.label"
:value="item.id"/>
</el-select>
</div>
</template>
<template #code="{data}">
<div>
<KeywordSearch
v-model="data.code"
:type="'productInfoCode'"
:filter="that.queryInfo.brandId"
:count="15"/>
</div>
</template>
<template #name="{data}">
<div>
<KeywordSearch
v-model="data.name"
:type="'productInfoName'"
:filter="that.queryInfo.brandId"
:count="15"/>
</div>
</template>
<template #customerGroupId="{data}">
<div>
<CustomerGroupDataListDialog
v-model="data.customerGroupId"
:is-can-edit="false"
/>
</div>
</template>
</Search>
</ContentWrap>
@ -57,6 +86,12 @@
</div>
<div v-else>-</div>
</template>
<template #fileIds="{row}">
<div v-if="row.fileIds">
<AccessoryDownload size="small" v-model="row.fileIds"/>
</div>
<div v-else>-</div>
</template>
</Table>
</ContentWrap>
@ -74,10 +109,12 @@
<script lang="ts" setup name="ProductInfoList">
import {allSchemas} from './config.data'
import Search from '@/components/Search/src/Search.vue'
import {ProductInfoApi} from '@/api/oms/productinfo'
import DataForm from './DataForm.vue'
import {ProductTypeApi} from "@/api/base/producttype";
import CustomerGroupDataListDialog
from "@/components/Dialog/src/CustomerGroupDataListDialog/index.vue";
/** 稿件图片库 */
defineOptions({name: 'ProductInfoList'})
@ -134,10 +171,19 @@ const that = reactive({
showValue: '',
visible: false,
keyword: '',
codeKeywordList: [
{
value: 'abc',
label: '请选择'
}
],
typeList: [],
selectRow: [],
queryInfo: {
productTypeId: null,
code: '',
name: '',
brandId: '',
}
})
@ -147,6 +193,7 @@ const getLabel = (id) => {
const res = that.typeList.find(item => item.id === id);
return res ? res.label : id
}
const openDialog = (filter = {}) => {
that.queryInfo = {
...that.queryInfo,
@ -199,9 +246,7 @@ const initInput = async () => {
const ids = that.inputVal.split(",");
let tmpInput = [];
let tmpShow = [];
debugger
for (let i = 0; i < ids.length; i++) {
debugger;
const data = await ProductTypeApi.getProductType(ids[i])
tmpInput.push(data[props.dataKey]);
tmpShow.push(data[props.showKey]);
@ -250,14 +295,6 @@ const {getList, setSearchParams} = tableMethods
/** 添加/修改操作 */
const formRef = ref()
const openForm = (type: string, id?: number) => {
formRef.value.open(type, id)
}
/** 删除按钮操作 */
const handleDelete = (id: number) => {
tableMethods.delList(id, false)
}
/** 初始化 **/
onMounted(() => {

View File

@ -554,7 +554,7 @@ export default {
},
designInfo: {
auditSubmitText: '批稿',
auditTips: '此稿件仅仅作为核对内容使用,不作为最终印刷稿件;',
auditTips: '此稿件仅仅作为核对内容使用,不作为最终印刷稿件;',
cancelText: '取消',
titlePreview: '预览',
titleUpdateDesign: '更新',

View File

@ -337,7 +337,7 @@ const changeRow = async (row,key:string) => {
let qty = row.qty
let discount = row.discount
let amount = (price*qty) * (1-(discount/100))
row.amount = amount.toFixed(2);
row.amount = amount.toFixed(4);
//100*(1-(2/100))
}
}

View File

@ -4,7 +4,13 @@ import request from '@/config/axios'
// 关键字 API
export const KeywordApi = {
// 查询关键字列表
/**
*
* @param filter
* @param type AbsBaseKeywordSearchHandler bean名称
* @param keyword
* @param maxCount
*/
list: async (filter: string, type: string, keyword: string, maxCount = 10) => {
return await request.get({
url: `/oms/base/keyword/list/${type}/${maxCount}`,

View File

@ -5,11 +5,11 @@
<div style="max-height: 90vh; overflow:auto;" v-loading="loading">
<span style="color: #ff0000;font-size: 22px; padding-left: 4px">
*{{ t('designInfo.tipsInfo')}}
*{{ t('designInfo.tipsInfo') }}
</span>
<div style="padding-bottom: 4px">
<el-checkbox style="margin: 8px" v-model="that.reView">
{{ t('designInfo.titleSyncUpdateDesign')}}
{{ t('designInfo.titleSyncUpdateDesign') }}
</el-checkbox>
<el-button
v-show="!that.reView"
@ -53,7 +53,8 @@
<el-form-item label="Style" v-show="that.draftDesignList.length > 1">
<div class="flex ml-3">
<div>
<el-select class="min-w-100" v-model="that.draftDesignId" @change="changeType">
<el-select class="min-w-100" v-model="that.draftDesignId"
@change="changeType">
<el-option
v-for="(item) in that.draftDesignList"
:key="item.id"
@ -205,7 +206,8 @@
:key="index" style="display: flex">
<div class="img-box">
<div style="display: flex;width: 380px;align-items: center" :class="{ 'error_tip': errorItem(tmp.key,index) }">
<div style="display: flex;width: 380px;align-items: center"
:class="{ 'error_tip': errorItem(tmp.key,index) }">
<el-image
style="width: 64px; height: 64px"
fit="contain"
@ -305,7 +307,7 @@
<script lang="ts" setup name="DynamicPropConfig">
// @ts-nocheck
import {ElButton, ElLoading, ElSelect, ElSelectV2} from "element-plus";
import {ElButton, ElLoading, ElSelect, ElSelectV2} from "element-plus";
import {reactive, watch} from 'vue'
import {useMessage} from "@/hooks/web/useMessage";
import {DraftDesignDataApi} from "@/api/oms/draftdesigndata";
@ -322,7 +324,7 @@ import {useI18n} from "@/hooks/web/useI18n";
const {t} = useI18n()
const svgSize= computed(() => {
const svgSize = computed(() => {
return {
width: that.svgWidth * 10,
height: that.svgHeight * 10,
@ -392,7 +394,7 @@ const imageSize = computed(() => {
if (that.sizeInfo) {
return {
// @ts-ignore
width: that.sizeInfo.width, height: that.sizeInfo.height
width: that.sizeInfo.width, height: that.sizeInfo.height
}
}
return {
@ -588,6 +590,34 @@ const washingInfoListByType = (type, index = -1) => {
const getIngredientInfoListByType = (type) => {
return that.ingredientInfoList.filter(item => item.type === type)
}
/**
* 等待 图片加载完成
* @param container
*/
async function waitAllImagesLoaded(container) {
const imgs = Array.from(container.querySelectorAll('img'));
for (const img of imgs) {
await waitImageLoad(img);
console.log(`图片加载完成:${img.src} 宽度: ${img.naturalWidth}px高度: ${img.naturalHeight}px`);
}
}
function waitImageLoad(img) {
return new Promise((resolve, reject) => {
if (img.complete && img.naturalWidth > 0) {
resolve();
} else {
img.onload = () => resolve();
img.onerror = () =>{
useMessage().notifyError(t('designInfo.tipsUploadError'))
that.pageLoading.close();
reject( new Error('图片加载失败: ' + img.src) );
}
}
});
}
const uploadFile = async (fileName) => {
return new Promise((resolve, reject) => {
let svgElement = document.getElementById(that.svgId);
@ -596,48 +626,69 @@ const uploadFile = async (fileName) => {
console.log("svgElement?.innerHTML",)
// Blob SVG
saveMaxSvg(svgElement,fileName);
// saveMaxSvg(svgElement,fileName);
// //
// const link = document.createElement('a');
// link.href = URL.createObjectURL(blob);
// link.download = 'graph.html'; //
// //
// link.click();
domtoimage.toBlob(svgElement, {
width: that.svgWidth + 10,
height: that.svgHeight + 10,
type: 'image/png',
scale:4,
quality: 1
}).then((blob) => {
const file = new File([blob], fileName, {type: 'image/png'});
FileApi.updateFile({file: file})
.then((res) => {
if (res.code === 0) {
resolve({
...res,
filename: fileName
})
} else {
reject(res)
}
}).catch((res) => {
reject(res)
waitAllImagesLoaded(svgElement).then(async d => {
that.pageLoading.close();
that.pageLoading = ElLoading.service({
lock: true,
text: t('designInfo.tipsDataTransmitting')+'90%',
background: 'rgba(0,0,0,0.5)'
})
}).catch(function (error) {
useMessage().notifyError(t('designInfo.tipsUploadError'))
console.error('转换或上传过程中出现错误:', error);
});
domtoimage.toBlob(svgElement, {
width: that.svgWidth + 10,
height: that.svgHeight + 10,
type: 'image/png',
scale: 4,
quality: 1
}).then((blob) => {
that.pageLoading.close();
that.pageLoading = ElLoading.service({
lock: true,
text: t('designInfo.tipsDataTransmitting')+'98%',
background: 'rgba(0,0,0,0.5)'
})
const file = new File([blob], fileName, {type: 'image/png'});
FileApi.updateFile({file: file})
.then((res) => {
if (res.code === 0) {
resolve({
...res,
filename: fileName
})
} else {
reject(res)
}
}).catch((res) => {
reject(res)
})
}).catch(function (error) {
useMessage().notifyError(t('designInfo.tipsUploadError'))
that.pageLoading.close();
console.error('转换或上传过程中出现错误:', error);
});
})
// 1111
// 22222
});
}
const saveMaxSvg = (svgElement,fileName)=>{
const saveMaxSvg = (svgElement, fileName) => {
const htmlStr = getHtml(`${svgElement?.parentElement?.innerHTML}`);
const blob = new Blob([htmlStr], { type: 'text/html' });
const file = new File([blob], fileName+".html", {type: 'text/html'});
const blob = new Blob([htmlStr], {type: 'text/html'});
const file = new File([blob], fileName + ".html", {type: 'text/html'});
FileApi.updateFile({file: file})
.then((res) => {
console.log("res"+res)
console.log("res" + res)
if (res.code === 0) {
that.sourceFile = res.data
} else {
@ -1102,10 +1153,10 @@ const checkPropInfo = (info) => {
const showLabel1 = newInfo[key].dataInfo[0].showLabel
for (let j = 0; j < infos.length; j++) {
console.log("newInfo[key].canInput",showLabel1,newInfo[key])
if( newInfo[key].canInput){
console.log("newInfo[key].canInput", showLabel1, newInfo[key])
if (newInfo[key].canInput) {
infos[j].label = showLabel1
}else{
} else {
outerLoop: for (let i = 0; i < infoList.length; i++) {
const row1 = infoList[i];
for (let k = 0; k < row1.langMapping.length; k++) {
@ -1328,7 +1379,7 @@ const changeType = () => {
const getPropInfo = () => {
//
return new Promise((resolve) => {
if( that.changeCount > 0){
if (that.changeCount > 0) {
useMessage().warning(t('designInfo.tipsDataChange'));
return;
}
@ -1360,7 +1411,7 @@ const getPropInfo = () => {
that.svgHeight = dom.height;
that.svgWidth = dom.width;
console.log("that.svgHeight",that.svgHeight,that.svgWidth)
console.log("that.svgHeight", that.svgHeight, that.svgWidth)
document.querySelector(`#${that.svgId} svg`).style.left = `${svgDom.left - dom.left}px`;
document.querySelector(`#${that.svgId} svg`).style.top = `${svgDom.top - dom.top}px`;
// that.pageLoading.close()

View File

@ -38,6 +38,15 @@
:count="15"/>
</div>
</template>
<template #name="{data}">
<div>
<KeywordSearch
v-model="data.name"
:type="'productInfoName'"
:filter="that.queryInfo.brandId"
:count="15"/>
</div>
</template>
<template #customerGroupId="{data}">
<div>
<CustomerGroupDataListDialog
@ -173,6 +182,7 @@ const that = reactive({
queryInfo: {
productTypeId: null,
code: '',
name: '',
brandId: '',
}
})

View File

@ -558,7 +558,7 @@ export default {
},
designInfo: {
auditSubmitText: '批稿',
auditTips: '此稿件仅仅作为核对内容使用,不作为最终印刷稿件;',
auditTips: '此稿件仅仅作为核对内容使用,不作为最终印刷稿件;',
cancelText: '取消',
titlePreview: '预览',
titleUpdateDesign: '更新',

View File

@ -565,13 +565,13 @@ const formRules = reactive({
orderFollowerUser: [{required: true, message: t('createOrder.placeOrderFollowerUser'), trigger: 'blur'}],
brandId: [{required: true, message: t('createOrder.ruleMsgBrandId'), trigger: 'blur'}],
contractCode: [{required: true, message: t('createOrder.ruleMsgContractCode'), trigger: 'blur'}],
phone: [{required: true, message: t('createOrder.ruleMsgPhone'), trigger: 'blur'},
{
pattern: /^(?:(?:\+|00)86)?1(?:3[\d]|4[5-79]|5[0-35-9]|6[5-7]|7[0-8]|8[\d]|9[189])\d{8}$/,
message: t('createOrder.ruleMsgPhoneError'),
trigger: 'blur'
}
],
// phone: [{required: true, message: t('createOrder.ruleMsgPhone'), trigger: 'blur'},
// {
// pattern: /^(?:(?:\+|00)86)?1(?:3[\d]|4[5-79]|5[0-35-9]|6[5-7]|7[0-8]|8[\d]|9[189])\d{8}$/,
// message: t('createOrder.ruleMsgPhoneError'),
// trigger: 'blur'
// }
// ],
})
const activeNames = ref(['1', '2', '3'])