+ revive xuan web chat.
This commit is contained in:
@@ -1,11 +1,5 @@
|
||||
<?php
|
||||
|
||||
use function zin\jsVar;
|
||||
|
||||
if(strpos($_SERVER['HTTP_USER_AGENT'], 'xuanxuan') != false): ?>
|
||||
<?php if(strpos($_SERVER['HTTP_USER_AGENT'], 'xuanxuan') != false): ?>
|
||||
<?php global $app; $app->setClientTheme('blue'); ?>
|
||||
<style>
|
||||
</style>
|
||||
<script>
|
||||
$('html').addClass('xxc-embed');
|
||||
|
||||
@@ -22,7 +16,6 @@ function updateAppMenu()
|
||||
});
|
||||
window.open('xxc://extension.zentao-integrated.updateAppMenu/' + encodeURIComponent(JSON.stringify(menuItems)));
|
||||
}
|
||||
|
||||
updateAppMenu();
|
||||
|
||||
$(document).on('showapp', function(e, app)
|
||||
@@ -34,4 +27,81 @@ $(document).on('hideapp', function(e, app)
|
||||
window.open('xxc://extension.zentao-integrated.hideAppMenuItem/' + encodeURIComponent(JSON.stringify({id: app.code, openedApps:Object.keys($.apps.openedApps)})));
|
||||
});
|
||||
</script>
|
||||
<?php else: ?>
|
||||
<?php
|
||||
global $app;
|
||||
$im = $app->control->loadModel('im');
|
||||
$xxdStatus = $im->getXxdStatus();
|
||||
if(isset($app->config->xuanxuan->turnon) && $app->config->xuanxuan->turnon && $xxdStatus == 'online')
|
||||
{
|
||||
$xuanConfig = new stdclass();
|
||||
$token = $im->userGetAuthToken($app->user->id, 'zentaoweb');
|
||||
$clientUrl = isset($app->config->webClientUrl) ? $app->config->webClientUrl : 'data/xuanxuan/web/index.html';
|
||||
$backendUrl = $im->getServer('zentao');
|
||||
|
||||
$xuanConfig->clientUrl = $clientUrl;
|
||||
$xuanConfig->server = ($app->config->xuanxuan->https == 'on' ? 'https' : 'http') . '://' . parse_url($backendUrl, PHP_URL_HOST) . ':' . $app->config->xuanxuan->commonPort;
|
||||
$xuanConfig->account = $app->user->account;
|
||||
$xuanConfig->authKey = $token->token;
|
||||
$xuanConfig->debug = $app->config->debug;
|
||||
}
|
||||
?>
|
||||
<?php if(isset($xuanConfig)): ?>
|
||||
<style>
|
||||
#xx-embed-container {z-index: 1010!important;}
|
||||
#xx-embed-container .xx-embed-has-animation {transition: min-width .5s ease-out, min-height .5s ease-out, transform, opacity!important;}
|
||||
#xx-embed-container .xx-embed {width: 330px; height: 100%; box-shadow: 0 4px 6px -1px rgba(0,0,0,0.1),0 2px 4px -1px rgba(0,0,0,0.06)!important;}
|
||||
#xx-embed-container .xx-embed.xx-embed-hidden,
|
||||
#xx-embed-container .xx-embed.xx-embed-collapsed {width: 330px!important; height: 100%!important; opacity: .7; pointer-events: none; transform: translateY(100%); display: block!important;}
|
||||
#xx-embed-container .xx-embed.has-chat-view {min-width: 1000px!important;}
|
||||
#xx-embed-container .xx-embed-body {min-height: initial!important;}
|
||||
</style>
|
||||
<?php js::import($app->getWebRoot() . 'data/xuanxuan/sdk/sdk.min.js'); ?>
|
||||
<script>
|
||||
/* Toggle xuan client popover */
|
||||
function toggleXuanClient()
|
||||
{
|
||||
if(!window.xuan.shown) window.xuan.show();
|
||||
else window.xuan.toggleCollapse();
|
||||
}
|
||||
|
||||
/* Handle chat notice change */
|
||||
function handleXuanNoticeChange(notice)
|
||||
{
|
||||
$('#chatNoticeBadge').toggleClass('show', !!notice.count).text(notice.count);
|
||||
}
|
||||
|
||||
/* Handle client route change */
|
||||
function handleXuanRouteChange(route)
|
||||
{
|
||||
var hasShowChatView = route.indexOf('#/chats/') === 0 && !!route.split('/')[3];
|
||||
$('#' + window.xuan.id).toggleClass('has-chat-view', hasShowChatView);
|
||||
}
|
||||
|
||||
$(function()
|
||||
{
|
||||
const xuanConfig = <?php echo json_encode($xuanConfig); ?>;
|
||||
if(typeof Xuanxuan !== 'undefined')
|
||||
{
|
||||
/* Set client global options*/
|
||||
Xuanxuan.setGlobalOptions(
|
||||
{
|
||||
position: 'right',
|
||||
width: 280,
|
||||
preload: true,
|
||||
showHeader: false,
|
||||
onNotice: handleXuanNoticeChange,
|
||||
onRouteChange: handleXuanRouteChange,
|
||||
lang: '<?php echo $app->getClientLang()?>'
|
||||
});
|
||||
|
||||
/* Create client instance */
|
||||
window.xuan = new Xuanxuan(xuanConfig);
|
||||
|
||||
/* Setup xuan web chat, see zin/wg/chatbtn. */
|
||||
window.setupXuan();
|
||||
}
|
||||
})
|
||||
</script>
|
||||
<?php endif; ?>
|
||||
<?php endif; ?>
|
||||
|
||||
+118
-18
@@ -7,39 +7,139 @@ class chatBtn extends wg
|
||||
public static function getPageCSS(): ?string
|
||||
{
|
||||
return <<<CSS
|
||||
#chat-container {position: fixed; left: 96px; right: 0; height: calc(100% - 40px); display: none;}
|
||||
#ai-chat-view {position: fixed; right: 0; width: 330px; bottom: 40px; top: 49px; outline: 1px solid; outline-color: rgba(var(--color-border-rgb),var(--tw-border-opacity))}
|
||||
#chat-container {position: fixed; left: 100px; right: 0; height: calc(100% - 36px); display: none;}
|
||||
.hide-menu #chat-container {left: 64px;}
|
||||
|
||||
#chat-switch {position: absolute; width: 330px; height: 52px; right: 0; background: #fff; display: flex; justify-content: center; align-items: center; border-bottom: 1px solid #eee; z-index: 20;}
|
||||
.chat-switch-bg {display: flex; justify-content: center; align-items: center; background-color: #eff5ff; border-radius: 16px;}
|
||||
.chat-switch-item {width: 96px; padding: 4px 0; border-radius: 16px; text-align: center; color: #838a9c; position: relative; user-select: none; cursor: pointer;}
|
||||
.chat-switch-item:hover {color: #838a9c;}
|
||||
.chat-switch-item.active {font-weight: bold; color: #fff; background-color: #5999fc;}
|
||||
.chat-switch-item.has-notice::after {content: ''; position: absolute; right: 26px; top: 4px; width: 6px; height: 6px; border-radius: 50%; background-color: #ff535d;}
|
||||
|
||||
#xuan-chat-view {position: absolute; width: 100%; height: 100%; z-index: 10; display: none;}
|
||||
#xuan-chat-view #xx-embed-container {position: absolute; bottom: 0; left: 0; right: 0; top: 0;}
|
||||
|
||||
#ai-chat-view {position: fixed; right: 0; width: 330px; bottom: 36px; top: 49px; outline: 1px solid; outline-color: rgba(var(--color-border-rgb),var(--tw-border-opacity))}
|
||||
#ai-chat-frame {height: 100%; width: 100%;}
|
||||
|
||||
.unconfigured {position: absolute; width: 330px; padding: 20px; right: 0; top: 0; bottom: 0; background: #fff; outline: 1px solid #eee;}
|
||||
.unconfigured > div {margin-bottom: 10px;}
|
||||
#xuan-chat-view .unconfigured {top: 50px;}
|
||||
|
||||
#reload-ai-chat.disabled {cursor: wait; color: #999!important;}
|
||||
CSS;
|
||||
}
|
||||
|
||||
public static function getPageJS(): ?string
|
||||
{
|
||||
$aiChatURL = createLink('ai', 'chat');
|
||||
global $app;
|
||||
|
||||
$isAIConfigured = $app->control->loadModel('ai')->hasModelsAvailable();
|
||||
$hasAIChatPriv = commonModel::hasPriv('ai', 'chat');
|
||||
|
||||
$xuanUnconfiguredTip = sprintf($app->lang->index->chat->unconfiguredFormat, $app->lang->index->chat->chat, (common::hasPriv('setting', 'xuanxuan') ? sprintf($app->lang->index->chat->goConfigureFormat, helper::createLink('setting', 'xuanxuan'), $app->lang->index->chat->chat) : $app->lang->index->chat->contactAdminForHelp));
|
||||
$aiUnconfiguredTip = sprintf($app->lang->index->chat->unconfiguredFormat, $app->lang->index->chat->ai, (common::hasPriv('ai', 'models') ? sprintf($app->lang->index->chat->goConfigureFormat, helper::createLink('ai', 'models') . '#app=admin', $app->lang->index->chat->ai) : $app->lang->index->chat->contactAdminForHelp));
|
||||
$aiUnauthorizedTip = $app->lang->index->chat->unauthorized;
|
||||
$aiChatURL = createLink('ai', 'chat');
|
||||
|
||||
$aiChatView = "<iframe id='ai-chat-frame' src='$aiChatURL' frameborder='no' allowtransparency='true' scrolling='auto' hidefocus></iframe>";
|
||||
if(!$hasAIChatPriv) $aiChatView = "<div class='unconfigured text-gray'>$aiUnauthorizedTip</div>";
|
||||
if(!$isAIConfigured) $aiChatView = "<div class='unconfigured text-gray'>$aiUnconfiguredTip</div>";
|
||||
|
||||
$chatContainer = <<<HTML
|
||||
<div id="chat-container">
|
||||
<div id="ai-chat-view">
|
||||
<iframe id="ai-chat-frame" src="{$aiChatURL}" frameborder="no" allowtransparency="true" scrolling="auto" hidefocus></iframe>
|
||||
<div id="chat-switch">
|
||||
<div class="chat-switch-bg">
|
||||
<a class="chat-switch-item" data-value="chat">{$app->lang->index->chat->chat}</a>
|
||||
<a class="chat-switch-item active" data-value="ai">{$app->lang->index->chat->ai}</a>
|
||||
</div>
|
||||
</div>
|
||||
<div id="xuan-chat-view">
|
||||
<div class="unconfigured text-gray">$xuanUnconfiguredTip</div>
|
||||
</div>
|
||||
<div id="ai-chat-view">$aiChatView</div>
|
||||
</div>
|
||||
HTML;
|
||||
|
||||
$xuanDefaultOpener = ($hasAIChatPriv && $isAIConfigured) ? '' : 'if(window.xuan) $(`#chat-switch .chat-switch-item[data-value="chat"]`).trigger("click");';
|
||||
|
||||
return <<<JAVASCRIPT
|
||||
window.toggleChatContainer = () => {
|
||||
if(!$('#chat-container').length)
|
||||
{
|
||||
$('body').append(`{$chatContainer}`);
|
||||
$('#ai-chat-frame').on('load', () => {window.toggleChatContainer();});
|
||||
return;
|
||||
}
|
||||
if(window.xuan) window.xuan[window.xuan.shown ? 'expand' : 'show']();
|
||||
$('#chat-btn').toggleClass('active');
|
||||
$('#chat-container').toggle();
|
||||
}
|
||||
$(document).on('click', e =>
|
||||
{
|
||||
if(!$('#chat-container').length || $('#chat-container').prop('style') && $('#chat-container').prop('style').display !== 'block') return;
|
||||
if($(e.target).closest('#ai-chat-view,#chat-btn').length) return;
|
||||
window.toggleChatContainer();
|
||||
});
|
||||
};
|
||||
|
||||
/* Setup xuan web chat. */
|
||||
window.setupXuan = () => {
|
||||
/* Move xuan web client into #xuan-chat-view. */
|
||||
if(window.xuan && !document.querySelector('#xuan-chat-view #xx-embed-container'))
|
||||
{
|
||||
document.querySelector('#xuan-chat-view').prepend(document.querySelector('#xx-embed-container'));
|
||||
|
||||
/* Switch to xuan chat by default if AI is not available. */
|
||||
$xuanDefaultOpener
|
||||
|
||||
/* Update badge on chat. */
|
||||
window.xuan._options.onNotice = function(notice)
|
||||
{
|
||||
window.handleXuanNoticeChange(notice);
|
||||
$('.chat-switch-item[data-value="chat"]').toggleClass('has-notice', !!notice.count);
|
||||
};
|
||||
|
||||
/* Set style into xuan frame. */
|
||||
let tries = 0;
|
||||
const xuanFrameWaitLoop = setInterval(function()
|
||||
{
|
||||
if(document.querySelector('#xx-embed-container iframe'))
|
||||
{
|
||||
clearInterval(xuanFrameWaitLoop);
|
||||
tries = 0;
|
||||
const chatsViewWaitLoop = setInterval(function()
|
||||
{
|
||||
if(document.querySelector('#xx-embed-container iframe').contentDocument.querySelector('.app-chats-menu'))
|
||||
{
|
||||
clearInterval(chatsViewWaitLoop);
|
||||
document.querySelector('#xx-embed-container iframe').contentDocument.querySelector('.app-chats-menu').style.cssText = 'width: 330px !important; top: 48px;';
|
||||
document.querySelector('#xx-embed-container iframe').contentDocument.querySelector('.app-chats-cache').style.cssText = 'right: 330px !important;';
|
||||
document.querySelector('#xx-embed-container iframe').contentDocument.querySelector('.app-chats-menu-search').style.cssText = 'padding: 10px 8px !important;';
|
||||
document.querySelector('#xx-embed-container iframe').contentDocument.querySelector('.app-chats-menu-search .btn:first-child').style.cssText = 'display: none;';
|
||||
}
|
||||
else
|
||||
{
|
||||
if(++tries > 20) clearInterval(chatsViewWaitLoop);
|
||||
}
|
||||
}, 200);
|
||||
}
|
||||
else
|
||||
{
|
||||
if(++tries > 20) clearInterval(xuanFrameWaitLoop);
|
||||
}
|
||||
}, 200);
|
||||
}
|
||||
};
|
||||
(() => {
|
||||
/* Insert chat views. */
|
||||
$('body').append(`{$chatContainer}`);
|
||||
|
||||
/* Handle switch events. */
|
||||
$('#chat-switch .chat-switch-item').on('click', e =>
|
||||
{
|
||||
const target = $(e.target);
|
||||
target.addClass('active').siblings().removeClass('active');
|
||||
$('#ai-chat-view').toggle(target.data('value') === 'ai');
|
||||
$('#xuan-chat-view').toggle(target.data('value') === 'chat');
|
||||
});
|
||||
|
||||
/* Handle clicking outside. */
|
||||
$(document).on('click', e =>
|
||||
{
|
||||
if(!$('#chat-container').length || $('#chat-container').prop('style') && $('#chat-container').prop('style').display !== 'block') return;
|
||||
if($(e.target).closest('#chat-switch,#xuan-chat-view>.unconfigured,#ai-chat-view,#chat-btn').length) return;
|
||||
window.toggleChatContainer();
|
||||
});
|
||||
})();
|
||||
JAVASCRIPT;
|
||||
}
|
||||
|
||||
|
||||
@@ -17,8 +17,6 @@ else
|
||||
$scoreNotice = '';
|
||||
if($config->vision != 'lite') $scoreNotice = $this->loadModel('score')->getNotice();
|
||||
|
||||
$shouldShowAIChat = hasPriv('ai', 'chat') && $this->loadModel('ai')->hasModelsAvailable();
|
||||
|
||||
jsVar('scoreNotice', $scoreNotice);
|
||||
jsVar('edition', $config->edition);
|
||||
jsVar('vision', $config->vision);
|
||||
@@ -101,7 +99,7 @@ div
|
||||
setID('appsToolbar'),
|
||||
setClass('space-x-1'),
|
||||
hasPriv('search', 'index') ? globalSearch() : null,
|
||||
$shouldShowAIChat ? chatBtn() : null,
|
||||
chatBtn(),
|
||||
item
|
||||
(
|
||||
setClass('ghost btn-zentao px-1'),
|
||||
|
||||
Reference in New Issue
Block a user