Merge remote-tracking branch 'origin/master'

# Conflicts:
#	src/views/crm/business/BusinessForm.vue
pull/781/head
苑坤 2025-04-28 14:54:47 +08:00
commit d762e636dd
6 changed files with 175 additions and 137 deletions

View File

@ -99,6 +99,7 @@ const queryFormRef = ref() // 搜索的表单
const exportLoading = ref(false) // const exportLoading = ref(false) //
/** 打开弹窗 */ /** 打开弹窗 */
const open = async (data: []) => { const open = async (data: []) => {
console.log('%csrc/components/product/index.vue:102 data', 'color: #007acc;', data);
dialogVisible.value = true dialogVisible.value = true
multipleSelection.value = data multipleSelection.value = data
await getList() await getList()
@ -148,7 +149,13 @@ const submitForm = async () => {
emit('success', multipleSelection.value) emit('success', multipleSelection.value)
} }
const setSelections = async () => { const setSelections = async () => {
const selections = multipleSelection.value const selections = multipleSelection.value.map(item => {
return {
id: item.productId
}
})
console.log('%csrc/components/product/index.vue:153 list.value', 'color: #007acc;', list.value);
if (selections && selections.length > 0) { if (selections && selections.length > 0) {
list.value.forEach((row: any) => { list.value.forEach((row: any) => {
if (selections.some(item => item.id === row.id)) { if (selections.some(item => item.id === row.id)) {

View File

@ -535,7 +535,7 @@ const remainingRouter: AppRouteRecordRaw[] = [
component: () => import('@/views/crm/business/detail/index.vue') component: () => import('@/views/crm/business/detail/index.vue')
}, },
{ {
path: 'business/add/:id', path: 'business/add',
name: 'CrmBusinessAdd', name: 'CrmBusinessAdd',
meta: { meta: {
title: '商机新增', title: '商机新增',
@ -545,6 +545,17 @@ const remainingRouter: AppRouteRecordRaw[] = [
}, },
component: () => import('@/views/crm/business/BusinessForm.vue') component: () => import('@/views/crm/business/BusinessForm.vue')
}, },
{
path: 'business/edit',
name: 'CrmBusinesseEdit',
meta: {
title: '商机编辑',
noCache: true,
hidden: true,
activeMenu: '/crm/business'
},
component: () => import('@/views/crm/business/BusinessForm.vue')
},
{ {
path: 'contract/detail/:id', path: 'contract/detail/:id',
name: 'CrmContractDetail', name: 'CrmContractDetail',

View File

@ -23,7 +23,7 @@
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="产品单位" align="center" prop="unit"> <el-table-column label="单位" align="center" prop="unit">
<template #default="scope"> <template #default="scope">
<dict-tag :type="DICT_TYPE.CRM_PRODUCT_UNIT" :value="scope.row.unit" /> <dict-tag :type="DICT_TYPE.CRM_PRODUCT_UNIT" :value="scope.row.unit" />
</template> </template>

View File

@ -17,7 +17,7 @@
<el-form-item label="负责人" prop="ownerUserId"> <el-form-item label="负责人" prop="ownerUserId">
<el-select <el-select
v-model="formData.ownerUserId" v-model="formData.ownerUserId"
:disabled="formType !== 'create'" :disabled="formType"
placeholder="请选择负责人" placeholder="请选择负责人"
class="w-1/1" class="w-1/1"
> >
@ -34,7 +34,7 @@
<el-form-item label="需求提交人" prop="requestorUserId"> <el-form-item label="需求提交人" prop="requestorUserId">
<el-select <el-select
v-model="formData.requestorUserId" v-model="formData.requestorUserId"
:disabled="formType !== 'create'" :disabled="formType"
placeholder="请选择需求提交人" placeholder="请选择需求提交人"
class="w-1/1" class="w-1/1"
> >
@ -85,7 +85,7 @@
placeholder="请选择商机状态组" placeholder="请选择商机状态组"
clearable clearable
class="w-1/1" class="w-1/1"
:disabled="formType !== 'create'" :disabled="formType"
> >
<el-option <el-option
v-for="item in statusTypeList" v-for="item in statusTypeList"
@ -185,8 +185,9 @@
<el-tab-pane label="产品清单" name="product"> <el-tab-pane label="产品清单" name="product">
<BusinessProductForm <BusinessProductForm
ref="productFormRef" ref="productFormRef"
v-model="formData.products" :products="formData.products"
:disabled="disabled" :disabled="disabled"
@success="setList"
/> />
</el-tab-pane> </el-tab-pane>
</el-tabs> </el-tabs>
@ -222,30 +223,31 @@
</el-row> </el-row>
</el-form> </el-form>
<el-button @click="submitForm" type="primary" :disabled="formLoading"> </el-button> <el-button @click="submitForm" type="primary" :disabled="formLoading"> </el-button>
<el-button @click="dialogVisible = false"> </el-button> <el-button @click="goBack"> </el-button>
</ContentWrap> </ContentWrap>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { getIntDictOptions, getStrDictOptions, DICT_TYPE, getBoolDictOptions } from '@/utils/dict' import { ref, watch, onMounted } from 'vue';
import * as BusinessApi from '@/api/crm/business' import { getIntDictOptions, getStrDictOptions, DICT_TYPE, getBoolDictOptions } from '@/utils/dict';
import * as BusinessStatusApi from '@/api/crm/business/status' import * as BusinessApi from '@/api/crm/business';
import * as CustomerApi from '@/api/crm/customer' import * as BusinessStatusApi from '@/api/crm/business/status';
import * as UserApi from '@/api/system/user' import * as CustomerApi from '@/api/crm/customer';
import * as DeptApi from '@/api/system/dept' import * as UserApi from '@/api/system/user';
import { useUserStore } from '@/store/modules/user' import * as DeptApi from '@/api/system/dept';
import { defaultProps, handleTree } from '@/utils/tree' import { useUserStore } from '@/store/modules/user';
import BusinessProductForm from './components/BusinessProductForm.vue' import { defaultProps, handleTree } from '@/utils/tree';
import { erpPriceMultiply, erpPriceInputFormatter } from '@/utils' import BusinessProductForm from './components/BusinessProductForm.vue';
const deptTree = ref() // import { erpPriceMultiply, erpPriceInputFormatter } from '@/utils';
const { proxy }: any = getCurrentInstance();
const { t } = useI18n() //
const message = useMessage() //
const dialogVisible = ref(false) // const { proxy }: any = getCurrentInstance();
const dialogTitle = ref('') // const { t } = useI18n();
const formLoading = ref(false) // 12 const message = useMessage();
const formType = ref('') // create - update -
const dialogVisible = ref(false);
const dialogTitle = ref('');
const formLoading = ref(false);
const formType = ref('');
const formData = ref({ const formData = ref({
id: undefined, id: undefined,
name: undefined, name: undefined,
@ -260,116 +262,118 @@ const formData = ref({
statusId: undefined, statusId: undefined,
endStatus: undefined, endStatus: undefined,
dealTime: undefined, dealTime: undefined,
onlinePrice: undefined, onlinePrice: 0,
offlinePrice: undefined, offlinePrice: 0,
totalProductPrice: undefined, totalProductPrice: 0,
discountPercent: undefined, totalPrice: 0,
totalPrice: undefined,
saleStage: undefined, saleStage: undefined,
paymentTerm: undefined, paymentTerm: undefined,
creditMethod: undefined, creditMethod: undefined,
creditCalcCycle: undefined, creditCalcCycle: undefined,
creditLimit: undefined, creditLimit: undefined,
techSupport: undefined, techSupport: undefined,
products: [], products: []
}) });
const formRules = reactive({ const formRules = reactive({
name: [{ required: true, message: '商机名称不能为空', trigger: 'blur' }], name: [{ required: true, message: '商机名称不能为空', trigger: 'blur' }],
customerId: [{ required: true, message: '客户不能为空', trigger: 'blur' }], customerId: [{ required: true, message: '客户不能为空', trigger: 'blur' }],
ownerUserId: [{ required: true, message: '负责人不能为空', trigger: 'blur' }], ownerUserId: [{ required: true, message: '负责人不能为空', trigger: 'blur' }],
statusTypeId: [{ required: true, message: '商机状态组不能为空', trigger: 'blur' }] statusTypeId: [{ required: true, message: '商机状态组不能为空', trigger: 'blur' }]
}) });
const formRef = ref() // Ref const formRef = ref();
const userOptions = ref<UserApi.UserVO[]>([]) // const userOptions = ref<UserApi.UserVO[]>([]);
const statusTypeList = ref([]) // const statusTypeList = ref([]);
const customerList = ref([]) // const customerList = ref([]);
const deptTree = ref();
/** 子表的表单 */ const subTabsName = ref('product');
const subTabsName = ref('product') const productFormRef = ref();
const productFormRef = ref()
/** 计算 discountPrice、totalPrice 价格 */
watch( watch(
() => formData.value.products, () => formData.value.products,
(val) => { (newProducts) => {
if (!val) { if (!Array.isArray(newProducts)) {
return console.warn('formData.value.products is not an array');
return;
} }
const totalOnlinePrice = val.products.reduce((prev, curr) => prev + curr.onlinePrice, 0) let totalOnlinePrice = 0;
const totalOfflinePrice = val.products.reduce((prev, curr) => prev + curr.offlinePrice, 0) let totalOfflinePrice = 0;
//
formData.value.onlinePrice = totalOnlinePrice for (const product of newProducts) {
formData.value.offlinePrice = totalOfflinePrice if (typeof product.onlinePrice === 'number') {
formData.value.totalPrice = totalOnlinePrice + totalOfflinePrice totalOnlinePrice += product.onlinePrice;
}
if (typeof product.offlinePrice === 'number') {
totalOfflinePrice += product.offlinePrice;
}
}
formData.value.onlinePrice = totalOnlinePrice.toFixed(2)
formData.value.offlinePrice = totalOfflinePrice.toFixed(2)
let all = totalOnlinePrice + totalOfflinePrice
formData.value.totalPrice = all.toFixed(2)
}, },
{ deep: true } { deep: true }
) );
/** 打开弹窗 */
const open = async (type: string, id?: number, customerId?: number, contactId?: number) => { const open = async (id?: number, customerId?: number) => {
dialogVisible.value = true
dialogTitle.value = t('action.' + type)
resetForm()
//
if (id) { if (id) {
formLoading.value = true formLoading.value = true;
try { try {
formData.value = await BusinessApi.getBusiness(id) const data = await BusinessApi.getBusiness(id);
console.log('%csrc/views/crm/business/BusinessForm.vue:325 data', 'color: #007acc;', data);
formData.value = {
...data,
products: data.products || []
};
} finally { } finally {
formLoading.value = false formLoading.value = false;
} }
} else { } else {
if (customerId) { if (customerId) {
formData.value.customerId = customerId formData.value.customerId = customerId;
formData.value.customerDefault = true // formData.value.customerDefault = true;
}
// contactId
if (contactId) {
formData.value.contactId = contactId
} }
} }
// if (!formType.value) {
if (formType.value === 'create') { formData.value.ownerUserId = useUserStore().getUser.id;
formData.value.ownerUserId = useUserStore().getUser.id
} }
} };
defineExpose({ open }) // open
const setList = (newProducts) => {
formData.value.products = newProducts;
};
const { push } = useRouter()
/** 提交表单 */
const emit = defineEmits(['success']) // success
const submitForm = async () => { const submitForm = async () => {
//
// console.log('%csrc/views/crm/business/BusinessForm.vue:346 productFormRef.value.formData', 'color: #007acc;', productFormRef.value);
// formData.value.products = productFormRef.value.formData;
//
if (!productFormRef.value) return; if (!productFormRef.value) return;
const valid = await productFormRef.value.validate(); const valid = await productFormRef.value.validate();
formData.value.products = valid
if (!valid) return; if (!valid) return;
//
formLoading.value = true
try {
const data = formData.value as unknown as BusinessApi.BusinessVO
if (formType.value === 'create') {
await BusinessApi.createBusiness(data)
message.success(t('common.createSuccess'))
} else {
await BusinessApi.updateBusiness(data)
message.success(t('common.updateSuccess'))
}
dialogVisible.value = false
//
emit('success')
} finally {
formLoading.value = false
}
}
/** 重置表单 */ formLoading.value = true;
try {
const data = formData.value as unknown as BusinessApi.BusinessVO;
if (!formType.value) {
await BusinessApi.createBusiness(data);
message.success(t('common.createSuccess'));
} else {
await BusinessApi.updateBusiness(data);
message.success(t('common.updateSuccess'));
}
dialogVisible.value = false;
goBack()
} finally {
formLoading.value = false;
}
};
const goBack = ()=> {
proxy.$router.go(-1)
}
const resetForm = () => { const resetForm = () => {
formData.value = { formData.value = {
id: undefined, id: undefined,
@ -378,24 +382,22 @@ const resetForm = () => {
ownerUserId: undefined, ownerUserId: undefined,
statusTypeId: undefined, statusTypeId: undefined,
dealTime: undefined, dealTime: undefined,
totalPrice: undefined, totalPrice: 0,
products: [], products: [],
contactId: undefined, contactId: undefined,
customerDefault: false customerDefault: false
} };
formRef.value?.resetFields() formRef.value?.resetFields();
} };
/** 初始化 */
const { params } = useRoute() const route = useRoute();
onMounted(async () => { onMounted(async () => {
formType.value = params.id const customerId = route.query.customerId;
// formType.value = route.query.id;
customerList.value = await CustomerApi.getCustomerSimpleList() if (formType.value) open(formType.value, customerId)
// customerList.value = await CustomerApi.getCustomerSimpleList();
statusTypeList.value = await BusinessStatusApi.getBusinessStatusTypeSimpleList() statusTypeList.value = await BusinessStatusApi.getBusinessStatusTypeSimpleList();
// userOptions.value = await UserApi.getSimpleUserList();
userOptions.value = await UserApi.getSimpleUserList() deptTree.value = handleTree(await DeptApi.getSimpleDeptList());
// });
deptTree.value = handleTree(await DeptApi.getSimpleDeptList())
})
</script> </script>

View File

@ -10,25 +10,21 @@
> >
<el-table :data="formData" class="-mt-10px"> <el-table :data="formData" class="-mt-10px">
<el-table-column label="序号" type="index" align="center" width="60" /> <el-table-column label="序号" type="index" align="center" width="60" />
<el-table-column label="产品名称" align="center" prop="name" /> <el-table-column label="产品名称" align="center" prop="productName" />
<el-table-column label="产品分类" min-width="150"> <el-table-column label="产品类别" align="center" prop="category" width="160">
<template #default="{ row }"> <template #default="scope">
<el-form-item class="mb-0px!"> <dict-tag :type="DICT_TYPE.CRM_PRODUCT_CATEGORY" :value="scope.row.category" />
<el-input disabled v-model="row.productCategoryId" />
</el-form-item>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="条码" min-width="150"> <el-table-column label="产品明细" align="center" prop="detailType" width="160">
<template #default="{ row }"> <template #default="scope">
<el-form-item class="mb-0px!"> <dict-tag :type="DICT_TYPE.CRM_PRODUCT_DETAIL_TYPE" :value="scope.row.detailType" />
<el-input disabled v-model="row.productNo" />
</el-form-item>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="单位" min-width="80"> <el-table-column label="单位" min-width="80">
<template #default="scope"> <template #default="scope">
<dict-tag :type="DICT_TYPE.CRM_PRODUCT_UNIT" :value="scope.row.unit" /> <dict-tag :type="DICT_TYPE.CRM_PRODUCT_UNIT" :value="scope.row.productUnit" />
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="线上价格(元)" fixed="right" min-width="140"> <el-table-column label="线上价格(元)" fixed="right" min-width="140">
@ -82,7 +78,7 @@ import { ref, watch, onMounted, defineProps, defineExpose, reactive } from 'vue'
import * as ProductApi from '@/api/crm/product'; import * as ProductApi from '@/api/crm/product';
import { erpPriceInputFormatter, erpPriceMultiply } from '@/utils'; import { erpPriceInputFormatter, erpPriceMultiply } from '@/utils';
import ProductForm from '@/components/product/index.vue' import ProductForm from '@/components/product/index.vue'
import { DICT_TYPE } from '@/utils/dict'; import { DICT_TYPE, getIntDictOptions } from '@/utils/dict';
const formLoading = ref(false); // const formLoading = ref(false); //
@ -144,10 +140,28 @@ watch(
// }; // };
// formData.value.push(newRow); // formData.value.push(newRow);
// }; // };
const emit = defineEmits(['update:products']); const emit = defineEmits(['success']) // success
const getList = (val: []) => { const getList = (val: []) => {
formData.value = val for(let i = formData.value.length - 1; i >= 0; i--) {
emit('update:products', val); let obj = formData.value[i]
if(!val.some(v => v.id === obj.productId)) formData.value.splice(i, 1)
}
val.forEach(item => {
if(!formData.value.some(v => v.productId === item.id)) {
formData.value.push({
"productId": item.id,
"category": item.category,
"productName": item.name,
"detailType": item.detailType,
"productUnit": item.unit,
"onlinePrice": '',
"offlinePrice": '',
"count": ''
})
}
})
emit('success', formData.value)
} }
const productRef = ref() // Ref const productRef = ref() // Ref
const handleAdd = () => { const handleAdd = () => {

View File

@ -26,7 +26,7 @@
<Icon class="mr-5px" icon="ep:refresh" /> <Icon class="mr-5px" icon="ep:refresh" />
重置 重置
</el-button> </el-button>
<el-button v-hasPermi="['crm:business:create']" type="primary" @click="openForm('create')"> <el-button v-hasPermi="['crm:business:create']" type="primary" @click="openFormAdd()">
<Icon class="mr-5px" icon="ep:plus" /> <Icon class="mr-5px" icon="ep:plus" />
新增 新增
</el-button> </el-button>
@ -136,7 +136,7 @@
v-hasPermi="['crm:business:update']" v-hasPermi="['crm:business:update']"
link link
type="primary" type="primary"
@click="openForm('update', scope.row.id)" @click="openFormEdit(scope.row)"
> >
编辑 编辑
</el-button> </el-button>
@ -232,8 +232,12 @@ const openCustomerDetail = (id: number) => {
/** 添加/修改操作 */ /** 添加/修改操作 */
const formRef = ref() const formRef = ref()
const openForm = (id: number) => { const openFormEdit = (row: Object) => {
push({ name: 'CrmBusinessAdd', params: { id: 'create' } })
push({ name: 'CrmBusinesseEdit', query: { id: row.id, customerId: row.customerId } })
}
const openFormAdd = () => {
push({ name: 'CrmBusinessAdd' })
} }
/** 删除按钮操作 */ /** 删除按钮操作 */