0325-海宁pad走访
This commit is contained in:
@@ -312,6 +312,13 @@ public class SysCampaignController extends BaseController {
|
||||
.stream().collect(HashMap::new, (m, v) -> m.put(v.getUuid(),v.getModelName()), HashMap::putAll)));
|
||||
}
|
||||
|
||||
@PostMapping("/updateVisitInfoFeedback")
|
||||
@ApiOperation("更新PAD走访反馈")
|
||||
@Log(title ="pad走访记录-更新走访反馈", businessType = BusinessType.UPDATE)
|
||||
public AjaxResult updateVisitInfoFeedback(@RequestBody VisitInfoFeedbackUpdateDTO updateDTO) {
|
||||
return toAjax(sysCampaignService.updateVisitInfoFeedback(updateDTO));
|
||||
}
|
||||
|
||||
@PostMapping("/delete")
|
||||
@ApiOperation("根据campaignId删除任务")
|
||||
@Log(title = "走访-根据campaignId删除任务")
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
package com.ruoyi.ibs.list.domain;
|
||||
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
public class VisitFeedbackItemDTO implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@ApiModelProperty(value = "反馈类型")
|
||||
private String feedbackType;
|
||||
|
||||
@ApiModelProperty(value = "反馈产品列表")
|
||||
private List<String> products;
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package com.ruoyi.ibs.list.domain;
|
||||
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
public class VisitInfoFeedbackUpdateDTO implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@ApiModelProperty(value = "走访记录ID")
|
||||
private Long id;
|
||||
|
||||
@ApiModelProperty(value = "走访渠道")
|
||||
private String source;
|
||||
|
||||
@ApiModelProperty(value = "客户意愿结构化数据")
|
||||
private List<VisitFeedbackItemDTO> feedbackItems;
|
||||
|
||||
@ApiModelProperty(value = "客户意愿拼接值")
|
||||
private String intentionProductValue;
|
||||
|
||||
private String userName;
|
||||
|
||||
private String userRole;
|
||||
|
||||
private String deptId;
|
||||
}
|
||||
@@ -156,6 +156,8 @@ public interface SysCampaignMapper extends BaseMapper<SysCampaign> {
|
||||
|
||||
List<VisitInfoVO> selectVisitInfoList(VisitInfoDTO visitInfoDTO);
|
||||
|
||||
int updateVisitInfoFeedback(VisitInfoFeedbackUpdateDTO updateDTO);
|
||||
|
||||
@Update("UPDATE sys_campaign SET del_flag = '2' where campaign_id = #{campaignId}")
|
||||
int deleteSysCampaignByCampaignId(@Param("campaignId") String campaignId);
|
||||
|
||||
|
||||
@@ -115,5 +115,7 @@ public interface ISysCampaignService {
|
||||
|
||||
public List<VisitInfoVO> selectVisitInfoVoList(VisitInfoDTO visitInfoDTO);
|
||||
|
||||
int updateVisitInfoFeedback(VisitInfoFeedbackUpdateDTO updateDTO);
|
||||
|
||||
public int deleteSysCampaign(String campaignId);
|
||||
}
|
||||
|
||||
@@ -2178,6 +2178,23 @@ public class SysCampaignServiceImpl implements ISysCampaignService
|
||||
return sysCampaignMapper.selectVisitInfoList(visitInfoDTO);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int updateVisitInfoFeedback(VisitInfoFeedbackUpdateDTO updateDTO) {
|
||||
if (updateDTO == null || updateDTO.getId() == null) {
|
||||
throw new ServiceException("走访记录ID不能为空");
|
||||
}
|
||||
updateDTO.setUserName(SecurityUtils.getUsername());
|
||||
updateDTO.setUserRole(SecurityUtils.userRole());
|
||||
updateDTO.setDeptId(String.valueOf(SecurityUtils.getDeptId()));
|
||||
updateDTO.setSource(StringUtils.trimToNull(updateDTO.getSource()));
|
||||
updateDTO.setIntentionProductValue(buildIntentionProductValue(updateDTO.getFeedbackItems()));
|
||||
int rows = sysCampaignMapper.updateVisitInfoFeedback(updateDTO);
|
||||
if (rows <= 0) {
|
||||
throw new ServiceException("走访记录不存在或无权限修改");
|
||||
}
|
||||
return rows;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int deleteSysCampaign(String campaignId) {
|
||||
SysCampaign sysCampaign = sysCampaignMapper.selectSysCampaignByCampaignId(campaignId);
|
||||
@@ -2186,4 +2203,28 @@ public class SysCampaignServiceImpl implements ISysCampaignService
|
||||
}
|
||||
return sysCampaignMapper.deleteSysCampaignByCampaignId(campaignId);
|
||||
}
|
||||
private String buildIntentionProductValue(List<VisitFeedbackItemDTO> feedbackItems) {
|
||||
if (feedbackItems == null || feedbackItems.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
String joinedValue = feedbackItems.stream()
|
||||
.filter(Objects::nonNull)
|
||||
.map(item -> {
|
||||
String feedbackType = StringUtils.trimToNull(item.getFeedbackType());
|
||||
List<String> products = Optional.ofNullable(item.getProducts())
|
||||
.orElse(Collections.emptyList())
|
||||
.stream()
|
||||
.map(StringUtils::trimToNull)
|
||||
.filter(Objects::nonNull)
|
||||
.distinct()
|
||||
.collect(Collectors.toList());
|
||||
if (feedbackType == null || products.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
return feedbackType + ":" + String.join(",", products);
|
||||
})
|
||||
.filter(Objects::nonNull)
|
||||
.collect(Collectors.joining(";"));
|
||||
return StringUtils.trimToNull(joinedValue);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1425,4 +1425,22 @@
|
||||
order by vi.sign_in_time desc
|
||||
</select>
|
||||
|
||||
<update id="updateVisitInfoFeedback" parameterType="VisitInfoFeedbackUpdateDTO">
|
||||
update visit_info vi
|
||||
left join sys_dept d on vi.dept_id = d.dept_id
|
||||
set vi.source = #{source},
|
||||
vi.intention_product_value = #{intentionProductValue},
|
||||
vi.update_by = #{userName},
|
||||
vi.update_time = sysdate()
|
||||
where vi.id = #{id}
|
||||
<if test="userRole != null">
|
||||
<choose>
|
||||
<when test="userRole == 'manager'"> and vi.vis_id = #{userName} </when>
|
||||
<when test="userRole == 'outlet'"> and d.dept_id = #{deptId} </when>
|
||||
<when test="userRole == 'branch'"> and (d.dept_id = #{deptId} or find_in_set(#{deptId},d.ancestors)) </when>
|
||||
<when test="userRole in {'head', 'ops', 'public', 'private'}"> and left(d.dept_id,3) = left(#{deptId},3) </when>
|
||||
</choose>
|
||||
</if>
|
||||
</update>
|
||||
|
||||
</mapper>
|
||||
|
||||
@@ -6,4 +6,12 @@ export function getPADVisitRecord(query) {
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export function updatePADVisitFeedback(data) {
|
||||
return request({
|
||||
url: `/system/campaign/updateVisitInfoFeedback`,
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
@@ -380,9 +380,14 @@
|
||||
<el-table-column align="left" prop="filename" label="批量导入文件名" show-overflow-tooltip width="180px" v-if="columns[23].visible"></el-table-column>
|
||||
<el-table-column align="left" prop="outCallStatus" label="外呼状态" show-overflow-tooltip width="120px" v-if="columns[24].visible"></el-table-column>
|
||||
<el-table-column align="left" prop="outCallIntention" label="客户意愿" show-overflow-tooltip width="140px" v-if="columns[25].visible"></el-table-column>
|
||||
<el-table-column align="left" prop="source" label="来源" show-overflow-tooltip width="120px" v-if="columns[26].visible"></el-table-column>
|
||||
<el-table-column align="left" prop="source" label="走访渠道" show-overflow-tooltip width="120px" v-if="columns[26].visible"></el-table-column>
|
||||
<el-table-column align="left" prop="analysisValue" label="nlp模型提取" show-overflow-tooltip width="140px" v-if="columns[27].visible"></el-table-column>
|
||||
<el-table-column align="left" prop="facility" label="预授信额度" show-overflow-tooltip width="140px" v-if="columns[28].visible"></el-table-column>
|
||||
<el-table-column align="center" label="操作" fixed="right" width="100">
|
||||
<template slot-scope="scope">
|
||||
<el-button type="text" size="mini" @click="handleEditFeedback(scope.row)">编辑</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<el-pagination
|
||||
@size-change="handleSizeChange"
|
||||
@@ -393,22 +398,87 @@
|
||||
:total="total"
|
||||
:current-page="pageNum"
|
||||
></el-pagination>
|
||||
|
||||
<el-dialog
|
||||
title="编辑走访反馈"
|
||||
:visible.sync="feedbackDialogVisible"
|
||||
width="960px"
|
||||
custom-class="feedback-dialog"
|
||||
append-to-body
|
||||
@close="resetFeedbackForm"
|
||||
>
|
||||
<el-form ref="feedbackFormRef" :model="feedbackForm" label-width="100px" class="feedback-form">
|
||||
<el-form-item label="走访渠道" required>
|
||||
<el-radio-group v-model="feedbackForm.source">
|
||||
<el-radio v-for="item in sourceOptions" :key="item" :label="item">{{ item }}</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="客户意愿" required>
|
||||
<div class="feedback-groups">
|
||||
<section v-for="type in feedbackTypeOptions" :key="type" class="feedback-group">
|
||||
<div class="feedback-group__title">{{ type }}</div>
|
||||
<el-checkbox-group v-model="feedbackForm.feedbackSelections[type]">
|
||||
<el-checkbox
|
||||
v-for="product in feedbackProductOptions"
|
||||
:key="`${type}-${product}`"
|
||||
:label="product"
|
||||
>
|
||||
{{ product }}
|
||||
</el-checkbox>
|
||||
</el-checkbox-group>
|
||||
</section>
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item label="预览结果">
|
||||
<div class="feedback-preview">{{ feedbackPreview || "-" }}</div>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<span slot="footer">
|
||||
<el-button @click="feedbackDialogVisible = false">取消</el-button>
|
||||
<el-button type="primary" :loading="feedbackSubmitting" @click="handleSubmitFeedback">确定</el-button>
|
||||
</span>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import { mapGetters } from "vuex";
|
||||
import { getPADVisitRecord } from "@/api/task/PADvisitRecord.js";
|
||||
import { Message } from "element-ui";
|
||||
import { getPADVisitRecord, updatePADVisitFeedback } from "@/api/task/PADvisitRecord.js";
|
||||
|
||||
const SOURCE_OPTIONS = ["企业微信", "PAD"];
|
||||
const FEEDBACK_TYPE_OPTIONS = ["拒绝", "考虑", "意愿", "其他", "愿意", "现场办理"];
|
||||
const FEEDBACK_PRODUCT_OPTIONS = [
|
||||
"丰收互联",
|
||||
"贷款",
|
||||
"电子社保卡签约",
|
||||
"基金",
|
||||
"贵金属",
|
||||
"信用卡",
|
||||
"医保电子凭证",
|
||||
"社保卡",
|
||||
"理财签约"
|
||||
];
|
||||
|
||||
function createEmptyFeedbackSelections() {
|
||||
return FEEDBACK_TYPE_OPTIONS.reduce((result, item) => {
|
||||
result[item] = [];
|
||||
return result;
|
||||
}, {});
|
||||
}
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
selectedTab: "0",
|
||||
tableData: [],
|
||||
Loading: false,
|
||||
feedbackDialogVisible: false,
|
||||
feedbackSubmitting: false,
|
||||
total: 0,
|
||||
pageSize: 10,
|
||||
pageNum: 1,
|
||||
sourceOptions: SOURCE_OPTIONS,
|
||||
feedbackTypeOptions: FEEDBACK_TYPE_OPTIONS,
|
||||
feedbackProductOptions: FEEDBACK_PRODUCT_OPTIONS,
|
||||
searchForm: {
|
||||
visName: "",
|
||||
visId: "",
|
||||
@@ -447,14 +517,19 @@ export default {
|
||||
{ key: 23, label: "批量导入文件名", visible: true },
|
||||
{ key: 24, label: "外呼状态", visible: true },
|
||||
{ key: 25, label: "客户意愿", visible: true },
|
||||
{ key: 26, label: "来源", visible: true },
|
||||
{ key: 26, label: "走访渠道", visible: true },
|
||||
{ key: 27, label: "nlp模型提取", visible: true },
|
||||
{ key: 28, label: "预授信额度", visible: true }
|
||||
],
|
||||
columns875: [
|
||||
{ key: 29, label: "异常走访标签", visible: true },
|
||||
{ key: 30, label: "异常走访信息", visible: true },
|
||||
]
|
||||
],
|
||||
feedbackForm: {
|
||||
id: null,
|
||||
source: "",
|
||||
feedbackSelections: createEmptyFeedbackSelections()
|
||||
}
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
@@ -493,6 +568,9 @@ export default {
|
||||
// 海宁
|
||||
is875() {
|
||||
return this.userName.slice(0, 3) === '875'
|
||||
},
|
||||
feedbackPreview() {
|
||||
return this.buildFeedbackValue(this.feedbackForm.feedbackSelections)
|
||||
}
|
||||
},
|
||||
created() {
|
||||
@@ -516,6 +594,77 @@ export default {
|
||||
this.initVisitingTaskList();
|
||||
},
|
||||
methods: {
|
||||
resetFeedbackForm() {
|
||||
this.feedbackSubmitting = false;
|
||||
this.feedbackForm = {
|
||||
id: null,
|
||||
source: "",
|
||||
feedbackSelections: createEmptyFeedbackSelections()
|
||||
};
|
||||
},
|
||||
parseFeedbackValue(value) {
|
||||
const feedbackSelections = createEmptyFeedbackSelections();
|
||||
if (!value) {
|
||||
return feedbackSelections;
|
||||
}
|
||||
value.split(";").forEach((segment) => {
|
||||
const [type, products] = segment.split(":");
|
||||
if (!type || !feedbackSelections[type]) {
|
||||
return;
|
||||
}
|
||||
feedbackSelections[type] = (products || "")
|
||||
.split(",")
|
||||
.map(item => item && item.trim())
|
||||
.filter(Boolean);
|
||||
});
|
||||
return feedbackSelections;
|
||||
},
|
||||
buildFeedbackItems(feedbackSelections) {
|
||||
return this.feedbackTypeOptions
|
||||
.map((type) => ({
|
||||
feedbackType: type,
|
||||
products: (feedbackSelections[type] || []).filter(Boolean)
|
||||
}))
|
||||
.filter(item => item.products.length > 0);
|
||||
},
|
||||
buildFeedbackValue(feedbackSelections) {
|
||||
return this.buildFeedbackItems(feedbackSelections)
|
||||
.map(item => `${item.feedbackType}:${item.products.join(",")}`)
|
||||
.join(";");
|
||||
},
|
||||
handleEditFeedback(row) {
|
||||
this.feedbackForm = {
|
||||
id: row.id,
|
||||
source: row.source || "",
|
||||
feedbackSelections: this.parseFeedbackValue(row.intentionProductValue)
|
||||
};
|
||||
this.feedbackDialogVisible = true;
|
||||
},
|
||||
handleSubmitFeedback() {
|
||||
const feedbackItems = this.buildFeedbackItems(this.feedbackForm.feedbackSelections);
|
||||
if (!this.feedbackForm.source) {
|
||||
this.$message.warning("请选择走访渠道");
|
||||
return;
|
||||
}
|
||||
if (!feedbackItems.length) {
|
||||
this.$message.warning("请至少选择一组客户意愿和营销产品");
|
||||
return;
|
||||
}
|
||||
const payload = {
|
||||
id: this.feedbackForm.id,
|
||||
source: this.feedbackForm.source || null,
|
||||
feedbackItems
|
||||
};
|
||||
this.feedbackSubmitting = true;
|
||||
updatePADVisitFeedback(payload).then(() => {
|
||||
this.$message.success("保存成功");
|
||||
this.feedbackDialogVisible = false;
|
||||
this.resetFeedbackForm();
|
||||
this.initVisitingTaskList();
|
||||
}).finally(() => {
|
||||
this.feedbackSubmitting = false;
|
||||
});
|
||||
},
|
||||
handleChange(val) {
|
||||
this.pageSize = 10;
|
||||
this.pageNum = 1;
|
||||
@@ -752,6 +901,67 @@ export default {
|
||||
}
|
||||
}
|
||||
|
||||
.feedback-form {
|
||||
::v-deep .el-form-item.is-required:not(.is-no-asterisk) > .el-form-item__label::before {
|
||||
color: #f56c6c;
|
||||
margin-right: 4px;
|
||||
}
|
||||
|
||||
.feedback-groups {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.feedback-group {
|
||||
padding: 10px 12px;
|
||||
border: 1px solid #ebeef5;
|
||||
border-radius: 8px;
|
||||
background: #fafbfc;
|
||||
}
|
||||
|
||||
.feedback-group__title {
|
||||
margin-bottom: 10px;
|
||||
color: #303133;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
::v-deep .el-checkbox-group {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 6px 8px;
|
||||
}
|
||||
|
||||
::v-deep .el-checkbox {
|
||||
margin-right: 0;
|
||||
min-width: auto;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.feedback-preview {
|
||||
min-height: 20px;
|
||||
color: #606266;
|
||||
word-break: break-all;
|
||||
}
|
||||
}
|
||||
|
||||
::v-deep .feedback-dialog {
|
||||
border-radius: 18px;
|
||||
overflow: hidden;
|
||||
|
||||
.el-dialog__header {
|
||||
padding: 20px 24px 16px;
|
||||
}
|
||||
|
||||
.el-dialog__body {
|
||||
padding: 12px 24px 20px;
|
||||
}
|
||||
|
||||
.el-dialog__footer {
|
||||
padding: 10px 24px 20px;
|
||||
}
|
||||
}
|
||||
|
||||
.quesiton {
|
||||
color: #b9b9b9;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user