| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698 |
- <template>
- <div class="conversation-panel">
- <!-- 配置加载中 -->
- <div v-if="configLoading" class="loading-tip">
- <i class="el-icon-loading"></i>
- <p>正在加载配置...</p>
- </div>
- <!-- 配置加载失败 / 无配置 -->
- <div v-else-if="configError" class="empty-tip">
- <i class="el-icon-warning"></i>
- <p>{{ configErrorMsg || '企微未配置,请先配置企微应用' }}</p>
- </div>
- <!-- 未登录 -->
- <div v-else-if="!isLoggedIn" class="login-area">
- <div class="login-tip">请扫码登录企微</div>
- <div id="login-container" ref="loginContainer" class="login-container"></div>
- </div>
- <!-- 已登录但未选择员工(群聊不需要员工) -->
- <div v-else-if="!staffUserId && !chatId" class="empty-tip">
- <i class="el-icon-info"></i>
- <p>请选择员工</p>
- </div>
- <!-- 已登录但未选择客户(群聊不需要客户) -->
- <div v-else-if="!customerId && !chatId" class="empty-tip">
- <i class="el-icon-info"></i>
- <p>请选择客户查看会话</p>
- </div>
- <!-- 会话加载中 -->
- <div v-else-if="!isReady" class="loading-tip">
- <i class="el-icon-loading"></i>
- <p>正在加载会话记录...</p>
- </div>
- <!-- 会话为空 -->
- <div v-else-if="msgList.length === 0" class="empty-tip">
- <i class="el-icon-info"></i>
- <p>暂无会话数据</p>
- </div>
- <!-- 聊天窗口 -->
- <div v-else id="chat-container" ref="chatContainer"></div>
- </div>
- </template>
- <script>
- import * as ww from '@wecom/jssdk';
- import { qwLogin, qwSignature, qwConversations, getQwSessionConfig } from '@/api/qw/companySession';
- // ========== 全局配置缓存 ==========
- window._QW_CONFIG_CACHE = window._QW_CONFIG_CACHE || new Map();
- window._QW_CONFIG_PENDING = window._QW_CONFIG_PENDING || new Map();
- const CACHE_TTL = 5 * 60 * 1000;
- function getCachedConfig(corpId) {
- if (!corpId) return null;
- const cached = window._QW_CONFIG_CACHE.get(corpId);
- if (cached && (Date.now() - cached.timestamp) < CACHE_TTL) {
- return cached.config;
- }
- window._QW_CONFIG_CACHE.delete(corpId);
- return null;
- }
- function setCachedConfig(corpId, config) {
- if (!corpId) return;
- window._QW_CONFIG_CACHE.set(corpId, { config, timestamp: Date.now() });
- }
- export default {
- name: 'ConversationPanelPure',
- props: {
- corpId: { type: String, default: null },
- customerId: { type: String, default: null },
- staffUserId: { type: String, default: null },
- chatId: { type: String, default: null }
- },
- data() {
- return {
- isLoggedIn: false,
- isReady: false,
- msgList: [],
- cursor: '',
- hasMore: true,
- loadingMore: false,
- chatInstance: null,
- _sdkInited: false,
- config: { corpid: '', agentid: '', domain: '' },
- configReady: false,
- configError: false,
- configLoading: false,
- configErrorMsg: '',
- tokenCheckTimer: null,
- _hasEmittedLogout: false,
- _loginPanelCreated: false,
- _loginPanelTimer: null
- };
- },
- watch: {
- customerId(newId, oldId) {
- if (newId && newId !== oldId && this.isLoggedIn && (this.staffUserId || this.chatId) && this.configReady && !this.configError) {
- this.resetAndReload();
- }
- },
- staffUserId(newVal, oldVal) {
- if (newVal && newVal !== oldVal && this.customerId && this.isLoggedIn && this.configReady && !this.configError) {
- this.resetAndReload();
- }
- },
- chatId(newVal, oldVal) {
- if (newVal && newVal !== oldVal && this.isLoggedIn && this.configReady && !this.configError) {
- this.resetAndReload();
- }
- },
- corpId(newId, oldId) {
- if (newId !== oldId) {
- this.destroyChat();
- this.isReady = false;
- this._sdkInited = false;
- this._hasEmittedLogout = false;
- this._loginPanelCreated = false;
- if (this._loginPanelTimer) clearTimeout(this._loginPanelTimer);
- this.msgList = [];
- this.cursor = '';
- this.hasMore = true;
- this.loadingMore = false;
- this.isLoggedIn = false;
- const cached = getCachedConfig(newId);
- if (cached) {
- this.config = cached;
- this.configReady = true;
- this.configError = false;
- this.configLoading = false;
- this.handleAuthAndLoad();
- } else {
- this.configReady = false;
- this.configError = false;
- this.configLoading = false;
- this.initConfig().then(() => {
- if (this.configReady && !this.configError) this.handleAuthAndLoad();
- });
- }
- }
- },
- isLoggedIn: {
- handler(newVal) {
- if (!newVal && this.configReady && !this.configError && this._sdkInited && !this._loginPanelCreated) {
- this.tryCreateLoginPanel();
- }
- },
- immediate: true
- }
- },
- async mounted() {
- this._hasEmittedLogout = false;
- this._loginPanelCreated = false;
- const cached = getCachedConfig(this.corpId);
- if (cached) {
- this.config = cached;
- this.configReady = true;
- this.configError = false;
- this.configLoading = false;
- await this.handleAuthAndLoad();
- } else {
- await this.initConfig();
- if (this.configReady && !this.configError) await this.handleAuthAndLoad();
- }
- this.tokenCheckTimer = setInterval(() => {
- if (this.isLoggedIn && this.configReady && !this.configError && this._sdkInited) {
- this.getAgentConfigSignature().catch(e => console.warn('签名刷新失败', e));
- }
- }, 90 * 60 * 1000);
- },
- beforeDestroy() {
- if (this.tokenCheckTimer) clearInterval(this.tokenCheckTimer);
- if (this._loginPanelTimer) clearTimeout(this._loginPanelTimer);
- this.destroyChat();
- },
- methods: {
- // ========== Storage Key ==========
- getStorageKey(corpId, suffix = 'expire') {
- return `wecom_session_${corpId}_${suffix}`;
- },
- checkLoginState(corpId) {
- const expire = localStorage.getItem(this.getStorageKey(corpId, 'expire'));
- return expire && Date.now() < parseInt(expire, 10);
- },
- storeLoginState(corpId) {
- localStorage.setItem(this.getStorageKey(corpId, 'expire'), String(Date.now() + 115 * 60 * 1000));
- },
- clearLoginState(corpId) {
- localStorage.removeItem(this.getStorageKey(corpId, 'expire'));
- },
- // ========== 配置加载 ==========
- async initConfig() {
- if (!this.corpId || this.configReady || this.configError) return;
- const cached = getCachedConfig(this.corpId);
- if (cached) {
- this.config = cached;
- this.configReady = true;
- this.configError = false;
- this.configLoading = false;
- return;
- }
- if (window._QW_CONFIG_PENDING.has(this.corpId)) {
- return window._QW_CONFIG_PENDING.get(this.corpId);
- }
- this.configLoading = true;
- const promise = (async () => {
- try {
- const res = await getQwSessionConfig(this.corpId);
- let configData = null;
- if (res?.code === 200 && res.data?.corpid) configData = res.data;
- else if (res?.data?.corpid) configData = res.data;
- else if (res?.corpid) configData = res;
- if (!configData?.corpid || !configData?.agentid) throw new Error('配置数据不完整');
- const cfg = {
- corpid: configData.corpid,
- agentid: String(configData.agentid),
- domain: configData.domain || ''
- };
- this.config = cfg;
- this.configReady = true;
- this.configError = false;
- setCachedConfig(this.corpId, cfg);
- } catch (e) {
- alert("请更换企微主体!!!")
- console.error('[ConversationPanel] 获取配置失败:', e);
- this.configReady = false;
- this.configError = true;
- this.configErrorMsg = e.message || '获取配置失败';
- } finally {
- this.configLoading = false;
- window._QW_CONFIG_PENDING.delete(this.corpId);
- }
- })();
- window._QW_CONFIG_PENDING.set(this.corpId, promise);
- return promise;
- },
- // ========== 认证与加载 ==========
- async handleAuthAndLoad() {
- if (this.configError) return;
- if (!this.configReady) {
- await this.initConfig();
- if (this.configError) return;
- }
- const sdkOk = await this.initSDKOnce();
- if (!sdkOk) return;
- const urlParams = new URLSearchParams(window.location.search);
- const code = urlParams.get('code');
- if (code) {
- window.history.replaceState({}, '', window.location.origin + window.location.pathname);
- try {
- await this.handleLogin(code);
- this.storeLoginState(this.corpId);
- this.isLoggedIn = true;
- if ((this.customerId && this.staffUserId) || this.chatId) await this.loadFirstPage();
- } catch (err) {
- this.clearLoginState(this.corpId);
- this.isLoggedIn = false;
- this.safeEmitLogout();
- }
- } else {
- if (this.checkLoginState(this.corpId)) {
- this.isLoggedIn = true;
- if ((this.customerId && this.staffUserId) || this.chatId) await this.loadFirstPage();
- } else {
- this.isLoggedIn = false;
- this.safeEmitLogout();
- this.tryCreateLoginPanel();
- }
- }
- },
- tryCreateLoginPanel() {
- if (this._loginPanelCreated || !this.configReady || this.configError || !this._sdkInited || this.isLoggedIn) return;
- if (this._loginPanelTimer) clearTimeout(this._loginPanelTimer);
- this.$nextTick(() => this.createLoginPanel());
- },
- createLoginPanel() {
- if (this._loginPanelCreated || !this.configReady || this.configError || !this._sdkInited || this.isLoggedIn) return;
- let container = this.$refs.loginContainer || document.getElementById('login-container');
- if (!container) {
- this._loginPanelTimer = setTimeout(() => this.createLoginPanel(), 1000);
- return;
- }
- if (container.getBoundingClientRect().width === 0 || container.getBoundingClientRect().height === 0) {
- this._loginPanelTimer = setTimeout(() => this.createLoginPanel(), 500);
- return;
- }
- container.innerHTML = '';
- this._loginPanelCreated = true;
- const redirectUri = window.location.origin + window.location.pathname;
- try {
- ww.createWWLoginPanel({
- el: container,
- params: {
- login_type: 'CorpApp',
- appid: this.config.corpid,
- agentid: this.config.agentid,
- redirect_uri: redirectUri,
- redirect_type: 'callback',
- state: 'state_' + Date.now()
- },
- onLoginSuccess: async ({ code }) => {
- try {
- await this.handleLogin(code);
- if ((this.customerId && this.staffUserId) || this.chatId) await this.loadFirstPage();
- } catch (e) {
- this.clearLoginState(this.corpId);
- this.isLoggedIn = false;
- this._loginPanelCreated = false;
- this.safeEmitLogout();
- }
- },
- onLoginError: () => {
- this._loginPanelCreated = false;
- this._loginPanelTimer = setTimeout(() => this.createLoginPanel(), 3000);
- }
- });
- } catch (e) {
- this._loginPanelCreated = false;
- this._loginPanelTimer = setTimeout(() => this.createLoginPanel(), 3000);
- }
- },
- safeEmitLogout() {
- if (this._hasEmittedLogout) return;
- this._hasEmittedLogout = true;
- this.$emit('logout');
- },
- async handleLogin(code) {
- const res = await qwLogin({ code, corpid: this.corpId });
- const data = this._extractResponse(res);
- if (data.errcode !== 0) throw new Error(data.errmsg || '登录失败');
- this.storeLoginState(this.corpId);
- this.isLoggedIn = true;
- this.$emit('login-success');
- },
- async getAgentConfigSignature() {
- if (!this.configReady || this.configError) throw new Error('配置未就绪');
- const currentUrl = window.location.href.split('#')[0];
- const res = await qwSignature({ url: currentUrl, corpid: this.corpId });
- const data = this._extractResponse(res);
- if (data.errcode && data.errcode !== 0) {
- this.clearLoginState(this.corpId);
- this.isLoggedIn = false;
- this.isReady = false;
- this.destroyChat();
- this._loginPanelCreated = false;
- if (!this.configError) this.safeEmitLogout();
- throw new Error(`签名失败: ${data.errmsg}`);
- }
- return { timestamp: data.timestamp, nonceStr: data.nonceStr, signature: data.signature };
- },
- async initSDKOnce() {
- if (this._sdkInited) return true;
- if (!this.configReady || this.configError) return false;
- try {
- await ww.register({
- corpId: this.config.corpid,
- agentId: this.config.agentid,
- jsApiList: ['selectExternalContact', 'shareAppMessage', 'wwapp.invokeJsApiByCallInfo'],
- getAgentConfigSignature: () => this.getAgentConfigSignature()
- });
- await ww.initOpenData();
- this._sdkInited = true;
- return true;
- } catch (e) {
- console.error('SDK初始化失败', e);
- return false;
- }
- },
- async loadFirstPage() {
- if (this.configError) return;
- try {
- const res = await qwConversations({
- customerId: this.customerId,
- staffUserId: this.staffUserId,
- limit: 100,
- cursor: this.cursor,
- corpid: this.corpId,
- chatId: this.chatId || undefined
- });
- const data = this._extractResponse(res);
- if (data.errcode !== 0) throw new Error(data.errmsg || '拉取失败');
- const rawList = data.data || [];
- this.msgList = this.processMessages(rawList, false);
- this.cursor = data.next_cursor || '';
- this.hasMore = data.has_more === 1;
- this.isReady = true;
- await this.$nextTick();
- if (this.msgList.length) await this.renderOrUpdateChat();
- } catch (e) {
- console.error('加载第一页失败', e);
- this.$message.error('加载会话失败:' + e.message);
- this.isReady = true;
- this.msgList = [];
- }
- },
- async loadMore() {
- if (!this.cursor || this.loadingMore || !this.hasMore || this.configError) return;
- this.loadingMore = true;
- try {
- const res = await qwConversations({
- customerId: this.customerId,
- staffUserId: this.staffUserId,
- limit: 50,
- cursor: this.cursor,
- corpid: this.corpId,
- chatId: this.chatId || undefined
- });
- const data = this._extractResponse(res);
- if (data.errcode !== 0) throw new Error(data.errmsg || '拉取更多失败');
- const newMessages = data.data || [];
- if (newMessages.length) {
- const processed = this.processMessages(newMessages, true);
- this.msgList.push(...processed);
- this.cursor = data.next_cursor || '';
- this.hasMore = data.has_more === 1;
- if (this.chatInstance) this.chatInstance.setData({ msgList: this.msgList });
- else await this.renderOrUpdateChat();
- } else {
- this.hasMore = false;
- }
- } catch (e) {
- console.error('加载更多失败', e);
- } finally {
- this.loadingMore = false;
- }
- },
- processMessages(messages, isAppend = false) {
- const prevLastMsg = isAppend && this.msgList.length > 0
- ? this.msgList[this.msgList.length - 1]
- : null
- let prevDate = prevLastMsg ? (prevLastMsg.dateStr || '') : ''
- return messages.map(msg => {
- const timeStr = msg.send_time_str || ''
- const parts = timeStr.split(' ')
- const dateStr = parts[0] || ''
- const displayTime = parts[1] ? parts[1].substring(0, 5) : ''
- const showDate = !!dateStr && dateStr !== prevDate
- prevDate = dateStr
- return {
- ...msg,
- dateStr,
- displayTime,
- showDate
- }
- })
- },
- async renderOrUpdateChat() {
- if (this.msgList.length === 0 || this.configError) return;
- const container = document.getElementById('chat-container');
- if (!container) return;
- if (this.chatInstance) {
- this.chatInstance.setData({ msgList: this.msgList });
- return;
- }
- const ok = await this.initSDKOnce();
- if (!ok) return;
- const factory = ww.createOpenDataFrameFactory();
- if (!factory) return;
- this.chatInstance = factory.createOpenDataFrame({
- el: container,
- template: `
- <scroll-view scroll-y="{{true}}" bindscrolltolower="onScrollToLower" style="height: 100%;">
- <view wx:for="{{data.msgList}}" wx:for-item="msg" wx:key="msgid" style="margin-bottom: 15px;">
- <!-- 日期分隔线:仅跨天时显示 -->
- <view wx:if="{{msg.showDate}}" style="text-align: center; margin-bottom: 12px;">
- <span style="background:#e9e9e9; color:#888; padding:3px 12px; border-radius:12px; font-size:11px;">{{ msg.dateStr }}</span>
- </view>
- <!-- 客户/机器人消息(左对齐) -->
- <view wx:if="{{msg.sender.type == 2 || msg.sender.type == 3}}" style="display:flex; flex-direction:row; align-items:flex-start;">
- <ww-open-data wx:if="{{msg.sender.type == 2}}" type="externalUserAvatar" openid="{{msg.sender.id}}" class="msg-avatar-left" />
- <ww-open-data wx:if="{{msg.sender.type == 3}}" type="userAvatar" openid="{{msg.sender.id}}" class="msg-avatar-left" />
- <view style="display:flex; flex-direction:column; max-width:70%; margin-left:10px;">
- <view style="display:flex; align-items:center; margin-bottom:4px;">
- <ww-open-data wx:if="{{msg.sender.type == 2}}" type="externalUserName" openid="{{msg.sender.id}}" class="sender-name" />
- <ww-open-data wx:if="{{msg.sender.type == 3}}" type="userName" openid="{{msg.sender.id}}" class="sender-name" />
- <span style="font-size:11px;color:#999;margin-left:8px;">{{ msg.displayTime }}</span>
- </view>
- <view class="msg-bubble msg-bubble-left">
- <ww-open-message message-id="{{msg.msgid}}" secret-key="{{msg.secretKey}}" open-type="viewMessage"/>
- </view>
- </view>
- </view>
- <!-- 员工消息(右对齐) -->
- <view wx:if="{{msg.sender.type == 1}}" style="display:flex; flex-direction:row; justify-content:flex-end; align-items:flex-start;">
- <view style="display:flex; flex-direction:column; align-items:flex-end; max-width:70%; margin-right:10px;">
- <view style="display:flex; align-items:center; margin-bottom:4px;">
- <span style="font-size:11px;color:#999;margin-right:8px;">{{ msg.displayTime }}</span>
- <ww-open-data type="userName" openid="{{msg.sender.id}}" class="sender-name" />
- </view>
- <view class="msg-bubble msg-bubble-right">
- <ww-open-message message-id="{{msg.msgid}}" secret-key="{{msg.secretKey}}" open-type="viewMessage"/>
- </view>
- </view>
- <ww-open-data type="userAvatar" openid="{{msg.sender.id}}" class="msg-avatar-right" />
- </view>
- <!-- 未知类型消息(居中,无头像) -->
- <view wx:if="{{msg.sender.type != 1 && msg.sender.type != 2 && msg.sender.type != 3}}" style="display:flex; justify-content:center;">
- <view class="msg-bubble msg-bubble-left">
- <ww-open-message message-id="{{msg.msgid}}" secret-key="{{msg.secretKey}}" open-type="viewMessage"/>
- </view>
- </view>
- </view>
- <view wx:if="{{data.hasMore && data.loadingMore}}" style="text-align:center; padding:10px; color:#999;">加载更多...</view>
- </scroll-view>
- `,
- style: `
- .msg-avatar-left {
- width: 36px;
- height: 36px;
- border-radius: 4px;
- flex-shrink: 0;
- }
- .msg-avatar-right {
- width: 36px;
- height: 36px;
- border-radius: 4px;
- flex-shrink: 0;
- }
- .sender-name {
- font-size: 12px;
- color: #666;
- max-width: 120px;
- overflow: hidden;
- text-overflow: ellipsis;
- white-space: nowrap;
- }
- .msg-bubble {
- padding: 10px 14px;
- border-radius: 8px;
- word-break: break-all;
- display: flex;
- align-items: center;
- }
- .msg-bubble-left {
- background: #ffffff;
- box-shadow: 0 1px 3px rgba(0,0,0,0.1);
- }
- .msg-bubble-right {
- background: #95ec69;
- box-shadow: 0 1px 3px rgba(0,0,0,0.1);
- }
- `,
- data: {
- msgList: this.msgList,
- hasMore: this.hasMore,
- loadingMore: this.loadingMore
- },
- methods: {
- onScrollToLower: () => {
- if (this.hasMore && !this.loadingMore && !this.configError) this.loadMore();
- }
- },
- error: (e) => {
- console.error('[企微组件] 错误', e);
- if (e && [42006, 42003, 40029].includes(e.errCode)) {
- if (!this.configError) {
- this.clearLoginState(this.corpId);
- this.isLoggedIn = false;
- this.isReady = false;
- this.destroyChat();
- this._loginPanelCreated = false;
- this.safeEmitLogout();
- }
- }
- },
- handleModal: ({ modalUrl, modalSize }) => {
- const mask = document.createElement('div');
- mask.style.cssText = 'position:fixed;top:0;left:0;width:100%;height:100%;background:rgba(0,0,0,0.5);z-index:9999;display:flex;align-items:center;justify-content:center;';
- const content = document.createElement('div');
- content.style.cssText = `position:relative;max-width:90vw;max-height:90vh;width:${modalSize?.width || 80}%;height:${modalSize?.height || 80}%;background:#fff;border-radius:8px;overflow:hidden;`;
- const closeBtn = document.createElement('button');
- closeBtn.innerText = '✕';
- closeBtn.style.cssText = 'position:absolute;top:10px;right:10px;z-index:10001;width:32px;height:32px;border-radius:50%;border:none;background:rgba(0,0,0,0.5);color:#fff;font-size:20px;cursor:pointer;display:flex;align-items:center;justify-content:center;';
- const iframe = document.createElement('iframe');
- iframe.src = modalUrl;
- iframe.style.cssText = 'width:100%;height:100%;border:none;';
- const cleanup = () => {
- mask.remove();
- document.removeEventListener('keydown', escHandler);
- };
- closeBtn.onclick = (e) => { e.stopPropagation(); cleanup(); };
- mask.onclick = (e) => { if (e.target === mask) cleanup(); };
- const escHandler = (e) => { if (e.key === 'Escape') cleanup(); };
- document.addEventListener('keydown', escHandler);
- content.appendChild(iframe);
- content.appendChild(closeBtn);
- mask.appendChild(content);
- document.body.appendChild(mask);
- return true;
- }
- });
- },
- async resetAndReload() {
- this.destroyChat();
- this.msgList = [];
- this.cursor = '';
- this.hasMore = true;
- this.loadingMore = false;
- this.isReady = false;
- await this.loadFirstPage();
- },
- destroyChat() {
- if (this.chatInstance) this.chatInstance = null;
- const container = document.getElementById('chat-container');
- if (container) container.innerHTML = '';
- },
- _extractResponse(res) {
- if (!res) return {};
- if (res.code !== undefined) return res;
- if (res.errcode !== undefined || res.timestamp || res.corpid || res.data) return res;
- if (res.data?.data) return res.data.data;
- return res;
- }
- }
- };
- </script>
- <style scoped>
- .conversation-panel {
- width: 100%;
- height: 100%;
- display: flex;
- flex-direction: column;
- background: #f5f6f7;
- position: relative;
- }
- .login-area, .empty-tip, .loading-tip {
- display: flex;
- flex-direction: column;
- align-items: center;
- justify-content: center;
- height: 100%;
- flex: 1;
- }
- .login-tip {
- font-size: 18px;
- color: #666;
- margin-bottom: 24px;
- }
- .empty-tip, .loading-tip {
- color: #999;
- font-size: 16px;
- }
- .empty-tip i, .loading-tip i {
- font-size: 48px;
- margin-bottom: 16px;
- }
- #chat-container {
- flex: 1;
- width: 100%;
- height: 100%;
- background: #f0f2f5;
- }
- .login-container {
- width: 320px;
- height: 380px;
- display: flex;
- align-items: center;
- justify-content: center;
- }
- </style>
- <style>
- #chat-container,
- #chat-container > div,
- #chat-container iframe {
- width: 100% !important;
- height: 100% !important;
- min-width: 100% !important;
- min-height: 100% !important;
- overflow: hidden;
- }
- </style>
|