This commit is contained in:
Vinjor 2025-11-25 11:34:44 +08:00
parent 2fa5bb9257
commit 7085c2c6bc
19 changed files with 531 additions and 298 deletions

View File

@ -7,6 +7,7 @@ import javax.servlet.http.HttpServletResponse;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.cus.service.ICusContactsService;
import com.ruoyi.cus.service.ICusMainSeasService;
import com.ruoyi.cus.service.ICusMainService;
import com.ruoyi.cus.vo.EmailVO;
import org.springframework.beans.BeanUtils;
@ -45,6 +46,8 @@ public class CusEmailController extends BaseController
@Autowired
private ICusMainService cusMainService;
@Autowired
private ICusMainSeasService cusMainSeasService;
@Autowired
private ICusContactsService cusContactsService;
/**
@ -77,12 +80,17 @@ public class CusEmailController extends BaseController
* 获取客户来往邮件详细信息
*/
@PreAuthorize("@ss.hasPermi('cus:email:query')")
@GetMapping(value = "/{id}")
public AjaxResult getInfo(@PathVariable("id") String id) {
@GetMapping(value = "/getInfo")
public AjaxResult getInfo(@RequestParam(value = "id") String id,@RequestParam(value = "ifSeas",required = false) Boolean ifSeas) {
EmailVO rtnVo = new EmailVO();
CusEmail cusEmail = cusEmailService.getById(id);
BeanUtils.copyProperties(cusEmail,rtnVo);
if(ifSeas){
//公海客户
rtnVo.setCusName(cusMainSeasService.getById(cusEmail.getCusId()).getFullName());
}else{
rtnVo.setCusName(cusMainService.getById(cusEmail.getCusId()).getFullName());
}
rtnVo.setContactName(cusContactsService.getById(cusEmail.getContactId()).getName());
return success(rtnVo);
}

View File

@ -169,8 +169,8 @@ public class CusMainController extends BaseController {
*/
@PreAuthorize("@ss.hasPermi('cus:main:query')")
@GetMapping(value = "/viewData")
public AjaxResult viewData(String id) {
return success(cusMainService.viewData(id));
public AjaxResult viewData(String id,Boolean seas) {
return success(cusMainService.viewData(id,seas));
}
/**

View File

@ -6,7 +6,14 @@ import java.util.List;
import javax.servlet.http.HttpServletResponse;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.cus.domain.CusMain;
import com.ruoyi.cus.domain.CusTimeAxis;
import com.ruoyi.cus.service.ICusMainService;
import com.ruoyi.cus.service.ICusTimeAxisService;
import com.ruoyi.cus.vo.MainVO;
import org.springframework.beans.BeanUtils;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
@ -35,10 +42,13 @@ import com.ruoyi.common.core.page.TableDataInfo;
*/
@RestController
@RequestMapping("/cus/seas")
public class CusMainSeasController extends BaseController
{
public class CusMainSeasController extends BaseController {
@Autowired
private ICusMainSeasService cusMainSeasService;
@Autowired
private ICusMainService cusMainService;
@Autowired
private ICusTimeAxisService cusTimeAxisService;
/**
* 查询公海客户信息列表
@ -105,9 +115,38 @@ public class CusMainSeasController extends BaseController
@PreAuthorize("@ss.hasPermi('cus:seas:remove')")
@Log(title = "公海客户信息", businessType = BusinessType.DELETE)
@DeleteMapping("/{ids}")
public AjaxResult remove(@PathVariable String[] ids)
{
public AjaxResult remove(@PathVariable String[] ids) {
List<String> list = new ArrayList<>(Arrays.asList(ids));
//时间轴信息
CusTimeAxis cusTimeAxis = new CusTimeAxis();
cusTimeAxis.setBusiMaxCatg("客户");
cusTimeAxis.setBusiCatg("修改");
cusTimeAxis.setContent("删除公海客户信息");
cusTimeAxis.setCusId(list.get(0));
cusTimeAxisService.saveNewTimeAxis(cusTimeAxis);
return toAjax(cusMainSeasService.removeByIds(list));
}
/**
* 转入公海
*/
@PostMapping("/moveToMain")
public AjaxResult moveToMain(@RequestBody CusMainSeas cusMainSeas){
SysUser sysUser = SecurityUtils.getLoginUser().getUser();
cusMainSeas = cusMainSeasService.getById(cusMainSeas.getId());
//转到公海表
CusMain cusMain = new CusMain();
BeanUtils.copyProperties(cusMainSeas, cusMain);
cusMainService.save(cusMain);
//时间轴信息
CusTimeAxis cusTimeAxis = new CusTimeAxis();
cusTimeAxis.setBusiMaxCatg("客户迁移");
cusTimeAxis.setBusiCatg("迁移");
cusTimeAxis.setContent("转出公海,操作人:"+sysUser.getNickName());
cusTimeAxis.setCusId(cusMain.getId());
cusTimeAxisService.saveNewTimeAxis(cusTimeAxis);
//真删除客户公海表信息
cusMainSeasService.deleteCus(cusMainSeas.getId());
return success();
}
}

View File

@ -30,7 +30,7 @@ public class CusMainSeas extends DlBaseEntity
/** 是否星标客户(0 否|1 是) */
@Excel(name = "是否星标客户(0 否|1 是)")
private Integer ifStar;
private Boolean ifStar;
/** 客户标签(多个英文逗号隔开) */
@Excel(name = "客户标签(多个英文逗号隔开)")

View File

@ -16,7 +16,15 @@ import org.apache.ibatis.annotations.Mapper;
* @date 2025-11-24
*/
@Mapper
public interface CusMainSeasMapper extends BaseMapper<CusMainSeas>
{
public interface CusMainSeasMapper extends BaseMapper<CusMainSeas> {
IPage<MainVO> queryListPage(@Param("entity") CusMainSeas entity, Page<CusMainSeas> page);
/**
* 删除公海客户信息
* @author vinjor-M
* @date 10:28 2025/11/25
* @param id 客户id
* @return int
**/
int deleteByCusId(String id);
}

View File

@ -27,4 +27,12 @@ public interface ICusMainSeasService extends IService<CusMainSeas> {
* @date 14:44 2025/11/20
**/
void saveOrUpdateByCusName(List<CusMainSeas> cusMainList);
/**
* 删除公海客户表信息
* @author vinjor-M
* @date 10:27 2025/11/25
* @param id TODO
**/
void deleteCus(String id);
}

View File

@ -49,7 +49,7 @@ public interface ICusMainService extends IService<CusMain> {
* @author vinjor-M
* @date 10:34 2025/11/18
**/
CusViewVO viewData(String id);
CusViewVO viewData(String id,Boolean seas);
/**

View File

@ -102,4 +102,16 @@ public class CusMainSeasServiceImpl extends ServiceImpl<CusMainSeasMapper,CusMai
this.updateBatchById(toUpdateList);
}
}
/**
* 删除公海客户表信息
*
* @param id TODO
* @author vinjor-M
* @date 10:27 2025/11/25
**/
@Override
public void deleteCus(String id) {
cusMainSeasMapper.deleteByCusId(id);
}
}

View File

@ -54,6 +54,8 @@ public class CusMainServiceImpl extends ServiceImpl<CusMainMapper,CusMain> impl
private ICusTimeAxisService cusTimeAxisService;
@Autowired
private IBaseCountryService baseCountryService;
@Autowired
private ICusMainSeasService cusMainSeasService;
@Override
public IPage<MainVO> queryListPage(MainVO pageReqVO, Page<CusMain> page) {
@ -232,26 +234,36 @@ public class CusMainServiceImpl extends ServiceImpl<CusMainMapper,CusMain> impl
* 查看客户信息面板数据对象
*
* @param id 数据id
* @param seas 是否是公海客户
* @return com.ruoyi.cus.vo.CusViewVO
* @author vinjor-M
* @date 10:34 2025/11/18
**/
@Override
public CusViewVO viewData(String id) {
public CusViewVO viewData(String id,Boolean seas) {
CusViewVO cusViewVO = new CusViewVO();
//1.客户主表信息
CusMain cusMain = this.getById(id);
MainVO mainVO = new MainVO();
if(seas){
//查公海客户信息
CusMainSeas cusMainSeas = cusMainSeasService.getById(id);
BeanUtils.copyProperties(cusMainSeas, mainVO);
mainVO.setCusLabelList(JSON.parseArray(cusMainSeas.getCusLabels()));
cusViewVO.setCusMain(mainVO);
}else{
//正常客户
CusMain cusMain = this.getById(id);
BeanUtils.copyProperties(cusMain, mainVO);
mainVO.setCusLabelList(JSON.parseArray(cusMain.getCusLabels()));
cusViewVO.setCusMain(mainVO);
}
//2.客户联系人信息
CusContacts contacts = new CusContacts();
contacts.setCusId(id);
cusViewVO.setContacts(cusContactsService.selectAllList(contacts));
//3.国旗信息
if (StringUtils.isNotEmpty(cusMain.getCountry())){
cusViewVO.setCountryImg(baseCountryService.queryByName(cusMain.getCountry()).getImg());
if (StringUtils.isNotEmpty(mainVO.getCountry())){
cusViewVO.setCountryImg(baseCountryService.queryByName(mainVO.getCountry()).getImg());
}
return cusViewVO;
}

View File

@ -68,4 +68,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
cms.if_star DESC,
cms.create_time DESC
</select>
<delete id="deleteByCusId">
DELETE FROM cus_main_seas WHERE id = #{cusId}
</delete>
</mapper>

View File

@ -10,10 +10,11 @@ export function listEmail(query) {
}
// 查询客户来往邮件详细
export function getEmail(id) {
export function getEmail(query) {
return request({
url: '/cus/email/' + id,
method: 'get'
url: '/cus/email/getInfo',
method: 'get',
params: query
})
}

View File

@ -25,6 +25,14 @@ export function addSeas(data) {
data: data
})
}
// 移出公海
export function moveToMain(data) {
return request({
url: '/cus/seas/moveToMain',
method: 'post',
data: data
})
}
// 修改公海客户信息
export function updateSeas(data) {

View File

@ -33,14 +33,14 @@
</el-col>
</el-form>
</el-row>
<el-row :gutter="10" class="mb8">
<el-row :gutter="10" class="mb8" v-if="!readOnly">
<el-col :span="1.5">
<el-button type="primary" plain size="mini" @click="newFollow" >
新建跟进
</el-button>
</el-col>
</el-row>
<div v-if="dataList && dataList.length>0" class="data-list">
<div v-if="dataList && dataList.length>0" :class="readOnly?'data-list-readOnly':'data-list'">
<el-collapse>
<el-collapse-item v-for="(item, index) in dataList" :key="index" :title="item.title+'年'" :name="item.title">
<el-timeline>
@ -66,6 +66,10 @@ export default {
type: String,
default: null
},
readOnly: {
type: Boolean,
default: true
},
contactList:{
type: Array,
default: []
@ -170,7 +174,11 @@ export default {
<style scoped>
.data-list {
height: calc(100vh - 400px);
max-height: calc(100vh - 400px);
overflow-y: auto;
}
.data-list-readOnly {
max-height: calc(100vh - 364px);
overflow-y: auto;
}
</style>

View File

@ -1,6 +1,6 @@
<template>
<div >
<el-row :gutter="10" class="mb8">
<el-row :gutter="10" class="mb8" v-if="!readOnly">
<el-col :span="1.5">
<el-button
type="primary"
@ -14,7 +14,7 @@
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
<el-table v-loading="loading" :data="bankList" class="data-list">
<el-table v-loading="loading" :data="bankList" :class="readOnly?'data-list-readOnly':'data-list'">
<el-table-column label="银行账号" align="center" prop="bankAccount" width="160px" />
<el-table-column label="银行名称" align="center" prop="bankName" width="160px"/>
<el-table-column label="币种" align="center" prop="currency" width="100px">
@ -27,7 +27,7 @@
<el-table-column label="银行地址" align="center" prop="bankAddress" width="120px" />
<el-table-column label="分支机构代码" align="center" prop="branchCode" width="160px"/>
<el-table-column label="税号" align="center" prop="taxId"/>
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="120px">
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="120px" v-if="!readOnly">
<template slot-scope="scope">
<el-button
size="mini"
@ -131,6 +131,10 @@ export default {
type: String,
default: null
},
readOnly: {
type: Boolean,
default: true
},
},
name: "bankList",
dicts: ['base_currency'],
@ -282,7 +286,11 @@ export default {
<style scoped>
.data-list {
height: calc(100vh - 408px);
max-height: calc(100vh - 408px);
overflow-y: auto;
}
.data-list-readOnly {
max-height: calc(100vh - 372px);
overflow-y: auto;
}
</style>

View File

@ -184,7 +184,11 @@ export default {
contactList: {
type: Array,
default: []
}
},
readOnly: {
type: Boolean,
default: true
},
},
name: 'emailList',
data() {
@ -280,7 +284,7 @@ export default {
handleUpdate(row) {
this.reset()
const id = row.id
getEmail(id).then(response => {
getEmail({id:id,ifSeas:this.readOnly}).then(response => {
this.form = response.data
this.open = true
this.title = '修改客户来往邮件'
@ -298,7 +302,7 @@ export default {
<style scoped>
.data-list {
height: calc(100vh - 416px);
max-height: calc(100vh - 416px);
overflow-y: auto;
}
.dl-star{

View File

@ -34,7 +34,7 @@
</el-form>
</el-row>
<el-table v-loading="loading" :data="followList" @row-click="handleRowClick">
<el-table v-loading="loading" :data="followList" @row-click="handleRowClick" class="data-list">
<el-table-column label="联系人" align="center" prop="contactName" width="140px">
<template slot-scope="scope">
<el-tooltip :content="scope.row.contactName" placement="top" effect="dark">
@ -96,7 +96,7 @@
</el-table-column>
<el-table-column label="跟进人" align="center" prop="userName" width="120"/>
<el-table-column label="协作人" align="center" prop="otherUserNames" width="180"/>
<el-table-column label="操作" fixed="right" align="center" class-name="small-padding fixed-width" width="180">
<el-table-column label="操作" fixed="right" align="center" class-name="small-padding fixed-width" width="180" v-if="!readOnly">
<template slot-scope="scope">
<el-button
v-if="!scope.row.ifCompleted"
@ -144,6 +144,10 @@ export default {
type: String,
default: null
},
readOnly: {
type: Boolean,
default: true
},
},
name: "followList",
dicts: ['cus_follow_way', 'sys_yes_no', 'cus_cycle_other_type', 'cus_follow_type', 'cus_cycle_type', 'cus_follow_step','cus_busi_name'],
@ -266,6 +270,10 @@ export default {
</script>
<style scoped>
.data-list {
max-height: calc(100vh - 416px);
overflow-y: auto;
}
.dl-text-ellipsis{
white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}

View File

@ -384,7 +384,7 @@ export default {
},
/** 客户代码点击事件 */
handleCodeClick(row) {
this.$router.push({ path: '/cus/viewForm', query: { id: row.id } })
this.$router.push({ path: '/cus/viewForm', query: { id: row.id,readOnly:false } })
},
/** 查询客户信息列表 */
getList() {
@ -518,7 +518,7 @@ export default {
setIfStar({id:row.id, ifStar:ifStar}).then((resp) => {
if(resp.code==200){
this.$message.success("操作成功");
this.getList()
this.handleQuery()
}else{
this.$message.error(resp.msg)
}
@ -547,6 +547,7 @@ export default {
}
}
</script>
<style scoped>
/* 可点击的客户代码样式 */
.clickable-code {

View File

@ -1,7 +1,7 @@
<template>
<div class="customer-detail-page">
<!-- 顶部核心信息区带阴影卡片 -->
<el-card class="top-core-card">
<el-card class="top-core-card" v-loading="loading">
<el-row :gutter="24" class="relative-position">
<!-- 左侧客户信息 -->
<el-col :span="18">
@ -83,27 +83,29 @@
<!-- 右侧操作按钮 -->
<el-col :span="6">
<div class="operation-buttons">
<el-button size="mini" class="btn-edit" @click="goEditCus">
<el-button size="mini" class="btn-edit" @click="goEditCus" v-if="!readOnly">
<i class="el-icon-edit"></i> 编辑
</el-button>
<el-button size="mini" class="btn-email">
<el-button size="mini" class="btn-email" v-if="!readOnly">
<i class="el-icon-message"></i> 写邮件
</el-button>
<el-button size="mini" class="btn-follow" @click="newFollow">
<el-button size="mini" class="btn-follow" @click="newFollow" v-if="!readOnly">
<i class="el-icon-plus"></i> 新建跟进
</el-button>
<el-dropdown class="more-dropdown" @command="handleCommand">
<el-button type="text" size="mini" class="btn-more">
更多 <i class="el-icon-arrow-down el-icon--mini"></i>
更多操作 <i class="el-icon-arrow-down el-icon--mini"></i>
</el-button>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item command="goSea">转入公海</el-dropdown-item>
<el-dropdown-item command="move">移交客户</el-dropdown-item>
<el-dropdown-item command="goSea" v-if="!readOnly">转入公海</el-dropdown-item>
<el-dropdown-item command="goMain" v-if="readOnly">移出公海</el-dropdown-item>
<el-dropdown-item command="move" v-if="!readOnly">移交客户</el-dropdown-item>
<el-dropdown-item command="del">删除</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</div>
</el-col>
<el-tag class="dl-tag" effect="dark" size="small" type="warning">公海客户</el-tag>
<!-- <div class="last-contact-date">最近联系 2024-12-11</div>-->
</el-row>
</el-card>
@ -113,19 +115,19 @@
<el-col :span="18" class="tab-container">
<el-tabs v-model="activeTab" type="card" class="custom-tabs">
<el-tab-pane label="时间轴" name="timeline" class="tab-pane-item">
<time-line :cusId="id" ref="timeLine" :contactList="contactList" @newFollow="newFollow"/>
<time-line :readOnly="readOnly" :cusId="id" ref="timeLine" :contactList="contactList" @newFollow="newFollow"/>
</el-tab-pane>
<el-tab-pane label="客户资料" name="info" class="tab-pane-item">
<company-info :cusId="id" ref="companyInfo"/>
</el-tab-pane>
<el-tab-pane label="银行信息" name="bank" class="tab-pane-item">
<bank-list :cusId="id" ref="bankList"/>
<bank-list :readOnly="readOnly" :cusId="id" ref="bankList"/>
</el-tab-pane>
<el-tab-pane label="邮件" name="email" class="tab-pane-item">
<email-list :cusId="id" ref="emailList" :contactList="contactList"/>
<email-list :readOnly="readOnly" :cusId="id" ref="emailList" :contactList="contactList"/>
</el-tab-pane>
<el-tab-pane label="跟进" name="follow" class="tab-pane-item">
<follow-list :cusId="id" ref="followList" @editFollow="editFollow"/>
<follow-list :readOnly="readOnly" :cusId="id" ref="followList" @editFollow="editFollow"/>
</el-tab-pane>
<el-tab-pane label="WhatsApp" name="whatsapp" class="tab-pane-item">
<whats-app :cusId="id" ref="whatsApp"/>
@ -133,7 +135,7 @@
</el-tabs>
</el-col>
<!-- 右侧联系人区域 -->
<el-col :span="6" class="contacts-col">
<el-col :span="6" class="contacts-col" v-loading="loading">
<el-card class="contact-sidebar">
<!-- 联系人头部 -->
<div slot="header" class="contact-header">
@ -141,14 +143,14 @@
<div class="search-box">
<el-input
v-model="searchText"
@change="filterContact"
@input="filterContact"
placeholder="搜索名称/邮箱"
size="mini"
prefix-icon="el-icon-search"
class="contact-search"
/>
</div>
<div class="contact-add" @click="newContanct">
<div class="contact-add" @click="newContanct" v-if="!readOnly">
<i class="el-icon-plus"/>
</div>
</div>
@ -168,7 +170,7 @@
<i class="el-icon-copy-document icon-color" @click="$copyText(item.name)"/>
</div>
<!-- 操作按钮-->
<div class="contact-opt">
<div class="contact-opt" v-if="!readOnly">
<i class="el-icon-edit-outline cursor-pointer" @click="editContact(item)"/>
<!-- 删除-->
<i class="el-icon-delete delete-icon" @click="removeContacts(item.idStr)"/>
@ -220,7 +222,8 @@
<script>
import { updateUser} from '@/api/cus/manager'
import { delMain} from "@/api/cus/main";
import { delMain,moveToSeas} from "@/api/cus/main";
import { delSeas,moveToMain} from "@/api/cus/seas";
import { delContacts } from "@/api/cus/contacts";
import TimeLine from '../axis/timeLine'
import CompanyInfo from '../company/companyInfo'
@ -240,6 +243,9 @@ export default {
components: { SelectAllUser, LabelForm, EditForm, FollowForm, WhatsApp, FollowList, EmailList, BankList, CompanyInfo, TimeLine },
data() {
return {
loading:true,
//
readOnly:true,
id:"",
//
searchText:"",
@ -260,6 +266,9 @@ export default {
}
},
created() {
if(this.$route.query.readOnly){
this.readOnly = false
}
if(this.$route.query.id){
this.id = this.$route.query.id;
this.getMainInfo()
@ -319,8 +328,12 @@ export default {
getImg(imgPath) {
return require(`@/assets/flags/${imgPath}`);
},
/**
* 获取客户主要信息
*/
getMainInfo() {
viewData({id:this.id}).then(res => {
viewData({id:this.id,seas:this.readOnly}).then(res => {
this.loading = false
if(res.code==200){
this.mainInfo = res.data
this.contactList = res.data.contacts
@ -376,7 +389,12 @@ export default {
handleCommand(command){
switch (command) {
case 'goSea':
// TODO
//
this.moveToSeasFun()
break
case 'goMain':
//
this.moveToMain()
break
case 'del':
this.handleDelete()
@ -386,11 +404,53 @@ export default {
break
}
},
/**
*移出公海
*/
moveToMain(){
let that = this
this.$modal.confirm('是否确认将编号为"' + this.mainInfo.cusMain.cusCode + '"的客户移出公海?').then(function() {
moveToMain({ id:this.id}).then((resp) => {
if(resp.code==200){
that.$modal.msgSuccess('操作成功')
setTimeout(()=>{
this.closeAndBack()
},500)
}else{
that.$message.error(resp.msg)
}
})
})
},
/**
* 移入公海
*/
moveToSeasFun(){
let that = this
this.$modal.confirm('是否确认将编号为"' + this.mainInfo.cusMain.cusCode + '"的客户移入公海?').then(function() {
moveToSeas({ id:this.id}).then((resp) => {
if(resp.code==200){
that.$modal.msgSuccess('操作成功')
setTimeout(()=>{
this.closeAndBack()
},500)
}else{
that.$message.error(resp.msg)
}
})
})
},
/** 删除按钮操作 */
handleDelete() {
let that = this
let id = this.id
this.$modal.confirm('是否确认删除改客户?').then(function() {
this.$modal.confirm('是否确认删除该客户?').then(function() {
if(that.readOnly){
//
return delSeas(id)
}else{
return delMain(id);
}
}).then(() => {
this.$modal.msgSuccess("删除成功");
setTimeout(()=>{
@ -703,6 +763,7 @@ export default {
.contact-header {
display: flex;
gap: 20px;
justify-content: space-between;
align-items: center;
padding-bottom: 1px;
@ -715,7 +776,7 @@ export default {
}
.contact-search {
width: 140px;
width: 100%;
border-radius: 4px;
}
@ -768,7 +829,9 @@ export default {
.contact-item:hover{
border: 1px solid #1890ff;
}
.search-box{
flex: 1;
}
.contact-add{
display: flex;
cursor: pointer;
@ -933,4 +996,9 @@ export default {
cursor: pointer;
}
.dl-tag{
position: absolute;
left: -8px;
bottom: -8px;
}
</style>

View File

@ -1,14 +1,6 @@
<template>
<div class="app-container">
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
<el-form-item label="客户标签(多个英文逗号隔开)" prop="cusLabels">
<el-input
v-model="queryParams.cusLabels"
placeholder="请输入客户标签(多个英文逗号隔开)"
clearable
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="90px">
<el-form-item label="客户代码" prop="cusCode">
<el-input
v-model="queryParams.cusCode"
@ -33,37 +25,15 @@
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<el-form-item label="国家/地区" prop="country">
<el-input
v-model="queryParams.country"
placeholder="请输入国家/地区"
clearable
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<el-form-item label="时区" prop="zoneName">
<el-input
v-model="queryParams.zoneName"
placeholder="请输入时区"
clearable
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<el-form-item label="主营产品(多个英文逗号隔开)" prop="mainProds">
<el-input
v-model="queryParams.mainProds"
placeholder="请输入主营产品(多个英文逗号隔开)"
clearable
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<el-form-item label="企业网站" prop="siteUrl">
<el-input
v-model="queryParams.siteUrl"
placeholder="请输入企业网站"
clearable
@keyup.enter.native="handleQuery"
<el-form-item label="客户类型" prop="cusType">
<el-select v-model="queryParams.cusType" placeholder="请选择客户类型" clearable>
<el-option
v-for="dict in dict.type.cus_type"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
@ -74,35 +44,13 @@
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button
type="primary"
type="info"
plain
icon="el-icon-plus"
icon="el-icon-upload2"
size="mini"
@click="handleAdd"
v-hasPermi="['cus:seas:add']"
>新增</el-button>
</el-col>
<el-col :span="1.5">
<el-button
type="success"
plain
icon="el-icon-edit"
size="mini"
:disabled="single"
@click="handleUpdate"
v-hasPermi="['cus:seas:edit']"
>修改</el-button>
</el-col>
<el-col :span="1.5">
<el-button
type="danger"
plain
icon="el-icon-delete"
size="mini"
:disabled="multiple"
@click="handleDelete"
v-hasPermi="['cus:seas:remove']"
>删除</el-button>
@click="handleImport"
>导入
</el-button>
</el-col>
<el-col :span="1.5">
<el-button
@ -114,50 +62,161 @@
v-hasPermi="['cus:seas:export']"
>导出</el-button>
</el-col>
<el-col :span="1.5">
<el-button
type="info"
plain
icon="el-icon-upload2"
size="mini"
@click="handleImport"
>导入
</el-button>
</el-col>
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
<el-table v-loading="loading" :data="seasList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="主键" align="center" prop="id" />
<el-table-column label="是否星标客户(0 否|1 是)" align="center" prop="ifStar" />
<el-table-column label="客户标签(多个英文逗号隔开)" align="center" prop="cusLabels" />
<el-table-column label="客户代码" align="center" prop="cusCode" />
<el-table-column label="客户名称" align="center" prop="fullName" />
<el-table-column label="客户简称" align="center" prop="shortName" />
<el-table-column label="客户类型" align="center" prop="cusType" />
<el-table-column label="国家/地区" align="center" prop="country" />
<el-table-column label="时区" align="center" prop="zoneName" />
<el-table-column label="主营产品(多个英文逗号隔开)" align="center" prop="mainProds" />
<el-table-column label="企业网站" align="center" prop="siteUrl" />
<el-table-column label="备注" align="center" prop="remark" />
<el-table-column label="附件" align="center" prop="files" />
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<el-table v-loading="loading" :data="seasList">
<el-table-column fixed label="客户代码" align="center" prop="cusCode" width="130">
<template slot-scope="scope">
<span class="clickable-code" @click="handleCodeClick(scope.row)">
{{ scope.row.cusCode }}
</span>
</template>
</el-table-column>
<el-table-column fixed label="客户名称" align="center" prop="fullName" width="130">
<template slot-scope="scope">
<el-tooltip :content="scope.row.fullName" placement="top" effect="dark">
<div class="text-ellipsis-single">{{ scope.row.fullName }}</div>
</el-tooltip>
</template>
</el-table-column>
<el-table-column label="星标" align="center" prop="ifStar">
<template slot-scope="scope">
<i v-if="scope.row.ifStar" title="取消星标" class="el-icon-star-on"
style="color: #ffba00;font-size: 18px;"></i>
<i v-else class="el-icon-star-off" title="星标" style="cursor: pointer" @click="setIfStar(scope.row,true)"></i>
</template>
</el-table-column>
<el-table-column label="客户简称" align="center" prop="shortName" width="130">
<template slot-scope="scope">
<el-tooltip :content="scope.row.shortName" placement="top" effect="dark">
<div class="text-ellipsis-single">{{ scope.row.shortName }}</div>
</el-tooltip>
</template>
</el-table-column>
<el-table-column label="跟进阶段" align="center" prop="followStep" width="120">
<template slot-scope="scope">
<el-tooltip :content="scope.row.followStep" placement="top" effect="dark">
<div class="text-ellipsis-single">{{ scope.row.followStep }}</div>
</el-tooltip>
</template>
</el-table-column>
<el-table-column label="标签" align="center" prop="cusLabels" width="100">
<template slot-scope="scope">
<el-tooltip
placement="top-start"
effect="light"
:disabled="!scope.row.cusLabelList || scope.row.cusLabelList.length === 0"
>
<!-- 单元格内容首个标签 + 编辑图标优化抖动问题 -->
<div class="tag-cell-container">
<!-- 默认显示第一个标签空时显示无标签 -->
<el-tag
v-if="scope.row.cusLabelList && scope.row.cusLabelList.length > 0"
:color="scope.row.cusLabelList[0].color"
size="mini"
effect="dark"
class="default-tag"
>
{{ scope.row.cusLabelList[0].name }}
</el-tag>
<!-- 图标容器固定尺寸避免布局偏移 -->
<i class="el-icon-collection-tag edit-tag-icon" @click="setCusLabel(scope.row)"></i>
</div>
<div slot="content" class="tooltip-tag-list">
<el-tag
v-for="(tag, index) in scope.row.cusLabelList"
:key="index"
:color="tag.color"
size="mini"
effect="dark"
class="tooltip-tag-item"
>
{{ tag.name }}
</el-tag>
</div>
</el-tooltip>
</template>
</el-table-column>
<el-table-column label="客户类型" align="center" prop="cusType" width="120"/>
<el-table-column label="客户来源" align="center" prop="cusFrom" width="120"/>
<el-table-column label="客户等级" align="center" prop="cusLevel" width="120"/>
<el-table-column label="国家/地区" align="center" prop="country" width="120">
<template slot-scope="scope">
<el-tooltip :content="scope.row.country" placement="top" effect="dark">
<div class="text-ellipsis-single">{{ scope.row.country }}</div>
</el-tooltip>
</template>
</el-table-column>
<el-table-column label="时区" align="center" prop="zoneName" width="120">
<template slot-scope="scope">
<el-tooltip :content="scope.row.zoneName" placement="top" effect="dark">
<div class="text-ellipsis-single">{{ scope.row.zoneName }}</div>
</el-tooltip>
</template>
</el-table-column>
<el-table-column label="主营产品" align="center" prop="mainProds" width="140">
<template slot-scope="scope">
<el-tooltip :content="scope.row.mainProds" placement="top" effect="dark">
<div class="text-ellipsis-single">{{ scope.row.mainProds }}</div>
</el-tooltip>
</template>
</el-table-column>
<el-table-column label="业务类型" align="center" prop="busiType" width="120"/>
<el-table-column label="企业网站" align="center" prop="siteUrl">
<template slot-scope="scope">
<el-tooltip :content="scope.row.siteUrl" placement="top" effect="dark">
<div class="text-ellipsis-single">{{ scope.row.siteUrl }}</div>
</el-tooltip>
</template>
</el-table-column>
<el-table-column label="联系地址" align="center" prop="contactAddress" width="120">
<template slot-scope="scope">
<el-tooltip :content="scope.row.contactAddress" placement="top" effect="dark">
<div class="text-ellipsis-single">{{ scope.row.contactAddress }}</div>
</el-tooltip>
</template>
</el-table-column>
<el-table-column label="联系人" align="center" prop="contactName" width="120">
<template slot-scope="scope">
<el-tooltip :content="scope.row.contactName" placement="top" effect="dark">
<div class="text-ellipsis-single">{{ scope.row.contactName }}</div>
</el-tooltip>
</template>
</el-table-column>
<el-table-column label="业务员" align="center" prop="userName" width="120">
<template slot-scope="scope">
<el-tooltip :content="scope.row.userName" placement="top" effect="dark">
<div class="text-ellipsis-single">{{ scope.row.userName }}</div>
</el-tooltip>
</template>
</el-table-column>
<el-table-column label="最近联系时间" align="center" prop="contactTime" width="120">
<template slot-scope="scope">
<span>{{ parseTime(scope.row.contactTime, '{y}-{m}-{d}') }}</span>
</template>
</el-table-column>
<el-table-column label="移入公海时间" align="center" prop="createTime" width="120">
<template slot-scope="scope">
<span>{{ parseTime(scope.row.createTime, '{y}-{m}-{d}') }}</span>
</template>
</el-table-column>
<el-table-column fixed="right" label="操作" align="center" class-name="small-padding fixed-width" width="200">
<template slot-scope="scope">
<el-button
size="mini"
type="text"
icon="el-icon-edit"
@click="handleUpdate(scope.row)"
v-hasPermi="['cus:seas:edit']"
>修改</el-button>
@click="moveToMain(scope.row)"
>移出公海
</el-button>
<el-button
size="mini"
type="text"
icon="el-icon-delete"
@click="handleDelete(scope.row)"
v-hasPermi="['cus:seas:remove']"
>删除</el-button>
v-hasPermi="['cus:main:remove']"
>删除
</el-button>
</template>
</el-table-column>
</el-table>
@ -169,50 +228,8 @@
:limit.sync="queryParams.pageSize"
@pagination="getList"
/>
<!-- 添加或修改公海客户信息对话框 -->
<el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>
<el-form ref="form" :model="form" :rules="rules" label-width="80px">
<el-form-item label="是否星标客户(0 否|1 是)" prop="ifStar">
<el-input v-model="form.ifStar" placeholder="请输入是否星标客户(0 否|1 是)" />
</el-form-item>
<el-form-item label="客户标签(多个英文逗号隔开)" prop="cusLabels">
<el-input v-model="form.cusLabels" placeholder="请输入客户标签(多个英文逗号隔开)" />
</el-form-item>
<el-form-item label="客户代码" prop="cusCode">
<el-input v-model="form.cusCode" placeholder="请输入客户代码" />
</el-form-item>
<el-form-item label="客户名称" prop="fullName">
<el-input v-model="form.fullName" placeholder="请输入客户名称" />
</el-form-item>
<el-form-item label="客户简称" prop="shortName">
<el-input v-model="form.shortName" placeholder="请输入客户简称" />
</el-form-item>
<el-form-item label="国家/地区" prop="country">
<el-input v-model="form.country" placeholder="请输入国家/地区" />
</el-form-item>
<el-form-item label="时区" prop="zoneName">
<el-input v-model="form.zoneName" placeholder="请输入时区" />
</el-form-item>
<el-form-item label="主营产品(多个英文逗号隔开)" prop="mainProds">
<el-input v-model="form.mainProds" placeholder="请输入主营产品(多个英文逗号隔开)" />
</el-form-item>
<el-form-item label="企业网站" prop="siteUrl">
<el-input v-model="form.siteUrl" placeholder="请输入企业网站" />
</el-form-item>
<el-form-item label="备注" prop="remark">
<el-input v-model="form.remark" type="textarea" placeholder="请输入内容" />
</el-form-item>
<el-form-item label="附件" prop="files">
<el-input v-model="form.files" type="textarea" placeholder="请输入内容" />
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button type="primary" @click="submitForm"> </el-button>
<el-button @click="cancel"> </el-button>
</div>
</el-dialog>
<!-- 给客户设置标签-->
<label-form ref="labelForm" @ok="getList"/>
<!-- 客户信息导入对话框 -->
<el-dialog :title="upload.title" :visible.sync="upload.open" width="400px" append-to-body>
<el-upload ref="upload" :limit="1" accept=".xlsx, .xls" :headers="upload.headers"
@ -235,28 +252,23 @@
</template>
<script>
import { listSeas, getSeas, delSeas, addSeas, updateSeas } from "@/api/cus/seas";
import { listSeas, getSeas, delSeas, addSeas, updateSeas,moveToMain } from "@/api/cus/seas";
import { getToken } from '@/utils/auth'
import LabelForm from '../main/labelForm'
export default {
name: "Seas",
components: { LabelForm },
dicts: ['cus_main_product', 'cus_label', 'cus_type', 'sys_user_sex', 'cus_from', 'cus_level', 'cus_busi_type', 'cus_follow_step'],
data() {
return {
//
loading: true,
//
ids: [],
//
single: true,
//
multiple: true,
//
showSearch: true,
//
total: 0,
//
//
seasList: [],
//
title: "",
//
open: false,
//
@ -269,14 +281,17 @@ export default {
shortName: null,
cusType: null,
country: null,
zoneName: null,
mainProds: null,
siteUrl: null,
remark: null,
files: null,
mainProds: null
},
//
contactList: [],
chooseCusId: null,
//
cusMain: {
id: '',
fullName: '',
cusCode: ''
},
//
form: {},
//
upload: {
//
@ -292,15 +307,26 @@ export default {
//
url: process.env.VUE_APP_BASE_API + '/cus/main/importCusSeas'
},
//
rules: {
}
};
},
activated() {
//
this.getList()
},
created() {
this.getList();
this.getList()
},
methods: {
/**
* 给客户设置标签
*/
setCusLabel(row) {
this.$refs.labelForm.setTags(row)
},
/** 客户代码点击事件 */
handleCodeClick(row) {
this.$router.push({ path: '/cus/viewForm', query: { id: row.id } })
},
/** 查询公海客户信息列表 */
getList() {
this.loading = true;
@ -332,35 +358,6 @@ export default {
this.upload.title = '用户导入'
this.upload.open = true
},
//
cancel() {
this.open = false;
this.reset();
},
//
reset() {
this.form = {
id: null,
ifStar: null,
cusLabels: null,
cusCode: null,
fullName: null,
shortName: null,
cusType: null,
country: null,
zoneName: null,
mainProds: null,
siteUrl: null,
remark: null,
files: null,
delFlag: null,
creator: null,
createTime: null,
updater: null,
updateTime: null
};
this.resetForm("form");
},
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.pageNum = 1;
@ -371,53 +368,27 @@ export default {
this.resetForm("queryForm");
this.handleQuery();
},
//
handleSelectionChange(selection) {
this.ids = selection.map(item => item.id)
this.single = selection.length!==1
this.multiple = !selection.length
},
/** 新增按钮操作 */
handleAdd() {
this.reset();
this.open = true;
this.title = "添加公海客户信息";
},
/** 修改按钮操作 */
handleUpdate(row) {
this.reset();
const id = row.id || this.ids
getSeas(id).then(response => {
this.form = response.data;
this.open = true;
this.title = "修改公海客户信息";
});
},
/** 提交按钮 */
submitForm() {
this.$refs["form"].validate(valid => {
if (valid) {
if (this.form.id != null) {
updateSeas(this.form).then(response => {
this.$modal.msgSuccess("修改成功");
this.open = false;
this.getList();
});
/**
*移出公海
*/
moveToMain(row){
let that = this
this.$modal.confirm('是否确认将编号为"' + row.cusCode + '"的客户移出公海?').then(function() {
moveToMain({ id:row.id}).then((resp) => {
if(resp.code==200){
that.handleQuery()
that.$modal.msgSuccess('操作成功')
}else{
addSeas(this.form).then(response => {
this.$modal.msgSuccess("新增成功");
this.open = false;
this.getList();
});
that.$message.error(resp.msg)
}
}
});
})
})
},
/** 删除按钮操作 */
handleDelete(row) {
const ids = row.id || this.ids;
this.$modal.confirm('是否确认删除公海客户信息编号为"' + ids + '"的数据项').then(function() {
return delSeas(ids);
const id = row.id
this.$modal.confirm('是否确认删除编号为"' + row.cusCode + '"的公海客户?').then(function() {
return delSeas(id);
}).then(() => {
this.getList();
this.$modal.msgSuccess("删除成功");
@ -432,3 +403,68 @@ export default {
}
};
</script>
<style scoped>
/* 可点击的客户代码样式 */
.clickable-code {
color: #1890ff;
cursor: pointer;
text-decoration: underline;
}
.clickable-code:hover {
color: #40a9ff;
}
/* 核心优化:使用固定容器 + 绝对定位避免布局抖动 */
.tag-cell-container {
display: flex;
align-items: center;
justify-content: center;
padding: 4px 0;
/* 关键:固定行高,防止内容撑开高度变化 */
min-height: 24px;
}
/* 实际编辑图标:绝对定位覆盖在占位符上 */
.edit-tag-icon {
cursor: pointer;
display: none;
}
/* 鼠标悬停时显示图标(仅改变透明度,不影响布局) */
.tag-cell-container:hover .edit-tag-icon {
display: inline-block;
}
.edit-tag-icon:hover {
color: #409eff;
}
/* 其他原有样式保持不变 */
.default-tag {
max-width: 100px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
::v-deep .tooltip-tag-list {
display: flex;
flex-wrap: wrap;
gap: 6px;
padding: 6px 8px;
}
::v-deep .tooltip-tag-item {
max-width: 120px;
margin: 5px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.el-tag {
border: 1px solid transparent !important;
}
</style>