Skip to content

Commit 596ad88

Browse files
YunaiVgitee-org
authored andcommitted
!308 【增加CRM:产品以及产品分类】
Merge pull request !308 from ZanGe丶/master
2 parents f0c4510 + ff6f05d commit 596ad88

File tree

8 files changed

+847
-1
lines changed

8 files changed

+847
-1
lines changed

src/api/crm/product/index.ts

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import request from '@/config/axios'
2+
3+
export interface ProductVO {
4+
id: number
5+
name: string
6+
no: string
7+
unit: string
8+
price: number
9+
status: number
10+
categoryId: number
11+
description: string
12+
ownerUserId: number
13+
}
14+
15+
// 查询产品列表
16+
export const getProductPage = async (params) => {
17+
return await request.get({ url: `/crm/product/page`, params })
18+
}
19+
20+
// 查询产品详情
21+
export const getProduct = async (id: number) => {
22+
return await request.get({ url: `/crm/product/get?id=` + id })
23+
}
24+
25+
// 新增产品
26+
export const createProduct = async (data: ProductVO) => {
27+
return await request.post({ url: `/crm/product/create`, data })
28+
}
29+
30+
// 修改产品
31+
export const updateProduct = async (data: ProductVO) => {
32+
return await request.put({ url: `/crm/product/update`, data })
33+
}
34+
35+
// 删除产品
36+
export const deleteProduct = async (id: number) => {
37+
return await request.delete({ url: `/crm/product/delete?id=` + id })
38+
}
39+
40+
// 导出产品 Excel
41+
export const exportProduct = async (params) => {
42+
return await request.download({ url: `/crm/product/export-excel`, params })
43+
}

src/api/crm/productCategory/index.ts

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import request from '@/config/axios'
2+
3+
export interface ProductCategoryVO {
4+
id: number
5+
name: string
6+
parentId: number
7+
}
8+
9+
// 查询产品分类详情
10+
export const getProductCategory = async (id: number) => {
11+
return await request.get({ url: `/crm/product-category/get?id=` + id })
12+
}
13+
14+
// 新增产品分类
15+
export const createProductCategory = async (data: ProductCategoryVO) => {
16+
return await request.post({ url: `/crm/product-category/create`, data })
17+
}
18+
19+
// 修改产品分类
20+
export const updateProductCategory = async (data: ProductCategoryVO) => {
21+
return await request.put({ url: `/crm/product-category/update`, data })
22+
}
23+
24+
// 删除产品分类
25+
export const deleteProductCategory = async (id: number) => {
26+
return await request.delete({ url: `/crm/product-category/delete?id=` + id })
27+
}
28+
29+
// 产品分类列表
30+
export const getProductCategoryList = async (params) => {
31+
return await request.get({ url: `/crm/product-category/list`, params })
32+
}

