# Conflicts:
#	.env.local
pull/781/head
felix 2025-04-28 12:22:24 +08:00
commit 9546839b34
42 changed files with 21362 additions and 423 deletions

4
.vscode/launch.json vendored
View File

@ -5,9 +5,9 @@
"version": "0.2.0",
"configurations": [
{
"type": "msedge",
"type": "chrome",
"request": "launch",
"name": "Launch Edge against localhost",
"name": "Launch chrome against localhost",
"url": "http://localhost",
"webRoot": "${workspaceFolder}/src",
"sourceMaps": true

17494
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,41 @@
import request from '@/config/axios'
// 票据模版 VO
export interface BillTemplateVO {
id: number // 主键
name: string // 名称
status: number // 状态
}
// 票据模版 API
export const BillTemplateApi = {
// 查询票据模版分页
getBillTemplatePage: async (params: any) => {
return await request.get({ url: `/crm/bill-template/page`, params })
},
// 查询票据模版详情
getBillTemplate: async (id: number) => {
return await request.get({ url: `/crm/bill-template/get?id=` + id })
},
// 新增票据模版
createBillTemplate: async (data: BillTemplateVO) => {
return await request.post({ url: `/crm/bill-template/create`, data })
},
// 修改票据模版
updateBillTemplate: async (data: BillTemplateVO) => {
return await request.put({ url: `/crm/bill-template/update`, data })
},
// 删除票据模版
deleteBillTemplate: async (id: number) => {
return await request.delete({ url: `/crm/bill-template/delete?id=` + id })
},
// 导出票据模版 Excel
exportBillTemplate: async (params) => {
return await request.download({ url: `/crm/bill-template/export-excel`, params })
},
}

View File

@ -0,0 +1,46 @@
import request from '@/config/axios'
// 票据模版产品关联 VO
export interface BillTemplateProductVO {
id: number // 主键
billTemplateId: number // 票据模版ID
productId: number // 产品ID
productInvoice: number // 产品票据
productInvoiceItems: number // 产品开票项目
serviceFeeInvoice: number // 服务费票据
serviceFeeInvoiceItems: number // 服务费开票项目
status: number // 状态
}
// 票据模版产品关联 API
export const BillTemplateProductApi = {
// 查询票据模版产品关联分页
getBillTemplateProductPage: async (params: any) => {
return await request.get({ url: `/crm/bill-template-product/page`, params })
},
// 查询票据模版产品关联详情
getBillTemplateProduct: async (id: number) => {
return await request.get({ url: `/crm/bill-template-product/get?id=` + id })
},
// 新增票据模版产品关联
createBillTemplateProduct: async (data: BillTemplateProductVO) => {
return await request.post({ url: `/crm/bill-template-product/create`, data })
},
// 修改票据模版产品关联
updateBillTemplateProduct: async (data: BillTemplateProductVO) => {
return await request.put({ url: `/crm/bill-template-product/update`, data })
},
// 删除票据模版产品关联
deleteBillTemplateProduct: async (id: number) => {
return await request.delete({ url: `/crm/bill-template-product/delete?id=` + id })
},
// 导出票据模版产品关联 Excel
exportBillTemplateProduct: async (params) => {
return await request.download({ url: `/crm/bill-template-product/export-excel`, params })
},
}

View File

@ -21,7 +21,6 @@ export interface BusinessVO {
dealTime: Date
totalProductPrice: number
totalPrice: number
discountPercent: number
remark: string
creator: string // 创建人
creatorName?: string // 创建人名称

View File

@ -1,36 +1,45 @@
import request from '@/config/axios'
import { TransferReqVO } from '@/api/crm/permission'
import { ContractAAuthorizedCompanyVO } from '@/api/crm/contractaauthorizedcompany'
import { ContractAAuthorizedPersonVO } from '@/api/crm/contractaauthorizedperson'
import { ContractBAuthorizedPersonVO } from '@/api/crm/contractbauthorizedperson'
export interface ContractVO {
id: number
name: string
no: string
customerId: number
customerName?: string
businessId: number
businessName: string
contactLastTime: Date
ownerUserId: number
ownerUserName?: string
ownerUserDeptName?: string
processInstanceId: number
auditStatus: number
orderDate: Date
startTime: Date
endTime: Date
totalProductPrice: number
discountPercent: number
totalPrice: number
totalReceivablePrice: number
signContactId: number
signContactName?: string
signUserId: number
signUserName: string
remark: string
createTime?: Date
creator: string
creatorName: string
updateTime?: Date
id: number // 编号,主键自增
name: string // 合同名称
no: string // 合同编号
customerId: number // 客户编号
quotationId: number // 报价单编号
invoiceTemplateId: number // 票据模板Id
processInstanceId: string // 工作流编号
auditStatus: number // 审批状态
startTime: Date // 合同签订日期
endTime: Date // 合同结束时间
penaltyRate: number // 违约金比例
latePaymentRate: number // 延期付款利率
settleMethod: string // 财务结算方式 1按月 2按项目
lastPayDate: Date // 最晚付款日期
contractTerm: number // 合同期限(月)
signUserId: number // 报价签约人
signPhoneNumber: number // 签约人联系电话
signEmail: string // 签约人Email
signWechat: string // 签约人微信
offlinePrice: number // 线下总金额,单位:元
onlinePrice: number // 线上总金额,单位:元
ownerUserId: number // 商机负责人
expanderUserId: number // 拓展人
pricingUserId: number // 方案报价人
afterSaleUserId: number // 售后维护人
collUserId: number // 协作人id
totalPrice: number // 合同总金额
contractBody: string // 合同正文
contractAgreement: string // 合同补充协议
creditMethod: number // 授信方式
creditCalcCycle: number // 授信计算周期
creditLimit: number // 授信额度
partnerCompanyId: number // 合作主体 关联部门id
deptId: number // 归属部门Id
quotationTimes: number // 第几次报价
products?: [
{
id: number
@ -44,6 +53,9 @@ export interface ContractVO {
totalPrice: number
}
]
contractAAuthorizedCompanys? : ContractAAuthorizedCompanyVO[]
contractAAuthorizedPersons? : ContractAAuthorizedPersonVO[]
contractBAuthorizedPersons? : ContractBAuthorizedPersonVO[]
}
// 查询 CRM 合同列表
@ -112,3 +124,30 @@ export const getAuditContractCount = async () => {
export const getRemindContractCount = async () => {
return await request.get({ url: '/crm/contract/remind-count' })
}
// ==================== 子表CRM 合同产品关联) ====================
// 获得CRM 合同产品关联列表
export const getContractProductListByContractId = async (contractId) => {
return await request.get({ url: `/crm/contract/contract-product/list-by-contract-id?contractId=` + contractId })
}
// ==================== 子表(合同甲方关联单位) ====================
// 获得合同甲方关联单位列表
export const getContractAAuthorizedCompanyListByContractId = async (contractId) => {
return await request.get({ url: `/crm/contract/contract-A-authorized-company/list-by-contract-id?contractId=` + contractId })
}
// ==================== 子表(合同甲方授权人信息) ====================
// 获得合同甲方授权人信息列表
export const getContractAAuthorizedPersonListByContractId = async (contractId) => {
return await request.get({ url: `/crm/contract/contract-A-authorized-person/list-by-contract-id?contractId=` + contractId })
}
// ==================== 子表(合同乙方授权人信息) ====================
// 获得合同乙方授权人信息列表
export const getContractBAuthorizedPersonListByContractId = async (contractId) => {
return await request.get({ url: `/crm/contract/contract-B-authorized-person/list-by-contract-id?contractId=` + contractId })
}

View File

@ -0,0 +1,48 @@
import request from '@/config/axios'
// 合同甲方关联单位 VO
export interface ContractAAuthorizedCompanyVO {
id: number // ID
contractId: number // 合同ID
customerName: string // 单位名称
unifiedCreditCode: string // 统一社会信用代码
invoicingAddress: string // 开票地址
invoicingTelephone: string // 开票电话
companyBank: string // 开户行
companyAccount: string // 开户账号
companyAddress: string // 通讯地址
deleted: number // 删除标记1-正常,-1-已删除
}
// 合同甲方关联单位 API
export const ContractAAuthorizedCompanyApi = {
// 查询合同甲方关联单位分页
getContractAAuthorizedCompanyPage: async (params: any) => {
return await request.get({ url: `/crm/contract-A-authorized-company/page`, params })
},
// 查询合同甲方关联单位详情
getContractAAuthorizedCompany: async (id: number) => {
return await request.get({ url: `/crm/contract-A-authorized-company/get?id=` + id })
},
// 新增合同甲方关联单位
createContractAAuthorizedCompany: async (data: ContractAAuthorizedCompanyVO) => {
return await request.post({ url: `/crm/contract-A-authorized-company/create`, data })
},
// 修改合同甲方关联单位
updateContractAAuthorizedCompany: async (data: ContractAAuthorizedCompanyVO) => {
return await request.put({ url: `/crm/contract-A-authorized-company/update`, data })
},
// 删除合同甲方关联单位
deleteContractAAuthorizedCompany: async (id: number) => {
return await request.delete({ url: `/crm/contract-A-authorized-company/delete?id=` + id })
},
// 导出合同甲方关联单位 Excel
exportContractAAuthorizedCompany: async (params) => {
return await request.download({ url: `/crm/contract-A-authorized-company/export-excel`, params })
}
}

View File

@ -0,0 +1,48 @@
import request from '@/config/axios'
// 合同甲方授权人信息 VO
export interface ContractAAuthorizedPersonVO {
id: number // ID
contractId: number // 合同ID
customerContactId: number // 客户联系人ID
customerName: string // 姓名
authPersonType: number // 授权人类型
phoneNumber: string // 手机号
wechat: string // 微信号
idNumber: string // 身份证号
email: string // 电子邮箱
deleted: number // 删除标记1-正常,-1-已删除
}
// 合同甲方授权人信息 API
export const ContractAAuthorizedPersonApi = {
// 查询合同甲方授权人信息分页
getContractAAuthorizedPersonPage: async (params: any) => {
return await request.get({ url: `/crm/contract-A-authorized-person/page`, params })
},
// 查询合同甲方授权人信息详情
getContractAAuthorizedPerson: async (id: number) => {
return await request.get({ url: `/crm/contract-A-authorized-person/get?id=` + id })
},
// 新增合同甲方授权人信息
createContractAAuthorizedPerson: async (data: ContractAAuthorizedPersonVO) => {
return await request.post({ url: `/crm/contract-A-authorized-person/create`, data })
},
// 修改合同甲方授权人信息
updateContractAAuthorizedPerson: async (data: ContractAAuthorizedPersonVO) => {
return await request.put({ url: `/crm/contract-A-authorized-person/update`, data })
},
// 删除合同甲方授权人信息
deleteContractAAuthorizedPerson: async (id: number) => {
return await request.delete({ url: `/crm/contract-A-authorized-person/delete?id=` + id })
},
// 导出合同甲方授权人信息 Excel
exportContractAAuthorizedPerson: async (params) => {
return await request.download({ url: `/crm/contract-A-authorized-person/export-excel`, params })
}
}

View File

@ -0,0 +1,50 @@
import request from '@/config/axios'
// 合同乙方授权人信息 VO
export interface ContractBAuthorizedPersonVO {
id: number // ID
contractId: number // 合同ID
userId: number // 用户ID
name: string // 姓名
authType: number // 类别:商旅负责人、技术负责人、服务对接人
postId: number // 岗位
userRank: number // 职级
authDesc: string // 权限说明
phoneNumber: string // 手机号
wechat: string // 微信号
email: string // 电子邮箱
deleted: number // 删除标记1-正常,-1-已删除
}
// 合同乙方授权人信息 API
export const ContractBAuthorizedPersonApi = {
// 查询合同乙方授权人信息分页
getContractBAuthorizedPersonPage: async (params: any) => {
return await request.get({ url: `/crm/contract-B-authorized-person/page`, params })
},
// 查询合同乙方授权人信息详情
getContractBAuthorizedPerson: async (id: number) => {
return await request.get({ url: `/crm/contract-B-authorized-person/get?id=` + id })
},
// 新增合同乙方授权人信息
createContractBAuthorizedPerson: async (data: ContractBAuthorizedPersonVO) => {
return await request.post({ url: `/crm/contract-B-authorized-person/create`, data })
},
// 修改合同乙方授权人信息
updateContractBAuthorizedPerson: async (data: ContractBAuthorizedPersonVO) => {
return await request.put({ url: `/crm/contract-B-authorized-person/update`, data })
},
// 删除合同乙方授权人信息
deleteContractBAuthorizedPerson: async (id: number) => {
return await request.delete({ url: `/crm/contract-B-authorized-person/delete?id=` + id })
},
// 导出合同乙方授权人信息 Excel
exportContractBAuthorizedPerson: async (params) => {
return await request.download({ url: `/crm/contract-B-authorized-person/export-excel`, params })
}
}

View File

@ -8,6 +8,9 @@ export interface ProductVO {
price: number
status: number
categoryId: number
offlinePrice: number
onlinePrice: number
totalPrice: number
categoryName?: string
description: string
ownerUserId: number

View File

@ -0,0 +1,74 @@
import request from '@/config/axios'
// CRM 方案报价 VO
export interface QuotationVO {
no: string // 报价单编号
customerId: number // 客户id
businessId: number // 商机编号
invoiceTemplateId: number // 票据模板Id
processInstanceId: string // 工作流编号
auditStatus: number // 审批状态
ownerUserId: number // 商机负责人
expanderUserId: number // 拓展人
pricingUserId: number // 方案报价人
signUserId: number // 报价签约人
signPhoneNumber: number // 签约人联系电话
signEmail: string // 签约人Email
signWechat: string // 签约人微信
startTime: Date // 开始时间
totalPrice: number // 合同总金额
paymentTerm: string // 账期
creditMethod: number // 授信方式
creditCalcCycle: number // 授信计算周期
creditLimit: number // 授信额度
partnerCompanyId: number // 合作主体 关联部门id
deptId: number // 归属部门Id
creator: string // 创建者
createTime: Date // 创建时间
updater: string // 更新者
}
// CRM 方案报价 API
export const QuotationApi = {
// 查询CRM 方案报价分页
getQuotationPage: async (params: any) => {
return await request.get({ url: `/crm/quotation/page`, params })
},
// 查询CRM 方案报价详情
getQuotation: async (id: number) => {
return await request.get({ url: `/crm/quotation/get?id=` + id })
},
// 获得 CRM 方案列表(精简)
getSimpleQuotationList : async () => {
return await request.get({ url: `/crm/quotation/simple-all-list` })
},
// 新增CRM 方案报价
createQuotation: async (data: QuotationVO) => {
return await request.post({ url: `/crm/quotation/create`, data })
},
// 修改CRM 方案报价
updateQuotation: async (data: QuotationVO) => {
return await request.put({ url: `/crm/quotation/update`, data })
},
// 删除CRM 方案报价
deleteQuotation: async (id: number) => {
return await request.delete({ url: `/crm/quotation/delete?id=` + id })
},
// 导出CRM 方案报价 Excel
exportQuotation: async (params) => {
return await request.download({ url: `/crm/quotation/export-excel`, params })
},
// ==================== 子表CRM 报价产品关联) ====================
// 获得CRM 报价产品关联列表
getQuotationProductListByQuotationId: async (quotationId) => {
return await request.get({ url: `/crm/quotation/quotation-product/list-by-quotation-id?quotationId=` + quotationId })
}
}

View File

@ -0,0 +1,163 @@
<template>
<Dialog title="添加商品" v-model="dialogVisible" width="50%">
<!-- 搜索工作栏 -->
<el-form
class="-mb-15px"
:model="queryParams"
ref="queryFormRef"
:inline="true"
label-width="100px"
>
<el-form-item label="单位名称" prop="name">
<el-input
v-model="queryParams.name"
placeholder="请输入单位名称"
clearable
class="!w-240px"
/>
</el-form-item>
<el-form-item>
<el-button @click="handleQuery">
<Icon class="mr-5px" icon="ep:search" />
搜索
</el-button>
<el-button @click="resetQuery">
<Icon class="mr-5px" icon="ep:refresh" />
重置
</el-button>
</el-form-item>
</el-form>
<!-- 列表 -->
<ContentWrap>
<el-table ref="multipleTableRef" v-loading="loading" :data="list" @selection-change="handleSelectionChange" :row-key="getRowKey" :stripe="true" :show-overflow-tooltip="true">
<el-table-column label="序号" align="center" type="selection" :reserve-selection="true" />
<el-table-column label="产品名称" align="center" prop="name" />
<el-table-column label="产品类型" align="center" prop="category" width="160">
<template #default="scope">
<dict-tag :type="DICT_TYPE.CRM_PRODUCT_CATEGORY" :value="scope.row.category" />
</template>
</el-table-column>
<el-table-column label="产品明细类型" align="center" prop="detailType" width="160">
<template #default="scope">
<dict-tag :type="DICT_TYPE.CRM_PRODUCT_DETAIL_TYPE" :value="scope.row.detailType" />
</template>
</el-table-column>
<el-table-column label="产品单位" align="center" prop="unit">
<template #default="scope">
<dict-tag :type="DICT_TYPE.CRM_PRODUCT_UNIT" :value="scope.row.unit" />
</template>
</el-table-column>
</el-table>
<!-- 分页 -->
<Pagination
:total="total"
v-model:page="queryParams.pageNo"
v-model:limit="queryParams.pageSize"
@pagination="getList"
/>
</ContentWrap>
<template #footer>
<el-button @click="submitForm" type="primary" :disabled="formLoading"> </el-button>
<el-button @click="dialogVisible = false"> </el-button>
</template>
</Dialog>
</template>
<script setup lang="ts">
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
import * as ProductApi from '@/api/crm/product'
import { dateFormatter } from '@/utils/formatTime'
import download from '@/utils/download'
import { BillTemplateApi, BillTemplateVO } from '@/api/crm/billtemplate'
/** 票据模版 列表 */
defineOptions({ name: 'BillTemplate' })
const message = useMessage() //
const { t } = useI18n() //
const loading = ref(true) //
const list = ref<BillTemplateVO[]>([]) //
const total = ref(0) //
const multipleSelection = ref([])
const dialogVisible = ref(false) //
const multipleTableRef = ref()
const selectedRowKeys = ref<number[]>([]);
const queryParams = reactive({
pageNo: 1,
pageSize: 10,
name: undefined,
status: 1,
})
const queryFormRef = ref() //
const exportLoading = ref(false) //
/** 打开弹窗 */
const open = async (data: []) => {
dialogVisible.value = true
multipleSelection.value = data
await getList()
await setSelections()
}
defineExpose({ open }) // open
// id
const getRowKey = (row) => {
return row.id
}
/** 查询列表 */
const getList = async () => {
loading.value = true
try {
const data = await ProductApi.getProductPage(queryParams)
list.value = data.list
total.value = data.total
} finally {
loading.value = false
}
}
const handleSelectionChange = (val: []) => {
multipleSelection.value = val
}
/** 搜索按钮操作 */
const handleQuery = () => {
queryParams.pageNo = 1
getList()
}
/** 重置按钮操作 */
const resetQuery = () => {
queryFormRef.value.resetFields()
multipleTableRef.value.clearSelection()
handleQuery()
}
const emit = defineEmits(['success']) // success
const submitForm = async () => {
//
dialogVisible.value = false
//
emit('success', multipleSelection.value)
}
const setSelections = async () => {
const selections = multipleSelection.value
if (selections && selections.length > 0) {
list.value.forEach((row: any) => {
if (selections.some(item => item.id === row.id)) {
multipleTableRef.value.toggleRowSelection(row, true); //
}
});
} else {
multipleTableRef.value.clearSelection(); //
}
}
</script>

View File

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

View File

@ -212,6 +212,11 @@ export enum DICT_TYPE {
CRM_PERMISSION_LEVEL = 'crm_permission_level', // CRM 数据权限的级别
CRM_PRODUCT_UNIT = 'crm_product_unit', // CRM 产品单位
CRM_FOLLOW_UP_TYPE = 'crm_follow_up_type', // CRM 跟进方式
CRM_PRODUCT_INVOICE='crm_product_invoice',// 产品票据
CRM_PRODUCT_INVOICE_ITEMS='crm_product_invoice_items',// 产品开票项目
CRM_SERVICE_FEE_INVOICE='crm_service_fee_invoice',// 服务费票据
CRM_SERVICE_FEE_INVOICE_ITEMS='crm_service_fee_invoice_items',// 服务费开票项目
CRM_PRODUCT_CATEGORY = "crm_product_category", //CRM 产品类型
CRM_PRODUCT_DETAIL_TYPE = "crm_product_detail_type", //CRM 产品明细类型
CRM_COOPERATION_TYPE = "crm_cooperation_type", //CRM 合作类型

View File

@ -0,0 +1,213 @@
<template>
<Dialog :title="dialogTitle" v-model="dialogVisible" width="1280">
<el-form
ref="formRef"
:model="formData"
:rules="formRules"
label-width="120px"
v-loading="formLoading"
>
<el-row>
<el-col :span="8">
<el-form-item label="票据模版名称" prop="name">
<el-input v-model="formData.name" placeholder="请输入名称" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="状态" prop="status">
<el-select v-model="formData.status" clearable placeholder="请选择模板状态">
<el-option
v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_STATUS)"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item>
</el-col>
</el-row>
<!-- 子表的表单 -->
<ContentWrap>
<el-tabs v-model="subTabsName" class="-mt-15px -mb-10px">
<el-tab-pane label="产品清单" name="product">
<BusinessProductForm
ref="productFormRef"
:products="formData.products"
:disabled="disabled"
/>
</el-tab-pane>
</el-tabs>
</ContentWrap>
</el-form>
<template #footer>
<el-button @click="submitForm" type="primary" :disabled="formLoading"> </el-button>
<el-button @click="dialogVisible = false"> </el-button>
</template>
</Dialog>
</template>
<script setup lang="ts">
import { getIntDictOptions, getStrDictOptions, DICT_TYPE, getBoolDictOptions } from '@/utils/dict'
import * as BusinessApi from '@/api/crm/business'
import * as BusinessStatusApi from '@/api/crm/business/status'
import * as CustomerApi from '@/api/crm/customer'
import * as UserApi from '@/api/system/user'
import * as DeptApi from '@/api/system/dept'
import { useUserStore } from '@/store/modules/user'
import { defaultProps, handleTree } from '@/utils/tree'
import BusinessProductForm from './components/index.vue'
import { erpPriceMultiply, erpPriceInputFormatter } from '@/utils'
const deptTree = ref() //
const { t } = useI18n() //
const message = useMessage() //
const dialogVisible = ref(false) //
const dialogTitle = ref('') //
const formLoading = ref(false) // 12
const formType = ref('') // create - update -
const formData = ref({
id: undefined,
name: undefined,
customerId: undefined,
followUpStatus: undefined,
contactLastTime: undefined,
contactNextTime: undefined,
ownerUserId: undefined,
deptId: undefined,
requestorUserId: undefined,
statusTypeId: undefined,
statusId: undefined,
endStatus: undefined,
dealTime: undefined,
onlinePrice: undefined,
offlinePrice: undefined,
totalProductPrice: undefined,
totalPrice: undefined,
saleStage: undefined,
paymentTerm: undefined,
creditMethod: undefined,
creditCalcCycle: undefined,
creditLimit: undefined,
techSupport: undefined
})
const formRules = reactive({
name: [{ required: true, message: '票据模版不能为空', trigger: 'blur' }],
customerId: [{ required: true, message: '客户不能为空', trigger: 'blur' }],
ownerUserId: [{ required: true, message: '负责人不能为空', trigger: 'blur' }],
statusTypeId: [{ required: true, message: '商机状态组不能为空', trigger: 'blur' }]
})
const formRef = ref() // Ref
const userOptions = ref<UserApi.UserVO[]>([]) //
const statusTypeList = ref([]) //
const customerList = ref([]) //
/** 子表的表单 */
const subTabsName = ref('product')
const productFormRef = ref()
/** 计算 discountPrice、totalPrice 价格 */
watch(
() => formData.value,
(val) => {
if (!val) {
return
}
const totalOnlinePrice = val.products.reduce((prev, curr) => prev + curr.onlinePrice, 0)
const totalOfflinePrice = val.products.reduce((prev, curr) => prev + curr.offlinePrice, 0)
//
formData.value.onlinePrice = totalOnlinePrice
formData.value.offlinePrice = totalOfflinePrice
formData.value.totalPrice = totalOnlinePrice + totalOfflinePrice
},
{ deep: true }
)
/** 打开弹窗 */
const open = async (type: string, id?: number, customerId?: number, contactId?: number) => {
dialogVisible.value = true
dialogTitle.value = t('action.' + type)
formType.value = type
resetForm()
//
if (id) {
formLoading.value = true
try {
formData.value = await BusinessApi.getBusiness(id)
} finally {
formLoading.value = false
}
} else {
if (customerId) {
formData.value.customerId = customerId
formData.value.customerDefault = true //
}
// contactId
if (contactId) {
formData.value.contactId = contactId
}
}
//
customerList.value = await CustomerApi.getCustomerSimpleList()
//
statusTypeList.value = await BusinessStatusApi.getBusinessStatusTypeSimpleList()
//
userOptions.value = await UserApi.getSimpleUserList()
//
deptTree.value = handleTree(await DeptApi.getSimpleDeptList())
//
if (formType.value === 'create') {
formData.value.ownerUserId = useUserStore().getUser.id
}
}
defineExpose({ open }) // open
/** 提交表单 */
const emit = defineEmits(['success']) // success
const submitForm = async () => {
//
if (!formRef) return
const valid = await formRef.value.validate()
if (!valid) return
// await productFormRef.value.validate()
// console.log(formData.value,444); // ref
await nextTick(() => {
if (productFormRef.value) {
productFormRef.value.validate();
}
});
//
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
}
}
/** 重置表单 */
const resetForm = () => {
formData.value = {
id: undefined,
name: undefined,
customerId: undefined,
ownerUserId: undefined,
statusTypeId: undefined,
dealTime: undefined,
totalPrice: undefined,
products: [],
contactId: undefined,
customerDefault: false
}
formRef.value?.resetFields()
}
</script>

View File

@ -0,0 +1,210 @@
<template>
<el-form
ref="formRef"
:model="formData"
:rules="formRules"
v-loading="formLoading"
label-width="0px"
:inline-message="true"
:disabled="disabled"
>
<el-table :data="formData" class="-mt-10px">
<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="category" width="160">
<template #default="scope">
<dict-tag :type="DICT_TYPE.CRM_PRODUCT_CATEGORY" :value="scope.row.category" />
</template>
</el-table-column>
<el-table-column label="产品明细类型" align="center" prop="detailType" width="160">
<template #default="scope">
<dict-tag :type="DICT_TYPE.CRM_PRODUCT_DETAIL_TYPE" :value="scope.row.detailType" />
</template>
</el-table-column>
<el-table-column label="产品单位" align="center" prop="unit">
<template #default="scope">
<dict-tag :type="DICT_TYPE.CRM_PRODUCT_UNIT" :value="scope.row.unit" />
</template>
</el-table-column>
<el-table-column label="产品票据" fixed="right" min-width="140">
<template #default="{ row, $index }">
<el-form-item :prop="`${$index}.productInvoice`" class="mb-0px!">
<el-select v-model="row.productInvoice" placeholder="请选择状态" clearable class="!w-240px">
<el-option
v-for="dict in getIntDictOptions(DICT_TYPE.CRM_PRODUCT_INVOICE)"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item>
</template>
</el-table-column>
<el-table-column label="产品开具项目" fixed="right" min-width="140">
<template #default="{ row, $index }">
<el-form-item :prop="`${$index}.productInvoiceItems`" class="mb-0px!">
<el-select v-model="row.productInvoiceItems" placeholder="请选择状态" clearable class="!w-240px">
<el-option
v-for="dict in getIntDictOptions(DICT_TYPE.CRM_PRODUCT_INVOICE_ITEMS)"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item>
</template>
</el-table-column>
<el-table-column label="服务费票据" prop="serviceFeeInvoice" fixed="right" min-width="140">
<template #default="{ row, $index }">
<el-form-item :prop="`${$index}.serviceFeeInvoice`" class="mb-0px!">
<el-select v-model="row.serviceFeeInvoice" placeholder="请选择状态" clearable class="!w-240px">
<el-option
v-for="dict in getIntDictOptions(DICT_TYPE.CRM_SERVICE_FEE_INVOICE)"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item>
</template>
</el-table-column>
<el-table-column label="服务开具项目" prop="serviceFeeInvoiceItems" fixed="right" min-width="140">
<template #default="{ row, $index }">
<el-form-item :prop="`${$index}.serviceFeeInvoiceItems`" class="mb-0px!">
<el-select v-model="row.serviceFeeInvoiceItems" placeholder="请选择状态" clearable class="!w-240px">
<el-option
v-for="dict in getIntDictOptions(DICT_TYPE.CRM_SERVICE_FEE_INVOICE_ITEMS)"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item>
</template>
</el-table-column>
<el-table-column align="center" fixed="right" label="操作" width="60">
<template #default="{ $index }">
<el-button @click="handleDelete($index)" link></el-button>
</template>
</el-table-column>
</el-table>
</el-form>
<el-row justify="center" class="mt-3" v-if="!disabled">
<el-button @click="handleAdd" round>+ 添加产品</el-button>
</el-row>
<!-- 表单弹窗添加/修改 -->
<ProductForm ref="productRef" @success="getList" />
</template>
<script setup lang="ts">
import * as ProductApi from '@/api/crm/product'
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
import { erpPriceInputFormatter, erpPriceMultiply } from '@/utils'
import ProductForm from '@/components/product/index.vue'
const props = defineProps<{
products: undefined
disabled: false
}>()
const formLoading = ref(false) //
const formData = ref([])
const formRules = reactive({
productInvoice: [{ required: true, message: '产品票据不能为空', trigger: 'change' }],
getIntDictOptions: [{ required: true, message: '产品开具项目不能为空', trigger: 'change' }],
serviceFeeInvoice: [{ required: true, message: '服务费票据不能为空', trigger: 'change' }],
serviceFeeInvoiceItems: [{ required: true, message: '服务开具项目不能为空', trigger: 'change' }]
})
const formRef = ref([]) // Ref
const productList = ref<ProductApi.ProductVO[]>([]) //
/** 初始化设置产品项 */
watch(
() => props.products,
async (val) => {
formData.value = val
},
{ immediate: true }
)
/** 监听合同产品变化,计算合同产品总价 */
watch(
() => formData.value,
(val) => {
if (!val || val.length === 0) {
return
}
//
val.forEach((item) => {
if (item.offlinePrice != null && item.onlinePrice != null) {
item.totalPrice = item.offlinePrice + item.onlinePrice
} else {
item.totalPrice = 0
}
})
},
{ deep: true }
)
const getList = (val: []) => {
formData.value = val
}
// /** */
// const handleAdd = () => {
// const row = {
// id: undefined,
// productId: undefined,
// productName: undefined,
// productCategoryId: undefined, //
// productUnit: undefined, //
// productNo: undefined, //
// productPrice: undefined, //
// onlinePrice: undefined, //
// offlinePrice: undefined, //
// totalPrice: undefined,
// businessPrice: undefined,
// count: 1
// }
// formData.value.push(row)
// }
const productRef = ref() // Ref
const handleAdd = () => {
productRef.value.open(formData.value)
}
/** 删除按钮操作 */
const handleDelete = (index: number) => {
formData.value.splice(index, 1)
}
/** 处理产品变更 */
const onChangeProduct = (productId, row) => {
const product = productList.value.find((item) => item.id === productId)
if (product) {
row.productId = product.id
row.productName = product.name
row.productCategoryId = 1
row.productUnit = product.unit
row.onlinePrice = product.onlinePrice
row.offlinePrice = product.offlinePrice
row.totalPrice = product.totalPrice
row.productNo = product.no
row.productPrice = product.price
row.businessPrice = product.price
}
}
/** 表单校验 */
const validate = () => {
return formRef.value.validate()
}
defineExpose({ validate })
/** 初始化 */
onMounted(async () => {
productList.value = await ProductApi.getProductSimpleList()
})
</script>

View File

@ -0,0 +1,210 @@
<template>
<ContentWrap>
<!-- 搜索工作栏 -->
<el-form
class="-mb-15px"
:model="queryParams"
ref="queryFormRef"
:inline="true"
label-width="100px"
>
<el-form-item label="票据模板名称" prop="name">
<el-input
v-model="queryParams.name"
placeholder="请输入名称"
clearable
@keyup.enter="handleQuery"
class="!w-240px"
/>
</el-form-item>
<el-form-item label="状态" prop="status">
<el-select
v-model="queryParams.status"
placeholder="请选择模板状态"
clearable
class="!w-240px"
>
<el-option
v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_STATUS)"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item>
<el-form-item label="创建时间" prop="createTime">
<el-date-picker
v-model="queryParams.createTime"
value-format="YYYY-MM-DD HH:mm:ss"
type="daterange"
start-placeholder="开始日期"
end-placeholder="结束日期"
:default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]"
class="!w-220px"
/>
</el-form-item>
<el-form-item>
<el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button>
<el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button>
<el-button
type="primary"
plain
@click="openForm('create')"
v-hasPermi="['crm:bill-template:create']"
>
<Icon icon="ep:plus" class="mr-5px" /> 新增
</el-button>
<el-button
type="success"
plain
@click="handleExport"
:loading="exportLoading"
v-hasPermi="['crm:bill-template:export']"
>
<Icon icon="ep:download" class="mr-5px" /> 导出
</el-button>
</el-form-item>
</el-form>
</ContentWrap>
<!-- 列表 -->
<ContentWrap>
<el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true">
<!-- <el-table-column label="主键" align="center" prop="id" /> -->
<el-table-column label="名称" align="center" prop="name" />
<el-table-column label="状态" align="center" prop="status" >
<template #default="scope">
<dict-tag :type="DICT_TYPE.COMMON_STATUS" :value="scope.row.status" />
</template>
</el-table-column>
<el-table-column label="创建人" align="center" prop="creator" />
<el-table-column
label="创建时间"
align="center"
prop="createTime"
:formatter="dateFormatter"
width="180px"
/>
<el-table-column label="操作" align="center" min-width="120px">
<template #default="scope">
<el-button
link
type="primary"
@click="openForm('update', scope.row.id)"
v-hasPermi="['crm:bill-template:update']"
>
编辑
</el-button>
<el-button
link
type="danger"
@click="handleDelete(scope.row.id)"
v-hasPermi="['crm:bill-template:delete']"
>
删除
</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页 -->
<Pagination
:total="total"
v-model:page="queryParams.pageNo"
v-model:limit="queryParams.pageSize"
@pagination="getList"
/>
</ContentWrap>
<!-- 表单弹窗添加/修改 -->
<BillTemplateForm ref="formRef" @success="getList" />
</template>
<script setup lang="ts">
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
import { dateFormatter } from '@/utils/formatTime'
import download from '@/utils/download'
import { BillTemplateApi, BillTemplateVO } from '@/api/crm/billtemplate'
import BillTemplateForm from './BillTemplateForm.vue'
/** 票据模版 列表 */
defineOptions({ name: 'BillTemplate' })
const message = useMessage() //
const { t } = useI18n() //
const loading = ref(true) //
const list = ref<BillTemplateVO[]>([]) //
const total = ref(0) //
const queryParams = reactive({
pageNo: 1,
pageSize: 10,
name: undefined,
status: undefined,
createTime: [],
})
const queryFormRef = ref() //
const exportLoading = ref(false) //
/** 查询列表 */
const getList = async () => {
loading.value = true
try {
const data = await BillTemplateApi.getBillTemplatePage(queryParams)
list.value = data.list
total.value = data.total
} finally {
loading.value = false
}
}
/** 搜索按钮操作 */
const handleQuery = () => {
queryParams.pageNo = 1
getList()
}
/** 重置按钮操作 */
const resetQuery = () => {
queryFormRef.value.resetFields()
handleQuery()
}
/** 添加/修改操作 */
const formRef = ref()
const openForm = (type: string, id?: number) => {
formRef.value.open(type, id)
}
/** 删除按钮操作 */
const handleDelete = async (id: number) => {
try {
//
await message.delConfirm()
//
await BillTemplateApi.deleteBillTemplate(id)
message.success(t('common.delSuccess'))
//
await getList()
} catch {}
}
/** 导出按钮操作 */
const handleExport = async () => {
try {
//
await message.exportConfirm()
//
exportLoading.value = true
const data = await BillTemplateApi.exportBillTemplate(queryParams)
download.excel(data, '票据模版.xls')
} catch {
} finally {
exportLoading.value = false
}
}
/** 初始化 **/
onMounted(() => {
getList()
})
</script>

View File

@ -0,0 +1,160 @@
<template>
<Dialog :title="dialogTitle" v-model="dialogVisible">
<el-form
ref="formRef"
:model="formData"
:rules="formRules"
label-width="120px"
v-loading="formLoading"
>
<el-form-item label="票据模版ID" prop="billTemplateId">
<el-input v-model="formData.billTemplateId" placeholder="请输入票据模版ID" />
</el-form-item>
<el-form-item label="产品ID" prop="productId">
<el-input v-model="formData.productId" placeholder="请输入产品ID" />
</el-form-item>
<el-form-item label="产品票据" prop="productInvoice">
<el-select v-model="formData.status" clearable placeholder="请选择产品票据">
<el-option
v-for="dict in getIntDictOptions(DICT_TYPE.CRM_PRODUCT_INVOICE)"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item>
<el-form-item label="产品开票项目" prop="productInvoiceItems">
<el-select v-model="formData.status" clearable placeholder="请选择产品开票项目">
<el-option
v-for="dict in getIntDictOptions(DICT_TYPE.CRM_PRODUCT_INVOICE_ITEMS)"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item>
<el-form-item label="服务费票据" prop="serviceFeeInvoice">
<el-select v-model="formData.status" clearable placeholder="请选择服务费票据">
<el-option
v-for="dict in getIntDictOptions(DICT_TYPE.CRM_SERVICE_FEE_INVOICE)"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item>
<el-form-item label="服务费开票项目" prop="serviceFeeInvoiceItems">
<el-select v-model="formData.status" clearable placeholder="请选择服务费开票项目">
<el-option
v-for="dict in getIntDictOptions(DICT_TYPE.CRM_SERVICE_FEE_INVOICE_ITEMS)"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item>
<el-form-item label="状态" prop="status">
<el-select v-model="formData.status" clearable placeholder="请选择状态">
<el-option
v-for="dict in getIntDictOptions(DICT_TYPE.CRM_PRODUCT_STATUS)"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item>
</el-form>
<template #footer>
<el-button @click="submitForm" type="primary" :disabled="formLoading"> </el-button>
<el-button @click="dialogVisible = false"> </el-button>
</template>
</Dialog>
</template>
<script setup lang="ts">
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
import { BillTemplateProductApi, BillTemplateProductVO } from '@/api/crm/billtemplateproduct'
/** 票据模版产品关联 表单 */
defineOptions({ name: 'BillTemplateProductForm' })
const { t } = useI18n() //
const message = useMessage() //
const dialogVisible = ref(false) //
const dialogTitle = ref('') //
const formLoading = ref(false) // 12
const formType = ref('') // create - update -
const formData = ref({
id: undefined,
billTemplateId: undefined,
productId: undefined,
productInvoice: undefined,
productInvoiceItems: undefined,
serviceFeeInvoice: undefined,
serviceFeeInvoiceItems: undefined,
status: undefined,
})
const formRules = reactive({
billTemplateId: [{ required: true, message: '票据模版ID不能为空', trigger: 'blur' }],
productId: [{ required: true, message: '产品ID不能为空', trigger: 'blur' }],
status: [{ required: true, message: '状态不能为空', trigger: 'blur' }],
})
const formRef = ref() // Ref
/** 打开弹窗 */
const open = async (type: string, id?: number) => {
dialogVisible.value = true
dialogTitle.value = t('action.' + type)
formType.value = type
resetForm()
//
if (id) {
formLoading.value = true
try {
formData.value = await BillTemplateProductApi.getBillTemplateProduct(id)
} finally {
formLoading.value = false
}
}
}
defineExpose({ open }) // open
/** 提交表单 */
const emit = defineEmits(['success']) // success
const submitForm = async () => {
//
await formRef.value.validate()
//
formLoading.value = true
try {
const data = formData.value as unknown as BillTemplateProductVO
if (formType.value === 'create') {
await BillTemplateProductApi.createBillTemplateProduct(data)
message.success(t('common.createSuccess'))
} else {
await BillTemplateProductApi.updateBillTemplateProduct(data)
message.success(t('common.updateSuccess'))
}
dialogVisible.value = false
//
emit('success')
} finally {
formLoading.value = false
}
}
/** 重置表单 */
const resetForm = () => {
formData.value = {
id: undefined,
billTemplateId: undefined,
productId: undefined,
productInvoice: undefined,
productInvoiceItems: undefined,
serviceFeeInvoice: undefined,
serviceFeeInvoiceItems: undefined,
status: undefined,
}
formRef.value?.resetFields()
}
</script>

View File

@ -0,0 +1,210 @@
<template>
<ContentWrap>
<!-- 搜索工作栏 -->
<el-form
class="-mb-15px"
:model="queryParams"
ref="queryFormRef"
:inline="true"
label-width="100px"
>
<el-form-item label="票据模版名称" prop="billTemplateId">
<el-input
v-model="queryParams.billTemplateId"
placeholder="请输入票据模版名称"
clearable
@keyup.enter="handleQuery"
class="!w-240px"
/>
</el-form-item>
<el-form-item label="状态" prop="status">
<el-select
v-model="queryParams.status"
placeholder="请选择状态"
clearable
class="!w-240px"
>
<el-option
v-for="dict in getIntDictOptions(DICT_TYPE.CRM_PRODUCT_STATUS)"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item>
<el-form-item>
<el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button>
<el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button>
<el-button
type="primary"
plain
@click="openForm('create')"
v-hasPermi="['crm:bill-template-product:create']"
>
<Icon icon="ep:plus" class="mr-5px" /> 新增
</el-button>
<el-button
type="success"
plain
@click="handleExport"
:loading="exportLoading"
v-hasPermi="['crm:bill-template-product:export']"
>
<Icon icon="ep:download" class="mr-5px" /> 导出
</el-button>
</el-form-item>
</el-form>
</ContentWrap>
<!-- 列表 -->
<ContentWrap>
<el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true">
<el-table-column label="序号" align="center" prop="id" />
<!-- <el-table-column label="票据模版ID" align="center" prop="billTemplateId" /> -->
<!-- <el-table-column label="产品ID" align="center" prop="productId" /> -->
<el-table-column label="产品名称" align="center" prop="productId" />
<el-table-column label="产品分类" align="center" prop="productId" />
<el-table-column label="产品明细分类" align="center" prop="productId" />
<el-table-column label="单位" align="center" prop="productId" />
<el-table-column label="产品票据" align="center" prop="productInvoice" />
<el-table-column label="产品开票项目" align="center" prop="productInvoiceItems" />
<el-table-column label="服务费票据" align="center" prop="serviceFeeInvoice" />
<el-table-column label="服务费开票项目" align="center" prop="serviceFeeInvoiceItems" />
<!-- <el-table-column label="状态" align="center" prop="status" /> -->
<el-table-column
label="创建时间"
align="center"
prop="createTime"
:formatter="dateFormatter"
width="180px"
/>
<el-table-column label="操作" align="center" min-width="120px">
<template #default="scope">
<el-button
link
type="primary"
@click="openForm('update', scope.row.id)"
v-hasPermi="['crm:bill-template-product:update']"
>
编辑
</el-button>
<el-button
link
type="danger"
@click="handleDelete(scope.row.id)"
v-hasPermi="['crm:bill-template-product:delete']"
>
删除
</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页 -->
<Pagination
:total="total"
v-model:page="queryParams.pageNo"
v-model:limit="queryParams.pageSize"
@pagination="getList"
/>
</ContentWrap>
<!-- 表单弹窗添加/修改 -->
<BillTemplateProductForm ref="formRef" @success="getList" />
</template>
<script setup lang="ts">
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
import { dateFormatter } from '@/utils/formatTime'
import download from '@/utils/download'
import { BillTemplateProductApi, BillTemplateProductVO } from '@/api/crm/billtemplateproduct'
import BillTemplateProductForm from './BillTemplateProductForm.vue'
/** 票据模版产品关联 列表 */
defineOptions({ name: 'BillTemplateProduct' })
const message = useMessage() //
const { t } = useI18n() //
const loading = ref(true) //
const list = ref<BillTemplateProductVO[]>([]) //
const total = ref(0) //
const queryParams = reactive({
pageNo: 1,
pageSize: 10,
billTemplateId: undefined,
productId: undefined,
productInvoice: undefined,
productInvoiceItems: undefined,
serviceFeeInvoice: undefined,
serviceFeeInvoiceItems: undefined,
status: undefined,
createTime: [],
})
const queryFormRef = ref() //
const exportLoading = ref(false) //
/** 查询列表 */
const getList = async () => {
loading.value = true
try {
const data = await BillTemplateProductApi.getBillTemplateProductPage(queryParams)
list.value = data.list
total.value = data.total
} finally {
loading.value = false
}
}
/** 搜索按钮操作 */
const handleQuery = () => {
queryParams.pageNo = 1
getList()
}
/** 重置按钮操作 */
const resetQuery = () => {
queryFormRef.value.resetFields()
handleQuery()
}
/** 添加/修改操作 */
const formRef = ref()
const openForm = (type: string, id?: number) => {
formRef.value.open(type, id)
}
/** 删除按钮操作 */
const handleDelete = async (id: number) => {
try {
//
await message.delConfirm()
//
await BillTemplateProductApi.deleteBillTemplateProduct(id)
message.success(t('common.delSuccess'))
//
await getList()
} catch {}
}
/** 导出按钮操作 */
const handleExport = async () => {
try {
//
await message.exportConfirm()
//
exportLoading.value = true
const data = await BillTemplateProductApi.exportBillTemplateProduct(queryParams)
download.excel(data, '票据模版产品关联.xls')
} catch {
} finally {
exportLoading.value = false
}
}
/** 初始化 **/
onMounted(() => {
getList()
})
</script>

View File

@ -1,5 +1,5 @@
<template>
<Dialog :title="dialogTitle" v-model="dialogVisible" width="1280">
<ContentWrap class="mt-10px">
<el-form
ref="formRef"
:model="formData"
@ -18,6 +18,24 @@
<el-select
v-model="formData.ownerUserId"
:disabled="formType !== 'create'"
placeholder="请选择负责人"
class="w-1/1"
>
<el-option
v-for="item in userOptions"
:key="item.id"
:label="item.nickname"
:value="item.id"
/>
</el-select>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="需求提交人" prop="requestorUserId">
<el-select
v-model="formData.requestorUserId"
:disabled="formType !== 'create'"
placeholder="请选择需求提交人"
class="w-1/1"
>
<el-option
@ -46,8 +64,20 @@
</el-select>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="8">
<el-form-item label="归属部门" prop="deptId">
<el-tree-select
v-model="formData.deptId"
:data="deptTree"
:props="defaultProps"
filterable
check-strictly
node-key="id"
placeholder="请选择归属部门"
/>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="商机状态组" prop="statusTypeId">
<el-select
@ -77,6 +107,72 @@
/>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="销售阶段" prop="saleStage">
<el-select v-model="formData.saleStage" placeholder="请选择销售阶段">
<el-option
v-for="dict in getIntDictOptions('sale_stage')"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="账期" prop="paymentTerm">
<el-select v-model="formData.paymentTerm" placeholder="请选择账期">
<el-option
v-for="dict in getStrDictOptions('payment_term')"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="授信方式" prop="creditMethod">
<el-select v-model="formData.creditMethod" placeholder="请选择授信方式">
<el-option
v-for="dict in getIntDictOptions('credit_method')"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="授信计算周期" prop="creditCalcCycle">
<el-select v-model="formData.creditCalcCycle" placeholder="请选择授信计算周期">
<el-option
v-for="dict in getIntDictOptions('credit_calc_cycle')"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="授信额度" prop="creditLimit">
<el-input v-model="formData.creditLimit" placeholder="请输入授信额度" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="是否技术需求支持" prop="techSupport">
<el-radio-group v-model="formData.techSupport">
<el-radio
v-for="dict in getBoolDictOptions('tech_support')"
:key="dict.value"
:label="dict.value"
>
{{ dict.label }}
</el-radio>
</el-radio-group>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="备注" prop="remark">
<el-input type="textarea" v-model="formData.remark" placeholder="请输入备注" />
@ -89,7 +185,7 @@
<el-tab-pane label="产品清单" name="product">
<BusinessProductForm
ref="productFormRef"
:products="formData.products"
v-model="formData.products"
:disabled="disabled"
/>
</el-tab-pane>
@ -97,53 +193,52 @@
</ContentWrap>
<el-row>
<el-col :span="8">
<el-form-item label="产品总金额" prop="totalProductPrice">
<el-form-item label="线上总金额" prop="onlinePrice">
<el-input
disabled
v-model="formData.totalProductPrice"
v-model="formData.onlinePrice"
:formatter="erpPriceInputFormatter"
/>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="整单折扣(%" prop="discountPercent">
<el-input-number
v-model="formData.discountPercent"
placeholder="请输入整单折扣"
controls-position="right"
:min="0"
:precision="2"
class="!w-1/1"
<el-form-item label="线下总金额" prop="offlinePrice">
<el-input
disabled
v-model="formData.offlinePrice"
:formatter="erpPriceInputFormatter"
/>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="折扣后金额" prop="price">
<el-form-item label="总金额" prop="totalPrice">
<el-input
disabled
v-model="formData.totalPrice"
placeholder="请输入商机金额"
:formatter="erpPriceInputFormatter"
/>
</el-form-item>
</el-col>
</el-row>
</el-form>
<template #footer>
<el-button @click="submitForm" type="primary" :disabled="formLoading"> </el-button>
<el-button @click="dialogVisible = false"> </el-button>
</template>
</Dialog>
</ContentWrap>
</template>
<script setup lang="ts">
import { getIntDictOptions, getStrDictOptions, DICT_TYPE, getBoolDictOptions } from '@/utils/dict'
import * as BusinessApi from '@/api/crm/business'
import * as BusinessStatusApi from '@/api/crm/business/status'
import * as CustomerApi from '@/api/crm/customer'
import * as UserApi from '@/api/system/user'
import * as DeptApi from '@/api/system/dept'
import { useUserStore } from '@/store/modules/user'
import { defaultProps, handleTree } from '@/utils/tree'
import BusinessProductForm from './components/BusinessProductForm.vue'
import { erpPriceMultiply, erpPriceInputFormatter } from '@/utils'
const deptTree = ref() //
const { proxy }: any = getCurrentInstance();
const { t } = useI18n() //
const message = useMessage() //
@ -155,16 +250,27 @@ const formData = ref({
id: undefined,
name: undefined,
customerId: undefined,
followUpStatus: undefined,
contactLastTime: undefined,
contactNextTime: undefined,
ownerUserId: undefined,
deptId: undefined,
requestorUserId: undefined,
statusTypeId: undefined,
statusId: undefined,
endStatus: undefined,
dealTime: undefined,
discountPercent: 0,
onlinePrice: undefined,
offlinePrice: undefined,
totalProductPrice: undefined,
totalPrice: undefined,
remark: undefined,
saleStage: undefined,
paymentTerm: undefined,
creditMethod: undefined,
creditCalcCycle: undefined,
creditLimit: undefined,
techSupport: undefined,
products: [],
contactId: undefined,
customerDefault: false
})
const formRules = reactive({
name: [{ required: true, message: '商机名称不能为空', trigger: 'blur' }],
@ -183,29 +289,25 @@ const productFormRef = ref()
/** 计算 discountPrice、totalPrice 价格 */
watch(
() => formData.value,
() => formData.value.products,
(val) => {
if (!val) {
return
}
const totalProductPrice = val.products.reduce((prev, curr) => prev + curr.totalPrice, 0)
const discountPrice =
val.discountPercent != null
? erpPriceMultiply(totalProductPrice, val.discountPercent / 100.0)
: 0
const totalPrice = totalProductPrice - discountPrice
const totalOnlinePrice = val.products.reduce((prev, curr) => prev + curr.onlinePrice, 0)
const totalOfflinePrice = val.products.reduce((prev, curr) => prev + curr.offlinePrice, 0)
//
formData.value.totalProductPrice = totalProductPrice
formData.value.totalPrice = totalPrice
formData.value.onlinePrice = totalOnlinePrice
formData.value.offlinePrice = totalOfflinePrice
formData.value.totalPrice = totalOnlinePrice + totalOfflinePrice
},
{ deep: true }
)
/** 打开弹窗 */
const open = async (type: string, id?: number, customerId?: number, contactId?: number) => {
dialogVisible.value = true
dialogTitle.value = t('action.' + type)
formType.value = type
resetForm()
//
if (id) {
@ -225,12 +327,7 @@ const open = async (type: string, id?: number, customerId?: number, contactId?:
formData.value.contactId = contactId
}
}
//
customerList.value = await CustomerApi.getCustomerSimpleList()
//
statusTypeList.value = await BusinessStatusApi.getBusinessStatusTypeSimpleList()
//
userOptions.value = await UserApi.getSimpleUserList()
//
if (formType.value === 'create') {
formData.value.ownerUserId = useUserStore().getUser.id
@ -238,14 +335,20 @@ const open = async (type: string, id?: number, customerId?: number, contactId?:
}
defineExpose({ open }) // open
/** 提交表单 */
const emit = defineEmits(['success']) // success
const submitForm = async () => {
//
if (!formRef) return
const valid = await formRef.value.validate()
if (!valid) return
await productFormRef.value.validate()
//
// 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;
const valid = await productFormRef.value.validate();
formData.value.products = valid
if (!valid) return;
//
formLoading.value = true
try {
@ -274,14 +377,24 @@ const resetForm = () => {
ownerUserId: undefined,
statusTypeId: undefined,
dealTime: undefined,
discountPercent: 0,
totalProductPrice: undefined,
totalPrice: undefined,
remark: undefined,
products: [],
contactId: undefined,
customerDefault: false
}
formRef.value?.resetFields()
}
/** 初始化 */
const { params } = useRoute()
onMounted(async () => {
formType.value = params.id
//
customerList.value = await CustomerApi.getCustomerSimpleList()
//
statusTypeList.value = await BusinessStatusApi.getBusinessStatusTypeSimpleList()
//
userOptions.value = await UserApi.getSimpleUserList()
//
deptTree.value = handleTree(await DeptApi.getSimpleDeptList())
})
</script>

View File

@ -10,23 +10,12 @@
>
<el-table :data="formData" class="-mt-10px">
<el-table-column label="序号" type="index" align="center" width="60" />
<el-table-column label="产品名称" min-width="180">
<template #default="{ row, $index }">
<el-form-item :prop="`${$index}.productId`" :rules="formRules.productId" class="mb-0px!">
<el-select
v-model="row.productId"
clearable
filterable
@change="onChangeProduct($event, row)"
placeholder="请选择产品"
>
<el-option
v-for="item in productList"
:key="item.id"
:label="item.name"
:value="item.id"
/>
</el-select>
<el-table-column label="产品名称" align="center" prop="name" />
<el-table-column label="产品分类" min-width="150">
<template #default="{ row }">
<el-form-item class="mb-0px!">
<el-input disabled v-model="row.productCategoryId" />
</el-form-item>
</template>
</el-table-column>
@ -38,22 +27,15 @@
</template>
</el-table-column>
<el-table-column label="单位" min-width="80">
<template #default="{ row }">
<dict-tag :type="DICT_TYPE.CRM_PRODUCT_UNIT" :value="row.productUnit" />
<template #default="scope">
<dict-tag :type="DICT_TYPE.CRM_PRODUCT_UNIT" :value="scope.row.unit" />
</template>
</el-table-column>
<el-table-column label="价格(元)" min-width="120">
<template #default="{ row }">
<el-form-item class="mb-0px!">
<el-input disabled v-model="row.productPrice" :formatter="erpPriceInputFormatter" />
</el-form-item>
</template>
</el-table-column>
<el-table-column label="售价(元)" fixed="right" min-width="140">
<el-table-column label="线上价格(元)" fixed="right" min-width="140">
<template #default="{ row, $index }">
<el-form-item :prop="`${$index}.businessPrice`" class="mb-0px!">
<el-form-item :prop="`${$index}.onlinePrice`" class="mb-0px!">
<el-input-number
v-model="row.businessPrice"
v-model="row.onlinePrice"
controls-position="right"
:min="0.001"
:precision="2"
@ -62,14 +44,14 @@
</el-form-item>
</template>
</el-table-column>
<el-table-column label="数量" prop="count" fixed="right" min-width="120">
<el-table-column label="线下价格(元)" fixed="right" min-width="140">
<template #default="{ row, $index }">
<el-form-item :prop="`${$index}.count`" :rules="formRules.count" class="mb-0px!">
<el-form-item :prop="`${$index}.offlinePrice`" class="mb-0px!">
<el-input-number
v-model="row.count"
v-model="row.offlinePrice"
controls-position="right"
:min="0.001"
:precision="3"
:precision="2"
class="!w-100%"
/>
</el-form-item>
@ -92,92 +74,116 @@
<el-row justify="center" class="mt-3" v-if="!disabled">
<el-button @click="handleAdd" round>+ 添加产品</el-button>
</el-row>
<!-- 表单弹窗添加/修改 -->
<ProductForm ref="productRef" @success="getList" />
</template>
<script setup lang="ts">
import * as ProductApi from '@/api/crm/product'
import { erpPriceInputFormatter, erpPriceMultiply } from '@/utils'
import { DICT_TYPE } from '@/utils/dict'
import { ref, watch, onMounted, defineProps, defineExpose, reactive } from 'vue';
import * as ProductApi from '@/api/crm/product';
import { erpPriceInputFormatter, erpPriceMultiply } from '@/utils';
import ProductForm from '@/components/product/index.vue'
import { DICT_TYPE } from '@/utils/dict';
const props = defineProps<{
products: undefined
disabled: false
}>()
const formLoading = ref(false) //
const formData = ref([])
const formLoading = ref(false); //
const formData = ref([]); //
const formRules = reactive({
productId: [{ required: true, message: '产品不能为空', trigger: 'blur' }],
businessPrice: [{ required: true, message: '合同价格不能为空', trigger: 'blur' }],
count: [{ required: true, message: '产品数量不能为空', trigger: 'blur' }]
})
const formRef = ref([]) // Ref
const productList = ref<ProductApi.ProductVO[]>([]) //
/** 初始化设置产品项 */
});
const formRef = ref(null); // Ref
const productList = ref<ProductApi.ProductVO[]>([]); //
const props = defineProps<{
products: any[]; // products
disabled: boolean; // disabled
}>();
//
watch(
() => props.products,
async (val) => {
formData.value = val
(val) => {
formData.value = val || []; // formData
},
{ immediate: true }
)
);
/** 监听合同产品变化,计算合同产品总价 */
//
watch(
() => formData.value,
(val) => {
if (!val || val.length === 0) {
return
return;
}
//
val.forEach((item) => {
if (item.businessPrice != null && item.count != null) {
item.totalPrice = erpPriceMultiply(item.businessPrice, item.count)
if (item.offlinePrice != null && item.onlinePrice != null) {
item.totalPrice = item.offlinePrice + item.onlinePrice;
} else {
item.totalPrice = undefined
item.totalPrice = 0;
}
})
});
},
{ deep: true }
)
);
/** 新增按钮操作 */
//
// const handleAdd = () => {
// const newRow = {
// id: undefined,
// productId: undefined,
// productName: undefined,
// productCategoryId: undefined, //
// productUnit: undefined, //
// productNo: undefined, //
// productPrice: undefined, //
// onlinePrice: undefined, // 线
// offlinePrice: undefined, // 线
// totalPrice: undefined, //
// businessPrice: undefined, //
// count: 1 // 1
// };
// formData.value.push(newRow);
// };
const emit = defineEmits(['update:products']);
const getList = (val: []) => {
formData.value = val
emit('update:products', val);
}
const productRef = ref() // Ref
const handleAdd = () => {
const row = {
id: undefined,
productId: undefined,
productUnit: undefined, //
productNo: undefined, //
productPrice: undefined, //
businessPrice: undefined,
count: 1
}
formData.value.push(row)
productRef.value.open(formData.value)
}
/** 删除按钮操作 */
//
const handleDelete = (index: number) => {
formData.value.splice(index, 1)
}
formData.value.splice(index, 1);
};
/** 处理产品变更 */
//
const onChangeProduct = (productId, row) => {
const product = productList.value.find((item) => item.id === productId)
const product = productList.value.find((item) => item.id === productId);
if (product) {
row.productUnit = product.unit
row.productNo = product.no
row.productPrice = product.price
row.businessPrice = product.price
row.productId = product.id;
row.productName = product.name;
row.productCategoryId = product.categoryId; //
row.productUnit = product.unit;
row.onlinePrice = product.onlinePrice;
row.offlinePrice = product.offlinePrice;
row.totalPrice = product.onlinePrice + product.offlinePrice; //
row.productNo = product.no;
row.productPrice = product.price;
row.businessPrice = product.price;
}
}
};
/** 表单校验 */
//
const validate = () => {
return formRef.value.validate()
}
defineExpose({ validate })
return formData.value
};
defineExpose({ validate });
/** 初始化 */
//
onMounted(async () => {
productList.value = await ProductApi.getProductSimpleList()
})
productList.value = await ProductApi.getProductSimpleList();
});
</script>

