feat: 基础模块更新到最新

This commit is contained in:
tangtaoit 2024-05-09 14:09:56 +08:00
parent e99c00df8f
commit e8e98d7751
30 changed files with 1562 additions and 703 deletions

View File

@ -23,6 +23,7 @@
"react-scroll": "^1.8.4",
"react-spinners": "^0.11.0",
"react-viewer": "^3.2.2",
"sensitive-word-tool": "^1.1.9",
"wukongimjssdk": "^1.2.7"
},
"devDependencies": {

View File

@ -27,6 +27,7 @@ import RouteContext from "./Service/Context";
import { ConnectStatus } from "wukongimjssdk";
import { WKBaseContext } from "./Components/WKBase";
import StorageService from "./Service/StorageService";
import { ProhibitwordsService } from "./Service/ProhibitwordsService";
export enum ThemeMode {
light,
@ -315,7 +316,8 @@ export default class WKApp extends ProviderListener {
startMain() {
this.connectIM();
WKApp.dataSource.contactsSync();
WKApp.dataSource.contactsSync(); // 同步通讯录
ProhibitwordsService.shared.sync(); // 同步敏感词
}
connectIM() {

View File

@ -1,84 +0,0 @@
import { Toast } from "@douyinfe/semi-ui";
import { SubscriberChangeListener } from "wukongimjssdk";
import { WKSDK } from "wukongimjssdk";
import React from "react";
import { Component, ReactNode } from "react";
import WKApp from "../../App";
import { GroupRole, SubscriberStatus } from "../../Service/Const";
import RouteContext, { FinishButtonContext } from "../../Service/Context";
import { ChannelSettingRouteData } from "../ChannelSetting/context";
import { IndexTableItem } from "../IndexTable";
import SmallTableEdit from "../SmallTableEdit";
import UserSelect from "../UserSelect";
import "./index.css"
export interface ChannelBlacklistProps {
routeContext: RouteContext<ChannelSettingRouteData>
}
export default class ChannelBlacklist extends Component<ChannelBlacklistProps> {
subscriberChangeListener!: SubscriberChangeListener
componentDidMount() {
this.subscriberChangeListener = () => {
this.setState({})
}
WKSDK.shared().channelManager.addSubscriberChangeListener(this.subscriberChangeListener)
}
componentWillUnmount() {
WKSDK.shared().channelManager.removeSubscriberChangeListener(this.subscriberChangeListener)
}
render(): ReactNode {
const { routeContext } = this.props
const data = routeContext.routeData()
return <div className="wk-channelblacklist">
<SmallTableEdit addTitle="添加黑名单" items={data.subscriberAll.filter((s) => s.status === SubscriberStatus.blacklist).map((subscriber) => {
return {
id: subscriber.uid,
icon: subscriber.avatar,
name: subscriber.remark || subscriber.name,
showAction: true,
onAction: () => {
WKApp.dataSource.channelDataSource.blacklistRemove(data.channel,[subscriber.uid]).catch((err)=>{
Toast.error(err.msg)
})
}
}
})} onAdd={() => {
var btnContext: FinishButtonContext
var selectItems: IndexTableItem[] = []
routeContext.push(<UserSelect onSelect={(items) => {
if (items.length === 0) {
btnContext.disable(true)
} else {
btnContext.disable(false)
}
selectItems = items
}} users={data.subscribers.filter((subscriber) => subscriber.role !== GroupRole.manager && subscriber.role !== GroupRole.owner && subscriber.status === SubscriberStatus.normal).map((item) => {
return new IndexTableItem(item.uid, item.name, item.avatar)
})}></UserSelect>, {
title: "选择成员",
showFinishButton: true,
onFinish: async () => {
btnContext.loading(true)
await WKApp.dataSource.channelDataSource.blacklistAdd(data.channel, selectItems.map((item) => {
return item.id
})).catch((err) => {
Toast.error(err.msg)
})
btnContext.loading(false)
routeContext.pop()
},
onFinishContext: (context) => {
btnContext = context
btnContext.disable(true)
}
})
}}></SmallTableEdit>
</div>
}
}

View File

@ -1,7 +0,0 @@
.wk-channelmanagerlist {
overflow-y: auto;
width: 100%;
height: 100%;
}

View File

@ -1,76 +0,0 @@
import { Popconfirm, Toast } from "@douyinfe/semi-ui";
import { Channel, ChannelTypePerson } from "wukongimjssdk";
import React from "react";
import { Component, ReactNode } from "react";
import WKApp from "../../App";
import { GroupRole, SubscriberStatus } from "../../Service/Const";
import RouteContext, { FinishButtonContext } from "../../Service/Context";
import { ChannelSettingRouteData } from "../ChannelSetting/context";
import { IndexTableItem } from "../IndexTable";
import SmallTableEdit from "../SmallTableEdit";
import UserSelect from "../UserSelect";
import "./index.css"
export interface ChannelManagerListProps {
routeContext: RouteContext<ChannelSettingRouteData>
}
export default class ChannelManagerList extends Component<ChannelManagerListProps> {
render(): ReactNode {
const { routeContext } = this.props
const data = routeContext.routeData()
return <div className="wk-channelmanagerlist">
<SmallTableEdit addTitle="添加管理员" items={data.subscribers.filter((s) => {
return s.role === GroupRole.manager || s.role === GroupRole.owner
}).map((subscriber) => {
return {
id: subscriber.uid,
icon: WKApp.shared.avatarUser(subscriber.uid),
name: subscriber.remark || subscriber.name,
showAction: subscriber.role !== GroupRole.owner,
onAction: () => {
WKApp.dataSource.channelDataSource.managerRemove(data.channel, [subscriber.uid]).catch((err) => {
Toast.error(err.msg)
})
}
}
})} onAdd={() => {
var btnContext: FinishButtonContext
var selectItems: IndexTableItem[] = []
routeContext.push(<UserSelect onSelect={(items) => {
if (items.length === 0) {
btnContext.disable(true)
} else {
btnContext.disable(false)
}
selectItems = items
}} users={data.subscribers.filter((subscriber) => subscriber.role !== GroupRole.manager && subscriber.role !== GroupRole.owner && subscriber.status === SubscriberStatus.normal).map((item) => {
return new IndexTableItem(item.uid, item.name, item.avatar)
})}></UserSelect>, {
title: "选择管理员",
showFinishButton: true,
onFinish: async () => {
btnContext.loading(true)
await WKApp.dataSource.channelDataSource.managerAdd(data.channel, selectItems.map((item) => {
return item.id
})).catch((err) => {
Toast.error(err.msg)
})
btnContext.loading(false)
routeContext.pop()
},
onFinishContext: (context) => {
btnContext = context
btnContext.disable(true)
}
})
}}></SmallTableEdit>
</div>
}
}

View File

@ -2,6 +2,7 @@ import { Channel } from "wukongimjssdk";
import WKApp from "../../App";
import { ChannelQrcodeResp } from "../../Service/DataSource/DataSource";
import { ProviderListener } from "../../Service/Provider";
import { Toast } from "@douyinfe/semi-ui";
export class ChannelQRCodeVM extends ProviderListener {
@ -17,8 +18,13 @@ export class ChannelQRCodeVM extends ProviderListener {
this.requestQRCode()
}
async requestQRCode() {
this.qrcodeResp = await WKApp.dataSource.channelDataSource.qrcode(this.channel)
this.notifyListener()
requestQRCode() {
WKApp.dataSource.channelDataSource.qrcode(this.channel).then((resp)=>{
this.qrcodeResp = resp
this.notifyListener()
}).catch((err) => {
Toast.error(err.msg)
})
}
}

View File

@ -87,10 +87,10 @@ export class ChannelSettingVM extends ProviderListener {
this.subscribers = WKSDK.shared().channelManager.getSubscribes(this.channel)
if(this.subscribers && this.subscribers.length>0) {
for (const subscriber of this.subscribers) {
subscriber.channel = this.channel
if(subscriber.uid === WKApp.loginInfo.uid) {
this.subscriberOfMe = subscriber
this.routeData.subscriberOfMe = this.subscriberOfMe
break
}
}
}

View File

@ -1,4 +1,4 @@
import { Channel, ChannelTypeGroup, ChannelTypePerson, ConversationAction, WKSDK, Message, MessageContent, MessageStatus, Subscriber, Conversation, MessageExtra,CMDContent, PullMode } from "wukongimjssdk";
import { Channel, ChannelTypeGroup, ChannelTypePerson, ConversationAction, WKSDK, Message, MessageContent, MessageStatus, Subscriber, Conversation, MessageExtra, CMDContent, PullMode, MessageContentType } from "wukongimjssdk";
import WKApp from "../../App";
import { SyncMessageOptions } from "../../Service/DataSource/DataProvider";
import { MessageWrap } from "../../Service/Model";
@ -12,6 +12,7 @@ import { MessageListener, MessageStatusListener } from "wukongimjssdk";
import { SendackPacket, Setting } from "wukongimjssdk";
import MergeforwardContent from "../../Messages/Mergeforward";
import { TypingListener, TypingManager } from "../../Service/TypingManager";
import { ProhibitwordsService } from "../../Service/ProhibitwordsService";
export default class ConversationVM extends ProviderListener {
@ -247,10 +248,10 @@ export default class ConversationVM extends ProviderListener {
if (!message.channel.isEqual(this.channel)) {
return
}
if(message.contentType == MessageContentTypeConst.rtcData) {
if (message.contentType == MessageContentTypeConst.rtcData) {
return
}
if(message.header.noPersist) { // 不存储的消息不显示
if (message.header.noPersist) { // 不存储的消息不显示
return
}
if (!message.send && message.header.reddot) {
@ -276,7 +277,7 @@ export default class ConversationVM extends ProviderListener {
} else if (cmdContent.cmd === 'syncMessageExtra') { // 同步消息扩展
console.log("messageExtra---->", message.channel)
if (message.channel.isEqual(this.channel)) {
WKSDK.shared().chatManager.syncMessageExtras(this.channel, this.findMaxExtraVersion()).then((messageExtras)=>{
WKSDK.shared().chatManager.syncMessageExtras(this.channel, this.findMaxExtraVersion()).then((messageExtras) => {
this.updateMessageByMessageExtras(messageExtras)
})
}
@ -353,8 +354,8 @@ export default class ConversationVM extends ProviderListener {
WKSDK.shared().conversationManager.openConversation = conversation
}
this.requestMessagesOfFirstPage(undefined,()=>{
if(this.onFirstMessagesLoaded) {
this.requestMessagesOfFirstPage(undefined, () => {
if (this.onFirstMessagesLoaded) {
this.onFirstMessagesLoaded()
}
})
@ -572,7 +573,7 @@ export default class ConversationVM extends ProviderListener {
let extraVersion = 0
for (let i = this.messages.length - 1; i >= 0; i--) {
const message = this.messages[i]
if(message.remoteExtra.extraVersion>extraVersion) {
if (message.remoteExtra.extraVersion > extraVersion) {
extraVersion = message.remoteExtra.extraVersion
}
}
@ -641,7 +642,6 @@ export default class ConversationVM extends ProviderListener {
this.unreadCount = this.lastMessage.messageSeq - this.browseToMessageSeq
}
}
console.log("oldUnreadCount--->", oldUnreadCount, this.unreadCount)
if (oldUnreadCount != this.unreadCount) {
const conversation = WKSDK.shared().conversationManager.findConversation(this.channel)
if (conversation) {
@ -751,9 +751,9 @@ export default class ConversationVM extends ProviderListener {
const remoteMessages = await WKApp.conversationProvider.syncMessages(this.channel, opts)
const newMessages = new Array<Message>()
if(remoteMessages && remoteMessages.length>0) {
if (remoteMessages && remoteMessages.length > 0) {
remoteMessages.forEach(msg => {
if (!msg.isDeleted) {
if (!msg.isDeleted) {
newMessages.push(msg)
}
});
@ -763,14 +763,14 @@ export default class ConversationVM extends ProviderListener {
allMessages = this.sortMessages(allMessages)
if (remoteMessages && remoteMessages.length > 0) {
if(lastRemoteMessageSeq <= 0 && remoteMessages.length >= opts.limit) {
if (lastRemoteMessageSeq <= 0 && remoteMessages.length >= opts.limit) {
this.pullupHasMore = true
}else if (lastRemoteMessageSeq > remoteMessages[remoteMessages.length - 1].messageSeq) {
} else if (lastRemoteMessageSeq > remoteMessages[remoteMessages.length - 1].messageSeq) {
this.pullupHasMore = true
} else {
this.pullupHasMore = false
}
}else {
} else {
this.pullupHasMore = false;
}
let initMessage: MessageWrap | undefined
@ -816,7 +816,16 @@ export default class ConversationVM extends ProviderListener {
let newMessages = messages
this.distinctMessages(newMessages)
newMessages = this.insertTimeOrHistorySplit(newMessages)
for (let i = 0; i < newMessages.length; i++) {
const message = newMessages[i]
if (message.contentType === MessageContentType.text) {
message.content.text = ProhibitwordsService.shared.filter(message.content.text)
}
}
this.messages = this.genMessageLinkedData(newMessages)
this.notifyListener(() => {
if (callback) {
callback()
@ -831,7 +840,7 @@ export default class ConversationVM extends ProviderListener {
if (minMessage?.messageSeq === 1) { // 如果最小messageSeq=1 说明下拉没消息了直接return
return
}
if (minMessage == null || minMessage.messageSeq <= 0 ) { // 没有消息直接return
if (minMessage == null || minMessage.messageSeq <= 0) { // 没有消息直接return
return
}
console.log("pulldownMessages--->")
@ -844,9 +853,9 @@ export default class ConversationVM extends ProviderListener {
let remoteMessages = await WKApp.conversationProvider.syncMessages(this.channel, opts)
const newMessages = new Array<Message>()
if(remoteMessages && remoteMessages.length>0) {
if (remoteMessages && remoteMessages.length > 0) {
remoteMessages.forEach(msg => {
if (!msg.isDeleted) {
if (!msg.isDeleted) {
newMessages.push(msg)
}
});
@ -865,7 +874,7 @@ export default class ConversationVM extends ProviderListener {
async pullupMessages() {
this.loading = true
const maxMessage = this.getMessageMax()
if (maxMessage == null || maxMessage.messageSeq <= 0 ) { // 没有消息直接return
if (maxMessage == null || maxMessage.messageSeq <= 0) { // 没有消息直接return
console.log("没有maxMessage")
return
}
@ -878,9 +887,9 @@ export default class ConversationVM extends ProviderListener {
let remoteMessages = await WKApp.conversationProvider.syncMessages(this.channel, opts)
const newMessages = new Array<Message>()
if(remoteMessages && remoteMessages.length>0) {
if (remoteMessages && remoteMessages.length > 0) {
remoteMessages.forEach(msg => {
if (!msg.isDeleted) {
if (!msg.isDeleted) {
newMessages.push(msg)
}
});
@ -888,7 +897,7 @@ export default class ConversationVM extends ProviderListener {
if (remoteMessages.length < opts.limit) {
this.pullupHasMore = false
console.log("没有更多消息了")
}else {
} else {
this.pullupHasMore = true
console.log("还有更多消息")
}

View File

@ -76,15 +76,17 @@ export class Subscribers extends Component<SubscribersProps> {
></img>
</div>
) : undefined} */}
{WKApp.endpoints.organizationalTool(
channel,
<div className="wk-subscribers-item">
<img
src={require("./assets/icon_add_more_gray.png")}
alt=""
/>
</div>
)}
{vm.showAdd()
? WKApp.endpoints.organizationalTool(
channel,
<div className="wk-subscribers-item">
<img
src={require("./assets/icon_add_more_gray.png")}
alt=""
/>
</div>
)
: undefined}
{vm.showRemove() ? (
<div
className="wk-subscribers-item"
@ -108,12 +110,20 @@ export class Subscribers extends Component<SubscribersProps> {
context.push(
<IndexTable
items={vm.subscribers.map((s) => {
const vercode = s.orgData?.vercode;
return new IndexTableItem(
s.uid,
s.remark || s.name,
WKApp.shared.avatarUser(s.uid)
WKApp.shared.avatarUser(s.uid),
);
})}
onSelect={(item: IndexTableItem[]) => {
const optItem = item[0];
WKApp.shared.baseContext.showUserInfo(
optItem.id,
channel,
);
}}
></IndexTable>,
new RouteContextConfig({
title: "成员列表",

View File

@ -24,7 +24,7 @@ export class SubscribersVM extends ProviderListener {
}
get subscribersTop():Subscriber[] {
let showMemberNum = this.shouldShowMemberNum()
const subscribers = this.routeData.subscribers

View File

@ -14,7 +14,7 @@ import RouteContext, { FinishButtonContext } from "../../Service/Context";
import { Image } from '@douyinfe/semi-ui';
export interface UserInfoProps extends HTMLProps<any>{
export interface UserInfoProps extends HTMLProps<any> {
uid: string
fromChannel?: Channel // 从那个频道进来的
sections?: Section[]
@ -25,6 +25,66 @@ export interface UserInfoProps extends HTMLProps<any>{
export default class UserInfo extends Component<UserInfoProps> {
getBottomPanel(vm: UserInfoVM, context: RouteContext<any>) {
if (vm.isSelf()) {
return undefined
}
var content = <></>
if (vm.relation() === UserRelation.friend) {
content = <Button theme='solid' type="primary" onClick={() => {
WKApp.shared.baseContext.hideUserInfo()
WKApp.endpoints.showConversation(new Channel(vm.uid, ChannelTypePerson))
}}></Button>
} else {
if (!vm.vercode || vm.vercode == "") { // 没有验证码,不显示添加好友按钮
return undefined
}
content = <Button onClick={() => {
let msg = "我是"
if (vm.fromChannelInfo) {
msg += `群聊"${vm.fromChannelInfo.title}"的${WKApp.loginInfo.name}`
} else {
msg += `${WKApp.loginInfo.name}`
}
var finishButtonContext: FinishButtonContext
context.push(<FriendApplyUI placeholder={msg} onMessage={(m) => {
msg = m
if (!m || m === "") {
finishButtonContext.disable(true)
} else {
finishButtonContext.disable(false)
}
}}></FriendApplyUI>, {
title: "申请添加朋友",
showFinishButton: true,
onFinishContext: (ctx) => {
finishButtonContext = ctx
finishButtonContext.disable(false)
},
onFinish: async () => {
finishButtonContext.loading(true)
await WKApp.dataSource.commonDataSource.friendApply({
uid: vm.uid,
remark: msg,
vercode: vm.vercode || ""
}).then(() => {
WKApp.shared.baseContext.hideUserInfo()
}).catch((err) => {
Toast.error(err.msg)
})
finishButtonContext.loading(false)
}
})
}} ></Button>
}
return <div className="wk-userInfo-footer">
<div className="wk-userinfo-footer-sendbutton">
{content}
</div>
</div>
}
render() {
const { uid, onClose, fromChannel, vercode } = this.props
@ -66,7 +126,7 @@ export default class UserInfo extends Component<UserInfoProps> {
}
{
vm.shouldShowShort() ? <li>
{WKApp.config.appName} {vm.channelInfo?.orgData.short_no}
{WKApp.config.appName} {vm.channelInfo?.orgData.short_no || ''}
</li> : undefined
}
@ -86,54 +146,7 @@ export default class UserInfo extends Component<UserInfoProps> {
<br></br>
</div>
{
vm.isSelf() ? undefined : <div className="wk-userInfo-footer">
<div className="wk-userinfo-footer-sendbutton">
{
vm.relation() === UserRelation.friend ? <Button theme='solid' type="primary" onClick={() => {
WKApp.shared.baseContext.hideUserInfo()
WKApp.endpoints.showConversation(new Channel(vm.uid, ChannelTypePerson))
}}></Button> : <Button onClick={() => {
let msg = "我是"
if (vm.fromChannelInfo) {
msg += `群聊"${vm.fromChannelInfo.title}"的${WKApp.loginInfo.name}`
} else {
msg += `${WKApp.loginInfo.name}`
}
var finishButtonContext: FinishButtonContext
context.push(<FriendApplyUI placeholder={msg} onMessage={(m) => {
msg = m
if (!m || m === "") {
finishButtonContext.disable(true)
} else {
finishButtonContext.disable(false)
}
}}></FriendApplyUI>, {
title: "申请添加朋友",
showFinishButton: true,
onFinishContext: (ctx) => {
finishButtonContext = ctx
finishButtonContext.disable(false)
},
onFinish: async () => {
finishButtonContext.loading(true)
await WKApp.dataSource.commonDataSource.friendApply({
uid: vm.uid,
remark: msg,
vercode: vm.vercode || ""
}).then(() => {
console.log("WKApp.shared.baseContext-->",WKApp.shared.baseContext)
WKApp.shared.baseContext.hideUserInfo()
}).catch((err) => {
Toast.error(err.msg)
})
finishButtonContext.loading(false)
}
})
}} ></Button>
}
</div>
</div>
this.getBottomPanel(vm, context)
}
</div>

View File

@ -11,6 +11,7 @@ import { ProviderListener } from "../../Service/Provider";
import WKApp from "../../App";
import RouteContext from "../../Service/Context";
import { GroupRole } from "../../Service/Const";
import { Convert } from "../../Service/Convert";
export class UserInfoRouteData {
uid!: string;
@ -29,8 +30,6 @@ export class UserInfoVM extends ProviderListener {
fromChannelInfo?: ChannelInfo;
channelInfo?: ChannelInfo;
vercode?: string;
short_no?: string;
channelInfoListener!: ChannelInfoListener;
subscriberChangeListener?: SubscriberChangeListener;
constructor(uid: string, fromChannel?: Channel, vercode?: string) {
@ -63,28 +62,7 @@ export class UserInfoVM extends ProviderListener {
this.reloadFromChannelInfo();
this.channelInfoListener = (channelInfo: ChannelInfo) => {
if (
channelInfo.channel.channelType === ChannelTypePerson &&
channelInfo.channel.channelID === this.uid
) {
this.reloadChannelInfo();
}
if (this.fromChannel) {
if (channelInfo.channel.isEqual(this.fromChannel)) {
this.reloadFromChannelInfo();
}
}
this.notifyListener();
};
WKSDK.shared().channelManager.addListener(this.channelInfoListener);
const channel = new Channel(this.uid, ChannelTypePerson);
this.channelInfo = WKSDK.shared().channelManager.getChannelInfo(channel);
WKSDK.shared().channelManager.fetchChannelInfo(channel);
if (this.channelInfo) {
this.notifyListener();
}
this.reloadChannelInfo();
}
didUnMount(): void {
@ -93,7 +71,6 @@ export class UserInfoVM extends ProviderListener {
this.subscriberChangeListener
);
}
WKSDK.shared().channelManager.removeListener(this.channelInfoListener);
}
reloadSubscribers() {
@ -139,21 +116,10 @@ export class UserInfoVM extends ProviderListener {
}
shouldShowShort() {
if(!this.short_no){
return false
}
if (this.short_no === "") {
return false;
} else {
if (!this.fromChannel) {
return true;
}
if (this.myIsManagerOrCreator()) {
return true;
}
return true;
if (this.channelInfo?.orgData?.short_no) {
return true
}
return false
}
relation(): number {
@ -223,15 +189,16 @@ export class UserInfoVM extends ProviderListener {
}
async reloadChannelInfo() {
this.channelInfo = WKSDK.shared().channelManager.getChannelInfo(
new Channel(this.uid, ChannelTypePerson)
);
if (this.uid && this.channelInfo) {
const res = await WKApp.apiClient.get(`users/${this.uid}`, {
param: { group_no: this.channelInfo?.channel?.channelID },
});
this.short_no = res.short_no;
const res = await WKApp.apiClient.get(`users/${this.uid}`, {
param: { group_no: this.fromChannel?.channelID || '' },
});
this.channelInfo = Convert.userToChannelInfo(res);
if (!this.vercode || this.vercode == "") {
if (res.vercode && res.vercode !== "") {
this.vercode = res.vercode
}
}
this.notifyListener();
}
reloadFromChannelInfo() {
@ -242,4 +209,4 @@ export class UserInfoVM extends ProviderListener {
this.notifyListener();
}
}
}
}

View File

@ -1,4 +1,4 @@
import WKSDK from "wukongimjssdk";
import WKSDK, { MessageContentType } from "wukongimjssdk";
import { ChannelInfoListener } from "wukongimjssdk";
import { ConnectStatus, ConnectStatusListener } from "wukongimjssdk";
import { ConversationAction, ConversationListener } from "wukongimjssdk";
@ -7,6 +7,7 @@ import WKApp, { MessageDeleteListener } from "../../App";
import { ConversationWrap } from "../../Service/Model";
import { ProviderListener } from "../../Service/Provider";
import { animateScroll, scroller } from 'react-scroll';
import { ProhibitwordsService } from "../../Service/ProhibitwordsService";
export class ChatVM extends ProviderListener {
conversations: ConversationWrap[] = new Array()
@ -85,6 +86,9 @@ export class ChatVM extends ProviderListener {
}
if (action === ConversationAction.add) {
console.log("ConversationAction-----add")
if(conversation.lastMessage?.content && conversation.lastMessage?.contentType === MessageContentType.text) {
conversation.lastMessage.content.text = ProhibitwordsService.shared.filter(conversation.lastMessage?.content.text)
}
this.conversations = [new ConversationWrap(conversation), ...this.conversations]
this.notifyListener()
} else if (action === ConversationAction.update) {
@ -92,6 +96,9 @@ export class ChatVM extends ProviderListener {
const existConversation = this.findConversation(conversation.channel)
if (existConversation) {
existConversation.conversation = conversation
if(existConversation.lastMessage?.content && existConversation.lastMessage?.contentType === MessageContentType.text) {
existConversation.lastMessage.content.text = ProhibitwordsService.shared.filter(existConversation.lastMessage?.content.text)
}
}
this.sortConversations()
@ -232,6 +239,9 @@ export class ChatVM extends ProviderListener {
const conversations = await WKSDK.shared().conversationManager.sync({})
if (conversations && conversations.length > 0) {
for (const conversation of conversations) {
if(conversation.lastMessage?.content && conversation.lastMessage?.contentType == MessageContentType.text) {
conversation.lastMessage.content.text = ProhibitwordsService.shared.filter(conversation.lastMessage.content.text)
}
conversationWraps.push(new ConversationWrap(conversation))
}
}

View File

@ -0,0 +1,198 @@
import BigNumber from "bignumber.js";
import { Setting } from "wukongimjssdk";
import { WKSDK, ChannelInfo, Channel, Conversation, Message, MessageStatus, ChannelTypePerson, ChannelTypeGroup,ConversationExtra,Reminder, MessageExtra } from "wukongimjssdk";
export class Convert {
static toConversation(conversationMap: any): Conversation {
const conversation = new Conversation()
conversation.channel = new Channel(conversationMap['channel_id'], conversationMap['channel_type'])
conversation.unread = conversationMap['unread'] || 0;
conversation.timestamp = conversationMap['timestamp'] || 0;
let recents = conversationMap["recents"];
if (recents && recents.length > 0) {
const messageModel = this.toMessage(recents[0]);
conversation.lastMessage = messageModel
}
conversation.extra = {}
conversation.extra.top = conversationMap["stick"]
if(conversationMap["extra"]) {
conversation.remoteExtra = this.toConversationExtra(conversation.channel,conversationMap["extra"])
}
return conversation
}
static toReminder(reminderMap:any) :Reminder {
const reminder = new Reminder()
reminder.channel = new Channel(reminderMap['channel_id'], reminderMap['channel_type'])
reminder.messageID = reminderMap["message_id"]
reminder.messageSeq = reminderMap["message_seq"]
reminder.reminderID = reminderMap["id"]
reminder.reminderType = reminderMap["reminder_type"]
reminder.text = reminderMap["text"]
reminder.data = reminderMap["data"]
reminder.isLocate = reminderMap["is_locate"] === 1
reminder.version = reminderMap["version"]
reminder.done = reminderMap["done"] === 1
return reminder
}
static toConversationExtra(channel:Channel,conversationExtraMap:any) :ConversationExtra {
const conversationExtra = new ConversationExtra()
conversationExtra.channel = channel
conversationExtra.browseTo = conversationExtraMap["browse_to"]
conversationExtra.keepMessageSeq = conversationExtraMap["keep_message_seq"]
conversationExtra.keepOffsetY = conversationExtraMap["keep_offset_y"]
conversationExtra.draft = conversationExtraMap["draft"]||""
conversationExtra.version = conversationExtraMap["version"]
return conversationExtra
}
static toMessage(msgMap: any): Message {
const message = new Message();
if (msgMap['message_idstr']) {
message.messageID = msgMap['message_idstr'];
} else {
message.messageID = new BigNumber(msgMap['message_id']).toString();
}
if (msgMap["header"]) {
message.header.reddot = msgMap["header"]["red_dot"] === 1 ? true : false
}
if (msgMap["setting"]) {
message.setting = Setting.fromUint8(msgMap["setting"])
}
if (msgMap["revoke"]) {
message.remoteExtra.revoke = msgMap["revoke"] === 1 ? true : false
}
if(msgMap["message_extra"]) {
const messageExtra = msgMap["message_extra"]
message.remoteExtra = this.toMessageExtra(messageExtra)
}
message.clientSeq = msgMap["client_seq"]
message.channel = new Channel(msgMap['channel_id'], msgMap['channel_type']);
message.messageSeq = msgMap["message_seq"]
message.clientMsgNo = msgMap["client_msg_no"]
message.fromUID = msgMap["from_uid"]
message.timestamp = msgMap["timestamp"]
message.status = MessageStatus.Normal
const contentObj = msgMap["payload"]
let contentType = 0
if (contentObj) {
contentType = contentObj.type
}
const messageContent = WKSDK.shared().getMessageContent(contentType)
if (contentObj) {
messageContent.decode(this.stringToUint8Array(JSON.stringify(contentObj)))
}
message.content = messageContent
message.isDeleted = msgMap["is_deleted"] === 1
return message
}
static toMessageExtra(msgExtraMap: any) :MessageExtra {
const messageExtra = new MessageExtra()
if (msgExtraMap['message_id_str']) {
messageExtra.messageID = msgExtraMap['message_id_str'];
} else {
messageExtra.messageID = new BigNumber(msgExtraMap['message_id']).toString();
}
messageExtra.messageSeq = msgExtraMap["message_seq"]
messageExtra.readed = msgExtraMap["readed"] === 1
if(msgExtraMap["readed_at"] && msgExtraMap["readed_at"]>0) {
messageExtra.readedAt = new Date(msgExtraMap["readed_at"] )
}
messageExtra.revoke = msgExtraMap["revoke"] === 1
if(msgExtraMap["revoker"]) {
messageExtra.revoker = msgExtraMap["revoker"]
}
messageExtra.readedCount = msgExtraMap["readed_count"] || 0
messageExtra.unreadCount = msgExtraMap["unread_count"] || 0
messageExtra.extraVersion = msgExtraMap["extra_version"] || 0
messageExtra.editedAt = msgExtraMap["edited_at"] || 0
const contentEditObj = msgExtraMap["content_edit"]
if(contentEditObj) {
const contentEditContentType = contentEditObj.type
const contentEditContent = WKSDK.shared().getMessageContent(contentEditContentType)
const contentEditPayloadData = this.stringToUint8Array(JSON.stringify(contentEditObj))
contentEditContent.decode(contentEditPayloadData)
messageExtra.contentEditData = contentEditPayloadData
messageExtra.contentEdit = contentEditContent
messageExtra.isEdit = true
}
return messageExtra
}
static userToChannelInfo(data: any): ChannelInfo {
let channelInfo = new ChannelInfo()
channelInfo.channel = new Channel(data.uid, ChannelTypePerson);
channelInfo.title = data.name;
channelInfo.mute = data.mute === 1;
channelInfo.top = data.top === 1;
channelInfo.online = data.online === 1;
channelInfo.lastOffline = data.last_offline
channelInfo.orgData = data.extra || {};
channelInfo.orgData = { ...channelInfo.orgData, ...data }
channelInfo.orgData.remark = data.remark ?? "";
channelInfo.orgData.displayName = data.remark && data.remark !== "" ? data.remark : channelInfo.title;
channelInfo.orgData.shortNo = data.short_no ?? ""
channelInfo.logo = data.logo
if (!channelInfo.logo || channelInfo.logo === "") {
channelInfo.logo = `users/${data.uid}/avatar`
}
if (data.category === "system" || data.category === "customerService") { // 官方账号
channelInfo.orgData.identityIcon = "./identity_icon/official.png"
channelInfo.orgData.identitySize = { width: "18px", height: "18px" }
} else if (data.category === "visitor") {
channelInfo.orgData.identityIcon = "./identity_icon/visitor.png"
channelInfo.orgData.identitySize = { width: "48px", height: "24px" }
}
return channelInfo
}
static groupToChannelInfo(data: any): ChannelInfo {
let channelInfo = new ChannelInfo()
channelInfo.channel = new Channel(data.group_no, ChannelTypeGroup);
channelInfo.title = data.name;
channelInfo.mute = data.mute === 1;
channelInfo.top = data.top === 1;
channelInfo.online = data.online === 1;
channelInfo.lastOffline = data.last_offline
channelInfo.orgData = data.extra || {};
channelInfo.orgData = { ...channelInfo.orgData, ...data }
channelInfo.orgData.remark = data.remark ?? "";
channelInfo.orgData.displayName = data.remark && data.remark !== "" ? data.remark : channelInfo.title;
channelInfo.orgData.forbidden = data.forbidden;
channelInfo.orgData.invite = data.invite;
channelInfo.orgData.forbiddenAddFriend = data.forbidden_add_friend;
channelInfo.orgData.save = data.save;
channelInfo.logo = data.logo
if (!channelInfo.logo || channelInfo.logo === "") {
channelInfo.logo = `groups/${data.group_no}/avatar`
}
return channelInfo
}
static stringToUint8Array(str: string): Uint8Array {
const newStr = unescape(encodeURIComponent(str))
var arr = [];
for (var i = 0, j = newStr.length; i < j; ++i) {
arr.push(newStr.charCodeAt(i));
}
var tmpUint8Array = new Uint8Array(arr);
return tmpUint8Array
}
}

View File

@ -0,0 +1,58 @@
import WKApp from "../App"
import StorageService from "./StorageService"
import SensitiveWordTool from 'sensitive-word-tool'
export class ProhibitwordsService {
private sensitiveWordTool = new SensitiveWordTool({
})
private constructor() {
}
public static shared = new ProhibitwordsService()
public prohibitwords: Array<any> = []
// 同步敏感词
async sync() {
this.load()
this.refresh()
let lastVersion = 0;
if (this.prohibitwords.length > 0) {
lastVersion = this.prohibitwords[this.prohibitwords.length - 1].version
}
const results = await WKApp.apiClient.get("message/prohibit_words/sync", {
param: {
version: lastVersion
},
})
if (results && results.length > 0) {
for (const result of results) {
if (result.version > lastVersion) {
this.prohibitwords.push(result)
}
}
this.save()
this.refresh()
}
}
// 从存储加载敏感词
load() {
const prohibitwordsJson = StorageService.shared.getItem("prohibitwords")
if (prohibitwordsJson && prohibitwordsJson.length > 0) {
this.prohibitwords = JSON.parse(prohibitwordsJson)
}
}
save() {
StorageService.shared.setItem("prohibitwords", JSON.stringify(this.prohibitwords))
}
refresh() {
const words = this.prohibitwords.map((item) => {
return item.content
})
this.sensitiveWordTool.addWords(words)
}
filter(v:string) {
return this.sensitiveWordTool.filter(v)
}
}

View File

@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1715234815334" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2234" width="72" height="72" xmlns:xlink="http://www.w3.org/1999/xlink"><path d="M973.609745 750.236863 853.432379 750.236863 853.432379 630.039569c0-26.709513-20.033381-46.743891-46.743891-46.743891s-46.743891 20.034377-46.743891 46.743891l0 126.87542L626.391052 756.914989c-26.709513 0-46.743891 20.033381-46.743891 46.743891s20.033381 46.743891 46.743891 46.743891l126.87542 0 0 126.855492c0 26.709513 20.033381 46.743891 46.743891 46.743891 26.709513 0 46.743891-20.034377 46.743891-46.743891L846.754254 850.40277 973.609745 850.40277c26.711506 0 46.743891-20.032384 46.743891-46.743891C1027.031761 776.949366 1000.322248 750.236863 973.609745 750.236863L973.609745 750.236863 973.609745 750.236863zM505.222231 871.609954 505.222231 871.609954 87.137882 871.609954c-15.500585 0-27.135989-11.634407-27.135989-27.175846l0-759.112174c0-15.511546 11.635403-27.145953 27.135989-27.145953l740.858431 0c15.47966 0 27.11606 11.635403 27.11606 27.145953l0 410.313131 0 0 0 0c0 15.500585 11.656329 27.136985 27.176843 27.136985 15.499589 0 27.135989-11.6364 27.135989-27.136985l0 0 0 0L909.425205 58.176977c0-31.031064-27.135989-58.178014-58.177017-58.178014L60.000897-0.001036C28.979797-0.001036 1.82388 27.145913 1.82388 58.176977l0 813.432977c0 31.001171 27.155918 58.157088 58.177017 58.157088l445.221334 0 0 0c15.499589 0 27.135989-11.656329 27.135989-27.155918C532.356226 883.224433 520.720823 871.609954 505.222231 871.609954L505.222231 871.609954 505.222231 871.609954zM499.513639 736.881608c20.033381-40.065765 66.777271-66.777271 113.521162-66.777271l53.42102 0 0-60.098149c0-53.422016 33.389632-100.165907 80.132526-120.198291l0 0 0 0-66.776275-200.319857-233.718457 300.485763-66.777271-106.843036L132.261565 736.881608 499.513639 736.881608 499.513639 736.881608 499.513639 736.881608zM379.316344 309.51046c0-40.054804-33.388636-73.444436-73.453404-73.444436s-73.454401 33.389632-73.454401 73.444436c0 40.066761 33.388636 73.454401 73.454401 73.454401S379.316344 349.577221 379.316344 309.51046L379.316344 309.51046 379.316344 309.51046z" fill="#E46342" p-id="2235"></path></svg>

After

Width:  |  Height:  |  Size: 2.3 KiB

View File

@ -45,4 +45,7 @@ export * from "./Components/ListItem";
export * from "./Components/IndexTable"
export * from "./Components/UserSelect";
export * from "./Components/MeInfo";
export * from "./Components/MeInfo";
export * from "./Service/Context";
export * from "./Components/SmallTableEdit";
export * from "./Service/Convert";

View File

@ -69,8 +69,6 @@ import { DefaultEmojiService } from "./Service/EmojiService";
import IconClick from "./Components/IconClick";
import EmojiToolbar from "./Components/EmojiToolbar";
import MergeforwardContent, { MergeforwardCell } from "./Messages/Mergeforward";
import ChannelManagerList from "./Components/ChannelManagerList";
import ChannelBlacklist from "./Components/ChannelBlacklist";
import { UserInfoRouteData } from "./Components/UserInfo/vm";
import { IconAlertCircle } from "@douyinfe/semi-icons";
import { TypingManager } from "./Service/TypingManager";
@ -78,6 +76,7 @@ import APIClient from "./Service/APIClient";
import { ChannelAvatar } from "./Components/ChannelAvatar";
import { ScreenshotCell, ScreenshotContent } from "./Messages/Screenshot";
import ImageToolbar from "./Components/ImageToolbar";
import { ProhibitwordsService } from "./Service/ProhibitwordsService";
export default class BaseModule implements IModule {
messageTone?: Howl;
@ -86,6 +85,8 @@ export default class BaseModule implements IModule {
return "base";
}
init(): void {
APIClient.shared.logoutCallback = () => {
WKApp.shared.logout();
};
@ -314,7 +315,6 @@ export default class BaseModule implements IModule {
} else if (cmdContent.cmd === "syncReminders") {
// 同步提醒项
WKSDK.shared().reminderManager.sync();
this.tipsAudio();
} else if (cmdContent.cmd === "messageRevoke") {
// 消息撤回
const channel = message.channel;
@ -334,8 +334,8 @@ export default class BaseModule implements IModule {
);
}
} else if (cmdContent.cmd === "userAvatarUpdate") { // 用户头像更新
WKApp.shared.changeChannelAvatarTag(new Channel(param.uid, ChannelTypePerson));
WKApp.dataSource.notifyContactsChange();
WKApp.shared.changeChannelAvatarTag(new Channel(param.uid, ChannelTypePerson));
WKApp.dataSource.notifyContactsChange();
}
});
@ -388,7 +388,6 @@ export default class BaseModule implements IModule {
this.registerUserInfo(); // 注册用户资料功能
this.registerChannelSettings(); // 注册频道设置功能
this.registerChannelManages(); // 注册频道管理功能
this.registerMessageContextMenus(); // 注册消息上下文菜单
this.registerChatToolbars(); // 注册聊天工具栏
@ -511,7 +510,7 @@ export default class BaseModule implements IModule {
WKApp.endpoints.registerChatToolbar("chattoolbar.image", (ctx) => {
return (
<ImageToolbar
icon={require("./assets/toolbars/func_screenshot.svg").default}
icon={require("./assets/toolbars/func_upload_image.svg").default}
conversationContext={ctx}
></ImageToolbar>
);
@ -524,8 +523,8 @@ export default class BaseModule implements IModule {
return {
title: "发起群聊",
icon: require(`${isDark
? "./assets/popmenus_startchat_dark.png"
: "./assets/popmenus_startchat.png"
? "./assets/popmenus_startchat_dark.png"
: "./assets/popmenus_startchat.png"
}`),
onClick: () => {
const channel: any = {
@ -1036,7 +1035,7 @@ export default class BaseModule implements IModule {
return new IndexTableItem(
item.uid,
item.name,
item.avatar
item.avatar,
);
})}
></UserSelect>,
@ -1429,221 +1428,4 @@ export default class BaseModule implements IModule {
90000
);
}
registerChannelManages() {
WKApp.shared.channelManageRegister(
"channel.setting.manage.invite",
(context) => {
const data = context.routeData() as ChannelSettingRouteData;
const channel = data.channel;
const channelInfo = data.channelInfo;
return new Section({
subtitle:
"启用后,群成员需要群主或管理员确认才能邀请朋友进群。扫描二维码进群将同时停用。",
rows: [
new Row({
cell: ListItemSwitch,
properties: {
title: "群聊邀请确认",
checked: channelInfo?.orgData?.invite === 1,
onCheck: (v: boolean, ctx: ListItemSwitchContext) => {
ctx.loading = true;
ChannelSettingManager.shared
.invite(v, channel)
.then(() => {
ctx.loading = false;
data.refresh();
})
.catch((err) => {
ctx.loading = false;
});
},
},
}),
],
});
}
);
WKApp.shared.channelManageRegister(
"channel.setting.manage.transfer",
(context) => {
const data = context.routeData() as ChannelSettingRouteData;
const channel = data.channel;
const subscriberOfMe = data.subscriberOfMe;
if (!subscriberOfMe || subscriberOfMe.role !== GroupRole.owner) {
return;
}
return new Section({
rows: [
new Row({
cell: ListItem,
properties: {
title: "群主管理权转让",
onClick: () => {
context.push(
<UserSelect
cantMulit={true}
onSelect={(items) => {
const item = items[0];
WKApp.shared.baseContext.showAlert({
content: "你将自动放弃群主身份",
onOk: () => {
WKApp.dataSource.channelDataSource
.channelTransferOwner(channel, item.id)
.then(() => {
context.popToRoot();
})
.catch((err) => {
Toast.error(err.msg);
});
},
});
}}
users={data.subscribers
.filter(
(subscriber) =>
!(
subscriber.uid === WKApp.loginInfo.uid ||
subscriber.uid === WKApp.config.fileHelperUID ||
subscriber.uid === WKApp.config.systemUID
)
)
.map((item) => {
return new IndexTableItem(
item.uid,
item.name,
item.avatar
);
})}
></UserSelect>,
{
title: "选择新的群主",
showFinishButton: false,
onFinish: async () => {
context.pop();
},
onFinishContext: (context) => { },
}
);
},
},
}),
],
});
}
);
WKApp.shared.channelManageRegister(
"channel.setting.manage.mute",
(context) => {
const data = context.routeData() as ChannelSettingRouteData;
const channel = data.channel;
const channelInfo = data.channelInfo;
return new Section({
title: "成员设置",
subtitle: "全员禁言启用后,只允许群主和管理员发言。",
rows: [
new Row({
cell: ListItemSwitch,
properties: {
title: "全员禁言",
checked: channelInfo?.orgData?.forbidden === 1,
onCheck: (v: boolean, ctx: ListItemSwitchContext) => {
ctx.loading = true;
ChannelSettingManager.shared
.forbidden(v, channel)
.then(() => {
ctx.loading = false;
data.refresh();
})
.catch((err) => {
ctx.loading = false;
});
},
},
}),
],
});
}
);
WKApp.shared.channelManageRegister(
"channel.setting.manage.prohibitAddFriend",
(context) => {
const data = context.routeData() as ChannelSettingRouteData;
const channel = data.channel;
const channelInfo = data.channelInfo;
return new Section({
rows: [
new Row({
cell: ListItemSwitch,
properties: {
title: "禁止群成员互加好友",
checked: channelInfo?.orgData?.forbidden_add_friend === 1,
onCheck: (v: boolean, ctx: ListItemSwitchContext) => {
ctx.loading = true;
ChannelSettingManager.shared
.forbiddenAddFriend(v, channel)
.then(() => {
ctx.loading = false;
data.refresh();
})
.catch((err) => {
ctx.loading = false;
});
},
},
}),
],
});
}
);
WKApp.shared.channelManageRegister(
"channel.setting.manage.blacklist",
(context) => {
return new Section({
rows: [
new Row({
cell: ListItem,
properties: {
title: "群黑名单",
onClick: () => {
context.push(
<ChannelBlacklist
routeContext={context}
></ChannelBlacklist>,
{
title: "群黑名单",
}
);
},
},
}),
],
});
}
);
WKApp.shared.channelManageRegister(
"channel.setting.manage.managerlist",
(context) => {
const data = context.routeData() as ChannelSettingRouteData;
const subscriberOfMe = data.subscriberOfMe;
if (subscriberOfMe?.role !== GroupRole.owner) {
return;
}
return new Section({
title: "群主、管理员",
rows: [
new Row({
cell: ChannelManagerList,
properties: {
routeContext: context,
},
}),
],
});
}
);
}
}

View File

@ -6,9 +6,10 @@ import { toSimplized } from "@tsdaodao/base";
import { getPinyin } from "@tsdaodao/base";
import classnames from "classnames";
import { Toast } from "@douyinfe/semi-ui";
import { Channel, ChannelTypePerson, WKSDK } from "wukongimjssdk";
import { Channel, ChannelTypePerson, WKSDK,ChannelInfoListener,ChannelInfo } from "wukongimjssdk";
import { ContactsListManager } from "../Service/ContactsListManager";
import { Card } from "@tsdaodao/base/src/Messages/Card";
import WKAvatar from "@tsdaodao/base/src/Components/WKAvatar";
@ -22,6 +23,7 @@ export class ContactsState {
export default class ContactsList extends Component<any, ContactsState> {
contactsChangeListener!: ContactsChangeListener
channelInfoListener!: ChannelInfoListener
contextMenusContext!: ContextMenusContext
baseContext!: WKBaseContext
constructor(props: any) {
@ -38,10 +40,31 @@ export default class ContactsList extends Component<any, ContactsState> {
this.rebuildIndex()
}
this.channelInfoListener = (channelInfo:ChannelInfo)=>{
if(channelInfo.channel.channelType !== ChannelTypePerson) {
return
}
//是否包含
let exist = false
WKApp.dataSource.contactsList.forEach((v)=>{
if(v.uid === channelInfo.channel.channelID) {
exist = true
v.name = channelInfo.title
v.remark = channelInfo?.orgData.remark
return
}
})
if(exist) {
this.rebuildIndex()
}
}
WKApp.dataSource.addContactsChangeListener(this.contactsChangeListener)
this.rebuildIndex()
WKSDK.shared().channelManager.addListener(this.channelInfoListener)
ContactsListManager.shared.setRefreshList = () => {
this.setState({})
}
@ -50,6 +73,7 @@ export default class ContactsList extends Component<any, ContactsState> {
componentWillUnmount() {
ContactsListManager.shared.setRefreshList = undefined
WKApp.dataSource.removeContactsChangeListener(this.contactsChangeListener)
WKSDK.shared().channelManager.removeListener(this.channelInfoListener)
}
@ -81,7 +105,7 @@ export default class ContactsList extends Component<any, ContactsState> {
})
}
buildIndex(contacts: Contacts[]) {
const indexItemMap = new Map<string, Contacts[]>()
let indexList = []
@ -148,7 +172,7 @@ export default class ContactsList extends Component<any, ContactsState> {
{i === 0 ? indexName : ""}
</div>
<div className="wk-contacts-section-item-avatar">
<img src={item.avatar}></img>
<WKAvatar channel={new Channel(item.uid, ChannelTypePerson)}></WKAvatar>
</div>
<div className="wk-contacts-section-item-name">
{name}
@ -212,7 +236,6 @@ export default class ContactsList extends Component<any, ContactsState> {
WKSDK.shared().chatManager.send(card, channel)
}
Toast.success("分享成功!")
}
}, "分享名片")
}

View File

@ -1,29 +1,31 @@
.wk-friendadd .wk-search-box{
border-radius: 0px;
background-color: white;
margin: 0px 15px;
.wk-friendadd .wk-search-box {
border-radius: 0px;
background-color: white;
}
.wk-friendadd .wk-search-input {
max-width: 100%;
max-width: 100%;
}
.wk-friendadd .wk-search-input input {
width: 250px;
width: 250px;
}
.wk-friendadd-content-qrcode {
margin-top: 10px;
text-align: center;
margin-top: 10px;
text-align: center;
display: flex;
align-items: center;
justify-content: center;
}
body[theme-mode=dark] .wk-friendadd-content-qrcode {
color: white;
body[theme-mode="dark"] .wk-friendadd-content-qrcode {
color: white;
}
.wk-friendadd-content-qrcode img {
width: 18px;
height: 18px;
cursor: pointer;
}
margin-left: 4px;
width: 18px;
height: 18px;
cursor: pointer;
}

View File

@ -1,66 +1,100 @@
import { WKApp, WKViewQueueHeader, Provider } from "@tsdaodao/base";
import React from "react";
import { Component, ReactNode } from "react";
import "./index.css"
import "./index.css";
import { GroupSaveVM } from "./vm";
import { Button,Toast } from "@douyinfe/semi-ui";
import { IndexTableItem,ContactsSelect } from "@tsdaodao/base";
import { Button, Toast } from "@douyinfe/semi-ui";
import { IndexTableItem, ContactsSelect } from "@tsdaodao/base";
import { FinishButtonContext } from "@tsdaodao/base/src/Service/Context";
export default class GroupSave extends Component {
render(): ReactNode {
return <Provider create={()=>{
return new GroupSaveVM()
}} render={(vm:GroupSaveVM)=>{
return <div className="wk-groupsave">
<WKViewQueueHeader title="保存的群" onBack={() => {
WKApp.routeLeft.pop()
}} action={<div className="wk-viewqueueheader-content-action">
<Button size="small" onClick={()=>{
var selectItems: IndexTableItem[]
var finishButtonContext: FinishButtonContext
WKApp.routeLeft.push(<ContactsSelect showFinishButton={true} onFinishButtonContext={(context) => {
finishButtonContext = context
}} onSelect={(items) => {
selectItems = items
}} showHeader={true} onBack={() => {
WKApp.routeLeft.pop()
}} onFinished={() => {
if (selectItems && selectItems.length > 0) {
finishButtonContext.loading(true)
WKApp.dataSource.channelDataSource.createChannel(selectItems.map((item) => {
return item.id
})).then(() => {
finishButtonContext.loading(false)
WKApp.routeLeft.pop()
}).catch((err) => {
Toast.error(err.msg)
finishButtonContext.loading(false)
})
}
}}></ContactsSelect>)
}} ></Button>
</div>}></WKViewQueueHeader>
<div className="wk-groupsave-content">
render(): ReactNode {
return (
<Provider
create={() => {
return new GroupSaveVM();
}}
render={(vm: GroupSaveVM) => {
return (
<div className="wk-groupsave">
<WKViewQueueHeader
title="保存的群"
onBack={() => {
WKApp.routeLeft.pop();
}}
action={
<div className="wk-viewqueueheader-content-action">
<Button
size="small"
onClick={() => {
var selectItems: IndexTableItem[];
var finishButtonContext: FinishButtonContext;
WKApp.routeLeft.push(
<ContactsSelect
showFinishButton={true}
onFinishButtonContext={(context) => {
finishButtonContext = context;
}}
onSelect={(items) => {
selectItems = items;
}}
showHeader={true}
onBack={() => {
WKApp.routeLeft.pop();
}}
onFinished={() => {
if (selectItems && selectItems.length > 0) {
finishButtonContext.loading(true);
WKApp.dataSource.channelDataSource
.createChannel(
selectItems.map((item) => {
return item.id;
})
)
.then(() => {
finishButtonContext.loading(false);
WKApp.routeLeft.pop();
})
.catch((err) => {
Toast.error(err.msg);
finishButtonContext.loading(false);
});
}
}}
></ContactsSelect>
);
}}
>
</Button>
</div>
}
></WKViewQueueHeader>
<div className="wk-groupsave-content">
<ul>
{
vm.groups.map((g)=>{
return <li key={g.channel.channelID} onClick={()=>{
WKApp.endpoints.showConversation(g.channel)
}}>
<div className="wk-groupsave-content-avatar">
<img src={WKApp.shared.avatarChannel(g.channel)}></img>
</div>
<div className="wk-groupsave-content-title">{g.title}</div>
</li>
})
}
{vm.groups.map((g) => {
return (
<li
key={g.channel.channelID}
onClick={() => {
WKApp.endpoints.showConversation(g.channel);
}}
>
<div className="wk-groupsave-content-avatar">
<img src={WKApp.shared.avatarChannel(g.channel)} alt="" />
</div>
<div className="wk-groupsave-content-title">
{g.title}
</div>
</li>
);
})}
</ul>
</div>
</div>
</div>
}}>
</Provider>
}
}
);
}}
></Provider>
);
}
}

View File

@ -1,92 +1,58 @@
import {
FriendApplyState,
WKApp,
WKViewQueueHeader,
Provider,
} from "@tsdaodao/base";
import { FriendApplyState, WKApp, WKViewQueueHeader, Provider } from "@tsdaodao/base";
import React from "react";
import { Component, ReactNode } from "react";
import { Button } from "@douyinfe/semi-ui";
import "./index.css";
import { Button } from '@douyinfe/semi-ui';
import "./index.css"
import { NewFriendVM } from "./vm";
import "./index.css";
import "./index.css"
import { FriendAdd } from "../FriendAdd";
export class NewFriend extends Component {
render(): ReactNode {
return (
<Provider
create={() => {
return new NewFriendVM();
}}
render={(vm: NewFriendVM) => {
return (
<div className="wk-newfriend">
<WKViewQueueHeader
title="新朋友"
onBack={() => {
WKApp.routeLeft.pop();
}}
action={
<div className="wk-viewqueueheader-content-action">
<Button
size="small"
onClick={() => {
WKApp.routeLeft.push(
<FriendAdd
onBack={() => {
WKApp.routeLeft.pop();
}}
></FriendAdd>
);
}}
>
</Button>
</div>
}
></WKViewQueueHeader>
<div className="wk-newfriend-content">
<ul>
{vm.friendApplys.map((f) => {
return (
<li key={f.to_uid}>
<div className="wk-newfriend-content-avatar">
<img src={WKApp.shared.avatarUser(f.to_uid)}></img>
</div>
<div className="wk-newfriend-content-title">
<div className="wk-newfriend-content-title-name">
{f.to_name}
</div>
<div className="wk-newfriend-content-title-remark">
{f.remark}
</div>
</div>
<div className="wk-newfriend-content-action">
<Button
loading={
vm.currentFriendApply?.to_uid === f.to_uid &&
vm.sureLoading
}
disabled={f.status == FriendApplyState.accepted}
onClick={() => {
vm.friendSure(f);
}}
>
{f.status == FriendApplyState.accepted
? "已添加"
: "确认"}
</Button>
</div>
</li>
);
})}
</ul>
</div>
render(): ReactNode {
return <Provider create={() => {
return new NewFriendVM()
}} render={(vm: NewFriendVM) => {
return <div className="wk-newfriend">
<WKViewQueueHeader title="新朋友" onBack={() => {
WKApp.routeLeft.pop()
}} action={<div className="wk-viewqueueheader-content-action">
<Button size="small" onClick={()=>{
WKApp.routeLeft.push(<FriendAdd onBack={()=>{
WKApp.routeLeft.pop()
}}></FriendAdd>)
}} ></Button>
</div>}></WKViewQueueHeader>
<div className="wk-newfriend-content">
<ul>
{
vm.friendApplys.map((f) => {
return <li key={f.to_uid} >
<div className="wk-newfriend-content-avatar">
<img src={WKApp.shared.avatarUser(f.to_uid)}></img>
</div>
<div className="wk-newfriend-content-title">
<div className="wk-newfriend-content-title-name">
{f.to_name}
</div>
<div className="wk-newfriend-content-title-remark">
{f.remark}
</div>
</div>
<div className="wk-newfriend-content-action">
<Button loading={vm.currentFriendApply?.to_uid === f.to_uid && vm.sureLoading } disabled={f.status == FriendApplyState.accepted} onClick={()=>{
vm.friendSure(f)
}}>{f.status == FriendApplyState.accepted ? "已添加" : "确认"}</Button>
</div>
</li>
})
}
</ul>
</div>
</div>
);
}}
></Provider>
);
}
}
}}>
</Provider>
}
}

View File

@ -33,7 +33,8 @@ export class NewFriendVM extends ProviderListener {
.friendSure(apply.token || "")
.then(() => {
apply.status = FriendApplyState.accepted;
WKApp.shared.updateFriendApply(apply);
// WKApp.shared.updateFriendApply(apply);
this.delFriendApply(apply);
this.sureLoading = false;
this.notifyListener();
})
@ -70,6 +71,9 @@ export class NewFriendVM extends ProviderListener {
}
async clearFriendApply(): Promise<void> {
if (WKApp.loginInfo.isLogined()) {
WKApp.loginInfo.setStorageItem(`${WKApp.loginInfo.uid}-friend-applys-unread-count`, '0')
}
await WKApp.apiClient.delete(`/user/reddot/friendApply`);
}

View File

@ -0,0 +1,223 @@
.wk-main-modal-organizational-group-new .semi-modal-body-wrapper {
margin: 0;
}
.wk-main-modal-organizational-group-new .semi-modal-close {
display: none;
}
.wk-main-modal-organizational-group-new .semi-modal-body {
height: 500px;
display: flex;
}
.wk-main-modal-organizational-group-new .semi-modal-content {
border: none !important;
padding: 0px !important;
}
.wk-organizational-group-new-left {
width: 50%;
height: 100%;
background-color: var(--wk-color-secondary);
padding: 12px;
display: flex;
flex-direction: column;
}
.wk-organizational-group-new-left .group-new-left-main {
flex: 1 1 auto;
overflow-y: auto;
}
.wk-organizational-group-new-left .group-new-left-search {
margin-bottom: 12px;
}
.wk-organizational-group-new-left
.group-new-left-search
.group-new-left-search-input.semi-input-wrapper-focus {
border: var(--wk-color-theme) solid 1px;
}
.wk-organizational-group-new-left
.group-new-left-search
.group-new-left-search-input.semi-input-wrapper-focus:active {
border: var(--wk-color-theme) solid 1px;
}
/* 好友选择 */
.group-new-left-main .friend-opt .organization-name {
display: flex;
align-items: center;
padding: 12px 8px;
color: var(--semi-color-text-0);
cursor: pointer;
}
.group-new-left-main .friend-opt .organization-name:hover {
background-color: var(--semi-color-fill-0);
}
.group-new-left-main .friend-opt .friend-opt-main .friend-opt-item {
display: flex;
align-items: center;
padding: 12px 8px;
font-size: 14px;
line-height: 20px;
color: var(--semi-color-text-0);
row-gap: 0;
}
.group-new-left-main .friend-opt .friend-opt-main .friend-opt-item:hover {
background-color: var(--semi-color-fill-0);
}
/* 组织架构选择 */
.group-new-left-main .organizational-opt {
height: 100%;
display: flex;
flex-direction: column;
}
.group-new-left-main .organizational-opt-header {
height: 44px;
margin-bottom: 12px;
border-radius: 4px;
}
.group-new-left-main .organizational-opt-header .wk-viewqueueheader {
background-color: var(--semi-color-fill-0);
height: 100%;
}
.group-new-left-main .organizational-opt-main {
flex: 1 1 auto;
overflow-y: auto;
}
.organizational-tree {
width: 100%;
height: 100%;
}
.organizational-tree .semi-input-wrapper-focus {
border: var(--wk-color-theme) solid 1px;
}
.organizational-tree .semi-input-wrapper-focus:active {
border: var(--wk-color-theme) solid 1px;
}
.organizational-tree .semi-tree-option-list {
padding-top: 0;
flex: 1 1 auto;
overflow-y: auto;
}
.organizational-tree .semi-tree-option-list li.semi-tree-option {
box-sizing: border-box;
padding-top: 12px;
padding-bottom: 12px;
}
.semi-checkbox:hover .semi-checkbox-inner-display {
background: var(--semi-color-fill-0);
box-shadow: inset 0 0 0 1px var(--wk-color-theme);
}
.semi-checkbox:hover .semi-checkbox-inner-checked .semi-checkbox-inner-display {
background: var(--wk-color-theme);
border-color: var(--wk-color-theme);
color: var(--semi-color-white);
}
.organizational-tree .semi-tree-option-list-block .department-icon {
color: var(--wk-color-theme);
}
.organizational-tree
.semi-tree-option-list-block
.semi-tree-option-selected
.department-icon {
color: #fff;
}
.organizational-tree .semi-tree-option-list-block .semi-tree-option-selected {
background-color: var(--wk-color-theme);
color: #fff;
}
.organizational-tree
.semi-tree-option-list-block
.semi-tree-option-selected:hover {
background-color: var(--wk-color-theme);
color: #fff;
}
.organizational-tree
.semi-tree-option-list-block
.semi-tree-option-selected
.semi-tree-option-expand-icon {
color: #fff;
}
.wk-organizational-group-new-right {
width: 50%;
height: 100%;
background-color: var(--wk-color-item);
padding: 12px;
display: flex;
flex-direction: column;
}
.wk-organizational-group-new-right .organizational-group-new-right-title {
font-size: 15px;
font-weight: bold;
}
.wk-organizational-group-new-right .organizational-group-new-right-body {
flex: 1 1 auto;
overflow-y: auto;
margin: 12px 0;
}
.wk-organizational-group-new-right
.organizational-group-new-right-body
.opt-personnel-item {
padding: 12px 0;
display: flex;
justify-content: space-between;
align-items: center;
font-size: 14px;
line-height: 20px;
word-break: break-word;
color: var(--semi-color-text-0);
}
.wk-organizational-group-new-right
.organizational-group-new-right-body
.opt-personnel-item
.user-info {
display: flex;
align-items: center;
}
.wk-organizational-group-new-right
.organizational-group-new-right-body
.opt-personnel-item
.close-icon {
display: flex;
align-items: center;
}
.wk-organizational-group-new-right
.organizational-group-new-right-body
.opt-personnel-item
.close-icon
.semi-icon {
cursor: pointer;
width: 24px;
height: 24px;
border-radius: 6px;
display: flex;
align-items: center;
justify-content: center;
}
.wk-organizational-group-new-right
.organizational-group-new-right-body
.opt-personnel-item
.close-icon
.semi-icon:hover {
background-color: rgb(112, 117, 121, 0.08);
}
.wk-organizational-group-new-right .organizational-group-new-right-footer {
display: flex;
justify-content: flex-end;
}
.wk-but-ok:hover {
background-color: var(--wk-color-theme) !important;
}

View File

@ -0,0 +1,667 @@
import React from "react";
import { Component, ReactNode } from "react";
import {
Modal,
Button,
Space,
Tree,
Input,
CheckboxGroup,
Checkbox,
Toast,
} from "@douyinfe/semi-ui";
import { BasicTreeNodeData } from "@douyinfe/semi-foundation/lib/cjs/tree/foundation";
import { WKApp, ThemeMode, WKViewQueueHeader } from "@tsdaodao/base";
import WKAvatar from "@tsdaodao/base/src/Components/WKAvatar";
import "./index.css";
interface IPorpsOrganizationalGroupNew {
channel: {
channelID: string;
channelType: number;
};
showAdd?: boolean;
render?: JSX.Element;
remove?: () => void;
}
interface ISateOrganizationalGroupNew {
showModal: boolean;
optTitle: string;
organizationInfo: {
name: string;
is_upload_logo?: number;
org_id?: string;
short_no?: string;
};
treeData: any[];
optPersonnelData: any[];
isFriend: boolean;
friendData: any[];
friendSearchData: any[];
searchVaule: string;
}
export class OrganizationalGroupNew extends Component<
IPorpsOrganizationalGroupNew,
ISateOrganizationalGroupNew
> {
state: ISateOrganizationalGroupNew = {
showModal: false,
optTitle: "",
organizationInfo: {
name: "",
},
treeData: [],
optPersonnelData: [],
isFriend: true,
friendData: [],
friendSearchData: [],
searchVaule: "",
};
treeRef: any = React.createRef();
constructor(props: IPorpsOrganizationalGroupNew) {
super(props);
}
componentDidMount(): void {
this.getFriendData();
}
// 获取加入公司
async getJoinOrganization() {
const res = await WKApp.apiClient.get("/organization/joined");
if (res && res.length > 0) {
const organizationInfo = res[0];
this.setState({
organizationInfo: organizationInfo,
});
if (organizationInfo) {
this.getOrganizationDepartment(organizationInfo.org_id);
}
}
}
// 获取加入公司所有部门
async getOrganizationDepartment(org_id: string) {
const res = await WKApp.apiClient.get(
`/organizations/${org_id}/department`
);
// 部门
const departments: any = this.handleTree(res.departments);
// 人员
const employees: any[] = [];
res.employees.map((y: any) => {
employees.push({
label: y.employee_name,
value: y.employee_id,
key: `uid_${y.uid}`,
icon: (
<WKAvatar
src={WKApp.shared.avatarUser(y.uid as string)}
style={{ width: "20px", height: "20px", marginRight: "6px" }}
/>
),
is_employee: true,
...y,
});
});
this.setState({
treeData: [...departments, ...employees],
});
}
// 处理全部子部门人数
handelEmployeesNum(arr: any[]) {
let employeesNums: number[] = [];
arr.map((item) => {
if (item.employees && item.employees.length > 0) {
employeesNums.push(item.employees.length);
}
if (item.children && item.children.length > 0) {
const employeesNum = this.handelEmployeesNum(item.children);
employeesNums = [...employeesNums, ...employeesNum];
}
});
return employeesNums;
}
handleTree(arr: any[]) {
const OTree: any = [];
arr.map((item: any) => {
let children: any[] = [];
let employeesNum: number = 0;
// 获取当前部门人数
if (item.employees) {
employeesNum = parseInt(item.employees.length);
}
// 获取全部子部门人数
if (item.children) {
const employeesNums = this.handelEmployeesNum(item.children);
employeesNums.map((num) => {
employeesNum += num;
});
}
// 有组织和人员
if (item.children && item.employees) {
children = this.handleTree(item.children);
const employees: any[] = [];
item.employees.map((y: any) => {
employees.push({
label: y.employee_name,
value: y.employee_id,
key: `${item.dept_id}_${y.employee_id}`,
icon: (
<WKAvatar
src={WKApp.shared.avatarUser(y.uid as string)}
style={{ width: "24px", height: "24px", marginRight: "6px" }}
/>
),
is_employee: true,
...y,
});
});
children = [...children, ...employees];
}
// 只有人员
if (!item.children && item.employees) {
const employees: any[] = [];
item.employees.map((y: any) => {
employees.push({
label: y.employee_name,
value: y.employee_id,
key: `${item.dept_id}_${y.employee_id}`,
icon: (
<WKAvatar
src={WKApp.shared.avatarUser(y.uid as string)}
style={{ width: "20px", height: "20px", marginRight: "6px" }}
/>
),
is_employee: true,
...y,
});
});
children = [...children, ...employees];
}
OTree.push({
label: employeesNum > 0 ? `${item.name}(${employeesNum})` : `${item.name}`,
value: item.dept_id,
key: item.short_no,
icon: (
<span
className="department-icon"
style={{ display: "flex", marginRight: "6px" }}
>
<svg
width="24px"
height="18px"
viewBox="0 0 24 18"
version="1.1"
xmlns="http://www.w3.org/2000/svg"
>
<g
id="页面-1"
stroke="none"
strokeWidth="1"
fill="none"
fillRule="evenodd"
>
<g
id="02"
transform="translate(-98.000000, -131.000000)"
fill="currentColor"
fillRule="nonzero"
>
<g
id="wenjianjia"
transform="translate(98.000000, 131.000000)"
>
<path
d="M21.343573,4.88046647 C21.6937698,4.9154519 22.0439666,5.00291545 22.3941634,5.14285714 C22.7443602,5.28279883 23.0484784,5.47959184 23.3065182,5.73323615 C23.5645579,5.98688047 23.7580877,6.30612245 23.8871076,6.6909621 C24.0161275,7.07580175 24.0345589,7.5393586 23.9424018,8.08163265 C23.905539,8.22157434 23.8318134,8.56705539 23.7212249,9.1180758 C23.6106365,9.66909621 23.4816166,10.303207 23.3341653,11.0204082 C23.186714,11.7376093 23.0208313,12.4766764 22.8365172,13.2376093 C22.6522031,13.9985423 22.4771047,14.6501458 22.311222,15.1924198 C22.219065,15.5072886 22.0854373,15.8309038 21.9103389,16.1632653 C21.7352405,16.4956268 21.5094557,16.7973761 21.2329845,17.0685131 C20.9565134,17.3396501 20.624748,17.5626822 20.2376884,17.7376093 C19.8506288,17.9125364 19.3898435,18 18.8553326,18 L3.53883076,18 C3.15177114,18 2.75088797,17.9212828 2.33618124,17.7638484 C1.92147451,17.606414 1.53902275,17.3833819 1.18882596,17.0947522 C0.838629164,16.8061224 0.552942306,16.4562682 0.331765384,16.0451895 C0.110588461,15.6341108 0,15.1749271 0,14.6676385 L0,3.41107872 C0,2.34402332 0.304118268,1.50874636 0.912354805,0.905247813 C1.52059134,0.301749271 2.37765192,0 3.48353653,0 L17.0859173,0 C17.4914083,0 17.9107229,0.0743440233 18.343861,0.22303207 C18.7769991,0.371720117 19.1686666,0.577259475 19.5188634,0.839650146 C19.8690602,1.10204082 20.154747,1.40816327 20.375924,1.75801749 C20.5971009,2.10787172 20.7076894,2.48396501 20.7076894,2.88629738 L20.7076894,3.17492711 L19.2700394,3.17492711 C18.532783,3.17492711 17.6711145,3.17055394 16.6850341,3.16180758 C15.6989536,3.15306122 14.6483633,3.14868805 13.5332629,3.14868805 C12.4181626,3.14868805 11.3767879,3.14431487 10.4091389,3.13556851 C9.44148987,3.12682216 8.60746856,3.12244898 7.90707497,3.12244898 L6.63530767,3.12244898 C6.15609101,3.12244898 5.78285495,3.26676385 5.5155995,3.55539359 C5.24834405,3.84402332 5.04099069,4.2244898 4.89353941,4.696793 C4.74608813,5.20408163 4.58020543,5.74198251 4.39589133,6.31049563 C4.21157723,6.87900875 4.04569454,7.40816327 3.89824326,7.89795918 C3.71392915,8.47521866 3.52961505,9.03498542 3.34530095,9.57725948 C3.30843813,9.71720117 3.29000672,9.83090379 3.29000672,9.91836735 C3.29000672,10.2157434 3.39598733,10.4650146 3.60794855,10.6661808 C3.81990976,10.8673469 4.08255736,10.96793 4.39589133,10.96793 C4.96726505,10.96793 5.35432466,10.6268222 5.55707017,9.94460641 L7.02236728,4.85422741 C9.41845061,4.87172012 11.6117884,4.88046647 13.6023807,4.88046647 L21.343573,4.88046647 L21.343573,4.88046647 Z"
id="路径"
></path>
</g>
</g>
</g>
</svg>
</span>
),
dept_id: item.dept_id,
org_id: item.org_id,
name: item.name,
is_department: true,
children: children,
});
});
return OTree;
}
onSelectOrganization(
selectedKey: string,
selected: boolean,
selectedNode: BasicTreeNodeData
) {
if (selected) {
const getOptTreeData = [];
getOptTreeData.push(selectedNode);
const newOptTreeData = [
...this.state.optPersonnelData,
...this.handOptTree(getOptTreeData),
];
// 处理去重
const uniqueArr = Object.values(
newOptTreeData.reduce((acc, curr) => {
acc[curr.uid] = curr;
return acc;
}, {})
);
this.setState({
optPersonnelData: [...uniqueArr],
});
}
}
handOptTree(arr: any[]) {
let OTree: any = [];
arr.map((item: any) => {
if (item.children && item.is_department) {
const res = this.handOptTree(item.children);
OTree = [...OTree, ...res];
}
if (item.is_employee) {
OTree.push({
name: item.employee_name,
uid: item.uid,
});
}
});
return OTree;
}
onDelOptPersonnel(uid: string) {
const newOpt = this.state.optPersonnelData.filter((item) => {
return item.uid !== uid;
});
this.setState({
optPersonnelData: [...newOpt],
});
}
getFriendData() {
const setFriendData: any[] = [];
WKApp.dataSource.contactsList.map((item) => {
setFriendData.push({
name: item.name,
uid: item.uid,
});
});
this.setState({
friendData: [...setFriendData],
friendSearchData: [...setFriendData],
});
}
onFriendChange(value: string[]) {
const getFriendOpt: any[] = [];
const { friendData, optPersonnelData } = this.state;
value.map((i) => {
friendData.map((y) => {
if (i == y.uid) {
getFriendOpt.push(y);
}
});
});
const newPersonnelData = [...getFriendOpt, ...optPersonnelData];
// 处理去重
const uniqueArr = Object.values(
newPersonnelData.reduce((acc, curr) => {
acc[curr.uid] = curr;
return acc;
}, {})
);
this.setState({
optPersonnelData: [...uniqueArr],
});
}
onOrginzational() {
this.setState({
isFriend: false,
searchVaule: "",
});
}
onShowModal() {
const { channelType } = this.props.channel;
this.getJoinOrganization();
this.setState({
showModal: true,
optTitle: channelType === 1 ? "创建群" : "请选择联系人",
});
}
onCancel() {
this.setState({
showModal: false,
isFriend: true,
optPersonnelData: [],
});
this.props.remove && this.props.remove();
}
async onOK() {
const channel = this.props.channel as any;
const { optPersonnelData } = this.state;
if (optPersonnelData.length == 0) {
return Toast.warning("请选择联系人");
}
const getOptPersonnelData = optPersonnelData.map((item) => {
return item.uid;
});
// 创建群
if (channel.channelType == 1) {
await WKApp.dataSource.channelDataSource.createChannel([
...getOptPersonnelData,
channel.channelID,
]);
}
// 添加联系人
if (channel.channelType == 2) {
await WKApp.dataSource.channelDataSource.addSubscribers(
channel,
getOptPersonnelData
);
}
this.onCancel();
}
onChangeSearch(value: string) {
const { friendSearchData, isFriend } = this.state;
if (isFriend) {
this.setState({
searchVaule: value,
friendData: friendSearchData.filter((item) => {
return item.name.toLowerCase().indexOf(value.toLowerCase()) !== -1;
}),
});
}
if (!isFriend) {
this.setState({
searchVaule: value,
});
console.log(this.treeRef);
this.treeRef && this.treeRef.current.search(value);
}
}
render(): ReactNode {
const isDark = WKApp.config.themeMode === ThemeMode.dark;
const {
showModal,
treeData,
optTitle,
optPersonnelData,
organizationInfo,
isFriend,
friendData,
searchVaule,
} = this.state;
const { showAdd, render } = this.props;
return (
<div
onClick={(e) => {
e.stopPropagation();
}}
>
{showAdd && (
<div
onClick={() => {
this.onShowModal();
}}
>
<svg
viewBox="0 0 22 22"
fill={WKApp.config.themeColor}
width="20px"
height="20px"
focusable="false"
aria-hidden="true"
>
<g clipPath="url(#clip_user_add)">
<path
fillRule="evenodd"
clipRule="evenodd"
d="M19.0796 19.8369C17.7027 17.6014 14.7559 15.5 10.4593 15.5C6.16267 15.5 3.21588 17.6014 1.83892 19.8369C1.19533 20.8817 2.10818 22 3.33535 22H17.5832C18.8104 22 19.7232 20.8817 19.0796 19.8369Z"
></path>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M14.0499 10.4204C14.5723 10.2065 15.0693 9.55774 15.2962 8.71085C15.5913 7.60959 15.5327 6.62629 14.7285 6.3203C14.6504 2.48444 13.1369 1 9.96023 1C6.7838 1 5.27021 2.48424 5.19199 6.31952C4.38589 6.62467 4.32701 7.60866 4.62233 8.7108C4.84958 9.55892 5.34768 10.2083 5.87087 10.4213C6.71146 12.5727 8.24123 14.013 9.96023 14.013C11.6795 14.013 13.2094 12.5723 14.0499 10.4204Z"
></path>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M20 1C19.4478 1 19 1.44772 19 2V4H17C16.4478 4 16 4.44772 16 5C16 5.55228 16.4478 6 17 6H19V8C19 8.55228 19.4478 9 20 9C20.5523 9 21 8.55228 21 8V6H23C23.5523 6 24 5.55228 24 5C24 4.44772 23.5523 4 23 4H21V2C21 1.44772 20.5523 1 20 1Z"
></path>
</g>
<defs>
<clipPath id="clip_user_add">
<rect width="24" height="24" fill="currentColor"></rect>
</clipPath>
</defs>
</svg>
<div className="wk-conversation-header-mask"></div>
</div>
)}
{render && (
<div
onClick={() => {
this.onShowModal();
}}
>
{render}
</div>
)}
<Modal
width={640}
className="wk-main-modal-organizational-group-new"
footer={null}
closeIcon={<div></div>}
visible={showModal}
centered
maskClosable={false}
onCancel={() => {
this.onCancel();
}}
>
<div className="wk-organizational-group-new-left">
<div className="group-new-left-search">
<Input
className="group-new-left-search-input"
placeholder="搜索"
value={searchVaule}
showClear
onChange={(value) => {
this.onChangeSearch(value);
}}
/>
</div>
<div className="group-new-left-main">
{isFriend ? (
<div className="friend-opt">
{/* 组织架构 */}
{organizationInfo?.org_id && (
<div
className="organization-name"
onClick={() => {
this.onOrginzational();
}}
>
<img
style={{
width: "24px",
height: "24px",
marginRight: "6px",
}}
src={require("../../assets/organizational_new.png")}
alt=""
/>
<span>{organizationInfo?.name}</span>
</div>
)}
<div className="friend-opt-main">
<CheckboxGroup
style={{ width: "100%" }}
onChange={(value) => {
this.onFriendChange(value);
}}
>
{friendData.map((friend) => {
return (
<Checkbox
key={friend.uid}
value={friend.uid}
className="friend-opt-item"
>
<WKAvatar
src={WKApp.shared.avatarUser(
friend.uid as string
)}
style={{
width: "24px",
height: "24px",
marginRight: "6px",
}}
/>
<span>{friend.name}</span>
</Checkbox>
);
})}
</CheckboxGroup>
</div>
</div>
) : (
<div className="organizational-opt">
<div className="organizational-opt-header">
<WKViewQueueHeader
title={organizationInfo.name}
onBack={() => {
this.setState({
isFriend: true,
searchVaule: "",
});
}}
/>
</div>
<div className="organizational-opt-main">
<Tree
ref={this.treeRef}
treeData={treeData}
filterTreeNode
searchRender={false}
multiple
showFilteredOnly={true}
className="organizational-tree"
onSelect={(
selectedKey: string,
selected: boolean,
selectedNode: BasicTreeNodeData
) => {
this.onSelectOrganization(
selectedKey,
selected,
selectedNode
);
}}
/>
</div>
</div>
)}
</div>
</div>
<div className="wk-organizational-group-new-right">
<div className="organizational-group-new-right-title">
{optTitle}
</div>
<div className="organizational-group-new-right-body">
{optPersonnelData &&
optPersonnelData.map((item, index) => {
return (
<div key={item.uid} className="opt-personnel-item">
<div className="user-info">
<WKAvatar
src={WKApp.shared.avatarUser(item.uid as string)}
style={{
width: "24px",
height: "24px",
marginRight: "6px",
}}
/>
<span>{item.name}</span>
</div>
<div
className="close-icon"
onClick={() => {
this.onDelOptPersonnel(item.uid);
}}
>
<span className="semi-icon semi-icon-default semi-icon-close">
<svg
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
width="1em"
height="1em"
focusable="false"
aria-hidden="true"
>
<path
d="M17.6568 19.7782C18.2426 20.3639 19.1924 20.3639 19.7782 19.7782C20.3639 19.1924 20.3639 18.2426 19.7782 17.6568L14.1213 12L19.7782 6.34313C20.3639 5.75734 20.3639 4.8076 19.7782 4.22181C19.1924 3.63602 18.2426 3.63602 17.6568 4.22181L12 9.87866L6.34313 4.22181C5.75734 3.63602 4.8076 3.63602 4.22181 4.22181C3.63602 4.8076 3.63602 5.75734 4.22181 6.34313L9.87866 12L4.22181 17.6568C3.63602 18.2426 3.63602 19.1924 4.22181 19.7782C4.8076 20.3639 5.75734 20.3639 6.34313 19.7782L12 14.1213L17.6568 19.7782Z"
fill="currentColor"
></path>
</svg>
</span>
</div>
</div>
);
})}
</div>
<div className="organizational-group-new-right-footer">
<Space spacing="medium">
<Button
style={{ width: 80 }}
onClick={() => {
this.onCancel();
}}
>
</Button>
<Button
style={{ width: 80 }}
className="wk-but-ok"
theme="solid"
type="primary"
onClick={() => {
this.onOK();
}}
>
</Button>
</Space>
</div>
</div>
</Modal>
</div>
);
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

@ -1,5 +1,5 @@
export { default as ContactsModule } from "./module";
export { default as ContactsModule } from "./module"
export { default as ContactsList } from "./Contacts"
export { default as ContactsList } from "./Contacts";
export { OrganizationalGroupNew } from "./Organizational/GroupNew/index";

View File

@ -6,11 +6,13 @@ import {
ThemeMode,
} from "@tsdaodao/base";
import React from "react";
import ReactDOM from "react-dom";
import Blacklist from "./Blacklist";
import { FriendAdd } from "./FriendAdd";
import GroupSave from "./GroupSave";
import { NewFriend } from "./NewFriend";
import { ContactsListManager } from "./Service/ContactsListManager";
import { OrganizationalGroupNew } from "./Organizational/GroupNew/index";
export default class ContactsModule implements IModule {
id(): string {
@ -30,18 +32,20 @@ export default class ContactsModule implements IModule {
);
// 获取好友未申请添加数量
let unreadCount = 0;
if (WKApp.loginInfo.isLogined()) {
WKApp.apiClient.get(`/user/reddot/friendApply`).then((res) => {
unreadCount = res.count;
WKApp.menus.refresh();
});
}
// let unreadCount = 0;
// if(WKApp.loginInfo.isLogined()){
// WKApp.apiClient.get(`/user/reddot/friendApply`).then(res=>{
// unreadCount = res.count;
// console.log('====', unreadCount)
// WKApp.mittBus.emit('friend-applys-unread-count', unreadCount)
// WKApp.menus.refresh();
// })
// }
WKApp.endpoints.registerContactsHeader("friends.new", (param: any) => {
return (
<IconListItem
badge={unreadCount}
badge={ WKApp.shared.getFriendApplysUnreadCount() }
title="新朋友"
icon={require("./assets/friend_new.png")}
backgroudColor={"var(--wk-color-secondary)"}
@ -101,5 +105,43 @@ export default class ContactsModule implements IModule {
},
};
});
// this.registerOrganizational();
WKApp.endpoints.registerOrganizationalTool(
"contacts.organizational.group.add",
(param) => {
const channel = param.channel as any;
return (
<OrganizationalGroupNew channel={channel} render={param.render} />
);
}
);
WKApp.endpoints.registerOrganizationalLayer(
"contacts.organizational.layer",
(param) => {
const channel = param.channel as any;
const div = document.createElement("div");
const ref: any = React.createRef();
document.body.appendChild(div);
const remove = () => {
if (!ref.current) return;
ReactDOM.unmountComponentAtNode(div);
document.body.removeChild(div);
};
ReactDOM.render(
<OrganizationalGroupNew
ref={ref}
channel={channel}
remove={remove}
/>,
div
);
ref.current.onShowModal();
}
);
}
}
}

View File

@ -11760,6 +11760,11 @@ send@0.18.0:
range-parser "~1.2.1"
statuses "2.0.1"
sensitive-word-tool@^1.1.9:
version "1.1.9"
resolved "https://registry.npmjs.org/sensitive-word-tool/-/sensitive-word-tool-1.1.9.tgz#e4dd5f077e4ca1bf3cb6ff14597071ac1e1f1e80"
integrity sha512-wcte3zwF6Fd/xoPtbqS6FgTZGe/hoIPJ8olDcvf1C2coix2va7kq7R5Ciy+mwvMjcROGyRvB6QGzjdfj1IZdzw==
serialize-error@^7.0.1:
version "7.0.1"
resolved "https://registry.npmmirror.com/serialize-error/-/serialize-error-7.0.1.tgz#f1360b0447f61ffb483ec4157c737fab7d778e18"