diff --git a/src/api/crm/quotation/index.ts b/src/api/crm/quotation/index.ts index 1192cfac1..9c9aa5735 100644 --- a/src/api/crm/quotation/index.ts +++ b/src/api/crm/quotation/index.ts @@ -75,6 +75,11 @@ export const QuotationApi = { return await request.put({ url: `/crm/quotation/submit?id=${id}` }) }, + // 客户确认 + enterQuotation: async (data: QuotationVO) => { + return await request.post({ url: `/crm/quotation/customer-confirm`, data }) + }, + // ==================== 子表(CRM 报价产品关联) ==================== // 获得CRM 报价产品关联列表 diff --git a/src/utils/dict.ts b/src/utils/dict.ts index 3bc697721..382f09d23 100644 --- a/src/utils/dict.ts +++ b/src/utils/dict.ts @@ -202,6 +202,7 @@ export enum DICT_TYPE { // ========== CRM - 客户管理模块 ========== CRM_AUDIT_STATUS = 'crm_audit_status', // CRM 审批状态 + CRM_CONFIRM_STATUS = 'crm_confirm_status', // CRM 客户确认状态 CRM_BIZ_TYPE = 'crm_biz_type', // CRM 业务类型 CRM_BUSINESS_END_STATUS_TYPE = 'crm_business_end_status_type', // CRM 商机结束状态类型 CRM_RECEIVABLE_RETURN_TYPE = 'crm_receivable_return_type', // CRM 回款的还款方式 diff --git a/src/views/crm/contract/ContractForm.vue b/src/views/crm/contract/ContractForm.vue index f833d6662..d213f5c4c 100644 --- a/src/views/crm/contract/ContractForm.vue +++ b/src/views/crm/contract/ContractForm.vue @@ -18,15 +18,16 @@ - + @@ -339,6 +342,7 @@ v-model="formData.endTime" type="date" value-format="x" + format="YYYY-MM-DD" placeholder="选择合同结束时间" style="width: 100%" /> @@ -753,7 +757,7 @@ const handleQuotationChange = async (quotationId: number) => { const customerRes = await ContractApi.quotationTimes(quotation.id); formData.value.quotationTimes = customerRes.quotationTimes; - await onCustomerChange(quotation.customerId); + // await onCustomerChange(quotation.customerId); await onPartnerChange(quotation.partnerCompanyId); } catch (err) { } finally { @@ -786,6 +790,9 @@ const onCustomerChange = async (customerId: string) => { formData.value.financingInfo = customerRes.financingInfo; formData.value.socialEmployeeNum = customerRes.socialEmployeeNum; formData.value.registeredAddress = customerRes.registeredAddress; + formData.value.quotationId = customerRes.quotationId; + await handleQuotationChange(customerRes.quotationId); + } catch (err) { console.error('获取客户详情失败:', err); } finally { diff --git a/src/views/crm/contract/components/ContractBAuthorizedPersonForm.vue b/src/views/crm/contract/components/ContractBAuthorizedPersonForm.vue index 6c99a9ba5..88702d451 100644 --- a/src/views/crm/contract/components/ContractBAuthorizedPersonForm.vue +++ b/src/views/crm/contract/components/ContractBAuthorizedPersonForm.vue @@ -52,11 +52,11 @@ - - + + - {{row.deptName}} + {{row.postName}} @@ -185,7 +185,7 @@ const handleAdd = () => { userId: undefined, name: undefined, authType: undefined, - deptName: undefined, + postName: undefined, userRank: undefined, authDesc: undefined, phoneNumber: undefined, @@ -207,8 +207,8 @@ const changeUser = (i, val) => { let arr = props.userOptions.filter(v => v.id === val) if(arr.length) { formData.value[i].name = arr[0].nickname - formData.value[i].deptName = arr[0].deptName - formData.value[i].postId = arr[0].deptId + formData.value[i].postName = arr[0].postName + formData.value[i].postId = arr[0].postId } } const getList = (val: []) => { @@ -223,7 +223,7 @@ const getList = (val: []) => { "name": item.name, "authType": item.post, "postId": item.userRank, - "deptName": item.deptName, + "postName": item.postName, "userRank": item.mobile, "authDesc": item.authDesc, "wechat":item. wechat, diff --git a/src/views/crm/contract/index.vue b/src/views/crm/contract/index.vue index 4369a991d..457ba235c 100644 --- a/src/views/crm/contract/index.vue +++ b/src/views/crm/contract/index.vue @@ -246,7 +246,7 @@ handleCommand(command, scope.row)" v-hasPermi="[ 'crm:contract:update', - 'crm:contract:delete' + 'crm:contract:delete', + 'crm:contract:print', + 'crm:contract:change' ]" > 更多 @@ -273,19 +275,19 @@ 合同变更 打印合同 删除 @@ -402,7 +404,7 @@ const handleCommand = (command: string, row: UserApi.UserVO) => { handleChange(row) break case 'handlePrint': - getPrintTemplate(row) + onPrintContract(row) break case 'handleDelete': handleDelete(row.id) @@ -461,32 +463,33 @@ const onPrintContract = async (row: ContractApi.ContractVO) => { throw new Error('返回的合同文件为空') } - const contentType = res.type.toLowerCase() - let fileType = 'application/msword' // 默认使用 .doc 的 MIME 类型 - let extension = '.doc' + // const contentType = res.type.toLowerCase() + // let fileType = 'application/msword' // 默认使用 .doc 的 MIME 类型 + // let extension = '.doc' - if (contentType.includes('wordprocessingml')) { - fileType = 'application/vnd.openxmlformats-officedocument.wordprocessingml.document' // 对应 .docx 文件的 MIME 类型 - extension = '.docx' - } - // 创建 blob 对象并设置文件类型 - const blob = new Blob([res], { type: fileType }) + // if (contentType.includes('wordprocessingml')) { + // fileType = 'application/vnd.openxmlformats-officedocument.wordprocessingml.document' // 对应 .docx 文件的 MIME 类型 + // extension = '.docx' + // } + // // 创建 blob 对象并设置文件类型 + // const blob = new Blob([res], { type: fileType }) - const url = window.URL.createObjectURL(blob) - const link = document.createElement('a') - link.href = url - const disposition = res.headers['content-disposition'] - let fileName = '合同.docx' - if (disposition && disposition.includes('filename=')) { - const match = disposition.match(/filename="?([^"]+)"?/) - if (match?.[1]) { - fileName = decodeURIComponent(match[1]) - } - } - link.click() - window.URL.revokeObjectURL(url) + // const url = window.URL.createObjectURL(blob) + // const link = document.createElement('a') + // link.href = url + // const disposition = res.headers['content-disposition'] + // let fileName = '合同.docx' + // if (disposition && disposition.includes('filename=')) { + // const match = disposition.match(/filename="?([^"]+)"?/) + // if (match?.[1]) { + // fileName = decodeURIComponent(match[1]) + // } + // } + // link.click() + console.log('%csrc/views/crm/contract/index.vue:489 res.data', 'color: #007acc;', res); + window.open(res) - message.success('提交审核成功!') + // message.success('提交审核成功!') await getList() } catch (error) { console.error('合同下载失败', error) diff --git a/src/views/crm/productprocurementcost/ProductProcurementCostForm.vue b/src/views/crm/productprocurementcost/ProductProcurementCostForm.vue index 9224ac4f3..9dd62d39e 100644 --- a/src/views/crm/productprocurementcost/ProductProcurementCostForm.vue +++ b/src/views/crm/productprocurementcost/ProductProcurementCostForm.vue @@ -1,60 +1,107 @@ - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 确 定 @@ -67,7 +114,9 @@ import { ProductProcurementCostApi, ProductProcurementCostVO } from '@/api/crm/p /** 产品采购成本 表单 */ defineOptions({ name: 'ProductProcurementCostForm' }) +import * as ProductApi from '@/api/crm/product' +const productList = ref([]) // 产品列表 const { t } = useI18n() // 国际化 const message = useMessage() // 消息弹窗 @@ -78,6 +127,9 @@ const formType = ref('') // 表单的类型:create - 新增;update - 修改 const formData = ref({ projectId: undefined, projectCode: undefined, + category: undefined, + detailType: undefined, + unit: undefined, customerPrice: undefined, purchaseCost: undefined, agentFeePreReturn: undefined, @@ -117,6 +169,7 @@ const open = async (type: string, id?: number) => { dialogTitle.value = t('action.' + type) formType.value = type resetForm() + console.log('%csrc/views/crm/productprocurementcost/ProductProcurementCostForm.vue:172 id', 'color: #007acc;', id); // 修改时,设置数据 if (id) { formLoading.value = true @@ -129,6 +182,16 @@ const open = async (type: string, id?: number) => { } defineExpose({ open }) // 提供 open 方法,用于打开弹窗 + +const changeProduct = (val) => { + let arr = productList.value.filter(v => v.id === val) + if (arr.length) { + formData.value.projectCode = arr[0]['projectCode'] + formData.value.category = arr[0]['category'] + formData.value.detailType = arr[0]['detailType'] + formData.value.unit = arr[0]['unit'] + } +} /** 提交表单 */ const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调 const submitForm = async () => { @@ -175,4 +238,8 @@ const resetForm = () => { } formRef.value?.resetFields() } +onMounted( async() => { + productList.value = await ProductApi.getProductSimpleList() + +}) \ No newline at end of file diff --git a/src/views/crm/productprocurementcost/index.vue b/src/views/crm/productprocurementcost/index.vue index 39022e834..271bb49ca 100644 --- a/src/views/crm/productprocurementcost/index.vue +++ b/src/views/crm/productprocurementcost/index.vue @@ -87,12 +87,12 @@ :formatter="dateFormatter" width="180px" /> - + 编辑 diff --git a/src/views/crm/quotation/index.vue b/src/views/crm/quotation/index.vue index c2fbe6a28..f47dbf42d 100644 --- a/src/views/crm/quotation/index.vue +++ b/src/views/crm/quotation/index.vue @@ -6,7 +6,7 @@ :model="queryParams" ref="queryFormRef" :inline="true" - label-width="90px" + label-width="110px" > + + + + + + + + + + - 提交审核 - + command="handleSubmit" + v-if="checkPermi(['crm:contract:update']) && scope.row.auditStatus === 0" + > + 提交审核 + + + 客户确认 + { case 'handleProcessDetail': handleProcessDetail(row) break + case 'handleEnter': + handleEnter(row) + break default: break } @@ -350,6 +380,12 @@ const handleProcessDetail = (row) => { } }) } + +const handleEnter = async (row) => { + await QuotationApi.enterQuotation({id: row.id}) + message.success('操作成功!') + +} const getName = (opt, val) => { const arr = opt.filter(v => v.id == val) return arr.length ? arr[0]['nickname'] : '' diff --git a/src/views/system/user/UserForm.vue b/src/views/system/user/UserForm.vue index 630688a33..786dac5d1 100644 --- a/src/views/system/user/UserForm.vue +++ b/src/views/system/user/UserForm.vue @@ -69,7 +69,7 @@ - + ({ username: [{ required: true, message: '用户名称不能为空', trigger: 'blur' }], nickname: [{ required: true, message: '用户昵称不能为空', trigger: 'blur' }], password: [{ required: true, message: '用户密码不能为空', trigger: 'blur' }], + postIds: [{ required: true, message: '岗位不能为空', trigger: 'blur' }], email: [ { type: 'email',