Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
B
baifang-java
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
单欣鑫
baifang-java
Commits
677dd111
Commit
677dd111
authored
Jun 10, 2025
by
法拉51246
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
增加每个页面批量删除
增加查看详情 增加多选合并打印和全选合并打印 增加过滤角色权限分配,低权限不能分配高权限
parent
5a69a12f
Changes
26
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
26 changed files
with
543 additions
and
275 deletions
+543
-275
OAuth2TokenServiceImpl.java
.../module/system/service/oauth2/OAuth2TokenServiceImpl.java
+1
-1
RoleServiceImpl.java
...dao/module/system/service/permission/RoleServiceImpl.java
+14
-1
CustomerInfoController.java
...controller/admin/customerinfo/CustomerInfoController.java
+21
-0
CustomerInfoSaveReqVO.java
...ntroller/admin/customerinfo/vo/CustomerInfoSaveReqVO.java
+3
-0
InfoController.java
...ao/module/visit/controller/admin/info/InfoController.java
+54
-0
InfoPrintVO.java
...ao/module/visit/controller/admin/info/vo/InfoPrintVO.java
+2
-1
InfoSaveReqVO.java
.../module/visit/controller/admin/info/vo/InfoSaveReqVO.java
+9
-2
ProductController.java
...ule/visit/controller/admin/product/ProductController.java
+21
-0
ErrorCodeConstants.java
.../iocoder/yudao/module/visit/enums/ErrorCodeConstants.java
+3
-0
CustomerInfoService.java
...odule/visit/service/customerinfo/CustomerInfoService.java
+2
-0
CustomerInfoServiceImpl.java
...e/visit/service/customerinfo/CustomerInfoServiceImpl.java
+10
-0
InfoService.java
.../iocoder/yudao/module/visit/service/info/InfoService.java
+6
-0
InfoServiceImpl.java
...oder/yudao/module/visit/service/info/InfoServiceImpl.java
+167
-4
ProductService.java
...er/yudao/module/visit/service/product/ProductService.java
+2
-0
ProductServiceImpl.java
...udao/module/visit/service/product/ProductServiceImpl.java
+12
-0
index.ts
...i/yudao-ui-admin-vue3/src/api/visit/customerinfo/index.ts
+5
-0
index.ts
yudao-ui/yudao-ui-admin-vue3/src/api/visit/info/index.ts
+15
-0
index.ts
yudao-ui/yudao-ui-admin-vue3/src/api/visit/product/index.ts
+6
-0
UploadImgs.vue
...i-admin-vue3/src/components/UploadFile/src/UploadImgs.vue
+6
-6
CustomerInfoForm.vue
...in-vue3/src/views/visit/customerinfo/CustomerInfoForm.vue
+11
-6
index.vue
...udao-ui-admin-vue3/src/views/visit/customerinfo/index.vue
+34
-3
InfoForm.vue
...-ui/yudao-ui-admin-vue3/src/views/visit/info/InfoForm.vue
+39
-18
index.vue
yudao-ui/yudao-ui-admin-vue3/src/views/visit/info/index.vue
+74
-12
index.vue
...-ui/yudao-ui-admin-vue3/src/views/visit/product/index.vue
+24
-0
PrintContent.vue
...yudao-ui-admin-vue3/src/views/visit/util/PrintContent.vue
+2
-2
VisitPrint.vue
...i/yudao-ui-admin-vue3/src/views/visit/util/VisitPrint.vue
+0
-219
No files found.
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2TokenServiceImpl.java
View file @
677dd111
...
...
@@ -204,7 +204,7 @@ public class OAuth2TokenServiceImpl implements OAuth2TokenService {
private
Map
<
String
,
String
>
buildUserInfo
(
Long
userId
,
Integer
userType
)
{
if
(
userType
.
equals
(
UserTypeEnum
.
ADMIN
.
getValue
()))
{
AdminUserDO
user
=
adminUserService
.
getUser
(
userId
);
Long
branchDeptId
=
findBranchDeptId
(
user
.
getDeptId
(),
100L
);
//找到分公司Id,这里的100L是总公司的ID,未来某一天如果有变,就改这里
Long
branchDeptId
=
findBranchDeptId
(
user
.
getDeptId
(),
100L
);
//找到分公司Id,这里的100L是总公司的ID,未来某一天如果有变,就改这里
锚点
return
MapUtil
.
builder
(
LoginUser
.
INFO_KEY_NICKNAME
,
user
.
getNickname
())
.
put
(
LoginUser
.
INFO_KEY_DEPT_ID
,
StrUtil
.
toStringOrNull
(
branchDeptId
)).
build
();
}
else
if
(
userType
.
equals
(
UserTypeEnum
.
MEMBER
.
getValue
()))
{
...
...
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/permission/RoleServiceImpl.java
View file @
677dd111
...
...
@@ -9,10 +9,13 @@ import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import
cn.iocoder.yudao.framework.common.pojo.PageResult
;
import
cn.iocoder.yudao.framework.common.util.collection.CollectionUtils
;
import
cn.iocoder.yudao.framework.common.util.object.BeanUtils
;
import
cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils
;
import
cn.iocoder.yudao.module.system.controller.admin.permission.vo.role.RolePageReqVO
;
import
cn.iocoder.yudao.module.system.controller.admin.permission.vo.role.RoleSaveReqVO
;
import
cn.iocoder.yudao.module.system.dal.dataobject.permission.RoleDO
;
import
cn.iocoder.yudao.module.system.dal.dataobject.permission.UserRoleDO
;
import
cn.iocoder.yudao.module.system.dal.mysql.permission.RoleMapper
;
import
cn.iocoder.yudao.module.system.dal.mysql.permission.UserRoleMapper
;
import
cn.iocoder.yudao.module.system.dal.redis.RedisKeyConstants
;
import
cn.iocoder.yudao.module.system.enums.permission.DataScopeEnum
;
import
cn.iocoder.yudao.module.system.enums.permission.RoleCodeEnum
;
...
...
@@ -30,6 +33,7 @@ import org.springframework.util.StringUtils;
import
javax.annotation.Resource
;
import
java.util.*
;
import
java.util.stream.Collectors
;
import
static
cn
.
iocoder
.
yudao
.
framework
.
common
.
exception
.
util
.
ServiceExceptionUtil
.
exception
;
import
static
cn
.
iocoder
.
yudao
.
framework
.
common
.
util
.
collection
.
CollectionUtils
.
convertMap
;
...
...
@@ -50,6 +54,8 @@ public class RoleServiceImpl implements RoleService {
@Resource
private
RoleMapper
roleMapper
;
@Resource
private
UserRoleMapper
userRoleMapper
;
@Override
@Transactional
(
rollbackFor
=
Exception
.
class
)
...
...
@@ -187,7 +193,14 @@ public class RoleServiceImpl implements RoleService {
@Override
public
List
<
RoleDO
>
getRoleListByStatus
(
Collection
<
Integer
>
statuses
)
{
return
roleMapper
.
selectListByStatus
(
statuses
);
List
<
RoleDO
>
roleDOS
=
roleMapper
.
selectListByStatus
(
statuses
);
//判断当前用户是不是超级管理员,如果不是,需要过滤掉超级管理员
List
<
UserRoleDO
>
userRoleDOS
=
userRoleMapper
.
selectListByUserId
(
SecurityFrameworkUtils
.
getLoginUserId
());
// 如果userRoleDOS中没有roleId=1的记录,则过滤掉roleDOS中id=1的记录
roleDOS
=
userRoleDOS
.
stream
().
noneMatch
(
userRole
->
userRole
.
getRoleId
()
==
1L
)
?
roleDOS
.
stream
().
filter
(
role
->
role
.
getId
()
!=
1L
).
collect
(
Collectors
.
toList
())
:
roleDOS
;
return
roleDOS
;
}
@Override
...
...
yudao-module-visit/src/main/java/cn/iocoder/yudao/module/visit/controller/admin/customerinfo/CustomerInfoController.java
View file @
677dd111
...
...
@@ -76,6 +76,27 @@ public class CustomerInfoController {
return
success
(
true
);
}
@DeleteMapping
(
"/deleteByIds"
)
@Operation
(
summary
=
"批量删除客户信息"
)
@Parameter
(
name
=
"ids"
,
description
=
"编号"
,
required
=
true
)
@PreAuthorize
(
"@ss.hasPermission('visit:customer-info:delete')"
)
@Idempotent
(
timeout
=
10
,
message
=
"重复请求,请稍后重试"
)
public
CommonResult
<
Boolean
>
deleteCustomerInfoByIds
(
@RequestParam
(
"ids"
)
String
ids
)
{
//如果ids是空的,返回异常提示,请先选择需要打印的记录
if
(
StringUtil
.
isEmpty
(
ids
))
{
throw
new
RuntimeException
(
"请先选择需要删除的记录"
);
}
String
[]
split
=
ids
.
split
(
","
);
//转为List
List
<
Long
>
idList
=
new
ArrayList
<>();
for
(
String
s
:
split
)
{
idList
.
add
(
Long
.
parseLong
(
s
));
}
customerInfoService
.
deleteCustomerInfoByIds
(
idList
);
return
success
(
true
);
}
@GetMapping
(
"/get"
)
@Operation
(
summary
=
"获得客户信息"
)
@Parameter
(
name
=
"id"
,
description
=
"编号"
,
required
=
true
,
example
=
"1024"
)
...
...
yudao-module-visit/src/main/java/cn/iocoder/yudao/module/visit/controller/admin/customerinfo/vo/CustomerInfoSaveReqVO.java
View file @
677dd111
...
...
@@ -60,12 +60,15 @@ public class CustomerInfoSaveReqVO {
private
String
locationImage
;
@Schema
(
description
=
"产品信息"
)
@NotEmpty
(
message
=
"产品信息不能为空"
)
private
String
productIds
;
@Schema
(
description
=
"产品信息名称"
)
@NotEmpty
(
message
=
"产品信息名称不能为空"
)
private
String
productNames
;
@Schema
(
description
=
"客户部门"
)
@NotEmpty
(
message
=
"客户部门不能为空"
)
private
String
department
;
}
\ No newline at end of file
yudao-module-visit/src/main/java/cn/iocoder/yudao/module/visit/controller/admin/info/InfoController.java
View file @
677dd111
...
...
@@ -67,6 +67,26 @@ public class InfoController {
return
success
(
true
);
}
@DeleteMapping
(
"/deleteByIds"
)
@Operation
(
summary
=
"删除客户拜访记录"
)
@Parameter
(
name
=
"ids"
,
description
=
"编号"
,
required
=
true
)
@PreAuthorize
(
"@ss.hasPermission('visit:info:delete')"
)
@Idempotent
(
timeout
=
10
,
message
=
"重复请求,请稍后重试"
)
public
CommonResult
<
Boolean
>
deleteInfoByIds
(
@RequestParam
(
"ids"
)
String
ids
)
{
//如果ids是空的,返回异常提示,请先选择需要打印的记录
if
(
StringUtil
.
isEmpty
(
ids
))
{
throw
new
RuntimeException
(
"请先选择需要删除的记录"
);
}
String
[]
split
=
ids
.
split
(
","
);
//转为List
List
<
Long
>
idList
=
new
ArrayList
<>();
for
(
String
s
:
split
)
{
idList
.
add
(
Long
.
parseLong
(
s
));
}
infoService
.
deleteInfoByIds
(
idList
);
return
success
(
true
);
}
@GetMapping
(
"/get"
)
@Operation
(
summary
=
"获得客户拜访记录"
)
@Parameter
(
name
=
"id"
,
description
=
"编号"
,
required
=
true
,
example
=
"1024"
)
...
...
@@ -78,6 +98,7 @@ public class InfoController {
@GetMapping
(
"/getPrintListByIds"
)
@Operation
(
summary
=
"获得客户拜访打印信息根据Ids"
)
@PreAuthorize
(
"@ss.hasPermission('visit:info:print')"
)
public
CommonResult
<
List
<
InfoPrintVO
>>
getPrintListByIds
(
@RequestParam
(
"ids"
)
String
ids
)
{
//如果ids是空的,返回异常提示,请先选择需要打印的记录
if
(
StringUtil
.
isEmpty
(
ids
))
{
...
...
@@ -93,8 +114,41 @@ public class InfoController {
return
success
(
info
);
}
@GetMapping
(
"/getUnionPrintListByIds"
)
@Operation
(
summary
=
"获得合并客户拜访打印信息根据Ids"
)
@PreAuthorize
(
"@ss.hasPermission('visit:info:print')"
)
public
CommonResult
<
List
<
InfoPrintVO
>>
getUnionPrintListByIds
(
@RequestParam
(
"ids"
)
String
ids
)
{
//如果ids是空的,返回异常提示,请先选择需要打印的记录
if
(
StringUtil
.
isEmpty
(
ids
))
{
throw
new
RuntimeException
(
"请先选择需要合并的打印的记录"
);
}
String
[]
split
=
ids
.
split
(
","
);
//转为List
List
<
Long
>
idList
=
new
ArrayList
<>();
for
(
String
s
:
split
)
{
idList
.
add
(
Long
.
parseLong
(
s
));
}
List
<
InfoPrintVO
>
info
=
infoService
.
getUnionInfoByIds
(
idList
);
return
success
(
info
);
}
@GetMapping
(
"/getUnionAllPrintListByIds"
)
@Operation
(
summary
=
"获得合并客户拜访打印信息根据Ids"
)
@PreAuthorize
(
"@ss.hasPermission('visit:info:print')"
)
public
CommonResult
<
List
<
InfoPrintVO
>>
getUnionAllPrintListByIds
(
@Valid
InfoPageReqVO
pageReqVO
)
{
PageResult
<
InfoDO
>
pageResult
=
infoService
.
getInfoPage
(
pageReqVO
);
if
(
pageResult
.
getTotal
()==
0
){
throw
new
RuntimeException
(
"当前不存在可打印数据"
);
}
List
<
InfoDO
>
list
=
pageResult
.
getList
();
List
<
InfoPrintVO
>
info
=
infoService
.
getUnionAllInfoByIds
(
list
);
return
success
(
info
);
}
@GetMapping
(
"/getPrintListByCompanyName"
)
@Operation
(
summary
=
"获得客户拜访打印信息根据公司名称"
)
@PreAuthorize
(
"@ss.hasPermission('visit:customer-info:print')"
)
public
CommonResult
<
List
<
InfoPrintVO
>>
getPrintListByCompanyName
(
@RequestParam
(
"companyName"
)
String
companyName
)
{
InfoPrintVO
info
=
infoService
.
getInfoByCompanyName
(
companyName
);
List
<
InfoPrintVO
>
infoPrintVO
=
new
ArrayList
<>();
...
...
yudao-module-visit/src/main/java/cn/iocoder/yudao/module/visit/controller/admin/info/vo/InfoPrintVO.java
View file @
677dd111
...
...
@@ -10,6 +10,7 @@ import lombok.Data;
import
lombok.NoArgsConstructor
;
import
java.math.BigDecimal
;
import
java.time.LocalDate
;
import
java.time.LocalDateTime
;
import
java.util.List
;
...
...
@@ -32,7 +33,7 @@ public class InfoPrintVO {
private
String
locationImage
;
@Schema
(
description
=
"最近四次拜访日期"
,
requiredMode
=
Schema
.
RequiredMode
.
REQUIRED
)
private
List
<
LocalDate
Time
>
visitDate
;
private
List
<
LocalDate
>
visitDate
;
@Schema
(
description
=
"拜访品种"
)
private
String
visitProductNames
;
...
...
yudao-module-visit/src/main/java/cn/iocoder/yudao/module/visit/controller/admin/info/vo/InfoSaveReqVO.java
View file @
677dd111
...
...
@@ -17,7 +17,6 @@ public class InfoSaveReqVO {
private
Long
id
;
@Schema
(
description
=
"客户姓名"
,
requiredMode
=
Schema
.
RequiredMode
.
REQUIRED
)
@NotEmpty
(
message
=
"客户姓名不能为空"
)
private
String
customerName
;
@Schema
(
description
=
"联系方式"
)
...
...
@@ -68,27 +67,35 @@ public class InfoSaveReqVO {
private
Integer
customerStatus
;
@Schema
(
description
=
"拜访品种(多选,逗号分隔)"
)
@NotEmpty
(
message
=
"拜访品种不能为空"
)
private
String
visitProductIds
;
@Schema
(
description
=
"拜访品种名称(多选,逗号分隔)"
)
@NotEmpty
(
message
=
"拜访品种名称不能为空"
)
private
String
visitProductNames
;
@Schema
(
description
=
"拜访方式(字典)"
)
@NotNull
(
message
=
"拜访方式不能为空"
)
private
Integer
visitMethod
;
@Schema
(
description
=
"拜访类型(字典)"
,
example
=
"2"
)
@NotNull
(
message
=
"拜访类型不能为空"
)
private
Integer
visitType
;
@Schema
(
description
=
"客户所属部门(字典)"
)
@NotEmpty
(
message
=
"客户所属部门不能为空"
)
private
String
department
;
@Schema
(
description
=
"服务内容"
)
@NotEmpty
(
message
=
"服务内容不能为空"
)
private
String
serviceContent
;
@Schema
(
description
=
"客户反馈"
)
@NotEmpty
(
message
=
"客户反馈不能为空"
)
private
String
customerFeedback
;
@Schema
(
description
=
"服务记录图片URL列表(JSON数组)"
)
@Schema
(
description
=
"服务记录图片"
)
@NotEmpty
(
message
=
"服务记录图片不能为空"
)
private
String
serviceImages
;
}
\ No newline at end of file
yudao-module-visit/src/main/java/cn/iocoder/yudao/module/visit/controller/admin/product/ProductController.java
View file @
677dd111
...
...
@@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.visit.controller.admin.product;
import
cn.iocoder.yudao.framework.common.enums.CommonStatusEnum
;
import
cn.iocoder.yudao.framework.idempotent.core.annotation.Idempotent
;
import
cn.iocoder.yudao.module.system.enums.common.SexEnum
;
import
com.fhs.common.utils.StringUtil
;
import
io.swagger.v3.oas.annotations.Parameters
;
import
org.springframework.web.bind.annotation.*
;
import
javax.annotation.Resource
;
...
...
@@ -71,6 +72,26 @@ public class ProductController {
return
success
(
true
);
}
@DeleteMapping
(
"/deleteByIds"
)
@Operation
(
summary
=
"删除产品"
)
@Parameter
(
name
=
"ids"
,
description
=
"编号"
,
required
=
true
)
@PreAuthorize
(
"@ss.hasPermission('visit:product:delete')"
)
@Idempotent
(
timeout
=
10
,
message
=
"重复请求,请稍后重试"
)
public
CommonResult
<
Boolean
>
deleteProductByIds
(
@RequestParam
(
"ids"
)
String
ids
)
{
//如果ids是空的,返回异常提示,请先选择需要打印的记录
if
(
StringUtil
.
isEmpty
(
ids
))
{
throw
new
RuntimeException
(
"请先选择需要删除的记录"
);
}
String
[]
split
=
ids
.
split
(
","
);
//转为List
List
<
Long
>
idList
=
new
ArrayList
<>();
for
(
String
s
:
split
)
{
idList
.
add
(
Long
.
parseLong
(
s
));
}
productService
.
deleteProductByIds
(
idList
);
return
success
(
true
);
}
@GetMapping
(
"/get"
)
@Operation
(
summary
=
"获得产品"
)
@Parameter
(
name
=
"id"
,
description
=
"编号"
,
required
=
true
,
example
=
"1024"
)
...
...
yudao-module-visit/src/main/java/cn/iocoder/yudao/module/visit/enums/ErrorCodeConstants.java
View file @
677dd111
...
...
@@ -13,14 +13,17 @@ public interface ErrorCodeConstants {
ErrorCode
PRODUCT_NOT_EXISTS
=
new
ErrorCode
(
1001000001
,
"产品不存在"
);
ErrorCode
PRODUCT_PRODUCT_EXISTS
=
new
ErrorCode
(
1001000002
,
"导入产品已存在!"
);
ErrorCode
PRODUCT_IMPORT_LIST_IS_EMPTY
=
new
ErrorCode
(
1001000003
,
"导入产品数据不能为空!"
);
ErrorCode
PRODUCT_LIST_NOT_EXISTS
=
new
ErrorCode
(
1001000004
,
"所选产品中含有不存在的数据"
);
// ========== 客户信息 ==========
ErrorCode
CUSTOMER_INFO_NOT_EXISTS
=
new
ErrorCode
(
1002000001
,
"客户信息不存在"
);
ErrorCode
CUSTOMER_INFO_COMPANY_NAME_DUPLICATE
=
new
ErrorCode
(
1002000002
,
"该公司名称已存在"
);
ErrorCode
CUSTOMER_INFO_IMPORT_LIST_IS_EMPTY
=
new
ErrorCode
(
1002000003
,
"导入客户数据不能为空!"
);
ErrorCode
CUSTOMER_INFO_EXISTS
=
new
ErrorCode
(
1002000004
,
"导入客户数据已存在!"
);
ErrorCode
CUSTOMER_INFO_LIST_NOT_EXISTS
=
new
ErrorCode
(
1002000005
,
"所选客户信息中含有不存在的数据"
);
// ========== 客户拜访记录 ==========
ErrorCode
INFO_NOT_EXISTS
=
new
ErrorCode
(
1003000001
,
"客户拜访记录不存在"
);
ErrorCode
INFO_LIST_NOT_EXISTS
=
new
ErrorCode
(
1003000002
,
"所选客户拜访记录中含有不存在的数据"
);
}
yudao-module-visit/src/main/java/cn/iocoder/yudao/module/visit/service/customerinfo/CustomerInfoService.java
View file @
677dd111
...
...
@@ -59,4 +59,6 @@ public interface CustomerInfoService {
List
<
CustomerInfoDO
>
getAllCustomerInfo
();
CustomerImportRespVO
importCustomerList
(
List
<
CustomerImportExcelVO
>
list
,
Boolean
updateSupport
);
void
deleteCustomerInfoByIds
(
List
<
Long
>
idList
);
}
\ No newline at end of file
yudao-module-visit/src/main/java/cn/iocoder/yudao/module/visit/service/customerinfo/CustomerInfoServiceImpl.java
View file @
677dd111
...
...
@@ -106,6 +106,16 @@ public class CustomerInfoServiceImpl implements CustomerInfoService {
customerInfoMapper
.
deleteById
(
id
);
}
@Override
public
void
deleteCustomerInfoByIds
(
List
<
Long
>
idList
)
{
// 校验存在
if
(
customerInfoMapper
.
selectByIds
(
idList
).
size
()
!=
idList
.
size
()){
throw
exception
(
INFO_LIST_NOT_EXISTS
);
}
// 删除
customerInfoMapper
.
deleteByIds
(
idList
);
}
private
void
validateCustomerInfoExists
(
Long
id
)
{
if
(
customerInfoMapper
.
selectById
(
id
)
==
null
)
{
throw
exception
(
CUSTOMER_INFO_NOT_EXISTS
);
...
...
yudao-module-visit/src/main/java/cn/iocoder/yudao/module/visit/service/info/InfoService.java
View file @
677dd111
...
...
@@ -65,4 +65,10 @@ public interface InfoService {
* @return 客户拜访记录
*/
InfoPrintVO
getInfoByCompanyName
(
String
companyName
);
List
<
InfoPrintVO
>
getUnionInfoByIds
(
List
<
Long
>
idList
);
void
deleteInfoByIds
(
List
<
Long
>
idList
);
List
<
InfoPrintVO
>
getUnionAllInfoByIds
(
List
<
InfoDO
>
list
);
}
\ No newline at end of file
yudao-module-visit/src/main/java/cn/iocoder/yudao/module/visit/service/info/InfoServiceImpl.java
View file @
677dd111
This diff is collapsed.
Click to expand it.
yudao-module-visit/src/main/java/cn/iocoder/yudao/module/visit/service/product/ProductService.java
View file @
677dd111
...
...
@@ -61,4 +61,6 @@ public interface ProductService {
ProductImportRespVO
importProductList
(
List
<
ProductImportExcelVO
>
list
,
Boolean
updateSupport
);
List
<
ProductSimpleReqVO
>
getProductByIds
(
String
productIds
);
void
deleteProductByIds
(
List
<
Long
>
idList
);
}
\ No newline at end of file
yudao-module-visit/src/main/java/cn/iocoder/yudao/module/visit/service/product/ProductServiceImpl.java
View file @
677dd111
...
...
@@ -65,6 +65,18 @@ public class ProductServiceImpl implements ProductService {
productMapper
.
deleteById
(
id
);
}
@Override
public
void
deleteProductByIds
(
List
<
Long
>
idList
)
{
// 校验存在
if
(
productMapper
.
selectByIds
(
idList
).
size
()
!=
idList
.
size
()){
throw
exception
(
PRODUCT_LIST_NOT_EXISTS
);
}
// 删除
productMapper
.
deleteByIds
(
idList
);
}
private
void
validateProductExists
(
Long
id
)
{
if
(
productMapper
.
selectById
(
id
)
==
null
)
{
throw
exception
(
PRODUCT_NOT_EXISTS
);
...
...
yudao-ui/yudao-ui-admin-vue3/src/api/visit/customerinfo/index.ts
View file @
677dd111
...
...
@@ -65,6 +65,11 @@ export const CustomerInfoApi = {
return
await
request
.
delete
({
url
:
`/visit/customer-info/delete?id=`
+
id
})
},
// 批量删除客户信息
deleteCustomerInfoByIds
:
async
(
ids
:
String
)
=>
{
return
await
request
.
delete
({
url
:
`/visit/customer-info/deleteByIds?ids=`
+
ids
})
},
// 导出客户信息 Excel
exportCustomerInfo
:
async
(
params
)
=>
{
return
await
request
.
download
({
url
:
`/visit/customer-info/export-excel`
,
params
})
...
...
yudao-ui/yudao-ui-admin-vue3/src/api/visit/info/index.ts
View file @
677dd111
...
...
@@ -67,6 +67,15 @@ export const InfoApi = {
return
await
request
.
get
({
url
:
`/visit/info/get?id=`
+
id
})
},
//查询合并打印客户拜访记录
getUnionPrintListByIds
:
async
(
ids
:
string
)
=>
{
return
await
request
.
get
({
url
:
`/visit/info/getUnionPrintListByIds`
,
params
:
{
ids
}
})
},
//查询合并打印客户拜访记录(筛选出的所有数据)
getUnionAllPrintListByIds
:
async
(
params
:
any
)
=>
{
return
await
request
.
get
({
url
:
`/visit/info/getUnionAllPrintListByIds`
,
params
})
},
//查询打印客户拜访记录
getPrintListByIds
:
async
(
ids
:
string
)
=>
{
return
await
request
.
get
({
url
:
`/visit/info/getPrintListByIds`
,
params
:
{
ids
}
})
...
...
@@ -93,6 +102,12 @@ export const InfoApi = {
return
await
request
.
delete
({
url
:
`/visit/info/delete?id=`
+
id
})
},
// 删除客户拜访记录
deleteInfoByIds
:
async
(
ids
:
String
)
=>
{
return
await
request
.
delete
({
url
:
`/visit/info/deleteByIds?ids=`
+
ids
})
},
// 导出客户拜访记录 Excel
exportInfo
:
async
(
params
)
=>
{
return
await
request
.
download
({
url
:
`/visit/info/export-excel`
,
params
})
...
...
yudao-ui/yudao-ui-admin-vue3/src/api/visit/product/index.ts
View file @
677dd111
...
...
@@ -41,6 +41,12 @@ export const ProductApi = {
return
await
request
.
delete
({
url
:
`/visit/product/delete?id=`
+
id
})
},
// 批量删除产品
deleteProductByIds
:
async
(
ids
:
String
)
=>
{
return
await
request
.
delete
({
url
:
`/visit/product/deleteByIds?ids=`
+
ids
})
},
// 导出产品 Excel
exportProduct
:
async
(
params
)
=>
{
return
await
request
.
download
({
url
:
`/visit/product/export-excel`
,
params
})
...
...
yudao-ui/yudao-ui-admin-vue3/src/components/UploadFile/src/UploadImgs.vue
View file @
677dd111
...
...
@@ -16,10 +16,9 @@
:on-success=
"uploadSuccess"
list-type=
"picture-card"
>
<div
class=
"upload-empty"
>
<div
class=
"upload-empty"
>
<slot
name=
"empty"
>
<Icon
icon=
"ep:plus"
/>
<!--
<span>
请上传图片
</span>
-->
</slot>
</div>
<template
#file
="
{ file }">
...
...
@@ -116,6 +115,7 @@ const beforeUpload: UploadProps['beforeUpload'] = (rawFile) => {
// 图片上传成功
interface
UploadEmits
{
(
e
:
'
update:modelValue
'
,
value
:
string
[]):
void
(
e
:
'
change
'
,
value
:
string
[]):
void
}
const
emit
=
defineEmits
<
UploadEmits
>
()
...
...
@@ -159,16 +159,16 @@ watch(
const
emitUpdateModelValue
=
()
=>
{
let
result
:
string
[]
=
fileList
.
value
.
map
((
file
)
=>
file
.
url
!
)
emit
(
'
update:modelValue
'
,
result
)
emit
(
'
change
'
,
result
)
}
// 删除图片
const
handleRemove
=
(
uploadFile
:
UploadFile
)
=>
{
fileList
.
value
=
fileList
.
value
.
filter
(
(
item
)
=>
item
.
url
!==
uploadFile
.
url
||
item
.
name
!==
uploadFile
.
name
)
emit
(
'
update:modelValue
'
,
fileList
.
value
.
map
((
file
)
=>
file
.
url
!
)
)
const
result
=
fileList
.
value
.
map
((
file
)
=>
file
.
url
!
)
emit
(
'
update:modelValue
'
,
result
)
emit
(
'
change
'
,
result
)
}
// 图片上传错误提示
...
...
yudao-ui/yudao-ui-admin-vue3/src/views/visit/customerinfo/CustomerInfoForm.vue
View file @
677dd111
...
...
@@ -6,6 +6,7 @@
:rules=
"formRules"
label-width=
"100px"
v-loading=
"formLoading"
:disabled=
"isDetail"
>
<el-form-item
label=
"客户姓名"
prop=
"customerName"
>
<el-input
v-model=
"formData.customerName"
placeholder=
"请输入客户姓名"
/>
...
...
@@ -51,7 +52,7 @@
placeholder=
"请点击右侧选择"
readonly
>
<template
#append
>
<template
#append
v-if=
"formType !== 'detail'"
>
<el-link
type=
"primary"
@
click=
"openMapPicker"
>
选择地址
</el-link>
</
template
>
</el-input>
...
...
@@ -69,13 +70,13 @@
<!-- <el-form-item label="定位静态图 URL" prop="locationImage">-->
<!-- <UploadImg v-model="formData.locationImage" />-->
<!-- </el-form-item>-->
<el-form-item
label=
"产品信息"
prop=
"product
Id
s"
>
<el-form-item
label=
"产品信息"
prop=
"product
Name
s"
>
<el-input
v-model=
"formData.productNames"
placeholder=
"请点击右侧选择"
readonly
>
<
template
#append
>
<
template
#append
v-if=
"formType !== 'detail'"
>
<el-link
type=
"primary"
@
click=
"openProduct"
>
选择产品
</el-link>
</
template
>
</el-input>
...
...
@@ -92,7 +93,7 @@
</el-form-item>
</el-form>
<
template
#footer
>
<el-button
@
click=
"submitForm"
type=
"primary"
:disabled=
"formLoading"
>
确 定
</el-button>
<el-button
@
click=
"submitForm"
type=
"primary"
:disabled=
"formLoading"
v-if=
"formType !== 'detail'"
>
确 定
</el-button>
<el-button
@
click=
"dialogVisible = false"
>
取 消
</el-button>
</
template
>
</Dialog>
...
...
@@ -111,6 +112,7 @@ defineOptions({ name: 'CustomerInfoForm' })
const
{
t
}
=
useI18n
()
// 国际化
const
message
=
useMessage
()
// 消息弹窗
const
isDetail
=
computed
(()
=>
formType
.
value
===
'
detail
'
)
//detail说明是查看详情,要只读
const
dialogVisible
=
ref
(
false
)
// 弹窗的是否展示
const
dialogTitle
=
ref
(
''
)
// 弹窗的标题
const
formLoading
=
ref
(
false
)
// 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
...
...
@@ -159,11 +161,13 @@ const formRules = reactive({
provinceName
:
[{
required
:
true
,
message
:
'
省名称不能为空
'
,
trigger
:
'
blur
'
}],
cityName
:
[{
required
:
true
,
message
:
'
市名称不能为空
'
,
trigger
:
'
blur
'
}],
areaName
:
[{
required
:
true
,
message
:
'
区名称不能为空
'
,
trigger
:
'
blur
'
}],
regionFullName
:
[{
required
:
true
,
message
:
'
所在地区不能为空
'
,
trigger
:
'
blur
'
}],
regionFullName
:
[{
required
:
true
,
message
:
'
所在地区不能为空
'
,
trigger
:
'
change
'
}],
locationText
:
[{
required
:
true
,
message
:
'
详细地址不能为空
'
,
trigger
:
'
change
'
}],
longitude
:
[{
required
:
true
,
message
:
'
经度不能为空
'
,
trigger
:
'
blur
'
}],
latitude
:
[{
required
:
true
,
message
:
'
纬度不能为空
'
,
trigger
:
'
blur
'
}],
locationImage
:
[{
required
:
true
,
message
:
'
定位静态图不能为空
'
,
trigger
:
'
blur
'
}],
productNames
:
[{
required
:
true
,
message
:
'
产品信息名称不能为空
'
,
trigger
:
'
blur|change
'
}],
productIds
:
[{
required
:
true
,
message
:
'
产品信息不能为空
'
,
trigger
:
'
blur|change
'
}],
})
const
formRef
=
ref
()
// 表单 Ref
...
...
@@ -179,6 +183,7 @@ const handleUpdateProduct = (ids: string,names: string) => {
//存入表单
formData
.
value
.
productIds
=
ids
;
formData
.
value
.
productNames
=
names
;
formRef
.
value
?.
validateField
(
'
productNames
'
)
};
//===========================================地图相关操作=================================
const
mapPickerRef
=
ref
()
...
...
@@ -221,7 +226,7 @@ const handleLocation = ({ lat, lng, address }) => {
city
=
'
市辖区
'
}
selectedOptions
.
value
=
[
province
,
city
,
area
]
//专门回显用的
formRef
.
value
?.
validateField
(
'
regionFullName
'
)
}
else
{
console
.
warn
(
'
逆地址解析失败:
'
,
res
.
data
.
message
)
}
...
...
yudao-ui/yudao-ui-admin-vue3/src/views/visit/customerinfo/index.vue
View file @
677dd111
...
...
@@ -72,6 +72,14 @@
>
<Icon
icon=
"ep:plus"
class=
"mr-5px"
/>
新增
</el-button>
<el-button
type=
"danger"
plain
@
click=
"handleDeleteRows()"
v-hasPermi=
"['visit:customer-info:delete']"
>
<Icon
icon=
"ep:delete"
class=
"mr-5px"
/>
删除
</el-button>
<el-button
type=
"warning"
plain
...
...
@@ -100,7 +108,7 @@
<el-table-column
label=
"编号"
align=
"center"
prop=
"id"
/>
<el-table-column
label=
"客户姓名"
align=
"center"
prop=
"customerName"
min-width=
"100px"
/>
<el-table-column
label=
"联系方式"
align=
"center"
prop=
"contact"
min-width=
"120px"
/>
<el-table-column
label=
"公司名称"
align=
"center"
prop=
"companyName"
min-width=
"
16
0px"
/>
<el-table-column
label=
"公司名称"
align=
"center"
prop=
"companyName"
min-width=
"
24
0px"
/>
<el-table-column
label=
"性质等级"
align=
"center"
prop=
"customerType"
>
<template
#default
="
scope
"
>
<dict-tag
:type=
"DICT_TYPE.CUSTOMER_TYPE"
:value=
"scope.row.customerType"
/>
...
...
@@ -119,7 +127,7 @@
<ProductList
:ids=
scope.row.productIds
/>
</
template
>
</el-table-column>
<el-table-column
label=
"操作"
align=
"center"
min-width=
"3
2
0px"
fixed=
"right"
>
<el-table-column
label=
"操作"
align=
"center"
min-width=
"3
8
0px"
fixed=
"right"
>
<
template
#default
="
scope
"
>
<el-button
link
...
...
@@ -132,9 +140,17 @@
link
type=
"primary"
@
click=
"handlePrint(scope.row.companyName)"
v-hasPermi=
"['visit:customer-info:print']"
>
打印拜访记录
</el-button>
<el-button
link
type=
"primary"
@
click=
"openForm('detail', scope.row.id)"
>
查看详情
</el-button>
<el-button
link
type=
"primary"
...
...
@@ -166,7 +182,6 @@
<!-- 表单弹窗:添加/修改 -->
<CustomerInfoForm
ref=
"formRef"
@
success=
"getList"
/>
<!-- 打印弹窗 -->
<!-- <VisitPrint ref="visitPrintRef" :data="selectedData" />-->
<Teleport
to=
"body"
>
<div
v-show=
"false"
>
<PrintContent
ref=
"printComp"
:dataList=
"printData"
/>
...
...
@@ -300,6 +315,22 @@ const handleDelete = async (id: number) => {
}
catch
{}
}
/** 删除按钮操作 */
const
handleDeleteRows
=
async
()
=>
{
try
{
if
(
multipleSelection
.
value
.
length
===
0
)
{
return
message
.
warning
(
'
请先选择需要删除的记录
'
)
}
// 删除的二次确认
await
message
.
delConfirm
()
// 发起删除
await
CustomerInfoApi
.
deleteCustomerInfoByIds
(
multipleSelection
.
value
.
join
(
'
,
'
))
message
.
success
(
t
(
'
common.delSuccess
'
))
// 刷新列表
await
getList
()
}
catch
{}
}
/** 导出按钮操作 */
const
handleExport
=
async
()
=>
{
try
{
...
...
yudao-ui/yudao-ui-admin-vue3/src/views/visit/info/InfoForm.vue
View file @
677dd111
...
...
@@ -6,8 +6,9 @@
:rules=
"formRules"
label-width=
"100px"
v-loading=
"formLoading"
:disabled=
"isDetail"
>
<el-form-item
label=
"
拜访人
姓名"
prop=
"customerName"
>
<el-form-item
label=
"
客户
姓名"
prop=
"customerName"
>
<el-input
v-model=
"formData.customerName"
placeholder=
"请输入拜访人姓名"
/>
</el-form-item>
<el-form-item
label=
"联系方式"
prop=
"contact"
>
...
...
@@ -38,7 +39,7 @@
placeholder=
"请点击右侧选择"
readonly
>
<template
#append
>
<template
#append
v-if=
"formType !== 'detail'"
>
<el-link
type=
"primary"
@
click=
"openMapPicker"
>
选择地址
</el-link>
</
template
>
</el-input>
...
...
@@ -78,7 +79,7 @@
placeholder=
"请点击右侧选择"
readonly
>
<
template
#append
>
<
template
#append
v-if=
"formType !== 'detail'"
>
<el-link
type=
"primary"
@
click=
"openProduct"
>
选择产品
</el-link>
</
template
>
</el-input>
...
...
@@ -124,38 +125,40 @@
/>
</el-form-item>
<el-form-item
label=
"服务记录图片"
prop=
"serviceImages"
>
<UploadImgs
v-model=
"formData.serviceImages"
:limit=
"9"
/>
<UploadImgs
:class=
"{hide_box: isDetail}"
v-model=
"formData.serviceImages"
:limit=
"9"
@
change=
"onServiceImageChange"
:disabled=
"isDetail"
/>
</el-form-item>
</el-form>
<
template
#footer
>
<el-button
@
click=
"submitForm"
type=
"primary"
:disabled=
"formLoading"
>
确 定
</el-button>
<el-button
@
click=
"submitForm"
type=
"primary"
:disabled=
"formLoading"
v-if=
"formType !== 'detail'"
>
确 定
</el-button>
<el-button
@
click=
"dialogVisible = false"
>
取 消
</el-button>
</
template
>
</Dialog>
<productTable
ref=
"productListRef"
@
updateProduct=
"handleUpdateProduct"
:ids=
"formData.visitProductIds"
/>
</template>
<
script
setup
lang=
"ts"
>
import
{
getIntDictOptions
,
DICT_TYPE
,
getStrDictOptions
}
from
'
@/utils/dict
'
import
{
InfoApi
,
InfoVO
}
from
'
@/api/visit/info
'
import
{
CustomerInfoApi
}
from
'
@/api/visit/customerinfo
'
import
{
DICT_TYPE
,
getIntDictOptions
,
getStrDictOptions
}
from
'
@/utils/dict
'
import
{
InfoApi
,
InfoVO
}
from
'
@/api/visit/info
'
import
{
CustomerInfoApi
}
from
'
@/api/visit/customerinfo
'
import
productTable
from
"
@/views/visit/customerinfo/productTable.vue
"
;
import
{
pcaTextArr
}
from
'
element-china-area-data
'
import
{
pcaTextArr
}
from
'
element-china-area-data
'
import
MapPicker
from
'
../util/MapPicker.vue
'
import
{
onMounted
}
from
'
vue
'
import
request
from
"
@/config/axios
"
;
import
{
onMounted
}
from
'
vue
'
import
{
homesApi
}
from
"
@/api/visit/home
"
;
/** 客户拜访记录 表单 */
defineOptions
({
name
:
'
InfoForm
'
})
const
upload_btn
=
ref
(
true
)
//定义图片上传展示隐藏
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
formType
=
ref
(
''
)
// 表单的类型:create - 新增;update - 修改
;detail - 查看
const
selectedOptions
=
ref
<
string
[]
>
([])
const
isDetail
=
computed
(()
=>
formType
.
value
===
'
detail
'
)
//detail说明是查看详情,要只读
const
formData
=
ref
({
id
:
undefined
,
customerName
:
undefined
,
...
...
@@ -181,21 +184,25 @@ const formData = ref({
serviceImages
:
[],
})
const
formRules
=
reactive
({
customerName
:
[{
required
:
true
,
message
:
'
拜访人姓名不能为空
'
,
trigger
:
'
blur
'
}],
companyName
:
[{
required
:
true
,
message
:
'
客户公司名称不能为空
'
,
trigger
:
'
blur|change
'
}],
provinceName
:
[{
required
:
true
,
message
:
'
省名称不能为空
'
,
trigger
:
'
blur
'
}],
cityName
:
[{
required
:
true
,
message
:
'
市名称不能为空
'
,
trigger
:
'
blur
'
}],
areaName
:
[{
required
:
true
,
message
:
'
区名称不能为空
'
,
trigger
:
'
blur
'
}],
regionFullName
:
[{
required
:
true
,
message
:
'
所在地区不能为空
'
,
trigger
:
'
blur
'
}],
regionFullName
:
[{
required
:
true
,
message
:
'
所在地区不能为空
'
,
trigger
:
'
change
'
}],
locationText
:
[{
required
:
true
,
message
:
'
详细地址不能为空
'
,
trigger
:
'
change
'
}],
longitude
:
[{
required
:
true
,
message
:
'
经度不能为空
'
,
trigger
:
'
blur
'
}],
latitude
:
[{
required
:
true
,
message
:
'
纬度不能为空
'
,
trigger
:
'
blur
'
}],
locationImage
:
[{
required
:
true
,
message
:
'
定位静态图不能为空
'
,
trigger
:
'
blur
'
}],
visitDate
:
[{
required
:
true
,
message
:
'
拜访日期不能为空
'
,
trigger
:
'
blur
'
}],
customerStatus
:
[{
required
:
true
,
message
:
'
性质等级不能为空
'
,
trigger
:
'
blur
'
}],
department
:
[{
required
:
true
,
message
:
'
客户部门不能为空
'
,
trigger
:
'
blur
'
}],
customerStatus
:
[{
required
:
true
,
message
:
'
性质等级不能为空
'
,
trigger
:
'
change
'
}],
department
:
[{
required
:
true
,
message
:
'
客户部门不能为空
'
,
trigger
:
'
change
'
}],
visitMethod
:
[{
required
:
true
,
message
:
'
拜访方式不能为空
'
,
trigger
:
'
change
'
}],
visitType
:
[{
required
:
true
,
message
:
'
拜访类型不能为空
'
,
trigger
:
'
change
'
}],
visitProductNames
:
[{
required
:
true
,
message
:
'
拜访品种名称不能为空
'
,
trigger
:
'
blur|change
'
}],
visitProductIds
:
[{
required
:
true
,
message
:
'
拜访品种不能为空
'
,
trigger
:
'
blur|change
'
}],
serviceContent
:
[{
required
:
true
,
message
:
'
请输入服务内容
'
,
trigger
:
'
blur
'
},
{
max
:
500
,
message
:
'
最多输入 500 个字符
'
,
trigger
:
'
blur
'
}],
customerFeedback
:
[{
required
:
true
,
message
:
'
请输入客户反馈
'
,
trigger
:
'
blur
'
},
{
max
:
500
,
message
:
'
最多输入 500 个字符
'
,
trigger
:
'
blur
'
}],
serviceImages
:
[{
required
:
true
,
message
:
'
请上传服务记录图片
'
,
trigger
:
'
blur|change
'
}]
})
const
formRef
=
ref
()
// 表单 Ref
//===========================================下拉搜索相关====================================
...
...
@@ -268,6 +275,11 @@ const handleSelect = (item: Record<string, any>) => {
formData
.
value
.
customerStatus
=
item
.
customerStatus
;
formData
.
value
.
department
=
item
.
department
;
restoreSelectedOptionsFromForm
();
formRef
.
value
?.
validateField
(
'
companyName
'
)
formRef
.
value
?.
validateField
(
'
regionFullName
'
)
formRef
.
value
?.
validateField
(
'
locationText
'
)
formRef
.
value
?.
validateField
(
'
customerStatus
'
)
formRef
.
value
?.
validateField
(
'
department
'
)
}
//===========================================选择产品相关操作=================================
const
productListRef
=
ref
<
InstanceType
<
typeof
productTable
>
|
null
>
(
null
);
// 产品列表 Ref
...
...
@@ -282,6 +294,7 @@ const handleUpdateProduct = (ids: string,names: string) => {
formData
.
value
.
visitProductIds
=
ids
;
console
.
log
(
names
)
formData
.
value
.
visitProductNames
=
names
;
formRef
.
value
?.
validateField
(
'
visitProductNames
'
)
};
//===========================================地图相关操作=================================
const
mapPickerRef
=
ref
()
...
...
@@ -323,7 +336,7 @@ const handleLocation = ({ lat, lng, address }) => {
city
=
'
市辖区
'
}
selectedOptions
.
value
=
[
province
,
city
,
area
]
//专门回显用的
formRef
.
value
?.
validateField
(
'
regionFullName
'
)
}
else
{
console
.
warn
(
'
逆地址解析失败:
'
,
res
.
data
.
message
)
}
...
...
@@ -442,8 +455,16 @@ const resetForm = () => {
formRef
.
value
?.
resetFields
()
selectedOptions
.
value
=
[];
}
const
onServiceImageChange
=
()
=>
{
// 强制触发校验
formRef
.
value
?.
validateField
(
'
serviceImages
'
)
}
onMounted
(()
=>
{
loadAll
()
})
</
script
>
<
style
lang=
"scss"
scoped
>
.
hide_box
:
:
v-deep
.
el-upload--picture-card
{
display
:
none
;
}
</
style
>
yudao-ui/yudao-ui-admin-vue3/src/views/visit/info/index.vue
View file @
677dd111
...
...
@@ -117,6 +117,14 @@
>
<Icon
icon=
"ep:plus"
class=
"mr-5px"
/>
新增
</el-button>
<el-button
type=
"danger"
plain
@
click=
"handleDeleteRows()"
v-hasPermi=
"['visit:info:delete']"
>
<Icon
icon=
"ep:delete"
class=
"mr-5px"
/>
删除
</el-button>
<el-button
type=
"success"
plain
...
...
@@ -131,9 +139,28 @@
plain
@
click=
"handlePrint"
:loading=
"exportLoading"
v-hasPermi=
"['visit:info:print']"
>
<Icon
icon=
"ep:printer"
class=
"mr-5px"
/>
批量打印
</el-button>
<el-button
type=
"warning"
plain
@
click=
"handleUnionPrint"
:loading=
"exportLoading"
v-hasPermi=
"['visit:info:print']"
>
<Icon
icon=
"ep:printer"
class=
"mr-5px"
/>
多选合并打印
</el-button>
<el-button
type=
"primary"
plain
@
click=
"handleUnionAllPrint"
:loading=
"exportLoading"
v-hasPermi=
"['visit:info:print']"
>
<Icon
icon=
"ep:printer"
class=
"mr-5px"
/>
全选合并打印
</el-button>
</el-form-item>
</el-form>
</ContentWrap>
...
...
@@ -145,7 +172,7 @@
<el-table-column
label=
"主键ID"
align=
"center"
prop=
"id"
/>
<el-table-column
label=
"拜访人姓名"
align=
"center"
prop=
"customerName"
width=
"100px"
/>
<el-table-column
label=
"联系方式"
align=
"center"
prop=
"contact"
width=
"120px"
/>
<el-table-column
label=
"客户公司名称"
align=
"center"
prop=
"companyName"
width=
"
16
0px"
/>
<el-table-column
label=
"客户公司名称"
align=
"center"
prop=
"companyName"
width=
"
24
0px"
/>
<el-table-column
label=
"所在地区"
align=
"center"
prop=
"regionFullName"
width=
"160px"
/>
<el-table-column
label=
"拜访日期"
...
...
@@ -181,8 +208,15 @@
:formatter=
"dateFormatter"
width=
"180px"
/>
<el-table-column
label=
"操作"
align=
"center"
min-width=
"1
2
0px"
fixed=
"right"
>
<el-table-column
label=
"操作"
align=
"center"
min-width=
"1
8
0px"
fixed=
"right"
>
<
template
#default
="
scope
"
>
<el-button
link
type=
"primary"
@
click=
"openForm('detail', scope.row.id)"
>
查看详情
</el-button>
<el-button
link
type=
"primary"
...
...
@@ -214,8 +248,6 @@
<!-- 表单弹窗:添加/修改 -->
<InfoForm
ref=
"formRef"
@
success=
"getList"
/>
<!-- 打印弹窗 -->
<!-- <VisitPrint ref="visitPrintRef" :data="selectedData" />-->
<Teleport
to=
"body"
>
<div
v-show=
"false"
>
<PrintContent
ref=
"printComp"
:dataList=
"printData"
/>
...
...
@@ -228,20 +260,17 @@
import
{
DICT_TYPE
,
getIntDictOptions
}
from
'
@/utils/dict
'
import
{
dateFormatter
}
from
'
@/utils/formatTime
'
import
download
from
'
@/utils/download
'
import
{
InfoApi
,
Info
PrintVO
,
Info
VO
}
from
'
@/api/visit/info
'
import
{
InfoApi
,
InfoVO
}
from
'
@/api/visit/info
'
import
InfoForm
from
'
./InfoForm.vue
'
import
ProductList
from
"
@/views/visit/util/ProductList.vue
"
;
import
{
useRoute
}
from
'
vue-router
'
import
{
usePrint
}
from
'
../util/print
'
import
{
usePrint
}
from
'
../util/print
'
import
PrintContent
from
'
../util/PrintContent.vue
'
//==========
const
printData
=
ref
([
{
name
:
'
张三
'
,
date
:
'
2025-06-01
'
,
remark
:
'
拜访说明
'
,
image
:
'
https://image.xnszz.com//20250604/visit_1749022959344.png
'
},
{
name
:
'
李四
'
,
date
:
'
2025-06-03
'
,
remark
:
'
再次拜访
'
,
image
:
'
https://image.xnszz.com//20250604/visit_1749022959344.png
'
},
])
const
printData
=
ref
([])
const
printComp
=
ref
()
...
...
@@ -250,7 +279,24 @@ const handlePrint = async () => {
return
message
.
warning
(
'
请先选择需要打印的记录
'
)
}
printData
.
value
=
await
InfoApi
.
getPrintListByIds
(
multipleSelection
.
value
.
join
(
'
,
'
))
console
.
log
(
printData
.
value
)
await
nextTick
()
const
dom
=
printComp
.
value
.
printRef
if
(
dom
)
usePrint
(
dom
)
}
const
handleUnionPrint
=
async
()
=>
{
if
(
multipleSelection
.
value
.
length
===
0
)
{
return
message
.
warning
(
'
请先选择需要合并打印的记录
'
)
}
printData
.
value
=
await
InfoApi
.
getUnionPrintListByIds
(
multipleSelection
.
value
.
join
(
'
,
'
))
await
nextTick
()
const
dom
=
printComp
.
value
.
printRef
if
(
dom
)
usePrint
(
dom
)
}
const
handleUnionAllPrint
=
async
()
=>
{
if
(
list
.
value
.
length
===
0
)
{
return
message
.
warning
(
'
当前不存在可打印数据
'
)
}
printData
.
value
=
await
InfoApi
.
getUnionAllPrintListByIds
(
queryParams
)
await
nextTick
()
const
dom
=
printComp
.
value
.
printRef
if
(
dom
)
usePrint
(
dom
)
...
...
@@ -315,7 +361,7 @@ const resetQuery = () => {
handleQuery
()
}
/** 添加/修改操作 */
/** 添加/修改
/查看
操作 */
const
formRef
=
ref
()
const
openForm
=
(
type
:
string
,
id
?:
number
)
=>
{
formRef
.
value
.
open
(
type
,
id
)
...
...
@@ -334,6 +380,22 @@ const handleDelete = async (id: number) => {
}
catch
{}
}
/** 批量删除按钮操作 */
const
handleDeleteRows
=
async
()
=>
{
try
{
if
(
multipleSelection
.
value
.
length
===
0
)
{
return
message
.
warning
(
'
请先选择需要删除的记录
'
)
}
// 删除的二次确认
await
message
.
delConfirm
()
// 发起删除
await
InfoApi
.
deleteInfoByIds
(
multipleSelection
.
value
.
join
(
'
,
'
))
message
.
success
(
t
(
'
common.delSuccess
'
))
// 刷新列表
await
getList
()
}
catch
{}
}
/** 导出按钮操作 */
const
handleExport
=
async
()
=>
{
try
{
...
...
yudao-ui/yudao-ui-admin-vue3/src/views/visit/product/index.vue
View file @
677dd111
...
...
@@ -72,6 +72,14 @@
>
<Icon
icon=
"ep:plus"
class=
"mr-5px"
/>
新增
</el-button>
<el-button
type=
"danger"
plain
@
click=
"handleDeleteRows()"
v-hasPermi=
"['visit:product:delete']"
>
<Icon
icon=
"ep:delete"
class=
"mr-5px"
/>
删除
</el-button>
<el-button
type=
"warning"
plain
...
...
@@ -233,6 +241,22 @@ const handleDelete = async (id: number) => {
}
catch
{}
}
/** 批量删除按钮操作 */
const
handleDeleteRows
=
async
()
=>
{
try
{
if
(
multipleSelection
.
value
.
length
===
0
)
{
return
message
.
warning
(
'
请先选择需要删除的记录
'
)
}
// 删除的二次确认
await
message
.
delConfirm
()
// 发起删除
await
ProductApi
.
deleteProductByIds
(
multipleSelection
.
value
.
join
(
'
,
'
))
message
.
success
(
t
(
'
common.delSuccess
'
))
// 刷新列表
await
getList
()
}
catch
{}
}
/** 导出按钮操作 */
const
handleExport
=
async
()
=>
{
try
{
...
...
yudao-ui/yudao-ui-admin-vue3/src/views/visit/util/PrintContent.vue
View file @
677dd111
...
...
@@ -92,7 +92,7 @@
justify-content: center; /* 水平居中 */
height: 40px; /* 与其他 div 高度一致 */
width: 100%; /* 需要设定宽度 */"
>
{{
formatDate
(
time
)
}}
</li>
>
{{
time
[
0
]
+
"
年
"
+
time
[
1
]
+
"
月
"
+
time
[
2
]
+
"
日
"
}}
</li>
</ul>
<div
style=
"display: flex;
...
...
@@ -170,7 +170,7 @@ const printRef = ref(null)
const
formatDate
=
(
date
:
string
|
Date
)
=>
{
const
d
=
new
Date
(
date
)
return
d
.
toLocaleString
()
return
d
.
toLocale
Date
String
()
}
const
getSafeImages
=
(
val
?:
string
)
=>
{
if
(
!
val
||
val
===
''
)
return
[]
...
...
yudao-ui/yudao-ui-admin-vue3/src/views/visit/util/VisitPrint.vue
deleted
100644 → 0
View file @
5a69a12f
<
template
>
<div
ref=
"printArea"
style=
"padding: 20px; font-size: 14px;"
v-show=
"visible"
>
<div
v-for=
"(item, index) in data"
:key=
"index"
class=
"print-page"
>
<h2
style=
"text-align: center; margin-bottom: 16px;"
>
渠道服务记录
</h2>
<div
style=
"display: flex;"
>
<!-- 左侧 1/3 -->
<div
style=
"width: 33%;"
>
<img
crossorigin=
"anonymous"
:src=
"item.locationImage"
style=
"width: 100%; height: 120px;"
alt=
""
/>
<p>
业务员:
{{
item
.
salesman
}}
</p>
<p>
服务数量:
{{
item
.
serviceCount
}}
</p>
<h4>
拜访时间记录
</h4>
<ul>
<li
v-for=
"(time, i) in item.visitDate"
:key=
"i"
>
{{
formatDate
(
time
)
}}
</li>
</ul>
<h4>
主要服务内容
</h4>
<ul>
<li
v-for=
"(content, i) in item.serviceContent"
:key=
"i"
>
内容
{{
i
+
1
}}
:
{{
content
}}
</li>
</ul>
</div>
<!-- 右侧 2/3 -->
<div
style=
"width: 67%; padding-left: 20px;"
>
<p>
客户名称:
{{
item
.
companyName
}}
</p>
<p>
客户部门:
{{
getDictLabel
(
DICT_TYPE
.
CUSTOMER_DEPT
,
item
.
department
)
}}
</p>
<p>
推广产品:
{{
item
.
visitProductNames
}}
</p>
<div
style=
"border: 1px solid #ccc; padding: 10px; margin-bottom: 10px;"
>
拜访签到实景图
</div>
<div
style=
"display: flex; flex-wrap: wrap; gap: 10px;"
>
<img
crossorigin=
"anonymous"
v-for=
"(url, i) in getSafeImages(item.serviceImages)"
:key=
"i"
:src=
"url"
style=
"width: calc(50% - 5px); height: 100px; object-fit: cover; border: 1px solid #ccc;"
/>
</div>
</div>
</div>
<div
style=
"page-break-after: always;"
></div>
</div>
</div>
</
template
>
<
script
setup
lang=
"ts"
>
import
{
nextTick
,
ref
}
from
'
vue
'
import
{
getDictLabel
,
DICT_TYPE
}
from
'
@/utils/dict
'
const
props
=
defineProps
<
{
data
:
any
[]
}
>
()
const
printArea
=
ref
<
HTMLElement
|
null
>
(
null
)
const
visible
=
ref
(
false
)
const
formatDate
=
(
date
:
string
|
Date
)
=>
{
const
d
=
new
Date
(
date
)
return
d
.
toLocaleString
()
}
const
getSafeImages
=
(
val
?:
string
)
=>
{
if
(
!
val
)
return
[]
return
val
.
split
(
'
,
'
).
slice
(
0
,
4
)
}
const
print
=
async
()
=>
{
const
printWindow
=
window
.
open
(
''
,
'
_blank
'
)
if
(
!
printWindow
)
return
let
html
=
''
for
(
const
item
of
props
.
data
)
{
html
+=
`
<div class="print-page">
<h2>渠道服务记录</h2>
<div style="display: flex;">
<div style="width: 33%;">
<img src="
${
item
.
locationImage
}
" crossorigin="anonymous" style="width: 100%; height: 120px;" />
<p>业务员:
${
item
.
salesman
||
''
}
</p>
<p>服务数量:
${
item
.
serviceCount
||
''
}
</p>
<h4>拜访时间记录</h4>
<ul>
${(
item
.
visitDate
||
[]).
map
((
t
:
string
)
=>
`<li>
${
formatDate
(
t
)}
</li>`
).
join
(
''
)}
</ul>
<h4>主要服务内容</h4>
<ul>
${(
item
.
serviceContent
||
[]).
map
((
c
:
string
,
i
:
number
)
=>
`<li>内容
${
i
+
1
}
:
${
c
}
</li>`
).
join
(
''
)}
</ul>
</div>
<div style="width: 67%; padding-left: 20px;">
<p>客户名称:
${
item
.
companyName
||
''
}
</p>
<p>客户部门:
${
getDictLabel
(
DICT_TYPE
.
CUSTOMER_DEPT
,
item
.
department
)
||
''
}
</p>
<p>推广产品:
${
item
.
visitProductNames
||
''
}
</p>
<div style="border: 1px solid #ccc; padding: 10px; margin-bottom: 10px;">拜访签到实景图</div>
<div style="display: flex; flex-wrap: wrap; gap: 10px;">
${(
item
.
serviceImages
||
''
)
.
split
(
'
,
'
)
.
slice
(
0
,
4
)
.
map
(
url
=>
`<img src="
${
url
}
" crossorigin="anonymous" style="width: calc(50% - 5px); height: 100px; object-fit: cover; border: 1px solid #ccc;" />`
)
.
join
(
''
)}
</div>
</div>
</div>
<div style="page-break-after: always;"></div>
</div>
`
}
printWindow
.
document
.
write
(
`
<html>
<head>
<title>打印</title>
<meta charset="UTF-8">
<style>
body, html {
margin: 0;
padding: 0;
width: 210mm;
height: auto;
font-size: 14px;
-webkit-print-color-adjust: exact;
}
.print-page {
width: 210mm;
height: 297mm;
padding: 20mm;
box-sizing: border-box;
page-break-after: always;
overflow: hidden;
}
h2 {
text-align: center;
margin-bottom: 16px;
}
img {
max-width: 100%;
object-fit: cover;
}
</style>
</head>
<body>
${
html
}
</body>
</html>
`
)
printWindow
.
document
.
close
()
printWindow
.
focus
()
printWindow
.
print
()
printWindow
.
close
()
}
defineExpose
({
print
})
</
script
>
<
style
scoped
>
/* 普通预览样式 */
#printWrapper
{
width
:
210mm
;
margin
:
0
auto
;
font-size
:
14px
;
}
.print-page
{
width
:
210mm
;
height
:
297mm
;
padding
:
20mm
;
box-sizing
:
border-box
;
page-break-after
:
always
;
overflow
:
hidden
;
position
:
relative
;
display
:
flex
;
flex-direction
:
column
;
justify-content
:
space-between
;
border
:
1px
dashed
transparent
;
}
.print-page
h2
{
text-align
:
center
;
margin-bottom
:
16px
;
}
.print-page
img
{
max-width
:
100%
;
object-fit
:
cover
;
}
.print-page
.images-wrapper
{
display
:
flex
;
flex-wrap
:
wrap
;
gap
:
10px
;
}
.print-page
.images-wrapper
img
{
width
:
calc
(
50%
-
5px
);
height
:
100px
;
border
:
1px
solid
#ccc
;
}
/* 打印样式 */
@media
print
{
body
,
html
{
margin
:
0
;
padding
:
0
;
width
:
210mm
;
height
:
auto
;
-webkit-print-color-adjust
:
exact
;
}
#printWrapper
{
width
:
210mm
;
margin
:
0
auto
;
}
.print-page
{
width
:
210mm
;
height
:
297mm
;
page-break-after
:
always
;
overflow
:
hidden
;
box-sizing
:
border-box
;
padding
:
20mm
;
}
.no-print
{
display
:
none
!important
;
}
}
</
style
>
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment