Browse Source

富文本编辑器支持AI操作,需要AI助手1.0.3版本

tags/6.5.2
tianya 6 days ago
parent
commit
a1e8ffcfd0
7 changed files with 290 additions and 7 deletions
  1. +231
    -0
      src/admin/ai_dialog.php
  2. +26
    -2
      src/admin/api.php
  3. +2
    -2
      src/admin/archives_do.php
  4. +2
    -2
      src/admin/inc/inc_archives_functions.php
  5. BIN
      src/static/ckeditor/plugins/dedebizai/icons/dedebizai.png
  6. +28
    -0
      src/static/ckeditor/plugins/dedebizai/plugin.js
  7. +1
    -1
      src/system/inc/inc_fun_funAdmin.php

+ 231
- 0
src/admin/ai_dialog.php View File

@@ -0,0 +1,231 @@
<?php
/**
* 富文本AI对话框
*
* @version $id:ai_dialog.php 2025 tianya $
* @package DedeBIZ.Dialog
* @copyright Copyright (c) 2022 DedeBIZ.COM
* @license GNU GPL v2 (https://www.dedebiz.com/license)
* @link https://www.dedebiz.com
*/
require_once(dirname(__FILE__) . "/config.php");

if (empty($f)) {
$f = 'form1.enclosure';
}
if (empty($comeback)) {
$comeback = '';
}
$addparm = '';
if (!empty($CKEditor)) {
$addparm = '&CKEditor=' . $CKEditor;
}
if (!empty($CKEditorFuncNum)) {
$addparm .= '&CKEditorFuncNum=' . $CKEditorFuncNum;
}
if (!empty($noeditor)) {
$addparm .= '&noeditor=yes';
}
?>
<!DOCTYPE html>
<html>

<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1,shrink-to-fit=no">
<title>AI提示词对话框</title>
<link rel="stylesheet" href="/static/web/css/font-awesome.min.css">
<link rel="stylesheet" href="/static/web/css/bootstrap.min.css">
<link rel="stylesheet" href="/static/web/css/admin.css">
<script src="/static/web/js/jquery.min.js"></script>
<script src="/static/web/js/bootstrap.min.js"></script>
<script src="/static/web/js/admin.main.js"></script>
</head>

<body class="p-3">
<div class="card shadow-sm">
<div class="card-header">小德AI助手:内容处理</div>
<div class="card-body">
<div class="form-group">
<div class="alert alert-warning mb-0" role="alert">
<span>处理过程中请勿关闭当前对话框,否则富文本处理将终止</span>
</div>
</div>
<div class="form-group">
<textarea id="prompt" class="form-control" style="height:160px" placeholder="请输入内容处理要求,例如:我需要将内容润色下,希望更专业"></textarea>
</div>
<div class="form-group">
<label for="modelid" class="form-label">选择模型</label>
<select id="modelid" class="form-control">
<?php
$dsql->SetQuery("SELECT AM.*,A.title as aititle FROM `#@__ai_model` AM LEFT JOIN `#@__ai` A ON A.id = AM.aiid ORDER BY AM.sortrank ASC,AM.id DESC");
$dsql->Execute();
while ($row = $dsql->GetObject()) {
?>
<option value="<?php echo $row->id; ?>" <?php echo $row->isdefault == 1 ? ' selected' : ''; ?>><?php echo $row->model; ?> <?php echo $row->aititle; ?></option>
<?php
}
?>
</select>
</div>
<div class="form-group">
<button type="button" id="btnAIAction" class="btn btn-success btn-sm">确定</button>
</div>
</div>
</div>
<script>
$("#btnAIAction").click(async function() {
let body = window.opener.CKEDITOR.instances["<?php echo $f ?>"].getData();
console.log(body);
let prompt = document.getElementById("prompt").value;
let modelid = document.getElementById("modelid").value;
let req = await fetch(`api.php?action=get_ai_server&pname=body_edit&modelid=${modelid}&prompt=${prompt}`);
let resp = await req.json();
if (resp.code !== 0) {
ShowMsg("获取服务器地址失败");
return
}

let req2 = await fetch(`api.php?action=get_setbody_url`);
let resp2 = await req2.json();
if (resp2.code !== 0) {
ShowMsg("获取服务器地址失败");
return
}
let req3 = await fetch(resp2.data, {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
body: new URLSearchParams({
body: body
})
});
let resp3 = await req3.json();
if (resp3.code !== 0) {
ShowMsg("提交原始内容失败");
return
}
let eventSource = new EventSource(resp.data);
//新增状态跟踪变量
let currentKey = null;
let tagBuffer = "";
let isClosingTag = false;
$("#mdlAI").modal('hide');
window.opener.CKEDITOR.instances["<?php echo $f ?>"].getCommand('openDedeBIZAi').disable();
$("#btnAIAction").attr("disabled", "disabled");
$("#prompt").attr("disabled", "disabled");
$("#modelid").attr("disabled", "disabled");
prompt = "";
let bodyHtml = "";
let lastChar = "";
eventSource.onmessage = (event) => {
const chars = event.data.split('');
chars.forEach(char => {
if (lastChar === '\\' && char === 'r') {
char = '<br>'; //替换为br标签
lastChar = ""; //清空追踪字符
} else {
lastChar = char; //记录当前字符
}
if (char === '\\') {
return; //如果是反斜杠,跳过处理
}
if (currentKey) {
if (char === '{') {
isClosingTag = true;
tagBuffer = '{';
return;
}
if (isClosingTag) {
tagBuffer += char;
if (tagBuffer === `{/${currentKey}}`) {
if (currentKey == "content") {
window.opener.CKEDITOR.instances["<?php echo $f ?>"].setReadOnly(false);
bodyHtml = "";
} else {
const input = document.querySelector(`[name="${currentKey}"]`);
if (input) $(input).prop("disabled", false).removeClass("disabled"); //恢复输入状态
}
currentKey = null;
isClosingTag = false;
tagBuffer = "";
return;
}
if (!`{/${currentKey}}`.startsWith(tagBuffer)) {
if (currentKey == "content") {
bodyHtml += tagBuffer;
console.log(bodyHtml);
window.opener.CKEDITOR.instances["<?php echo $f ?>"].setData(bodyHtml)
} else {
const input = document.querySelector(`[name="${currentKey}"]`);
if (input) input.value += tagBuffer;
}
isClosingTag = false;
tagBuffer = "";
}
} else {
if (currentKey == "content") {
bodyHtml += char;
window.opener.CKEDITOR.instances["<?php echo $f ?>"].setData(bodyHtml)
} else {
const input = document.querySelector(`[name="${currentKey}"]`);
if (input) {
input.value += char;
input.scrollTop = input.scrollHeight; //滚动到底部
}
}
}
} else {
if (char === '{') {
tagBuffer = '{';
} else if (tagBuffer.startsWith('{')) {
tagBuffer += char;
if (char === '}') {
const match = tagBuffer.match(/{([^>]+)}/);
if (match) {
currentKey = match[1];
if (currentKey == "content") {
window.opener.CKEDITOR.instances["<?php echo $f ?>"].setReadOnly(true);
} else {
const input = document.querySelector(`[name="${currentKey}"]`);
if (input) {
$(input).prop("disabled", true).addClass("disabled"); //仅禁用当前输入框
input.value = "";
}
}
}
tagBuffer = "";
}
}
}
});
};
eventSource.onerror = (error) => {
if (error.target.readyState === EventSource.CONNECTING) {
ShowMsg("连接失败,请确保您已开启并正确配置了DedeBIZ小德AI助手。 <a class='text-success' href='https://www.dedebiz.com/ai?from=dedebiz' target='_blank'>如何配置?</a>");
} else if (typeof error.data !== "undefined" && error.data !== "" && error.target.readyState !== EventSource.CLOSED) {
ShowMsg(error.data);
}
window.opener.CKEDITOR.instances["<?php echo $f ?>"].getCommand('openDedeBIZAi').enable();
$("#btnAI").prop("disabled", false);
$("#btnAIAction").prop("disabled", false);
$("#prompt").prop("disabled", false);
$("#modelid").prop("disabled", false);
eventSource.close();
};
//监听特定事件close
eventSource.addEventListener('close', (event) => {
console.log('SSE connection closed:', event.data);
window.opener.CKEDITOR.instances["<?php echo $f ?>"].getCommand('openDedeBIZAi').enable();
$("#btnAIAction").prop("disabled", false);
$("#prompt").prop("disabled", false);
$("#modelid").prop("disabled", false);
eventSource.close(); //关闭连接
window.opener.CKEDITOR.instances["<?php echo $f ?>"].setReadOnly(false);
});
});
</script>
</body>