src/utils/dict.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -192,5 +192,6 @@ export enum DICT_TYPE {
192192
CRM_RETURN_TYPE = 'crm_return_type',
193193
CRM_CUSTOMER_INDUSTRY = 'crm_customer_industry',
194194
CRM_CUSTOMER_LEVEL = 'crm_customer_level',
195-
CRM_CUSTOMER_SOURCE = 'crm_customer_source'
195+
CRM_CUSTOMER_SOURCE = 'crm_customer_source',
196+
CRM_PRODUCT_STATUS = 'crm_product_status'
196197
}
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
<template>
2+
<Dialog v-model="dialogVisible" :max-height="500" :scroll="true" title="产品详情">
3+
<el-descriptions :column="1" border>
4+
<el-descriptions-item label="产品名称">
5+
{{ detailData.name }}
6+
</el-descriptions-item>
7+
<el-descriptions-item label="创建时间">
8+
{{ formatDate(detailData.createTime) }}
9+
</el-descriptions-item>
10+
<el-descriptions-item label="状态">
11+
<dict-tag :type="DICT_TYPE.CRM_PRODUCT_STATUS" :value="detailData.status" />
12+
</el-descriptions-item>
13+
<el-descriptions-item label="产品分类">
14+
{{ productCategoryList?.find((c) => c.id === detailData.categoryId)?.name }}
15+
</el-descriptions-item>
16+
<el-descriptions-item label="产品编码">
17+
{{ detailData.no }}
18+
</el-descriptions-item>
19+
<el-descriptions-item label="产品描述">
20+
{{ detailData.description }}
21+
</el-descriptions-item>
22+
<el-descriptions-item label="负责人">
23+
{{ detailData.ownerUserId }}
24+
</el-descriptions-item>
25+
<el-descriptions-item label="单位">
26+
<dict-tag :type="DICT_TYPE.PRODUCT_UNIT" :value="detailData.unit" />
27+
</el-descriptions-item>
28+
<el-descriptions-item label="价格">
29+
{{ fenToYuan(detailData.price) }}元
30+
</el-descriptions-item>
31+
</el-descriptions>
32+
</Dialog>
33+
</template>
34+
<script setup lang="ts">
35+
import { DICT_TYPE } from '@/utils/dict'
36+
import * as ProductCategoryApi from '@/api/crm/productCategory'
37+
import * as ProductApi from '@/api/crm/product'
38+
import { formatDate } from '@/utils/formatTime'
39+
import { fenToYuan } from '@/utils'
40+
import { getSimpleUserList, UserVO } from '@/api/system/user'
41+
42+
defineOptions({ name: 'CrmProductDetail' })
43+
44+
const { t } = useI18n() // 国际化
45+
46+
const dialogVisible = ref(false) // 弹窗的是否展示
47+
const detailLoading = ref(false) // 表单的加载中
48+
const detailData = ref() // 详情数据
49+
50+
/** 打开弹窗 */
51+
const open = async (data: ProductApi.ProductVO) => {
52+
dialogVisible.value = true
53+
// 设置数据
54+
detailLoading.value = true
55+
try {
56+
detailData.value = data
57+
} finally {
58+
detailLoading.value = false
59+
}
60+
}
61+
defineExpose({ open }) // 提供 open 方法,用于打开弹窗
62+
63+
const productCategoryList = ref([]) // 产品分类树
64+
const userList = ref<UserVO[]>([]) // 系统用户
65+
66+
onMounted(async () => {
67+
productCategoryList.value = await ProductCategoryApi.getProductCategoryList({})
68+
userList.value = await getSimpleUserList()
69+
})
70+
</script>

src/views/crm/product/ProductForm.vue

