提交 272b2066 作者: 施汉文

🌈 style: 添加客服标签弹框

上级 2bce3e95
<template>
<div>
<div class="flex justify-between leading-[22px] font-medium">
<div class="text-[#363E49] text-[13px]">已选择标签</div>
<iconpark-icon
@click="clear"
name="icon-qingkong"
class="text-[16px] text-[#B0B2B5] hover:text-primary cursor-pointer"
></iconpark-icon>
</div>
<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"
>
<div
class="flex items-center px-[6px] h-[22px] rounded-[4px] bg-[#F5F5F5]"
:class="[
checkedIds && checkedIds.includes(item[options.value])
? ''
: 'bg-[#E8F1FD] ',
]"
v-for="(item, index) in value"
:key="index"
>
{{ item[options.label] }}
<i
v-if="checkedIds && !checkedIds.includes(item[options.value])"
@click="remove(item)"
class="el-icon-close ml-[6px] cursor-pointer text-[#B0B2B5] hover:text-primary"
></i>
</div>
</div>
</div>
</template>
<script>
export default {
name: "TagsCheck",
props: {
value: {
type: Array,
default: () => [],
},
options: {
type: Object,
default: () => ({ label: "label", value: "value" }),
},
checkedIds: {
type: Array,
default: () => null,
},
},
methods: {
remove(item) {
this.$emit(
"input",
this.value.filter((i) => i !== item)
);
},
clear() {
this.$emit(
"input",
this.value.filter((i) =>
this.checkedIds.includes(i[this.options.value])
)
);
},
},
};
</script>
<template>
<Drawer
title="添加客服标签"
:visible="show"
size="360px"
@close="close"
@ok="okFn"
>
<div class="pt-[10px] px-[12px] h-full flex flex-col pb-[100px]">
<TagsCheck
v-model="list"
:checked-ids="defaultCheckedIds"
:options="{ label: '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="inputValue"
@input="debouncedHandleChange"
class="custom-input"
placeholder="请输入标签名称"
prefix-icon="el-icon-search"
>
</el-input>
<el-tree
:key="list.length"
@check-change="checkChange"
class="mt-[10px]"
:data="data"
show-checkbox
node-key="id"
ref="tree"
:default-expanded-keys="list?.map((item) => item.id)"
:default-checked-keys="list?.map((item) => item.id)"
:props="{
children: 'children',
label: 'label',
}"
>
</el-tree>
</div>
</div>
</Drawer>
</template>
<script>
import Drawer from "@/components/common/Drawer.vue";
import TagsCheck from "@/views/popup/AddTagsDrawer/components/TagsCheck.vue";
import { roleLabelSearch, editRoleLabel, roleGetRoleLabel } from "@/api/game";
import { mapState } from "vuex";
import { debounce } from "@/utils/index.js";
export default {
components: {
Drawer,
TagsCheck,
},
name: "AddTagsDrawer",
props: {
gameUserInfo: {
type: Object,
default: () => ({}),
},
},
data() {
return {
show: true,
inputValue: "",
defaultCheckedIds: [],
list: [], //默认选中
nowList: [], //当前选中
data: [],
};
},
computed: {
...mapState("user", [
"cser_id",
"cser_name",
"corp_id",
"weixin_blongs_id_list",
]),
...mapState("game", ["accountSelect"]),
checkedId() {
return (
this.list
?.filter((item) => !this.defaultCheckedIds.includes(item.id))
?.map((item) => item.id) || []
);
},
},
mounted() {
this.init();
},
// watch: {
// accountSelect: {
// handler(newVal, oldVal) {
// if (newVal !== oldVal) {
// this.getRoleLabel();
// }
// },
// immediate: true,
// },
// },
methods: {
async init() {
await this.getRoleLabel();
await this.handleChange();
},
open() {
this.show = true;
},
close() {
this.show = false;
this.inputValue = "";
},
async okFn() {
if (!this.checkedId?.length) {
this.$message.error("请选择标签");
return;
}
console.log(this.gameUserInfo);
if (
!this.gameUserInfo ||
!this.gameUserInfo.role_info ||
!this.gameUserInfo.role_info.role_id
) {
this.$message.error("该账号未创角无法添加标签");
return;
}
const res = await editRoleLabel({
role_id: this.gameUserInfo.role_info.role_id || "",
member_id: this.accountSelect,
label_id: this.checkedId,
user_id: this.cser_id,
user_name: this.cser_name,
role_name: this.gameUserInfo.role_info.role_name || "",
cp_role_id: this.gameUserInfo.role_info.cp_role_id || "",
});
if (res.status_code === 1) {
this.$message.success(res.msg);
this.close();
} else {
this.$message.error(res.msg || "添加失败");
}
},
// 创建防抖版本的handleChange方法,复用原始handleChange逻辑
debouncedHandleChange: debounce(function () {
// 直接调用原始handleChange方法
this.handleChange();
}, 300),
// 原始handleChange方法,用于在需要时直接调用(非防抖)
async handleChange() {
const weixin_blongs_id = this.weixin_blongs_id_list.map(
(item) => item.value
);
const res = await roleLabelSearch({
label_name: this.inputValue || "",
weixin_blongs_id: weixin_blongs_id || [],
label_type: 2,
});
this.data = this.format(res.data.data);
// 初始化时应用禁用规则
this.$nextTick(() => {
this.applyInitialDisabledState();
});
},
// 初始化时应用禁用规则
applyInitialDisabledState() {
// 按分组ID对默认选中的节点进行分类
const groupedChecked = {};
this.list.forEach((node) => {
const groupId = node.label_group_id;
if (!groupedChecked[groupId]) {
groupedChecked[groupId] = [];
}
groupedChecked[groupId].push(node);
});
// 更新树的禁用状态
this.updateTreeDisabledState(groupedChecked);
},
async getRoleLabel() {
if (!this.accountSelect) return;
const weixin_blongs_id = this.weixin_blongs_id_list.map(
(item) => item.value
);
const res = await roleGetRoleLabel({
member_id: this.accountSelect,
weixin_blongs_id: weixin_blongs_id,
});
// 筛选出标签类型为2的标签,2为客服标签
this.list =
res.data.data.filter((item) => item.label_type == 2)?.[0]?.label || [];
this.defaultCheckedIds = this.list?.map((item) => item.id) || [];
},
format(data) {
const list = [];
data.forEach((item) => {
const findItem = list.find((i) => {
return i.id === item.label_group_id;
});
if (findItem) {
findItem.children.push({
id: item.id,
label: item.label_name,
label_group_id: item.label_group_id, // 确保每个标签项都有label_group_id属性
...item,
disabled: this.defaultCheckedIds.includes(item.id),
});
} else {
list.push({
id: item.label_group_id,
label: item.label_group_name,
label_group_id: item.label_group_id,
children: [
{
...item,
label: item.label_name,
label_group_id: item.label_group_id, // 确保每个标签项都有label_group_id属性
disabled: this.defaultCheckedIds.includes(item.id),
},
],
});
}
});
return list;
},
checkChange(data, check, indeterminate) {
// 获取所有选中的叶子节点(没有children的节点)
const checkedNodes =
this.$refs.tree.getCheckedNodes().filter((item) => !item.children) ||
[];
// 按分组ID对选中的节点进行分类
const groupedChecked = {};
checkedNodes.forEach((node) => {
const groupId = node.label_group_id;
if (!groupedChecked[groupId]) {
groupedChecked[groupId] = [];
}
groupedChecked[groupId].push(node);
});
// 如果某个分组有超过一个选中的节点,则只保留最新选中的那个
let finalChecked = [];
Object.keys(groupedChecked).forEach((groupId) => {
const groupNodes = groupedChecked[groupId];
if (groupNodes.length > 1) {
// 如果当前操作是选中节点,且该节点属于这个分组,则保留当前操作的节点,取消同组其他节点
if (check && data.label_group_id === parseInt(groupId)) {
finalChecked.push(data);
// 取消同组其他节点的选中状态
groupNodes.forEach((node) => {
if (node.id !== data.id) {
this.$refs.tree.setChecked(node, false);
}
});
} else {
// 否则保留第一个节点
finalChecked.push(groupNodes[0]);
}
} else {
finalChecked = finalChecked.concat(groupNodes);
}
});
this.list = finalChecked;
// 更新树的数据,禁用同组的其他节点
this.updateTreeDisabledState(groupedChecked);
},
// 更新树的禁用状态,同一分组下只能选一个
updateTreeDisabledState(groupedChecked) {
this.data = this.data.map((group) => {
const groupId = group.id;
const checkedInGroup = groupedChecked[groupId] || [];
// 如果该分组已经有选中的节点,则禁用同组的其他节点
if (checkedInGroup.length > 0) {
group.children = group.children.map((child) => {
// 如果该节点不是已选中的节点,则禁用它
if (
!checkedInGroup.some((checkedNode) => checkedNode.id === child.id)
) {
return {
...child,
disabled: true,
};
}
return child;
});
} else {
// 如果该分组没有选中的节点,则启用所有节点
group.children = group.children.map((child) => {
// 除非它是默认选中的节点
return {
...child,
disabled: this.defaultCheckedIds.includes(child.id),
};
});
}
return group;
});
},
},
};
</script>
...@@ -30,8 +30,14 @@ ...@@ -30,8 +30,14 @@
<div v-if="accountSelect && accountSelect !== '' && gameUserInfo.username"> <div v-if="accountSelect && accountSelect !== '' && gameUserInfo.username">
<div class="item rowFlex columnCenter spaceBetween"> <div class="item rowFlex columnCenter spaceBetween">
<div class="rowFlex"> <div class="rowFlex">
<span class="label" style="min-width:40px;">账号:</span> <span class="label" style="min-width: 40px">账号:</span>
<p class="text rowFlex flexWarp">{{ gameUserInfo.username }} <span v-if="gameUserInfo.account_type==2" class="account_type">(内) 勿回,找组长!!!</span> <LastLogin/> </p> <p class="text rowFlex flexWarp">
{{ gameUserInfo.username }}
<span v-if="gameUserInfo.account_type == 2" class="account_type"
>(内) 勿回,找组长!!!</span
>
<LastLogin />
</p>
</div> </div>
</div> </div>
<!-- 营销面板 --> <!-- 营销面板 -->
...@@ -46,7 +52,7 @@ ...@@ -46,7 +52,7 @@
<div class="item rowFlex columnCenter spaceBetween"> <div class="item rowFlex columnCenter spaceBetween">
<div class="rowFlex columnCenter"> <div class="rowFlex columnCenter">
<span class="label">是否易主:</span> <span class="label">是否易主:</span>
<p class="text">{{ change_user == 1 ? '是' : '否' }}</p> <p class="text">{{ change_user == 1 ? "是" : "否" }}</p>
<el-popconfirm <el-popconfirm
confirm-button-text="确定" confirm-button-text="确定"
cancel-button-text="取消" cancel-button-text="取消"
...@@ -69,7 +75,7 @@ ...@@ -69,7 +75,7 @@
<div class="item rowFlex columnCenter spaceBetween"> <div class="item rowFlex columnCenter spaceBetween">
<div class="rowFlex columnCenter"> <div class="rowFlex columnCenter">
<span class="label">白名单:</span> <span class="label">白名单:</span>
<p class="text">{{ change_name == 1 ? '是' : '否' }}</p> <p class="text">{{ change_name == 1 ? "是" : "否" }}</p>
<el-popconfirm <el-popconfirm
confirm-button-text="确定" confirm-button-text="确定"
cancel-button-text="取消" cancel-button-text="取消"
...@@ -91,7 +97,7 @@ ...@@ -91,7 +97,7 @@
<div class="item rowFlex columnCenter spaceBetween"> <div class="item rowFlex columnCenter spaceBetween">
<div class="rowFlex columnCenter"> <div class="rowFlex columnCenter">
<span class="label">异常账号:</span> <span class="label">异常账号:</span>
<p class="text">{{ change_risk == 1 ? '是' : '否' }}</p> <p class="text">{{ change_risk == 1 ? "是" : "否" }}</p>
<el-popconfirm <el-popconfirm
confirm-button-text="确定" confirm-button-text="确定"
cancel-button-text="取消" cancel-button-text="取消"
...@@ -140,7 +146,7 @@ ...@@ -140,7 +146,7 @@
<div class="item rowFlex columnCenter spaceBetween"> <div class="item rowFlex columnCenter spaceBetween">
<div class="rowFlex columnCenter"> <div class="rowFlex columnCenter">
<span class="label">近期是否被封禁:</span> <span class="label">近期是否被封禁:</span>
<p class="text">{{ gameUserInfo.is_banned == 1 ? '是' : '否' }}</p> <p class="text">{{ gameUserInfo.is_banned == 1 ? "是" : "否" }}</p>
</div> </div>
</div> </div>
<div class="item rowFlex columnCenter spaceBetween"> <div class="item rowFlex columnCenter spaceBetween">
...@@ -289,30 +295,34 @@ ...@@ -289,30 +295,34 @@
<div class="rowFlex columnCenter"> <div class="rowFlex columnCenter">
<span class="label">注册时间:</span> <span class="label">注册时间:</span>
<p class="text"> <p class="text">
{{ moment(gameUserInfo.reg_time * 1000).format('YYYY-MM-DD') }} {{ moment(gameUserInfo.reg_time * 1000).format("YYYY-MM-DD") }}
</p> </p>
</div> </div>
</div> </div>
</div> </div>
<AddTagsDrawer ref="addTagsDrawer" :game-user-info="gameUserInfo" />
</div> </div>
</template> </template>
<script> <script>
import { bindMobile, getMemberLabel, editMemberLabel } from '@/api/game'; import { bindMobile, getMemberLabel, editMemberLabel } from "@/api/game";
import { mapState, mapMutations } from 'vuex'; import { mapState, mapMutations } from "vuex";
import { toTransfer, syncSessionIntelTag } from '@/api/works'; import { toTransfer, syncSessionIntelTag } from "@/api/works";
import MarketingPanel from './MarketingPanel.vue'; import MarketingPanel from "./MarketingPanel.vue";
import moment from 'moment'; import moment from "moment";
import ZyouTag from './ZyouTag.vue'; import ZyouTag from "./ZyouTag.vue";
import { debounce } from '@/utils'; import { debounce } from "@/utils";
import LastLogin from '@/views/components/quickSendGame/sendGame/lastLogin.vue'; import LastLogin from "@/views/components/quickSendGame/sendGame/lastLogin.vue";
import AddTagsDrawer from "@/views/popup/AddTagsDrawer/index.vue";
export default { export default {
name: 'gameUserInfo', name: "gameUserInfo",
components: { components: {
ZyouTag, ZyouTag,
MarketingPanel, MarketingPanel,
LastLogin, LastLogin,
AddTagsDrawer,
}, },
props: ['gameUserInfo', 'chatUserDetails'], props: ["gameUserInfo", "chatUserDetails"],
data() { data() {
return { return {
moment, moment,
...@@ -321,18 +331,18 @@ export default { ...@@ -321,18 +331,18 @@ export default {
change_name: 0, change_name: 0,
change_risk: 0, change_risk: 0,
change_appraisal: 0, change_appraisal: 0,
newMobileValue: '', newMobileValue: "",
isRefresh: false, isRefresh: false,
showWxNumber: true, showWxNumber: true,
}; };
}, },
computed: { computed: {
...mapState('game', ['accountSelect']), ...mapState("game", ["accountSelect"]),
}, },
watch: { watch: {
accountSelect: { accountSelect: {
handler: debounce(function (newVal, oldVal) { handler: debounce(function (newVal, oldVal) {
if (newVal && newVal !== '' && newVal !== oldVal) { if (newVal && newVal !== "" && newVal !== oldVal) {
this.getMemberLabel(); this.getMemberLabel();
} }
}, 200), }, 200),
...@@ -341,7 +351,7 @@ export default { ...@@ -341,7 +351,7 @@ export default {
}, },
mounted() { mounted() {
this.$nextTick(() => { this.$nextTick(() => {
if (this.accountSelect && this.accountSelect !== '') { if (this.accountSelect && this.accountSelect !== "") {
this.getMemberLabel(); this.getMemberLabel();
} }
}); });
...@@ -370,13 +380,13 @@ export default { ...@@ -370,13 +380,13 @@ export default {
this.change_name = change_name.label_value; this.change_name = change_name.label_value;
this.change_risk = change_risk.label_value; this.change_risk = change_risk.label_value;
this.change_appraisal = change_appraisal.label_value; this.change_appraisal = change_appraisal.label_value;
this.$emit('changeAppraisal', this.change_appraisal); this.$emit("changeAppraisal", this.change_appraisal);
} else { } else {
this.change_user = 0; this.change_user = 0;
this.change_name = 0; this.change_name = 0;
this.change_risk = 0; this.change_risk = 0;
this.change_appraisal = 0; this.change_appraisal = 0;
this.$emit('changeAppraisal', this.change_appraisal); this.$emit("changeAppraisal", this.change_appraisal);
} }
}); });
}, },
...@@ -395,7 +405,7 @@ export default { ...@@ -395,7 +405,7 @@ export default {
? (this.change_name = 0) ? (this.change_name = 0)
: (this.change_name = 1); : (this.change_name = 1);
} }
this.$emit('update:show', false); this.$emit("update:show", false);
}); });
}, },
// 异常账号 // 异常账号
...@@ -413,7 +423,7 @@ export default { ...@@ -413,7 +423,7 @@ export default {
? (this.change_risk = 0) ? (this.change_risk = 0)
: (this.change_risk = 1); : (this.change_risk = 1);
} }
this.$emit('update:show', false); this.$emit("update:show", false);
}); });
}, },
changeUserFn() { changeUserFn() {
...@@ -430,7 +440,7 @@ export default { ...@@ -430,7 +440,7 @@ export default {
? (this.change_user = 0) ? (this.change_user = 0)
: (this.change_user = 1); : (this.change_user = 1);
} }
this.$emit('update:show', false); this.$emit("update:show", false);
}); });
}, },
toTransfer() { toTransfer() {
...@@ -452,12 +462,12 @@ export default { ...@@ -452,12 +462,12 @@ export default {
this.newMobileValue this.newMobileValue
) )
) { ) {
this.$message.warning('请填写正确的手机号'); this.$message.warning("请填写正确的手机号");
return false; return false;
} }
this.gameUserInfo.mobile = this.gameUserInfo.mobile =
this.newMobileValue.substr(0, 3) + this.newMobileValue.substr(0, 3) +
'****' + "****" +
this.newMobileValue.substr(7); this.newMobileValue.substr(7);
this.showUserMobile = false; this.showUserMobile = false;
const data = { const data = {
...@@ -487,11 +497,11 @@ export default { ...@@ -487,11 +497,11 @@ export default {
this.$message.success(res.msg); this.$message.success(res.msg);
this.$set( this.$set(
this.chatUserDetails, this.chatUserDetails,
'intelligence_tag_group', "intelligence_tag_group",
res.data.intel_tag res.data.intel_tag
); );
} else { } else {
this.$message.warning('每个用户半个小时仅能更新一次'); this.$message.warning("每个用户半个小时仅能更新一次");
} }
} }
} catch (error) { } catch (error) {
...@@ -506,7 +516,7 @@ export default { ...@@ -506,7 +516,7 @@ export default {
<style lang="scss" scoped> <style lang="scss" scoped>
.gameUserDetails { .gameUserDetails {
width: 100%; width: 100%;
.account_type{ .account_type {
color: red; color: red;
font-weight: bold; font-weight: bold;
} }
......
...@@ -12,7 +12,9 @@ module.exports = { ...@@ -12,7 +12,9 @@ module.exports = {
darkMode: false, darkMode: false,
theme: { theme: {
extend: { extend: {
colors: {
primary: '#267EF0',
}
} }
}, },
variants: { variants: {
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论