提交 ed833235 作者: 施汉文

🌈 style: 关联标签修改

上级 5e642f8d
<template>
<div class="w-full h-full flex items-center justify-center">
<div class="flex flex-col justify-center">
<iconpark-icon name="Empty-2" class="text-[80px]"></iconpark-icon>
<slot></slot>
</div>
</div>
</template>
<script>
export default {
name: "CommonEmpty",
};
</script>
<style></style>
<template>
<Drawer
title="添加关联标签"
:visible="show"
size="360px"
@close="close"
@ok="submit"
>
<div class="pt-[10px] px-[12px] h-full flex flex-col pb-[100px]">
<div v-if="checkbox" class="pb-[10px]">
<el-checkbox v-model="is_tag_sync" :true-label="1" :false-label="0"
>在当前主体不同客服号同步进行打标</el-checkbox
>
</div>
<TagsCheck
v-model="allCheckoutList"
:options="{ label: 'name', value: 'id' }"
/>
<div class="flex-1 overflow-y-auto">
<div class="text-[#363E49] text-[13px] mt-[12px] mb-[8px]">
选择标签
</div>
<el-input
v-model.trim="searchValue"
@input="debouncedHandleChange"
class="custom-input"
placeholder="请输入标签名称"
prefix-icon="el-icon-search"
>
</el-input>
<div
ref="treeContainer"
class="tree-container"
@scroll="handleTreeScroll"
>
<el-tree
@check-change="checkChange"
class="mt-[10px]"
:data="handleCheckTagList"
show-checkbox
node-key="id"
ref="tree"
:default-expanded-keys="allCheckoutList?.map((item) => item.id)"
:default-checked-keys="allCheckoutList?.map((item) => item.id)"
:props="{
children: 'children',
label: 'label',
}"
>
</el-tree>
</div>
</div>
</div>
</Drawer>
</template>
<script type="text/javascript">
import { searchTags } from "@/api/works";
import Drawer from "@/components/common/Drawer.vue";
import TagsCheck from "@/views/popup/AddTagsDrawer/components/TagsCheck.vue";
import { debounce } from "@/utils/index.js";
export default {
name: "selectTag",
components: {
Drawer,
TagsCheck,
},
// checkList 选中的标签 checkbox 是否显示 在当前主体不同客服号同步进行打标
props: ["show", "checkList", "title", "checkbox"],
data() {
return {
searchValue: "",
radio: "add",
is_tag_sync: 1,
checkTagList: [],
isScrollBottom: false,
tagPageInfo: {
page: 0,
page_size: 20,
},
handleCheckTagList: [],
selectedTags: [],
searchTagsInput: "",
allCheckoutList: [], // 所有的选中标签
};
},
computed: {},
watch: {
// 监听checkList变化,更新选中状态
checkList: {
handler(newVal) {
this.selectedTags = newVal;
// 将传入的checkList转换为allCheckoutList格式
this.convertToAllCheckoutList(newVal);
// 如果树形结构已经加载完成,重新应用选中状态
if (this.handleCheckTagList.length > 0) {
this.$nextTick(() => {
// 设置初始选中状态
this.allCheckoutList.forEach((tag) => {
if (this.$refs.tree) {
this.$refs.tree.setChecked(tag.id, true);
}
});
});
}
},
deep: true,
},
// 监听allCheckoutList变化,更新树形结构的选中状态
allCheckoutList: {
handler(newVal) {
this.$nextTick(() => {
if (this.$refs.tree) {
// 先获取当前树形结构中存在的所有标签ID
const treeTagIds = new Set();
const traverseTree = (nodes) => {
nodes.forEach((node) => {
if (node.children) {
traverseTree(node.children);
} else {
treeTagIds.add(node.id);
}
});
};
traverseTree(this.handleCheckTagList);
// 只设置当前树形结构中存在的标签的选中状态
const checkedIds = newVal
.filter((tag) => treeTagIds.has(tag.id))
.map((tag) => tag.id);
this.$refs.tree.setCheckedKeys(checkedIds);
}
});
},
deep: true,
},
},
mounted() {
// 确保allCheckoutList先被设置
this.convertToAllCheckoutList(this.checkList);
this.startSearch();
},
methods: {
// 将传入的checkList转换为allCheckoutList格式
convertToAllCheckoutList(data) {
// 创建初始回显数据的数组
const initialTags = [];
if (data && data.length > 0) {
data.forEach((group) => {
if (group.tag && group.tag.length > 0) {
group.tag.forEach((tag) => {
initialTags.push({
...tag,
label_group_id: group.group_id,
label_group_name: group.group_name,
});
});
}
});
}
// 如果allCheckoutList已有数据(用户已选择标签),则合并数据
if (this.allCheckoutList.length > 0) {
// 创建一个集合用于快速查找已存在的标签ID
const existingTagIds = new Set(
this.allCheckoutList.map((tag) => tag.id)
);
// 只添加初始回显数据中不存在的标签
const newTags = initialTags.filter(
(tag) => !existingTagIds.has(tag.id)
);
// 合并数据
this.allCheckoutList = [...this.allCheckoutList, ...newTags];
} else {
// 如果allCheckoutList为空,直接使用初始回显数据
this.allCheckoutList = initialTags;
}
},
// 创建防抖版本的handleChange方法,复用原始handleChange逻辑
debouncedHandleChange: debounce(function () {
// 直接调用原始handleChange方法
this.inputSearch();
}, 300),
// 处理请求的数据
handleAllTagData() {
if (this.checkTagList.length > 0) {
// 转换数据格式为树形结构
this.handleCheckTagList = this.format(this.checkTagList);
// 设置初始选中状态
this.$nextTick(() => {
if (this.allCheckoutList.length > 0) {
// 先取消所有选中状态,避免重复选择
this.$refs.tree.setCheckedKeys([]);
// 获取当前树形结构中存在的所有标签ID
const treeTagIds = new Set();
const traverseTree = (nodes) => {
nodes.forEach((node) => {
if (node.children) {
traverseTree(node.children);
} else {
treeTagIds.add(node.id);
}
});
};
traverseTree(this.handleCheckTagList);
// 只设置当前树形结构中存在的标签的选中状态
// 这样可以保留已选择但不在当前搜索结果中的标签
const checkedIds = this.allCheckoutList
.filter((tag) => treeTagIds.has(tag.id))
.map((tag) => tag.id);
this.$refs.tree.setCheckedKeys(checkedIds);
}
// 确保树容器的滚动事件正常工作
this.$nextTick(() => {
this.handleTreeScroll();
});
});
} else {
this.handleCheckTagList = [];
}
},
// 转换数据为树形结构
format(data) {
const list = [];
data.forEach((item) => {
// 遍历每个标签组
item.tag.forEach((tag) => {
const findGroup = list.find((i) => {
return i.id === item.group_id;
});
if (findGroup) {
// 如果组已存在,添加标签到组
findGroup.children.push({
id: tag.id,
label: tag.name,
label_group_id: item.group_id,
label_group_name: item.group_name,
...tag,
});
} else {
// 如果组不存在,创建新组
list.push({
id: item.group_id,
label: item.group_name || "未分组",
label_group_id: item.group_id,
children: [
{
...tag,
label: tag.name,
label_group_id: item.group_id,
label_group_name: item.group_name || "未分组",
},
],
});
}
});
});
return list;
},
// 处理节点选中状态变化
checkChange(data, check, indeterminate) {
// 如果是分组节点(有children),不处理
if (data.children) return;
// 获取当前树形结构中所有选中的叶子节点
const checkedNodes =
this.$refs.tree.getCheckedNodes().filter((item) => !item.children) ||
[];
// 获取当前树形结构中所有的节点ID
const currentTreeTagIds = new Set();
const traverseTree = (nodes) => {
nodes.forEach((node) => {
if (node.children) {
traverseTree(node.children);
} else {
currentTreeTagIds.add(node.id);
}
});
};
traverseTree(this.handleCheckTagList);
// 保留不在当前树形结构中显示但已经选中的节点(初始化回显数据)
const existingTags = this.allCheckoutList.filter(
(tag) => !currentTreeTagIds.has(tag.id)
);
// 合并当前树形结构中选中的节点和保留的节点
const mergedTags = [...existingTags, ...checkedNodes];
// 去重,确保每个标签只出现一次
const uniqueTags = [];
const uniqueIds = new Set();
mergedTags.forEach((tag) => {
if (!uniqueIds.has(tag.id)) {
uniqueIds.add(tag.id);
uniqueTags.push(tag);
}
});
// 更新allCheckoutList
this.allCheckoutList = uniqueTags;
},
// 处理树形结构滚动事件
handleTreeScroll() {
const container = this.$refs.treeContainer;
if (container) {
const { scrollTop, scrollHeight, clientHeight } = container;
// 当滚动到底部时(距离底部100px以内),加载更多数据
if (
scrollHeight - scrollTop - clientHeight < 100 &&
!this.isScrollBottom
) {
this.startSearch();
}
}
},
removeDp2(arr1, arr2) {
const list1 = JSON.parse(JSON.stringify(arr1));
let list2 = [];
arr2.length === 0
? (list2 = [])
: (list2 = JSON.parse(JSON.stringify(arr2)));
for (let k = 0; k < arr1.length; k++) {
for (let i = 0; i < arr2.length; i++) {
if (arr2[i].group_id === arr1[k].group_id) {
list2[i].isfilter = true;
}
}
}
const list = list2.filter((item) => !item.isfilter);
return list.concat(list1);
},
// 数组去重
removeDp(arr1, arr2, id) {
let arr;
arr2 ? (arr = arr1.concat(arr2)) : (arr = arr1);
const obj = {};
const newArray = arr.reduce((pre, cur) => {
if (!obj[cur[id]]) {
obj[cur[id]] = true;
pre.unshift(cur);
}
return pre;
}, []);
return newArray;
},
// 默认勾选的数据 有数据就处理
handleDefaultCheck(data) {
// 先删选出来 有哪些 标签组已选中
data.map((item) => {
this.selectedTags.map((i) => {
const list = [];
if (item.group_id === i.group_id) {
// 匹配到了
this.$set(item, "checkList", i.tag);
this.allCheckoutList = this.removeDp(
i.tag,
this.allCheckoutList,
"id"
);
i.tag.map((k, j) => {
list.push(k.id);
});
this.$set(item, "checkItem", list);
item.isIndeterminate =
item.checkItem.length > 0 &&
item.checkItem.length < item.tag.length;
return;
} else {
return;
}
});
item.checkItem.length === item.tag.length
? (item.checkAll = true)
: (item.checkAll = false);
});
},
inputSearch() {
this.searchTagsInput = this.searchValue;
this.tagPageInfo = {
page: 0,
page_size: 20,
};
this.isScrollBottom = false;
this.startSearch();
},
async startSearch(resize) {
if (!this.isScrollBottom) {
// 数据未加载完
let data = {};
this.tagPageInfo.page++;
const obj = { tag_name: this.searchTagsInput };
data = { ...obj };
this.requestCheckTag(data);
} else {
// 数据已加载完
this.handleAllTagData();
}
},
requestCheckTag(data) {
const params = {
...data,
...this.tagPageInfo,
};
searchTags(params).then((res) => {
res.data.data.length < 20
? (this.isScrollBottom = true)
: (this.isScrollBottom = false);
if (this.tagPageInfo.page == 1) {
this.checkTagList = res.data.data;
} else {
// 数据去重
this.checkTagList = this.removeDp(
res.data.data,
this.checkTagList,
"group_id"
);
}
this.handleAllTagData();
});
},
// 清除全部
clear() {
this.allCheckoutList = [];
// 重新获取标签数据并刷新树
this.tagPageInfo = {
page: 0,
page_size: 20,
};
this.isScrollBottom = false;
this.startSearch();
},
close() {
this.allCheckoutList = [];
this.$emit("update:show", false);
},
// 点击确定
submit() {
// 转换数据格式为适合提交的结构
const groupedTags = {};
this.allCheckoutList.forEach((tag) => {
const groupId = tag.label_group_id;
if (!groupedTags[groupId]) {
groupedTags[groupId] = {
group_id: groupId,
group_name: tag.label_group_name,
tag: [],
};
}
groupedTags[groupId].tag.push(tag);
});
const submitData = Object.values(groupedTags);
this.selectedTags = submitData;
// 客户管理批量修改标签里面
this.$emit("submit", submitData, this.is_tag_sync);
this.$emit("update:show", false);
},
// 删除某一个tag
removeTag(item) {
// 从已选列表中删除
const index = this.allCheckoutList.findIndex((i) => i.id === item.id);
if (index !== -1) {
this.allCheckoutList.splice(index, 1);
}
// 更新树的选中状态
if (this.$refs.tree) {
this.$refs.tree.setChecked(item.id, false);
}
},
},
};
</script>
<style lang="scss" scoped>
.newPageNovel {
width: 100%;
padding: 0 20px;
height: 100%;
overflow: auto;
padding-bottom: 120px;
.input {
width: 250px;
}
.contnet {
width: 100%;
height: auto;
margin-top: 20px;
.contnetLeft {
width: auto;
height: 600px;
border-radius: 8px;
border: 1px solid rgba(0, 0, 0, 0.06);
overflow: auto;
position: relative;
padding-top: 40px;
}
.contnetRight {
width: 260px;
height: 600px;
border-radius: 8px;
border: 1px solid rgba(0, 0, 0, 0.06);
position: relative;
padding-top: 40px;
.checkList {
width: 100%;
height: 100%;
overflow: auto;
padding: 0 20px;
padding-bottom: 40px;
}
.checkTagItem {
width: 100%;
height: 26px;
margin-top: 10px;
font-size: 14px;
font-family: PingFangSC-Regular, PingFang SC;
font-weight: 400;
color: #333333;
background: #eeeeef;
border-radius: 4px;
padding: 0 10px;
line-height: 26px;
cursor: pointer;
.icon-caidan {
margin-right: 5px;
}
span {
max-width: 100px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
display: inline-block;
}
}
}
.title {
width: 100%;
height: 40px;
background: #f6f8f9;
border-radius: 8px 8px 0px 0px;
border-bottom: 1px solid rgba(0, 0, 0, 0.06);
font-size: 14px;
font-family: PingFangSC-Medium, PingFang SC;
font-weight: 500;
color: #333333;
padding: 0 20px;
position: absolute;
left: 0;
top: 0;
.clear {
font-size: 14px;
font-family: PingFangSC-Medium, PingFang SC;
font-weight: 500;
color: #409eff;
cursor: pointer;
}
}
.checkBoxList {
width: 100%;
height: calc(100%);
padding: 20px;
overflow: auto;
.boxItem {
margin-bottom: 20px;
}
// 选择成员
.boxItemMember {
width: 100%;
height: auto;
.imageItem {
width: 100%;
height: 50px;
.image {
width: 30px;
height: 30px;
border-radius: 15px;
position: relative;
top: 10px;
margin-right: 5px;
}
}
}
}
}
}
.dialog-footer {
width: calc(100% - 20px);
position: absolute;
right: 20px;
bottom: 0;
padding-top: 20px;
padding-bottom: 20px;
border-top: 1px solid rgba(0, 0, 0, 0.06);
justify-content: flex-end;
background: #fff;
z-index: 100;
.btn {
width: 84px;
height: 32px;
}
}
.radioGroup {
margin-top: 20px;
margin-left: 20px;
}
.custom-input {
width: 100%;
}
.el-input__inner {
border-radius: 4px;
}
.el-tree {
width: 100%;
}
.tree-container {
height: 300px; /* 设置合适的高度 */
overflow-y: auto;
padding-right: 10px; /* 为滚动条预留空间 */
}
</style>
<!--
<template>
<el-drawer
:lock-scroll="true"
:title="title || '选择标签'"
......@@ -487,5 +1123,4 @@
margin-top: 20px;
margin-left: 20px;
}
</style>
\ No newline at end of file
</style> -->
......@@ -11,6 +11,7 @@
<div
class="flex flex-wrap content-start gap-[8px] mt-[8px] pb-[12px] border-b-[1px] border-dashed border-b-[#E5E5E6] h-[112px] overflow-y-auto"
>
<template v-if="value.length > 0">
<div
class="flex items-center px-[6px] h-[22px] rounded-[4px] bg-[#F5F5F5]"
:class="[
......@@ -28,12 +29,22 @@
class="el-icon-close ml-[6px] cursor-pointer text-[#B0B2B5] hover:text-primary"
></i>
</div>
</template>
<Empty v-else>
<div class="text-[#6D7176] text-[12px]">
未选择标签,点击下方标签进行勾选
</div>
</Empty>
</div>
</div>
</template>
<script>
import Empty from "@/components/common/Empty.vue";
export default {
name: "TagsCheck",
components: {
Empty,
},
props: {
value: {
type: Array,
......@@ -45,7 +56,7 @@ export default {
},
checkedIds: {
type: Array,
default: () => null,
default: () => [],
},
},
methods: {
......@@ -54,6 +65,7 @@ export default {
"input",
this.value.filter((i) => i !== item)
);
this.$emit("change");
},
clear() {
this.$emit(
......@@ -62,6 +74,7 @@ export default {
this.checkedIds.includes(i[this.options.value])
)
);
this.$emit("change");
},
},
};
......
......@@ -11,6 +11,7 @@
v-model="list"
:checked-ids="defaultCheckedIds"
:options="{ label: 'label_name', value: 'id' }"
@change="applyInitialDisabledState"
/>
<div class="flex-1 overflow-y-auto">
<div class="text-[#363E49] text-[13px] mt-[12px] mb-[8px]">
......@@ -137,6 +138,7 @@ export default {
if (res.status_code === 1) {
this.$message.success(res.msg);
this.close();
this.$emit("submitOk");
} else {
this.$message.error(res.msg || "添加失败");
}
......
......@@ -149,58 +149,76 @@
/>
<!-- 游戏标签 -->
<div class="item rowFlex columnCenter spaceBetween tagsLost">
<div class="rowFlex">
<div class="item rowFlex columnCenter spaceBetween tagsLost group">
<div class="rowFlex items-center">
<span class="label">关联标签:</span>
<div
v-if="
chatUserDetails.tag_group &&
chatUserDetails.tag_group.length > 0
"
class="flex space-x-[4px] items-center"
>
<!-- 第一个标签组的所有标签 -->
<el-tag
v-for="(items, indexs) in chatUserDetails.tag_group[0].tag"
:key="indexs"
>{{ items.name }}</el-tag
<div class="flex items-center space-x-[4px] cursor-pointer">
<div
v-for="(items, index) in chatUserDetails.tag_group[0].tag"
:key="index"
class="px-[6px] rounded-[4px] bg-[#F5F5F5] gap-[4px] flex items-center h-[22px]"
>
<!-- 如果有多个标签组,显示+n -->
<el-popover
<div
v-if="chatUserDetails.tag_group.length > 1"
placement="top"
trigger="hover"
popper-class="tag-popover"
class="flex-1 truncate"
>
{{ items.name }}
</div>
<div
class="groups-popover-content"
style="max-height: 600px; overflow-y: auto"
v-else-if="chatUserDetails.tag_group.length > 0"
class="flex-1 truncate"
>
{{ items.name }}
</div>
<div v-else class="flex-1 truncate">--</div>
</div>
<el-tooltip
effect="dark"
placement="top"
v-if="chatUserDetails.tag_group.length > 1"
>
<div slot="content" class="space-y-[4px]">
<div
v-for="(
group, groupIndex
) in chatUserDetails.tag_group.slice(1)"
:key="groupIndex"
class="group-item"
class="grid grid-cols-3 gap-[4px] border-b-[1px] border-[rgba(255,255,255,0.2)] border-dashed py-[4px]"
>
<el-tag
<div
v-for="(tagItem, tagIndex) in group.tag"
:key="tagIndex"
style="margin-right: 10px"
>{{ tagItem.name }}</el-tag
class="px-[6px] w-full max-w-[200px] rounded-[4px] bg-[rgba(255,255,255,0.2)] flex items-center h-[22px] overflow-hidden"
>
<span class="truncate"> {{ tagItem?.name }} </span>
</div>
</div>
<span slot="reference" class="tag-more"
>+{{ chatUserDetails.tag_group.length - 1 }}</span
>
</el-popover>
</div>
<el-button type="text" size="small"
>+{{ chatUserDetails.tag_group.length - 1 }}
</el-button>
</el-tooltip>
<iconpark-icon
name="ziliao-tianjia-jb82dh2k"
class="group-hover:visible invisible text-primary text-[14px] ml-[6px]"
@click="editTags"
></iconpark-icon>
</div>
<i
</div>
</div>
<!-- <i
class="el-icon-edit icon"
style="font-size: 14px; margin-right: 10px"
@click="editTags"
></i>
></i> -->
</div>
<!-- 共享信息 -->
<shareInfo :chat-user-details="chatUserDetails" />
......
<template>
<div>
<div class="item spaceBetween zyouTag grid grid-cols-12">
<div class="rowFlex col-span-3">
<div class="item spaceBetween zyouTag grid grid-cols-12 max-w-[360px]">
<div class="rowFlex col-span-3 max-w-[100px]">
<span class="label">掌游标签:</span>
<!-- <i
:class="[
......@@ -59,10 +59,12 @@
</div>
<iconpark-icon
name="ziliao-tianjia-jb82dh2k"
class="group-hover:visible invisible text-primary text-[14px] ml-[6px]"
class="invisible text-primary text-[14px] ml-[6px]"
:class="{ 'group-hover:visible': item.label_type == 2 }"
@click="addTag(item.label)"
></iconpark-icon>
</div>
<Empty />
</div>
</div>
<!-- 掌游标签列表 -->
......@@ -147,7 +149,11 @@
</div>
</div>
</div> -->
<AddTagsDrawer ref="addTagsDrawer" :game-user-info="gameUserInfo" />
<AddTagsDrawer
ref="addTagsDrawer"
:game-user-info="gameUserInfo"
@submitOk="getRoleLabelList"
/>
</div>
</template>
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论