</html>

+ 26
- 2
src/admin/api.php View File

@@ -16,7 +16,7 @@ require_once(DEDEINC.'/userlogin.class.php');
@set_time_limit(0);
AjaxHead();
helper('cache');
$action = isset($action) && in_array($action, array('is_need_check_code', 'has_new_version', 'get_changed_files', 'update_backup', 'get_update_versions', 'update', 'upload_image','get_ai_server')) ? $action : '';
$action = isset($action) && in_array($action, array('is_need_check_code', 'has_new_version', 'get_changed_files', 'update_backup', 'get_update_versions', 'update', 'upload_image', 'get_ai_server', 'get_setbody_url')) ? $action : '';
$curDir = dirname(GetCurUrl());//当前目录
/**
* 登录鉴权
@@ -74,7 +74,7 @@ if ($action === 'is_need_check_code') {
$unQueryVer[] = "6.2.0";
}
$row = $dsql->GetOne("SELECT COUNT(*) as dd FROM `#@__sysconfig` WHERE varname = 'cfg_bizcore_api'");
if ($row['dd'] == 0) {
if (isset($row['dd']) && $row['dd'] == 0) {
$unQueryVer[] = "6.2.3";
}
if (!$dsql->IsTable("#@__sys_payment")) {
@@ -83,6 +83,13 @@ if ($action === 'is_need_check_code') {
if (!TableHasField("#@__arctype", "apienabled")) {
$unQueryVer[] = "6.2.7";
}
if (!$dsql->IsTable("#@__ai")) {
$unQueryVer[] = "6.5.0";
}
$row = $dsql->GetOne("SELECT COUNT(*) as dd FROM `#@__ai_prompt` WHERE pname = 'body_edit'");
if (isset($row['dd']) && $row['dd'] == 0) {
$unQueryVer[] = "6.5.1";
}
if (count($unQueryVer) > 0) {
$upsqls = GetUpdateSQL();
foreach ($unQueryVer as $vv) {
@@ -459,5 +466,22 @@ if ($action === 'is_need_check_code') {
"code" => 0,
"data" => $url,
));
} else if($action === 'get_setbody_url') {
$params = $_GET;
unset($params['action']);
checkLogin();
$params['timestamp'] = time(); // 加入时间戳
$cuserLogin = new userLogin();
$params['adminid'] = $cuserLogin->getUserID(); // 加入时间戳
$params['ip'] = $_SERVER['REMOTE_ADDR'] ?? '127.0.0.1'; // 获取客户端IP

ksort($params); // 按字典序排序
$queryString = http_build_query($params); // 生成查询字符串
$params['sign'] = md5($queryString . $cfg_ai_apikey); // 计算MD5签名
$url = $cfg_ai_server . '/api/setbody?' . http_build_query($params);
echo json_encode(array(
"code" => 0,
"data" => $url,
));
}
?>

+ 2
- 2
src/admin/archives_do.php View File

@@ -658,7 +658,7 @@ else if ($dopost == "makekw") {
if (strlen($keywords.$k) >= 60) {
break;
} else {
if (strlen($k) <= 2) continue;
if (strlen($k) <= 2 || $length == 3) continue;
$keywords .= $k.',';
}
}
@@ -666,7 +666,7 @@ else if ($dopost == "makekw") {
if (strlen($keywords.$k) >= 60) {
break;
} else if (!in_array($k, $titleindexs)) {
if (strlen($k) <= 2) continue;
if (strlen($k) <= 2 || $length == 3) continue;
$keywords .= $k.',';
}
}


+ 2
- 2
src/admin/inc/inc_archives_functions.php View File

@@ -560,7 +560,7 @@ function AnalyseHtmlBody($body, &$description, &$litpic, &$keywords, $dtype = ''
if (strlen($keywords.$k) >= 60) {
break;
} else {
if (strlen($k) <= 2) continue;
if (strlen($k) <= 2 || $length == 3) continue;
$keywords .= $k.',';
}
}
@@ -568,7 +568,7 @@ function AnalyseHtmlBody($body, &$description, &$litpic, &$keywords, $dtype = ''
if (strlen($keywords.$k) >= 60) {
break;
} else if (!in_array($k, $titleindexs)) {
if (strlen($k) <= 2) continue;
if (strlen($k) <= 2 || $length == 3) continue;
$keywords .= $k.',';
}
}


BIN
src/static/ckeditor/plugins/dedebizai/icons/dedebizai.png View File

Before After
Width: 16  |  Height: 16  |  Size: 469B

+ 28
- 0
src/static/ckeditor/plugins/dedebizai/plugin.js View File

@@ -0,0 +1,28 @@
CKEDITOR.plugins.add("dedebizai", {
icons: "dedebizai",
init: function (a) {
a.addCommand("openDedeBIZAi",
{
exec: function (a) {
var w = 800;
var h = 600;
var dualScreenLeft = window.screenLeft !== undefined ? window.screenLeft : window.screenX;
var dualScreenTop = window.screenTop !== undefined ? window.screenTop : window.screenY;
var width = window.innerWidth ? window.innerWidth : document.documentElement.clientWidth ? document.documentElement.clientWidth : screen.width;
var height = window.innerHeight ? window.innerHeight : document.documentElement.clientHeight ? document.documentElement.clientHeight : screen.height;
var systemZoom = width / window.screen.availWidth;
var posLeft = (width - w) / 2 / systemZoom + dualScreenLeft;
var posTop = (height - h) / 2 / systemZoom + dualScreenTop;
window.open("./ai_dialog.php?f=" + a.name + "&noeditor=yes", "popUpImagesWin", "scrollbars=yes,resizable=yes,statebar=no,width=800,height=460,left=" + posLeft + ", top=" + posTop);
}
});
a.ui.addButton("DedeBIZAi",
{
label: "AI助手",
command: "openDedeBIZAi",
toolbar: "insert"
})
}
});

+ 1
- 1
src/system/inc/inc_fun_funAdmin.php View File

@@ -146,7 +146,7 @@ function SpGetEditor($fname, $fvalue, $nheight = "350", $etype = "Basic", $gtype
if ($GLOBALS['cfg_db_language'] == "utf8mb4") {
$emoji = ",emoji";
}
$addConfig = ",{allowedContent:true,pasteFilter:null,filebrowserImageUploadUrl:'./dialog/select_images_post.php',filebrowserUploadUrl:'./dialog/select_media_post.php?ck=1',extraPlugins:'html5video,html5audio,dedepagebreak,ddfilebrowser,mimage,textindent,tabletools,tableresize,tableselection,codesnippet{$emoji}',codeSnippet_theme: 'default'}";
$addConfig = ",{allowedContent:true,pasteFilter:null,filebrowserImageUploadUrl:'./dialog/select_images_post.php',filebrowserUploadUrl:'./dialog/select_media_post.php?ck=1',extraPlugins:'html5video,html5audio,dedepagebreak,ddfilebrowser,mimage,dedebizai,textindent,tabletools,tableresize,tableselection,codesnippet{$emoji}',codeSnippet_theme: 'default'}";
}
if (defined('DEDEUSER')) {
$addConfig = ",{filebrowserImageUploadUrl:'api.php?action=upload&type=litpic&ck=1',filebrowserUploadUrl:'api.php?action=upload&type=media&ck=1',extraPlugins:'html5video,html5audio,textindent',filebrowserImageBrowseDisabled:true}";


Loading…
Cancel
Save