Merge branch 'master' of https://gitee.com/yudaocode/yudao-ui-admin-vue3
This commit is contained in:
commit
d9ed3d1a6b
57
src/api/crm/business/index.ts
Normal file
57
src/api/crm/business/index.ts
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
import request from '@/config/axios'
|
||||||
|
|
||||||
|
export interface BusinessVO {
|
||||||
|
id: number
|
||||||
|
name: string
|
||||||
|
statusTypeId: number
|
||||||
|
statusId: number
|
||||||
|
contactNextTime: Date
|
||||||
|
customerId: number
|
||||||
|
dealTime: Date
|
||||||
|
price: number
|
||||||
|
discountPercent: number
|
||||||
|
productPrice: number
|
||||||
|
remark: string
|
||||||
|
ownerUserId: number
|
||||||
|
roUserIds: string
|
||||||
|
rwUserIds: string
|
||||||
|
endStatus: number
|
||||||
|
endRemark: string
|
||||||
|
contactLastTime: Date
|
||||||
|
followUpStatus: number
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询 CRM 商机列表
|
||||||
|
export const getBusinessPage = async (params) => {
|
||||||
|
return await request.get({ url: `/crm/business/page`, params })
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询 CRM 商机列表,基于指定客户
|
||||||
|
export const getBusinessPageByCustomer = async (params) => {
|
||||||
|
return await request.get({ url: `/crm/business/page-by-customer`, params })
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询 CRM 商机详情
|
||||||
|
export const getBusiness = async (id: number) => {
|
||||||
|
return await request.get({ url: `/crm/business/get?id=` + id })
|
||||||
|
}
|
||||||
|
|
||||||
|
// 新增 CRM 商机
|
||||||
|
export const createBusiness = async (data: BusinessVO) => {
|
||||||
|
return await request.post({ url: `/crm/business/create`, data })
|
||||||
|
}
|
||||||
|
|
||||||
|
// 修改 CRM 商机
|
||||||
|
export const updateBusiness = async (data: BusinessVO) => {
|
||||||
|
return await request.put({ url: `/crm/business/update`, data })
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除 CRM 商机
|
||||||
|
export const deleteBusiness = async (id: number) => {
|
||||||
|
return await request.delete({ url: `/crm/business/delete?id=` + id })
|
||||||
|
}
|
||||||
|
|
||||||
|
// 导出 CRM 商机 Excel
|
||||||
|
export const exportBusiness = async (params) => {
|
||||||
|
return await request.download({ url: `/crm/business/export-excel`, params })
|
||||||
|
}
|
48
src/api/crm/businessStatusType/index.ts
Normal file
48
src/api/crm/businessStatusType/index.ts
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
import request from '@/config/axios'
|
||||||
|
|
||||||
|
export interface BusinessStatusTypeVO {
|
||||||
|
id: number
|
||||||
|
name: string
|
||||||
|
deptIds: number[]
|
||||||
|
status: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询商机状态类型列表
|
||||||
|
export const getBusinessStatusTypePage = async (params) => {
|
||||||
|
return await request.get({ url: `/crm/business-status-type/page`, params })
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询商机状态类型详情
|
||||||
|
export const getBusinessStatusType = async (id: number) => {
|
||||||
|
return await request.get({ url: `/crm/business-status-type/get?id=` + id })
|
||||||
|
}
|
||||||
|
|
||||||
|
// 新增商机状态类型
|
||||||
|
export const createBusinessStatusType = async (data: BusinessStatusTypeVO) => {
|
||||||
|
return await request.post({ url: `/crm/business-status-type/create`, data })
|
||||||
|
}
|
||||||
|
|
||||||
|
// 修改商机状态类型
|
||||||
|
export const updateBusinessStatusType = async (data: BusinessStatusTypeVO) => {
|
||||||
|
return await request.put({ url: `/crm/business-status-type/update`, data })
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除商机状态类型
|
||||||
|
export const deleteBusinessStatusType = async (id: number) => {
|
||||||
|
return await request.delete({ url: `/crm/business-status-type/delete?id=` + id })
|
||||||
|
}
|
||||||
|
|
||||||
|
// 导出商机状态类型 Excel
|
||||||
|
export const exportBusinessStatusType = async (params) => {
|
||||||
|
return await request.download({ url: `/crm/business-status-type/export-excel`, params })
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取商机状态类型信息列表
|
||||||
|
export const getBusinessStatusTypeList = async () => {
|
||||||
|
return await request.get({ url: `/crm/business-status-type/get-simple-list` })
|
||||||
|
}
|
||||||
|
|
||||||
|
// 根据类型ID获取商机状态信息列表
|
||||||
|
export const getBusinessStatusListByTypeId = async (typeId: number) => {
|
||||||
|
return await request.get({ url: `/crm/business-status-type/get-status-list?typeId=` + typeId })
|
||||||
|
}
|
@ -1,10 +1,3 @@
|
|||||||
/*
|
|
||||||
* @Author: zyna
|
|
||||||
* @Date: 2023-11-05 13:34:41
|
|
||||||
* @LastEditTime: 2023-11-11 16:20:19
|
|
||||||
* @FilePath: \yudao-ui-admin-vue3\src\api\crm\contact\index.ts
|
|
||||||
* @Description:
|
|
||||||
*/
|
|
||||||
import request from '@/config/axios'
|
import request from '@/config/axios'
|
||||||
|
|
||||||
export interface ContactVO {
|
export interface ContactVO {
|
||||||
@ -22,44 +15,53 @@ export interface ContactVO {
|
|||||||
id: number
|
id: number
|
||||||
parentId: number
|
parentId: number
|
||||||
qq: number
|
qq: number
|
||||||
webchat: string
|
wechat: string
|
||||||
sex: number
|
sex: number
|
||||||
policyMakers: boolean
|
master: boolean
|
||||||
creatorName: string
|
creatorName: string
|
||||||
updateTime?: Date
|
updateTime?: Date
|
||||||
createTime?: Date
|
createTime?: Date
|
||||||
customerName: string
|
customerName: string
|
||||||
|
areaName: string
|
||||||
|
ownerUserName: string
|
||||||
}
|
}
|
||||||
|
|
||||||
// 查询crm联系人列表
|
// 查询 CRM 联系人列表
|
||||||
export const getContactPage = async (params) => {
|
export const getContactPage = async (params) => {
|
||||||
return await request.get({ url: `/crm/contact/page`, params })
|
return await request.get({ url: `/crm/contact/page`, params })
|
||||||
}
|
}
|
||||||
|
|
||||||
// 查询crm联系人详情
|
// 查询 CRM 联系人列表,基于指定客户
|
||||||
|
export const getContactPageByCustomer = async (params: any) => {
|
||||||
|
return await request.get({ url: `/crm/contact/page-by-customer`, params })
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询 CRM 联系人详情
|
||||||
export const getContact = async (id: number) => {
|
export const getContact = async (id: number) => {
|
||||||
return await request.get({ url: `/crm/contact/get?id=` + id })
|
return await request.get({ url: `/crm/contact/get?id=` + id })
|
||||||
}
|
}
|
||||||
|
|
||||||
// 新增crm联系人
|
// 新增 CRM 联系人
|
||||||
export const createContact = async (data: ContactVO) => {
|
export const createContact = async (data: ContactVO) => {
|
||||||
return await request.post({ url: `/crm/contact/create`, data })
|
return await request.post({ url: `/crm/contact/create`, data })
|
||||||
}
|
}
|
||||||
|
|
||||||
// 修改crm联系人
|
// 修改 CRM 联系人
|
||||||
export const updateContact = async (data: ContactVO) => {
|
export const updateContact = async (data: ContactVO) => {
|
||||||
return await request.put({ url: `/crm/contact/update`, data })
|
return await request.put({ url: `/crm/contact/update`, data })
|
||||||
}
|
}
|
||||||
|
|
||||||
// 删除crm联系人
|
// 删除 CRM 联系人
|
||||||
export const deleteContact = async (id: number) => {
|
export const deleteContact = async (id: number) => {
|
||||||
return await request.delete({ url: `/crm/contact/delete?id=` + id })
|
return await request.delete({ url: `/crm/contact/delete?id=` + id })
|
||||||
}
|
}
|
||||||
|
|
||||||
// 导出crm联系人 Excel
|
// 导出 CRM 联系人 Excel
|
||||||
export const exportContact = async (params) => {
|
export const exportContact = async (params) => {
|
||||||
return await request.download({ url: `/crm/contact/export-excel`, params })
|
return await request.download({ url: `/crm/contact/export-excel`, params })
|
||||||
}
|
}
|
||||||
export const simpleAlllist = async () => {
|
|
||||||
return await request.get({ url: `/crm/contact/simpleAlllist` })
|
// 获得 CRM 联系人列表(精简)
|
||||||
|
export const getSimpleContactList = async () => {
|
||||||
|
return await request.get({ url: `/crm/contact/simple-all-list` })
|
||||||
}
|
}
|
||||||
|
@ -22,32 +22,37 @@ export interface ContractVO {
|
|||||||
remark: string
|
remark: string
|
||||||
}
|
}
|
||||||
|
|
||||||
// 查询合同列表
|
// 查询 CRM 合同列表
|
||||||
export const getContractPage = async (params) => {
|
export const getContractPage = async (params) => {
|
||||||
return await request.get({ url: `/crm/contract/page`, params })
|
return await request.get({ url: `/crm/contract/page`, params })
|
||||||
}
|
}
|
||||||
|
|
||||||
// 查询合同详情
|
// 查询 CRM 联系人列表,基于指定客户
|
||||||
|
export const getContractPageByCustomer = async (params: any) => {
|
||||||
|
return await request.get({ url: `/crm/contract/page-by-customer`, params })
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询 CRM 合同详情
|
||||||
export const getContract = async (id: number) => {
|
export const getContract = async (id: number) => {
|
||||||
return await request.get({ url: `/crm/contract/get?id=` + id })
|
return await request.get({ url: `/crm/contract/get?id=` + id })
|
||||||
}
|
}
|
||||||
|
|
||||||
// 新增合同
|
// 新增 CRM 合同
|
||||||
export const createContract = async (data: ContractVO) => {
|
export const createContract = async (data: ContractVO) => {
|
||||||
return await request.post({ url: `/crm/contract/create`, data })
|
return await request.post({ url: `/crm/contract/create`, data })
|
||||||
}
|
}
|
||||||
|
|
||||||
// 修改合同
|
// 修改 CRM 合同
|
||||||
export const updateContract = async (data: ContractVO) => {
|
export const updateContract = async (data: ContractVO) => {
|
||||||
return await request.put({ url: `/crm/contract/update`, data })
|
return await request.put({ url: `/crm/contract/update`, data })
|
||||||
}
|
}
|
||||||
|
|
||||||
// 删除合同
|
// 删除 CRM 合同
|
||||||
export const deleteContract = async (id: number) => {
|
export const deleteContract = async (id: number) => {
|
||||||
return await request.delete({ url: `/crm/contract/delete?id=` + id })
|
return await request.delete({ url: `/crm/contract/delete?id=` + id })
|
||||||
}
|
}
|
||||||
|
|
||||||
// 导出合同 Excel
|
// 导出 CRM 合同 Excel
|
||||||
export const exportContract = async (params) => {
|
export const exportContract = async (params) => {
|
||||||
return await request.download({ url: `/crm/contract/export-excel`, params })
|
return await request.download({ url: `/crm/contract/export-excel`, params })
|
||||||
}
|
}
|
||||||
|
@ -62,3 +62,8 @@ export const deleteCustomer = async (id: number) => {
|
|||||||
export const exportCustomer = async (params) => {
|
export const exportCustomer = async (params) => {
|
||||||
return await request.download({ url: `/crm/customer/export-excel`, params })
|
return await request.download({ url: `/crm/customer/export-excel`, params })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 客户列表
|
||||||
|
export const queryAllList = async () => {
|
||||||
|
return await request.get({ url: `/crm/customer/query-all-list` })
|
||||||
|
}
|
||||||
|
@ -9,6 +9,20 @@ export interface CustomerLimitConfigVO {
|
|||||||
dealCountEnabled?: boolean
|
dealCountEnabled?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 客户限制配置类型
|
||||||
|
*/
|
||||||
|
export enum LimitConfType {
|
||||||
|
/**
|
||||||
|
* 拥有客户数限制
|
||||||
|
*/
|
||||||
|
CUSTOMER_QUANTITY_LIMIT = 1,
|
||||||
|
/**
|
||||||
|
* 锁定客户数限制
|
||||||
|
*/
|
||||||
|
CUSTOMER_LOCK_LIMIT = 2
|
||||||
|
}
|
||||||
|
|
||||||
// 查询客户限制配置列表
|
// 查询客户限制配置列表
|
||||||
export const getCustomerLimitConfigPage = async (params) => {
|
export const getCustomerLimitConfigPage = async (params) => {
|
||||||
return await request.get({ url: `/crm/customer-limit-config/page`, params })
|
return await request.get({ url: `/crm/customer-limit-config/page`, params })
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import request from '@/config/axios'
|
import request from '@/config/axios'
|
||||||
|
import { ConfigVO } from '@/api/infra/config'
|
||||||
|
|
||||||
export interface CustomerPoolConfigVO {
|
export interface CustomerPoolConfigVO {
|
||||||
enabled?: boolean
|
enabled?: boolean
|
||||||
@ -14,6 +15,6 @@ export const getCustomerPoolConfig = async () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 更新客户公海规则设置
|
// 更新客户公海规则设置
|
||||||
export const updateCustomerPoolConfig = async (data: ConfigVO) => {
|
export const saveCustomerPoolConfig = async (data: ConfigVO) => {
|
||||||
return await request.put({ url: `/crm/customer-pool-config/update`, data })
|
return await request.put({ url: `/crm/customer-pool-config/save`, data })
|
||||||
}
|
}
|
@ -12,36 +12,60 @@ export interface PermissionVO {
|
|||||||
createTime?: Date
|
createTime?: Date
|
||||||
}
|
}
|
||||||
|
|
||||||
// 查询团队成员列表
|
/**
|
||||||
|
* CRM 业务类型枚举
|
||||||
|
*
|
||||||
|
* @author HUIHUI
|
||||||
|
*/
|
||||||
|
export enum BizTypeEnum {
|
||||||
|
CRM_LEADS = 1, // 线索
|
||||||
|
CRM_CUSTOMER = 2, // 客户
|
||||||
|
CRM_CONTACT = 3, // 联系人
|
||||||
|
CRM_BUSINESS = 5, // 商机
|
||||||
|
CRM_CONTRACT = 6 // 合同
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CRM 数据权限级别枚举
|
||||||
|
*/
|
||||||
|
export enum PermissionLevelEnum {
|
||||||
|
OWNER = 1, // 负责人
|
||||||
|
READ = 2, // 只读
|
||||||
|
WRITE = 3 // 读写
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获得数据权限列表(查询团队成员列表)
|
||||||
export const getPermissionList = async (params) => {
|
export const getPermissionList = async (params) => {
|
||||||
return await request.get({ url: `/crm/permission/list`, params })
|
return await request.get({ url: `/crm/permission/list`, params })
|
||||||
}
|
}
|
||||||
|
|
||||||
// 新增团队成员
|
// 创建数据权限(新增团队成员)
|
||||||
export const createPermission = async (data: PermissionVO) => {
|
export const createPermission = async (data: PermissionVO) => {
|
||||||
return await request.post({ url: `/crm/permission/create`, data })
|
return await request.post({ url: `/crm/permission/create`, data })
|
||||||
}
|
}
|
||||||
|
|
||||||
// 修改团队成员权限级别
|
// 编辑数据权限(修改团队成员权限级别)
|
||||||
export const updatePermission = async (data) => {
|
export const updatePermission = async (data) => {
|
||||||
return await request.put({ url: `/crm/permission/update`, data })
|
return await request.put({ url: `/crm/permission/update`, data })
|
||||||
}
|
}
|
||||||
|
|
||||||
// 删除团队成员
|
// 删除数据权限(删除团队成员)
|
||||||
export const deletePermission = async (params) => {
|
export const deletePermissionBatch = async (params) => {
|
||||||
return await request.delete({ url: '/crm/permission/delete', params })
|
return await request.delete({ url: '/crm/permission/delete', params })
|
||||||
}
|
}
|
||||||
|
|
||||||
// 退出团队
|
// 删除自己的数据权限(退出团队)
|
||||||
export const quitTeam = async (id) => {
|
export const deleteSelfPermission = async (id) => {
|
||||||
return await request.delete({ url: '/crm/permission/quit-team?id=' + id })
|
return await request.delete({ url: '/crm/permission/quit-team?id=' + id })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO @puhui999:调整下位置
|
||||||
// 领取公海数据
|
// 领取公海数据
|
||||||
export const receive = async (data: { bizType: number; bizId: number }) => {
|
export const receive = async (data: { bizType: number; bizId: number }) => {
|
||||||
return await request.put({ url: `/crm/permission/receive`, data })
|
return await request.put({ url: `/crm/permission/receive`, data })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO @puhui999:调整下位置
|
||||||
// 数据放入公海
|
// 数据放入公海
|
||||||
export const putPool = async (data: { bizType: number; bizId: number }) => {
|
export const putPool = async (data: { bizType: number; bizId: number }) => {
|
||||||
return await request.put({ url: `/crm/permission/put-pool`, data })
|
return await request.put({ url: `/crm/permission/put-pool`, data })
|
||||||
|
@ -6,46 +6,47 @@ export interface ReceivableVO {
|
|||||||
planId: number
|
planId: number
|
||||||
customerId: number
|
customerId: number
|
||||||
contractId: number
|
contractId: number
|
||||||
checkStatus: number
|
auditStatus: number
|
||||||
processInstanceId: number
|
processInstanceId: number
|
||||||
returnTime: Date
|
returnTime: Date
|
||||||
returnType: string
|
returnType: string
|
||||||
price: number
|
price: number
|
||||||
ownerUserId: number
|
ownerUserId: number
|
||||||
batchId: number
|
|
||||||
sort: number
|
sort: number
|
||||||
dataScope: number
|
|
||||||
dataScopeDeptIds: string
|
|
||||||
status: number
|
|
||||||
remark: string
|
remark: string
|
||||||
}
|
}
|
||||||
|
|
||||||
// 查询回款管理列表
|
// 查询回款列表
|
||||||
export const getReceivablePage = async (params) => {
|
export const getReceivablePage = async (params) => {
|
||||||
return await request.get({ url: `/crm/receivable/page`, params })
|
return await request.get({ url: `/crm/receivable/page`, params })
|
||||||
}
|
}
|
||||||
|
|
||||||
// 查询回款管理详情
|
// 查询回款列表
|
||||||
|
export const getReceivablePageByCustomer = async (params) => {
|
||||||
|
return await request.get({ url: `/crm/receivable/page-by-customer`, params })
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询回款详情
|
||||||
export const getReceivable = async (id: number) => {
|
export const getReceivable = async (id: number) => {
|
||||||
return await request.get({ url: `/crm/receivable/get?id=` + id })
|
return await request.get({ url: `/crm/receivable/get?id=` + id })
|
||||||
}
|
}
|
||||||
|
|
||||||
// 新增回款管理
|
// 新增回款
|
||||||
export const createReceivable = async (data: ReceivableVO) => {
|
export const createReceivable = async (data: ReceivableVO) => {
|
||||||
return await request.post({ url: `/crm/receivable/create`, data })
|
return await request.post({ url: `/crm/receivable/create`, data })
|
||||||
}
|
}
|
||||||
|
|
||||||
// 修改回款管理
|
// 修改回款
|
||||||
export const updateReceivable = async (data: ReceivableVO) => {
|
export const updateReceivable = async (data: ReceivableVO) => {
|
||||||
return await request.put({ url: `/crm/receivable/update`, data })
|
return await request.put({ url: `/crm/receivable/update`, data })
|
||||||
}
|
}
|
||||||
|
|
||||||
// 删除回款管理
|
// 删除回款
|
||||||
export const deleteReceivable = async (id: number) => {
|
export const deleteReceivable = async (id: number) => {
|
||||||
return await request.delete({ url: `/crm/receivable/delete?id=` + id })
|
return await request.delete({ url: `/crm/receivable/delete?id=` + id })
|
||||||
}
|
}
|
||||||
|
|
||||||
// 导出回款管理 Excel
|
// 导出回款 Excel
|
||||||
export const exportReceivable = async (params) => {
|
export const exportReceivable = async (params) => {
|
||||||
return await request.download({ url: `/crm/receivable/export-excel`, params })
|
return await request.download({ url: `/crm/receivable/export-excel`, params })
|
||||||
}
|
}
|
||||||
|
@ -23,6 +23,11 @@ export const getReceivablePlanPage = async (params) => {
|
|||||||
return await request.get({ url: `/crm/receivable-plan/page`, params })
|
return await request.get({ url: `/crm/receivable-plan/page`, params })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 查询回款计划列表
|
||||||
|
export const getReceivablePlanPageByCustomer = async (params) => {
|
||||||
|
return await request.get({ url: `/crm/receivable-plan/page-by-customer`, params })
|
||||||
|
}
|
||||||
|
|
||||||
// 查询回款计划详情
|
// 查询回款计划详情
|
||||||
export const getReceivablePlan = async (id: number) => {
|
export const getReceivablePlan = async (id: number) => {
|
||||||
return await request.get({ url: `/crm/receivable-plan/get?id=` + id })
|
return await request.get({ url: `/crm/receivable-plan/get?id=` + id })
|
@ -42,12 +42,16 @@ export const exportDemo03Student = async (params) => {
|
|||||||
|
|
||||||
// 获得学生课程列表
|
// 获得学生课程列表
|
||||||
export const getDemo03CourseListByStudentId = async (studentId) => {
|
export const getDemo03CourseListByStudentId = async (studentId) => {
|
||||||
return await request.get({ url: `/infra/demo03-student/demo03-course/list-by-student-id?studentId=` + studentId })
|
return await request.get({
|
||||||
|
url: `/infra/demo03-student/demo03-course/list-by-student-id?studentId=` + studentId
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// ==================== 子表(学生班级) ====================
|
// ==================== 子表(学生班级) ====================
|
||||||
|
|
||||||
// 获得学生班级
|
// 获得学生班级
|
||||||
export const getDemo03GradeByStudentId = async (studentId) => {
|
export const getDemo03GradeByStudentId = async (studentId) => {
|
||||||
return await request.get({ url: `/infra/demo03-student/demo03-grade/get-by-student-id?studentId=` + studentId })
|
return await request.get({
|
||||||
|
url: `/infra/demo03-student/demo03-grade/get-by-student-id?studentId=` + studentId
|
||||||
|
})
|
||||||
}
|
}
|
@ -42,12 +42,16 @@ export const exportDemo03Student = async (params) => {
|
|||||||
|
|
||||||
// 获得学生课程列表
|
// 获得学生课程列表
|
||||||
export const getDemo03CourseListByStudentId = async (studentId) => {
|
export const getDemo03CourseListByStudentId = async (studentId) => {
|
||||||
return await request.get({ url: `/infra/demo03-student/demo03-course/list-by-student-id?studentId=` + studentId })
|
return await request.get({
|
||||||
|
url: `/infra/demo03-student/demo03-course/list-by-student-id?studentId=` + studentId
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// ==================== 子表(学生班级) ====================
|
// ==================== 子表(学生班级) ====================
|
||||||
|
|
||||||
// 获得学生班级
|
// 获得学生班级
|
||||||
export const getDemo03GradeByStudentId = async (studentId) => {
|
export const getDemo03GradeByStudentId = async (studentId) => {
|
||||||
return await request.get({ url: `/infra/demo03-student/demo03-grade/get-by-student-id?studentId=` + studentId })
|
return await request.get({
|
||||||
|
url: `/infra/demo03-student/demo03-grade/get-by-student-id?studentId=` + studentId
|
||||||
|
})
|
||||||
}
|
}
|
@ -17,7 +17,7 @@ export interface FileClientConfig {
|
|||||||
export interface FileConfigVO {
|
export interface FileConfigVO {
|
||||||
id: number
|
id: number
|
||||||
name: string
|
name: string
|
||||||
storage: any
|
storage?: number
|
||||||
master: boolean
|
master: boolean
|
||||||
visible: boolean
|
visible: boolean
|
||||||
config: FileClientConfig
|
config: FileClientConfig
|
||||||
|
@ -7,8 +7,8 @@ export interface JobLogVO {
|
|||||||
handlerParam: string
|
handlerParam: string
|
||||||
cronExpression: string
|
cronExpression: string
|
||||||
executeIndex: string
|
executeIndex: string
|
||||||
beginTime: string
|
beginTime: Date
|
||||||
endTime: string
|
endTime: Date
|
||||||
duration: string
|
duration: string
|
||||||
status: number
|
status: number
|
||||||
createTime: string
|
createTime: string
|
||||||
|
@ -17,7 +17,7 @@ export interface ArticleVO {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 查询文章管理列表
|
// 查询文章管理列表
|
||||||
export const getArticlePage = async (params) => {
|
export const getArticlePage = async (params: any) => {
|
||||||
return await request.get({ url: `/promotion/article/page`, params })
|
return await request.get({ url: `/promotion/article/page`, params })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -26,6 +26,6 @@ export const getUserPage = (query) => {
|
|||||||
// 同步公众号粉丝
|
// 同步公众号粉丝
|
||||||
export const syncUser = (accountId) => {
|
export const syncUser = (accountId) => {
|
||||||
return request.post({
|
return request.post({
|
||||||
url: '/mp/tag/sync?accountId=' + accountId
|
url: '/mp/user/sync?accountId=' + accountId
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -14,7 +14,7 @@ export interface DeptVO {
|
|||||||
|
|
||||||
// 查询部门(精简)列表
|
// 查询部门(精简)列表
|
||||||
export const getSimpleDeptList = async (): Promise<DeptVO[]> => {
|
export const getSimpleDeptList = async (): Promise<DeptVO[]> => {
|
||||||
return await request.get({ url: '/system/dept/list-all-simple' })
|
return await request.get({ url: '/system/dept/simple-list' })
|
||||||
}
|
}
|
||||||
|
|
||||||
// 查询部门列表
|
// 查询部门列表
|
||||||
|
@ -14,8 +14,8 @@ export type DictDataVO = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 查询字典数据(精简)列表
|
// 查询字典数据(精简)列表
|
||||||
export const listSimpleDictData = () => {
|
export const getSimpleDictDataList = () => {
|
||||||
return request.get({ url: '/system/dict-data/list-all-simple' })
|
return request.get({ url: '/system/dict-data/simple-list' })
|
||||||
}
|
}
|
||||||
|
|
||||||
// 查询字典数据列表
|
// 查询字典数据列表
|
||||||
@ -45,5 +45,5 @@ export const deleteDictData = (id: number) => {
|
|||||||
|
|
||||||
// 导出字典类型数据
|
// 导出字典类型数据
|
||||||
export const exportDictData = (params) => {
|
export const exportDictData = (params) => {
|
||||||
return request.get({ url: '/system/dict-data/export', params })
|
return request.download({ url: '/system/dict-data/export', params })
|
||||||
}
|
}
|
||||||
|
@ -40,5 +40,5 @@ export const deleteDictType = (id: number) => {
|
|||||||
}
|
}
|
||||||
// 导出字典类型
|
// 导出字典类型
|
||||||
export const exportDictType = (params) => {
|
export const exportDictType = (params) => {
|
||||||
return request.get({ url: '/system/dict-type/export', params })
|
return request.download({ url: '/system/dict-type/export', params })
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,7 @@ export interface LoginLogVO {
|
|||||||
userId: number
|
userId: number
|
||||||
userType: number
|
userType: number
|
||||||
username: string
|
username: string
|
||||||
|
result: number
|
||||||
status: number
|
status: number
|
||||||
userIp: string
|
userIp: string
|
||||||
userAgent: string
|
userAgent: string
|
||||||
|
@ -37,5 +37,5 @@ export const deleteMailAccount = async (id: number) => {
|
|||||||
|
|
||||||
// 获得邮箱账号精简列表
|
// 获得邮箱账号精简列表
|
||||||
export const getSimpleMailAccountList = async () => {
|
export const getSimpleMailAccountList = async () => {
|
||||||
return request.get({ url: '/system/mail-account/list-all-simple' })
|
return request.get({ url: '/system/mail-account/simple-list' })
|
||||||
}
|
}
|
||||||
|
@ -20,7 +20,7 @@ export interface MenuVO {
|
|||||||
|
|
||||||
// 查询菜单(精简)列表
|
// 查询菜单(精简)列表
|
||||||
export const getSimpleMenusList = () => {
|
export const getSimpleMenusList = () => {
|
||||||
return request.get({ url: '/system/menu/list-all-simple' })
|
return request.get({ url: '/system/menu/simple-list' })
|
||||||
}
|
}
|
||||||
|
|
||||||
// 查询菜单列表
|
// 查询菜单列表
|
||||||
|
@ -13,6 +13,7 @@ export interface NotifyMessageVO {
|
|||||||
templateParams: string
|
templateParams: string
|
||||||
readStatus: boolean
|
readStatus: boolean
|
||||||
readTime: Date
|
readTime: Date
|
||||||
|
createTime: Date
|
||||||
}
|
}
|
||||||
|
|
||||||
// 查询站内信消息列表
|
// 查询站内信消息列表
|
||||||
|
@ -6,7 +6,7 @@ export interface NotifyTemplateVO {
|
|||||||
nickname: string
|
nickname: string
|
||||||
code: string
|
code: string
|
||||||
content: string
|
content: string
|
||||||
type: number
|
type?: number
|
||||||
params: string
|
params: string
|
||||||
status: number
|
status: number
|
||||||
remark: string
|
remark: string
|
||||||
|
@ -17,7 +17,7 @@ export const getPostPage = async (params: PageParam) => {
|
|||||||
|
|
||||||
// 获取岗位精简信息列表
|
// 获取岗位精简信息列表
|
||||||
export const getSimplePostList = async (): Promise<PostVO[]> => {
|
export const getSimplePostList = async (): Promise<PostVO[]> => {
|
||||||
return await request.get({ url: '/system/post/list-all-simple' })
|
return await request.get({ url: '/system/post/simple-list' })
|
||||||
}
|
}
|
||||||
|
|
||||||
// 查询岗位详情
|
// 查询岗位详情
|
||||||
|
@ -24,7 +24,7 @@ export const getRolePage = async (params: PageParam) => {
|
|||||||
|
|
||||||
// 查询角色(精简)列表
|
// 查询角色(精简)列表
|
||||||
export const getSimpleRoleList = async (): Promise<RoleVO[]> => {
|
export const getSimpleRoleList = async (): Promise<RoleVO[]> => {
|
||||||
return await request.get({ url: '/system/role/list-all-simple' })
|
return await request.get({ url: '/system/role/simple-list' })
|
||||||
}
|
}
|
||||||
|
|
||||||
// 查询角色详情
|
// 查询角色详情
|
||||||
|
@ -19,7 +19,7 @@ export const getSmsChannelPage = (params: PageParam) => {
|
|||||||
|
|
||||||
// 获得短信渠道精简列表
|
// 获得短信渠道精简列表
|
||||||
export function getSimpleSmsChannelList() {
|
export function getSimpleSmsChannelList() {
|
||||||
return request.get({ url: '/system/sms-channel/list-all-simple' })
|
return request.get({ url: '/system/sms-channel/simple-list' })
|
||||||
}
|
}
|
||||||
|
|
||||||
// 查询短信渠道详情
|
// 查询短信渠道详情
|
||||||
|
@ -1,15 +1,15 @@
|
|||||||
import request from '@/config/axios'
|
import request from '@/config/axios'
|
||||||
|
|
||||||
export interface SmsTemplateVO {
|
export interface SmsTemplateVO {
|
||||||
id: number | null
|
id?: number
|
||||||
type: number | null
|
type?: number
|
||||||
status: number
|
status: number
|
||||||
code: string
|
code: string
|
||||||
name: string
|
name: string
|
||||||
content: string
|
content: string
|
||||||
remark: string
|
remark: string
|
||||||
apiTemplateId: string
|
apiTemplateId: string
|
||||||
channelId: number | null
|
channelId?: number
|
||||||
channelCode?: string
|
channelCode?: string
|
||||||
params?: string[]
|
params?: string[]
|
||||||
createTime?: Date
|
createTime?: Date
|
||||||
|
@ -38,5 +38,5 @@ export const deleteTenantPackage = (id: number) => {
|
|||||||
}
|
}
|
||||||
// 获取租户套餐精简信息列表
|
// 获取租户套餐精简信息列表
|
||||||
export const getTenantPackageList = () => {
|
export const getTenantPackageList = () => {
|
||||||
return request.get({ url: '/system/tenant-package/get-simple-list' })
|
return request.get({ url: '/system/tenant-package/simple-list' })
|
||||||
}
|
}
|
||||||
|
@ -77,5 +77,5 @@ export const updateUserStatus = (id: number, status: number) => {
|
|||||||
|
|
||||||
// 获取用户精简信息列表
|
// 获取用户精简信息列表
|
||||||
export const getSimpleUserList = (): Promise<UserVO[]> => {
|
export const getSimpleUserList = (): Promise<UserVO[]> => {
|
||||||
return request.get({ url: '/system/user/list-all-simple' })
|
return request.get({ url: '/system/user/simple-list' })
|
||||||
}
|
}
|
||||||
|
@ -1,37 +1,25 @@
|
|||||||
import request from '@/config/axios'
|
import request from '@/config/axios'
|
||||||
|
|
||||||
export interface ProfileDept {
|
|
||||||
id: number
|
|
||||||
name: string
|
|
||||||
}
|
|
||||||
export interface ProfileRole {
|
|
||||||
id: number
|
|
||||||
name: string
|
|
||||||
}
|
|
||||||
export interface ProfilePost {
|
|
||||||
id: number
|
|
||||||
name: string
|
|
||||||
}
|
|
||||||
export interface SocialUser {
|
|
||||||
id: number
|
|
||||||
type: number
|
|
||||||
openid: string
|
|
||||||
token: string
|
|
||||||
rawTokenInfo: string
|
|
||||||
nickname: string
|
|
||||||
avatar: string
|
|
||||||
rawUserInfo: string
|
|
||||||
code: string
|
|
||||||
state: string
|
|
||||||
}
|
|
||||||
export interface ProfileVO {
|
export interface ProfileVO {
|
||||||
id: number
|
id: number
|
||||||
username: string
|
username: string
|
||||||
nickname: string
|
nickname: string
|
||||||
dept: ProfileDept
|
dept: {
|
||||||
roles: ProfileRole[]
|
id: number
|
||||||
posts: ProfilePost[]
|
name: string
|
||||||
socialUsers: SocialUser[]
|
}
|
||||||
|
roles: {
|
||||||
|
id: number
|
||||||
|
name: string
|
||||||
|
}[]
|
||||||
|
posts: {
|
||||||
|
id: number
|
||||||
|
name: string
|
||||||
|
}[]
|
||||||
|
socialUsers: {
|
||||||
|
type: number
|
||||||
|
openid: string
|
||||||
|
}[]
|
||||||
email: string
|
email: string
|
||||||
mobile: string
|
mobile: string
|
||||||
sex: number
|
sex: number
|
||||||
|
198
src/components/AppLinkInput/AppLinkSelectDialog.vue
Normal file
198
src/components/AppLinkInput/AppLinkSelectDialog.vue
Normal file
@ -0,0 +1,198 @@
|
|||||||
|
<template>
|
||||||
|
<Dialog v-model="dialogVisible" title="选择链接" width="65%">
|
||||||
|
<div class="h-500px flex gap-8px">
|
||||||
|
<!-- 左侧分组列表 -->
|
||||||
|
<el-scrollbar wrap-class="h-full" ref="groupScrollbar" view-class="flex flex-col">
|
||||||
|
<el-button
|
||||||
|
v-for="(group, groupIndex) in APP_LINK_GROUP_LIST"
|
||||||
|
:key="groupIndex"
|
||||||
|
:class="[
|
||||||
|
'm-r-16px m-l-0px! justify-start! w-90px',
|
||||||
|
{ active: activeGroup === group.name }
|
||||||
|
]"
|
||||||
|
ref="groupBtnRefs"
|
||||||
|
:text="activeGroup !== group.name"
|
||||||
|
:type="activeGroup === group.name ? 'primary' : 'default'"
|
||||||
|
@click="handleGroupSelected(group.name)"
|
||||||
|
>
|
||||||
|
{{ group.name }}
|
||||||
|
</el-button>
|
||||||
|
</el-scrollbar>
|
||||||
|
<!-- 右侧链接列表 -->
|
||||||
|
<el-scrollbar class="h-full flex-1" @scroll="handleScroll" ref="linkScrollbar">
|
||||||
|
<div v-for="(group, groupIndex) in APP_LINK_GROUP_LIST" :key="groupIndex">
|
||||||
|
<!-- 分组标题 -->
|
||||||
|
<div class="font-bold" ref="groupTitleRefs">{{ group.name }}</div>
|
||||||
|
<!-- 链接列表 -->
|
||||||
|
<el-tooltip
|
||||||
|
v-for="(appLink, appLinkIndex) in group.links"
|
||||||
|
:key="appLinkIndex"
|
||||||
|
:content="appLink.path"
|
||||||
|
placement="bottom"
|
||||||
|
>
|
||||||
|
<el-button
|
||||||
|
class="m-b-8px m-r-8px m-l-0px!"
|
||||||
|
:type="isSameLink(appLink.path, activeAppLink) ? 'primary' : 'default'"
|
||||||
|
@click="handleAppLinkSelected(appLink)"
|
||||||
|
>
|
||||||
|
{{ appLink.name }}
|
||||||
|
</el-button>
|
||||||
|
</el-tooltip>
|
||||||
|
</div>
|
||||||
|
</el-scrollbar>
|
||||||
|
</div>
|
||||||
|
<!-- 底部对话框操作按钮 -->
|
||||||
|
<template #footer>
|
||||||
|
<el-button type="primary" @click="handleSubmit">确 定</el-button>
|
||||||
|
<el-button @click="dialogVisible = false">取 消</el-button>
|
||||||
|
</template>
|
||||||
|
</Dialog>
|
||||||
|
<Dialog v-model="detailSelectDialog.visible" title="" width="50%">
|
||||||
|
<el-form class="min-h-200px">
|
||||||
|
<el-form-item
|
||||||
|
label="选择分类"
|
||||||
|
v-if="detailSelectDialog.type === APP_LINK_TYPE_ENUM.PRODUCT_CATEGORY_LIST"
|
||||||
|
>
|
||||||
|
<ProductCategorySelect
|
||||||
|
v-model="detailSelectDialog.id"
|
||||||
|
:parent-id="0"
|
||||||
|
@update:model-value="handleProductCategorySelected"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</Dialog>
|
||||||
|
</template>
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { APP_LINK_GROUP_LIST, APP_LINK_TYPE_ENUM } from './data'
|
||||||
|
import { ButtonInstance, ScrollbarInstance } from 'element-plus'
|
||||||
|
import { split } from 'lodash-es'
|
||||||
|
import ProductCategorySelect from '@/views/mall/product/category/components/ProductCategorySelect.vue'
|
||||||
|
import { getUrlNumberValue } from '@/utils'
|
||||||
|
|
||||||
|
// APP 链接选择弹框
|
||||||
|
defineOptions({ name: 'AppLinkSelectDialog' })
|
||||||
|
// 选中的分组,默认选中第一个
|
||||||
|
const activeGroup = ref(APP_LINK_GROUP_LIST[0].name)
|
||||||
|
// 选中的 APP 链接
|
||||||
|
const activeAppLink = ref('')
|
||||||
|
|
||||||
|
/** 打开弹窗 */
|
||||||
|
const dialogVisible = ref(false)
|
||||||
|
const open = (link: string) => {
|
||||||
|
activeAppLink.value = link
|
||||||
|
dialogVisible.value = true
|
||||||
|
|
||||||
|
// 滚动到当前的链接
|
||||||
|
const group = APP_LINK_GROUP_LIST.find((group) =>
|
||||||
|
group.links.some((linkItem) => isSameLink(linkItem.path, link))
|
||||||
|
)
|
||||||
|
if (group) {
|
||||||
|
// 使用 nextTick 的原因:可能 Dom 还没生成,导致滚动失败
|
||||||
|
nextTick(() => handleGroupSelected(group.name))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
defineExpose({ open })
|
||||||
|
|
||||||
|
// 处理 APP 链接选中
|
||||||
|
const handleAppLinkSelected = (appLink: any) => {
|
||||||
|
if (!isSameLink(appLink.path, activeAppLink.value)) {
|
||||||
|
activeAppLink.value = appLink.path
|
||||||
|
}
|
||||||
|
switch (appLink.type) {
|
||||||
|
case APP_LINK_TYPE_ENUM.PRODUCT_CATEGORY_LIST:
|
||||||
|
detailSelectDialog.value.visible = true
|
||||||
|
detailSelectDialog.value.type = appLink.type
|
||||||
|
// 返显
|
||||||
|
detailSelectDialog.value.id =
|
||||||
|
getUrlNumberValue('id', 'http://127.0.0.1' + activeAppLink.value) || undefined
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理绑定值更新
|
||||||
|
const emit = defineEmits<{
|
||||||
|
change: [link: string]
|
||||||
|
}>()
|
||||||
|
const handleSubmit = () => {
|
||||||
|
dialogVisible.value = false
|
||||||
|
emit('change', activeAppLink.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 分组标题引用列表
|
||||||
|
const groupTitleRefs = ref<HTMLInputElement[]>([])
|
||||||
|
/**
|
||||||
|
* 处理右侧链接列表滚动
|
||||||
|
* @param scrollTop 滚动条的位置
|
||||||
|
*/
|
||||||
|
const handleScroll = ({ scrollTop }: { scrollTop: number }) => {
|
||||||
|
const titleEl = groupTitleRefs.value.find((titleEl) => {
|
||||||
|
// 获取标题的位置信息
|
||||||
|
const { offsetHeight, offsetTop } = titleEl
|
||||||
|
// 判断标题是否在可视范围内
|
||||||
|
return scrollTop >= offsetTop && scrollTop < offsetTop + offsetHeight
|
||||||
|
})
|
||||||
|
// 只需处理一次
|
||||||
|
if (titleEl && activeGroup.value !== titleEl.textContent) {
|
||||||
|
activeGroup.value = titleEl.textContent || ''
|
||||||
|
// 同步左侧的滚动条位置
|
||||||
|
scrollToGroupBtn(activeGroup.value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 右侧滚动条
|
||||||
|
const linkScrollbar = ref<ScrollbarInstance>()
|
||||||
|
// 处理分组选中
|
||||||
|
const handleGroupSelected = (group: string) => {
|
||||||
|
activeGroup.value = group
|
||||||
|
const titleRef = groupTitleRefs.value.find((item) => item.textContent === group)
|
||||||
|
if (titleRef) {
|
||||||
|
// 滚动分组标题
|
||||||
|
linkScrollbar.value?.setScrollTop(titleRef.offsetTop)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 分组滚动条
|
||||||
|
const groupScrollbar = ref<ScrollbarInstance>()
|
||||||
|
// 分组引用列表
|
||||||
|
const groupBtnRefs = ref<ButtonInstance[]>([])
|
||||||
|
// 自动滚动分组按钮,确保分组按钮保持在可视区域内
|
||||||
|
const scrollToGroupBtn = (group: string) => {
|
||||||
|
const groupBtn = groupBtnRefs.value
|
||||||
|
.map((btn) => btn['ref'])
|
||||||
|
.find((ref) => ref.textContent === group)
|
||||||
|
if (groupBtn) {
|
||||||
|
groupScrollbar.value?.setScrollTop(groupBtn.offsetTop)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 是否为相同的链接(不比较参数,只比较链接)
|
||||||
|
const isSameLink = (link1: string, link2: string) => {
|
||||||
|
return split(link1, '?', 1)[0] === split(link2, '?', 1)[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
// 详情选择对话框
|
||||||
|
const detailSelectDialog = ref<{
|
||||||
|
visible: boolean
|
||||||
|
id?: number
|
||||||
|
type?: APP_LINK_TYPE_ENUM
|
||||||
|
}>({
|
||||||
|
visible: false,
|
||||||
|
id: undefined,
|
||||||
|
type: undefined
|
||||||
|
})
|
||||||
|
// 处理详情选择
|
||||||
|
const handleProductCategorySelected = (id: number) => {
|
||||||
|
const url = new URL(activeAppLink.value, 'http://127.0.0.1')
|
||||||
|
// 修改 id 参数
|
||||||
|
url.searchParams.set('id', `${id}`)
|
||||||
|
// 排除域名
|
||||||
|
activeAppLink.value = `${url.pathname}${url.search}`
|
||||||
|
// 关闭对话框
|
||||||
|
detailSelectDialog.value.visible = false
|
||||||
|
// 重置 id
|
||||||
|
detailSelectDialog.value.id = undefined
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<style lang="scss" scoped></style>
|
246
src/components/AppLinkInput/data.ts
Normal file
246
src/components/AppLinkInput/data.ts
Normal file
@ -0,0 +1,246 @@
|
|||||||
|
// APP 链接类型(需要特殊处理,例如商品详情)
|
||||||
|
export const enum APP_LINK_TYPE_ENUM {
|
||||||
|
// 拼团活动
|
||||||
|
ACTIVITY_COMBINATION,
|
||||||
|
// 秒杀活动
|
||||||
|
ACTIVITY_SECKILL,
|
||||||
|
// 文章详情
|
||||||
|
ARTICLE_DETAIL,
|
||||||
|
// 优惠券详情
|
||||||
|
COUPON_DETAIL,
|
||||||
|
// 自定义页面详情
|
||||||
|
DIY_PAGE_DETAIL,
|
||||||
|
// 品类列表
|
||||||
|
PRODUCT_CATEGORY_LIST,
|
||||||
|
// 商品列表
|
||||||
|
PRODUCT_LIST,
|
||||||
|
// 商品详情
|
||||||
|
PRODUCT_DETAIL_NORMAL,
|
||||||
|
// 拼团商品详情
|
||||||
|
PRODUCT_DETAIL_COMBINATION,
|
||||||
|
// 积分商品详情
|
||||||
|
PRODUCT_DETAIL_POINT,
|
||||||
|
// 秒杀商品详情
|
||||||
|
PRODUCT_DETAIL_SECKILL
|
||||||
|
}
|
||||||
|
|
||||||
|
// APP 链接列表(做一下持久化?)
|
||||||
|
export const APP_LINK_GROUP_LIST = [
|
||||||
|
{
|
||||||
|
name: '商城',
|
||||||
|
links: [
|
||||||
|
{
|
||||||
|
name: '首页',
|
||||||
|
path: '/pages/index/index'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '商品分类',
|
||||||
|
path: '/pages/index/category',
|
||||||
|
type: APP_LINK_TYPE_ENUM.PRODUCT_CATEGORY_LIST
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '购物车',
|
||||||
|
path: '/pages/index/cart'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '个人中心',
|
||||||
|
path: '/pages/index/user'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '商品搜索',
|
||||||
|
path: '/pages/index/search'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '自定义页面',
|
||||||
|
path: '/pages/index/page',
|
||||||
|
type: APP_LINK_TYPE_ENUM.DIY_PAGE_DETAIL
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '客服',
|
||||||
|
path: '/pages/chat/index'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '系统设置',
|
||||||
|
path: '/pages/public/setting'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '问题反馈',
|
||||||
|
path: '/pages/public/feedback'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '常见问题',
|
||||||
|
path: '/pages/public/faq'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '商品',
|
||||||
|
links: [
|
||||||
|
{
|
||||||
|
name: '商品列表',
|
||||||
|
path: '/pages/goods/list',
|
||||||
|
type: APP_LINK_TYPE_ENUM.PRODUCT_LIST
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '商品详情',
|
||||||
|
path: '/pages/goods/index',
|
||||||
|
type: APP_LINK_TYPE_ENUM.PRODUCT_DETAIL_NORMAL
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '拼团商品详情',
|
||||||
|
path: '/pages/goods/groupon',
|
||||||
|
type: APP_LINK_TYPE_ENUM.PRODUCT_DETAIL_COMBINATION
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '秒杀商品详情',
|
||||||
|
path: '/pages/goods/seckill',
|
||||||
|
type: APP_LINK_TYPE_ENUM.PRODUCT_DETAIL_SECKILL
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '积分商品详情',
|
||||||
|
path: '/pages/goods/score',
|
||||||
|
type: APP_LINK_TYPE_ENUM.PRODUCT_DETAIL_POINT
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '营销活动',
|
||||||
|
links: [
|
||||||
|
{
|
||||||
|
name: '拼团订单',
|
||||||
|
path: '/pages/activity/groupon/order'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '营销商品',
|
||||||
|
path: '/pages/activity/index'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '拼团活动',
|
||||||
|
path: '/pages/activity/groupon/list',
|
||||||
|
type: APP_LINK_TYPE_ENUM.ACTIVITY_COMBINATION
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '秒杀活动',
|
||||||
|
path: '/pages/activity/seckill/list',
|
||||||
|
type: APP_LINK_TYPE_ENUM.ACTIVITY_SECKILL
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '签到中心',
|
||||||
|
path: '/pages/app/sign'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '积分商城',
|
||||||
|
path: '/pages/app/score-shop'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '优惠券中心',
|
||||||
|
path: '/pages/coupon/list'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '优惠券详情',
|
||||||
|
path: '/pages/coupon/detail',
|
||||||
|
type: APP_LINK_TYPE_ENUM.COUPON_DETAIL
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '文章详情',
|
||||||
|
path: '/pages/public/richtext',
|
||||||
|
type: APP_LINK_TYPE_ENUM.ARTICLE_DETAIL
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '分销商城',
|
||||||
|
links: [
|
||||||
|
{
|
||||||
|
name: '分销中心',
|
||||||
|
path: '/pages/commission/index'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '申请分销商',
|
||||||
|
path: '/pages/commission/apply'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '推广商品',
|
||||||
|
path: '/pages/commission/goods'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '分销订单',
|
||||||
|
path: '/pages/commission/order'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '分享记录',
|
||||||
|
path: '/pages/commission/share-log'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '我的团队',
|
||||||
|
path: '/pages/commission/team'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '支付',
|
||||||
|
links: [
|
||||||
|
{
|
||||||
|
name: '充值余额',
|
||||||
|
path: '/pages/pay/recharge'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '充值记录',
|
||||||
|
path: '/pages/pay/recharge-log'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '申请提现',
|
||||||
|
path: '/pages/pay/withdraw'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '提现记录',
|
||||||
|
path: '/pages/pay/withdraw-log'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '用户中心',
|
||||||
|
links: [
|
||||||
|
{
|
||||||
|
name: '用户信息',
|
||||||
|
path: '/pages/user/info'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '用户订单',
|
||||||
|
path: '/pages/order/list'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '售后订单',
|
||||||
|
path: '/pages/order/aftersale/list'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '商品收藏',
|
||||||
|
path: '/pages/user/goods-collect'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '浏览记录',
|
||||||
|
path: '/pages/user/goods-log'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '地址管理',
|
||||||
|
path: '/pages/user/address/list'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '发票管理',
|
||||||
|
path: '/pages/user/invoice/list'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '用户佣金',
|
||||||
|
path: '/pages/user/wallet/commission'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '用户余额',
|
||||||
|
path: '/pages/user/wallet/money'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '用户积分',
|
||||||
|
path: '/pages/user/wallet/score'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
43
src/components/AppLinkInput/index.vue
Normal file
43
src/components/AppLinkInput/index.vue
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
<template>
|
||||||
|
<el-input v-model="appLink" placeholder="输入或选择链接">
|
||||||
|
<template #append>
|
||||||
|
<el-button @click="handleOpenDialog">选择</el-button>
|
||||||
|
</template>
|
||||||
|
</el-input>
|
||||||
|
<AppLinkSelectDialog ref="dialogRef" @change="handleLinkSelected" />
|
||||||
|
</template>
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { propTypes } from '@/utils/propTypes'
|
||||||
|
|
||||||
|
// APP 链接输入框
|
||||||
|
defineOptions({ name: 'AppLinkInput' })
|
||||||
|
// 定义属性
|
||||||
|
const props = defineProps({
|
||||||
|
// 当前选中的链接
|
||||||
|
modelValue: propTypes.string.def('')
|
||||||
|
})
|
||||||
|
// 当前的链接
|
||||||
|
const appLink = ref('')
|
||||||
|
// 选择对话框
|
||||||
|
const dialogRef = ref()
|
||||||
|
// 处理打开对话框
|
||||||
|
const handleOpenDialog = () => dialogRef.value?.open(appLink.value)
|
||||||
|
// 处理 APP 链接选中
|
||||||
|
const handleLinkSelected = (link: string) => (appLink.value = link)
|
||||||
|
|
||||||
|
// getter
|
||||||
|
watch(
|
||||||
|
() => props.modelValue,
|
||||||
|
() => (appLink.value = props.modelValue),
|
||||||
|
{ immediate: true }
|
||||||
|
)
|
||||||
|
|
||||||
|
// setter
|
||||||
|
const emit = defineEmits<{
|
||||||
|
'update:modelValue': [link: string]
|
||||||
|
}>()
|
||||||
|
watch(
|
||||||
|
() => appLink,
|
||||||
|
() => emit('update:modelValue', appLink.value)
|
||||||
|
)
|
||||||
|
</script>
|
@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<el-tabs stretch>
|
<el-tabs stretch>
|
||||||
<el-tab-pane label="内容">
|
<el-tab-pane label="内容" v-if="$slots.default">
|
||||||
<slot></slot>
|
<slot></slot>
|
||||||
</el-tab-pane>
|
</el-tab-pane>
|
||||||
<el-tab-pane label="样式" lazy>
|
<el-tab-pane label="样式" lazy>
|
||||||
|
@ -103,7 +103,7 @@
|
|||||||
</el-form-item>
|
</el-form-item>
|
||||||
</template>
|
</template>
|
||||||
<el-form-item label="链接" class="m-b-8px!" label-width="50px">
|
<el-form-item label="链接" class="m-b-8px!" label-width="50px">
|
||||||
<el-input placeholder="链接" v-model="element.url" />
|
<AppLinkInput v-model="element.url" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
@ -13,7 +13,7 @@
|
|||||||
</UploadImg>
|
</UploadImg>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="链接" prop="url">
|
<el-form-item label="链接" prop="url">
|
||||||
<el-input placeholder="链接" v-model="formData.url" />
|
<AppLinkInput v-model="formData.url" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
</ComponentContainerProperty>
|
</ComponentContainerProperty>
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
<UploadImg v-model="hotArea.imgUrl" height="80px" width="80px" />
|
<UploadImg v-model="hotArea.imgUrl" height="80px" width="80px" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="链接" :prop="`list[${index}].url`">
|
<el-form-item label="链接" :prop="`list[${index}].url`">
|
||||||
<el-input v-model="hotArea.url" placeholder="请输入链接" />
|
<AppLinkInput v-model="hotArea.url" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</template>
|
</template>
|
||||||
</template>
|
</template>
|
||||||
|
@ -38,7 +38,7 @@
|
|||||||
<InputWithColor v-model="element.subtitle" v-model:color="element.subtitleColor" />
|
<InputWithColor v-model="element.subtitle" v-model:color="element.subtitleColor" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="链接" prop="url">
|
<el-form-item label="链接" prop="url">
|
||||||
<el-input v-model="element.url" />
|
<AppLinkInput v-model="element.url" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="显示角标" prop="badge.show">
|
<el-form-item label="显示角标" prop="badge.show">
|
||||||
<el-switch v-model="element.badge.show" />
|
<el-switch v-model="element.badge.show" />
|
||||||
|
@ -31,7 +31,7 @@
|
|||||||
<InputWithColor v-model="element.subtitle" v-model:color="element.subtitleColor" />
|
<InputWithColor v-model="element.subtitle" v-model:color="element.subtitleColor" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="链接" prop="url">
|
<el-form-item label="链接" prop="url">
|
||||||
<el-input v-model="element.url" />
|
<AppLinkInput v-model="element.url" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
@ -48,7 +48,7 @@
|
|||||||
<InputWithColor v-model="element.title" v-model:color="element.titleColor" />
|
<InputWithColor v-model="element.title" v-model:color="element.titleColor" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="链接" prop="url">
|
<el-form-item label="链接" prop="url">
|
||||||
<el-input v-model="element.url" />
|
<AppLinkInput v-model="element.url" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="显示角标" prop="badge.show">
|
<el-form-item label="显示角标" prop="badge.show">
|
||||||
<el-switch v-model="element.badge.show" />
|
<el-switch v-model="element.badge.show" />
|
||||||
|
@ -35,7 +35,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="w-full flex flex-col gap-8px">
|
<div class="w-full flex flex-col gap-8px">
|
||||||
<el-input v-model="element.text" placeholder="请输入公告" />
|
<el-input v-model="element.text" placeholder="请输入公告" />
|
||||||
<el-input v-model="element.url" placeholder="请输入链接" />
|
<AppLinkInput v-model="element.url" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { ComponentStyle, DiyComponent } from '@/components/DiyEditor/util'
|
import { ComponentStyle, DiyComponent } from '@/components/DiyEditor/util'
|
||||||
|
|
||||||
/** 商品卡片属性 */
|
/** 商品栏属性 */
|
||||||
export interface ProductListProperty {
|
export interface ProductListProperty {
|
||||||
// 布局类型:双列 | 三列 | 水平滑动
|
// 布局类型:双列 | 三列 | 水平滑动
|
||||||
layoutType: 'twoCol' | 'threeCol' | 'horizSwiper'
|
layoutType: 'twoCol' | 'threeCol' | 'horizSwiper'
|
||||||
|
@ -66,7 +66,7 @@
|
|||||||
import { ProductListProperty } from './config'
|
import { ProductListProperty } from './config'
|
||||||
import * as ProductSpuApi from '@/api/mall/product/spu'
|
import * as ProductSpuApi from '@/api/mall/product/spu'
|
||||||
|
|
||||||
/** 商品卡片 */
|
/** 商品栏 */
|
||||||
defineOptions({ name: 'ProductList' })
|
defineOptions({ name: 'ProductList' })
|
||||||
// 定义属性
|
// 定义属性
|
||||||
const props = defineProps<{ property: ProductListProperty }>()
|
const props = defineProps<{ property: ProductListProperty }>()
|
||||||
|
@ -88,7 +88,7 @@ import { ProductListProperty } from './config'
|
|||||||
import { usePropertyForm } from '@/components/DiyEditor/util'
|
import { usePropertyForm } from '@/components/DiyEditor/util'
|
||||||
import SpuShowcase from '@/views/mall/product/spu/components/SpuShowcase.vue'
|
import SpuShowcase from '@/views/mall/product/spu/components/SpuShowcase.vue'
|
||||||
|
|
||||||
// 商品卡片属性面板
|
// 商品栏属性面板
|
||||||
defineOptions({ name: 'ProductListProperty' })
|
defineOptions({ name: 'ProductListProperty' })
|
||||||
|
|
||||||
const props = defineProps<{ modelValue: ProductListProperty }>()
|
const props = defineProps<{ modelValue: ProductListProperty }>()
|
||||||
|
@ -0,0 +1,25 @@
|
|||||||
|
import { ComponentStyle, DiyComponent } from '@/components/DiyEditor/util'
|
||||||
|
|
||||||
|
/** 营销文章属性 */
|
||||||
|
export interface PromotionArticleProperty {
|
||||||
|
// 文章编号
|
||||||
|
id: number
|
||||||
|
// 组件样式
|
||||||
|
style: ComponentStyle
|
||||||
|
}
|
||||||
|
|
||||||
|
// 定义组件
|
||||||
|
export const component = {
|
||||||
|
id: 'PromotionArticle',
|
||||||
|
name: '营销文章',
|
||||||
|
icon: 'ph:article-medium',
|
||||||
|
property: {
|
||||||
|
style: {
|
||||||
|
bgType: 'color',
|
||||||
|
bgColor: '',
|
||||||
|
marginLeft: 8,
|
||||||
|
marginRight: 8,
|
||||||
|
marginBottom: 8
|
||||||
|
} as ComponentStyle
|
||||||
|
}
|
||||||
|
} as DiyComponent<PromotionArticleProperty>
|
@ -0,0 +1,27 @@
|
|||||||
|
<template>
|
||||||
|
<div class="min-h-30px" v-html="article.content"></div>
|
||||||
|
</template>
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { PromotionArticleProperty } from './config'
|
||||||
|
import * as ArticleApi from '@/api/mall/promotion/article/index'
|
||||||
|
|
||||||
|
/** 营销文章 */
|
||||||
|
defineOptions({ name: 'PromotionArticle' })
|
||||||
|
// 定义属性
|
||||||
|
const props = defineProps<{ property: PromotionArticleProperty }>()
|
||||||
|
// 商品列表
|
||||||
|
const article = ref<ArticleApi.ArticleVO[]>({})
|
||||||
|
watch(
|
||||||
|
() => props.property.id,
|
||||||
|
async () => {
|
||||||
|
if (props.property.id) {
|
||||||
|
article.value = await ArticleApi.getArticle(props.property.id)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
immediate: true
|
||||||
|
}
|
||||||
|
)
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss"></style>
|
@ -0,0 +1,56 @@
|
|||||||
|
<template>
|
||||||
|
<ComponentContainerProperty v-model="formData.style">
|
||||||
|
<el-form label-width="40px" :model="formData">
|
||||||
|
<el-form-item label="文章" prop="id">
|
||||||
|
<el-select
|
||||||
|
v-model="formData.id"
|
||||||
|
placeholder="请选择文章"
|
||||||
|
class="w-full"
|
||||||
|
filterable
|
||||||
|
remote
|
||||||
|
:remote-method="queryArticleList"
|
||||||
|
:loading="loading"
|
||||||
|
>
|
||||||
|
<el-option
|
||||||
|
v-for="article in articles"
|
||||||
|
:key="article.id"
|
||||||
|
:label="article.title"
|
||||||
|
:value="article.id"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</ComponentContainerProperty>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { PromotionArticleProperty } from './config'
|
||||||
|
import { usePropertyForm } from '@/components/DiyEditor/util'
|
||||||
|
import * as ArticleApi from '@/api/mall/promotion/article/index'
|
||||||
|
|
||||||
|
// 营销文章属性面板
|
||||||
|
defineOptions({ name: 'PromotionArticleProperty' })
|
||||||
|
|
||||||
|
const props = defineProps<{ modelValue: PromotionArticleProperty }>()
|
||||||
|
const emit = defineEmits(['update:modelValue'])
|
||||||
|
const { formData } = usePropertyForm(props.modelValue, emit)
|
||||||
|
// 文章列表
|
||||||
|
const articles = ref<ArticleApi.ArticleVO>([])
|
||||||
|
|
||||||
|
// 加载中
|
||||||
|
const loading = ref(false)
|
||||||
|
// 查询文章列表
|
||||||
|
const queryArticleList = async (title?: string) => {
|
||||||
|
loading.value = true
|
||||||
|
const { list } = await ArticleApi.getArticlePage({ title, pageSize: 10 })
|
||||||
|
articles.value = list
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
|
|
||||||
|
// 初始化
|
||||||
|
onMounted(() => {
|
||||||
|
queryArticleList()
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss"></style>
|
@ -0,0 +1,64 @@
|
|||||||
|
import { ComponentStyle, DiyComponent } from '@/components/DiyEditor/util'
|
||||||
|
|
||||||
|
/** 拼团属性 */
|
||||||
|
export interface PromotionCombinationProperty {
|
||||||
|
// 布局类型:单列 | 三列
|
||||||
|
layoutType: 'oneCol' | 'threeCol'
|
||||||
|
// 商品字段
|
||||||
|
fields: {
|
||||||
|
// 商品名称
|
||||||
|
name: PromotionCombinationFieldProperty
|
||||||
|
// 商品价格
|
||||||
|
price: PromotionCombinationFieldProperty
|
||||||
|
}
|
||||||
|
// 角标
|
||||||
|
badge: {
|
||||||
|
// 是否显示
|
||||||
|
show: boolean
|
||||||
|
// 角标图片
|
||||||
|
imgUrl: string
|
||||||
|
}
|
||||||
|
// 上圆角
|
||||||
|
borderRadiusTop: number
|
||||||
|
// 下圆角
|
||||||
|
borderRadiusBottom: number
|
||||||
|
// 间距
|
||||||
|
space: number
|
||||||
|
// 拼团活动编号
|
||||||
|
activityId: number
|
||||||
|
// 组件样式
|
||||||
|
style: ComponentStyle
|
||||||
|
}
|
||||||
|
// 商品字段
|
||||||
|
export interface PromotionCombinationFieldProperty {
|
||||||
|
// 是否显示
|
||||||
|
show: boolean
|
||||||
|
// 颜色
|
||||||
|
color: string
|
||||||
|
}
|
||||||
|
|
||||||
|
// 定义组件
|
||||||
|
export const component = {
|
||||||
|
id: 'PromotionCombination',
|
||||||
|
name: '拼团',
|
||||||
|
icon: 'mdi:account-group',
|
||||||
|
property: {
|
||||||
|
activityId: undefined,
|
||||||
|
layoutType: 'oneCol',
|
||||||
|
fields: {
|
||||||
|
name: { show: true, color: '#000' },
|
||||||
|
price: { show: true, color: '#ff3000' }
|
||||||
|
},
|
||||||
|
badge: { show: false, imgUrl: '' },
|
||||||
|
borderRadiusTop: 8,
|
||||||
|
borderRadiusBottom: 8,
|
||||||
|
space: 8,
|
||||||
|
style: {
|
||||||
|
bgType: 'color',
|
||||||
|
bgColor: '',
|
||||||
|
marginLeft: 8,
|
||||||
|
marginRight: 8,
|
||||||
|
marginBottom: 8
|
||||||
|
} as ComponentStyle
|
||||||
|
}
|
||||||
|
} as DiyComponent<PromotionCombinationProperty>
|
@ -0,0 +1,125 @@
|
|||||||
|
<template>
|
||||||
|
<el-scrollbar class="z-1 min-h-30px" wrap-class="w-full" ref="containerRef">
|
||||||
|
<!-- 商品网格 -->
|
||||||
|
<div
|
||||||
|
class="grid overflow-x-auto"
|
||||||
|
:style="{
|
||||||
|
gridGap: `${property.space}px`,
|
||||||
|
gridTemplateColumns,
|
||||||
|
width: scrollbarWidth
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<!-- 商品 -->
|
||||||
|
<div
|
||||||
|
class="relative box-content flex flex-row flex-wrap overflow-hidden bg-white"
|
||||||
|
:style="{
|
||||||
|
borderTopLeftRadius: `${property.borderRadiusTop}px`,
|
||||||
|
borderTopRightRadius: `${property.borderRadiusTop}px`,
|
||||||
|
borderBottomLeftRadius: `${property.borderRadiusBottom}px`,
|
||||||
|
borderBottomRightRadius: `${property.borderRadiusBottom}px`
|
||||||
|
}"
|
||||||
|
v-for="(spu, index) in spuList"
|
||||||
|
:key="index"
|
||||||
|
>
|
||||||
|
<!-- 角标 -->
|
||||||
|
<div
|
||||||
|
v-if="property.badge.show"
|
||||||
|
class="absolute left-0 top-0 z-1 items-center justify-center"
|
||||||
|
>
|
||||||
|
<el-image fit="cover" :src="property.badge.imgUrl" class="h-26px w-38px" />
|
||||||
|
</div>
|
||||||
|
<!-- 商品封面图 -->
|
||||||
|
<el-image fit="cover" :src="spu.picUrl" :style="{ width: imageSize, height: imageSize }" />
|
||||||
|
<div
|
||||||
|
:class="[
|
||||||
|
'flex flex-col gap-8px p-8px box-border',
|
||||||
|
{
|
||||||
|
'w-[calc(100%-64px)]': columns === 2,
|
||||||
|
'w-full': columns === 3
|
||||||
|
}
|
||||||
|
]"
|
||||||
|
>
|
||||||
|
<!-- 商品名称 -->
|
||||||
|
<div
|
||||||
|
v-if="property.fields.name.show"
|
||||||
|
class="truncate text-12px"
|
||||||
|
:style="{ color: property.fields.name.color }"
|
||||||
|
>
|
||||||
|
{{ spu.name }}
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<!-- 商品价格 -->
|
||||||
|
<span
|
||||||
|
v-if="property.fields.price.show"
|
||||||
|
class="text-12px"
|
||||||
|
:style="{ color: property.fields.price.color }"
|
||||||
|
>
|
||||||
|
¥{{ spu.price }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</el-scrollbar>
|
||||||
|
</template>
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { PromotionCombinationProperty } from './config'
|
||||||
|
import * as ProductSpuApi from '@/api/mall/product/spu'
|
||||||
|
import * as CombinationActivityApi from '@/api/mall/promotion/combination/combinationActivity'
|
||||||
|
|
||||||
|
/** 拼团 */
|
||||||
|
defineOptions({ name: 'PromotionCombination' })
|
||||||
|
// 定义属性
|
||||||
|
const props = defineProps<{ property: PromotionCombinationProperty }>()
|
||||||
|
// 商品列表
|
||||||
|
const spuList = ref<ProductSpuApi.Spu[]>([])
|
||||||
|
watch(
|
||||||
|
() => props.property.activityId,
|
||||||
|
async () => {
|
||||||
|
if (!props.property.activityId) return
|
||||||
|
const activity = await CombinationActivityApi.getCombinationActivity(props.property.activityId)
|
||||||
|
if (!activity?.spuId) return
|
||||||
|
spuList.value = [await ProductSpuApi.getSpu(activity.spuId)]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
immediate: true,
|
||||||
|
deep: true
|
||||||
|
}
|
||||||
|
)
|
||||||
|
// 手机宽度
|
||||||
|
const phoneWidth = ref(375)
|
||||||
|
// 容器
|
||||||
|
const containerRef = ref()
|
||||||
|
// 商品的列数
|
||||||
|
const columns = ref(2)
|
||||||
|
// 滚动条宽度
|
||||||
|
const scrollbarWidth = ref('100%')
|
||||||
|
// 商品图大小
|
||||||
|
const imageSize = ref('0')
|
||||||
|
// 商品网络列数
|
||||||
|
const gridTemplateColumns = ref('')
|
||||||
|
// 计算布局参数
|
||||||
|
watch(
|
||||||
|
() => [props.property, phoneWidth, spuList.value.length],
|
||||||
|
() => {
|
||||||
|
// 计算列数
|
||||||
|
columns.value = props.property.layoutType === 'oneCol' ? 1 : 3
|
||||||
|
// 每列的宽度为:(总宽度 - 间距 * (列数 - 1))/ 列数
|
||||||
|
const productWidth =
|
||||||
|
(phoneWidth.value - props.property.space * (columns.value - 1)) / columns.value
|
||||||
|
// 商品图布局:2列时,左右布局 3列时,上下布局
|
||||||
|
imageSize.value = columns.value === 2 ? '64px' : `${productWidth}px`
|
||||||
|
// 指定列数
|
||||||
|
gridTemplateColumns.value = `repeat(${columns.value}, auto)`
|
||||||
|
// 不滚动
|
||||||
|
scrollbarWidth.value = '100%'
|
||||||
|
},
|
||||||
|
{ immediate: true, deep: true }
|
||||||
|
)
|
||||||
|
onMounted(() => {
|
||||||
|
// 提取手机宽度
|
||||||
|
phoneWidth.value = containerRef.value?.wrapRef?.offsetWidth || 375
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss"></style>
|
@ -0,0 +1,112 @@
|
|||||||
|
<template>
|
||||||
|
<ComponentContainerProperty v-model="formData.style">
|
||||||
|
<el-form label-width="80px" :model="formData">
|
||||||
|
<el-card header="拼团活动" class="property-group" shadow="never">
|
||||||
|
<el-form-item label="拼团活动" prop="activityId">
|
||||||
|
<el-select v-model="formData.activityId">
|
||||||
|
<el-option
|
||||||
|
v-for="activity in activityList"
|
||||||
|
:key="activity.id"
|
||||||
|
:label="activity.name"
|
||||||
|
:value="activity.id"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
</el-card>
|
||||||
|
<el-card header="商品样式" class="property-group" shadow="never">
|
||||||
|
<el-form-item label="布局" prop="type">
|
||||||
|
<el-radio-group v-model="formData.layoutType">
|
||||||
|
<el-tooltip class="item" content="单列" placement="bottom">
|
||||||
|
<el-radio-button label="oneCol">
|
||||||
|
<Icon icon="fluent:text-column-one-24-filled" />
|
||||||
|
</el-radio-button>
|
||||||
|
</el-tooltip>
|
||||||
|
<el-tooltip class="item" content="三列" placement="bottom">
|
||||||
|
<el-radio-button label="threeCol">
|
||||||
|
<Icon icon="fluent:text-column-three-24-filled" />
|
||||||
|
</el-radio-button>
|
||||||
|
</el-tooltip>
|
||||||
|
</el-radio-group>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="商品名称" prop="fields.name.show">
|
||||||
|
<div class="flex gap-8px">
|
||||||
|
<ColorInput v-model="formData.fields.name.color" />
|
||||||
|
<el-checkbox v-model="formData.fields.name.show" />
|
||||||
|
</div>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="商品价格" prop="fields.price.show">
|
||||||
|
<div class="flex gap-8px">
|
||||||
|
<ColorInput v-model="formData.fields.price.color" />
|
||||||
|
<el-checkbox v-model="formData.fields.price.show" />
|
||||||
|
</div>
|
||||||
|
</el-form-item>
|
||||||
|
</el-card>
|
||||||
|
<el-card header="角标" class="property-group" shadow="never">
|
||||||
|
<el-form-item label="角标" prop="badge.show">
|
||||||
|
<el-switch v-model="formData.badge.show" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="角标" prop="badge.imgUrl" v-if="formData.badge.show">
|
||||||
|
<UploadImg v-model="formData.badge.imgUrl" height="44px" width="72px">
|
||||||
|
<template #tip> 建议尺寸:36 * 22 </template>
|
||||||
|
</UploadImg>
|
||||||
|
</el-form-item>
|
||||||
|
</el-card>
|
||||||
|
<el-card header="商品样式" class="property-group" shadow="never">
|
||||||
|
<el-form-item label="上圆角" prop="borderRadiusTop">
|
||||||
|
<el-slider
|
||||||
|
v-model="formData.borderRadiusTop"
|
||||||
|
:max="100"
|
||||||
|
:min="0"
|
||||||
|
show-input
|
||||||
|
input-size="small"
|
||||||
|
:show-input-controls="false"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="下圆角" prop="borderRadiusBottom">
|
||||||
|
<el-slider
|
||||||
|
v-model="formData.borderRadiusBottom"
|
||||||
|
:max="100"
|
||||||
|
:min="0"
|
||||||
|
show-input
|
||||||
|
input-size="small"
|
||||||
|
:show-input-controls="false"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="间隔" prop="space">
|
||||||
|
<el-slider
|
||||||
|
v-model="formData.space"
|
||||||
|
:max="100"
|
||||||
|
:min="0"
|
||||||
|
show-input
|
||||||
|
input-size="small"
|
||||||
|
:show-input-controls="false"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
</el-card>
|
||||||
|
</el-form>
|
||||||
|
</ComponentContainerProperty>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { PromotionCombinationProperty } from './config'
|
||||||
|
import { usePropertyForm } from '@/components/DiyEditor/util'
|
||||||
|
import * as CombinationActivityApi from '@/api/mall/promotion/combination/combinationActivity'
|
||||||
|
import { CommonStatusEnum } from '@/utils/constants'
|
||||||
|
|
||||||
|
// 拼团属性面板
|
||||||
|
defineOptions({ name: 'PromotionCombinationProperty' })
|
||||||
|
|
||||||
|
const props = defineProps<{ modelValue: PromotionCombinationProperty }>()
|
||||||
|
const emit = defineEmits(['update:modelValue'])
|
||||||
|
const { formData } = usePropertyForm(props.modelValue, emit)
|
||||||
|
// 活动列表
|
||||||
|
const activityList = ref<CombinationActivityApi.CombinationActivityVO>([])
|
||||||
|
onMounted(async () => {
|
||||||
|
const { list } = await CombinationActivityApi.getCombinationActivityPage({
|
||||||
|
status: CommonStatusEnum.ENABLE
|
||||||
|
})
|
||||||
|
activityList.value = list
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss"></style>
|
@ -0,0 +1,64 @@
|
|||||||
|
import { ComponentStyle, DiyComponent } from '@/components/DiyEditor/util'
|
||||||
|
|
||||||
|
/** 秒杀属性 */
|
||||||
|
export interface PromotionSeckillProperty {
|
||||||
|
// 布局类型:单列 | 三列
|
||||||
|
layoutType: 'oneCol' | 'threeCol'
|
||||||
|
// 商品字段
|
||||||
|
fields: {
|
||||||
|
// 商品名称
|
||||||
|
name: PromotionSeckillFieldProperty
|
||||||
|
// 商品价格
|
||||||
|
price: PromotionSeckillFieldProperty
|
||||||
|
}
|
||||||
|
// 角标
|
||||||
|
badge: {
|
||||||
|
// 是否显示
|
||||||
|
show: boolean
|
||||||
|
// 角标图片
|
||||||
|
imgUrl: string
|
||||||
|
}
|
||||||
|
// 上圆角
|
||||||
|
borderRadiusTop: number
|
||||||
|
// 下圆角
|
||||||
|
borderRadiusBottom: number
|
||||||
|
// 间距
|
||||||
|
space: number
|
||||||
|
// 秒杀活动编号
|
||||||
|
activityId: number
|
||||||
|
// 组件样式
|
||||||
|
style: ComponentStyle
|
||||||
|
}
|
||||||
|
// 商品字段
|
||||||
|
export interface PromotionSeckillFieldProperty {
|
||||||
|
// 是否显示
|
||||||
|
show: boolean
|
||||||
|
// 颜色
|
||||||
|
color: string
|
||||||
|
}
|
||||||
|
|
||||||
|
// 定义组件
|
||||||
|
export const component = {
|
||||||
|
id: 'PromotionSeckill',
|
||||||
|
name: '秒杀',
|
||||||
|
icon: 'mdi:calendar-time',
|
||||||
|
property: {
|
||||||
|
activityId: undefined,
|
||||||
|
layoutType: 'oneCol',
|
||||||
|
fields: {
|
||||||
|
name: { show: true, color: '#000' },
|
||||||
|
price: { show: true, color: '#ff3000' }
|
||||||
|
},
|
||||||
|
badge: { show: false, imgUrl: '' },
|
||||||
|
borderRadiusTop: 8,
|
||||||
|
borderRadiusBottom: 8,
|
||||||
|
space: 8,
|
||||||
|
style: {
|
||||||
|
bgType: 'color',
|
||||||
|
bgColor: '',
|
||||||
|
marginLeft: 8,
|
||||||
|
marginRight: 8,
|
||||||
|
marginBottom: 8
|
||||||
|
} as ComponentStyle
|
||||||
|
}
|
||||||
|
} as DiyComponent<PromotionSeckillProperty>
|
@ -0,0 +1,125 @@
|
|||||||
|
<template>
|
||||||
|
<el-scrollbar class="z-1 min-h-30px" wrap-class="w-full" ref="containerRef">
|
||||||
|
<!-- 商品网格 -->
|
||||||
|
<div
|
||||||
|
class="grid overflow-x-auto"
|
||||||
|
:style="{
|
||||||
|
gridGap: `${property.space}px`,
|
||||||
|
gridTemplateColumns,
|
||||||
|
width: scrollbarWidth
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<!-- 商品 -->
|
||||||
|
<div
|
||||||
|
class="relative box-content flex flex-row flex-wrap overflow-hidden bg-white"
|
||||||
|
:style="{
|
||||||
|
borderTopLeftRadius: `${property.borderRadiusTop}px`,
|
||||||
|
borderTopRightRadius: `${property.borderRadiusTop}px`,
|
||||||
|
borderBottomLeftRadius: `${property.borderRadiusBottom}px`,
|
||||||
|
borderBottomRightRadius: `${property.borderRadiusBottom}px`
|
||||||
|
}"
|
||||||
|
v-for="(spu, index) in spuList"
|
||||||
|
:key="index"
|
||||||
|
>
|
||||||
|
<!-- 角标 -->
|
||||||
|
<div
|
||||||
|
v-if="property.badge.show"
|
||||||
|
class="absolute left-0 top-0 z-1 items-center justify-center"
|
||||||
|
>
|
||||||
|
<el-image fit="cover" :src="property.badge.imgUrl" class="h-26px w-38px" />
|
||||||
|
</div>
|
||||||
|
<!-- 商品封面图 -->
|
||||||
|
<el-image fit="cover" :src="spu.picUrl" :style="{ width: imageSize, height: imageSize }" />
|
||||||
|
<div
|
||||||
|
:class="[
|
||||||
|
'flex flex-col gap-8px p-8px box-border',
|
||||||
|
{
|
||||||
|
'w-[calc(100%-64px)]': columns === 2,
|
||||||
|
'w-full': columns === 3
|
||||||
|
}
|
||||||
|
]"
|
||||||
|
>
|
||||||
|
<!-- 商品名称 -->
|
||||||
|
<div
|
||||||
|
v-if="property.fields.name.show"
|
||||||
|
class="truncate text-12px"
|
||||||
|
:style="{ color: property.fields.name.color }"
|
||||||
|
>
|
||||||
|
{{ spu.name }}
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<!-- 商品价格 -->
|
||||||
|
<span
|
||||||
|
v-if="property.fields.price.show"
|
||||||
|
class="text-12px"
|
||||||
|
:style="{ color: property.fields.price.color }"
|
||||||
|
>
|
||||||
|
¥{{ spu.price }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</el-scrollbar>
|
||||||
|
</template>
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { PromotionSeckillProperty } from './config'
|
||||||
|
import * as ProductSpuApi from '@/api/mall/product/spu'
|
||||||
|
import * as SeckillActivityApi from '@/api/mall/promotion/seckill/seckillActivity'
|
||||||
|
|
||||||
|
/** 秒杀 */
|
||||||
|
defineOptions({ name: 'PromotionSeckill' })
|
||||||
|
// 定义属性
|
||||||
|
const props = defineProps<{ property: PromotionSeckillProperty }>()
|
||||||
|
// 商品列表
|
||||||
|
const spuList = ref<ProductSpuApi.Spu[]>([])
|
||||||
|
watch(
|
||||||
|
() => props.property.activityId,
|
||||||
|
async () => {
|
||||||
|
if (!props.property.activityId) return
|
||||||
|
const activity = await SeckillActivityApi.getSeckillActivity(props.property.activityId)
|
||||||
|
if (!activity?.spuId) return
|
||||||
|
spuList.value = [await ProductSpuApi.getSpu(activity.spuId)]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
immediate: true,
|
||||||
|
deep: true
|
||||||
|
}
|
||||||
|
)
|
||||||
|
// 手机宽度
|
||||||
|
const phoneWidth = ref(375)
|
||||||
|
// 容器
|
||||||
|
const containerRef = ref()
|
||||||
|
// 商品的列数
|
||||||
|
const columns = ref(2)
|
||||||
|
// 滚动条宽度
|
||||||
|
const scrollbarWidth = ref('100%')
|
||||||
|
// 商品图大小
|
||||||
|
const imageSize = ref('0')
|
||||||
|
// 商品网络列数
|
||||||
|
const gridTemplateColumns = ref('')
|
||||||
|
// 计算布局参数
|
||||||
|
watch(
|
||||||
|
() => [props.property, phoneWidth, spuList.value.length],
|
||||||
|
() => {
|
||||||
|
// 计算列数
|
||||||
|
columns.value = props.property.layoutType === 'oneCol' ? 1 : 3
|
||||||
|
// 每列的宽度为:(总宽度 - 间距 * (列数 - 1))/ 列数
|
||||||
|
const productWidth =
|
||||||
|
(phoneWidth.value - props.property.space * (columns.value - 1)) / columns.value
|
||||||
|
// 商品图布局:2列时,左右布局 3列时,上下布局
|
||||||
|
imageSize.value = columns.value === 2 ? '64px' : `${productWidth}px`
|
||||||
|
// 指定列数
|
||||||
|
gridTemplateColumns.value = `repeat(${columns.value}, auto)`
|
||||||
|
// 不滚动
|
||||||
|
scrollbarWidth.value = '100%'
|
||||||
|
},
|
||||||
|
{ immediate: true, deep: true }
|
||||||
|
)
|
||||||
|
onMounted(() => {
|
||||||
|
// 提取手机宽度
|
||||||
|
phoneWidth.value = containerRef.value?.wrapRef?.offsetWidth || 375
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss"></style>
|
@ -0,0 +1,112 @@
|
|||||||
|
<template>
|
||||||
|
<ComponentContainerProperty v-model="formData.style">
|
||||||
|
<el-form label-width="80px" :model="formData">
|
||||||
|
<el-card header="秒杀活动" class="property-group" shadow="never">
|
||||||
|
<el-form-item label="秒杀活动" prop="activityId">
|
||||||
|
<el-select v-model="formData.activityId">
|
||||||
|
<el-option
|
||||||
|
v-for="activity in activityList"
|
||||||
|
:key="activity.id"
|
||||||
|
:label="activity.name"
|
||||||
|
:value="activity.id"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
</el-card>
|
||||||
|
<el-card header="商品样式" class="property-group" shadow="never">
|
||||||
|
<el-form-item label="布局" prop="type">
|
||||||
|
<el-radio-group v-model="formData.layoutType">
|
||||||
|
<el-tooltip class="item" content="单列" placement="bottom">
|
||||||
|
<el-radio-button label="oneCol">
|
||||||
|
<Icon icon="fluent:text-column-one-24-filled" />
|
||||||
|
</el-radio-button>
|
||||||
|
</el-tooltip>
|
||||||
|
<el-tooltip class="item" content="三列" placement="bottom">
|
||||||
|
<el-radio-button label="threeCol">
|
||||||
|
<Icon icon="fluent:text-column-three-24-filled" />
|
||||||
|
</el-radio-button>
|
||||||
|
</el-tooltip>
|
||||||
|
</el-radio-group>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="商品名称" prop="fields.name.show">
|
||||||
|
<div class="flex gap-8px">
|
||||||
|
<ColorInput v-model="formData.fields.name.color" />
|
||||||
|
<el-checkbox v-model="formData.fields.name.show" />
|
||||||
|
</div>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="商品价格" prop="fields.price.show">
|
||||||
|
<div class="flex gap-8px">
|
||||||
|
<ColorInput v-model="formData.fields.price.color" />
|
||||||
|
<el-checkbox v-model="formData.fields.price.show" />
|
||||||
|
</div>
|
||||||
|
</el-form-item>
|
||||||
|
</el-card>
|
||||||
|
<el-card header="角标" class="property-group" shadow="never">
|
||||||
|
<el-form-item label="角标" prop="badge.show">
|
||||||
|
<el-switch v-model="formData.badge.show" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="角标" prop="badge.imgUrl" v-if="formData.badge.show">
|
||||||
|
<UploadImg v-model="formData.badge.imgUrl" height="44px" width="72px">
|
||||||
|
<template #tip> 建议尺寸:36 * 22 </template>
|
||||||
|
</UploadImg>
|
||||||
|
</el-form-item>
|
||||||
|
</el-card>
|
||||||
|
<el-card header="商品样式" class="property-group" shadow="never">
|
||||||
|
<el-form-item label="上圆角" prop="borderRadiusTop">
|
||||||
|
<el-slider
|
||||||
|
v-model="formData.borderRadiusTop"
|
||||||
|
:max="100"
|
||||||
|
:min="0"
|
||||||
|
show-input
|
||||||
|
input-size="small"
|
||||||
|
:show-input-controls="false"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="下圆角" prop="borderRadiusBottom">
|
||||||
|
<el-slider
|
||||||
|
v-model="formData.borderRadiusBottom"
|
||||||
|
:max="100"
|
||||||
|
:min="0"
|
||||||
|
show-input
|
||||||
|
input-size="small"
|
||||||
|
:show-input-controls="false"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="间隔" prop="space">
|
||||||
|
<el-slider
|
||||||
|
v-model="formData.space"
|
||||||
|
:max="100"
|
||||||
|
:min="0"
|
||||||
|
show-input
|
||||||
|
input-size="small"
|
||||||
|
:show-input-controls="false"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
</el-card>
|
||||||
|
</el-form>
|
||||||
|
</ComponentContainerProperty>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { PromotionSeckillProperty } from './config'
|
||||||
|
import { usePropertyForm } from '@/components/DiyEditor/util'
|
||||||
|
import * as SeckillActivityApi from '@/api/mall/promotion/seckill/seckillActivity'
|
||||||
|
import { CommonStatusEnum } from '@/utils/constants'
|
||||||
|
|
||||||
|
// 秒杀属性面板
|
||||||
|
defineOptions({ name: 'PromotionSeckillProperty' })
|
||||||
|
|
||||||
|
const props = defineProps<{ modelValue: PromotionSeckillProperty }>()
|
||||||
|
const emit = defineEmits(['update:modelValue'])
|
||||||
|
const { formData } = usePropertyForm(props.modelValue, emit)
|
||||||
|
// 活动列表
|
||||||
|
const activityList = ref<SeckillActivityApi.SeckillActivityVO>([])
|
||||||
|
onMounted(async () => {
|
||||||
|
const { list } = await SeckillActivityApi.getSeckillActivityPage({
|
||||||
|
status: CommonStatusEnum.ENABLE
|
||||||
|
})
|
||||||
|
activityList.value = list
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss"></style>
|
@ -88,7 +88,7 @@
|
|||||||
<el-input v-model="element.text" placeholder="请输入文字" />
|
<el-input v-model="element.text" placeholder="请输入文字" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item prop="url" label-width="0" class="m-b-0!">
|
<el-form-item prop="url" label-width="0" class="m-b-0!">
|
||||||
<el-input v-model="element.url" placeholder="请选择链接" />
|
<AppLinkInput v-model="element.url" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -92,7 +92,7 @@
|
|||||||
<el-input v-model="formData.more.text" />
|
<el-input v-model="formData.more.text" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="跳转链接" prop="more.url">
|
<el-form-item label="跳转链接" prop="more.url">
|
||||||
<el-input v-model="formData.more.url" placeholder="请输入跳转链接" />
|
<AppLinkInput v-model="formData.more.url" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</template>
|
</template>
|
||||||
</el-form>
|
</el-form>
|
||||||
|
@ -0,0 +1,21 @@
|
|||||||
|
import { ComponentStyle, DiyComponent } from '@/components/DiyEditor/util'
|
||||||
|
|
||||||
|
/** 用户卡片属性 */
|
||||||
|
export interface UserCardProperty {
|
||||||
|
// 组件样式
|
||||||
|
style: ComponentStyle
|
||||||
|
}
|
||||||
|
|
||||||
|
// 定义组件
|
||||||
|
export const component = {
|
||||||
|
id: 'UserCard',
|
||||||
|
name: '用户卡片',
|
||||||
|
icon: 'mdi:user-card-details',
|
||||||
|
property: {
|
||||||
|
style: {
|
||||||
|
bgType: 'color',
|
||||||
|
bgColor: '',
|
||||||
|
marginBottom: 8
|
||||||
|
} as ComponentStyle
|
||||||
|
}
|
||||||
|
} as DiyComponent<UserCardProperty>
|
@ -0,0 +1,29 @@
|
|||||||
|
<template>
|
||||||
|
<div class="flex flex-col">
|
||||||
|
<div class="flex items-center justify-between p-x-18px p-y-24px">
|
||||||
|
<div class="flex flex-1 items-center gap-16px">
|
||||||
|
<el-avatar :size="60">
|
||||||
|
<Icon icon="ep:avatar" :size="60" />
|
||||||
|
</el-avatar>
|
||||||
|
<span class="text-18px font-bold">芋道源码</span>
|
||||||
|
</div>
|
||||||
|
<Icon icon="tdesign:qrcode" :size="20" />
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="flex items-center justify-between justify-between bg-white p-x-20px p-y-8px text-12px"
|
||||||
|
>
|
||||||
|
<span class="color-#ff690d">点击绑定手机号</span>
|
||||||
|
<span class="rounded-26px bg-#ff6100 p-x-8px p-y-5px color-white">去绑定</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { UserCardProperty } from './config'
|
||||||
|
|
||||||
|
/** 用户卡片 */
|
||||||
|
defineOptions({ name: 'UserCard' })
|
||||||
|
// 定义属性
|
||||||
|
defineProps<{ property: UserCardProperty }>()
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss"></style>
|
@ -0,0 +1,17 @@
|
|||||||
|
<template>
|
||||||
|
<ComponentContainerProperty v-model="formData.style" />
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { UserCardProperty } from './config'
|
||||||
|
import { usePropertyForm } from '@/components/DiyEditor/util'
|
||||||
|
|
||||||
|
// 用户卡片属性面板
|
||||||
|
defineOptions({ name: 'UserCardProperty' })
|
||||||
|
|
||||||
|
const props = defineProps<{ modelValue: UserCardProperty }>()
|
||||||
|
const emit = defineEmits(['update:modelValue'])
|
||||||
|
const { formData } = usePropertyForm(props.modelValue, emit)
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss"></style>
|
@ -0,0 +1,23 @@
|
|||||||
|
import { ComponentStyle, DiyComponent } from '@/components/DiyEditor/util'
|
||||||
|
|
||||||
|
/** 用户卡券属性 */
|
||||||
|
export interface UserCouponProperty {
|
||||||
|
// 组件样式
|
||||||
|
style: ComponentStyle
|
||||||
|
}
|
||||||
|
|
||||||
|
// 定义组件
|
||||||
|
export const component = {
|
||||||
|
id: 'UserCoupon',
|
||||||
|
name: '用户卡券',
|
||||||
|
icon: 'ep:ticket',
|
||||||
|
property: {
|
||||||
|
style: {
|
||||||
|
bgType: 'color',
|
||||||
|
bgColor: '',
|
||||||
|
marginLeft: 8,
|
||||||
|
marginRight: 8,
|
||||||
|
marginBottom: 8
|
||||||
|
} as ComponentStyle
|
||||||
|
}
|
||||||
|
} as DiyComponent<UserCouponProperty>
|
@ -0,0 +1,15 @@
|
|||||||
|
<template>
|
||||||
|
<el-image
|
||||||
|
src="https://shopro.sheepjs.com/admin/static/images/shop/decorate/couponCardStyle.png"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { UserCouponProperty } from './config'
|
||||||
|
|
||||||
|
/** 用户卡券 */
|
||||||
|
defineOptions({ name: 'UserCoupon' })
|
||||||
|
// 定义属性
|
||||||
|
defineProps<{ property: UserCouponProperty }>()
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss"></style>
|
@ -0,0 +1,17 @@
|
|||||||
|
<template>
|
||||||
|
<ComponentContainerProperty v-model="formData.style" />
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { UserCouponProperty } from './config'
|
||||||
|
import { usePropertyForm } from '@/components/DiyEditor/util'
|
||||||
|
|
||||||
|
// 用户卡券属性面板
|
||||||
|
defineOptions({ name: 'UserCouponProperty' })
|
||||||
|
|
||||||
|
const props = defineProps<{ modelValue: UserCouponProperty }>()
|
||||||
|
const emit = defineEmits(['update:modelValue'])
|
||||||
|
const { formData } = usePropertyForm(props.modelValue, emit)
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss"></style>
|
@ -0,0 +1,23 @@
|
|||||||
|
import { ComponentStyle, DiyComponent } from '@/components/DiyEditor/util'
|
||||||
|
|
||||||
|
/** 用户订单属性 */
|
||||||
|
export interface UserOrderProperty {
|
||||||
|
// 组件样式
|
||||||
|
style: ComponentStyle
|
||||||
|
}
|
||||||
|
|
||||||
|
// 定义组件
|
||||||
|
export const component = {
|
||||||
|
id: 'UserOrder',
|
||||||
|
name: '用户订单',
|
||||||
|
icon: 'ep:list',
|
||||||
|
property: {
|
||||||
|
style: {
|
||||||
|
bgType: 'color',
|
||||||
|
bgColor: '',
|
||||||
|
marginLeft: 8,
|
||||||
|
marginRight: 8,
|
||||||
|
marginBottom: 8
|
||||||
|
} as ComponentStyle
|
||||||
|
}
|
||||||
|
} as DiyComponent<UserOrderProperty>
|
@ -0,0 +1,13 @@
|
|||||||
|
<template>
|
||||||
|
<el-image src="https://shopro.sheepjs.com/admin/static/images/shop/decorate/orderCardStyle.png" />
|
||||||
|
</template>
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { UserOrderProperty } from './config'
|
||||||
|
|
||||||
|
/** 用户订单 */
|
||||||
|
defineOptions({ name: 'UserOrder' })
|
||||||
|
// 定义属性
|
||||||
|
defineProps<{ property: UserOrderProperty }>()
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss"></style>
|
@ -0,0 +1,17 @@
|
|||||||
|
<template>
|
||||||
|
<ComponentContainerProperty v-model="formData.style" />
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { UserOrderProperty } from './config'
|
||||||
|
import { usePropertyForm } from '@/components/DiyEditor/util'
|
||||||
|
|
||||||
|
// 用户订单属性面板
|
||||||
|
defineOptions({ name: 'UserOrderProperty' })
|
||||||
|
|
||||||
|
const props = defineProps<{ modelValue: UserOrderProperty }>()
|
||||||
|
const emit = defineEmits(['update:modelValue'])
|
||||||
|
const { formData } = usePropertyForm(props.modelValue, emit)
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss"></style>
|
@ -0,0 +1,23 @@
|
|||||||
|
import { ComponentStyle, DiyComponent } from '@/components/DiyEditor/util'
|
||||||
|
|
||||||
|
/** 用户资产属性 */
|
||||||
|
export interface UserWalletProperty {
|
||||||
|
// 组件样式
|
||||||
|
style: ComponentStyle
|
||||||
|
}
|
||||||
|
|
||||||
|
// 定义组件
|
||||||
|
export const component = {
|
||||||
|
id: 'UserWallet',
|
||||||
|
name: '用户资产',
|
||||||
|
icon: 'ep:wallet-filled',
|
||||||
|
property: {
|
||||||
|
style: {
|
||||||
|
bgType: 'color',
|
||||||
|
bgColor: '',
|
||||||
|
marginLeft: 8,
|
||||||
|
marginRight: 8,
|
||||||
|
marginBottom: 8
|
||||||
|
} as ComponentStyle
|
||||||
|
}
|
||||||
|
} as DiyComponent<UserWalletProperty>
|
@ -0,0 +1,15 @@
|
|||||||
|
<template>
|
||||||
|
<el-image
|
||||||
|
src="https://shopro.sheepjs.com/admin/static/images/shop/decorate/walletCardStyle.png"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { UserWalletProperty } from './config'
|
||||||
|
|
||||||
|
/** 用户资产 */
|
||||||
|
defineOptions({ name: 'UserWallet' })
|
||||||
|
// 定义属性
|
||||||
|
defineProps<{ property: UserWalletProperty }>()
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss"></style>
|
@ -0,0 +1,17 @@
|
|||||||
|
<template>
|
||||||
|
<ComponentContainerProperty v-model="formData.style" />
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { UserWalletProperty } from './config'
|
||||||
|
import { usePropertyForm } from '@/components/DiyEditor/util'
|
||||||
|
|
||||||
|
// 用户资产属性面板
|
||||||
|
defineOptions({ name: 'UserWalletProperty' })
|
||||||
|
|
||||||
|
const props = defineProps<{ modelValue: UserWalletProperty }>()
|
||||||
|
const emit = defineEmits(['update:modelValue'])
|
||||||
|
const { formData } = usePropertyForm(props.modelValue, emit)
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss"></style>
|
@ -109,13 +109,19 @@ export const PAGE_LIBS = [
|
|||||||
},
|
},
|
||||||
{ name: '商品组件', extended: true, components: ['ProductCard', 'ProductList'] },
|
{ name: '商品组件', extended: true, components: ['ProductCard', 'ProductList'] },
|
||||||
{
|
{
|
||||||
name: '会员组件',
|
name: '用户组件',
|
||||||
extended: true,
|
extended: true,
|
||||||
components: ['UserCard', 'UserOrder', 'UserWallet', 'UserCoupon']
|
components: ['UserCard', 'UserOrder', 'UserWallet', 'UserCoupon']
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: '营销组件',
|
name: '营销组件',
|
||||||
extended: true,
|
extended: true,
|
||||||
components: ['CombinationCard', 'SeckillCard', 'PointCard', 'CouponCard']
|
components: [
|
||||||
|
'PromotionCombination',
|
||||||
|
'PromotionSeckill',
|
||||||
|
'PromotionPoint',
|
||||||
|
'CouponCard',
|
||||||
|
'PromotionArticle'
|
||||||
|
]
|
||||||
}
|
}
|
||||||
] as DiyComponentLibrary[]
|
] as DiyComponentLibrary[]
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<ElDialog v-model="showSearch" :show-close="false" title="菜单搜索">
|
<ElDialog v-if="isModal" v-model="showSearch" :show-close="false" title="菜单搜索">
|
||||||
<el-select
|
<el-select
|
||||||
filterable
|
filterable
|
||||||
:reserve-keyword="false"
|
:reserve-keyword="false"
|
||||||
@ -17,11 +17,39 @@
|
|||||||
/>
|
/>
|
||||||
</el-select>
|
</el-select>
|
||||||
</ElDialog>
|
</ElDialog>
|
||||||
|
<div v-else class="custom-hover" @click.stop="showTopSearch = !showTopSearch">
|
||||||
|
<Icon icon="ep:search" />
|
||||||
|
<el-select
|
||||||
|
filterable
|
||||||
|
:reserve-keyword="false"
|
||||||
|
remote
|
||||||
|
placeholder="请输入菜单内容"
|
||||||
|
:remote-method="remoteMethod"
|
||||||
|
class="overflow-hidden transition-all-600"
|
||||||
|
:class="showTopSearch ? 'w-220px ml2' : 'w-0'"
|
||||||
|
@change="handleChange"
|
||||||
|
>
|
||||||
|
<el-option
|
||||||
|
v-for="item in options"
|
||||||
|
:key="item.value"
|
||||||
|
:label="item.label"
|
||||||
|
:value="item.value"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
defineProps({
|
||||||
|
isModal: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
const router = useRouter() // 路由对象
|
const router = useRouter() // 路由对象
|
||||||
const showSearch = ref(false) // 是否显示弹框
|
const showSearch = ref(false) // 是否显示弹框
|
||||||
|
const showTopSearch = ref(false) // 是否显示顶部搜索框
|
||||||
const value: Ref = ref('') // 用户输入的值
|
const value: Ref = ref('') // 用户输入的值
|
||||||
|
|
||||||
const routers = router.getRoutes() // 路由对象
|
const routers = router.getRoutes() // 路由对象
|
||||||
@ -50,14 +78,21 @@ function remoteMethod(data) {
|
|||||||
|
|
||||||
function handleChange(path) {
|
function handleChange(path) {
|
||||||
router.push({ path })
|
router.push({ path })
|
||||||
|
hiddenTopSearch()
|
||||||
|
}
|
||||||
|
|
||||||
|
function hiddenTopSearch() {
|
||||||
|
showTopSearch.value = false
|
||||||
}
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
window.addEventListener('keydown', listenKey)
|
window.addEventListener('keydown', listenKey)
|
||||||
|
window.addEventListener('click', hiddenTopSearch)
|
||||||
})
|
})
|
||||||
|
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
window.removeEventListener('keydown', listenKey)
|
window.removeEventListener('keydown', listenKey)
|
||||||
|
window.removeEventListener('click', hiddenTopSearch)
|
||||||
})
|
})
|
||||||
|
|
||||||
// 监听 ctrl + k
|
// 监听 ctrl + k
|
||||||
|
@ -1,17 +1,17 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="upload-box">
|
<div class="upload-box">
|
||||||
<el-upload
|
<el-upload
|
||||||
:action="updateUrl"
|
|
||||||
:id="uuid"
|
:id="uuid"
|
||||||
:class="['upload', drag ? 'no-border' : '']"
|
|
||||||
:multiple="false"
|
|
||||||
:show-file-list="false"
|
|
||||||
:headers="uploadHeaders"
|
|
||||||
:before-upload="beforeUpload"
|
|
||||||
:on-success="uploadSuccess"
|
|
||||||
:on-error="uploadError"
|
|
||||||
:drag="drag"
|
|
||||||
:accept="fileType.join(',')"
|
:accept="fileType.join(',')"
|
||||||
|
:action="updateUrl"
|
||||||
|
:before-upload="beforeUpload"
|
||||||
|
:class="['upload', drag ? 'no-border' : '']"
|
||||||
|
:drag="drag"
|
||||||
|
:headers="uploadHeaders"
|
||||||
|
:multiple="false"
|
||||||
|
:on-error="uploadError"
|
||||||
|
:on-success="uploadSuccess"
|
||||||
|
:show-file-list="false"
|
||||||
>
|
>
|
||||||
<template v-if="modelValue">
|
<template v-if="modelValue">
|
||||||
<img :src="modelValue" class="upload-image" />
|
<img :src="modelValue" class="upload-image" />
|
||||||
@ -20,11 +20,11 @@
|
|||||||
<Icon icon="ep:edit" />
|
<Icon icon="ep:edit" />
|
||||||
<span v-if="showBtnText">{{ t('action.edit') }}</span>
|
<span v-if="showBtnText">{{ t('action.edit') }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="handle-icon" @click="imgViewVisible = true">
|
<div class="handle-icon" @click="imagePreview(modelValue)">
|
||||||
<Icon icon="ep:zoom-in" />
|
<Icon icon="ep:zoom-in" />
|
||||||
<span v-if="showBtnText">{{ t('action.detail') }}</span>
|
<span v-if="showBtnText">{{ t('action.detail') }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="handle-icon" @click="deleteImg" v-if="showDelete">
|
<div v-if="showDelete" class="handle-icon" @click="deleteImg">
|
||||||
<Icon icon="ep:delete" />
|
<Icon icon="ep:delete" />
|
||||||
<span v-if="showBtnText">{{ t('action.del') }}</span>
|
<span v-if="showBtnText">{{ t('action.del') }}</span>
|
||||||
</div>
|
</div>
|
||||||
@ -42,11 +42,6 @@
|
|||||||
<div class="el-upload__tip">
|
<div class="el-upload__tip">
|
||||||
<slot name="tip"></slot>
|
<slot name="tip"></slot>
|
||||||
</div>
|
</div>
|
||||||
<el-image-viewer
|
|
||||||
v-if="imgViewVisible"
|
|
||||||
@close="imgViewVisible = false"
|
|
||||||
:url-list="[modelValue]"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -56,6 +51,7 @@ import type { UploadProps } from 'element-plus'
|
|||||||
import { generateUUID } from '@/utils'
|
import { generateUUID } from '@/utils'
|
||||||
import { propTypes } from '@/utils/propTypes'
|
import { propTypes } from '@/utils/propTypes'
|
||||||
import { getAccessToken, getTenantId } from '@/utils/auth'
|
import { getAccessToken, getTenantId } from '@/utils/auth'
|
||||||
|
import { createImageViewer } from '@/components/ImageViewer'
|
||||||
|
|
||||||
defineOptions({ name: 'UploadImg' })
|
defineOptions({ name: 'UploadImg' })
|
||||||
|
|
||||||
@ -92,7 +88,12 @@ const message = useMessage() // 消息弹窗
|
|||||||
// 生成组件唯一id
|
// 生成组件唯一id
|
||||||
const uuid = ref('id-' + generateUUID())
|
const uuid = ref('id-' + generateUUID())
|
||||||
// 查看图片
|
// 查看图片
|
||||||
const imgViewVisible = ref(false)
|
const imagePreview = (imgUrl: string) => {
|
||||||
|
createImageViewer({
|
||||||
|
zIndex: 9999999,
|
||||||
|
urlList: [imgUrl]
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
const emit = defineEmits(['update:modelValue'])
|
const emit = defineEmits(['update:modelValue'])
|
||||||
|
|
||||||
@ -130,7 +131,7 @@ const uploadError = () => {
|
|||||||
message.notifyError('图片上传失败,请您重新上传!')
|
message.notifyError('图片上传失败,请您重新上传!')
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
<style scoped lang="scss">
|
<style lang="scss" scoped>
|
||||||
.is-error {
|
.is-error {
|
||||||
.upload {
|
.upload {
|
||||||
:deep(.el-upload),
|
:deep(.el-upload),
|
||||||
|
@ -7,6 +7,7 @@ import { Screenfull } from '@/layout/components/Screenfull'
|
|||||||
import { Breadcrumb } from '@/layout/components/Breadcrumb'
|
import { Breadcrumb } from '@/layout/components/Breadcrumb'
|
||||||
import { SizeDropdown } from '@/layout/components/SizeDropdown'
|
import { SizeDropdown } from '@/layout/components/SizeDropdown'
|
||||||
import { LocaleDropdown } from '@/layout/components/LocaleDropdown'
|
import { LocaleDropdown } from '@/layout/components/LocaleDropdown'
|
||||||
|
import RouterSearch from '@/components/RouterSearch/index.vue'
|
||||||
import { useAppStore } from '@/store/modules/app'
|
import { useAppStore } from '@/store/modules/app'
|
||||||
import { useDesign } from '@/hooks/web/useDesign'
|
import { useDesign } from '@/hooks/web/useDesign'
|
||||||
|
|
||||||
@ -25,6 +26,9 @@ const hamburger = computed(() => appStore.getHamburger)
|
|||||||
// 全屏图标
|
// 全屏图标
|
||||||
const screenfull = computed(() => appStore.getScreenfull)
|
const screenfull = computed(() => appStore.getScreenfull)
|
||||||
|
|
||||||
|
// 搜索图片
|
||||||
|
const search = computed(() => appStore.search)
|
||||||
|
|
||||||
// 尺寸图标
|
// 尺寸图标
|
||||||
const size = computed(() => appStore.getSize)
|
const size = computed(() => appStore.getSize)
|
||||||
|
|
||||||
@ -61,6 +65,7 @@ export default defineComponent({
|
|||||||
{screenfull.value ? (
|
{screenfull.value ? (
|
||||||
<Screenfull class="custom-hover" color="var(--top-header-text-color)"></Screenfull>
|
<Screenfull class="custom-hover" color="var(--top-header-text-color)"></Screenfull>
|
||||||
) : undefined}
|
) : undefined}
|
||||||
|
{search.value ? <RouterSearch isModal={false} /> : undefined}
|
||||||
{size.value ? (
|
{size.value ? (
|
||||||
<SizeDropdown class="custom-hover" color="var(--top-header-text-color)"></SizeDropdown>
|
<SizeDropdown class="custom-hover" color="var(--top-header-text-color)"></SizeDropdown>
|
||||||
) : undefined}
|
) : undefined}
|
||||||
|
@ -16,6 +16,7 @@ interface AppState {
|
|||||||
uniqueOpened: boolean
|
uniqueOpened: boolean
|
||||||
hamburger: boolean
|
hamburger: boolean
|
||||||
screenfull: boolean
|
screenfull: boolean
|
||||||
|
search: boolean
|
||||||
size: boolean
|
size: boolean
|
||||||
locale: boolean
|
locale: boolean
|
||||||
message: boolean
|
message: boolean
|
||||||
@ -52,6 +53,7 @@ export const useAppStore = defineStore('app', {
|
|||||||
uniqueOpened: true, // 是否只保持一个子菜单的展开
|
uniqueOpened: true, // 是否只保持一个子菜单的展开
|
||||||
hamburger: true, // 折叠图标
|
hamburger: true, // 折叠图标
|
||||||
screenfull: true, // 全屏图标
|
screenfull: true, // 全屏图标
|
||||||
|
search: true, // 搜索图标
|
||||||
size: true, // 尺寸图标
|
size: true, // 尺寸图标
|
||||||
locale: true, // 多语言图标
|
locale: true, // 多语言图标
|
||||||
message: true, // 消息图标
|
message: true, // 消息图标
|
||||||
|
@ -4,7 +4,7 @@ import { store } from '../index'
|
|||||||
import { DictDataVO } from '@/api/system/dict/types'
|
import { DictDataVO } from '@/api/system/dict/types'
|
||||||
import { CACHE_KEY, useCache } from '@/hooks/web/useCache'
|
import { CACHE_KEY, useCache } from '@/hooks/web/useCache'
|
||||||
const { wsCache } = useCache('sessionStorage')
|
const { wsCache } = useCache('sessionStorage')
|
||||||
import { listSimpleDictData } from '@/api/system/dict/dict.data'
|
import { getSimpleDictDataList } from '@/api/system/dict/dict.data'
|
||||||
|
|
||||||
export interface DictValueType {
|
export interface DictValueType {
|
||||||
value: any
|
value: any
|
||||||
@ -45,7 +45,7 @@ export const useDictStore = defineStore('dict', {
|
|||||||
this.dictMap = dictMap
|
this.dictMap = dictMap
|
||||||
this.isSetDict = true
|
this.isSetDict = true
|
||||||
} else {
|
} else {
|
||||||
const res = await listSimpleDictData()
|
const res = await getSimpleDictDataList()
|
||||||
// 设置数据
|
// 设置数据
|
||||||
const dictDataMap = new Map<string, any>()
|
const dictDataMap = new Map<string, any>()
|
||||||
res.forEach((dictData: DictDataVO) => {
|
res.forEach((dictData: DictDataVO) => {
|
||||||
@ -75,7 +75,7 @@ export const useDictStore = defineStore('dict', {
|
|||||||
},
|
},
|
||||||
async resetDict() {
|
async resetDict() {
|
||||||
wsCache.delete(CACHE_KEY.DICT_CACHE)
|
wsCache.delete(CACHE_KEY.DICT_CACHE)
|
||||||
const res = await listSimpleDictData()
|
const res = await getSimpleDictDataList()
|
||||||
// 设置数据
|
// 设置数据
|
||||||
const dictDataMap = new Map<string, any>()
|
const dictDataMap = new Map<string, any>()
|
||||||
res.forEach((dictData: DictDataVO) => {
|
res.forEach((dictData: DictDataVO) => {
|
||||||
|
@ -20,13 +20,20 @@ export interface DictDataType {
|
|||||||
cssClass: string
|
cssClass: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface NumberDictDataType extends DictDataType {
|
||||||
|
value: number
|
||||||
|
}
|
||||||
|
|
||||||
export const getDictOptions = (dictType: string) => {
|
export const getDictOptions = (dictType: string) => {
|
||||||
return dictStore.getDictByType(dictType) || []
|
return dictStore.getDictByType(dictType) || []
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getIntDictOptions = (dictType: string): DictDataType[] => {
|
export const getIntDictOptions = (dictType: string): NumberDictDataType[] => {
|
||||||
const dictOption: DictDataType[] = []
|
// 获得通用的 DictDataType 列表
|
||||||
const dictOptions: DictDataType[] = getDictOptions(dictType)
|
const dictOptions: DictDataType[] = getDictOptions(dictType)
|
||||||
|
// 转换成 number 类型的 NumberDictDataType 类型
|
||||||
|
// why 需要特殊转换:避免 IDEA 在 v-for="dict in getIntDictOptions(...)" 时,el-option 的 key 会告警
|
||||||
|
const dictOption: NumberDictDataType[] = []
|
||||||
dictOptions.forEach((dict: DictDataType) => {
|
dictOptions.forEach((dict: DictDataType) => {
|
||||||
dictOption.push({
|
dictOption.push({
|
||||||
...dict,
|
...dict,
|
||||||
@ -190,14 +197,12 @@ export enum DICT_TYPE {
|
|||||||
PROMOTION_BANNER_POSITION = 'promotion_banner_position', // banner 定位
|
PROMOTION_BANNER_POSITION = 'promotion_banner_position', // banner 定位
|
||||||
|
|
||||||
// ========== CRM - 客户管理模块 ==========
|
// ========== CRM - 客户管理模块 ==========
|
||||||
CRM_RECEIVABLE_CHECK_STATUS = 'crm_receivable_check_status',
|
CRM_AUDIT_STATUS = 'crm_audit_status', // CRM 审批状态
|
||||||
CRM_RETURN_TYPE = 'crm_return_type',
|
CRM_BIZ_TYPE = 'crm_biz_type', // CRM 业务类型
|
||||||
|
CRM_RECEIVABLE_RETURN_TYPE = 'crm_receivable_return_type', // CRM 回款的还款方式
|
||||||
CRM_CUSTOMER_INDUSTRY = 'crm_customer_industry',
|
CRM_CUSTOMER_INDUSTRY = 'crm_customer_industry',
|
||||||
CRM_CUSTOMER_LEVEL = 'crm_customer_level',
|
CRM_CUSTOMER_LEVEL = 'crm_customer_level',
|
||||||
CRM_CUSTOMER_SOURCE = 'crm_customer_source',
|
CRM_CUSTOMER_SOURCE = 'crm_customer_source',
|
||||||
CRM_PRODUCT_STATUS = 'crm_product_status',
|
CRM_PRODUCT_STATUS = 'crm_product_status',
|
||||||
|
CRM_PERMISSION_LEVEL = 'crm_permission_level' // CRM 数据权限的级别
|
||||||
// ========== CRM - 数据权限模块 ==========
|
|
||||||
CRM_BIZ_TYPE = 'crm_biz_type', // 数据模块类型
|
|
||||||
CRM_PERMISSION_LEVEL = 'crm_permission_level' // 用户数据权限类型
|
|
||||||
}
|
}
|
||||||
|
@ -62,7 +62,7 @@ export const defaultShortcuts = [
|
|||||||
* @description format 季度 + 星期 + 几周:"YYYY-mm-dd HH:MM:SS WWW QQQQ ZZZ"
|
* @description format 季度 + 星期 + 几周:"YYYY-mm-dd HH:MM:SS WWW QQQQ ZZZ"
|
||||||
* @returns 返回拼接后的时间字符串
|
* @returns 返回拼接后的时间字符串
|
||||||
*/
|
*/
|
||||||
export function formatDate(date: dayjs.ConfigType, format?: string): string {
|
export function formatDate(date: Date, format?: string): string {
|
||||||
// 日期不存在,则返回空
|
// 日期不存在,则返回空
|
||||||
if (!date) {
|
if (!date) {
|
||||||
return ''
|
return ''
|
||||||
@ -200,9 +200,9 @@ export function formatPast2(ms) {
|
|||||||
* @param cellValue 字段值
|
* @param cellValue 字段值
|
||||||
*/
|
*/
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
export const dateFormatter = (row, column, cellValue) => {
|
export const dateFormatter = (row, column, cellValue): string => {
|
||||||
if (!cellValue) {
|
if (!cellValue) {
|
||||||
return
|
return ''
|
||||||
}
|
}
|
||||||
return formatDate(cellValue)
|
return formatDate(cellValue)
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
import { toNumber } from 'lodash-es'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param component 需要注册的组件
|
* @param component 需要注册的组件
|
||||||
@ -263,3 +265,23 @@ export const calculateRelativeRate = (value?: number, reference?: number) => {
|
|||||||
|
|
||||||
return ((100 * ((value || 0) - reference)) / reference).toFixed(0)
|
return ((100 * ((value || 0) - reference)) / reference).toFixed(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取链接的参数值
|
||||||
|
* @param key 参数键名
|
||||||
|
* @param urlStr 链接地址,默认为当前浏览器的地址
|
||||||
|
*/
|
||||||
|
export const getUrlValue = (key: string, urlStr: string = location.href): string => {
|
||||||
|
if (!urlStr || !key) return ''
|
||||||
|
const url = new URL(decodeURIComponent(urlStr))
|
||||||
|
return url.searchParams.get(key) ?? ''
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取链接的参数值(值类型)
|
||||||
|
* @param key 参数键名
|
||||||
|
* @param urlStr 链接地址,默认为当前浏览器的地址
|
||||||
|
*/
|
||||||
|
export const getUrlNumberValue = (key: string, urlStr: string = location.href): number => {
|
||||||
|
return toNumber(getUrlValue(key, urlStr))
|
||||||
|
}
|
||||||
|
@ -41,7 +41,7 @@
|
|||||||
<li class="list-group-item">
|
<li class="list-group-item">
|
||||||
<Icon class="mr-5px" icon="ep:calendar" />
|
<Icon class="mr-5px" icon="ep:calendar" />
|
||||||
{{ t('profile.user.createTime') }}
|
{{ t('profile.user.createTime') }}
|
||||||
<div class="pull-right">{{ formatDate(userInfo?.createTime) }}</div>
|
<div class="pull-right">{{ formatDate(userInfo.createTime) }}</div>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
@ -55,7 +55,7 @@ import { getUserProfile, ProfileVO } from '@/api/system/user/profile'
|
|||||||
defineOptions({ name: 'ProfileUser' })
|
defineOptions({ name: 'ProfileUser' })
|
||||||
|
|
||||||
const { t } = useI18n()
|
const { t } = useI18n()
|
||||||
const userInfo = ref<ProfileVO>()
|
const userInfo = ref({} as ProfileVO)
|
||||||
const getUserInfo = async () => {
|
const getUserInfo = async () => {
|
||||||
const users = await getUserProfile()
|
const users = await getUserProfile()
|
||||||
userInfo.value = users
|
userInfo.value = users
|
||||||
|
279
src/views/crm/business/BusinessForm.vue
Normal file
279
src/views/crm/business/BusinessForm.vue
Normal file
@ -0,0 +1,279 @@
|
|||||||
|
<template>
|
||||||
|
<Dialog :title="dialogTitle" v-model="dialogVisible">
|
||||||
|
<el-form
|
||||||
|
ref="formRef"
|
||||||
|
:model="formData"
|
||||||
|
:rules="formRules"
|
||||||
|
label-width="100px"
|
||||||
|
v-loading="formLoading"
|
||||||
|
>
|
||||||
|
<el-form-item label="商机名称" prop="name">
|
||||||
|
<el-input v-model="formData.name" placeholder="请输入商机名称" />
|
||||||
|
</el-form-item>
|
||||||
|
<!-- TODO 芋艿:客户列表的组件 -->
|
||||||
|
<el-form-item label="客户名称" prop="customerName">
|
||||||
|
<el-popover
|
||||||
|
placement="bottom"
|
||||||
|
:width="600"
|
||||||
|
trigger="click"
|
||||||
|
:teleported="false"
|
||||||
|
:visible="showCustomer"
|
||||||
|
:offset="10"
|
||||||
|
>
|
||||||
|
<template #reference>
|
||||||
|
<el-input
|
||||||
|
placeholder="请选择客户"
|
||||||
|
@click="openCustomerSelect"
|
||||||
|
v-model="formData.customerName"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
<el-table :data="customerList" ref="multipleTableRef" @select="handleSelectionChange">
|
||||||
|
<el-table-column width="55" label="选择" type="selection" />
|
||||||
|
<el-table-column width="100" label="编号" property="id" />
|
||||||
|
<el-table-column width="150" label="客户名称" property="name" />
|
||||||
|
<el-table-column width="100" label="客户来源" prop="source" align="center">
|
||||||
|
<template #default="scope">
|
||||||
|
<dict-tag :type="DICT_TYPE.CRM_CUSTOMER_SOURCE" :value="scope.row.source" />
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="客户等级" align="center" prop="level" width="120">
|
||||||
|
<template #default="scope">
|
||||||
|
<dict-tag :type="DICT_TYPE.CRM_CUSTOMER_LEVEL" :value="scope.row.level" />
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
<!-- 分页 -->
|
||||||
|
<el-row :gutter="20">
|
||||||
|
<el-col>
|
||||||
|
<Pagination
|
||||||
|
:total="total"
|
||||||
|
v-model:page="queryParams.pageNo"
|
||||||
|
v-model:limit="queryParams.pageSize"
|
||||||
|
@pagination="getCustomerList"
|
||||||
|
layout="sizes, prev, pager, next"
|
||||||
|
/>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
<el-row :gutter="20">
|
||||||
|
<el-col :span="10" :offset="13">
|
||||||
|
<el-button @click="selectCustomer">确认</el-button>
|
||||||
|
<el-button @click="showCustomer = false">取消</el-button>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
</el-popover>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="商机状态类型" prop="statusTypeId">
|
||||||
|
<el-select
|
||||||
|
v-model="formData.statusTypeId"
|
||||||
|
placeholder="请选择商机状态类型"
|
||||||
|
clearable
|
||||||
|
size="small"
|
||||||
|
@change="changeBusinessStatusType"
|
||||||
|
>
|
||||||
|
<el-option
|
||||||
|
v-for="item in businessStatusTypeList"
|
||||||
|
:key="item.id"
|
||||||
|
:label="item.name"
|
||||||
|
:value="item.id"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="商机状态" prop="statusId">
|
||||||
|
<el-select v-model="formData.statusId" placeholder="请选择商机状态" clearable size="small">
|
||||||
|
<el-option
|
||||||
|
v-for="item in businessStatusList"
|
||||||
|
:key="item.id"
|
||||||
|
:label="item.name"
|
||||||
|
:value="item.id"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="预计成交日期" prop="dealTime">
|
||||||
|
<el-date-picker
|
||||||
|
v-model="formData.dealTime"
|
||||||
|
type="date"
|
||||||
|
value-format="x"
|
||||||
|
placeholder="选择预计成交日期"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="商机金额" prop="price">
|
||||||
|
<el-input v-model="formData.price" placeholder="请输入商机金额" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="整单折扣" prop="discountPercent">
|
||||||
|
<el-input v-model="formData.discountPercent" placeholder="请输入整单折扣" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="产品总金额" prop="productPrice">
|
||||||
|
<el-input v-model="formData.productPrice" placeholder="请输入产品总金额" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="备注" prop="remark">
|
||||||
|
<el-input v-model="formData.remark" placeholder="请输入备注" />
|
||||||
|
</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 * as BusinessApi from '@/api/crm/business'
|
||||||
|
import * as BusinessStatusTypeApi from '@/api/crm/businessStatusType'
|
||||||
|
import * as CustomerApi from '@/api/crm/customer'
|
||||||
|
import { DICT_TYPE } from '@/utils/dict'
|
||||||
|
import { ElTable } from 'element-plus'
|
||||||
|
|
||||||
|
const { t } = useI18n() // 国际化
|
||||||
|
const message = useMessage() // 消息弹窗
|
||||||
|
|
||||||
|
const dialogVisible = ref(false) // 弹窗的是否展示
|
||||||
|
const dialogTitle = ref('') // 弹窗的标题
|
||||||
|
const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
|
||||||
|
const formType = ref('') // 表单的类型:create - 新增;update - 修改
|
||||||
|
const formData = ref({
|
||||||
|
id: undefined,
|
||||||
|
name: undefined,
|
||||||
|
statusTypeId: undefined,
|
||||||
|
statusId: undefined,
|
||||||
|
contactNextTime: undefined,
|
||||||
|
customerId: undefined,
|
||||||
|
dealTime: undefined,
|
||||||
|
price: undefined,
|
||||||
|
discountPercent: undefined,
|
||||||
|
productPrice: undefined,
|
||||||
|
remark: undefined,
|
||||||
|
ownerUserId: undefined,
|
||||||
|
roUserIds: undefined,
|
||||||
|
rwUserIds: undefined,
|
||||||
|
endStatus: undefined,
|
||||||
|
endRemark: undefined,
|
||||||
|
contactLastTime: undefined,
|
||||||
|
followUpStatus: undefined
|
||||||
|
})
|
||||||
|
const formRules = reactive({
|
||||||
|
name: [{ required: true, message: '商机名称不能为空', trigger: 'blur' }]
|
||||||
|
})
|
||||||
|
const formRef = ref() // 表单 Ref
|
||||||
|
const businessStatusList = ref([]) // 商机状态列表
|
||||||
|
const businessStatusTypeList = ref([]) //商机状态类型列表
|
||||||
|
const loading = ref(true) // 列表的加载中
|
||||||
|
const total = ref(0) // 列表的总页数
|
||||||
|
const customerList = 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 BusinessApi.getBusiness(id)
|
||||||
|
} finally {
|
||||||
|
formLoading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 加载商机状态类型列表
|
||||||
|
businessStatusTypeList.value = await BusinessStatusTypeApi.getBusinessStatusTypeList()
|
||||||
|
}
|
||||||
|
defineExpose({ open }) // 提供 open 方法,用于打开弹窗
|
||||||
|
|
||||||
|
/** 提交表单 */
|
||||||
|
const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
|
||||||
|
const submitForm = async () => {
|
||||||
|
// 校验表单
|
||||||
|
if (!formRef) return
|
||||||
|
const valid = await formRef.value.validate()
|
||||||
|
if (!valid) return
|
||||||
|
// 提交请求
|
||||||
|
formLoading.value = true
|
||||||
|
try {
|
||||||
|
const data = formData.value as unknown as BusinessApi.BusinessVO
|
||||||
|
if (formType.value === 'create') {
|
||||||
|
await BusinessApi.createBusiness(data)
|
||||||
|
message.success(t('common.createSuccess'))
|
||||||
|
} else {
|
||||||
|
await BusinessApi.updateBusiness(data)
|
||||||
|
message.success(t('common.updateSuccess'))
|
||||||
|
}
|
||||||
|
dialogVisible.value = false
|
||||||
|
// 发送操作成功的事件
|
||||||
|
emit('success')
|
||||||
|
} finally {
|
||||||
|
formLoading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 重置表单 */
|
||||||
|
const resetForm = () => {
|
||||||
|
formData.value = {
|
||||||
|
id: undefined,
|
||||||
|
name: undefined,
|
||||||
|
statusTypeId: undefined,
|
||||||
|
statusId: undefined,
|
||||||
|
contactNextTime: undefined,
|
||||||
|
customerId: undefined,
|
||||||
|
dealTime: undefined,
|
||||||
|
price: undefined,
|
||||||
|
discountPercent: undefined,
|
||||||
|
productPrice: undefined,
|
||||||
|
remark: undefined,
|
||||||
|
ownerUserId: undefined,
|
||||||
|
roUserIds: undefined,
|
||||||
|
rwUserIds: undefined,
|
||||||
|
endStatus: undefined,
|
||||||
|
endRemark: undefined,
|
||||||
|
contactLastTime: undefined,
|
||||||
|
followUpStatus: undefined
|
||||||
|
}
|
||||||
|
formRef.value?.resetFields()
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 加载商机状态列表 */
|
||||||
|
const changeBusinessStatusType = async (typeId: number) => {
|
||||||
|
businessStatusList.value = await BusinessStatusTypeApi.getBusinessStatusListByTypeId(typeId)
|
||||||
|
}
|
||||||
|
|
||||||
|
const queryParams = reactive({
|
||||||
|
pageNo: 1,
|
||||||
|
pageSize: 10,
|
||||||
|
name: null,
|
||||||
|
mobile: null,
|
||||||
|
industryId: null,
|
||||||
|
level: null,
|
||||||
|
source: null
|
||||||
|
})
|
||||||
|
// 选择客户
|
||||||
|
const showCustomer = ref(false)
|
||||||
|
const openCustomerSelect = () => {
|
||||||
|
showCustomer.value = !showCustomer.value
|
||||||
|
queryParams.pageNo = 1
|
||||||
|
getCustomerList()
|
||||||
|
}
|
||||||
|
/** 查询客户列表 */
|
||||||
|
const getCustomerList = async () => {
|
||||||
|
loading.value = true
|
||||||
|
try {
|
||||||
|
const data = await CustomerApi.getCustomerPage(queryParams)
|
||||||
|
console.log(JSON.stringify(data))
|
||||||
|
customerList.value = data.list
|
||||||
|
total.value = data.total
|
||||||
|
} finally {
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const multipleTableRef = ref<InstanceType<typeof ElTable>>()
|
||||||
|
const multipleSelection = ref()
|
||||||
|
const handleSelectionChange = ({}, row) => {
|
||||||
|
multipleSelection.value = row
|
||||||
|
multipleTableRef.value!.clearSelection()
|
||||||
|
multipleTableRef.value!.toggleRowSelection(row, undefined)
|
||||||
|
}
|
||||||
|
|
||||||
|
const selectCustomer = () => {
|
||||||
|
formData.value.customerId = multipleSelection.value.id
|
||||||
|
formData.value.customerName = multipleSelection.value.name
|
||||||
|
showCustomer.value = !showCustomer.value
|
||||||
|
}
|
||||||
|
</script>
|
107
src/views/crm/business/components/BusinessList.vue
Normal file
107
src/views/crm/business/components/BusinessList.vue
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
<template>
|
||||||
|
<!-- 操作栏 -->
|
||||||
|
<el-row justify="end">
|
||||||
|
<el-button @click="openForm">
|
||||||
|
<Icon class="mr-5px" icon="ep:opportunity" />
|
||||||
|
创建商机
|
||||||
|
</el-button>
|
||||||
|
</el-row>
|
||||||
|
|
||||||
|
<!-- 列表 -->
|
||||||
|
<ContentWrap class="mt-10px">
|
||||||
|
<el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true">
|
||||||
|
<el-table-column label="商机名称" fixed="left" align="center" prop="name">
|
||||||
|
<template #default="scope">
|
||||||
|
<el-link type="primary" :underline="false" @click="openDetail(scope.row.id)">
|
||||||
|
{{ scope.row.name }}
|
||||||
|
</el-link>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="商机金额" align="center" prop="price" :formatter="fenToYuanFormat" />
|
||||||
|
<el-table-column label="客户名称" align="center" prop="customerName" />
|
||||||
|
<el-table-column label="商机组" align="center" prop="statusTypeName" />
|
||||||
|
<el-table-column label="商机阶段" align="center" prop="statusName" />
|
||||||
|
</el-table>
|
||||||
|
<!-- 分页 -->
|
||||||
|
<Pagination
|
||||||
|
:total="total"
|
||||||
|
v-model:page="queryParams.pageNo"
|
||||||
|
v-model:limit="queryParams.pageSize"
|
||||||
|
@pagination="getList"
|
||||||
|
/>
|
||||||
|
</ContentWrap>
|
||||||
|
|
||||||
|
<!-- 表单弹窗:添加 -->
|
||||||
|
<BusinessForm ref="formRef" @success="getList" />
|
||||||
|
</template>
|
||||||
|
<script setup lang="ts">
|
||||||
|
import * as BusinessApi from '@/api/crm/business'
|
||||||
|
import BusinessForm from './../BusinessForm.vue'
|
||||||
|
import { BizTypeEnum } from '@/api/crm/permission'
|
||||||
|
import { fenToYuanFormat } from '@/utils/formatter'
|
||||||
|
|
||||||
|
defineOptions({ name: 'CrmBusinessList' })
|
||||||
|
const props = defineProps<{
|
||||||
|
bizType: number // 业务类型
|
||||||
|
bizId: number // 业务编号
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const loading = ref(true) // 列表的加载中
|
||||||
|
const total = ref(0) // 列表的总页数
|
||||||
|
const list = ref([]) // 列表的数据
|
||||||
|
const queryParams = reactive({
|
||||||
|
pageNo: 1,
|
||||||
|
pageSize: 10,
|
||||||
|
customerId: undefined as unknown // 允许 undefined + number
|
||||||
|
})
|
||||||
|
|
||||||
|
/** 查询列表 */
|
||||||
|
const getList = async () => {
|
||||||
|
loading.value = true
|
||||||
|
try {
|
||||||
|
// 置空参数
|
||||||
|
queryParams.customerId = undefined
|
||||||
|
// 执行查询
|
||||||
|
let data = { list: [], total: 0 }
|
||||||
|
switch (props.bizType) {
|
||||||
|
case BizTypeEnum.CRM_CUSTOMER:
|
||||||
|
queryParams.customerId = props.bizId
|
||||||
|
data = await BusinessApi.getBusinessPageByCustomer(queryParams)
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
return
|
||||||
|
}
|
||||||
|
list.value = data.list
|
||||||
|
total.value = data.total
|
||||||
|
} finally {
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 搜索按钮操作 */
|
||||||
|
const handleQuery = () => {
|
||||||
|
queryParams.pageNo = 1
|
||||||
|
getList()
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 添加操作 */
|
||||||
|
const formRef = ref()
|
||||||
|
const openForm = () => {
|
||||||
|
formRef.value.open('create')
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 打开联系人详情 */
|
||||||
|
const { push } = useRouter()
|
||||||
|
const openDetail = (id: number) => {
|
||||||
|
push({ name: 'CrmBusinessDetail', params: { id } })
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 监听打开的 bizId + bizType,从而加载最新的列表 */
|
||||||
|
watch(
|
||||||
|
() => [props.bizId, props.bizType],
|
||||||
|
() => {
|
||||||
|
handleQuery()
|
||||||
|
},
|
||||||
|
{ immediate: true, deep: true }
|
||||||
|
)
|
||||||
|
</script>
|
207
src/views/crm/business/index.vue
Normal file
207
src/views/crm/business/index.vue
Normal file
@ -0,0 +1,207 @@
|
|||||||
|
<template>
|
||||||
|
<ContentWrap>
|
||||||
|
<!-- 搜索工作栏 -->
|
||||||
|
<el-form
|
||||||
|
class="-mb-15px"
|
||||||
|
:model="queryParams"
|
||||||
|
ref="queryFormRef"
|
||||||
|
:inline="true"
|
||||||
|
label-width="68px"
|
||||||
|
>
|
||||||
|
<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>
|
||||||
|
<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" @click="openForm('create')" v-hasPermi="['crm:business:create']">
|
||||||
|
<Icon icon="ep:plus" class="mr-5px" /> 新增
|
||||||
|
</el-button>
|
||||||
|
<el-button
|
||||||
|
type="success"
|
||||||
|
plain
|
||||||
|
@click="handleExport"
|
||||||
|
:loading="exportLoading"
|
||||||
|
v-hasPermi="['crm:business: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="name" />
|
||||||
|
<el-table-column label="客户名称" align="center" prop="customerName" />
|
||||||
|
<el-table-column label="商机金额" align="center" prop="price" />
|
||||||
|
<el-table-column
|
||||||
|
label="预计成交日期"
|
||||||
|
align="center"
|
||||||
|
prop="dealTime"
|
||||||
|
:formatter="dateFormatter"
|
||||||
|
width="180px"
|
||||||
|
/>
|
||||||
|
<el-table-column label="备注" align="center" prop="remark" />
|
||||||
|
<el-table-column label="商机状态类型" align="center" prop="statusTypeName" />
|
||||||
|
<el-table-column label="商机状态" align="center" prop="statusName" />
|
||||||
|
<el-table-column
|
||||||
|
label="更新时间"
|
||||||
|
align="center"
|
||||||
|
prop="updateTime"
|
||||||
|
:formatter="dateFormatter"
|
||||||
|
width="180px"
|
||||||
|
/>
|
||||||
|
<el-table-column
|
||||||
|
label="创建时间"
|
||||||
|
align="center"
|
||||||
|
prop="createTime"
|
||||||
|
:formatter="dateFormatter"
|
||||||
|
width="180px"
|
||||||
|
/>
|
||||||
|
<el-table-column label="负责人" align="center" prop="ownerUserId" />
|
||||||
|
<el-table-column label="创建人" align="center" prop="creator" />
|
||||||
|
<el-table-column label="跟进状态" align="center" prop="followUpStatus" />
|
||||||
|
<el-table-column label="操作" align="center" fixed="right" width="130px">
|
||||||
|
<template #default="scope">
|
||||||
|
<el-button
|
||||||
|
link
|
||||||
|
type="primary"
|
||||||
|
@click="openForm('update', scope.row.id)"
|
||||||
|
v-hasPermi="['crm:business:update']"
|
||||||
|
>
|
||||||
|
编辑
|
||||||
|
</el-button>
|
||||||
|
<el-button
|
||||||
|
link
|
||||||
|
type="danger"
|
||||||
|
@click="handleDelete(scope.row.id)"
|
||||||
|
v-hasPermi="['crm:business: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>
|
||||||
|
|
||||||
|
<!-- 表单弹窗:添加/修改 -->
|
||||||
|
<BusinessForm ref="formRef" @success="getList" />
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { dateFormatter } from '@/utils/formatTime'
|
||||||
|
import download from '@/utils/download'
|
||||||
|
import * as BusinessApi from '@/api/crm/business'
|
||||||
|
import BusinessForm from './BusinessForm.vue'
|
||||||
|
|
||||||
|
defineOptions({ name: 'CrmBusiness' })
|
||||||
|
|
||||||
|
const message = useMessage() // 消息弹窗
|
||||||
|
const { t } = useI18n() // 国际化
|
||||||
|
|
||||||
|
const loading = ref(true) // 列表的加载中
|
||||||
|
const total = ref(0) // 列表的总页数
|
||||||
|
const list = ref([]) // 列表的数据
|
||||||
|
const queryParams = reactive({
|
||||||
|
pageNo: 1,
|
||||||
|
pageSize: 10,
|
||||||
|
name: null,
|
||||||
|
statusTypeId: null,
|
||||||
|
statusId: null,
|
||||||
|
contactNextTime: [],
|
||||||
|
customerId: null,
|
||||||
|
dealTime: [],
|
||||||
|
price: null,
|
||||||
|
discountPercent: null,
|
||||||
|
productPrice: null,
|
||||||
|
remark: null,
|
||||||
|
ownerUserId: null,
|
||||||
|
createTime: [],
|
||||||
|
roUserIds: null,
|
||||||
|
rwUserIds: null,
|
||||||
|
endStatus: null,
|
||||||
|
endRemark: null,
|
||||||
|
contactLastTime: [],
|
||||||
|
followUpStatus: null
|
||||||
|
})
|
||||||
|
const queryFormRef = ref() // 搜索的表单
|
||||||
|
const exportLoading = ref(false) // 导出的加载中
|
||||||
|
|
||||||
|
/** 查询列表 */
|
||||||
|
const getList = async () => {
|
||||||
|
loading.value = true
|
||||||
|
try {
|
||||||
|
const data = await BusinessApi.getBusinessPage(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 BusinessApi.deleteBusiness(id)
|
||||||
|
message.success(t('common.delSuccess'))
|
||||||
|
// 刷新列表
|
||||||
|
await getList()
|
||||||
|
} catch {}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 导出按钮操作 */
|
||||||
|
const handleExport = async () => {
|
||||||
|
try {
|
||||||
|
// 导出的二次确认
|
||||||
|
await message.exportConfirm()
|
||||||
|
// 发起导出
|
||||||
|
exportLoading.value = true
|
||||||
|
const data = await BusinessApi.exportBusiness(queryParams)
|
||||||
|
download.excel(data, '商机.xls')
|
||||||
|
} catch {
|
||||||
|
} finally {
|
||||||
|
exportLoading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 初始化 **/
|
||||||
|
onMounted(() => {
|
||||||
|
getList()
|
||||||
|
})
|
||||||
|
</script>
|
167
src/views/crm/businessStatusType/BusinessStatusTypeForm.vue
Normal file
167
src/views/crm/businessStatusType/BusinessStatusTypeForm.vue
Normal file
@ -0,0 +1,167 @@
|
|||||||
|
<template>
|
||||||
|
<Dialog :title="dialogTitle" v-model="dialogVisible">
|
||||||
|
<el-form
|
||||||
|
ref="formRef"
|
||||||
|
:model="formData"
|
||||||
|
:rules="formRules"
|
||||||
|
label-width="100px"
|
||||||
|
v-loading="formLoading"
|
||||||
|
>
|
||||||
|
<el-form-item label="状态类型名" prop="name">
|
||||||
|
<el-input v-model="formData.name" placeholder="请输入状态类型名" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="应用部门" prop="deptIds">
|
||||||
|
<el-tree
|
||||||
|
ref="treeRef"
|
||||||
|
:data="deptList"
|
||||||
|
:props="defaultProps"
|
||||||
|
:check-strictly="!checkStrictly"
|
||||||
|
node-key="id"
|
||||||
|
placeholder="请选择归属部门"
|
||||||
|
show-checkbox
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="状态设置" prop="statusList">
|
||||||
|
<el-table border style="width: 100%" :data="formData.statusList">
|
||||||
|
<el-table-column align="center" label="状态" width="120" prop="star">
|
||||||
|
<template #default="scope">
|
||||||
|
<el-text>状态{{ scope.$index + 1 }}</el-text>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column align="center" label="状态名称" width="120" prop="name">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<el-input v-model="row.name" placeholder="请输入状态名称" />
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column width="120" align="center" label="赢单率" prop="percent">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<el-input v-model="row.percent" placeholder="请输入赢单率" />
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="操作" align="center">
|
||||||
|
<template #default="scope">
|
||||||
|
<el-button link type="primary" @click="addStatusArea(scope.$index)"> 添加 </el-button>
|
||||||
|
<el-button
|
||||||
|
link
|
||||||
|
type="danger"
|
||||||
|
@click="deleteStatusArea(scope.$index)"
|
||||||
|
v-show="scope.$index > 0"
|
||||||
|
>
|
||||||
|
删除
|
||||||
|
</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
</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 * as BusinessStatusTypeApi from '@/api/crm/businessStatusType'
|
||||||
|
import { defaultProps, handleTree } from '@/utils/tree'
|
||||||
|
import * as DeptApi from '@/api/system/dept'
|
||||||
|
|
||||||
|
const { t } = useI18n() // 国际化
|
||||||
|
const message = useMessage() // 消息弹窗
|
||||||
|
|
||||||
|
const dialogVisible = ref(false) // 弹窗的是否展示
|
||||||
|
const dialogTitle = ref('') // 弹窗的标题
|
||||||
|
const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
|
||||||
|
const formType = ref('') // 表单的类型:create - 新增;update - 修改
|
||||||
|
const formData = ref({
|
||||||
|
id: 0,
|
||||||
|
name: '',
|
||||||
|
deptIds: [],
|
||||||
|
statusList: []
|
||||||
|
})
|
||||||
|
const formRules = reactive({
|
||||||
|
name: [{ required: true, message: '状态类型名不能为空', trigger: 'blur' }]
|
||||||
|
})
|
||||||
|
const formRef = ref() // 表单 Ref
|
||||||
|
const deptList = ref<Tree[]>([]) // 树形结构
|
||||||
|
const treeRef = ref() // 菜单树组件 Ref
|
||||||
|
const checkStrictly = ref(true) // 是否严格模式,即父子不关联
|
||||||
|
|
||||||
|
/** 打开弹窗 */
|
||||||
|
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 BusinessStatusTypeApi.getBusinessStatusType(id)
|
||||||
|
treeRef.value.setCheckedKeys(formData.value.deptIds)
|
||||||
|
if (formData.value.statusList.length == 0) {
|
||||||
|
addStatusArea(0)
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
formLoading.value = false
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
addStatusArea(0)
|
||||||
|
}
|
||||||
|
// 加载部门树
|
||||||
|
deptList.value = handleTree(await DeptApi.getSimpleDeptList())
|
||||||
|
}
|
||||||
|
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 BusinessStatusTypeApi.BusinessStatusTypeVO
|
||||||
|
data.deptIds = treeRef.value.getCheckedKeys(false)
|
||||||
|
if (formType.value === 'create') {
|
||||||
|
await BusinessStatusTypeApi.createBusinessStatusType(data)
|
||||||
|
message.success(t('common.createSuccess'))
|
||||||
|
} else {
|
||||||
|
await BusinessStatusTypeApi.updateBusinessStatusType(data)
|
||||||
|
message.success(t('common.updateSuccess'))
|
||||||
|
}
|
||||||
|
dialogVisible.value = false
|
||||||
|
// 发送操作成功的事件
|
||||||
|
emit('success')
|
||||||
|
} finally {
|
||||||
|
formLoading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 重置表单 */
|
||||||
|
const resetForm = () => {
|
||||||
|
checkStrictly.value = true
|
||||||
|
formData.value = {
|
||||||
|
id: 0,
|
||||||
|
name: '',
|
||||||
|
deptIds: [],
|
||||||
|
statusList: []
|
||||||
|
}
|
||||||
|
treeRef.value?.setCheckedNodes([])
|
||||||
|
formRef.value?.resetFields()
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 添加状态 */
|
||||||
|
const addStatusArea = () => {
|
||||||
|
const data = formData.value
|
||||||
|
data.statusList.push({
|
||||||
|
name: '',
|
||||||
|
percent: ''
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 删除状态 */
|
||||||
|
const deleteStatusArea = (index: number) => {
|
||||||
|
const data = formData.value
|
||||||
|
data.statusList.splice(index, 1)
|
||||||
|
}
|
||||||
|
</script>
|
171
src/views/crm/businessStatusType/index.vue
Normal file
171
src/views/crm/businessStatusType/index.vue
Normal file
@ -0,0 +1,171 @@
|
|||||||
|
<template>
|
||||||
|
<ContentWrap>
|
||||||
|
<!-- 搜索工作栏 -->
|
||||||
|
<el-form
|
||||||
|
class="-mb-15px"
|
||||||
|
:model="queryParams"
|
||||||
|
ref="queryFormRef"
|
||||||
|
:inline="true"
|
||||||
|
label-width="68px"
|
||||||
|
>
|
||||||
|
<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:business-status-type:create']"
|
||||||
|
>
|
||||||
|
<Icon icon="ep:plus" class="mr-5px" /> 新增
|
||||||
|
</el-button>
|
||||||
|
<el-button
|
||||||
|
type="success"
|
||||||
|
plain
|
||||||
|
@click="handleExport"
|
||||||
|
:loading="exportLoading"
|
||||||
|
v-hasPermi="['crm:business-status-type: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="name" />
|
||||||
|
<el-table-column label="使用的部门" align="center" prop="deptNames" />
|
||||||
|
<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">
|
||||||
|
<template #default="scope">
|
||||||
|
<el-button
|
||||||
|
link
|
||||||
|
type="primary"
|
||||||
|
@click="openForm('update', scope.row.id)"
|
||||||
|
v-hasPermi="['crm:business-status-type:update']"
|
||||||
|
>
|
||||||
|
编辑
|
||||||
|
</el-button>
|
||||||
|
<el-button
|
||||||
|
link
|
||||||
|
type="danger"
|
||||||
|
@click="handleDelete(scope.row.id)"
|
||||||
|
v-hasPermi="['crm:business-status-type: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>
|
||||||
|
|
||||||
|
<!-- 表单弹窗:添加/修改 -->
|
||||||
|
<BusinessStatusTypeForm ref="formRef" @success="getList" />
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { dateFormatter } from '@/utils/formatTime'
|
||||||
|
import download from '@/utils/download'
|
||||||
|
import * as BusinessStatusTypeApi from '@/api/crm/businessStatusType'
|
||||||
|
import BusinessStatusTypeForm from './BusinessStatusTypeForm.vue'
|
||||||
|
|
||||||
|
defineOptions({ name: 'BusinessStatusType' })
|
||||||
|
|
||||||
|
const message = useMessage() // 消息弹窗
|
||||||
|
const { t } = useI18n() // 国际化
|
||||||
|
|
||||||
|
const loading = ref(true) // 列表的加载中
|
||||||
|
const list = ref([]) // 列表的数据
|
||||||
|
const total = ref(0) // 列表的总页数
|
||||||
|
const queryParams = reactive({
|
||||||
|
pageNo: 1,
|
||||||
|
pageSize: 10
|
||||||
|
})
|
||||||
|
const queryFormRef = ref() // 搜索的表单
|
||||||
|
const exportLoading = ref(false) // 导出的加载中
|
||||||
|
|
||||||
|
/** 查询列表 */
|
||||||
|
const getList = async () => {
|
||||||
|
loading.value = true
|
||||||
|
try {
|
||||||
|
const data = await BusinessStatusTypeApi.getBusinessStatusTypePage(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 formCustomerRef = ref()
|
||||||
|
const openCustomerForm = (id?: number) => {
|
||||||
|
formCustomerRef.value.open(id)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 删除按钮操作 */
|
||||||
|
const handleDelete = async (id: number) => {
|
||||||
|
try {
|
||||||
|
// 删除的二次确认
|
||||||
|
await message.delConfirm()
|
||||||
|
// 发起删除
|
||||||
|
await BusinessStatusTypeApi.deleteBusinessStatusType(id)
|
||||||
|
message.success(t('common.delSuccess'))
|
||||||
|
// 刷新列表
|
||||||
|
await getList()
|
||||||
|
} catch {}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 导出按钮操作 */
|
||||||
|
const handleExport = async () => {
|
||||||
|
try {
|
||||||
|
// 导出的二次确认
|
||||||
|
await message.exportConfirm()
|
||||||
|
// 发起导出
|
||||||
|
exportLoading.value = true
|
||||||
|
const data = await BusinessStatusTypeApi.exportBusinessStatusType(queryParams)
|
||||||
|
download.excel(data, '商机状态类型.xls')
|
||||||
|
} catch {
|
||||||
|
} finally {
|
||||||
|
exportLoading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 初始化 **/
|
||||||
|
onMounted(() => {
|
||||||
|
getList()
|
||||||
|
})
|
||||||
|
</script>
|
@ -10,9 +10,16 @@
|
|||||||
<el-form-item label="线索名称" prop="name">
|
<el-form-item label="线索名称" prop="name">
|
||||||
<el-input v-model="formData.name" placeholder="请输入线索名称" />
|
<el-input v-model="formData.name" placeholder="请输入线索名称" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<!-- TODO wanwan 客户选择 -->
|
<!-- TODO 芋艿:后续客户的选择 -->
|
||||||
<el-form-item label="客户" prop="customerId">
|
<el-form-item label="客户" prop="customerId">
|
||||||
<el-input v-model="formData.customerId" placeholder="请选择客户" />
|
<el-select v-model="formData.customerId" clearable placeholder="请选择客户">
|
||||||
|
<el-option
|
||||||
|
v-for="item in customerList"
|
||||||
|
:key="item.id"
|
||||||
|
:label="item.name"
|
||||||
|
:value="item.id"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="下次联系时间" prop="contactNextTime">
|
<el-form-item label="下次联系时间" prop="contactNextTime">
|
||||||
<el-date-picker
|
<el-date-picker
|
||||||
@ -47,6 +54,7 @@
|
|||||||
</template>
|
</template>
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import * as ClueApi from '@/api/crm/clue'
|
import * as ClueApi from '@/api/crm/clue'
|
||||||
|
import * as CustomerApi from '@/api/crm/customer'
|
||||||
|
|
||||||
const { t } = useI18n() // 国际化
|
const { t } = useI18n() // 国际化
|
||||||
const message = useMessage() // 消息弹窗
|
const message = useMessage() // 消息弹窗
|
||||||
@ -55,6 +63,7 @@ const dialogVisible = ref(false) // 弹窗的是否展示
|
|||||||
const dialogTitle = ref('') // 弹窗的标题
|
const dialogTitle = ref('') // 弹窗的标题
|
||||||
const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
|
const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
|
||||||
const formType = ref('') // 表单的类型:create - 新增;update - 修改
|
const formType = ref('') // 表单的类型:create - 新增;update - 修改
|
||||||
|
const customerList = ref([]) // 客户列表
|
||||||
const formData = ref({
|
const formData = ref({
|
||||||
id: undefined,
|
id: undefined,
|
||||||
name: undefined,
|
name: undefined,
|
||||||
@ -79,6 +88,12 @@ const open = async (type: string, id?: number) => {
|
|||||||
dialogTitle.value = t('action.' + type)
|
dialogTitle.value = t('action.' + type)
|
||||||
formType.value = type
|
formType.value = type
|
||||||
resetForm()
|
resetForm()
|
||||||
|
const customerData = await CustomerApi.getCustomerPage({
|
||||||
|
pageNo: 1,
|
||||||
|
pageSize: 100,
|
||||||
|
pool: false
|
||||||
|
})
|
||||||
|
customerList.value = customerData.list
|
||||||
// 修改时,设置数据
|
// 修改时,设置数据
|
||||||
if (id) {
|
if (id) {
|
||||||
formLoading.value = true
|
formLoading.value = true
|
||||||
|
@ -1,138 +0,0 @@
|
|||||||
<template>
|
|
||||||
<!-- 操作栏 -->
|
|
||||||
<el-row justify="end">
|
|
||||||
<el-button type="primary" @click="openForm">
|
|
||||||
<Icon class="mr-5px" icon="ep:plus" />
|
|
||||||
新增
|
|
||||||
</el-button>
|
|
||||||
<el-button @click="handleEdit">
|
|
||||||
<Icon class="mr-5px" icon="ep:edit" />
|
|
||||||
编辑
|
|
||||||
</el-button>
|
|
||||||
<el-button @click="handleDelete">
|
|
||||||
<Icon class="mr-5px" icon="ep:delete" />
|
|
||||||
移除
|
|
||||||
</el-button>
|
|
||||||
<el-button type="danger" @click="handleQuit"> 退出团队</el-button>
|
|
||||||
</el-row>
|
|
||||||
<!-- 团队成员展示 -->
|
|
||||||
<el-table
|
|
||||||
v-loading="loading"
|
|
||||||
:data="list"
|
|
||||||
:show-overflow-tooltip="true"
|
|
||||||
:stripe="true"
|
|
||||||
class="mt-20px"
|
|
||||||
@selection-change="handleSelectionChange"
|
|
||||||
>
|
|
||||||
<el-table-column type="selection" width="55" />
|
|
||||||
<el-table-column align="center" label="姓名" prop="nickname" />
|
|
||||||
<el-table-column align="center" label="部门" prop="deptName" />
|
|
||||||
<el-table-column align="center" label="岗位" prop="postNames" />
|
|
||||||
<el-table-column align="center" label="权限级别" prop="level">
|
|
||||||
<template #default="{ row }">
|
|
||||||
<dict-tag :type="DICT_TYPE.CRM_PERMISSION_LEVEL" :value="row.level" />
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column :formatter="dateFormatter" align="center" label="加入时间" prop="createTime" />
|
|
||||||
</el-table>
|
|
||||||
<CrmPermissionForm ref="permissionFormRef" @success="getList" />
|
|
||||||
</template>
|
|
||||||
<script lang="ts" setup>
|
|
||||||
import { dateFormatter } from '@/utils/formatTime'
|
|
||||||
import { ElTable } from 'element-plus'
|
|
||||||
import * as PermissionApi from '@/api/crm/permission'
|
|
||||||
import { useUserStoreWithOut } from '@/store/modules/user'
|
|
||||||
import CrmPermissionForm from './CrmPermissionForm.vue'
|
|
||||||
import { CrmPermissionLevelEnum } from './index'
|
|
||||||
import { DICT_TYPE } from '@/utils/dict'
|
|
||||||
|
|
||||||
defineOptions({ name: 'CrmPermissionList' })
|
|
||||||
|
|
||||||
const message = useMessage() // 消息
|
|
||||||
|
|
||||||
const props = defineProps<{
|
|
||||||
bizType: number // 模块类型
|
|
||||||
bizId: number // 模块数据编号
|
|
||||||
}>()
|
|
||||||
const loading = ref(true) // 列表的加载中
|
|
||||||
const list = ref<PermissionApi.PermissionVO[]>([]) // 列表的数据
|
|
||||||
|
|
||||||
/** 查询列表 */
|
|
||||||
const getList = async () => {
|
|
||||||
loading.value = true
|
|
||||||
try {
|
|
||||||
const data = await PermissionApi.getPermissionList({
|
|
||||||
bizType: props.bizType,
|
|
||||||
bizId: props.bizId
|
|
||||||
})
|
|
||||||
list.value = data
|
|
||||||
} finally {
|
|
||||||
loading.value = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const multipleSelection = ref<PermissionApi.PermissionVO[]>([]) // 选择的团队成员
|
|
||||||
const handleSelectionChange = (val: PermissionApi.PermissionVO[]) => {
|
|
||||||
multipleSelection.value = val
|
|
||||||
}
|
|
||||||
|
|
||||||
const permissionFormRef = ref<InstanceType<typeof CrmPermissionForm>>() // 权限表单 Ref
|
|
||||||
/**
|
|
||||||
* 编辑团队成员
|
|
||||||
*/
|
|
||||||
const handleEdit = () => {
|
|
||||||
if (multipleSelection.value?.length === 0) {
|
|
||||||
message.warning('请先选择团队成员后操作!')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
const ids = multipleSelection.value?.map((item) => item.id)
|
|
||||||
permissionFormRef.value?.open('update', props.bizType, props.bizId, ids)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 移除团队成员
|
|
||||||
*/
|
|
||||||
const handleDelete = async () => {
|
|
||||||
if (multipleSelection.value?.length === 0) {
|
|
||||||
message.warning('请先选择团队成员后操作!')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
await message.delConfirm()
|
|
||||||
const ids = multipleSelection.value?.map((item) => item.id)
|
|
||||||
await PermissionApi.deletePermission({
|
|
||||||
bizType: props.bizType,
|
|
||||||
bizId: props.bizId,
|
|
||||||
ids
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 添加团队成员
|
|
||||||
*/
|
|
||||||
const openForm = () => {
|
|
||||||
permissionFormRef.value?.open('create', props.bizType, props.bizId)
|
|
||||||
}
|
|
||||||
|
|
||||||
const userStore = useUserStoreWithOut() // 用户信息缓存
|
|
||||||
/**
|
|
||||||
* 退出团队
|
|
||||||
*/
|
|
||||||
const handleQuit = async () => {
|
|
||||||
const permission = list.value.find(
|
|
||||||
(item) => item.userId === userStore.getUser.id && item.level === CrmPermissionLevelEnum.OWNER
|
|
||||||
)
|
|
||||||
if (permission) {
|
|
||||||
message.warning('负责人不能退出团队!')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
const userPermission = list.value.find((item) => item.userId === userStore.getUser.id)
|
|
||||||
await PermissionApi.quitTeam(userPermission?.id)
|
|
||||||
}
|
|
||||||
|
|
||||||
watch(
|
|
||||||
() => props.bizId,
|
|
||||||
() => {
|
|
||||||
getList()
|
|
||||||
},
|
|
||||||
{ immediate: true, deep: true }
|
|
||||||
)
|
|
||||||
</script>
|
|
@ -1,15 +0,0 @@
|
|||||||
import CrmPermissionList from './CrmPermissionList.vue'
|
|
||||||
|
|
||||||
enum CrmBizTypeEnum {
|
|
||||||
CRM_LEADS = 1, // 线索
|
|
||||||
CRM_CUSTOMER = 2, // 客户
|
|
||||||
CRM_CONTACTS = 3, // 联系人
|
|
||||||
CRM_BUSINESS = 5, // 商机
|
|
||||||
CRM_CONTRACT = 6 // 合同
|
|
||||||
}
|
|
||||||
|
|
||||||
enum CrmPermissionLevelEnum {
|
|
||||||
OWNER = 1 // 负责人
|
|
||||||
}
|
|
||||||
|
|
||||||
export { CrmPermissionList, CrmBizTypeEnum, CrmPermissionLevelEnum }
|
|
@ -12,9 +12,9 @@
|
|||||||
v-model="formData.userIds"
|
v-model="formData.userIds"
|
||||||
:data="userTree"
|
:data="userTree"
|
||||||
:props="defaultProps"
|
:props="defaultProps"
|
||||||
check-on-click-node
|
|
||||||
multiple
|
multiple
|
||||||
filterable
|
filterable
|
||||||
|
check-on-click-node
|
||||||
node-key="id"
|
node-key="id"
|
||||||
placeholder="请选择规则适用人群"
|
placeholder="请选择规则适用人群"
|
||||||
/>
|
/>
|
||||||
@ -25,8 +25,8 @@
|
|||||||
:data="deptTree"
|
:data="deptTree"
|
||||||
:props="defaultProps"
|
:props="defaultProps"
|
||||||
multiple
|
multiple
|
||||||
check-strictly
|
|
||||||
filterable
|
filterable
|
||||||
|
check-strictly
|
||||||
node-key="id"
|
node-key="id"
|
||||||
placeholder="请选择规则适用部门"
|
placeholder="请选择规则适用部门"
|
||||||
/>
|
/>
|
||||||
@ -57,11 +57,11 @@
|
|||||||
</template>
|
</template>
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import * as CustomerLimitConfigApi from '@/api/crm/customerLimitConfig'
|
import * as CustomerLimitConfigApi from '@/api/crm/customerLimitConfig'
|
||||||
import { LimitConfType } from '@/views/crm/customerLimitConfig/customerLimitConf'
|
|
||||||
import * as DeptApi from '@/api/system/dept'
|
import * as DeptApi from '@/api/system/dept'
|
||||||
import { defaultProps, handleTree } from '@/utils/tree'
|
import { defaultProps, handleTree } from '@/utils/tree'
|
||||||
import * as UserApi from '@/api/system/user'
|
import * as UserApi from '@/api/system/user'
|
||||||
import { cloneDeep } from 'lodash-es'
|
import { cloneDeep } from 'lodash-es'
|
||||||
|
import { LimitConfType } from '@/api/crm/customerLimitConfig'
|
||||||
|
|
||||||
const { t } = useI18n() // 国际化
|
const { t } = useI18n() // 国际化
|
||||||
const message = useMessage() // 消息弹窗
|
const message = useMessage() // 消息弹窗
|
@ -19,8 +19,16 @@
|
|||||||
>
|
>
|
||||||
<el-table-column label="编号" align="center" prop="id" />
|
<el-table-column label="编号" align="center" prop="id" />
|
||||||
<el-table-column label="规则类型" align="center" prop="type" />
|
<el-table-column label="规则类型" align="center" prop="type" />
|
||||||
<el-table-column label="规则适用人群" align="center" prop="userNames" />
|
<el-table-column
|
||||||
<el-table-column label="规则适用部门" align="center" prop="deptNames" />
|
label="规则适用人群"
|
||||||
|
align="center"
|
||||||
|
:formatter="(row) => row.users?.map((user: any) => user.nickname).join(',')"
|
||||||
|
/>
|
||||||
|
<el-table-column
|
||||||
|
label="规则适用部门"
|
||||||
|
align="center"
|
||||||
|
:formatter="(row) => row.depts?.map((dept: any) => dept.name).join(',')"
|
||||||
|
/>
|
||||||
<el-table-column
|
<el-table-column
|
||||||
:label="
|
:label="
|
||||||
confType === LimitConfType.CUSTOMER_QUANTITY_LIMIT ? '拥有客户数上限' : '锁定客户数上限'
|
confType === LimitConfType.CUSTOMER_QUANTITY_LIMIT ? '拥有客户数上限' : '锁定客户数上限'
|
||||||
@ -80,11 +88,11 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { dateFormatter } from '@/utils/formatTime'
|
import { dateFormatter } from '@/utils/formatTime'
|
||||||
import * as CustomerLimitConfigApi from '@/api/crm/customerLimitConfig'
|
import * as CustomerLimitConfigApi from '@/api/crm/customerLimitConfig'
|
||||||
import CustomerLimitConfigForm from '@/views/crm/customerLimitConfig/CustomerLimitConfigForm.vue'
|
import CustomerLimitConfigForm from '@/views/crm/config/customerLimitConfig/CustomerLimitConfigForm.vue'
|
||||||
import { LimitConfType } from '@/views/crm/customerLimitConfig/customerLimitConf'
|
|
||||||
import { DICT_TYPE } from '@/utils/dict'
|
import { DICT_TYPE } from '@/utils/dict'
|
||||||
|
import { LimitConfType } from '@/api/crm/customerLimitConfig'
|
||||||
|
|
||||||
defineOptions({ name: 'CustomerLimitConfDetails' })
|
defineOptions({ name: 'CustomerLimitConfigList' })
|
||||||
|
|
||||||
const message = useMessage() // 消息弹窗
|
const message = useMessage() // 消息弹窗
|
||||||
const { t } = useI18n() // 国际化
|
const { t } = useI18n() // 国际化
|
19
src/views/crm/config/customerLimitConfig/index.vue
Normal file
19
src/views/crm/config/customerLimitConfig/index.vue
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
<template>
|
||||||
|
<!-- 列表 -->
|
||||||
|
<ContentWrap>
|
||||||
|
<el-tabs>
|
||||||
|
<el-tab-pane label="拥有客户数限制">
|
||||||
|
<CustomerLimitConfigList :confType="LimitConfType.CUSTOMER_QUANTITY_LIMIT" />
|
||||||
|
</el-tab-pane>
|
||||||
|
<el-tab-pane label="锁定客户数限制">
|
||||||
|
<CustomerLimitConfigList :confType="LimitConfType.CUSTOMER_LOCK_LIMIT" />
|
||||||
|
</el-tab-pane>
|
||||||
|
</el-tabs>
|
||||||
|
</ContentWrap>
|
||||||
|
</template>
|
||||||
|
<script setup lang="ts">
|
||||||
|
import CustomerLimitConfigList from '@/views/crm/config/customerLimitConfig/CustomerLimitConfigList.vue'
|
||||||
|
import { LimitConfType } from '@/api/crm/customerLimitConfig'
|
||||||
|
|
||||||
|
defineOptions({ name: 'CrmCustomerLimitConfig' })
|
||||||
|
</script>
|
@ -23,7 +23,7 @@
|
|||||||
</template>
|
</template>
|
||||||
<!-- 表单 -->
|
<!-- 表单 -->
|
||||||
<el-form-item label="客户公海规则设置" prop="enabled">
|
<el-form-item label="客户公海规则设置" prop="enabled">
|
||||||
<el-radio-group v-model="formData.enabled" class="ml-4">
|
<el-radio-group v-model="formData.enabled" @change="changeEnable" class="ml-4">
|
||||||
<el-radio :label="false" size="large">不启用</el-radio>
|
<el-radio :label="false" size="large">不启用</el-radio>
|
||||||
<el-radio :label="true" size="large">启用</el-radio>
|
<el-radio :label="true" size="large">启用</el-radio>
|
||||||
</el-radio-group>
|
</el-radio-group>
|
||||||
@ -36,7 +36,11 @@
|
|||||||
天未成交
|
天未成交
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="提前提醒设置" prop="notifyEnabled">
|
<el-form-item label="提前提醒设置" prop="notifyEnabled">
|
||||||
<el-radio-group v-model="formData.notifyEnabled" class="ml-4">
|
<el-radio-group
|
||||||
|
v-model="formData.notifyEnabled"
|
||||||
|
@change="changeNotifyEnable"
|
||||||
|
class="ml-4"
|
||||||
|
>
|
||||||
<el-radio :label="false" size="large">不提醒</el-radio>
|
<el-radio :label="false" size="large">不提醒</el-radio>
|
||||||
<el-radio :label="true" size="large">提醒</el-radio>
|
<el-radio :label="true" size="large">提醒</el-radio>
|
||||||
</el-radio-group>
|
</el-radio-group>
|
||||||
@ -52,11 +56,10 @@
|
|||||||
</ContentWrap>
|
</ContentWrap>
|
||||||
</template>
|
</template>
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import * as CustomerPoolConfApi from '@/api/crm/customerPoolConf'
|
import * as CustomerPoolConfigApi from '@/api/crm/customerPoolConfig'
|
||||||
import { CardTitle } from '@/components/Card'
|
import { CardTitle } from '@/components/Card'
|
||||||
|
|
||||||
// TODO @wanwan:CustomerPoolConf =》 CustomerPoolConfig;另外,我们在 crm 目录下,新建一个 config 目录,然后把 customerPoolConfig 和 customerLimitConfig 都挪进
|
defineOptions({ name: 'CrmCustomerPoolConfig' })
|
||||||
defineOptions({ name: 'CustomerPoolConf' })
|
|
||||||
|
|
||||||
const message = useMessage() // 消息弹窗
|
const message = useMessage() // 消息弹窗
|
||||||
const { t } = useI18n() // 国际化
|
const { t } = useI18n() // 国际化
|
||||||
@ -78,7 +81,7 @@ const formRef = ref() // 表单 Ref
|
|||||||
const getConfig = async () => {
|
const getConfig = async () => {
|
||||||
try {
|
try {
|
||||||
formLoading.value = true
|
formLoading.value = true
|
||||||
const data = await CustomerPoolConfApi.getCustomerPoolConfig()
|
const data = await CustomerPoolConfigApi.getCustomerPoolConfig()
|
||||||
if (data === null) {
|
if (data === null) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -97,8 +100,8 @@ const onSubmit = async () => {
|
|||||||
// 提交请求
|
// 提交请求
|
||||||
formLoading.value = true
|
formLoading.value = true
|
||||||
try {
|
try {
|
||||||
const data = formData.value as unknown as CustomerPoolConfApi.CustomerPoolConfigVO
|
const data = formData.value as unknown as CustomerPoolConfigApi.CustomerPoolConfigVO
|
||||||
await CustomerPoolConfApi.updateCustomerPoolConfig(data)
|
await CustomerPoolConfigApi.saveCustomerPoolConfig(data)
|
||||||
message.success(t('common.updateSuccess'))
|
message.success(t('common.updateSuccess'))
|
||||||
await getConfig()
|
await getConfig()
|
||||||
formLoading.value = false
|
formLoading.value = false
|
||||||
@ -107,27 +110,22 @@ const onSubmit = async () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO @wanwan:el-radio-group 选择后,触发会不会更好哈;
|
/** 更改客户公海规则设置 */
|
||||||
watch(
|
const changeEnable = () => {
|
||||||
() => formData.value.enabled,
|
if (!formData.value.enabled) {
|
||||||
(val: boolean) => {
|
|
||||||
if (!val) {
|
|
||||||
formData.value.contactExpireDays = undefined
|
formData.value.contactExpireDays = undefined
|
||||||
formData.value.dealExpireDays = undefined
|
formData.value.dealExpireDays = undefined
|
||||||
formData.value.notifyEnabled = false
|
formData.value.notifyEnabled = false
|
||||||
formData.value.notifyDays = undefined
|
formData.value.notifyDays = undefined
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
|
||||||
// TODO @wanwan:el-radio-group 选择后,触发会不会更好哈;
|
/** 更改提前提醒设置 */
|
||||||
watch(
|
const changeNotifyEnable = () => {
|
||||||
() => formData.value.notifyEnabled,
|
if (!formData.value.notifyEnabled) {
|
||||||
(val: boolean) => {
|
|
||||||
if (!val) {
|
|
||||||
formData.value.notifyDays = undefined
|
formData.value.notifyDays = undefined
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
getConfig()
|
getConfig()
|
@ -1,86 +1,56 @@
|
|||||||
<template>
|
<template>
|
||||||
<Dialog :title="dialogTitle" v-model="dialogVisible" :width="800">
|
<Dialog :title="dialogTitle" v-model="dialogVisible" :width="820">
|
||||||
<el-form
|
<el-form
|
||||||
ref="formRef"
|
ref="formRef"
|
||||||
:model="formData"
|
:model="formData"
|
||||||
:rules="formRules"
|
:rules="formRules"
|
||||||
label-width="130px"
|
label-width="110px"
|
||||||
v-loading="formLoading"
|
v-loading="formLoading"
|
||||||
:inline="true"
|
|
||||||
>
|
>
|
||||||
|
<el-row :gutter="20">
|
||||||
|
<el-col :span="12">
|
||||||
<el-form-item label="姓名" prop="name">
|
<el-form-item label="姓名" prop="name">
|
||||||
<el-input v-model="formData.name" placeholder="请输入姓名" />
|
<el-input input-style="width:190px;" v-model="formData.name" placeholder="请输入姓名" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="12">
|
||||||
<el-form-item label="负责人" prop="ownerUserId">
|
<el-form-item label="负责人" prop="ownerUserId">
|
||||||
<el-select
|
<el-select
|
||||||
v-model="ownerUserList"
|
v-model="formData.ownerUserId"
|
||||||
placeholder="请选择负责人"
|
placeholder="请选择负责人"
|
||||||
multiple
|
|
||||||
value-key="id"
|
value-key="id"
|
||||||
lable-key="nickname"
|
lable-key="nickname"
|
||||||
@click="openOwerForm('open')"
|
|
||||||
>
|
>
|
||||||
<el-option
|
<el-option
|
||||||
v-for="item in ownerUserList"
|
v-for="item in userList"
|
||||||
:key="item.id"
|
:key="item.id"
|
||||||
:label="item.nickname"
|
:label="item.nickname"
|
||||||
:value="item"
|
:value="item.id"
|
||||||
/>
|
/>
|
||||||
</el-select>
|
</el-select>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<!-- TODO 芋艿:封装成一个组件 -->
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
<el-row>
|
||||||
|
<el-col :span="12">
|
||||||
<el-form-item label="客户名称" prop="customerName">
|
<el-form-item label="客户名称" prop="customerName">
|
||||||
<el-popover
|
<el-select
|
||||||
placement="bottom"
|
v-model="formData.customerId"
|
||||||
:width="600"
|
placeholder="请选择客户"
|
||||||
trigger="click"
|
value-key="id"
|
||||||
:teleported="false"
|
lable-key="name"
|
||||||
:visible="showCustomer"
|
|
||||||
:offset="10"
|
|
||||||
>
|
>
|
||||||
<template #reference>
|
<el-option
|
||||||
<el-input
|
v-for="item in customerList"
|
||||||
placeholder="请选择"
|
:key="item.id"
|
||||||
@click="openCustomerSelect"
|
:label="item.name"
|
||||||
v-model="formData.customerName"
|
:value="item.id"
|
||||||
/>
|
/>
|
||||||
</template>
|
</el-select>
|
||||||
<el-table :data="list" ref="multipleTableRef" @select="handleSelectionChange">
|
|
||||||
<el-table-column label="选择" type="selection" width="55" />
|
|
||||||
<el-table-column width="100" property="id" label="编号" />
|
|
||||||
<el-table-column width="150" property="name" label="客户名称" />
|
|
||||||
<el-table-column label="客户来源" align="center" prop="source" width="100">
|
|
||||||
<template #default="scope">
|
|
||||||
<dict-tag :type="DICT_TYPE.CRM_CUSTOMER_SOURCE" :value="scope.row.source" />
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column label="客户等级" align="center" prop="level" width="120">
|
|
||||||
<template #default="scope">
|
|
||||||
<dict-tag :type="DICT_TYPE.CRM_CUSTOMER_LEVEL" :value="scope.row.level" />
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
</el-table>
|
|
||||||
<!-- 分页 -->
|
|
||||||
<el-row :gutter="20">
|
|
||||||
<el-col>
|
|
||||||
<Pagination
|
|
||||||
:total="total"
|
|
||||||
v-model:page="queryParams.pageNo"
|
|
||||||
v-model:limit="queryParams.pageSize"
|
|
||||||
@pagination="getList"
|
|
||||||
layout="sizes, prev, pager, next"
|
|
||||||
/>
|
|
||||||
</el-col>
|
|
||||||
</el-row>
|
|
||||||
<el-row :gutter="20">
|
|
||||||
<el-col :span="10" :offset="13">
|
|
||||||
<el-button @click="selectCustomer">确认</el-button>
|
|
||||||
<el-button @click="showCustomer = false">取消</el-button>
|
|
||||||
</el-col>
|
|
||||||
</el-row>
|
|
||||||
</el-popover>
|
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="性别" prop="sex">
|
</el-col>
|
||||||
|
<el-col :span="12"
|
||||||
|
><el-form-item label="性别" prop="sex">
|
||||||
<el-select v-model="formData.sex" placeholder="请选择">
|
<el-select v-model="formData.sex" placeholder="请选择">
|
||||||
<el-option
|
<el-option
|
||||||
v-for="dict in getIntDictOptions(DICT_TYPE.SYSTEM_USER_SEX)"
|
v-for="dict in getIntDictOptions(DICT_TYPE.SYSTEM_USER_SEX)"
|
||||||
@ -88,23 +58,52 @@
|
|||||||
:label="dict.label"
|
:label="dict.label"
|
||||||
:value="dict.value"
|
:value="dict.value"
|
||||||
/>
|
/>
|
||||||
</el-select>
|
</el-select> </el-form-item
|
||||||
</el-form-item>
|
></el-col>
|
||||||
|
</el-row>
|
||||||
|
<el-row>
|
||||||
|
<el-col :span="12">
|
||||||
<el-form-item label="手机号" prop="mobile">
|
<el-form-item label="手机号" prop="mobile">
|
||||||
<el-input v-model="formData.mobile" placeholder="请输入手机号" />
|
<el-input
|
||||||
|
input-style="width:190px;"
|
||||||
|
v-model="formData.mobile"
|
||||||
|
placeholder="请输入手机号"
|
||||||
|
/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="12">
|
||||||
<el-form-item label="座机" prop="telephone">
|
<el-form-item label="座机" prop="telephone">
|
||||||
<el-input v-model="formData.telephone" placeholder="请输入座机" style="width: 215px" />
|
<el-input v-model="formData.telephone" placeholder="请输入座机" style="width: 215px" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
<el-row>
|
||||||
|
<el-col :span="12">
|
||||||
<el-form-item label="邮箱" prop="email">
|
<el-form-item label="邮箱" prop="email">
|
||||||
<el-input v-model="formData.email" placeholder="请输入邮箱" />
|
<el-input
|
||||||
|
input-style="width:190px;"
|
||||||
|
v-model="formData.email"
|
||||||
|
placeholder="请输入邮箱"
|
||||||
|
/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="12">
|
||||||
<el-form-item label="QQ" prop="qq">
|
<el-form-item label="QQ" prop="qq">
|
||||||
<el-input v-model="formData.qq" placeholder="请输入QQ" style="width: 215px" />
|
<el-input v-model="formData.qq" placeholder="请输入QQ" style="width: 215px" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="微信" prop="webchat">
|
</el-col>
|
||||||
<el-input v-model="formData.webchat" placeholder="请输入微信" />
|
</el-row>
|
||||||
|
<el-row>
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="微信" prop="wechat">
|
||||||
|
<el-input
|
||||||
|
input-style="width:190px;"
|
||||||
|
v-model="formData.wechat"
|
||||||
|
placeholder="请输入微信"
|
||||||
|
/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="12">
|
||||||
<el-form-item label="下次联系时间" prop="nextTime">
|
<el-form-item label="下次联系时间" prop="nextTime">
|
||||||
<el-date-picker
|
<el-date-picker
|
||||||
v-model="formData.nextTime"
|
v-model="formData.nextTime"
|
||||||
@ -113,9 +112,30 @@
|
|||||||
placeholder="选择下次联系时间"
|
placeholder="选择下次联系时间"
|
||||||
/>
|
/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="地址" prop="address">
|
</el-col>
|
||||||
<el-input v-model="formData.address" placeholder="请输入地址" />
|
</el-row>
|
||||||
|
<el-row>
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="所在地" prop="areaId">
|
||||||
|
<el-tree-select
|
||||||
|
v-model="formData.areaId"
|
||||||
|
:data="areaList"
|
||||||
|
:props="defaultProps"
|
||||||
|
:render-after-expand="true"
|
||||||
|
/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="地址" prop="address">
|
||||||
|
<el-input
|
||||||
|
input-style="width:190px;"
|
||||||
|
v-model="formData.address"
|
||||||
|
placeholder="请输入地址"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col> </el-row
|
||||||
|
><el-row>
|
||||||
|
<el-col :span="12">
|
||||||
<el-form-item label="直属上级" prop="parentId">
|
<el-form-item label="直属上级" prop="parentId">
|
||||||
<el-select v-model="formData.parentId" placeholder="请选择">
|
<el-select v-model="formData.parentId" placeholder="请选择">
|
||||||
<el-option
|
<el-option
|
||||||
@ -127,13 +147,16 @@
|
|||||||
/>
|
/>
|
||||||
</el-select>
|
</el-select>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="12">
|
||||||
<el-form-item label="职位" prop="post">
|
<el-form-item label="职位" prop="post">
|
||||||
<el-input v-model="formData.post" placeholder="请输入职位" />
|
<el-input input-style="width:190px;" v-model="formData.post" placeholder="请输入职位" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
</el-col> </el-row
|
||||||
<el-form-item label="是否关键决策人" prop="policyMakers" style="width: 400px">
|
><el-row>
|
||||||
<el-radio-group v-model="formData.policyMakers">
|
<el-col :span="12"
|
||||||
|
><el-form-item label="是否关键决策人" prop="master" style="width: 400px">
|
||||||
|
<el-radio-group v-model="formData.master">
|
||||||
<el-radio
|
<el-radio
|
||||||
v-for="dict in getBoolDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING)"
|
v-for="dict in getBoolDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING)"
|
||||||
:key="dict.value"
|
:key="dict.value"
|
||||||
@ -143,28 +166,29 @@
|
|||||||
</el-radio>
|
</el-radio>
|
||||||
</el-radio-group>
|
</el-radio-group>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="备注" prop="remark">
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
<el-row>
|
||||||
|
<el-col :span="24"
|
||||||
|
><el-form-item label="备注" prop="remark">
|
||||||
<el-input v-model="formData.remark" placeholder="请输入备注" />
|
<el-input v-model="formData.remark" placeholder="请输入备注" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
</el-form>
|
</el-form>
|
||||||
<template #footer>
|
<template #footer>
|
||||||
<el-button @click="submitForm" type="primary" :disabled="formLoading">确 定</el-button>
|
<el-button @click="submitForm" type="primary" :disabled="formLoading">确 定</el-button>
|
||||||
<el-button @click="dialogVisible = false">取 消</el-button>
|
<el-button @click="dialogVisible = false">取 消</el-button>
|
||||||
</template>
|
</template>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
<OwerSelect
|
|
||||||
ref="owerRef"
|
|
||||||
@confirmOwerSelect="owerSelectValue"
|
|
||||||
:initOwerUser="formData.ownerUserId"
|
|
||||||
/>
|
|
||||||
</template>
|
</template>
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import * as ContactApi from '@/api/crm/contact'
|
import * as ContactApi from '@/api/crm/contact'
|
||||||
import { DICT_TYPE, getIntDictOptions, getBoolDictOptions } from '@/utils/dict'
|
import { DICT_TYPE, getIntDictOptions, getBoolDictOptions } from '@/utils/dict'
|
||||||
import OwerSelect from './OwerSelect.vue'
|
|
||||||
import * as UserApi from '@/api/system/user'
|
import * as UserApi from '@/api/system/user'
|
||||||
import * as CustomerApi from '@/api/crm/customer'
|
import * as CustomerApi from '@/api/crm/customer'
|
||||||
import { ElTable } from 'element-plus'
|
import * as AreaApi from '@/api/system/area'
|
||||||
|
import { defaultProps } from '@/utils/tree'
|
||||||
|
|
||||||
const { t } = useI18n() // 国际化
|
const { t } = useI18n() // 国际化
|
||||||
const message = useMessage() // 消息弹窗
|
const message = useMessage() // 消息弹窗
|
||||||
@ -172,6 +196,7 @@ const dialogVisible = ref(false) // 弹窗的是否展示
|
|||||||
const dialogTitle = ref('') // 弹窗的标题
|
const dialogTitle = ref('') // 弹窗的标题
|
||||||
const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
|
const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
|
||||||
const formType = ref('') // 表单的类型:create - 新增;update - 修改
|
const formType = ref('') // 表单的类型:create - 新增;update - 修改
|
||||||
|
const areaList = ref([]) // 地区列表
|
||||||
const formData = ref({
|
const formData = ref({
|
||||||
nextTime: undefined,
|
nextTime: undefined,
|
||||||
mobile: undefined,
|
mobile: undefined,
|
||||||
@ -188,21 +213,10 @@ const formData = ref({
|
|||||||
name: undefined,
|
name: undefined,
|
||||||
post: undefined,
|
post: undefined,
|
||||||
qq: undefined,
|
qq: undefined,
|
||||||
webchat: undefined,
|
wechat: undefined,
|
||||||
sex: undefined,
|
sex: undefined,
|
||||||
policyMakers: undefined
|
master: false,
|
||||||
})
|
areaId: undefined
|
||||||
const loading = ref(true) // 列表的加载中
|
|
||||||
const total = ref(0) // 列表的总页数
|
|
||||||
const list = ref([]) // 列表的数据
|
|
||||||
const queryParams = reactive({
|
|
||||||
pageNo: 1,
|
|
||||||
pageSize: 10,
|
|
||||||
name: null,
|
|
||||||
mobile: null,
|
|
||||||
industryId: null,
|
|
||||||
level: null,
|
|
||||||
source: null
|
|
||||||
})
|
})
|
||||||
const formRules = reactive({
|
const formRules = reactive({
|
||||||
name: [{ required: true, message: '姓名不能为空', trigger: 'blur' }],
|
name: [{ required: true, message: '姓名不能为空', trigger: 'blur' }],
|
||||||
@ -212,56 +226,34 @@ const formRules = reactive({
|
|||||||
const formRef = ref() // 表单 Ref
|
const formRef = ref() // 表单 Ref
|
||||||
const ownerUserList = ref<any[]>([])
|
const ownerUserList = ref<any[]>([])
|
||||||
const userList = ref<UserApi.UserVO[]>([]) // 用户列表
|
const userList = ref<UserApi.UserVO[]>([]) // 用户列表
|
||||||
|
// TODO 芋艿:统一的客户选择面板
|
||||||
|
const customerList = ref<CustomerApi.CustomerVO[]>([]) // 客户列表
|
||||||
|
const allContactList = ref([]) // 所有联系人列表
|
||||||
/** 打开弹窗 */
|
/** 打开弹窗 */
|
||||||
const open = async (type: string, id?: number) => {
|
const open = async (type: string, id?: number) => {
|
||||||
dialogVisible.value = true
|
dialogVisible.value = true
|
||||||
dialogTitle.value = t('action.' + type)
|
dialogTitle.value = t('action.' + type)
|
||||||
formType.value = type
|
formType.value = type
|
||||||
allContactList.value = await ContactApi.simpleAlllist()
|
|
||||||
resetForm()
|
resetForm()
|
||||||
|
allContactList.value = await ContactApi.getSimpleContactList()
|
||||||
|
userList.value = await UserApi.getSimpleUserList()
|
||||||
|
customerList.value = await CustomerApi.queryAllList()
|
||||||
|
areaList.value = await AreaApi.getAreaTree()
|
||||||
// 修改时,设置数据
|
// 修改时,设置数据
|
||||||
if (id) {
|
if (id) {
|
||||||
formLoading.value = true
|
formLoading.value = true
|
||||||
try {
|
try {
|
||||||
formData.value = await ContactApi.getContact(id)
|
formData.value = await ContactApi.getContact(id)
|
||||||
userList.value = await UserApi.getSimpleUserList()
|
|
||||||
await gotOwnerUser(formData.value.ownerUserId)
|
|
||||||
} finally {
|
} finally {
|
||||||
formLoading.value = false
|
formLoading.value = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
defineExpose({ open }) // 提供 open 方法,用于打开弹窗
|
defineExpose({ open }) // 提供 open 方法,用于打开弹窗
|
||||||
|
|
||||||
/** 查询列表 */
|
|
||||||
const getList = async () => {
|
|
||||||
loading.value = true
|
|
||||||
try {
|
|
||||||
const data = await CustomerApi.getCustomerPage(queryParams)
|
|
||||||
list.value = data.list
|
|
||||||
total.value = data.total
|
|
||||||
} finally {
|
|
||||||
loading.value = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const gotOwnerUser = (owerUserId: any) => {
|
|
||||||
if (owerUserId !== null) {
|
|
||||||
owerUserId.split(',').forEach((item: string) => {
|
|
||||||
userList.value.find((user: { id: any }) => {
|
|
||||||
if (user.id == item) {
|
|
||||||
ownerUserList.value.push(user)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 提交表单 */
|
/** 提交表单 */
|
||||||
const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
|
const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
|
||||||
const submitForm = async () => {
|
const submitForm = async () => {
|
||||||
owerSelectValue(ownerUserList)
|
// owerSelectValue(ownerUserList)
|
||||||
// 校验表单
|
// 校验表单
|
||||||
if (!formRef) return
|
if (!formRef) return
|
||||||
const valid = await formRef.value.validate()
|
const valid = await formRef.value.validate()
|
||||||
@ -302,52 +294,11 @@ const resetForm = () => {
|
|||||||
name: undefined,
|
name: undefined,
|
||||||
post: undefined,
|
post: undefined,
|
||||||
qq: undefined,
|
qq: undefined,
|
||||||
webchat: undefined,
|
wechat: undefined,
|
||||||
sex: undefined,
|
sex: undefined,
|
||||||
policyMakers: undefined
|
master: false
|
||||||
}
|
}
|
||||||
formRef.value?.resetFields()
|
formRef.value?.resetFields()
|
||||||
ownerUserList.value = []
|
ownerUserList.value = []
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 添加/修改操作 */
|
|
||||||
// TODO @zyna:owner?拼写要注意哈;
|
|
||||||
const owerRef = ref()
|
|
||||||
const openOwerForm = (type: string) => {
|
|
||||||
owerRef.value.open(type, ownerUserList.value)
|
|
||||||
}
|
|
||||||
const owerSelectValue = (value) => {
|
|
||||||
ownerUserList.value = value.value
|
|
||||||
formData.value.ownerUserId = undefined
|
|
||||||
value.value.forEach((item, index) => {
|
|
||||||
if (index != 0) {
|
|
||||||
formData.value.ownerUserId = formData.value.ownerUserId + ',' + item.id
|
|
||||||
} else {
|
|
||||||
formData.value.ownerUserId = item.id
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
// 选择客户
|
|
||||||
const showCustomer = ref(false)
|
|
||||||
const openCustomerSelect = () => {
|
|
||||||
showCustomer.value = !showCustomer.value
|
|
||||||
queryParams.pageNo = 1
|
|
||||||
getList()
|
|
||||||
}
|
|
||||||
const multipleTableRef = ref<InstanceType<typeof ElTable>>()
|
|
||||||
const multipleSelection = ref()
|
|
||||||
const handleSelectionChange = ({}, row) => {
|
|
||||||
multipleSelection.value = row
|
|
||||||
multipleTableRef.value!.clearSelection()
|
|
||||||
multipleTableRef.value!.toggleRowSelection(row, undefined)
|
|
||||||
}
|
|
||||||
const selectCustomer = () => {
|
|
||||||
formData.value.customerId = multipleSelection.value.id
|
|
||||||
formData.value.customerName = multipleSelection.value.name
|
|
||||||
showCustomer.value = !showCustomer.value
|
|
||||||
}
|
|
||||||
const allContactList = ref([]) // 所有联系人列表
|
|
||||||
onMounted(async () => {
|
|
||||||
allContactList.value = await ContactApi.simpleAlllist()
|
|
||||||
})
|
|
||||||
</script>
|
</script>
|
||||||
|
112
src/views/crm/contact/components/ContactList.vue
Normal file
112
src/views/crm/contact/components/ContactList.vue
Normal file
@ -0,0 +1,112 @@
|
|||||||
|
<template>
|
||||||
|
<!-- 操作栏 -->
|
||||||
|
<el-row justify="end">
|
||||||
|
<el-button @click="openForm">
|
||||||
|
<Icon class="mr-5px" icon="system-uicons:contacts" />
|
||||||
|
创建联系人
|
||||||
|
</el-button>
|
||||||
|
</el-row>
|
||||||
|
|
||||||
|
<!-- 列表 -->
|
||||||
|
<ContentWrap class="mt-10px">
|
||||||
|
<el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true">
|
||||||
|
<el-table-column label="姓名" fixed="left" align="center" prop="name">
|
||||||
|
<template #default="scope">
|
||||||
|
<el-link type="primary" :underline="false" @click="openDetail(scope.row.id)">
|
||||||
|
{{ scope.row.name }}
|
||||||
|
</el-link>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="手机号" align="center" prop="mobile" />
|
||||||
|
<el-table-column label="职位" align="center" prop="post" />
|
||||||
|
<el-table-column label="直属上级" align="center" prop="parentName" />
|
||||||
|
<el-table-column label="是否关键决策人" align="center" prop="master">
|
||||||
|
<template #default="scope">
|
||||||
|
<dict-tag :type="DICT_TYPE.INFRA_BOOLEAN_STRING" :value="scope.row.master" />
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<!-- TODO 芋艿:【操作:设为首要联系人】 -->
|
||||||
|
</el-table>
|
||||||
|
<!-- 分页 -->
|
||||||
|
<Pagination
|
||||||
|
:total="total"
|
||||||
|
v-model:page="queryParams.pageNo"
|
||||||
|
v-model:limit="queryParams.pageSize"
|
||||||
|
@pagination="getList"
|
||||||
|
/>
|
||||||
|
</ContentWrap>
|
||||||
|
|
||||||
|
<!-- 表单弹窗:添加 -->
|
||||||
|
<ContactForm ref="formRef" @success="getList" />
|
||||||
|
</template>
|
||||||
|
<script setup lang="ts">
|
||||||
|
import * as ContactApi from '@/api/crm/contact'
|
||||||
|
import ContactForm from './../ContactForm.vue'
|
||||||
|
import { DICT_TYPE } from '@/utils/dict'
|
||||||
|
import { BizTypeEnum } from '@/api/crm/permission'
|
||||||
|
|
||||||
|
defineOptions({ name: 'CrmContactList' })
|
||||||
|
const props = defineProps<{
|
||||||
|
bizType: number // 业务类型
|
||||||
|
bizId: number // 业务编号
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const loading = ref(true) // 列表的加载中
|
||||||
|
const total = ref(0) // 列表的总页数
|
||||||
|
const list = ref([]) // 列表的数据
|
||||||
|
const queryParams = reactive({
|
||||||
|
pageNo: 1,
|
||||||
|
pageSize: 10,
|
||||||
|
customerId: undefined as unknown // 允许 undefined + number
|
||||||
|
})
|
||||||
|
|
||||||
|
/** 查询列表 */
|
||||||
|
const getList = async () => {
|
||||||
|
loading.value = true
|
||||||
|
try {
|
||||||
|
// 置空参数
|
||||||
|
queryParams.customerId = undefined
|
||||||
|
// 执行查询
|
||||||
|
let data = { list: [], total: 0 }
|
||||||
|
switch (props.bizType) {
|
||||||
|
case BizTypeEnum.CRM_CUSTOMER:
|
||||||
|
queryParams.customerId = props.bizId
|
||||||
|
data = await ContactApi.getContactPageByCustomer(queryParams)
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
return
|
||||||
|
}
|
||||||
|
list.value = data.list
|
||||||
|
total.value = data.total
|
||||||
|
} finally {
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 搜索按钮操作 */
|
||||||
|
const handleQuery = () => {
|
||||||
|
queryParams.pageNo = 1
|
||||||
|
getList()
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 添加操作 */
|
||||||
|
const formRef = ref()
|
||||||
|
const openForm = () => {
|
||||||
|
formRef.value.open('create')
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 打开联系人详情 */
|
||||||
|
const { push } = useRouter()
|
||||||
|
const openDetail = (id: number) => {
|
||||||
|
push({ name: 'CrmContactDetail', params: { id } })
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 监听打开的 bizId + bizType,从而加载最新的列表 */
|
||||||
|
watch(
|
||||||
|
() => [props.bizId, props.bizType],
|
||||||
|
() => {
|
||||||
|
handleQuery()
|
||||||
|
},
|
||||||
|
{ immediate: true, deep: true }
|
||||||
|
)
|
||||||
|
</script>
|
@ -8,7 +8,7 @@
|
|||||||
<el-descriptions-item label="姓名">
|
<el-descriptions-item label="姓名">
|
||||||
{{ contact.name }}
|
{{ contact.name }}
|
||||||
</el-descriptions-item>
|
</el-descriptions-item>
|
||||||
<el-descriptions-item label="客户名称">
|
<el-descriptions-item label="客户">
|
||||||
{{ contact.customerName }}
|
{{ contact.customerName }}
|
||||||
</el-descriptions-item>
|
</el-descriptions-item>
|
||||||
<el-descriptions-item label="手机">
|
<el-descriptions-item label="手机">
|
||||||
@ -24,14 +24,17 @@
|
|||||||
{{ contact.qq }}
|
{{ contact.qq }}
|
||||||
</el-descriptions-item>
|
</el-descriptions-item>
|
||||||
<el-descriptions-item label="微信">
|
<el-descriptions-item label="微信">
|
||||||
{{ contact.webchat }}
|
{{ contact.wechat }}
|
||||||
</el-descriptions-item>
|
|
||||||
<el-descriptions-item label="详细地址">
|
|
||||||
{{ contact.address }}
|
|
||||||
</el-descriptions-item>
|
</el-descriptions-item>
|
||||||
<el-descriptions-item label="下次联系时间">
|
<el-descriptions-item label="下次联系时间">
|
||||||
{{ contact.nextTime ? formatDate(contact.nextTime) : '空' }}
|
{{ contact.nextTime ? formatDate(contact.nextTime) : '空' }}
|
||||||
</el-descriptions-item>
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="所在地">
|
||||||
|
{{ contact.areaName }}
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="详细地址">
|
||||||
|
{{ contact.address }}
|
||||||
|
</el-descriptions-item>
|
||||||
<el-descriptions-item label="性别">
|
<el-descriptions-item label="性别">
|
||||||
<dict-tag :type="DICT_TYPE.SYSTEM_USER_SEX" :value="contact.sex" />
|
<dict-tag :type="DICT_TYPE.SYSTEM_USER_SEX" :value="contact.sex" />
|
||||||
</el-descriptions-item>
|
</el-descriptions-item>
|
||||||
@ -46,7 +49,7 @@
|
|||||||
</template>
|
</template>
|
||||||
<el-descriptions :column="2">
|
<el-descriptions :column="2">
|
||||||
<el-descriptions-item label="负责人">
|
<el-descriptions-item label="负责人">
|
||||||
{{ gotOwnerUser(contact.ownerUserId) }}
|
{{ contact.ownerUserName }}
|
||||||
</el-descriptions-item>
|
</el-descriptions-item>
|
||||||
<el-descriptions-item label="创建人">
|
<el-descriptions-item label="创建人">
|
||||||
{{ contact.creatorName }}
|
{{ contact.creatorName }}
|
||||||
@ -66,29 +69,9 @@
|
|||||||
import * as ContactApi from '@/api/crm/contact'
|
import * as ContactApi from '@/api/crm/contact'
|
||||||
import { DICT_TYPE } from '@/utils/dict'
|
import { DICT_TYPE } from '@/utils/dict'
|
||||||
import { formatDate } from '@/utils/formatTime'
|
import { formatDate } from '@/utils/formatTime'
|
||||||
import * as UserApi from '@/api/system/user'
|
|
||||||
const { contact } = defineProps<{ contact: ContactApi.ContactVO }>()
|
const { contact } = defineProps<{ contact: ContactApi.ContactVO }>()
|
||||||
|
|
||||||
// 展示的折叠面板
|
// 展示的折叠面板
|
||||||
const activeNames = ref(['basicInfo', 'systemInfo'])
|
const activeNames = ref(['basicInfo', 'systemInfo'])
|
||||||
const gotOwnerUser = (owerUserId: string) => {
|
|
||||||
let ownerName = ''
|
|
||||||
if (owerUserId !== null && owerUserId != undefined) {
|
|
||||||
owerUserId.split(',').forEach((item: string, index: number) => {
|
|
||||||
if (index != 0) {
|
|
||||||
ownerName =
|
|
||||||
ownerName + ',' + userList.value.find((user: { id: any }) => user.id == item)?.nickname
|
|
||||||
} else {
|
|
||||||
ownerName = userList.value.find((user: { id: any }) => user.id == item)?.nickname || ''
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
return ownerName
|
|
||||||
}
|
|
||||||
const userList = ref<UserApi.UserVO[]>([]) // 用户列表
|
|
||||||
/** 初始化 **/
|
|
||||||
onMounted(async () => {
|
|
||||||
userList.value = await UserApi.getSimpleUserList()
|
|
||||||
})
|
|
||||||
</script>
|
</script>
|
||||||
<style scoped lang="scss"></style>
|
<style scoped lang="scss"></style>
|
||||||
|
@ -46,7 +46,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<ContentWrap class="mt-10px">
|
<ContentWrap class="mt-10px">
|
||||||
<el-descriptions :column="5" direction="vertical">
|
<el-descriptions :column="5" direction="vertical">
|
||||||
<el-descriptions-item label="客户名称">
|
<el-descriptions-item label="客户">
|
||||||
{{ contact.customerName }}
|
{{ contact.customerName }}
|
||||||
</el-descriptions-item>
|
</el-descriptions-item>
|
||||||
<el-descriptions-item label="职务">
|
<el-descriptions-item label="职务">
|
||||||
@ -63,33 +63,18 @@
|
|||||||
<!-- TODO wanwan:这个 tab 拉满哈,可以更好看; -->
|
<!-- TODO wanwan:这个 tab 拉满哈,可以更好看; -->
|
||||||
<el-col :span="18">
|
<el-col :span="18">
|
||||||
<el-tabs>
|
<el-tabs>
|
||||||
<el-tab-pane label="详细资料">
|
<el-tab-pane label="基本信息">
|
||||||
<!-- TODO wanwan:这个 ml-2 是不是可以优化下,不要整个左移,而是里面的内容有个几 px 的偏移,不顶在框里 -->
|
<!-- TODO wanwan:这个 ml-2 是不是可以优化下,不要整个左移,而是里面的内容有个几 px 的偏移,不顶在框里 -->
|
||||||
<ContactDetails class="ml-2" :contact="contact" />
|
<ContactDetails class="ml-2" :contact="contact" />
|
||||||
</el-tab-pane>
|
</el-tab-pane>
|
||||||
<el-tab-pane label="活动" lazy> 活动</el-tab-pane>
|
<el-tab-pane label="跟进记录" lazy> 跟进记录</el-tab-pane>
|
||||||
<el-tab-pane label="邮件" lazy> 邮件</el-tab-pane>
|
|
||||||
<el-tab-pane label="工商信息" lazy> 工商信息</el-tab-pane>
|
|
||||||
<!-- TODO wanwan 以下标签上的数量需要接口统计返回 -->
|
|
||||||
<el-tab-pane label="客户" lazy>
|
|
||||||
<template #label> 客户<el-badge :value="12" class="item" type="primary" /> </template>
|
|
||||||
客户
|
|
||||||
</el-tab-pane>
|
|
||||||
<el-tab-pane label="团队成员" lazy>
|
|
||||||
<template #label> 团队成员<el-badge :value="2" class="item" type="primary" /> </template>
|
|
||||||
团队成员
|
|
||||||
</el-tab-pane>
|
|
||||||
<el-tab-pane label="商机" lazy> 商机</el-tab-pane>
|
<el-tab-pane label="商机" lazy> 商机</el-tab-pane>
|
||||||
<el-tab-pane label="合同" lazy>
|
<el-tab-pane label="附件" lazy> 附件</el-tab-pane>
|
||||||
<template #label> 合同<el-badge :value="3" class="item" type="primary" /> </template>
|
<!-- TODO wanwan 以下标签上的数量需要接口统计返回 -->
|
||||||
合同
|
<el-tab-pane label="操作记录" lazy>
|
||||||
|
<template #label> 操作记录<el-badge :value="12" class="item" type="primary" /> </template>
|
||||||
|
操作记录
|
||||||
</el-tab-pane>
|
</el-tab-pane>
|
||||||
<el-tab-pane label="回款" lazy>
|
|
||||||
<template #label> 回款<el-badge :value="4" class="item" type="primary" /> </template>
|
|
||||||
回款
|
|
||||||
</el-tab-pane>
|
|
||||||
<el-tab-pane label="回访" lazy> 回访</el-tab-pane>
|
|
||||||
<el-tab-pane label="发票" lazy> 发票</el-tab-pane>
|
|
||||||
</el-tabs>
|
</el-tabs>
|
||||||
</el-col>
|
</el-col>
|
||||||
|
|
||||||
@ -105,10 +90,10 @@ import ContactBasicInfo from '@/views/crm/contact/detail/ContactBasicInfo.vue'
|
|||||||
import ContactDetails from '@/views/crm/contact/detail/ContactDetails.vue'
|
import ContactDetails from '@/views/crm/contact/detail/ContactDetails.vue'
|
||||||
import ContactForm from '@/views/crm/contact/ContactForm.vue'
|
import ContactForm from '@/views/crm/contact/ContactForm.vue'
|
||||||
import { formatDate } from '@/utils/formatTime'
|
import { formatDate } from '@/utils/formatTime'
|
||||||
import * as CustomerApi from '@/api/crm/customer'
|
|
||||||
// TODO 芋艿:后面在 review 么?
|
// TODO 芋艿:后面在 review 么?
|
||||||
|
|
||||||
defineOptions({ name: 'ContactDetail' })
|
defineOptions({ name: 'CrmContactDetail' })
|
||||||
|
|
||||||
const { delView } = useTagsViewStore() // 视图操作
|
const { delView } = useTagsViewStore() // 视图操作
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
const { currentRoute } = useRouter() // 路由
|
const { currentRoute } = useRouter() // 路由
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<ContentWrap>
|
<ContentWrap>
|
||||||
<!-- 搜索工作栏 -->
|
<!-- 搜索工作栏 -->
|
||||||
<!-- TODO zyna:筛选项,按照需求简化下 -->
|
|
||||||
<el-form
|
<el-form
|
||||||
class="-mb-15px"
|
class="-mb-15px"
|
||||||
:model="queryParams"
|
:model="queryParams"
|
||||||
@ -9,14 +8,22 @@
|
|||||||
:inline="true"
|
:inline="true"
|
||||||
label-width="68px"
|
label-width="68px"
|
||||||
>
|
>
|
||||||
<el-form-item label="客户编号" prop="customerId">
|
<el-form-item label="客户" prop="customerId">
|
||||||
<el-input
|
<el-select
|
||||||
v-model="queryParams.customerId"
|
v-model="queryParams.customerId"
|
||||||
placeholder="请输入客户编号"
|
placeholder="请选择客户"
|
||||||
clearable
|
value-key="id"
|
||||||
|
lable-key="name"
|
||||||
@keyup.enter="handleQuery"
|
@keyup.enter="handleQuery"
|
||||||
class="!w-240px"
|
clearable
|
||||||
|
>
|
||||||
|
<el-option
|
||||||
|
v-for="item in customerList"
|
||||||
|
:key="item.id"
|
||||||
|
:label="item.name"
|
||||||
|
:value="item.id"
|
||||||
/>
|
/>
|
||||||
|
</el-select>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="姓名" prop="name">
|
<el-form-item label="姓名" prop="name">
|
||||||
<el-input
|
<el-input
|
||||||
@ -55,9 +62,9 @@
|
|||||||
class="!w-240px"
|
class="!w-240px"
|
||||||
/>
|
/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="微信" prop="webchat">
|
<el-form-item label="微信" prop="wechat">
|
||||||
<el-input
|
<el-input
|
||||||
v-model="queryParams.webchat"
|
v-model="queryParams.wechat"
|
||||||
placeholder="请输入微信"
|
placeholder="请输入微信"
|
||||||
clearable
|
clearable
|
||||||
@keyup.enter="handleQuery"
|
@keyup.enter="handleQuery"
|
||||||
@ -97,32 +104,28 @@
|
|||||||
<el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true">
|
<el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true">
|
||||||
<el-table-column label="姓名" fixed="left" align="center" prop="name">
|
<el-table-column label="姓名" fixed="left" align="center" prop="name">
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
<el-link type="primary" :underline="false" @click="openDetail(scope.row.id)">{{
|
<el-link type="primary" :underline="false" @click="openDetail(scope.row.id)">
|
||||||
scope.row.name
|
{{ scope.row.name }}
|
||||||
}}</el-link>
|
</el-link>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column label="客户名称" fixed="left" align="center" prop="customerName" />
|
<el-table-column label="客户" fixed="left" align="center" prop="customerName" />
|
||||||
<el-table-column label="性别" align="center" prop="sex">
|
<el-table-column label="性别" align="center" prop="sex">
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
<dict-tag :type="DICT_TYPE.SYSTEM_USER_SEX" :value="scope.row.sex" />
|
<dict-tag :type="DICT_TYPE.SYSTEM_USER_SEX" :value="scope.row.sex" />
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column label="职位" align="center" prop="post" />
|
<el-table-column label="职位" align="center" prop="post" />
|
||||||
<el-table-column label="是否关键决策人" align="center" prop="policyMakers">
|
<el-table-column label="是否关键决策人" align="center" prop="master">
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
<dict-tag :type="DICT_TYPE.INFRA_BOOLEAN_STRING" :value="scope.row.policyMakers" />
|
<dict-tag :type="DICT_TYPE.INFRA_BOOLEAN_STRING" :value="scope.row.master" />
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column label="直属上级" align="center" prop="parentId">
|
|
||||||
<template #default="scope">
|
|
||||||
{{ allContactList.find((contact) => contact.id === scope.row.parentId)?.name }}
|
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
|
<el-table-column label="直属上级" align="center" prop="parentName" />
|
||||||
<el-table-column label="手机号" align="center" prop="mobile" />
|
<el-table-column label="手机号" align="center" prop="mobile" />
|
||||||
<el-table-column label="座机" align="center" prop="telephone" />
|
<el-table-column label="座机" align="center" prop="telephone" />
|
||||||
<el-table-column label="QQ" align="center" prop="qq" />
|
<el-table-column label="QQ" align="center" prop="qq" />
|
||||||
<el-table-column label="微信" align="center" prop="webchat" />
|
<el-table-column label="微信" align="center" prop="wechat" />
|
||||||
<el-table-column label="邮箱" align="center" prop="email" />
|
<el-table-column label="邮箱" align="center" prop="email" />
|
||||||
<el-table-column label="地址" align="center" prop="address" />
|
<el-table-column label="地址" align="center" prop="address" />
|
||||||
<el-table-column
|
<el-table-column
|
||||||
@ -142,7 +145,7 @@
|
|||||||
/>
|
/>
|
||||||
<el-table-column label="负责人" align="center" prop="ownerUserId">
|
<el-table-column label="负责人" align="center" prop="ownerUserId">
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
{{ gotOwnerUser(scope.row.ownerUserId) }}
|
{{ scope.row.ownerUserName }}
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<!-- <el-table-column label="所属部门" align="center" prop="ownerUserId" /> -->
|
<!-- <el-table-column label="所属部门" align="center" prop="ownerUserId" /> -->
|
||||||
@ -211,7 +214,6 @@ import download from '@/utils/download'
|
|||||||
import * as ContactApi from '@/api/crm/contact'
|
import * as ContactApi from '@/api/crm/contact'
|
||||||
import ContactForm from './ContactForm.vue'
|
import ContactForm from './ContactForm.vue'
|
||||||
import { DICT_TYPE } from '@/utils/dict'
|
import { DICT_TYPE } from '@/utils/dict'
|
||||||
import * as UserApi from '@/api/system/user'
|
|
||||||
import * as CustomerApi from '@/api/crm/customer'
|
import * as CustomerApi from '@/api/crm/customer'
|
||||||
|
|
||||||
defineOptions({ name: 'CrmContact' })
|
defineOptions({ name: 'CrmContact' })
|
||||||
@ -222,6 +224,7 @@ const { t } = useI18n() // 国际化
|
|||||||
const loading = ref(true) // 列表的加载中
|
const loading = ref(true) // 列表的加载中
|
||||||
const total = ref(0) // 列表的总页数
|
const total = ref(0) // 列表的总页数
|
||||||
const list = ref([]) // 列表的数据
|
const list = ref([]) // 列表的数据
|
||||||
|
const customerList = ref<CustomerApi.CustomerVO[]>([]) // 客户列表
|
||||||
const queryParams = reactive({
|
const queryParams = reactive({
|
||||||
pageNo: 1,
|
pageNo: 1,
|
||||||
pageSize: 10,
|
pageSize: 10,
|
||||||
@ -239,13 +242,12 @@ const queryParams = reactive({
|
|||||||
name: null,
|
name: null,
|
||||||
post: null,
|
post: null,
|
||||||
qq: null,
|
qq: null,
|
||||||
webchat: null,
|
wechat: null,
|
||||||
sex: null,
|
sex: null,
|
||||||
policyMakers: null
|
policyMakers: null
|
||||||
})
|
})
|
||||||
const queryFormRef = ref() // 搜索的表单
|
const queryFormRef = ref() // 搜索的表单
|
||||||
const exportLoading = ref(false) // 导出的加载中
|
const exportLoading = ref(false) // 导出的加载中
|
||||||
const userList = ref<UserApi.UserVO[]>([]) // 用户列表
|
|
||||||
|
|
||||||
/** 查询列表 */
|
/** 查询列表 */
|
||||||
const getList = async () => {
|
const getList = async () => {
|
||||||
@ -305,35 +307,15 @@ const handleExport = async () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO @zyna:这个负责人的读取,放在后端好点
|
|
||||||
const gotOwnerUser = (owerUserId: string) => {
|
|
||||||
let ownerName = ''
|
|
||||||
if (owerUserId !== null) {
|
|
||||||
owerUserId.split(',').forEach((item: string, index: number) => {
|
|
||||||
if (index != 0) {
|
|
||||||
ownerName =
|
|
||||||
ownerName + ',' + userList.value.find((user: { id: any }) => user.id == item)?.nickname
|
|
||||||
} else {
|
|
||||||
ownerName = userList.value.find((user: { id: any }) => user.id == item)?.nickname || ''
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
return ownerName
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 打开客户详情 */
|
/** 打开客户详情 */
|
||||||
const { push } = useRouter()
|
const { push } = useRouter()
|
||||||
const openDetail = (id: number) => {
|
const openDetail = (id: number) => {
|
||||||
push({ name: 'CrmContactDetail', params: { id } })
|
push({ name: 'CrmContactDetail', params: { id } })
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO @zyna:这个上级的读取,放在后端读取,更合适;因为可能数据量比较大
|
|
||||||
const allContactList = ref([]) //所有联系人列表
|
|
||||||
const allCustomerList = ref([]) //客户列表
|
|
||||||
/** 初始化 **/
|
/** 初始化 **/
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
await getList()
|
await getList()
|
||||||
userList.value = await UserApi.getSimpleUserList()
|
customerList.value = await CustomerApi.queryAllList()
|
||||||
allContactList.value = await ContactApi.simpleAlllist()
|
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
132
src/views/crm/contract/components/ContractList.vue
Normal file
132
src/views/crm/contract/components/ContractList.vue
Normal file
@ -0,0 +1,132 @@
|
|||||||
|
<template>
|
||||||
|
<!-- 操作栏 -->
|
||||||
|
<el-row justify="end">
|
||||||
|
<el-button @click="openForm">
|
||||||
|
<Icon class="mr-5px" icon="clarity:contract-line" />
|
||||||
|
创建合同
|
||||||
|
</el-button>
|
||||||
|
</el-row>
|
||||||
|
|
||||||
|
<!-- 列表 -->
|
||||||
|
<ContentWrap class="mt-10px">
|
||||||
|
<el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true">
|
||||||
|
<el-table-column label="合同名称" fixed="left" align="center" prop="name">
|
||||||
|
<template #default="scope">
|
||||||
|
<el-link type="primary" :underline="false" @click="openDetail(scope.row.id)">
|
||||||
|
{{ scope.row.name }}
|
||||||
|
</el-link>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="合同编号" align="center" prop="no" />
|
||||||
|
<el-table-column label="客户名称" align="center" prop="customerName" />
|
||||||
|
<el-table-column
|
||||||
|
label="合同金额(元)"
|
||||||
|
align="center"
|
||||||
|
prop="price"
|
||||||
|
:formatter="fenToYuanFormat"
|
||||||
|
/>
|
||||||
|
<el-table-column
|
||||||
|
label="开始时间"
|
||||||
|
align="center"
|
||||||
|
prop="startTime"
|
||||||
|
:formatter="dateFormatter"
|
||||||
|
width="180px"
|
||||||
|
/>
|
||||||
|
<el-table-column
|
||||||
|
label="结束时间"
|
||||||
|
align="center"
|
||||||
|
prop="endTime"
|
||||||
|
:formatter="dateFormatter"
|
||||||
|
width="180px"
|
||||||
|
/>
|
||||||
|
<el-table-column align="center" label="状态" prop="auditStatus">
|
||||||
|
<template #default="scope">
|
||||||
|
<dict-tag :type="DICT_TYPE.CRM_AUDIT_STATUS" :value="scope.row.auditStatus" />
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
<!-- 分页 -->
|
||||||
|
<Pagination
|
||||||
|
:total="total"
|
||||||
|
v-model:page="queryParams.pageNo"
|
||||||
|
v-model:limit="queryParams.pageSize"
|
||||||
|
@pagination="getList"
|
||||||
|
/>
|
||||||
|
</ContentWrap>
|
||||||
|
|
||||||
|
<!-- 表单弹窗:添加 -->
|
||||||
|
<ContractForm ref="formRef" @success="getList" />
|
||||||
|
</template>
|
||||||
|
<script setup lang="ts">
|
||||||
|
import * as ContractApi from '@/api/crm/contract'
|
||||||
|
import ContractForm from './../ContractForm.vue'
|
||||||
|
import { BizTypeEnum } from '@/api/crm/permission'
|
||||||
|
import { fenToYuanFormat } from '@/utils/formatter'
|
||||||
|
import { dateFormatter } from '@/utils/formatTime'
|
||||||
|
import { DICT_TYPE } from '@/utils/dict'
|
||||||
|
|
||||||
|
defineOptions({ name: 'CrmContractList' })
|
||||||
|
const props = defineProps<{
|
||||||
|
bizType: number // 业务类型
|
||||||
|
bizId: number // 业务编号
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const loading = ref(true) // 列表的加载中
|
||||||
|
const total = ref(0) // 列表的总页数
|
||||||
|
const list = ref([]) // 列表的数据
|
||||||
|
const queryParams = reactive({
|
||||||
|
pageNo: 1,
|
||||||
|
pageSize: 10,
|
||||||
|
customerId: undefined as unknown // 允许 undefined + number
|
||||||
|
})
|
||||||
|
|
||||||
|
/** 查询列表 */
|
||||||
|
const getList = async () => {
|
||||||
|
loading.value = true
|
||||||
|
try {
|
||||||
|
// 置空参数
|
||||||
|
queryParams.customerId = undefined
|
||||||
|
// 执行查询
|
||||||
|
let data = { list: [], total: 0 }
|
||||||
|
switch (props.bizType) {
|
||||||
|
case BizTypeEnum.CRM_CUSTOMER:
|
||||||
|
queryParams.customerId = props.bizId
|
||||||
|
data = await ContractApi.getContractPageByCustomer(queryParams)
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
return
|
||||||
|
}
|
||||||
|
list.value = data.list
|
||||||
|
total.value = data.total
|
||||||
|
} finally {
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 搜索按钮操作 */
|
||||||
|
const handleQuery = () => {
|
||||||
|
queryParams.pageNo = 1
|
||||||
|
getList()
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 添加 */
|
||||||
|
const formRef = ref()
|
||||||
|
const openForm = () => {
|
||||||
|
formRef.value.open('create')
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 打开合同详情 */
|
||||||
|
const { push } = useRouter()
|
||||||
|
const openDetail = (id: number) => {
|
||||||
|
push({ name: 'CrmContractDetail', params: { id } })
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 监听打开的 bizId + bizType,从而加载最新的列表 */
|
||||||
|
watch(
|
||||||
|
() => [props.bizId, props.bizType],
|
||||||
|
() => {
|
||||||
|
handleQuery()
|
||||||
|
},
|
||||||
|
{ immediate: true, deep: true }
|
||||||
|
)
|
||||||
|
</script>
|
@ -1,228 +0,0 @@
|
|||||||
import type { CrudSchema } from '@/hooks/web/useCrudSchemas'
|
|
||||||
import { dateFormatter } from '@/utils/formatTime'
|
|
||||||
|
|
||||||
// 表单校验
|
|
||||||
export const rules = reactive({
|
|
||||||
name: [required]
|
|
||||||
})
|
|
||||||
|
|
||||||
// TODO @dbh52:不使用 crud 模式哈,使用标准的 ep 代码哈;主要后续 crud schema 可能会改
|
|
||||||
// CrudSchema https://doc.iocoder.cn/vue3/crud-schema/
|
|
||||||
const crudSchemas = reactive<CrudSchema[]>([
|
|
||||||
{
|
|
||||||
label: '合同编号',
|
|
||||||
field: 'id',
|
|
||||||
isForm: false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: '合同名称',
|
|
||||||
field: 'name',
|
|
||||||
isSearch: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: '客户编号',
|
|
||||||
field: 'customerId',
|
|
||||||
isSearch: true,
|
|
||||||
form: {
|
|
||||||
component: 'InputNumber',
|
|
||||||
value: 0
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: '商机编号',
|
|
||||||
field: 'businessId',
|
|
||||||
isSearch: true,
|
|
||||||
form: {
|
|
||||||
component: 'InputNumber',
|
|
||||||
value: 0
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: '工作流编号',
|
|
||||||
field: 'processInstanceId',
|
|
||||||
isSearch: true,
|
|
||||||
form: {
|
|
||||||
component: 'InputNumber',
|
|
||||||
value: 0
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: '下单日期',
|
|
||||||
field: 'orderDate',
|
|
||||||
formatter: dateFormatter,
|
|
||||||
isSearch: true,
|
|
||||||
search: {
|
|
||||||
component: 'DatePicker',
|
|
||||||
componentProps: {
|
|
||||||
valueFormat: 'YYYY-MM-DD HH:mm:ss',
|
|
||||||
type: 'daterange',
|
|
||||||
defaultTime: [new Date('1 00:00:00'), new Date('1 23:59:59')]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
form: {
|
|
||||||
component: 'DatePicker',
|
|
||||||
componentProps: {
|
|
||||||
type: 'datetime',
|
|
||||||
valueFormat: 'x'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: '负责人的用户编号',
|
|
||||||
field: 'ownerUserId',
|
|
||||||
isSearch: true,
|
|
||||||
form: {
|
|
||||||
component: 'InputNumber',
|
|
||||||
value: 0
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: '创建时间',
|
|
||||||
field: 'createTime',
|
|
||||||
formatter: dateFormatter,
|
|
||||||
isSearch: true,
|
|
||||||
search: {
|
|
||||||
component: 'DatePicker',
|
|
||||||
componentProps: {
|
|
||||||
valueFormat: 'YYYY-MM-DD HH:mm:ss',
|
|
||||||
type: 'daterange',
|
|
||||||
defaultTime: [new Date('1 00:00:00'), new Date('1 23:59:59')]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
isForm: false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: '合同编号',
|
|
||||||
field: 'no',
|
|
||||||
isSearch: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: '开始时间',
|
|
||||||
field: 'startTime',
|
|
||||||
formatter: dateFormatter,
|
|
||||||
isSearch: true,
|
|
||||||
search: {
|
|
||||||
component: 'DatePicker',
|
|
||||||
componentProps: {
|
|
||||||
valueFormat: 'YYYY-MM-DD HH:mm:ss',
|
|
||||||
type: 'daterange',
|
|
||||||
defaultTime: [new Date('1 00:00:00'), new Date('1 23:59:59')]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
form: {
|
|
||||||
component: 'DatePicker',
|
|
||||||
componentProps: {
|
|
||||||
type: 'datetime',
|
|
||||||
valueFormat: 'x'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: '结束时间',
|
|
||||||
field: 'endTime',
|
|
||||||
formatter: dateFormatter,
|
|
||||||
isSearch: true,
|
|
||||||
search: {
|
|
||||||
component: 'DatePicker',
|
|
||||||
componentProps: {
|
|
||||||
valueFormat: 'YYYY-MM-DD HH:mm:ss',
|
|
||||||
type: 'daterange',
|
|
||||||
defaultTime: [new Date('1 00:00:00'), new Date('1 23:59:59')]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
form: {
|
|
||||||
component: 'DatePicker',
|
|
||||||
componentProps: {
|
|
||||||
type: 'datetime',
|
|
||||||
valueFormat: 'x'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: '合同金额',
|
|
||||||
field: 'price',
|
|
||||||
isSearch: true,
|
|
||||||
form: {
|
|
||||||
component: 'InputNumber',
|
|
||||||
value: 0
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: '整单折扣',
|
|
||||||
field: 'discountPercent',
|
|
||||||
isSearch: true,
|
|
||||||
form: {
|
|
||||||
component: 'InputNumber',
|
|
||||||
value: 0
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: '产品总金额',
|
|
||||||
field: 'productPrice',
|
|
||||||
isSearch: true,
|
|
||||||
form: {
|
|
||||||
component: 'InputNumber',
|
|
||||||
value: 0
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: '只读权限的用户编号数组',
|
|
||||||
field: 'roUserIds',
|
|
||||||
isSearch: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: '读写权限的用户编号数组',
|
|
||||||
field: 'rwUserIds',
|
|
||||||
isSearch: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: '联系人编号',
|
|
||||||
field: 'contactId',
|
|
||||||
isSearch: true,
|
|
||||||
form: {
|
|
||||||
component: 'InputNumber',
|
|
||||||
value: 0
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: '备注',
|
|
||||||
field: 'remark',
|
|
||||||
isSearch: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: '公司签约人',
|
|
||||||
field: 'signUserId',
|
|
||||||
isSearch: true,
|
|
||||||
form: {
|
|
||||||
component: 'InputNumber',
|
|
||||||
value: 0
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: '最后跟进时间',
|
|
||||||
field: 'contactLastTime',
|
|
||||||
formatter: dateFormatter,
|
|
||||||
isSearch: true,
|
|
||||||
search: {
|
|
||||||
component: 'DatePicker',
|
|
||||||
componentProps: {
|
|
||||||
valueFormat: 'YYYY-MM-DD HH:mm:ss',
|
|
||||||
type: 'daterange',
|
|
||||||
defaultTime: [new Date('1 00:00:00'), new Date('1 23:59:59')]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
form: {
|
|
||||||
component: 'DatePicker',
|
|
||||||
componentProps: {
|
|
||||||
type: 'datetime',
|
|
||||||
valueFormat: 'x'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: '操作',
|
|
||||||
field: 'action',
|
|
||||||
isForm: false
|
|
||||||
}
|
|
||||||
])
|
|
||||||
export const { allSchemas } = useCrudSchemas(crudSchemas)
|
|
@ -8,44 +8,6 @@
|
|||||||
:inline="true"
|
:inline="true"
|
||||||
label-width="68px"
|
label-width="68px"
|
||||||
>
|
>
|
||||||
<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="customerId">
|
|
||||||
<el-input
|
|
||||||
v-model="queryParams.customerId"
|
|
||||||
placeholder="请输入客户编号"
|
|
||||||
clearable
|
|
||||||
@keyup.enter="handleQuery"
|
|
||||||
class="!w-240px"
|
|
||||||
/>
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="商机编号" prop="businessId">
|
|
||||||
<el-input
|
|
||||||
v-model="queryParams.businessId"
|
|
||||||
placeholder="请输入商机编号"
|
|
||||||
clearable
|
|
||||||
@keyup.enter="handleQuery"
|
|
||||||
class="!w-240px"
|
|
||||||
/>
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="下单日期" prop="orderDate">
|
|
||||||
<el-date-picker
|
|
||||||
v-model="queryParams.orderDate"
|
|
||||||
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-240px"
|
|
||||||
/>
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="合同编号" prop="no">
|
<el-form-item label="合同编号" prop="no">
|
||||||
<el-input
|
<el-input
|
||||||
v-model="queryParams.no"
|
v-model="queryParams.no"
|
||||||
@ -55,6 +17,15 @@
|
|||||||
class="!w-240px"
|
class="!w-240px"
|
||||||
/>
|
/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
<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>
|
<el-form-item>
|
||||||
<el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button>
|
<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 @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button>
|
||||||
@ -75,6 +46,7 @@
|
|||||||
</ContentWrap>
|
</ContentWrap>
|
||||||
|
|
||||||
<!-- 列表 -->
|
<!-- 列表 -->
|
||||||
|
<!-- TODO 芋艿:各种字段要调整 -->
|
||||||
<ContentWrap>
|
<ContentWrap>
|
||||||
<el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true">
|
<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="id" />
|
||||||
@ -125,7 +97,6 @@
|
|||||||
width="180px"
|
width="180px"
|
||||||
/>
|
/>
|
||||||
<el-table-column label="备注" align="center" prop="remark" />
|
<el-table-column label="备注" align="center" prop="remark" />
|
||||||
|
|
||||||
<el-table-column label="操作" width="120px">
|
<el-table-column label="操作" width="120px">
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
<el-button
|
<el-button
|
||||||
@ -159,7 +130,6 @@
|
|||||||
<!-- 表单弹窗:添加/修改 -->
|
<!-- 表单弹窗:添加/修改 -->
|
||||||
<ContractForm ref="formRef" @success="getList" />
|
<ContractForm ref="formRef" @success="getList" />
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { dateFormatter } from '@/utils/formatTime'
|
import { dateFormatter } from '@/utils/formatTime'
|
||||||
import download from '@/utils/download'
|
import download from '@/utils/download'
|
||||||
|
@ -1,16 +0,0 @@
|
|||||||
<template>
|
|
||||||
<el-col>
|
|
||||||
<el-row>
|
|
||||||
<span class="text-xl font-bold">{{ customer.name }}</span>
|
|
||||||
</el-row>
|
|
||||||
</el-col>
|
|
||||||
<el-col class="mt-10px">
|
|
||||||
<!-- TODO 标签 -->
|
|
||||||
<!-- <Icon icon="ant-design:tag-filled" />-->
|
|
||||||
</el-col>
|
|
||||||
</template>
|
|
||||||
<script setup lang="ts">
|
|
||||||
import * as CustomerApi from '@/api/crm/customer'
|
|
||||||
|
|
||||||
const { customer } = defineProps<{ customer: CustomerApi.CustomerVO }>()
|
|
||||||
</script>
|
|
57
src/views/crm/customer/detail/CustomerDetailsHeader.vue
Normal file
57
src/views/crm/customer/detail/CustomerDetailsHeader.vue
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
<template>
|
||||||
|
<div v-loading="loading">
|
||||||
|
<div class="flex items-start justify-between">
|
||||||
|
<div>
|
||||||
|
<!-- 左上:客户基本信息 -->
|
||||||
|
<el-col>
|
||||||
|
<el-row>
|
||||||
|
<span class="text-xl font-bold">{{ customer.name }}</span>
|
||||||
|
</el-row>
|
||||||
|
</el-col>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<!-- 右上:按钮 -->
|
||||||
|
<el-button v-hasPermi="['crm:customer:update']" @click="openForm(customer.id)">
|
||||||
|
编辑
|
||||||
|
</el-button>
|
||||||
|
<el-button>更改成交状态</el-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<ContentWrap class="mt-10px">
|
||||||
|
<el-descriptions :column="5" direction="vertical">
|
||||||
|
<el-descriptions-item label="客户级别">
|
||||||
|
<dict-tag :type="DICT_TYPE.CRM_CUSTOMER_LEVEL" :value="customer.level" />
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="成交状态">
|
||||||
|
{{ customer.dealStatus ? '已成交' : '未成交' }}
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="负责人">{{ customer.ownerUserName }} </el-descriptions-item>
|
||||||
|
<!-- TODO wanwan 首要联系人? -->
|
||||||
|
<el-descriptions-item label="首要联系人" />
|
||||||
|
<!-- TODO wanwan 首要联系人电话? -->
|
||||||
|
<el-descriptions-item label="首要联系人电话">{{ customer.mobile }} </el-descriptions-item>
|
||||||
|
</el-descriptions>
|
||||||
|
</ContentWrap>
|
||||||
|
|
||||||
|
<!-- 表单弹窗:添加/修改 -->
|
||||||
|
<CustomerForm ref="formRef" @success="emit('refresh')" />
|
||||||
|
</template>
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { DICT_TYPE } from '@/utils/dict'
|
||||||
|
import * as CustomerApi from '@/api/crm/customer'
|
||||||
|
import CustomerForm from '../CustomerForm.vue'
|
||||||
|
|
||||||
|
const { customer, loading } = defineProps<{
|
||||||
|
customer: CustomerApi.CustomerVO // 客户信息
|
||||||
|
loading: boolean // 加载中
|
||||||
|
}>()
|
||||||
|
|
||||||
|
/** 修改操作 */
|
||||||
|
const formRef = ref()
|
||||||
|
const openForm = (id?: number) => {
|
||||||
|
formRef.value.open('update', id)
|
||||||
|
}
|
||||||
|
|
||||||
|
const emit = defineEmits(['refresh']) // 定义 success 事件,用于操作成功后的回调
|
||||||
|
</script>
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user