Skip to content

Commit 937fc17

Browse files
YunaiVgitee-org
authored andcommitted
!330 商机、商机状态类型页面
Merge pull request !330 from 刘先生/dev
2 parents eb97447 + 2700b08 commit 937fc17

File tree

6 files changed

+932
-0
lines changed

6 files changed

+932
-0
lines changed

src/api/crm/business/index.ts

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import request from '@/config/axios'
2+
3+
export interface BusinessVO {
4+
id: number
5+
name: string
6+
statusTypeId: number
7+
statusId: number
8+
contactNextTime: Date
9+
customerId: number
10+
dealTime: Date
11+
price: number
12+
discountPercent: number
13+
productPrice: number
14+
remark: string
15+
ownerUserId: number
16+
roUserIds: string
17+
rwUserIds: string
18+
endStatus: number
19+
endRemark: string
20+
contactLastTime: Date
21+
followUpStatus: number
22+
}
23+
24+
// 查询商机列表
25+
export const getBusinessPage = async (params) => {
26+
return await request.get({ url: `/crm/business/page`, params })
27+
}
28+
29+
// 查询商机详情
30+
export const getBusiness = async (id: number) => {
31+
return await request.get({ url: `/crm/business/get?id=` + id })
32+
}
33+
34+
// 新增商机
35+
export const createBusiness = async (data: BusinessVO) => {
36+
return await request.post({ url: `/crm/business/create`, data })
37+
}
38+
39+
// 修改商机
40+
export const updateBusiness = async (data: BusinessVO) => {
41+
return await request.put({ url: `/crm/business/update`, data })
42+
}
43+
44+
// 删除商机
45+
export const deleteBusiness = async (id: number) => {
46+
return await request.delete({ url: `/crm/business/delete?id=` + id })
47+
}
48+
49+
// 导出商机 Excel
50+
export const exportBusiness = async (params) => {
51+
return await request.download({ url: `/crm/business/export-excel`, params })
52+
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import request from '@/config/axios'
2+
3+
export interface BusinessStatusTypeVO {
4+
id: number
5+
name: string
6+
deptIds: number[]
7+
status: boolean
8+
}
9+
10+
// 查询商机状态类型列表
11+
export const getBusinessStatusTypePage = async (params) => {
12+
return await request.get({ url: `/crm/business-status-type/page`, params })
13+
}
14+
15+
// 查询商机状态类型详情
16+
export const getBusinessStatusType = async (id: number) => {
17+
return await request.get({ url: `/crm/business-status-type/get?id=` + id })
18+
}
19+
20+
// 新增商机状态类型
21+
export const createBusinessStatusType = async (data: BusinessStatusTypeVO) => {
22+
return await request.post({ url: `/crm/business-status-type/create`, data })
23+
}
24+
25+
// 修改商机状态类型
26+
export const updateBusinessStatusType = async (data: BusinessStatusTypeVO) => {
27+
return await request.put({ url: `/crm/business-status-type/update`, data })
28+
}
29+
30+
// 删除商机状态类型
31+
export const deleteBusinessStatusType = async (id: number) => {
32+
return await request.delete({ url: `/crm/business-status-type/delete?id=` + id })
33+
}
34+
35+
// 导出商机状态类型 Excel
36+
export const exportBusinessStatusType = async (params) => {
37+
return await request.download({ url: `/crm/business-status-type/export-excel`, params })
38+
}
39+
40+
// 获取商机状态类型信息列表
41+
export const getBusinessStatusTypeList = async () => {
42+
return await request.get({ url: `/crm/business-status-type/get-simple-list` })
43+
}
44+
45+
// 根据类型ID获取商机状态信息列表
46+
export const getBusinessStatusListByTypeId = async (typeId: number) => {
47+
return await request.get({ url: `/crm/business-status-type/get-status-list?typeId=` + typeId })
48+
}
Lines changed: 281 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,281 @@
1+
<template>
2+
<Dialog :title="dialogTitle" v-model="dialogVisible">
3+
<el-form
4+
ref="formRef"
5+
:model="formData"
6+
:rules="formRules"
7+
label-width="100px"
8+
v-loading="formLoading"
9+
>
10+
<el-form-item label="商机名称" prop="name">
11+
<el-input v-model="formData.name" placeholder="请输入商机名称" />
12+
</el-form-item>
13+
<el-form-item label="客户名称" prop="customerName">
14+
<el-popover
15+
placement="bottom"
16+
:width="600"
17+
trigger="click"
18+
:teleported="false"
19+
:visible="showCustomer"
20+
:offset="10"
21+
>
22+
<template #reference>
23+
<el-input
24+
placeholder="请选择客户"
25+
@click="openCustomerSelect"
26+
v-model="formData.customerName"
27+
/>
28+
</template>
29+
<el-table :data="customerList" ref="multipleTableRef" @select="handleSelectionChange">
30+
<el-table-column width="55" label="选择" type="selection" />
31+
<el-table-column width="100" label="编号" property="id" />
32+
<el-table-column width="150" label="客户名称" property="name" />
33+
<el-table-column width="100" label="客户来源" prop="source" align="center">
34+
<template #default="scope">
35+
<dict-tag :type="DICT_TYPE.CRM_CUSTOMER_SOURCE" :value="scope.row.source" />
36+
</template>
37+
</el-table-column>
38+
<el-table-column label="客户等级" align="center" prop="level" width="120">
39+
<template #default="scope">
40+
<dict-tag :type="DICT_TYPE.CRM_CUSTOMER_LEVEL" :value="scope.row.level" />
41+
</template>
42+
</el-table-column>
43+
</el-table>
44+
<!-- 分页 -->
45+
<el-row :gutter="20">
46+
<el-col>
47+
<Pagination
48+
:total="total"
49+
v-model:page="queryParams.pageNo"
50+
v-model:limit="queryParams.pageSize"
51+
@pagination="getCustomerList"
52+
layout="sizes, prev, pager, next"
53+
/>
54+
</el-col>
55+
</el-row>
56+
<el-row :gutter="20">
57+
<el-col :span="10" :offset="13">
58+
<el-button @click="selectCustomer">确认</el-button>
59+
<el-button @click="showCustomer = false">取消</el-button>
60+
</el-col>
61+
</el-row>
62+
</el-popover>
63+
</el-form-item>
64+
<el-form-item label="商机状态类型" prop="statusTypeId">
65+
<el-select
66+
v-model="formData.statusTypeId"
67+
placeholder="请选择商机状态类型"
68+
clearable
69+
size="small"
70+
@change="changeBusinessStatusType"
71+
>
72+
<el-option
73+
v-for="item in businessStatusTypeList"
74+
:key="item.id"
75+
:label="item.name"
76+
:value="item.id"
77+
/>
78+
</el-select>
79+
</el-form-item>
80+
<el-form-item label="商机状态" prop="statusId">
81+
<el-select
82+
v-model="formData.statusId"
83+
placeholder="请选择商机状态"
84+
clearable
85+
size="small"
86+
>
87+
<el-option
88+
v-for="item in businessStatusList"
89+
:key="item.id"
90+
:label="item.name"
91+
:value="item.id"
92+
/>
93+
</el-select>
94+
</el-form-item>
95+
<el-form-item label="预计成交日期" prop="dealTime">
96+
<el-date-picker
97+
v-model="formData.dealTime"
98+
type="date"
99+
value-format="x"
100+
placeholder="选择预计成交日期"
101+
/>
102+
</el-form-item>
103+
<el-form-item label="商机金额" prop="price">
104+
<el-input v-model="formData.price" placeholder="请输入商机金额" />
105+
</el-form-item>
106+
<el-form-item label="整单折扣" prop="discountPercent">
107+
<el-input v-model="formData.discountPercent" placeholder="请输入整单折扣" />
108+
</el-form-item>
109+
<el-form-item label="产品总金额" prop="productPrice">
110+
<el-input v-model="formData.productPrice" placeholder="请输入产品总金额" />
111+
</el-form-item>
112+
<el-form-item label="备注" prop="remark">
113+
<el-input v-model="formData.remark" placeholder="请输入备注" />
114+
</el-form-item>
115+
</el-form>
116+
<template #footer>
117+
<el-button @click="submitForm" type="primary" :disabled="formLoading">确 定</el-button>
118+
<el-button @click="dialogVisible = false">取 消</el-button>
119+
</template>
120+
</Dialog>
121+
</template>
122+
<script setup lang="ts">
123+
import * as BusinessApi from '@/api/crm/business'
124+
import * as BusinessStatusTypeApi from '@/api/crm/businessStatusType'
125+
import * as CustomerApi from "@/api/crm/customer";
126+
import { DICT_TYPE } from '@/utils/dict'
127+
import {ElTable} from "element-plus";
128+
129+
const { t } = useI18n() // 国际化
130+
const message = useMessage() // 消息弹窗
131+
132+
const dialogVisible = ref(false) // 弹窗的是否展示
133+
const dialogTitle = ref('') // 弹窗的标题
134+
const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
135+
const formType = ref('') // 表单的类型:create - 新增;update - 修改
136+
const formData = ref({
137+
id: undefined,
138+
name: undefined,
139+
statusTypeId: undefined,
140+
statusId: undefined,
141+
contactNextTime: undefined,
142+
customerId: undefined,
143+
dealTime: undefined,
144+
price: undefined,
145+
discountPercent: undefined,
146+
productPrice: undefined,
147+
remark: undefined,
148+
ownerUserId: undefined,
149+
roUserIds: undefined,
150+
rwUserIds: undefined,
151+
endStatus: undefined,
152+
endRemark: undefined,
153+
contactLastTime: undefined,
154+
followUpStatus: undefined
155+
})
156+
const formRules = reactive({
157+
name: [{ required: true, message: '商机名称不能为空', trigger: 'blur' }]
158+
})
159+
const formRef = ref() // 表单 Ref
160+
const businessStatusList = ref([]) // 商机状态列表
161+
const businessStatusTypeList = ref([]) //商机状态类型列表
162+
const loading = ref(true) // 列表的加载中
163+
const total = ref(0) // 列表的总页数
164+
const customerList = ref([]) // 客户列表的数据
165+
166+
/** 打开弹窗 */
167+
const open = async (type: string, id?: number) => {
168+
dialogVisible.value = true
169+
dialogTitle.value = t('action.' + type)
170+
formType.value = type
171+
resetForm()
172+
// 修改时,设置数据
173+
if (id) {
174+
formLoading.value = true
175+
try {
176+
formData.value = await BusinessApi.getBusiness(id)
177+
} finally {
178+
formLoading.value = false
179+
}
180+
}
181+
// 加载商机状态类型列表
182+
businessStatusTypeList.value = await BusinessStatusTypeApi.getBusinessStatusTypeList()
183+
}
184+
defineExpose({ open }) // 提供 open 方法,用于打开弹窗
185+
186+
/** 提交表单 */
187+
const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
188+
const submitForm = async () => {
189+
// 校验表单
190+
if (!formRef) return
191+
const valid = await formRef.value.validate()
192+
if (!valid) return
193+
// 提交请求
194+
formLoading.value = true
195+
try {
196+
const data = formData.value as unknown as BusinessApi.BusinessVO
197+
if (formType.value === 'create') {
198+
await BusinessApi.createBusiness(data)
199+
message.success(t('common.createSuccess'))
200+
} else {
201+
await BusinessApi.updateBusiness(data)
202+
message.success(t('common.updateSuccess'))
203+
}
204+
dialogVisible.value = false
205+
// 发送操作成功的事件
206+
emit('success')
207+
} finally {
208+
formLoading.value = false
209+
}
210+
}
211+
212+
/** 重置表单 */
213+
const resetForm = () => {
214+
formData.value = {
215+
id: undefined,
216+
name: undefined,
217+
statusTypeId: undefined,
218+
statusId: undefined,
219+
contactNextTime: undefined,
220+
customerId: undefined,
221+
dealTime: undefined,
222+
price: undefined,
223+
discountPercent: undefined,
224+
productPrice: undefined,
225+
remark: undefined,
226+
ownerUserId: undefined,
227+
roUserIds: undefined,
228+
rwUserIds: undefined,
229+
endStatus: undefined,
230+
endRemark: undefined,
231+
contactLastTime: undefined,
232+
followUpStatus: undefined
233+
}
234+
formRef.value?.resetFields()
235+
}
236+
const changeBusinessStatusType = async (id) => {
237+
// 加载商机状态列表
238+
businessStatusList.value = await BusinessStatusTypeApi.getBusinessStatusListByTypeId(id)
239+
}
240+
const queryParams = reactive({
241+
pageNo: 1,
242+
pageSize: 10,
243+
name: null,
244+
mobile: null,
245+
industryId: null,
246+
level: null,
247+
source: null
248+
})
249+
// 选择客户
250+
const showCustomer = ref(false)
251+
const openCustomerSelect = () => {
252+
showCustomer.value = !showCustomer.value
253+
queryParams.pageNo = 1
254+
getCustomerList()
255+
}
256+
/** 查询客户列表 */
257+
const getCustomerList = async () => {
258+
loading.value = true
259+
try {
260+
const data = await CustomerApi.getCustomerPage(queryParams)
261+
console.log(JSON.stringify(data))
262+
customerList.value = data.list
263+
total.value = data.total
264+
} finally {
265+
loading.value = false
266+
}
267+
}
268+
const multipleTableRef = ref<InstanceType<typeof ElTable>>()
269+
const multipleSelection = ref()
270+
const handleSelectionChange = ({}, row) => {
271+
multipleSelection.value = row
272+
multipleTableRef.value!.clearSelection()
273+
multipleTableRef.value!.toggleRowSelection(row, undefined)
274+
}
275+
276+
const selectCustomer = () => {
277+
formData.value.customerId = multipleSelection.value.id
278+
formData.value.customerName = multipleSelection.value.name
279+
showCustomer.value = !showCustomer.value
280+
}
281+
</script>

0 commit comments

Comments
 (0)