View File

@ -48,9 +48,8 @@
/>
</el-table>
<el-row class="mt-10px" justify="end">
<el-col :span="3"> 整单折扣{{ erpPriceInputFormatter(business.discountPercent) }}% </el-col>
<el-col :span="4">
产品总金额{{ erpPriceInputFormatter(business.totalProductPrice) }}
总金额{{ erpPriceInputFormatter(business.totalPrice) }}
</el-col>
</el-row>
</ContentWrap>

View File

@ -1,7 +1,4 @@
<template>
<doc-alert title="【商机】商机管理、商机状态" url="https://doc.iocoder.cn/crm/business/" />
<doc-alert title="【通用】数据权限" url="https://doc.iocoder.cn/crm/permission/" />
<ContentWrap>
<!-- 搜索工作栏 -->
<el-form
@ -164,7 +161,6 @@
</ContentWrap>
<!-- 表单弹窗添加/修改 -->
<BusinessForm ref="formRef" @success="getList" />
</template>
<script lang="ts" setup>
@ -236,8 +232,8 @@ const openCustomerDetail = (id: number) => {
/** 添加/修改操作 */
const formRef = ref()
const openForm = (type: string, id?: number) => {
formRef.value.open(type, id)
const openForm = (id: number) => {
push({ name: 'CrmBusinessAdd', params: { id: 'create' } })
}
/** 删除按钮操作 */

View File

@ -18,6 +18,7 @@
<el-select
v-model="formData.ownerUserId"
:disabled="formType !== 'create'"
filterable
class="w-1/1"
>
<el-option
@ -36,6 +37,7 @@
<el-select
:disabled="formData.customerDefault"
v-model="formData.customerId"
filterable
placeholder="请选择客户"
class="w-1/1"
>
@ -113,7 +115,7 @@
</el-col>
<el-col :span="12">
<el-form-item label="直属上级" prop="parentId">
<el-select v-model="formData.parentId" placeholder="请选择直属上级" class="w-1/1">
<el-select v-model="formData.parentId" filterable placeholder="请选择直属上级" class="w-1/1">
<el-option
v-for="item in contactList"
:key="item.id"

View File

@ -10,7 +10,7 @@
<el-row>
<el-col :span="8">
<el-form-item label="合同编号" prop="no">
<el-input disabled v-model="formData.no" placeholder="保存时自动生成" />
<el-input v-model="formData.no" placeholder="请输入合同编号" />
</el-form-item>
</el-col>
<el-col :span="8">
@ -18,34 +18,35 @@
<el-input v-model="formData.name" placeholder="请输入合同名称" />
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="8">
<el-form-item label="负责人" prop="ownerUserId">
<el-form-item label="报价单编号" prop="quotationId">
<el-select
v-model="formData.ownerUserId"
:disabled="formType !== 'create'"
@change="handleQuotationChange"
v-model="formData.quotationId"
class="w-1/1"
>
<el-option
v-for="item in userOptions"
v-for="item in quotationList"
:key="item.id"
:label="item.nickname"
:label="item.no"
:value="item.id"
/>
</el-select>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="8">
<el-form-item label="客户名称" prop="customerId">
<el-select
v-model="formData.customerId"
disabled=""
placeholder="请选择客户"
class="w-1/1"
@change="handleCustomerChange"
>
<el-option
v-for="item in customerList"
v-for="item in contactList"
:key="item.id"
:label="item.name"
:value="item.id"
@ -53,91 +54,286 @@
</el-select>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="商机名称" prop="businessId">
<el-select
@change="handleBusinessChange"
:disabled="!formData.customerId"
v-model="formData.businessId"
class="w-1/1"
>
<el-option
v-for="item in getBusinessOptions"
:key="item.id"
:label="item.name"
:value="item.id!"
/>
</el-select>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="8">
<el-form-item label="下单日期" prop="orderDate">
<el-date-picker
v-model="formData.orderDate"
placeholder="选择下单日期"
type="date"
value-format="x"
class="!w-1/1"
/>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="开始时间" prop="startTime">
<el-form-item label="合同签订日期" prop="startTime">
<el-date-picker
v-model="formData.startTime"
placeholder="选择开始时间"
type="date"
value-format="x"
class="!w-1/1"
placeholder="选择合同签订日期"
/>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="结束时间" prop="endTime">
<el-form-item label="合同结束时间" prop="endTime">
<el-date-picker
v-model="formData.endTime"
placeholder="选择结束时间"
type="date"
value-format="x"
class="!w-1/1"
placeholder="选择合同结束时间"
/>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="8">
<el-form-item label="公司签约人" prop="signUserId">
<el-select v-model="formData.signUserId" class="w-1/1">
<el-form-item label="票据模板" prop="invoiceTemplateId">
<el-input v-model="formData.invoiceTemplateId" placeholder="请输入票据模板" disabled />
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="8">
<el-form-item label="违约金比例" prop="penaltyRate">
<el-input v-model="formData.penaltyRate" placeholder="请输入违约金比例" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="延期付款利率" prop="latePaymentRate">
<el-input v-model="formData.latePaymentRate" placeholder="请输入延期付款利率" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="财务结算方式" prop="settleMethod">
<el-select v-model="formData.settleMethod" placeholder="请选择财务结算方式">
<el-option
v-for="item in userOptions"
:key="item.id"
:label="item.nickname"
:value="item.id!"
v-for="dict in getIntDictOptions('settle_method')"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="8">
<el-form-item label="最晚付款日期" prop="lastPayDate">
<el-date-picker
v-model="formData.lastPayDate"
type="date"
value-format="x"
placeholder="选择最晚付款日期"
/>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="合同期限(月)" prop="contractTerm">
<el-input v-model="formData.contractTerm" placeholder="请输入合同期限(月)" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="报价签约人" prop="signUserId">
<el-select v-model="formData.signUserId" placeholder="请选择报价签约人" disabled >
<el-option
v-for="dict in userOptions"
:key="dict.id"
:label="dict.nickname"
:value="dict.id"
/>
</el-select>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="8">
<el-form-item label="签约人联系电话" prop="signPhoneNumber">
<el-input v-model="formData.signPhoneNumber" placeholder="请输入签约人联系电话" disabled />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="签约人Email" prop="signEmail">
<el-input v-model="formData.signEmail" placeholder="请输入签约人Email" disabled />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="签约人微信" prop="signWechat">
<el-input v-model="formData.signWechat" placeholder="请输入签约人微信" disabled />
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="8">
<el-form-item label="商机负责人" prop="ownerUserId">
<el-select v-model="formData.ownerUserId" placeholder="请选择商机负责人" disabled>
<el-option
v-for="dict in userOptions"
:key="dict.id"
:label="dict.nickname"
:value="dict.id"
/>
</el-select>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="客户签约人" prop="signContactId">
<el-select
v-model="formData.signContactId"
:disabled="!formData.customerId"
class="w-1/1"
>
<el-form-item label="拓展人" prop="expanderUserId">
<el-select v-model="formData.expanderUserId" placeholder="请选择拓展人" disabled>
<el-option
v-for="item in getContactOptions"
:key="item.id"
:label="item.name"
:value="item.id"
v-for="dict in userOptions"
:key="dict.id"
:label="dict.nickname"
:value="dict.id"
/>
</el-select>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="备注" prop="remark">
<el-input v-model="formData.remark" placeholder="请输入备注" type="textarea" />
<el-form-item label="方案报价人" prop="pricingUserId">
<el-select v-model="formData.pricingUserId" placeholder="请选择方案报价人" disabled>
<el-option
v-for="dict in userOptions"
:key="dict.id"
:label="dict.nickname"
:value="dict.id"
/>
</el-select>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="8">
<el-form-item label="售后维护人" prop="afterSaleUserId">
<el-select v-model="formData.afterSaleUserId" placeholder="请选择售后维护人">
<el-option
v-for="dict in userOptions"
:key="dict.id"
:label="dict.nickname"
:value="dict.id"
/>
</el-select>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="协作人" prop="collUserId">
<el-select v-model="formData.collUserId" placeholder="请选择协作人">
<el-option
v-for="dict in userOptions"
:key="dict.id"
:label="dict.nickname"
:value="dict.id"
/>
</el-select>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="8">
<el-form-item label="授信方式" prop="creditMethod" >
<el-select v-model="formData.creditMethod" placeholder="请选择授信方式" disabled>
<el-option
v-for="dict in getIntDictOptions('credit_method')"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="授信计算周期" prop="creditCalcCycle" >
<el-select v-model="formData.creditCalcCycle" placeholder="请选择授信计算周期" disabled>
<el-option
v-for="dict in getIntDictOptions('credit_calc_cycle')"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="授信额度" prop="creditLimit">
<el-input v-model="formData.creditLimit" placeholder="请输入授信额度" disabled />
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="8">
<el-form-item label="合作主体" prop="partnerCompanyId">
<el-select v-model="formData.partnerCompanyId" placeholder="请选择合作主体" disabled >
<el-option
v-for="dict in deptList"
:key="dict.id"
:label="dict.name"
:value="dict.id"
/>
</el-select>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="归属部门" prop="deptId" >
<el-tree-select
v-model="formData.deptId"
:data="deptTree"
:props="defaultProps"
filterable
disabled
check-strictly
node-key="id"
placeholder="请选择归属部门"
/>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="第几次报价" prop="quotationTimes">
<el-input v-model="formData.quotationTimes" placeholder="请输入第几次报价" />
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<el-form-item label="合同正文" prop="contractBody">
<el-input v-model="formData.contractBody" placeholder="请输入合同正文" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="合同补充协议" prop="contractAgreement">
<el-input v-model="formData.contractAgreement" placeholder="请输入合同补充协议" />
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="8">
<el-form-item label="线上总金额" prop="onlinePrice">
<el-input
disabled
v-model="formData.onlinePrice"
placeholder="请输入线上总金额,单位:元"
:formatter="erpPriceInputFormatter"
/>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="线下总金额" prop="offlinePrice">
<el-input
disabled
v-model="formData.offlinePrice"
placeholder="请输入线下总金额,单位:元"
:formatter="erpPriceInputFormatter"
/>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="总金额" prop="price">
<el-input
disabled
v-model="formData.totalPrice"
:formatter="erpPriceInputFormatter"
/>
</el-form-item>
</el-col>
</el-row>
@ -151,41 +347,17 @@
:disabled="disabled"
/>
</el-tab-pane>
<el-tab-pane label="合同甲方关联单位" name="contractAAuthorizedCompany">
<ContractAAuthorizedCompanyForm ref="contractAAuthorizedCompanyFormRef" :contract-id="formData.id" />
</el-tab-pane>
<el-tab-pane label="合同甲方授权人信息" name="contractAAuthorizedPerson">
<ContractAAuthorizedPersonForm ref="contractAAuthorizedPersonFormRef" :contract-id="formData.id" />
</el-tab-pane>
<el-tab-pane label="合同乙方授权人信息" name="contractBAuthorizedPerson">
<ContractBAuthorizedPersonForm ref="contractBAuthorizedPersonFormRef" :contract-id="formData.id" />
</el-tab-pane>
</el-tabs>
</ContentWrap>
<el-row>
<el-col :span="8">
<el-form-item label="产品总金额" prop="totalProductPrice">
<el-input
disabled
v-model="formData.totalProductPrice"
:formatter="erpPriceInputFormatter"
/>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="整单折扣(%" prop="discountPercent">
<el-input-number
v-model="formData.discountPercent"
placeholder="请输入整单折扣"
controls-position="right"
:min="0"
:precision="2"
class="!w-1/1"
/>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="折扣后金额" prop="totalPrice">
<el-input
disabled
v-model="formData.totalPrice"
placeholder="请输入商机金额"
:formatter="erpPriceInputFormatter"
/>
</el-form-item>
</el-col>
</el-row>
</el-form>
<template #footer>
<el-button :disabled="formLoading" type="primary" @click="submitForm"></el-button>
@ -194,14 +366,18 @@
</Dialog>
</template>
<script lang="ts" setup>
import { getIntDictOptions, DICT_TYPE } from '@/utils/dict'
import * as CustomerApi from '@/api/crm/customer'
import * as ContractApi from '@/api/crm/contract'
import * as UserApi from '@/api/system/user'
import * as ContactApi from '@/api/crm/contact'
import * as BusinessApi from '@/api/crm/business'
import * as UserApi from '@/api/system/user'
import { QuotationApi,QuotationVO } from '@/api/crm/quotation'
import { erpPriceMultiply, erpPriceInputFormatter } from '@/utils'
import { useUserStore } from '@/store/modules/user'
import ContractProductForm from '@/views/crm/contract/components/ContractProductForm.vue'
import ContractAAuthorizedCompanyForm from './components/ContractAAuthorizedCompanyForm.vue'
import ContractAAuthorizedPersonForm from './components/ContractAAuthorizedPersonForm.vue'
import ContractBAuthorizedPersonForm from './components/ContractBAuthorizedPersonForm.vue'
const { t } = useI18n() //
const message = useMessage() //
@ -212,19 +388,40 @@ const formLoading = ref(false) // 表单的加载中1修改时的数据加
const formType = ref('') // create - update -
const formData = ref({
id: undefined,
no: undefined,
name: undefined,
no: undefined,
customerId: undefined,
businessId: undefined,
orderDate: undefined,
quotationId: undefined,
invoiceTemplateId: undefined,
processInstanceId: undefined,
auditStatus: undefined,
startTime: undefined,
endTime: undefined,
penaltyRate: undefined,
latePaymentRate: undefined,
settleMethod: undefined,
lastPayDate: undefined,
contractTerm: undefined,
signUserId: undefined,
signContactId: undefined,
signPhoneNumber: undefined,
signEmail: undefined,
signWechat: undefined,
offlinePrice: undefined,
onlinePrice: undefined,
ownerUserId: undefined,
discountPercent: 0,
totalProductPrice: undefined,
remark: undefined,
expanderUserId: undefined,
pricingUserId: undefined,
afterSaleUserId: undefined,
collUserId: undefined,
totalPrice: undefined,
contractBody: undefined,
contractAgreement: undefined,
creditMethod: undefined,
creditCalcCycle: undefined,
creditLimit: undefined,
partnerCompanyId: undefined,
deptId: undefined,
quotationTimes: undefined,
products: []
})
const formRules = reactive({
@ -236,12 +433,15 @@ const formRules = reactive({
const formRef = ref() // Ref
const userOptions = ref<UserApi.UserVO[]>([]) //
const customerList = ref([]) //
const businessList = ref<BusinessApi.BusinessVO[]>([])
const quotationList = ref<QuotationVO[]>([])
const contactList = ref<ContactApi.ContactVO[]>([])
/** 子表的表单 */
const subTabsName = ref('product')
const productFormRef = ref()
const contractAAuthorizedCompanyFormRef = ref()
const contractAAuthorizedPersonFormRef = ref()
const contractBAuthorizedPersonFormRef = ref()
/** 计算 discountPrice、totalPrice 价格 */
watch(
@ -250,15 +450,9 @@ watch(
if (!val) {
return
}
const totalProductPrice = val.products.reduce((prev, curr) => prev + curr.totalPrice, 0)
const discountPrice =
val.discountPercent != null
? erpPriceMultiply(totalProductPrice, val.discountPercent / 100.0)
: 0
const totalPrice = totalProductPrice - discountPrice
// const totalPrice = val.products.reduce((prev, curr) => prev + curr.totalPrice, 0)
//
formData.value.totalProductPrice = totalProductPrice
formData.value.totalPrice = totalPrice
// formData.value.totalPrice = totalPrice
},
{ deep: true }
)
@ -282,14 +476,15 @@ const open = async (type: string, id?: number) => {
customerList.value = await CustomerApi.getCustomerSimpleList()
//
userOptions.value = await UserApi.getSimpleUserList()
//
quotationList.value = await QuotationApi.getSimpleQuotationList()
//
if (formType.value === 'create') {
formData.value.ownerUserId = useUserStore().getUser.id
}
// if (formType.value === 'create') {
// formData.value.ownerUserId = useUserStore().getUser.id
// }
//
contactList.value = await ContactApi.getSimpleContactList()
//
businessList.value = await BusinessApi.getSimpleBusinessList()
}
defineExpose({ open }) // open
@ -305,6 +500,10 @@ const submitForm = async () => {
productFormRef.value.validate()
try {
const data = unref(formData.value) as unknown as ContractApi.ContractVO
//
data.contractAAuthorizedCompanys = contractAAuthorizedCompanyFormRef.value.getData()
data.contractAAuthorizedPersons = contractAAuthorizedPersonFormRef.value.getData()
data.contractBAuthorizedPersons = contractBAuthorizedPersonFormRef.value.getData()
if (formType.value === 'create') {
await ContractApi.createContract(data)
message.success(t('common.createSuccess'))
@ -324,46 +523,115 @@ const submitForm = async () => {
const resetForm = () => {
formData.value = {
id: undefined,
no: undefined,
name: undefined,
no: undefined,
customerId: undefined,
businessId: undefined,
orderDate: undefined,
quotationId: undefined,
invoiceTemplateId: undefined,
processInstanceId: undefined,
auditStatus: undefined,
startTime: undefined,
endTime: undefined,
penaltyRate: undefined,
latePaymentRate: undefined,
settleMethod: undefined,
lastPayDate: undefined,
contractTerm: undefined,
signUserId: undefined,
signContactId: undefined,
signPhoneNumber: undefined,
signEmail: undefined,
signWechat: undefined,
offlinePrice: undefined,
onlinePrice: undefined,
ownerUserId: undefined,
discountPercent: 0,
totalProductPrice: undefined,
remark: undefined,
expanderUserId: undefined,
pricingUserId: undefined,
afterSaleUserId: undefined,
collUserId: undefined,
totalPrice: undefined,
contractBody: undefined,
contractAgreement: undefined,
creditMethod: undefined,
creditCalcCycle: undefined,
creditLimit: undefined,
partnerCompanyId: undefined,
deptId: undefined,
quotationTimes: undefined,
products: []
}
formRef.value?.resetFields()
}
/** 处理切换客户 */
const handleCustomerChange = () => {
formData.value.businessId = undefined
formData.value.signContactId = undefined
formData.value.products = []
}
/** 处理商机变化 */
const handleBusinessChange = async (businessId: number) => {
const business = await BusinessApi.getBusiness(businessId)
business.products.forEach((item) => {
item.contractPrice = item.businessPrice
})
formData.value.products = business.products
const handleQuotationChange = async (quotationId: number) => {
if (!quotationId) return
try {
const quotation = await QuotationApi.getQuotation(quotationId)
formData.value.products = quotation.products;
formData.value.customerId = quotation.customerId;
formData.value.invoiceTemplateId = quotation.invoiceTemplateId;
formData.value.ownerUserId = quotation.ownerUserId;
formData.value.expanderUserId = quotation.expanderUserId;
formData.value.pricingUserId = quotation.pricingUserId;
formData.value.signUserId = quotation.signUserId;
formData.value.signPhoneNumber = quotation.signPhoneNumber;
formData.value.signEmail = quotation.signEmail;
formData.value.signWechat = quotation.signWechat;
formData.value.partnerCompanyId = quotation.partnerCompanyId;
formData.value.creditLimit = quotation.creditLimit;
formData.value.creditMethod = quotation.creditMethod;
formData.value.creditCalcCycle = quotation.creditCalcCycle;
formData.value.deptId = quotation.deptId;
formData.value.offlinePrice = quotation.offlinePrice;
formData.value.onlinePrice = quotation.onlinePrice;
formData.value.totalPrice = quotation.totalPrice;
// 🔁
await onCustomerChange(quotation.customerId);
} catch (err) {
} finally {
}
}
const onCustomerChange = async (customerId: string) => {
if (!customerId) return;
try {
formLoading.value = true;
const customerRes = await CustomerApi.getCustomer(customerId);
formData.value.cooperationType = customerRes.cooperationType;
formData.value.companyType = customerRes.companyType;
formData.value.listingStatus = customerRes.listingStatus;
formData.value.financingInfo = customerRes.financingInfo;
formData.value.paidInCapital = customerRes.paidInCapital;
formData.value.insuredCount = customerRes.insuredCount;
formData.value.establishmentDate = customerRes.establishmentDate;
formData.value.enterpriseType = customerRes.enterpriseType;
formData.value.businessStatus = customerRes.businessStatus;
formData.value.defendantRecord = customerRes.defendantRecord;
formData.value.businessAbnormal = customerRes.businessAbnormal;
formData.value.equityPledge = customerRes.equityPledge;
formData.value.dishonestRecord = customerRes.dishonestRecord;
formData.value.financingRecord = customerRes.financingRecord;
formData.value.enforcementRecord = customerRes.enforcementRecord;
} catch (err) {
console.error('获取客户详情失败:', err);
} finally {
formLoading.value = false;
}
}
/** 动态获取客户联系人 */
const getContactOptions = computed(() =>
contactList.value.filter((item) => item.customerId == formData.value.customerId)
)
/** 动态获取商机 */
const getBusinessOptions = computed(() =>
businessList.value.filter((item) => item.customerId == formData.value.customerId)
)
</script>

View File

@ -0,0 +1,145 @@
<template>
<el-form
ref="formRef"
:model="formData"
:rules="formRules"
v-loading="formLoading"
label-width="0px"
:inline-message="true"
>
<el-table :data="formData" class="-mt-10px">
<el-table-column label="序号" type="index" width="100" />
<el-table-column label="单位名称" min-width="150">
<template #default="{ row, $index }">
<el-form-item :prop="`${$index}.customerName`" :rules="formRules.customerName" class="mb-0px!">
<el-input v-model="row.customerName" placeholder="请输入单位名称" />
</el-form-item>
</template>
</el-table-column>
<el-table-column label="统一社会信用代码" min-width="150">
<template #default="{ row, $index }">
<el-form-item :prop="`${$index}.unifiedCreditCode`" :rules="formRules.unifiedCreditCode" class="mb-0px!">
<el-input v-model="row.unifiedCreditCode" placeholder="请输入统一社会信用代码" />
</el-form-item>
</template>
</el-table-column>
<el-table-column label="开票地址" min-width="150">
<template #default="{ row, $index }">
<el-form-item :prop="`${$index}.invoicingAddress`" :rules="formRules.invoicingAddress" class="mb-0px!">
<el-input v-model="row.invoicingAddress" placeholder="请输入开票地址" />
</el-form-item>
</template>
</el-table-column>
<el-table-column label="开票电话" min-width="150">
<template #default="{ row, $index }">
<el-form-item :prop="`${$index}.invoicingTelephone`" :rules="formRules.invoicingTelephone" class="mb-0px!">
<el-input v-model="row.invoicingTelephone" placeholder="请输入开票电话" />
</el-form-item>
</template>
</el-table-column>
<el-table-column label="开户行" min-width="150">
<template #default="{ row, $index }">
<el-form-item :prop="`${$index}.companyBank`" :rules="formRules.companyBank" class="mb-0px!">
<el-input v-model="row.companyBank" placeholder="请输入开户行" />
</el-form-item>
</template>
</el-table-column>
<el-table-column label="开户账号" min-width="150">
<template #default="{ row, $index }">
<el-form-item :prop="`${$index}.companyAccount`" :rules="formRules.companyAccount" class="mb-0px!">
<el-input v-model="row.companyAccount" placeholder="请输入开户账号" />
</el-form-item>
</template>
</el-table-column>
<el-table-column label="通讯地址" min-width="150">
<template #default="{ row, $index }">
<el-form-item :prop="`${$index}.companyAddress`" :rules="formRules.companyAddress" class="mb-0px!">
<el-input v-model="row.companyAddress" placeholder="请输入通讯地址" />
</el-form-item>
</template>
</el-table-column>
<el-table-column align="center" fixed="right" label="操作" width="60">
<template #default="{ $index }">
<el-button @click="handleDelete($index)" link></el-button>
</template>
</el-table-column>
</el-table>
</el-form>
<el-row justify="center" class="mt-3">
<el-button @click="handleAdd" round>+ 添加</el-button>
</el-row>
</template>
<script setup lang="ts">
import * as ContractApi from '@/api/crm/contract'
const props = defineProps<{
contractId: undefined // ID
}>()
const formLoading = ref(false) //
const formData = ref([])
const formRules = reactive({
contractId: [{ required: true, message: '合同ID不能为空', trigger: 'blur' }],
customerName: [{ required: true, message: '单位名称不能为空', trigger: 'blur' }],
unifiedCreditCode: [{ required: true, message: '统一社会信用代码不能为空', trigger: 'blur' }],
invoicingAddress: [{ required: true, message: '开票地址不能为空', trigger: 'blur' }],
invoicingTelephone: [{ required: true, message: '开票电话不能为空', trigger: 'blur' }],
companyBank: [{ required: true, message: '开户行不能为空', trigger: 'blur' }],
companyAccount: [{ required: true, message: '开户账号不能为空', trigger: 'blur' }]
})
const formRef = ref() // Ref
/** 监听主表的关联字段的变化,加载对应的子表数据 */
watch(
() => props.contractId,
async (val) => {
// 1.
formData.value = []
// 2. val
if (!val) {
return;
}
try {
formLoading.value = true
formData.value = await ContractApi.getContractAAuthorizedCompanyListByContractId(val)
} finally {
formLoading.value = false
}
},
{ immediate: true }
)
/** 新增按钮操作 */
const handleAdd = () => {
const row = {
id: undefined,
contractId: undefined,
customerName: undefined,
unifiedCreditCode: undefined,
invoicingAddress: undefined,
invoicingTelephone: undefined,
companyBank: undefined,
companyAccount: undefined,
companyAddress: undefined,
deletedFlag: undefined
}
row.contractId = props.contractId
formData.value.push(row)
}
/** 删除按钮操作 */
const handleDelete = (index) => {
formData.value.splice(index, 1)
}
/** 表单校验 */
const validate = () => {
return formRef.value.validate()
}
/** 表单值 */
const getData = () => {
return formData.value
}
defineExpose({ validate, getData })
</script>

View File

@ -0,0 +1,150 @@
<template>
<el-form
ref="formRef"
:model="formData"
:rules="formRules"
v-loading="formLoading"
label-width="0px"
:inline-message="true"
>
<el-table :data="formData" class="-mt-10px">
<el-table-column label="序号" type="index" width="100" />
<el-table-column label="客户联系人ID" min-width="150">
<template #default="{ row, $index }">
<el-form-item :prop="`${$index}.customerContactId`" :rules="formRules.customerContactId" class="mb-0px!">
<el-input v-model="row.customerContactId" placeholder="请输入客户联系人ID" />
</el-form-item>
</template>
</el-table-column>
<el-table-column label="姓名" min-width="150">
<template #default="{ row, $index }">
<el-form-item :prop="`${$index}.customerName`" :rules="formRules.customerName" class="mb-0px!">
<el-input v-model="row.customerName" placeholder="请输入姓名" />
</el-form-item>
</template>
</el-table-column>
<el-table-column label="授权人类型" min-width="150">
<template #default="{ row, $index }">
<el-form-item :prop="`${$index}.authPersonType`" :rules="formRules.authPersonType" class="mb-0px!">
<el-select v-model="row.authPersonType" placeholder="请选择授权人类型">
<el-option
v-for="dict in getIntDictOptions('auth_person_type')"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item>
</template>
</el-table-column>
<el-table-column label="手机号" min-width="150">
<template #default="{ row, $index }">
<el-form-item :prop="`${$index}.phoneNumber`" :rules="formRules.phoneNumber" class="mb-0px!">
<el-input v-model="row.phoneNumber" placeholder="请输入手机号" />
</el-form-item>
</template>
</el-table-column>
<el-table-column label="微信号" min-width="150">
<template #default="{ row, $index }">
<el-form-item :prop="`${$index}.wechat`" :rules="formRules.wechat" class="mb-0px!">
<el-input v-model="row.wechat" placeholder="请输入微信号" />
</el-form-item>
</template>
</el-table-column>
<el-table-column label="身份证号" min-width="150">
<template #default="{ row, $index }">
<el-form-item :prop="`${$index}.idNumber`" :rules="formRules.idNumber" class="mb-0px!">
<el-input v-model="row.idNumber" placeholder="请输入身份证号" />
</el-form-item>
</template>
</el-table-column>
<el-table-column label="电子邮箱" min-width="150">
<template #default="{ row, $index }">
<el-form-item :prop="`${$index}.email`" :rules="formRules.email" class="mb-0px!">
<el-input v-model="row.email" placeholder="请输入电子邮箱" />
</el-form-item>
</template>
</el-table-column>
<el-table-column align="center" fixed="right" label="操作" width="60">
<template #default="{ $index }">
<el-button @click="handleDelete($index)" link></el-button>
</template>
</el-table-column>
</el-table>
</el-form>
<el-row justify="center" class="mt-3">
<el-button @click="handleAdd" round>+ 添加</el-button>
</el-row>
</template>
<script setup lang="ts">
import * as ContractApi from '@/api/crm/contract'
import { getIntDictOptions, getStrDictOptions, DICT_TYPE, getBoolDictOptions } from '@/utils/dict'
const props = defineProps<{
contractId: undefined // ID
}>()
const formLoading = ref(false) //
const formData = ref([])
const formRules = reactive({
contractId: [{ required: true, message: '合同ID不能为空', trigger: 'blur' }],
customerContactId: [{ required: true, message: '客户联系人ID不能为空', trigger: 'blur' }],
customerName: [{ required: true, message: '姓名不能为空', trigger: 'blur' }],
phoneNumber: [{ required: true, message: '手机号不能为空', trigger: 'blur' }]
})
const formRef = ref() // Ref
/** 监听主表的关联字段的变化,加载对应的子表数据 */
watch(
() => props.contractId,
async (val) => {
// 1.
formData.value = []
// 2. val
if (!val) {
return;
}
try {
formLoading.value = true
formData.value = await ContractApi.getContractAAuthorizedPersonListByContractId(val)
} finally {
formLoading.value = false
}
},
{ immediate: true }
)
/** 新增按钮操作 */
const handleAdd = () => {
const row = {
id: undefined,
contractId: undefined,
customerContactId: undefined,
customerName: undefined,
authPersonType: undefined,
phoneNumber: undefined,
wechat: undefined,
idNumber: undefined,
email: undefined,
deletedFlag: undefined
}
row.contractId = props.contractId
formData.value.push(row)
}
/** 删除按钮操作 */
const handleDelete = (index) => {
formData.value.splice(index, 1)
}
/** 表单校验 */
const validate = () => {
return formRef.value.validate()
}
/** 表单值 */
const getData = () => {
return formData.value
}
defineExpose({ validate, getData })
</script>

View File

@ -0,0 +1,166 @@
<template>
<el-form
ref="formRef"
:model="formData"
:rules="formRules"
v-loading="formLoading"
label-width="0px"
:inline-message="true"
>
<el-table :data="formData" class="-mt-10px">
<el-table-column label="序号" type="index" width="100" />
<el-table-column label="用户ID" min-width="150">
<template #default="{ row, $index }">
<el-form-item :prop="`${$index}.userId`" :rules="formRules.userId" class="mb-0px!">
<el-input v-model="row.userId" placeholder="请输入用户ID" />
</el-form-item>
</template>
</el-table-column>
<el-table-column label="姓名" min-width="150">
<template #default="{ row, $index }">
<el-form-item :prop="`${$index}.name`" :rules="formRules.name" class="mb-0px!">
<el-input v-model="row.name" placeholder="请输入姓名" />
</el-form-item>
</template>
</el-table-column>
<el-table-column label="类别" min-width="150">
<template #default="{ row, $index }">
<el-form-item :prop="`${$index}.authType`" :rules="formRules.authType" class="mb-0px!">
<el-select v-model="row.authType" placeholder="请选择类别">
<el-option
v-for="dict in getIntDictOptions('auth_type')"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item>
</template>
</el-table-column>
<el-table-column label="岗位" min-width="150">
<template #default="{ row, $index }">
<el-form-item :prop="`${$index}.postId`" :rules="formRules.postId" class="mb-0px!">
<el-input v-model="row.postId" placeholder="请输入岗位" />
</el-form-item>
</template>
</el-table-column>
<el-table-column label="职级" min-width="150">
<template #default="{ row, $index }">
<el-form-item :prop="`${$index}.userRank`" :rules="formRules.userRank" class="mb-0px!">
<el-input v-model="row.userRank" placeholder="请输入职级" />
</el-form-item>
</template>
</el-table-column>
<el-table-column label="权限说明" min-width="150">
<template #default="{ row, $index }">
<el-form-item :prop="`${$index}.authDesc`" :rules="formRules.authDesc" class="mb-0px!">
<el-input v-model="row.authDesc" placeholder="请输入权限说明" />
</el-form-item>
</template>
</el-table-column>
<el-table-column label="手机号" min-width="150">
<template #default="{ row, $index }">
<el-form-item :prop="`${$index}.phoneNumber`" :rules="formRules.phoneNumber" class="mb-0px!">
<el-input v-model="row.phoneNumber" placeholder="请输入手机号" />
</el-form-item>
</template>
</el-table-column>
<el-table-column label="微信号" min-width="150">
<template #default="{ row, $index }">
<el-form-item :prop="`${$index}.wechat`" :rules="formRules.wechat" class="mb-0px!">
<el-input v-model="row.wechat" placeholder="请输入微信号" />
</el-form-item>
</template>
</el-table-column>
<el-table-column label="电子邮箱" min-width="150">
<template #default="{ row, $index }">
<el-form-item :prop="`${$index}.email`" :rules="formRules.email" class="mb-0px!">
<el-input v-model="row.email" placeholder="请输入电子邮箱" />
</el-form-item>
</template>
</el-table-column>
<el-table-column align="center" fixed="right" label="操作" width="60">
<template #default="{ $index }">
<el-button @click="handleDelete($index)" link></el-button>
</template>
</el-table-column>
</el-table>
</el-form>
<el-row justify="center" class="mt-3">
<el-button @click="handleAdd" round>+ 添加</el-button>
</el-row>
</template>
<script setup lang="ts">
import * as ContractApi from '@/api/crm/contract'
import { getIntDictOptions, getStrDictOptions, DICT_TYPE, getBoolDictOptions } from '@/utils/dict'
const props = defineProps<{
contractId: undefined // ID
}>()
const formLoading = ref(false) //
const formData = ref([])
const formRules = reactive({
contractId: [{ required: true, message: '合同ID不能为空', trigger: 'blur' }],
userId: [{ required: true, message: '用户ID不能为空', trigger: 'blur' }],
name: [{ required: true, message: '姓名不能为空', trigger: 'blur' }],
phoneNumber: [{ required: true, message: '手机号不能为空', trigger: 'blur' }]
})
const formRef = ref() // Ref
/** 监听主表的关联字段的变化,加载对应的子表数据 */
watch(
() => props.contractId,
async (val) => {
// 1.
formData.value = []
// 2. val
if (!val) {
return;
}
try {
formLoading.value = true
formData.value = await ContractApi.getContractBAuthorizedPersonListByContractId(val)
} finally {
formLoading.value = false
}
},
{ immediate: true }
)
/** 新增按钮操作 */
const handleAdd = () => {
const row = {
id: undefined,
contractId: undefined,
userId: undefined,
name: undefined,
authType: undefined,
postId: undefined,
userRank: undefined,
authDesc: undefined,
phoneNumber: undefined,
wechat: undefined,
email: undefined,
deletedFlag: undefined
}
row.contractId = props.contractId
formData.value.push(row)
}
/** 删除按钮操作 */
const handleDelete = (index) => {
formData.value.splice(index, 1)
}
/** 表单校验 */
const validate = () => {
return formRef.value.validate()
}
/** 表单值 */
const getData = () => {
return formData.value
}
defineExpose({ validate, getData })
</script>

View File

@ -8,77 +8,82 @@
:inline-message="true"
:disabled="disabled"
>
<el-table :data="formData" class="-mt-10px">
<el-table-column label="序号" type="index" align="center" width="60" />
<el-table-column label="产品名称" min-width="180">
<el-table :data="formData" class="-mt-10px">
<el-table-column label="序号" type="index" width="100" />
<el-table-column label="产品编号" min-width="150">
<template #default="{ row, $index }">
<el-form-item :prop="`${$index}.productId`" :rules="formRules.productId" class="mb-0px!">
<el-select
v-model="row.productId"
clearable
filterable
@change="onChangeProduct($event, row)"
placeholder="请选择产品"
>
<el-option
v-for="item in productList"
:key="item.id"
:label="item.name"
:value="item.id"
/>
</el-select>
<el-input v-model="row.productId" placeholder="请输入产品编号" disabled />
</el-form-item>
</template>
</el-table-column>
<el-table-column label="条码" min-width="150">
<template #default="{ row }">
<el-form-item class="mb-0px!">
<el-input disabled v-model="row.productNo" />
</el-form-item>
</template>
</el-table-column>
<el-table-column label="单位" min-width="80">
<template #default="{ row }">
<dict-tag :type="DICT_TYPE.CRM_PRODUCT_UNIT" :value="row.productUnit" />
</template>
</el-table-column>
<el-table-column label="价格(元)" min-width="120">
<template #default="{ row }">
<el-form-item class="mb-0px!">
<el-input disabled v-model="row.productPrice" :formatter="erpPriceInputFormatter" />
</el-form-item>
</template>
</el-table-column>
<el-table-column label="售价(元)" fixed="right" min-width="140">
<el-table-column label="产品名称" min-width="150">
<template #default="{ row, $index }">
<el-form-item :prop="`${$index}.contractPrice`" class="mb-0px!">
<el-input-number
v-model="row.contractPrice"
controls-position="right"
:min="0.001"
:precision="2"
class="!w-100%"
/>
<el-form-item :prop="`${$index}.productName`" :rules="formRules.productName" class="mb-0px!">
<el-input v-model="row.productName" placeholder="请输入产品名称" disabled />
</el-form-item>
</template>
</el-table-column>
<el-table-column label="数量" prop="count" fixed="right" min-width="120">
<el-table-column label="产品分类编号" min-width="150">
<template #default="{ row, $index }">
<el-form-item :prop="`${$index}.count`" :rules="formRules.count" class="mb-0px!">
<el-input-number
v-model="row.count"
controls-position="right"
:min="0.001"
:precision="3"
class="!w-100%"
/>
<el-form-item :prop="`${$index}.productCategoryId`" :rules="formRules.productCategoryId" class="mb-0px!">
<el-input v-model="row.productCategoryId" placeholder="请输入产品分类编号" disabled />
</el-form-item>
</template>
</el-table-column>
<el-table-column label="合计" prop="totalPrice" fixed="right" min-width="140">
<el-table-column label="产品单位" min-width="150">
<template #default="{ row, $index }">
<el-form-item :prop="`${$index}.totalPrice`" class="mb-0px!">
<el-input disabled v-model="row.totalPrice" :formatter="erpPriceInputFormatter" />
<el-form-item :prop="`${$index}.productUnit`" :rules="formRules.productUnit" class="mb-0px!">
<el-input v-model="row.productUnit" placeholder="请输入产品单位" disabled />
</el-form-item>
</template>
</el-table-column>
<el-table-column label="线上价格" min-width="150">
<template #default="{ row, $index }">
<el-form-item :prop="`${$index}.onlinePrice`" :rules="formRules.onlinePrice" class="mb-0px!">
<el-input v-model="row.onlinePrice" placeholder="请输入线上价格" disabled />
</el-form-item>
</template>
</el-table-column>
<el-table-column label="线下价格" min-width="150">
<template #default="{ row, $index }">
<el-form-item :prop="`${$index}.offlinePrice`" :rules="formRules.offlinePrice" class="mb-0px!">
<el-input v-model="row.offlinePrice" placeholder="请输入线下价格" disabled />
</el-form-item>
</template>
</el-table-column>
<el-table-column label="总计价格" min-width="150">
<template #default="{ row, $index }">
<el-form-item :prop="`${$index}.totalPrice`" :rules="formRules.totalPrice" class="mb-0px!">
<el-input v-model="row.totalPrice" placeholder="请输入总计价格" disabled />
</el-form-item>
</template>
</el-table-column>
<el-table-column label="产品票据" min-width="150">
<template #default="{ row, $index }">
<el-form-item :prop="`${$index}.productInvoice`" :rules="formRules.productInvoice" class="mb-0px!">
<el-input v-model="row.productInvoice" placeholder="请输入产品票据" disabled />
</el-form-item>
</template>
</el-table-column>
<el-table-column label="产品开具项目" min-width="150">
<template #default="{ row, $index }">
<el-form-item :prop="`${$index}.productInvoiceItem`" :rules="formRules.productInvoiceItem" class="mb-0px!">
<el-input v-model="row.productInvoiceItem" placeholder="请输入产品开具项目" disabled />
</el-form-item>
</template>
</el-table-column>
<el-table-column label="服务费票据" min-width="150">
<template #default="{ row, $index }">
<el-form-item :prop="`${$index}.serviceInvoice`" :rules="formRules.serviceInvoice" class="mb-0px!">
<el-input v-model="row.serviceInvoice" placeholder="请输入服务费票据" disabled />
</el-form-item>
</template>
</el-table-column>
<el-table-column label="服务开具项目" min-width="150">
<template #default="{ row, $index }">
<el-form-item :prop="`${$index}.serviceInvoiceItem`" :rules="formRules.serviceInvoiceItem" class="mb-0px!">
<el-input v-model="row.serviceInvoiceItem" placeholder="请输入服务开具项目" disabled />
</el-form-item>
</template>
</el-table-column>
@ -89,9 +94,6 @@
</el-table-column>
</el-table>
</el-form>
<el-row justify="center" class="mt-3" v-if="!disabled">
<el-button @click="handleAdd" round>+ 添加产品</el-button>
</el-row>
</template>
<script setup lang="ts">
import * as ProductApi from '@/api/crm/product'
@ -121,25 +123,6 @@ watch(
{ immediate: true }
)
/** 监听合同产品变化,计算合同产品总价 */
watch(
() => formData.value,
(val) => {
if (!val || val.length === 0) {
return
}
//
val.forEach((item) => {
if (item.contractPrice != null && item.count != null) {
item.totalPrice = erpPriceMultiply(item.contractPrice, item.count)
} else {
item.totalPrice = undefined
}
})
},
{ deep: true }
)
/** 新增按钮操作 */
const handleAdd = () => {
const row = {

View File

@ -48,9 +48,8 @@
/>
</el-table>
<el-row class="mt-10px" justify="end">
<el-col :span="3"> 整单折扣{{ erpPriceInputFormatter(contract.discountPercent) }}% </el-col>
<el-col :span="4">
产品总金额{{ erpPriceInputFormatter(contract.totalProductPrice) }}
总金额{{ erpPriceInputFormatter(contract.totalPrice) }}
</el-col>
</el-row>
</ContentWrap>

View File

@ -1,7 +1,4 @@
<template>
<doc-alert title="【合同】合同管理、合同提醒" url="https://doc.iocoder.cn/crm/contract/" />
<doc-alert title="【通用】数据权限" url="https://doc.iocoder.cn/crm/permission/" />
<ContentWrap>
<!-- 搜索工作栏 -->
<el-form

View File

@ -1,5 +1,5 @@
<template>
<Dialog v-model="dialogVisible" :title="dialogTitle">
<Dialog v-model="dialogVisible" :title="dialogTitle" width="50%">
<el-form
ref="formRef"
v-loading="formLoading"

View File

@ -12,8 +12,8 @@
<el-descriptions-item label="客户来源">
<dict-tag :type="DICT_TYPE.CRM_CUSTOMER_SOURCE" :value="customer.source" />
</el-descriptions-item>
<el-descriptions-item label="合作类型">{{ customer.cooperationType }}</el-descriptions-item>
<el-descriptions-item label="合作地区">{{ customer.cooperationArea }}</el-descriptions-item>
<el-descriptions-item label="合作类型">{{ getName(getIntDictOptions(DICT_TYPE.CRM_COOPERATION_TYPE),customer.cooperationType) }}</el-descriptions-item>
<el-descriptions-item label="合作地区">{{ getName(getIntDictOptions(DICT_TYPE.CRM_COOPERATION_AREA),customer.cooperationArea) }}</el-descriptions-item>
<el-descriptions-item label="客户级别">
<dict-tag :type="DICT_TYPE.CRM_CUSTOMER_LEVEL" :value="customer.level" />
</el-descriptions-item>
@ -66,7 +66,7 @@
</template>
<script lang="ts" setup>
import * as CustomerApi from '@/api/crm/customer'
import { DICT_TYPE } from '@/utils/dict'
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
import { formatDate } from '@/utils/formatTime'
defineOptions({ name: 'CrmCustomerDetailsInfo' })
@ -75,5 +75,11 @@ const { customer } = defineProps<{
}>()
const activeNames = ref(['basicInfo', 'systemInfo']) //
const getName = (opt, val) => {
console.log('%csrc/views/crm/customer/detail/CustomerDetailsInfo.vue:79 opt, val', 'color: #007acc;', opt, val);
const arr = opt.filter(v => v.value == val)
console.log('%csrc/views/crm/customer/detail/CustomerDetailsInfo.vue:81 arr', 'color: #007acc;', arr);
return arr.length ? arr[0]['label'] : ''
}
</script>
<style lang="scss" scoped></style>

View File

@ -51,7 +51,7 @@
<el-tab-pane label="联系人" lazy>
<ContactList :biz-id="customer.id!" :biz-type="BizTypeEnum.CRM_CUSTOMER" />
</el-tab-pane>
<!-- <el-tab-pane label="团队成员">
<el-tab-pane label="团队成员">
<PermissionList
ref="permissionListRef"
:biz-id="customer.id!"
@ -59,7 +59,7 @@
:show-action="!permissionListRef?.isPool || false"
@quit-team="close"
/>
</el-tab-pane> -->
</el-tab-pane>
<el-tab-pane label="商机" lazy>
<BusinessList :biz-id="customer.id!" :biz-type="BizTypeEnum.CRM_CUSTOMER" />
</el-tab-pane>

View File

@ -4,7 +4,7 @@
ref="formRef"
:model="formData"
:rules="formRules"
label-width="100px"
label-width="110px"
v-loading="formLoading"
>
<el-row>
@ -69,7 +69,7 @@
<Icon class="mr-5px" icon="ep:plus" />
添加联系人
</el-button>
<FollowUpRecordContactForm :contacts="formData.contacts" />
<FollowUpRecordContactForm @success="del" :contacts="formData.contacts" />
</el-form-item>
</el-col>
</el-row>
@ -111,7 +111,9 @@ const formData = ref({
nextTime: undefined,
picUrls: undefined,
fileUrls: undefined,
contactIds: undefined
contactIds: undefined,
contacts: []
})
const formRef = ref() // Ref
const formRules = reactive({
@ -126,6 +128,18 @@ const contactTableSelectRef = ref<InstanceType<typeof ContactListModal>>()
const handleOpenContact = () => {
contactTableSelectRef.value?.open()
}
const handleAddContact = (contactId: [], newContacts: ContactApi.ContactVO[]) => {
newContacts.forEach((contact) => {
if (!formData.value.contacts.some((item) => item.id === contact.id)) {
formData.value.contacts.push(contact)
}
})
}
const del = (index: number) => {
formData.value.contacts.splice(index, 1)
}
/** 打开弹窗 */
const open = async (bizType: number, bizId: number) => {
dialogVisible.value = true
@ -145,14 +159,12 @@ const submitForm = async () => {
//
formLoading.value = true
try {
const data = formData.value as unknown as ReturnVisitRecordVO
if (formType.value === 'create') {
await ReturnVisitRecordApi.createReturnVisitRecord(data)
message.success(t('common.createSuccess'))
} else {
await ReturnVisitRecordApi.updateReturnVisitRecord(data)
message.success(t('common.updateSuccess'))
}
const data = {
...formData.value,
contactIds: formData.value.contacts.map((item) => item.id),
} as unknown as ReturnVisitRecordVO
await ReturnVisitRecordApi.createReturnVisitRecord(data)
message.success(t('common.createSuccess'))
dialogVisible.value = false
//
emit('success')
@ -172,6 +184,7 @@ const resetForm = () => {
nextTime: undefined,
picUrls: undefined,
fileUrls: undefined,
contacts: [] ,
contactIds: undefined
}
formRef.value?.resetFields()

View File

@ -50,7 +50,6 @@
link
type="danger"
@click="handleDelete(scope.row.id)"
v-hasPermi="['crm:return-visit-record:delete']"
>
删除
</el-button>

View File

@ -52,7 +52,7 @@
<Icon class="mr-5px" icon="ep:plus" />
添加联系人
</el-button>
<FollowUpRecordContactForm :contacts="formData.contacts" />
<FollowUpRecordContactForm @success="del" :contacts="formData.contacts" />
</el-form-item>
</el-col>
<el-col :span="24" v-if="formData.bizType == BizTypeEnum.CRM_CUSTOMER">
@ -162,6 +162,10 @@ const handleAddContact = (contactId: [], newContacts: ContactApi.ContactVO[]) =>
})
}
const del = (index: number) => {
formData.value.contacts.splice(index, 1)
}
/** 关联商机 */
const businessTableSelectRef = ref<InstanceType<typeof BusinessListModal>>()
const handleOpenBusiness = () => {

View File

@ -17,7 +17,7 @@
</el-table-column>
<el-table-column align="center" fixed="right" label="操作" width="130">
<template #default="scope">
<el-button link type="danger" @click="handleDelete(scope.row.id)"> </el-button>
<el-button link type="danger" @click="handleDelete(scope.$index)"> </el-button>
</template>
</el-table-column>
</el-table>
@ -40,8 +40,10 @@ watch(
{ immediate: true }
)
const emit = defineEmits(['success']) // success
/** 删除按钮操作 */
const handleDelete = (index: number) => {
formData.value.splice(index, 1)
emit('success', index)
}
</script>

View File

@ -1,10 +1,10 @@
<template>
<Dialog :title="dialogTitle" v-model="dialogVisible">
<Dialog :title="dialogTitle" v-model="dialogVisible" width="50%">
<el-form
ref="formRef"
:model="formData"
:rules="formRules"
label-width="100px"
label-width="110px"
v-loading="formLoading"
>
<el-row>

View File

@ -0,0 +1,599 @@
<template>
<Dialog :title="dialogTitle" v-model="dialogVisible" width="1280">
<el-form
ref="formRef"
:model="formData"
:rules="formRules"
label-width="100px"
v-loading="formLoading"
>
<el-row>
<el-col :span="8">
<el-form-item label="报价单编号" prop="no">
<el-input v-model="formData.no" placeholder="请输入报价单编号" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="选择商机" prop="businessId">
<el-select v-model="formData.businessId" placeholder="请选择商机" @change="onBusinessChange">
<el-option
v-for="dict in businessList"
:key="dict.id"
:label="dict.name"
:value="dict.id"
/>
</el-select>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="8">
<el-form-item label="客户" prop="customerId">
<el-select v-model="formData.customerId" placeholder="请选择客户" @change="onCustomerChange">
<el-option
v-for="item in customerList"
:key="item.id"
:label="item.name"
:value="item.id"
/>
</el-select>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="合作类型" prop="cooperationType">
<el-input v-model="formData.cooperationType" disabled />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="公司类型" prop="companyType">
<el-input v-model="formData.companyType" disabled />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="上市情况" prop="listingStatus">
<el-input v-model="formData.listingStatus" disabled />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="融资信息" prop="financingInfo">
<el-input v-model="formData.financingInfo" disabled />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="实缴资金" prop="paidInCapital">
<el-input v-model="formData.paidInCapital" disabled />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="参保人数" prop="insuredCount">
<el-input v-model="formData.insuredCount" disabled />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="成立日期" prop="establishmentDate">
<el-input v-model="formData.establishmentDate" disabled />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="企业类型" prop="enterpriseType">
<el-input v-model="formData.enterpriseType" disabled />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="营业状态" prop="businessStatus">
<el-input v-model="formData.businessStatus" disabled />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="被告记录" prop="defendantRecord">
<el-radio-group v-model="formData.defendantRecord" :disabled="true">
<el-radio :label="true"></el-radio>
<el-radio :label="false"></el-radio>
</el-radio-group>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="经营异常" prop="businessAbnormal">
<el-radio-group v-model="formData.businessAbnormal" :disabled="true">
<el-radio :label="true"></el-radio>
<el-radio :label="false"></el-radio>
</el-radio-group>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="股权出质" prop="equityPledge">
<el-radio-group v-model="formData.equityPledge" :disabled="true">
<el-radio :label="true"></el-radio>
<el-radio :label="false"></el-radio>
</el-radio-group>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="失信记录" prop="dishonestRecord">
<el-radio-group v-model="formData.dishonestRecord" :disabled="true">
<el-radio :label="true"></el-radio>
<el-radio :label="false"></el-radio>
</el-radio-group>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="融资记录" prop="financingRecord">
<el-radio-group v-model="formData.financingRecord" :disabled="true">
<el-radio :label="true"></el-radio>
<el-radio :label="false"></el-radio>
</el-radio-group>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="被执行记录" prop="enforcementRecord">
<el-radio-group v-model="formData.enforcementRecord" :disabled="true">
<el-radio :label="true"></el-radio>
<el-radio :label="false"></el-radio>
</el-radio-group>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="8">
<el-form-item label="合作主体" prop="partnerCompanyId">
<el-select v-model="formData.partnerCompanyId" placeholder="请选择合作主体" @change="onPartnerChange">
<el-option
v-for="dict in deptList"
:key="dict.id"
:label="dict.name"
:value="dict.id"
/>
</el-select>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="开户行" prop="bBankName">
<el-input v-model="formData.bBankName" disabled />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="银行账号" prop="bBankAccount">
<el-input v-model="formData.bBankAccount" disabled />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="法人代表" prop="bLegalRepresentative">
<el-input v-model="formData.bLegalRepresentative" disabled />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="营业执照号" prop="bBusinessLicenseNumber">
<el-input v-model="formData.bBusinessLicenseNumber" disabled />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="营业执照" prop="bBusinessLicense">
<el-input v-model="formData.bBusinessLicense" disabled />
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="8">
<el-form-item label="票据模板" prop="invoiceTemplateId">
<el-select v-model="formData.invoiceTemplateId" placeholder="请选择票据模板">
<el-option
v-for="dict in userOptions"
:key="dict.id"
:label="dict.nickname"
:value="dict.id"
/>
</el-select>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="商机负责人" prop="ownerUserId">
<el-select v-model="formData.ownerUserId" placeholder="请选择商机负责人">
<el-option
v-for="dict in userOptions"
:key="dict.id"
:label="dict.nickname"
:value="dict.id"
/>
</el-select>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="拓展人" prop="expanderUserId">
<el-select v-model="formData.expanderUserId" placeholder="请选择拓展人">
<el-option
v-for="dict in userOptions"
:key="dict.id"
:label="dict.nickname"
:value="dict.id"
/>
</el-select>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="方案报价人" prop="pricingUserId">
<el-select v-model="formData.pricingUserId" placeholder="请选择方案报价人">
<el-option
v-for="dict in userOptions"
:key="dict.id"
:label="dict.nickname"
:value="dict.id"
/>
</el-select>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="报价签约人" prop="signUserId">
<el-select v-model="formData.signUserId" placeholder="请选择报价签约人">
<el-option
v-for="dict in userOptions"
:key="dict.id"
:label="dict.nickname"
:value="dict.id"
/>
</el-select>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="签约人联系电话" prop="signPhoneNumber">
<el-input v-model="formData.signPhoneNumber" placeholder="请输入签约人联系电话" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="签约人Email" prop="signEmail">
<el-input v-model="formData.signEmail" placeholder="请输入签约人Email" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="签约人微信" prop="signWechat">
<el-input v-model="formData.signWechat" placeholder="请输入签约人微信" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="账期" prop="paymentTerm">
<el-select v-model="formData.paymentTerm" placeholder="请选择账期">
<el-option
v-for="dict in getStrDictOptions('payment_term')"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="授信方式" prop="creditMethod">
<el-select v-model="formData.creditMethod" placeholder="请选择授信方式">
<el-option
v-for="dict in getIntDictOptions('credit_method')"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="授信计算周期" prop="creditCalcCycle">
<el-select v-model="formData.creditCalcCycle" placeholder="请选择授信计算周期">
<el-option
v-for="dict in getIntDictOptions('credit_calc_cycle')"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="授信额度" prop="creditLimit">
<el-input v-model="formData.creditLimit" placeholder="请输入授信额度" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="归属部门" prop="deptId">
<el-tree-select
v-model="formData.deptId"
:data="deptTree"
:props="defaultProps"
filterable
check-strictly
node-key="id"
placeholder="请选择归属部门"
/>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="8">
<el-form-item label="线上总金额" prop="onlinePrice">
<el-input
disabled
v-model="formData.onlinePrice"
:formatter="erpPriceInputFormatter"
/>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="线下总金额" prop="offlinePrice">
<el-input
disabled
v-model="formData.offlinePrice"
:formatter="erpPriceInputFormatter"
/>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="总金额" prop="totalPrice">
<el-input
disabled
v-model="formData.totalPrice"
:formatter="erpPriceInputFormatter"
/>
</el-form-item>
</el-col>
</el-row>
</el-form>
<!-- 子表的表单 -->
<el-tabs v-model="subTabsName">
<el-tab-pane label="报价产品关联" name="quotationProduct">
<QuotationProductForm ref="quotationProductFormRef" :quotation-id="formData.id" :business-id="formData.businessId" />
</el-tab-pane>
</el-tabs>
<template #footer>
<el-button @click="submitForm" type="primary" :disabled="formLoading"> </el-button>
<el-button @click="dialogVisible = false"> </el-button>
</template>
</Dialog>
</template>
<script setup lang="ts">
import { getIntDictOptions, getStrDictOptions, DICT_TYPE } from '@/utils/dict'
import { QuotationApi, QuotationVO } from '@/api/crm/quotation'
import { defaultProps, handleTree } from '@/utils/tree'
import QuotationProductForm from './components/QuotationProductForm.vue'
import * as CustomerApi from '@/api/crm/customer'
import * as UserApi from '@/api/system/user'
import * as DeptApi from '@/api/system/dept'
import * as BusinessApi from '@/api/crm/business'
import { erpPriceMultiply, erpPriceInputFormatter } from '@/utils'
/** CRM 方案报价 表单 */
defineOptions({ name: 'QuotationForm' })
const { t } = useI18n() //
const message = useMessage() //
const customerList = ref([]) //
const userOptions = ref<UserApi.UserVO[]>([]) //
const businessList = ref([])
const deptTree = ref() //
const deptList = ref() //
const invoiceTemplateList = ref([])
const dialogVisible = ref(false) //
const dialogTitle = ref('') //
const formLoading = ref(false) // 12
const formType = ref('') // create - update -
const formData = ref({
no: undefined,
customerId: undefined,
businessId: undefined,
invoiceTemplateId: undefined,
processInstanceId: undefined,
auditStatus: undefined,
ownerUserId: undefined,
expanderUserId: undefined,
pricingUserId: undefined,
signUserId: undefined,
signPhoneNumber: undefined,
signEmail: undefined,
signWechat: undefined,
paymentTerm: undefined,
creditMethod: undefined,
creditCalcCycle: undefined,
creditLimit: undefined,
partnerCompanyId: undefined,
deptId: undefined,
creator: undefined,
createTime: undefined,
updater: undefined,
onlinePrice: undefined,
offlinePrice: undefined,
totalPrice: undefined,
cooperationType: undefined, //
companyType: undefined, //
listingStatus: undefined, //
financingInfo: undefined, //
paidInCapital: undefined, //
insuredCount: undefined, //
establishmentDate: undefined, //
enterpriseType: undefined, //
businessStatus: undefined, //
defendantRecord: undefined, //
businessAbnormal: undefined, //
equityPledge: undefined, //
dishonestRecord: undefined, //
financingRecord: undefined, //
enforcementRecord: undefined, //
bBankName: undefined, //
bBankAccount: undefined, //
bLegalRepresentative: undefined, //
bBusinessLicenseNumber: undefined, //
bBusinessLicense: undefined //
})
const formRules = reactive({
no: [{ required: true, message: '报价单编号不能为空', trigger: 'blur' }],
customerId: [{ required: true, message: '客户id不能为空', trigger: 'change' }],
businessId: [{ required: true, message: '商机编号不能为空', trigger: 'change' }],
invoiceTemplateId: [{ required: true, message: '票据模板Id不能为空', trigger: 'change' }]
})
const formRef = ref() // Ref
/** 子表的表单 */
const subTabsName = ref('quotationProduct')
const quotationProductFormRef = ref()
const onBusinessChange = async (businessId: string) => {
if (!businessId) return
try {
formLoading.value = true;
const res = await BusinessApi.getBusiness(businessId);
formData.value.customerId = res.customerId;
formData.value.creditLimit = res.creditLimit; //
formData.value.ownerUserId = res.ownerUserId; //
formData.value.paymentTerm = res.paymentTerm;
formData.value.creditMethod = res.creditMethod;
formData.value.creditCalcCycle = res.creditCalcCycle;
formData.value.deptId = res.deptId;
formData.value.offlinePrice = res.offlinePrice;
formData.value.onlinePrice = res.onlinePrice;
formData.value.totalPrice = res.totalPrice;
// 🔁
await onCustomerChange(res.customerId);
} catch (err) {
} finally {
formLoading.value = false;
}
}
const onCustomerChange = async (customerId: string) => {
if (!customerId) return;
try {
formLoading.value = true;
const customerRes = await CustomerApi.getCustomer(customerId);
formData.value.cooperationType = customerRes.cooperationType;
formData.value.companyType = customerRes.companyType;
formData.value.listingStatus = customerRes.listingStatus;
formData.value.financingInfo = customerRes.financingInfo;
formData.value.paidInCapital = customerRes.paidInCapital;
formData.value.insuredCount = customerRes.insuredCount;
formData.value.establishmentDate = customerRes.establishmentDate;
formData.value.enterpriseType = customerRes.enterpriseType;
formData.value.businessStatus = customerRes.businessStatus;
formData.value.defendantRecord = customerRes.defendantRecord;
formData.value.businessAbnormal = customerRes.businessAbnormal;
formData.value.equityPledge = customerRes.equityPledge;
formData.value.dishonestRecord = customerRes.dishonestRecord;
formData.value.financingRecord = customerRes.financingRecord;
formData.value.enforcementRecord = customerRes.enforcementRecord;
} catch (err) {
console.error('获取客户详情失败:', err);
} finally {
formLoading.value = false;
}
}
const onPartnerChange = async (id: string) => {
if (!id) return
try {
formLoading.value = true;
const res = await DeptApi.getDept(id);
formData.value.bBankName = res.bankName;
formData.value.bBankAccount = res.bankAccount;
formData.value.bLegalRepresentative = res.legalRepresentative;
formData.value.bBusinessLicenseNumber = res.businessLicenseNumber;
formData.value.bBusinessLicense = res.businessLicense;
} catch (err) {
} finally {
formLoading.value = false;
}
}
/** 打开弹窗 */
const open = async (type: string, id?: number) => {
dialogVisible.value = true
dialogTitle.value = t('action.' + type)
formType.value = type
resetForm()
//
if (id) {
formLoading.value = true
try {
formData.value = await QuotationApi.getQuotation(id)
} finally {
formLoading.value = false
}
}
//
customerList.value = await CustomerApi.getCustomerSimpleList()
//
userOptions.value = await UserApi.getSimpleUserList()
businessList.value = await BusinessApi.getSimpleBusinessList()
//
deptTree.value = handleTree(await DeptApi.getSimpleDeptList())
deptList.value = await DeptApi.getSimpleDeptList()
}
defineExpose({ open }) // open
/** 提交表单 */
const emit = defineEmits(['success']) // success
const submitForm = async () => {
//
await formRef.value.validate()
//
try {
await quotationProductFormRef.value.validate()
} catch (e) {
subTabsName.value = 'quotationProduct'
return
}
//
formLoading.value = true
try {
const data = formData.value as unknown as QuotationVO
//
data.quotationProducts = quotationProductFormRef.value.getData()
if (formType.value === 'create') {
await QuotationApi.createQuotation(data)
message.success(t('common.createSuccess'))
} else {
await QuotationApi.updateQuotation(data)
message.success(t('common.updateSuccess'))
}
dialogVisible.value = false
//
emit('success')
} finally {
formLoading.value = false
}
}
/** 重置表单 */
const resetForm = () => {
formData.value = {
no: undefined,
customerId: undefined,
businessId: undefined,
invoiceTemplateId: undefined,
processInstanceId: undefined,
auditStatus: undefined,
ownerUserId: undefined,
expanderUserId: undefined,
pricingUserId: undefined,
signUserId: undefined,
signPhoneNumber: undefined,
signEmail: undefined,
signWechat: undefined,
startTime: undefined,
totalPrice: undefined,
paymentTerm: undefined,
creditMethod: undefined,
creditCalcCycle: undefined,
creditLimit: undefined,
partnerCompanyId: undefined,
deptId: undefined,
creator: undefined,
createTime: undefined,
updater: undefined
}
formRef.value?.resetFields()
}
</script>

View File

@ -0,0 +1,183 @@
<template>
<el-form
ref="formRef"
:model="formData"
:rules="formRules"
v-loading="formLoading"
label-width="0px"
:inline-message="true"
>
<el-table :data="formData" class="-mt-10px">
<el-table-column label="序号" type="index" width="100" />
<el-table-column label="产品编号" min-width="150">
<template #default="{ row, $index }">
<el-form-item :prop="`${$index}.productId`" :rules="formRules.productId" class="mb-0px!">
<el-input v-model="row.productId" placeholder="请输入产品编号" disabled />
</el-form-item>
</template>
</el-table-column>
<el-table-column label="产品名称" min-width="150">
<template #default="{ row, $index }">
<el-form-item :prop="`${$index}.productName`" :rules="formRules.productName" class="mb-0px!">
<el-input v-model="row.productName" placeholder="请输入产品名称" disabled />
</el-form-item>
</template>
</el-table-column>
<el-table-column label="产品分类编号" min-width="150">
<template #default="{ row, $index }">
<el-form-item :prop="`${$index}.productCategoryId`" :rules="formRules.productCategoryId" class="mb-0px!">
<el-input v-model="row.productCategoryId" placeholder="请输入产品分类编号" disabled />
</el-form-item>
</template>
</el-table-column>
<el-table-column label="产品单位" min-width="150">
<template #default="{ row, $index }">
<el-form-item :prop="`${$index}.productUnit`" :rules="formRules.productUnit" class="mb-0px!">
<el-input v-model="row.productUnit" placeholder="请输入产品单位" disabled />
</el-form-item>
</template>
</el-table-column>
<el-table-column label="线上价格" min-width="150">
<template #default="{ row, $index }">
<el-form-item :prop="`${$index}.onlinePrice`" :rules="formRules.onlinePrice" class="mb-0px!">
<el-input v-model="row.onlinePrice" placeholder="请输入线上价格" />
</el-form-item>
</template>
</el-table-column>
<el-table-column label="线下价格" min-width="150">
<template #default="{ row, $index }">
<el-form-item :prop="`${$index}.offlinePrice`" :rules="formRules.offlinePrice" class="mb-0px!">
<el-input v-model="row.offlinePrice" placeholder="请输入线下价格" />
</el-form-item>
</template>
</el-table-column>
<el-table-column label="总计价格" min-width="150">
<template #default="{ row, $index }">
<el-form-item :prop="`${$index}.totalPrice`" :rules="formRules.totalPrice" class="mb-0px!">
<el-input v-model="row.totalPrice" placeholder="请输入总计价格" />
</el-form-item>
</template>
</el-table-column>
<el-table-column label="产品票据" min-width="150">
<template #default="{ row, $index }">
<el-form-item :prop="`${$index}.productInvoice`" :rules="formRules.productInvoice" class="mb-0px!">
<el-input v-model="row.productInvoice" placeholder="请输入产品票据" />
</el-form-item>
</template>
</el-table-column>
<el-table-column label="产品开具项目" min-width="150">
<template #default="{ row, $index }">
<el-form-item :prop="`${$index}.productInvoiceItem`" :rules="formRules.productInvoiceItem" class="mb-0px!">
<el-input v-model="row.productInvoiceItem" placeholder="请输入产品开具项目" />
</el-form-item>
</template>
</el-table-column>
<el-table-column label="服务费票据" min-width="150">
<template #default="{ row, $index }">
<el-form-item :prop="`${$index}.serviceInvoice`" :rules="formRules.serviceInvoice" class="mb-0px!">
<el-input v-model="row.serviceInvoice" placeholder="请输入服务费票据" />
</el-form-item>
</template>
</el-table-column>
<el-table-column label="服务开具项目" min-width="150">
<template #default="{ row, $index }">
<el-form-item :prop="`${$index}.serviceInvoiceItem`" :rules="formRules.serviceInvoiceItem" class="mb-0px!">
<el-input v-model="row.serviceInvoiceItem" placeholder="请输入服务开具项目" />
</el-form-item>
</template>
</el-table-column>
<el-table-column align="center" fixed="right" label="操作" width="60">
<template #default="{ $index }">
<el-button @click="handleDelete($index)" link></el-button>
</template>
</el-table-column>
</el-table>
</el-form>
<el-row justify="center" class="mt-3">
<!-- <el-button @click="handleAdd" round>+ 添加CRM 报价产品关联</el-button> -->
</el-row>
</template>
<script setup lang="ts">
import { QuotationApi } from '@/api/crm/quotation'
import * as BusinessApi from '@/api/crm/business'
const props = defineProps<{
quotationId: undefined, //
businessId: undefined
}>()
const formLoading = ref(false) //
const formData = ref([])
const formRules = reactive({
quotationId: [{ required: true, message: '报价编号不能为空', trigger: 'blur' }],
productId: [{ required: true, message: '产品编号不能为空', trigger: 'blur' }],
productName: [{ required: true, message: '产品名称不能为空', trigger: 'blur' }],
productCategoryId: [{ required: true, message: '产品分类编号不能为空', trigger: 'blur' }],
productUnit: [{ required: true, message: '产品单位不能为空', trigger: 'blur' }],
onlinePrice: [{ required: true, message: '线上价格不能为空', trigger: 'blur' }],
offlinePrice: [{ required: true, message: '线下价格不能为空', trigger: 'blur' }],
totalPrice: [{ required: true, message: '总计价格不能为空', trigger: 'blur' }]
})
const formRef = ref() // Ref
/** 监听主表的关联字段的变化,加载对应的子表数据 */
watch(
() => props.businessId,
async (val) => {
// 1.
formData.value = []
// 2. val
if (!val) {
return;
}
try {
formLoading.value = true
// if(val.quotationId) {
// formData.value = await QuotationApi.getQuotationProductListByQuotationId(val.quotationId)
// } else if(val.businessId) {
const business = await BusinessApi.getBusiness(val);
formData.value = business.products
// }
} finally {
formLoading.value = false
}
},
{ immediate: true }
)
/** 新增按钮操作 */
const handleAdd = () => {
const row = {
quotationId: undefined,
productId: undefined,
productName: undefined,
productCategoryId: undefined,
productUnit: undefined,
onlinePrice: undefined,
offlinePrice: undefined,
totalPrice: undefined,
productInvoice: undefined,
productInvoiceItem: undefined,
serviceInvoice: undefined,
serviceInvoiceItem: undefined
}
row.quotationId = props.quotationId
formData.value.push(row)
}
/** 删除按钮操作 */
const handleDelete = (index) => {
formData.value.splice(index, 1)
}
/** 表单校验 */
const validate = () => {
return formRef.value.validate()
}
/** 表单值 */
const getData = () => {
return formData.value
}
defineExpose({ validate, getData })
</script>

View File

@ -0,0 +1,285 @@
<template>
<ContentWrap>
<!-- 搜索工作栏 -->
<el-form
class="-mb-15px"
:model="queryParams"
ref="queryFormRef"
:inline="true"
label-width="68px"
>
<el-form-item label="报价单编号" prop="no">
<el-input
v-model="queryParams.no"
placeholder="请输入报价单编号"
clearable
@keyup.enter="handleQuery"
class="!w-240px"
/>
</el-form-item>
<el-form-item label="客户" prop="customerId">
<el-select
v-model="queryParams.customerId"
placeholder="请选择客户"
clearable
class="!w-240px"
>
<el-option
v-for="dict in customerList"
:key="dict.id"
:label="dict.name"
:value="dict.id"
/>
</el-select>
</el-form-item>
<el-form-item label="商机负责人" prop="ownerUserId">
<el-select
v-model="queryParams.ownerUserId"
placeholder="请选择商机负责人"
clearable
class="!w-240px"
>
<el-option
v-for="dict in userOptions"
:key="dict.id"
:label="dict.nickName"
:value="dict.id"
/>
</el-select>
</el-form-item>
<el-form-item label="创建时间" prop="createTime">
<el-date-picker
v-model="queryParams.createTime"
value-format="YYYY-MM-DD HH:mm:ss"
type="daterange"
start-placeholder="开始日期"
end-placeholder="结束日期"
:default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]"
class="!w-220px"
/>
</el-form-item>
<el-form-item>
<el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button>
<el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button>
<el-button
type="primary"
plain
@click="openForm('create')"
v-hasPermi="['crm:quotation:create']"
>
<Icon icon="ep:plus" class="mr-5px" /> 新增
</el-button>
<el-button
type="success"
plain
@click="handleExport"
:loading="exportLoading"
v-hasPermi="['crm:quotation:export']"
>
<Icon icon="ep:download" class="mr-5px" /> 导出
</el-button>
</el-form-item>
</el-form>
</ContentWrap>
<!-- 列表 -->
<ContentWrap>
<el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true">
<el-table-column label="报价单编号" align="center" prop="no" />
<el-table-column label="客户名称" align="center" prop="customerId">
<template #default="scope">
<el-link
:underline="false"
type="primary"
@click="openCustomerDetail(scope.row.customerId)"
>
{{ scope.row.customerName }}
</el-link>
</template>
</el-table-column>
<el-table-column label="商机负责人" align="center" prop="ownerUserId">
<template #default="scope">
<el-link
:underline="false"
type="primary"
@click="openContactDetail(scope.row.ownerUserId)"
>
{{ scope.row.ownerUserName }}
</el-link>
</template>
</el-table-column>
<el-table-column label="账期" align="center" prop="paymentTerm">
<template #default="scope">
<dict-tag :type="'payment_term'" :value="scope.row.paymentTerm" />
</template>
</el-table-column>
<el-table-column label="授信方式" align="center" prop="creditMethod">
<template #default="scope">
<dict-tag :type="'credit_method'" :value="scope.row.creditMethod" />
</template>
</el-table-column>
<el-table-column label="授信计算周期" align="center" prop="creditCalcCycle">
<template #default="scope">
<dict-tag :type="'credit_calc_cycle'" :value="scope.row.creditCalcCycle" />
</template>
</el-table-column>
<el-table-column label="授信额度" align="center" prop="creditLimit" />
<el-table-column label="合作主体" align="center" prop="partnerCompanyName" />
<el-table-column label="审批状态" align="center" prop="auditStatus">
<template #default="scope">
<dict-tag :type="DICT_TYPE.SYSTEM_USER_SEX" :value="scope.row.auditStatus" />
</template>
</el-table-column>
<el-table-column
label="创建时间"
align="center"
prop="createTime"
:formatter="dateFormatter"
width="180px"
/>
<el-table-column label="操作" align="center" min-width="120px">
<template #default="scope">
<el-button
link
type="primary"
@click="openForm('update', scope.row.id)"
v-hasPermi="['crm:quotation:update']"
>
编辑
</el-button>
<el-button
link
type="danger"
@click="handleDelete(scope.row.id)"
v-hasPermi="['crm:quotation:delete']"
>
删除
</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页 -->
<Pagination
:total="total"
v-model:page="queryParams.pageNo"
v-model:limit="queryParams.pageSize"
@pagination="getList"
/>
</ContentWrap>
<!-- 表单弹窗添加/修改 -->
<QuotationForm ref="formRef" @success="getList" />
</template>
<script setup lang="ts">
import { getIntDictOptions, DICT_TYPE } from '@/utils/dict'
import { dateFormatter } from '@/utils/formatTime'
import download from '@/utils/download'
import { QuotationApi, QuotationVO } from '@/api/crm/quotation'
import QuotationForm from './QuotationForm.vue'
import * as CustomerApi from '@/api/crm/customer'
import * as UserApi from '@/api/system/user'
import * as DeptApi from '@/api/system/dept'
/** CRM 方案报价 列表 */
defineOptions({ name: 'Quotation' })
const message = useMessage() //
const { t } = useI18n() //
const userOptions = ref<UserApi.UserVO[]>([]) //
const customerList = ref([]) //
const { push } = useRouter()
const loading = ref(true) //
const list = ref<QuotationVO[]>([]) //
const total = ref(0) //
const queryParams = reactive({
pageNo: 1,
pageSize: 10,
no: undefined,
customerId: undefined,
ownerUserId: undefined,
partnerCompanyId: undefined,
createTime: []
})
const queryFormRef = ref() //
const exportLoading = ref(false) //
/** 查询列表 */
const getList = async () => {
loading.value = true
try {
const data = await QuotationApi.getQuotationPage(queryParams)
list.value = data.list
total.value = data.total
} finally {
loading.value = false
}
}
/** 搜索按钮操作 */
const handleQuery = () => {
queryParams.pageNo = 1
getList()
}
/** 重置按钮操作 */
const resetQuery = () => {
queryFormRef.value.resetFields()
handleQuery()
}
/** 添加/修改操作 */
const formRef = ref()
const openForm = (type: string, id?: number) => {
formRef.value.open(type, id)
}
/** 删除按钮操作 */
const handleDelete = async (id: number) => {
try {
//
await message.delConfirm()
//
await QuotationApi.deleteQuotation(id)
message.success(t('common.delSuccess'))
//
await getList()
} catch {}
}
/** 导出按钮操作 */
const handleExport = async () => {
try {
//
await message.exportConfirm()
//
exportLoading.value = true
const data = await QuotationApi.exportQuotation(queryParams)
download.excel(data, 'CRM 方案报价.xls')
} catch {
} finally {
exportLoading.value = false
}
}
/** 打开联系人详情 */
const openContactDetail = (id: number) => {
push({ name: 'CrmContactDetail', params: { id } })
}
/** 打开客户详情 */
const openCustomerDetail = (id: number) => {
push({ name: 'CrmCustomerDetail', params: { id } })
}
/** 初始化 **/
onMounted(async () => {
getList()
//
customerList.value = await CustomerApi.getCustomerSimpleList()
//
userOptions.value = await UserApi.getSimpleUserList()
})
</script>