Lines changed: 184 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,184 @@
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="no">
14+
<el-input v-model="formData.no" placeholder="请输入产品编码" />
15+
</el-form-item>
16+
<el-form-item label="单位" prop="unit">
17+
<el-select v-model="formData.unit" class="w-1/1" placeholder="请选择单位">
18+
<el-option
19+
v-for="dict in getIntDictOptions(DICT_TYPE.PRODUCT_UNIT)"
20+
:key="dict.value"
21+
:label="dict.label"
22+
:value="dict.value"
23+
/>
24+
</el-select>
25+
</el-form-item>
26+
<el-form-item label="价格" prop="price">
27+
<el-input type="number" v-model="formData.price" placeholder="请输入价格" />
28+
</el-form-item>
29+
<el-form-item label="状态" prop="status">
30+
<el-select v-model="formData.status" placeholder="请选择状态">
31+
<el-option
32+
v-for="dict in getIntDictOptions(DICT_TYPE.CRM_PRODUCT_STATUS)"
33+
:key="dict.value"
34+
:label="dict.label"
35+
:value="dict.value"
36+
/>
37+
</el-select>
38+
</el-form-item>
39+
<el-form-item label="产品分类" prop="categoryId">
40+
<el-cascader
41+
v-model="formData.categoryId"
42+
:options="productCategoryList"
43+
:props="defaultProps"
44+
class="w-1/1"
45+
clearable
46+
placeholder="请选择产品分类"
47+
filterable
48+
/>
49+
</el-form-item>
50+
<el-form-item label="产品描述" prop="description">
51+
<el-input v-model="formData.description" placeholder="请输入产品描述" />
52+
</el-form-item>
53+
<el-form-item label="负责人" prop="ownerUserId">
54+
<el-select
55+
v-model="formData.ownerUserId"
56+
placeholder="请选择负责人"
57+
:disabled="formData.id"
58+
>
59+
<el-option
60+
v-for="user in userList"
61+
:key="user.id"
62+
:label="user.nickname"
63+
:value="user.id"
64+
/>
65+
</el-select>
66+
</el-form-item>
67+
</el-form>
68+
<template #footer>
69+
<el-button @click="submitForm" type="primary" :disabled="formLoading">确 定</el-button>
70+
<el-button @click="dialogVisible = false">取 消</el-button>
71+
</template>
72+
</Dialog>
73+
</template>
74+
<script setup lang="ts">
75+
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
76+
import * as ProductApi from '@/api/crm/product'
77+
import * as ProductCategoryApi from '@/api/crm/productCategory'
78+
import { defaultProps, handleTree } from '@/utils/tree'
79+
import { getSimpleUserList, UserVO } from '@/api/system/user'
80+
import { useUserStore } from '@/store/modules/user'
81+
82+
defineOptions({ name: 'CrmProductForm' })
83+
84+
const { t } = useI18n() // 国际化
85+
const message = useMessage() // 消息弹窗
86+
87+
const dialogVisible = ref(false) // 弹窗的是否展示
88+
const dialogTitle = ref('') // 弹窗的标题
89+
const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
90+
const formType = ref('') // 表单的类型:create - 新增;update - 修改
91+
const userId = useUserStore().getUser.id // 当前登录的编号
92+
const formData = ref({
93+
id: undefined,
94+
name: undefined,
95+
no: undefined,
96+
unit: undefined,
97+
price: undefined,
98+
status: undefined,
99+
categoryId: undefined,
100+
description: undefined,
101+
ownerUserId: undefined
102+
})
103+
const formRules = reactive({
104+
name: [{ required: true, message: '产品名称不能为空', trigger: 'blur' }],
105+
no: [{ required: true, message: '产品编码不能为空', trigger: 'blur' }],
106+
status: [{ required: true, message: '状态不能为空', trigger: 'change' }],
107+
categoryId: [{ required: true, message: '产品分类ID不能为空', trigger: 'blur' }],
108+
ownerUserId: [{ required: true, message: '负责人不能为空', trigger: 'blur' }],
109+
unit: [{ required: true, message: '单位不能为空', trigger: 'blur' }],
110+
price: [{ required: true, message: '价格不能为空', trigger: 'blur' }]
111+
})
112+
113+
const formRef = ref() // 表单 Ref
114+
115+
/** 打开弹窗 */
116+
const open = async (type: string, id?: number) => {
117+
dialogVisible.value = true
118+
dialogTitle.value = t('action.' + type)
119+
formType.value = type
120+
resetForm()
121+
formData.value.ownerUserId = userId
122+
// 修改时,设置数据
123+
if (id) {
124+
formLoading.value = true
125+
try {
126+
formData.value = await ProductApi.getProduct(id)
127+
} finally {
128+
formLoading.value = false
129+
}
130+
}
131+
}
132+
defineExpose({ open }) // 提供 open 方法,用于打开弹窗
133+
134+
/** 提交表单 */
135+
const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
136+
137+
const submitForm = async () => {
138+
// 校验表单
139+
if (!formRef) return
140+
const valid = await formRef.value.validate()
141+
if (!valid) return
142+
// 提交请求
143+
formLoading.value = true
144+
try {
145+
const data = formData.value as unknown as ProductApi.ProductVO
146+
if (formType.value === 'create') {
147+
await ProductApi.createProduct(data)
148+
message.success(t('common.createSuccess'))
149+
} else {
150+
await ProductApi.updateProduct(data)
151+
message.success(t('common.updateSuccess'))
152+
}
153+
dialogVisible.value = false
154+
// 发送操作成功的事件
155+
emit('success')
156+
} finally {
157+
formLoading.value = false
158+
}
159+
}
160+
161+
/** 重置表单 */
162+
const resetForm = () => {
163+
formData.value = {
164+
id: undefined,
165+
name: undefined,
166+
no: undefined,
167+
unit: undefined,
168+
price: undefined,
169+
status: undefined,
170+
categoryId: undefined,
171+
description: undefined,
172+
ownerUserId: undefined
173+
}
174+
formRef.value?.resetFields()
175+
}
176+
const productCategoryList = ref<any[]>([]) // 产品分类树
177+
const userList = ref<UserVO[]>([]) // 系统用户
178+
179+
onMounted(async () => {
180+
const data = await ProductCategoryApi.getProductCategoryList({})
181+
productCategoryList.value = handleTree(data, 'id', 'parentId')
182+
userList.value = await getSimpleUserList()
183+
})
184+
</script>

0 commit comments

Comments
 (0)