This commit is contained in:
feng0207
2026-08-11 22:22:10 +08:00
commit 5e312c1266
6985 changed files with 1061169 additions and 0 deletions
+339
View File
@@ -0,0 +1,339 @@
<template>
<view class="container">
<lay-out noBottomHeight>
<div slot="header">
<view class="search-content padding">
<view class="uni-input-wrapper"><input class="uni-input" @confirm="initData" v-model="searchData.nickname" placeholder="搜索用户名称" /></view>
</view>
</div>
<div slot="content">
<!-- 通讯录导航 -->
<address-book :bookList="bookList" :letter="letter">
<template v-slot:addressBookList>
<view class="user-content">
<view class="user-list" v-for="(item, index) in Object.keys(bookList)" :key="index">
<view class="number padding">{{ item }}</view>
<view class="user" v-for="(val, i) in bookList[item]" :key="i" @click="selectUser(val)">
<view class="user-list-left"><image :src="val.avatar" mode=""></image></view>
<view class="user-list-right">{{ val.nickname }}</view>
</view>
</view>
</view>
</template>
</address-book>
</div>
</lay-out>
<uni-popup ref="userTagModel" type="bottom" animation>
<div class="userTag_container">
<div class="userTag_container_title">
<div :class="{ on: tabOn == 1 }" class="userTag_container_title_message" @click="tabOn = 1"><span>用户标签</span></div>
<div :class="{ on: tabOn == 2 }" class="userTag_container_title_message" @click="tabOn = 2"><span>分组标签</span></div>
<div class="closeModel" @click="closeUserTagModel"><span class="iconfont">&#xe6b5;</span></div>
</div>
<scroll-view scroll-y="true" class="userTag_container_list">
<div v-if="tabOn == 1" class="userTag_container_list_item" :class="{ mg40: index == 0 }" v-for="(item, index) in userTagList" :key="index">
<div class="userTag_container_list_item_label">
<span>{{ item.name }}</span>
</div>
<div class="userTag_container_list_item_value">
<span :class="{ select: val.disabled }" v-for="(val, i) in item.label" :key="i" @click="selectTags(val)">{{ val.label }}</span>
</div>
</div>
<div v-if="tabOn == 2" class="userTag_container_list_item mg40">
<div class="userTag_container_list_item_value">
<span :class="{ select: val.disabled }" v-for="(val, i) in groupList" :key="val" @click="selectGroups(val)">{{ val.group_name }}</span>
</div>
</div>
</scroll-view>
<div class="userTag_container_handle" @click="handleSetTags">
<div class="userTag_container_handle_button"><span>确定</span></div>
</div>
</div>
</uni-popup>
</view>
</template>
<script>
import AddressBook from './addressBookNavigation/addressBookNavigation';
import { makePy } from '../../utils/utils';
import { navigateTo } from 'pages/utils/uniApi.js';
import http from 'pages/api/index';
import api from 'pages/api/api.js';
export default {
components: { AddressBook },
data() {
return {
letter: ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'],
list: [],
search: '',
bookList: {},
customerServerData: {}, // 客服数据
searchData: {
nickname: '',
label_id: '',
group_id: ''
},
userTagList: [],
groupList: [],
tabOn: 1,
};
},
onLoad() {
this.customerServerData = this.$store.state.kefuInfo;
this.bookList = this.$store.state.bookList;
},
onShow() {
this.searchData.label_id = '';
this.searchData.group_id = '';
this.initData();
// { id: this.queryUserData.user_id }
http(api.userLabelSelect).then(res => {
this.userTagList = res;
});
http(api.UserGroup).then(res => {
this.groupList = res;
});
},
onNavigationBarButtonTap() {
// this.toTagList();
this.$refs.userTagModel.open();
},
methods: {
// 关闭弹窗
closeUserTagModel() {
this.$refs.userTagModel.close();
},
// 选择标签
selectTags(item) {
this.$set(item, 'disabled', !item.disabled);
},
selectGroups(item) {
this.$set(item, 'disabled', !item.disabled);
},
// 设置标签
handleSetTags() {
let postData = {
label_ids: [],
group_ids: []
};
console.log(111);
this.userTagList.forEach(item => {
item.label.forEach(val => {
if (val.disabled) {
postData.label_ids.push(val.id);
}
});
});
this.groupList.forEach(item => {
if (item.disabled) {
postData.group_ids.push(item.id);
}
});
console.log(postData);
this.searchData.label_id = postData.label_ids.join(',');
this.searchData.group_id = postData.group_ids.join();
this.closeUserTagModel();
this.initData(this.searchData);
},
// 查询当前客服的客户列表
initData() {
this.$store.dispatch('getBookList', this.searchData).then(res => {
this.bookList = res;
});
},
toTagList() {
navigateTo(1, '/pages/view/setTags/index');
},
selectUser(item) {
navigateTo(1, '/pages/view/customerServer/customerMessage', { user_id: item.to_user_id });
},
// 对通讯录按字母顺序做排序
letterSrot() {
const sortList = [];
const list = this.list;
this.letter.forEach(item => {
const obj = {};
obj.key = item;
obj.list = [];
for (let i = 0; i < list.length; i++) {
if (item == makePy(list[i].username).substr(0, 1)) {
obj.list.push(list[i]);
list.splice(i, 1);
i--;
}
}
if (obj.list.length) sortList.push(obj);
});
this.bookList = sortList;
}
}
};
</script>
<style lang="less" scoped>
@padding-l-r: 30rpx;
@padding-t-p: 20rpx;
@bgc-f: #ffffff;
@fontSize: 28rpx;
@imgSize: 64rpx;
.padding {
padding: @padding-t-p @padding-l-r;
}
.search-content {
position: relative;
background-color: @bgc-f;
input {
border: none;
background-color: #f5f6f9;
color: #c4c4c4;
font-size: @fontSize;
border-radius: 39px;
padding: @padding-t-p 0 @padding-t-p @padding-l-r;
text-align: center;
}
}
.user-content {
background-color: #eeeeee;
.user-list {
.number {
color: #666666;
font-size: @fontSize - 2rpx;
}
.user {
background-color: @bgc-f;
display: flex;
align-items: center;
padding: @padding-t-p @padding-l-r;
border-bottom: 1rpx solid #f0f2f7;
.user-list-left {
width: @imgSize;
height: @imgSize;
padding-right: 20rpx;
image {
width: @imgSize;
height: @imgSize;
border-radius: 50%;
}
}
.user-list-right {
color: #282828;
}
}
}
}
.header {
&_content {
display: flex;
justify-content: flex-end;
align-items: center;
position: relative;
height: 116rpx;
background: #1890fc;
padding: 0 30rpx;
/* #ifdef APP-PLUS */
padding-top: 64rpx;
/* #endif */
color: #fff;
> div {
flex: 1;
}
> div:nth-child(2) {
text-align: center;
font-weight: bold;
}
> div:nth-child(3) {
font-size: 14px;
text-align: right;
}
}
}
.userTag_container {
padding: 30rpx 40rpx;
// padding-bottom: 140rpx;
background: #fff;
width: 100%;
box-sizing: border-box;
&_title {
display: flex;
// justify-content: flex-end;
position: relative;
&_message {
flex: 1;
text-align: center;
// position: absolute;
// top: 50%;
// left: 50%;
// transform: translate(-50%, -50%);
&.on {
font-weight: bold;
color: #3875EA;
}
}
.closeModel {
position: absolute;
top: 0;
right: 0;
color: #c8cad0;
.iconfont {
font-size: 36rpx;
}
}
}
&_list {
height: 600rpx;
&_item {
margin-top: 78rpx;
&_label {
color: #282828;
font-weight: bold;
margin-bottom: 26rpx;
}
&_value {
display: grid;
grid-template-columns: repeat(3, 178rpx);
grid-row-gap: 26rpx;
grid-column-gap: 26rpx;
span {
font-size: 14px;
display: inline-block;
background: #f0f0f0;
text-align: center;
padding: 12rpx 0;
border-radius: 12rpx;
}
.select {
background: @primaryColor;
color: #fff;
}
}
}
.mg0 {
margin-top: 40rpx;
}
}
&_handle {
width: 610rpx;
height: 76rpx;
background: @primaryColor;
opacity: 1;
border-radius: 43px;
margin: auto;
margin-top: 44rpx;
display: flex;
align-items: center;
justify-content: center;
color: #fff;
font-weight: 600;
font-size: 14px;
}
}
</style>
@@ -0,0 +1,265 @@
<template>
<view>
<!-- 右边字母选择导航条 -->
<view class="searchLetter touchClass" :style="{ height: itemH * searchLetter.length + 'px', paddingRight: paddingRight + 'upx', width: searchLetterWidth + 'upx' }">
<view
v-for="(item, index) in searchLetter"
:style="{ height: itemH + 'px' }"
:key="index"
:data-letter="item.name"
@touchstart.stop.prevent="searchStart"
@touchmove.stop.prevent="searchMove"
@touchend.stop.prevent="searchEnd"
>
{{ item.name }}
</view>
</view>
<!-- 字母选择提示框 -->
<block v-if="isShowLetter">
<view class="showSlectedLetter">{{ showLetter }}</view>
</block>
<!-- 通讯录列表 -->
<scroll-view scroll-y :scrollTop="scrollTop"><slot name="addressBookList"></slot></scroll-view>
</view>
</template>
<script>
export default {
name: 'addressBookNavigation',
props: {
//通讯录名字集合
bookList: {
type: Object,
default() {
return {};
},
required: true
},
//索引集合
letter: {
type: Array,
default() {
return ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'];
}
},
//通讯录索引字母显示高度 upx
bookKeyHeight: {
type: Number,
default() {
return 100;
}
},
//通讯录名字显示高度 upx
bookItemHeight: {
type: Number,
default() {
return 40;
}
},
//右边索引条右边距 upx
paddingRight: {
type: Number,
default() {
return 20;
}
},
//若有搜索框则是搜索框高度 upx
searchHeight: {
type: Number,
default() {
return 20;
}
},
//右边索引条距离搜索框位置 upx
searchLetterTop: {
type: Number,
default() {
return 60;
}
},
//右边索引条宽度 upx
searchLetterWidth: {
type: Number,
default() {
return 40;
}
},
//减去的右边导航条高度 upx
subtractiveSearchLetterHeight: {
type: Number,
default() {
return 80;
}
},
//底部tab高度 使用原生导航栏固定为50px 自定义tab传入自定义tab高度 单位为px
tabHeight: {
type: Number,
default() {
return 100;
}
},
//导航栏高度
navHeight: {
type: Number,
default() {
return 44;
}
}
},
data() {
return {
searchLetter: [],
//右边索引条单个字母元素高度
itemH: 0,
//可滑动区域高度
usableHeight: 0,
screenWidth: 0,
isShowLetter: false,
showLetter: '',
scrollTop: 0,
badPageY: uni.upx2px(this.searchHeight + this.searchLetterTop),
letterPositionTop: 0,
scrollViewPositionTop: 0
};
},
mounted() {
//#ifdef H5
this.letterPositionTop = this.searchHeight + this.searchLetterTop + 88;
this.scrollViewPositionTop = this.searchHeight + 88;
//#endif
//#ifndef H5
this.letterPositionTop = this.searchHeight + this.searchLetterTop;
this.scrollViewPositionTop = this.searchHeight;
//#endif
// #ifdef APP-PLUS
this.letterPositionTop = this.searchHeight + this.searchLetterTop;
this.scrollViewPositionTop = this.searchHeight + 260;
console.log(1);
// #endif
this.countLetterHeight();
},
methods: {
//计算和记录右边每个搜索字母的高度和位置
countLetterHeight() {
const sysInfo = uni.getSystemInfoSync();
//#ifdef H5
const usableHeight = sysInfo.screenHeight - uni.upx2px(this.searchHeight) - this.tabHeight - this.navHeight;
//#endif
//#ifndef H5
const usableHeight = sysInfo.screenHeight - uni.upx2px(this.searchHeight) - this.tabHeight - this.navHeight - sysInfo.statusBarHeight;
//#endif
this.screenWidth = sysInfo.screenWidth;
const letter = this.letter;
const itemH = (usableHeight - uni.upx2px(this.subtractiveSearchLetterHeight) - 20) / letter.length;
const tempObj = [];
for (let i = 0; i < letter.length; i++) {
tempObj.push({
name: letter[i],
tHeight: i * itemH,
bHeight: (i + 1) * itemH
});
}
this.usableHeight = usableHeight;
this.itemH = itemH;
this.searchLetter = tempObj;
},
searchEnd(e) {
const that = this;
setTimeout(() => (that.isShowLetter = false), 500);
},
//点击选择
searchStart(e) {
console.log(e);
const pageY = e.touches[0].pageY - this.badPageY;
this.nowLetter(pageY);
},
//滑动选择
searchMove(e) {
const pageY = e.touches[0].pageY - this.badPageY;
const pageX = e.touches[0].pageX;
if (this.screenWidth - pageX > uni.upx2px(this.searchLetterWidth + this.paddingRight)) {
this.isShowLetter = false;
return;
}
if (pageY < 0 || pageY > this.itemH * this.searchLetter.length) {
this.isShowLetter = false;
return;
}
this.nowLetter(pageY);
},
//计算当前选中的字母并显示
nowLetter(pageY) {
const letterData = this.searchLetter;
for (let i = 0; i < letterData.length; i++) {
if (letterData[i].tHeight <= pageY && pageY <= letterData[i].bHeight) {
this.showLetter = letterData[i].name;
this.isShowLetter = true;
break;
}
}
this.scrollPage(this.showLetter);
},
//计算要滑动的距离
countScrollTop(showLetter) {
let scrollTop = 0;
let cityCount = 0;
let initialCount = 0;
for (let item of Object.keys(this.bookList)) {
if (showLetter == item) {
scrollTop = initialCount * uni.upx2px(this.bookKeyHeight) + cityCount * uni.upx2px(this.bookItemHeight);
break;
} else {
initialCount++;
cityCount += this.bookList[item].length;
}
}
return scrollTop;
},
//页面滑动到与选中字母对应的位置
scrollPage(showLetter) {
const scroll = this.countScrollTop(showLetter);
if (scroll != 0 || showLetter == Object.keys(this.bookList)[0]) this.scrollTop = scroll;
}
}
};
</script>
<style lang="less" scoped>
.searchLetter {
position: fixed;
top: 126rpx;
right: 0;
text-align: center;
display: flex;
flex-direction: column;
color: #666;
z-index: 999;
}
.touchClass {
color: #5a5a5a;
font-size: 28upx;
}
.showSlectedLetter {
background-color: rgba(0, 0, 0, 0.5);
display: flex;
justify-content: center;
align-items: center;
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 100upx;
height: 100upx;
border-radius: 10upx;
font-size: 44upx;
z-index: 99;
color: #fff;
}
scroll-view {
// position: fixed;
width: 100%;
}
</style>