Merge branch 'dev' of https://git.yfgame.vip/r/hangtag into dev

# Please enter a commit message to explain why this merge is necessary,
# especially if it merges an updated upstream into a topic branch.
#
# Lines starting with '#' will be ignored, and an empty message aborts
# the commit.
This commit is contained in:
yf 2025-03-05 00:23:58 +08:00
commit c5a8f8f032
20 changed files with 85 additions and 458 deletions

View File

@ -123,6 +123,7 @@ public class DeptDataPermissionRule implements DataPermissionRule {
return new EqualsTo(null, null); // WHERE null = null可以保证返回的数据为空
}
if(true) return null;
// 情况三拼接 Dept User 的条件最后组合
Expression deptExpression = buildDeptExpression(tableName,tableAlias, deptDataPermission.getDeptIds());
Expression userExpression = buildUserExpression(tableName, tableAlias, deptDataPermission.getSelf(), loginUser.getId());

View File

@ -1,6 +1,7 @@
package cn.hangtag.module.oms.controller.admin.saleorder.front;
import cn.hangtag.framework.common.pojo.CommonResult;
import cn.hangtag.framework.idempotent.core.annotation.Idempotent;
import cn.hangtag.module.oms.controller.admin.saleorder.front.dto.CreateSaleOrderDTO;
import cn.hangtag.module.oms.service.customer.CustomerService;
import cn.hangtag.module.oms.service.saleorder.SaleOrderService;
@ -13,6 +14,8 @@ import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import javax.validation.Valid;
import java.util.concurrent.TimeUnit;
import static cn.hangtag.framework.common.pojo.CommonResult.success;
@Tag(name = "销售订单")
@ -31,6 +34,7 @@ public class SaleOrderFrontController {
@PostMapping("/placeOrder")
@Operation(summary = "下单")
@Idempotent(timeout = 10, timeUnit = TimeUnit.SECONDS, message = "正在创建订单中,请勿重复提交")
public CommonResult<Long> placeOrder(@Valid @RequestBody CreateSaleOrderDTO dto) {
return success(saleOrderService.placeOrder(dto));
}
@ -40,10 +44,10 @@ public class SaleOrderFrontController {
return success(saleOrderService.editOrder(id,dto));
}
@GetMapping("/details/{id}")
@GetMapping("/details/{type}/{id}")
@Operation(summary = "编辑订单")
public CommonResult<CreateSaleOrderDTO> placeOrder(@PathVariable Long id) {
CreateSaleOrderDTO dto = saleOrderService.queryEditOrder(id);
public CommonResult<CreateSaleOrderDTO> placeOrder(@PathVariable String type,@PathVariable Long id) {
CreateSaleOrderDTO dto = saleOrderService.queryEditOrder(id,type);
return success(dto);
}

View File

@ -109,6 +109,12 @@ public class CreateSaleOrderDTO implements Serializable {
*/
private String currency;
/**
* 订单方式
*/
private String type;
/**
* 驳回原因
*/

View File

@ -136,7 +136,7 @@ public interface SaleOrderService {
* @param id ID
* @return {@link CreateSaleOrderDTO }
*/
CreateSaleOrderDTO queryEditOrder(Long id);
CreateSaleOrderDTO queryEditOrder(Long id,String type);
/**
* 查询上次订单品牌

View File

@ -759,6 +759,24 @@ public class SaleOrderServiceImpl implements SaleOrderService {
skuOrderSkuMapper.insertBatch(skuList);
updateCustomerInvoiceData(order);
//发送下单邮件
Long customerId = order.getCustomerId();
CustomerDO customerDO = customerMapper.selectById(customerId);
String orderFollowerUser = order.getOrderFollowerUser();
if (StringUtils.isNotBlank(orderFollowerUser) && customerDO!=null) {
AdminUserDO user = adminUserService.getUser(Long.valueOf(orderFollowerUser));
String email = user.getEmail();
MailSendMessage message = new MailSendMessage();
message.setAccountId(1L);
message.setMail(email);
message.setTitle("OMS订单系统");
StringBuffer content = new StringBuffer();
content.append("您好,客户:"+customerDO.getName()+" 来新的订单啦,订单号:" + order.getBillno() + ",下单时间:"+DateUtil.date().toString()+" 请进行处理!");
message.setContent(content.toString());
mailSendService.doSendMail(message);
}
return order.getId();
}
@ -766,7 +784,7 @@ public class SaleOrderServiceImpl implements SaleOrderService {
@Transactional(rollbackFor = Exception.class)
public Long editOrder(Long id, CreateSaleOrderDTO dto) {
SaleOrderDO saleOrderDO = checkEditOrder(id);
SaleOrderDO saleOrderDO = checkEditOrder(id,null);
// 校验订单
SaleOrderDO order = new SaleOrderDO(dto);
order.setId(saleOrderDO.getId());
@ -1119,9 +1137,9 @@ public class SaleOrderServiceImpl implements SaleOrderService {
}
@Override
public CreateSaleOrderDTO queryEditOrder(Long id) {
public CreateSaleOrderDTO queryEditOrder(Long id,String type) {
CreateSaleOrderDTO res = new CreateSaleOrderDTO();
SaleOrderDO saleOrderDO = checkEditOrder(id);
SaleOrderDO saleOrderDO = checkEditOrder(id,type);
BeanUtil.copyProperties(saleOrderDO, res);
LambdaQueryWrapper<SaleOrderEntryDO> entryQueryWrapper = new LambdaQueryWrapper<>();
entryQueryWrapper.eq(SaleOrderEntryDO::getParentId, saleOrderDO.getId());
@ -1327,7 +1345,7 @@ public class SaleOrderServiceImpl implements SaleOrderService {
}
}
private SaleOrderDO checkEditOrder(Long id) {
private SaleOrderDO checkEditOrder(Long id,String type) {
SaleOrderDO saleOrderDO = saleOrderMapper.selectById(id);
AssertUtil.isEmpty(saleOrderDO, "订单不存在");
String billStatus = saleOrderDO.getBillStatus();
@ -1335,7 +1353,9 @@ public class SaleOrderServiceImpl implements SaleOrderService {
boolean order1 = BillStatusEnum.isCanEditOrder(billStatus);
BillStatusEnum byValue = BillStatusEnum.getByValue(billStatus);
AssertUtil.isEmpty(byValue, "订单状态异常");
AssertUtil.isTrue(!order1, byValue.getName() + "状态不允许修改");
if(!StringUtils.isNotBlank(type)){
AssertUtil.isTrue(!order1, byValue.getName() + "状态不允许修改");
}
return saleOrderDO;
}

View File

@ -133,10 +133,10 @@ public class AdminUserServiceImpl implements AdminUserService {
public void updateUser(UserSaveReqVO updateReqVO) {
updateReqVO.setPassword(null); // 特殊此处不更新密码
updateReqVO.setUsername(updateReqVO.getEmail());
validateUsernameUnique(updateReqVO.getId(),updateReqVO.getUsername());
// 1. 校验正确性
AdminUserDO oldUser = validateUserForCreateOrUpdate(updateReqVO.getId(), updateReqVO.getUsername(),
updateReqVO.getMobile(), updateReqVO.getEmail(), updateReqVO.getDeptId(), updateReqVO.getPostIds());
// 2.1 更新用户
AdminUserDO updateObj = BeanUtils.toBean(updateReqVO, AdminUserDO.class);
userMapper.updateById(updateObj);
@ -292,10 +292,10 @@ public class AdminUserServiceImpl implements AdminUserService {
@Override
public AdminUserDO getUser(Long id) {
AdminUserDO adminUserDO = ADMIN_USERS_CACHE.get(id);
if(FuncUtil.isEmpty(adminUserDO)){
//if(FuncUtil.isEmpty(adminUserDO)){
adminUserDO = userMapper.selectById(id);
ADMIN_USERS_CACHE.put(id,adminUserDO);
}
// ADMIN_USERS_CACHE.put(id,adminUserDO);
//}
return adminUserDO;
}

View File

@ -1,71 +0,0 @@
<template>
<Dialog :title="dialogTitle" append-to-body v-model="dialogVisible">
<Form :disabled="disabled" ref="formRef" :schema="allSchemas.formSchema" :rules="rules" v-loading="formLoading" />
<template #footer>
<el-button v-if="!disabled" @click="submitForm" type="primary" :disabled="formLoading"> </el-button>
<el-button @click="dialogVisible = false"> </el-button>
</template>
</Dialog>
</template>
<script setup lang="ts">
import { rules, allSchemas } from './config.data'
import {CustomerGroupApi, CustomerGroupVO} from "@/api/oms/customergroup";
const { t } = useI18n() //
const message = useMessage() //
const dialogVisible = ref(false) //
const dialogTitle = ref('') //
const formLoading = ref(false) // 12
const formType = ref('') // create - update -
const formRef = ref() // Ref
const disabled = computed(() => {
return formType.value !== 'create' && formType.value !== 'update'
})
/** 打开弹窗 */
const open = async (type: string, id?: number) => {
dialogVisible.value = true
dialogTitle.value = t('action.' + type)
formType.value = type
//
if (id) {
formLoading.value = true
try {
const data = await CustomerGroupApi.getCustomerGroup(id)
formRef.value.setValues(data)
} finally {
formLoading.value = false
}
}
}
defineExpose({ open }) // open
/** 提交表单 */
const emit = defineEmits(['success']) // success
const submitForm = async () => {
//
if (!formRef) return
const valid = await formRef.value.getElFormRef().validate()
if (!valid) return
//
formLoading.value = true
try {
const data = formRef.value.formModel as CustomerGroupVO
if (formType.value === 'create') {
await CustomerGroupApi.createCustomerGroup(data)
message.success(t('common.createSuccess'))
} else {
await CustomerGroupApi.updateCustomerGroup(data)
message.success(t('common.updateSuccess'))
}
dialogVisible.value = false
//
emit('success')
} finally {
formLoading.value = false
}
}
</script>

View File

@ -1,33 +0,0 @@
import type { CrudSchema } from '@/hooks/web/useCrudSchemas'
// 表单校验
export const rules = reactive({
})
// CrudSchema https://doc.iocoder.cn/vue3/crud-schema/
const crudSchemas = reactive<CrudSchema[]>([
{
label: 'code',
field: 'code',
width: 200,
isSearch: true,
},
{
label: 'name',
field: 'name',
isSearch: true,
},
{
label: 'remark',
field: 'remark',
isTable: false,
},
{
label: 'options',
field: 'action',
isForm: false
}
])
export const { allSchemas } = useCrudSchemas(crudSchemas)

View File

@ -1,319 +0,0 @@
<template>
<slot>
<div>
<el-input
v-model="that.showValue"
clearable
:placeholder="props.placeholder"
@clear="clearData"
@click="viewDetails"
>
<template #append>
<el-button @click.stop="openDialog">
<Icon icon="ep:search"/>
</el-button>
</template>
</el-input>
</div>
</slot>
{{tmp}}
<Dialog
:title="dialogTitle"
width="80%"
append-to-body
v-model="that.visible"
@close="updateVisible(false)">
<div>
<!-- 搜索工作栏 -->
<ContentWrap>
<Search
:schema="allSchemas.searchSchema"
:is-col="false"
@search="setSearchParams"
@reset="setSearchParams">
<!-- 新增等操作按钮 -->
<template #actionMore>
<el-button v-if="isCanEdit"
type="primary"
plain
@click="openForm('create')"
v-hasPermi="['oms:draft-design-data:create']"
>
<Icon icon="ep:plus" class="mr-5px"/>
新增
</el-button>
<div v-else>
</div>
</template>
</Search>
</ContentWrap>
<!-- 列表 -->
<ContentWrap>
<Table
:columns="allSchemas.tableColumns"
:data="tableObject.tableList"
:loading="tableObject.loading"
:selection="true"
ref="tableRef"
@selection-change="selectionChange"
:pagination="{ total: tableObject.total
}"
v-model:pageSize="tableObject.pageSize"
v-model:currentPage="tableObject.currentPage"
>
<template #action="{ row }">
<div v-if="isCanEdit">
<el-button
link
type="primary"
@click="openForm('update', row.id)"
>
编辑
</el-button>
<el-button
link
type="danger"
@click="handleDelete(row.id)"
>
删除
</el-button>
</div>
<div v-else>
-
</div>
</template>
</Table>
</ContentWrap>
</div>
<template #footer>
<span class="dialog-footer">
<el-button @click="updateVisible(false,true)">{{ t('common.cancel') }}</el-button>
<el-button type="primary" @click="submit">{{ t('common.ok') }}</el-button>
</span>
</template>
</Dialog>
<!-- 表单弹窗添加/修改 -->
<DataForm ref="formRef" @success="getList"/>
</template>
<script lang="ts" setup name="ProductTypeDataListDialog">
import {allSchemas} from './config.data'
import DataForm from './DataForm.vue'
import {CustomerGroupApi} from "@/api/oms/customergroup";
defineOptions({name: 'CustomerGroupDataListDialog'})
const emit = defineEmits(['update:modelValue', 'update:visible', 'submit']) // success
const {t} = useI18n() //
const dialogTitle = ref('Customer group') //
const props = defineProps({
modelValue: {
type: [String,Number],
required: true
},
dataKey: {
type: String,
required: false,
default: 'id'
},
showKey: {
type: String,
required: false,
default: 'name'
},
multiple: {
type: Boolean,
required: false,
default: false
},
isCanEdit: {
type: Boolean,
required: false,
default: false
},
placeholder: {
type: String,
required: false,
default: 'selectText'
},
width: {
type: String,
required: false,
default: '64px'
},
height: {
type: String,
required: false,
default: '64px'
},
visible: {
type: Boolean,
required: false,
default: false
}
})
const that = reactive({
inputVal: '',
showValue: '',
visible: false,
})
const toStr = (data: any, def = '') => {
if (data !== null && data !== undefined) {
return `${data}`
}
return def
}
const openDialog = () => {
updateVisible(true);
}
const viewDetails = () => {
if (that.inputVal) {
openForm("preview", that.inputVal.split(",")[0])
} else {
openDialog();
}
}
let map = new Map();
const initInput = async () => {
const dataKey = that.inputVal + ',' + props.dataKey + ',' + props.showKey + ',' + props.multiple;
if (map.has(dataKey)) {
const data = map.get(dataKey)
if (data) {
that.inputVal = data.inputVal
that.showValue = data.showValue
console.log('缓存数据', data)
return;
}
}
const ids = that.inputVal.split(",");
let tmpInput = [];
let tmpShow = [];
for (let i = 0; i < ids.length; i++) {
const data = await CustomerGroupApi.getCustomerGroup(ids[i])
tmpInput.push(data[props.dataKey]);
tmpShow.push(data[props.showKey]);
}
that.inputVal = tmpInput.join(',');
that.showValue = tmpShow.join(',');
map.set(dataKey, {
inputVal: that.inputVal,
showValue: that.showValue
})
}
watch(() => props.visible, (newVal) => {
that.visible = newVal;
})
watch(() => props.modelValue, (newVal)=>{
that.inputVal = toStr(newVal,'')
if (that.inputVal) {
initInput();
}
},{
immediate: true
})
//
const tmp = computed(()=>{
setTimeout(()=>{
that.inputVal = toStr(props.modelValue,that.inputVal)
if (that.inputVal) {
initInput();
}
},100)
return ''
},{deep: true})
const clearData = () => {
that.inputVal = '';
that.showValue = '';
updateValue();
}
const updateVisible = (visible: boolean, clearInput = false) => {
that.visible = visible;
emit("update:visible", visible)
if (clearInput) {
clearData();
}
}
defineExpose({}) // open
const submit = () => {
updateValue();
updateVisible(false)
}
const updateValue = () => {
emit("update:modelValue", that.inputVal)
}
//
const selectionChange = (row) => {
if (row && row.length > 0) {
if (props.multiple) {
that.inputVal = row.map(item => item[props.dataKey || 'id']).join(',')
that.showValue = row.map(item => item[props.showKey || 'id']).join(',')
} else {
if (row.length > 1) {
useMessage().warning('单选数据,已忽略其他')
}
that.showValue = row[row.length - 1][props.showKey || 'id']
that.inputVal = row[row.length - 1][props.dataKey || 'id']
}
} else {
that.inputVal = ''
that.showValue = ''
}
}
const {tableObject, tableMethods} = useTable({
getListApi: CustomerGroupApi.getCustomerGroupPage, //
delListApi: CustomerGroupApi.deleteCustomerGroup //
})
//
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(() => {
getList()
})
</script>
<style lang="scss" scoped>
:deep(.el-input__wrapper) {
position: relative;
.el-input__inner {
padding-right: 18px;
}
.el-input__suffix {
position: absolute;
right: 8px;
top: 50%;
transform: translateY(-50%);
}
}
</style>

View File

@ -224,7 +224,7 @@ export default {
toDo: '待办',
introduction: '一个正经的简介',
shortcutOperation: '快捷入口',
operationData: '运营数据',
operationData: '待处理任务',
operation: '操作',
index: '指数',
personal: '个人',

View File

@ -56,7 +56,7 @@
<ShortcutCard />
</el-col>
<el-col :md="12">
<!-- 运营数据 -->
<!-- 待处理任务 -->
<OperationDataCard />
</el-col>
</el-row>

View File

@ -1,7 +1,7 @@
<template>
<el-card shadow="never">
<template #header>
<CardTitle title="运营数据" />
<CardTitle title="待处理任务" />
</template>
<div class="flex flex-row flex-wrap items-center gap-8 p-4">
<div
@ -25,7 +25,7 @@
import * as TradeStatisticsApi from '@/api/oms/statistics/trade'
import { CardTitle } from '@/components/Card'
/** 运营数据卡片 */
/** 待处理任务卡片 */
defineOptions({ name: 'OperationDataCard' })
const router = useRouter() //

View File

@ -109,11 +109,11 @@
>
<el-table-column width="30" label="选择" type="selection" />
<el-table-column label="ID" align="center" v-if="false" prop="id" />
<el-table-column label="编码" align="center" prop="number" width="120"/>
<el-table-column label="名称" align="center" prop="name" />
<el-table-column label="公司" align="center" prop="company" />
<el-table-column label="邮箱" align="center" prop="email" />
<el-table-column label="联系人" align="center" prop="contacts" />
<el-table-column label="编码" align="center" prop="number" width="150"/>
<el-table-column label="名称" align="center" prop="name" width="210"/>
<el-table-column label="公司" align="center" prop="company" width="210"/>
<el-table-column label="邮箱" align="center" prop="email" width="230"/>
<el-table-column label="联系人" align="center" prop="contacts" width="150"/>
<el-table-column label="联系人手机号" align="center" prop="phone" width="120"/>
<el-table-column label="所属地区" align="center" prop="areaId" />
<el-table-column label="数据状态" align="center" prop="status">

View File

@ -120,8 +120,8 @@ export const SaleOrderApi = {
return await request.post({ url: `/front/oms/sale-order/placeOrder`, data })
},
// 查询编辑订单
queryEditById: async (id: string) => {
return await request.get({ url: `/front/oms/sale-order/details/${id}` })
queryEditById: async (id: string,type: string) => {
return await request.get({ url: `/front/oms/sale-order/details/${type}/${id}` })
},
// 修改订单
editOrder: async (id: string,data: any) => {

View File

@ -507,6 +507,7 @@ export default {
waitAudit:'Wait Audit',
waitArrange:'Wait Arrange',
planned:'Planned',
copy:'copy',
edit:'edit',
details:'details',
operate:'operate',

View File

@ -224,7 +224,7 @@ export default {
toDo: '待办',
introduction: '一个正经的简介',
shortcutOperation: '快捷入口',
operationData: '运营数据',
operationData: '待处理任务',
operation: '操作',
index: '指数',
personal: '个人',
@ -502,6 +502,7 @@ export default {
waitAudit:'待审核',
waitArrange:'待排产',
planned:'已排产',
copy:'复制',
edit:'编辑',
details:'详情',
operate:'操作',

View File

@ -26,7 +26,7 @@
<ShortcutCard />
</el-col>
<el-col :md="12">
<!-- 运营数据 -->
<!-- 待处理任务 -->
<OperationDataCard />
</el-col>
</el-row>

View File

@ -25,7 +25,7 @@
import * as TradeStatisticsApi from '@/api/oms/statistics/trade'
import { CardTitle } from '@/components/Card'
/** 运营数据卡片 */
/** 待处理任务卡片 */
defineOptions({ name: 'OperationDataCard' })
const { t } = useI18n()
const router = useRouter() //

View File

@ -181,7 +181,7 @@
<el-row>
<el-col :span="12">
<el-form-item :label="t('createOrder.labelCurrency')" prop="orderFollowerUser">
<el-form-item :label="t('createOrder.labelCurrency')" prop="currency">
<el-select
v-model="formData.currency"
filterable
@ -490,6 +490,7 @@ const list = ref([]) // 列表的数据
const total = ref(0) //
const queryParams = reactive({
id: null,
type: null,
pageNo: 1,
pageSize: 10,
name: null,
@ -557,7 +558,7 @@ const formData = ref({
accessoryFileIds: '',
})
const isEditState = computed(() => {
return route.query.id != undefined
return route.query.id != undefined && route.query.type == undefined
})
const formRules = reactive({
contactName: [{required: true, message: t('createOrder.ruleMsgContactName'), trigger: 'blur'}],
@ -706,8 +707,10 @@ const submitPreHandler = (showMsg = true) => {
const addNewBill = () => {
console.log("stepRef.value.getTableData()", stepRef.value.getTableData())
submitPreHandler().then(res => {
formData.value.type = queryParams.type
console.log("formData222", formData.value)
if (formData.value.id) {
debugger;
if (formData.value.id && formData.value.type==undefined) {
SaleOrderApi.editOrder(formData.value.id, {
...formData.value
}).then(res => {
@ -735,10 +738,10 @@ const addNewBill = () => {
useMessage().warning("Error" + e.message ? e.message : e)
})
} else {
formData.value.id = null
SaleOrderApi.placeOrder({
...formData.value
}).then(res => {
useMessage().confirm(t('createOrder.tipsLoadingOrderCompleted'),{
confirmButtonText: t('createOrder.backHome'),
cancelButtonText: t('createOrder.viewOrder'),
@ -775,7 +778,8 @@ const addNewBill = () => {
onMounted(() => {
queryParams.id = '';
if (route.query) {
queryParams.id = route.query.id ?? ''
queryParams.id = route.query.id ?? '';
queryParams.type = route.query.type ?? '';
}
})
@ -834,7 +838,7 @@ onMounted(async () => {
}
//
if (queryParams.id) {
const res = await SaleOrderApi.queryEditById(queryParams.id);
const res = await SaleOrderApi.queryEditById(queryParams.id,queryParams.type);
formData.value = res
formData.value.bizdate = formatDate(new Date(res.bizdate), 'YYYY-MM-DD hh:mm:ss');
that.tmpFormData.planDate = formatDate(new Date(res.plansenddate), 'YYYY-MM-DD');
@ -843,6 +847,12 @@ onMounted(async () => {
...formData.value,
id: queryParams.id
}
if(queryParams.type!= undefined){ //
formData.value.contractCode = null;
formData.value.bizdate = null;
that.tmpFormData.planDate = calculateDateAfterDays(7);
}
console.log("res.saleOrderEntry",res.saleOrderEntry)
stepRef.value.init(res.saleOrderEntry ?? []);
}

View File

@ -233,9 +233,16 @@
:formatter="dateFormatter"
width="180px"
/>
<el-table-column :label="t('billlist.operate')" align="center" width="150" fixed="right">
<el-table-column :label="t('billlist.operate')" align="center" width="230" fixed="right">
<template #default="scope">
<router-link :to="'/order/createorder?id='+scope.row.id+'&type=copyAdd&_t=2'" >
<el-button
link
type="primary">
<Icon icon="ep:copydocument" />
{{t('billlist.copy')}}
</el-button>
</router-link>
<router-link :to="'/order/createorder?id='+scope.row.id+'&_t=2'" v-if="scope.row.billStatus == 'B' ">
<el-button
link