1. 使用说明

欢迎使用“AICC”的在线客服渠道接入服务。我们提供脚本嵌入和H5链接的方式,您可以在您的官网、网店、APP等渠道快速集成在线客服的能力。

2. 网页渠道

2.1. 接入方式

2.1.1. web纯链接方式

接入方式说明

web纯链接方式,此方式是一个可以直接咨询客服的URL,您可以将此URL放置到需要接入客服的地方。
web纯链接方式的接入成本最低,只需要企业自身前端将链接加入需要跳转的地方即可。

接入步骤

链接获取方式:使用管理员账号登录-系统设置-在线客服-渠道接入-网页,寻找到列表中的一个接入配置点击编辑(如果您的列表中没有可用的接入配置,请联系售后支持帮您进行配置)。详细路径见下图: image

进入编辑页面后点击 接入设置-复制,即可得到链接。详细路径见下图: image

参数说明

web纯链接方式支持以get传参的方式传递一些参数。
(注意:参数在传递时必须遵循get参数传递规范,必要的数据请进行URL编码,不然无法保障可用性。)

名称 是否必须 类型 说明

accessId

string

接入号ID,复制链接之后URL会自带此参数,请勿随意修改
示例: "d51d8423-**-**-8ced-6bf09b814040"

cno

string

接待座席号(一旦拼接cno参数,会话直接接入到对应座席,若对应座席无法接待,则按正常导航执行)
示例: "0001"

visitorId

string

访客ID,缺省为自动生成的UUID
示例: "0001"

visitorName

string

访客姓名,缺省为省份+城市+访客id后6位
示例: "访客"

visitorTag

string

访客标签,在座席端最多可以显示三个文字

tel

string

客户电话,用于匹配客户资料

province

string

访客会话时所在省份,缺省从IP解析

city

string

访客会话时所在城市,缺省从IP解析

replyWelcome

string

访客在欢迎语之后自动发送的文本

headerDisplay

string

是否展示头部导航栏(为0时不展示其余均展示),需要注意的是,隐藏头部导航栏之后,则原导航栏中的功能都不再展示

customerFields

string

客户资料信息,必须为JSON String数据格式。如果参数中有autoUpdate=1的传值,则在打开自动创建客户的前提下会根据该字段传参更新客户资料

visitorExtraInfo

string

访客自定义参数,必须为JSON String数据格式,可由访客接入会话时提供自定义参数,该值还包含询前表单内容。该值默认包含访客ID与访客名称。

linkCard

string

商品卡片信息,必须为JSON String数据格式,将在2.2章节进行详细介绍

language

string

语言 中文:zh_CN,英文:en_US,繁体:zh_TW,阿拉伯语:ar,缺省为 Accept-Language 值

initialQuickEntryName

string

是否在访客进线接入会话时自动弹起快捷入口所配置的订单卡片,值为配置的需要弹起的快捷入口的名称。

externalId

string

客户外部唯一标识,通常由企业自定义,在多个系统中同步或关联数据时通过该 id 识别为同一用户(如果同时传递了externalId、visitorId、tel,则按照externalId>visitorId>tel的优先级顺序来关联客户)。

代码示例
<script type="text/javascript">
const path = 'https://xxxxx.xx/chat.html';
const accessId = 'd51d8423-**-**-8ced-6bf09b814040';
const visitorId = '0001';
const visitorName = '访客';
const visitorTag = 'tag';
const tel = 'tel-number';
const province = 'province';
const city = 'city';
const replyWelcome = 'replay-welcome-text';
const headerDisplay = '0';
const customerFields = {
  yourNumberCustomerField:0,
  yourStringCustomerField:'stringValue',
  autoUpdate:1,
};
const visitorExtraInfo = {
  yourNumberCustomerField:0,
  yourStringCustomerField:'stringValue',
};
const language = 'zh_CN';
const cno = '0001';
const externalId = 'externalId';

const URL = `${path}?accessId=${accessId}&visitorId=${encodeURIComponent(visitorId)}&visitorName=${encodeURIComponent(visitorName)}&visitorTag=${encodeURIComponent(visitorTag)}&tel=${encodeURIComponent(tel)}&province=${encodeURIComponent(province)}&city=${encodeURIComponent(city)}&replyWelcome=${encodeURIComponent(replyWelcome)}&headerDisplay=${encodeURIComponent(headerDisplay)}&customerFields=${encodeURIComponent(JSON.stringify(customerFields))}&visitorExtraInfo=${encodeURIComponent(JSON.stringify(visitorExtraInfo))}&language=${language}&cno=${cno}&externalId=${encodeURIComponent(externalId)}`;

// URL: https://xxxxx.xx/chat.html?accessId=d51d8423-**-**-8ced-6bf09b814040&visitorId=0001&visitorName=%E8%AE%BF%E5%AE%A2&visitorTag=tag&tel=tel-number&province=province&city=city&replyWelcome=replay-welcome-text&headerDisplay=0&customerFields=%7B%22yourNumberCustomerField%22%3A0%2C%22yourStringCustomerField%22%3A%22stringValue%22%2C%22autoUpdate%22%3A1%7D&visitorExtraInfo=%7B%22yourNumberCustomerField%22%3A0%2C%22yourStringCustomerField%22%3A%22stringValue%22%7D&language=zh_CN&cno=0001&externalId=externalId
</script>

2.1.2. web-js方式

接入方式说明

是一段web端JS,可由对接企业前端开发人员通过集成开发实现访客端的接入。

接入步骤

接入代码获取方式:使用管理员账号登录-系统设置-在线客服-渠道接入-网页,寻找到列表中的一个接入配置点击编辑(如果您的列表中没有可用的接入配置,请联系售后支持帮您进行配置)。详细路径见下图: image

进入编辑页面后点击 接入设置-复制,即可得到接入代码。详细路径见下图: image

推荐使用服务异步加载方式,可规避因开发引用web-js错误而导致加载异常时直接导致整个前端页面无法加载的问题。
在需要展示客服咨询入口的前端,添加此段代码。添加完成后访问此页面就可以看到默认提供的客服咨询按钮,点击即可发起咨询。

对接完成后页面会存在名为ClinkChatWeb的全局对象,可以用过调用该对象上的方法获取未读消息数,或在每次有未读消息时触发回调。

<script>
  // 获取未读消息数,直接返回当前未读消息总数
  ClinkChatWeb.getUnReadNum();
  // 监听未读消息回调,每次有未读消息时,会触发callback
  ClinkChatWeb.registerUnRead(callback)
</script>
参数说明

代码段中的clinkWebchatOptions对象支持以下扩展参数。

名称 是否必须 类型 说明

accessId

string

接入号ID,复制JS代码之后clinkWebchatOptions对象会自带此参数,请勿随意修改
示例: "d51d8423-**-**-8ced-6bf09b814040"

cno

string

接待座席号(一旦拼接cno参数,会话直接接入到对应座席,若对应座席无法接待,则按正常导航执行)
示例: "0001"

visitorId

string

访客ID,缺省为自动生成的UUID
示例: "0001"

visitorName

string

访客姓名,缺省为省份+城市+访客id后6位
示例: "访客"

visitorTag

string

访客标签,在座席端最多可以显示三个文字

tel

string

手机号码,用于匹配客户资料

province

string

访客所在省份,缺省从IP解析

city

string

访客所在城市,缺省从IP解析

replyWelcome

string

访客在欢迎语之后自动发送的文本

headerDisplay

number

是否展示头部导航栏(为0时不展示其余均展示),需要注意的是,隐藏头部导航栏之后,则原导航栏中的功能都不再展示

customerFields

object

客户自定义字段,必须为JSON数据格式。如果参数中有autoUpdate=1的传值,则在打开自动创建客户的前提下会根据该字段传参更新客户资料

visitorExtraInfo

string

访客自定义参数,必须为JSON String数据格式,可由访客接入会话时提供自定义参数,该值还包含询前表单内容。该值默认包含访客ID与访客名称。

linkCard

object

商品卡片信息,必须为JSON数据格式,将在2.2章节进行详细介绍

language

string

语言 中文:zh_CN,英文:en_US,繁体:zh_TW,阿拉伯语:ar,缺省为 Accept-Language 值

headImgUrl

string

访客头像URL

callback

function

初始化成功的回调方法

initialQuickEntryName

string

是否在访客进线接入会话时自动弹起快捷入口所配置的订单卡片,值为配置的需要弹起的快捷入口的名称。

externalId

string

客户外部唯一标识,通常由企业自定义,在多个系统中同步或关联数据时通过该 id 识别为同一用户(如果同时传递了externalId、visitorId、tel,则按照externalId>visitorId>tel的优先级顺序来关联客户)。

代码示例
<script>
    (function(win, doc, src, opt) {
      win[opt] = win[opt] || function () {
      win[opt].options = arguments[0]};
      var script = doc.createElement("script");
      script.async = 1;
      script.src = src;
      doc.body.appendChild(script);
    })(window, document, "https://xxxx.xx/webchat.js?v="+Date.now(), "clinkWebchatOptions");
      clinkWebchatOptions({
        accessId: "d51d8423-**-**-8ced-6bf09b814040",
        visitorId: "0001",
        visitorName: "访客",
        visitorTag: "tag",
        tel: "tel-number",
        province: "province",
        city: "city",
        replyWelcome: "replay-welcome-text",
        headerDisplay: "0",
        customerFields: {
          yourNumberCustomerField: 0,
          yourStringCustomerField: "stringValue",
          autoUpdate: 1
        },
        visitorExtraInfo: encodeURIComponent(JSON.stringify({
          yourNumberCustomerField: 0,
          yourStringCustomerField: "stringValue",
        })),
        language: "zh_CN",
        cno:"0001",
        externalId:"externalId"
      });
</script>

2.1.3. Android嵌入H5

接入方式说明

如果想要在App内部快速接入,可由对接企业移动端开发人员通过webview加载我们的H5页面,来实现访客端的接入。

嵌入H5可能会遇到兼容性的问题,开发者可以通过手机自带浏览器或者微信浏览器加载H5页面做对比

接入步骤
项目介绍

本项目是一个 Android 应用中的在线客服 H5 集成方案,提供了完整的 H5 页面加载、文件上传下载、相机拍照、视频录制等功能。通过简单的集成,可以快速在您的应用中添加在线客服功能。

功能特性
  1. H5 页面加载和交互

  2. 文件上传(支持图片、视频、文档等)

  3. 文件下载

  4. 相机拍照

  5. 视频录制

  6. 返回键处理

集成步骤

1、下载示例代码

说明:此Demo仅供参考,请根据项目实际需要做适当调整

2、复制核心文件

将以下文件复制到您的项目中:

app/src/main/java/com/tinet/webviewdemo/
├── OnlineFragment.java
└── util/
    ├── TWebViewUtils.java
    ├── TWebChromeClient.java
    ├── TDownloadUtil.java
    └── TPermissionUtil.java

3、添加布局文件

创建 frg_online.xml 布局文件:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <WebView
        android:id="@+id/webView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>
配置说明

1、AndroidManifest.xml 配置

添加必要的权限和 FileProvider 配置:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="your.package.name">

    <!-- 权限声明 -->
    <uses-permission android:name="android.permission.INTERNET" />
    <!-- Android 13 及以下存储权限 -->
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="32" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="32" />
    <!-- Android 14 媒体权限 -->
    <uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
    <uses-permission android:name="android.permission.READ_MEDIA_VIDEO" />
    <uses-permission android:name="android.permission.READ_MEDIA_AUDIO" />
    <!-- 相机和录音权限 -->
    <uses-permission android:name="android.permission.CAMERA" />
    <uses-permission android:name="android.permission.RECORD_AUDIO" />

    <application
        android:requestLegacyExternalStorage="true"
        ...>

        <!-- FileProvider 配置 -->
        <provider
            android:name="androidx.core.content.FileProvider"
            android:authorities="${applicationId}.provider"
            android:exported="false"
            android:grantUriPermissions="true">
            <meta-data
                android:name="android.support.FILE_PROVIDER_PATHS"
                android:resource="@xml/file_paths" />
        </provider>

    </application>
</manifest>

2、FileProvider 路径配置

创建 res/xml/file_paths.xml 文件:

<?xml version="1.0" encoding="utf-8"?>
<paths>
    <external-path
        name="external_files"
        path="." />
    <external-files-path
        name="external_files"
        path="." />
    <cache-path
        name="cache"
        path="." />
    <external-cache-path
        name="external_cache"
        path="." />
    <files-path
        name="files"
        path="." />
</paths>
使用说明

1、在 Activity 中使用

public class YourActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // 创建 Fragment
        OnlineFragment fragment = new OnlineFragment();

        // 设置参数
        Bundle args = new Bundle();
        args.putString("url", "您的在线客服地址");
        fragment.setArguments(args);

        // 添加 Fragment
        getSupportFragmentManager()
            .beginTransaction()
            .replace(R.id.tinetContent, fragment)
            .commit();
    }
}
注意事项

1、Android 14 适配

  1. 使用新的媒体权限(READ_MEDIA_IMAGES/VIDEO/AUDIO)

  2. 旧版存储权限添加 maxSdkVersion="32"

  3. 使用 FileProvider 处理文件 URI

2、权限处理

  1. 使用 TPermissionUtil 统一管理权限

  2. 处理权限拒绝时调用 client.uploadFile(null)

  3. 权限说明文案统一管理

3、内存管理

  1. 及时清理 WebView 资源

  2. 使用弱引用防止内存泄漏

  3. 正确处理 ActivityResultLauncher

4、文件处理

  1. 大文件上传下载时注意性能

  2. 及时清理临时文件

  3. 正确处理文件选择回调

常见问题

1、文件选择不回调

  1. 检查 WebView 配置是否正确

  2. 确认 WebChromeClient 设置正确

  3. 检查权限是否已授予

  4. 确保权限拒绝时调用 client.uploadFile(null)

2、文件上传失败

  1. 检查文件大小限制

  2. 确认文件类型支持

  3. 检查网络连接

  4. 验证 FileProvider 配置

3、相机无法使用

  1. 检查相机权限

  2. 确认相机硬件可用

  3. 检查相机应用是否被禁用

  4. 验证权限说明文案

4、软键盘遮挡输入框

  1. 如果遇到软键盘遮挡输入框的问题,可以在 Activity 的 AndroidManifest.xml 中添加 windowSoftInputMode 配置:

<activity
    android:name=".OnlineActivity"
    android:windowSoftInputMode="adjustResize"
    android:screenOrientation="portrait">
</activity>
  1. 确保 WebView 的父容器(FrameLayout)高度为 match_parent

  2. 如果使用自定义布局,建议使用 FrameLayout 作为根布局,避免使用 ScrollView 等可能影响 WebView 滚动的容器

5、H5页面返回按钮无响应

  1. 确保在已将示例代码TWebViewUtils类中addJavascriptInterface正常调用:

webView.addJavascriptInterface(new Object() {
    @JavascriptInterface
    public void back() {
        if (context instanceof Activity) {
            ((Activity) context).finish();
        }
    }
}, "tinetWebviewApi");
  1. 确保 WebView 的 JavaScript 接口已启用(settings.setJavaScriptEnabled(true))

2.1.4. iOS嵌入H5

项目概述

本文档提供了将客服聊天H5接入iOS应用的详细步骤,包括项目配置、权限设置以及iOS 14.3前后不同的实现方式。该Demo基于WKWebView实现,仅支持iOS11以上的系统,支持通过链接形式快速集成客服聊天功能。

功能特点
  • 支持在应用内打开客服聊天页面

  • 支持录音功能(iOS 14.3以下及 iOS 18.0之后的版本)

  • 支持用户交互(前进/后退导航)

  • 自适应键盘显示/隐藏

  • 支持电话拨打功能

接入前准备
获取访问链接

您需要从获取聊天页面的访问链接,如:

https://webchat-bj.clink.cn/chat.html?accessId=xxxxxxxx
权限配置

在您的项目的`Info.plist`文件中添加以下权限:

<!-- 麦克风权限 -->
<key>NSMicrophoneUsageDescription</key>
<string>应用需要访问您的麦克风以便在聊天时进行录音</string>

<!-- 相机权限(如需要) -->
<key>NSCameraUsageDescription</key>
<string>应用需要访问您的相机以便在聊天时拍摄照片</string>

<!-- 相册权限 -->
<key>NSPhotoLibraryUsageDescription</key>
<string>应用需要访问您的相册以便在聊天时发送图片和视频</string>

<!-- 相册权限 -->
<key>NSPhotoLibraryAddUsageDescription</key>
<string>应用需要访问您的相册以便在拍摄时添加照片到你的相册</string>
接入步骤
1. 项目配置
  1. 确保您的项目引入了必要的框架:

    • WebKit.framework

    • AVFoundation.framework

    • Photos.framework

2. iOS 版本区分处理

根据iOS系统版本,需要使用不同的控制器实现WebView功能:

if (@available(iOS 14.3, *) && !@available(iOS 18.0, *)) {
    // 使用WKWebViewController(iOS 14.3 到 iOS 18.0 之间)
    WKWebViewController *webView = [[WKWebViewController alloc] init];
    webView.url = encodedURLString;
    [webView setModalTransitionStyle:UIModalTransitionStyleCrossDissolve];
    webView.modalPresentationStyle = UIModalPresentationFullScreen;
    [self.navigationController pushViewController:webView animated:YES];
} else {
    // 使用TOSWebViewController
    TOSWebViewController *webView = [[TOSWebViewController alloc] init];
    webView.url = encodedURLString;
    [webView setModalTransitionStyle:UIModalTransitionStyleCrossDissolve];
    webView.modalPresentationStyle = UIModalPresentationFullScreen;
    [self.navigationController pushViewController:webView animated:YES];
}
iOS 14.3以下及 iOS 18.0之后的版本详细说明(TOSWebViewController)

在该版本中,使用`TOSWebViewController`类实现WebView功能,该控制器具有以下特点:

1. 录音功能支持
  • 通过自定义录音实现

  • 支持H5与原生交互,处理录音权限、开始录音、保活和停止录音等操作

  • 实现录音保活机制(15秒倒计时自动停止)

// 关键代码示例
// 处理JS调用
if ([dic[@"action"] isEqualToString:@"recordPermission"]) {     //麦克风权限
   [self requestRecordPermission];
} else if ([dic[@"action"] isEqualToString:@"recordStart"]) {   //开始录音
   [self startCountdown];
   [self setupAudioRecorder];
} else if ([dic[@"action"] isEqualToString:@"recordAlive"]) {   //保活
   self.countdownTime = 15;
} else if ([dic[@"action"] isEqualToString:@"recordStop"]) {    //停止录音
   self.countdownTime = 0;
   if (self.timer == nil) {
        [self stopRecording];
        [self stopCountdown];
   }
}
2. UA标识设置
self.customUserAgent = @"native_ios_wkweb,tinet_native_record";

其中`tinet_native_record`表示支持原生录音功能

3. JS交互接口
  • recordPermission:麦克风权限检查

  • recordStart:开始录音

  • recordAlive:保活(重置15秒倒计时)

  • recordStop:停止录音

  • back:网页后退(在Web页面导航栏添加返回按钮的功能)

消息处理器注册与移除
// 在viewWillAppear中注册JS处理器
- (void)viewWillAppear:(BOOL)animated {
    [super viewWillAppear:animated];
    // 注册一个名为"back"的JS方法,设置处理接收JS方法的对象
    [self.wkWebView.configuration.userContentController addScriptMessageHandler:self name:FUNC_BACKTONATIVE];
}

// 在viewWillDisappear中移除JS处理器,防止内存泄漏
- (void)viewWillDisappear:(BOOL)animated {
    [super viewWillDisappear:animated];
    [self.wkWebView.configuration.userContentController removeScriptMessageHandlerForName:FUNC_BACKTONATIVE];
}

// 处理JS调用原生的back方法
- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message {
    if ([message.name isEqualToString:FUNC_BACKTONATIVE]) {
        [self backNative];
    }
}

// 网页后退实现
- (void)backNative {
    [self.wkWebView goBack];
}
4. 录音流程
  • 请求麦克风权限

  • 设置录音参数(16kHz采样率、单声道、16位PCM格式,此为固定配置,不可更改)

  • 生成临时缓存录音文件

  • 录音结束后将文件转为Base64发送给Web端

5. 键盘适配功能
  • 滚动时自动收起键盘,优化用户体验

// 滚动开始时收起键盘
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView {
    [self.view endEditing:YES];
}
  • 键盘弹出时自动调整WebView尺寸,防止输入框被遮挡

  • 监听键盘显示通知

// 键盘弹出时调整WebView位置
- (void)keyboardWillShow:(NSNotification *)notification {
    // 获取键盘的尺寸和动画时间
    NSDictionary *userInfo = notification.userInfo;
    CGRect keyboardFrame = [userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue];
    NSTimeInterval animationDuration = [userInfo[UIKeyboardAnimationDurationUserInfoKey] doubleValue];

    // 执行与键盘弹出相关的动画
    __weak typeof(self) weakSelf = self;
    [UIView animateWithDuration:animationDuration animations:^{
        __strong typeof(weakSelf) strongSelf = weakSelf;
        // 将wkWebView向上移动键盘高度
        strongSelf.wkWebView.frame = CGRectMake(0, kNavTop, [UIScreen.mainScreen bounds].size.width, [UIScreen.mainScreen bounds].size.height - keyboardFrame.size.height - kNavTop - kBottomBarHeight);
    }];
}
  • 监听键盘隐藏通知

// 键盘隐藏时恢复WebView位置
- (void)keyboardWillHide:(NSNotification *)notification {
    // 获取动画时间
    NSDictionary *userInfo = notification.userInfo;
    NSTimeInterval animationDuration = [userInfo[UIKeyboardAnimationDurationUserInfoKey] doubleValue];

    // 执行与键盘隐藏相关的动画
    __weak typeof(self) weakSelf = self;
    [UIView animateWithDuration:animationDuration animations:^{
        __strong typeof(weakSelf) strongSelf = weakSelf;
        // 将wkWebView恢复到原位置
        strongSelf.wkWebView.frame = CGRectMake(0, kNavTop, [UIScreen.mainScreen bounds].size.width, [UIScreen.mainScreen bounds].size.height - kNavTop - kBottomBarHeight);
    }];
}
iOS 14.3以上及 iOS 18.0之内的版本详细说明(WKWebViewController)

在该版本中,使用`WKWebViewController`类实现WebView功能,该控制器具有以下特点:

1. 更简化的实现
  • 不需要自行实现录音功能,Web端可直接调用系统的录音功能

  • 无需添加`tinet_native_record`标识

2. UA标识设置
self.customUserAgent = @"native_ios_wkweb";
3. JS交互接口
  • back:网页后退(在Web页面导航栏添加返回按钮的功能)

消息处理器注册与移除
// 在viewWillAppear中注册JS处理器
- (void)viewWillAppear:(BOOL)animated {
    [super viewWillAppear:animated];
    // 注册一个名为"back"的JS方法,设置处理接收JS方法的对象
    [self.wkWebView.configuration.userContentController addScriptMessageHandler:self name:FUNC_BACKTONATIVE];
}

// 在viewWillDisappear中移除JS处理器,防止内存泄漏
- (void)viewWillDisappear:(BOOL)animated {
    [super viewWillDisappear:animated];
    [self.wkWebView.configuration.userContentController removeScriptMessageHandlerForName:FUNC_BACKTONATIVE];
}

// 处理JS调用原生的back方法
- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message {
    if ([message.name isEqualToString:FUNC_BACKTONATIVE]) {
        [self backNative];
    }
}

// 网页后退实现
- (void)backNative {
    [self.wkWebView goBack];
}
4. 键盘适配功能
  • 与上面的版本相同,通过通知监听实现键盘自适应

  • 自动调整WebView尺寸,确保表单输入元素可见

  • 滚动时自动收起键盘

版本差异对比表
功能特点 iOS 14.3以下及 iOS 18.0之后的版本 (TOSWebViewController) iOS 14.3以上及 iOS 18.0之内的版本 (WKWebViewController)

录音实现

原生实现并传递给Web端

Web端直接调用系统录音

UA标识

native_ios_wkweb,tinet_native_record

native_ios_wkweb

JS交互接口

recordPermission, recordStart, recordAlive, recordStop, back

back

录音格式

16kHz, 单声道, 16位PCM

不适用(由Web端负责)

录音持续时间

15秒自动停止(可保活)

不适用(由Web端负责)

键盘适配

支持键盘显示/隐藏通知监听和自动调整布局

支持键盘显示/隐藏通知监听和自动调整布局

常见问题
1. 麦克风权限被拒绝
  • 当用户拒绝麦克风权限时,会显示提示对话框,引导用户到设置中开启权限

2. 网页加载失败
  • 检查网络连接

  • 确认URL是否正确

3. 录音保活机制
  • iOS 14.3以下及 iOS 18.0之后的版本中,录音默认15秒自动停止,需要通过recordAlive接口保活

  • iOS 14.3至iOS 18.0版本无需考虑此问题

4. 键盘遮挡输入框
  • 如果发现键盘仍然遮挡输入框,请检查是否正确设置了通知监听

  • 确保计算的高度考虑了刘海屏手机的底部安全区域(kBottomBarHeight)

5. JS交互内存泄漏
  • 确保在视图控制器消失前移除所有已注册的消息处理器

  • 如果不移除消息处理器,可能会导致循环引用和内存泄漏

6. 键盘弹出时,无法浏览最早的历史记录消息。
  • _wkWebView.scrollView.keyboardDismissMode = UIScrollViewKeyboardDismissModeOnDrag;

7. 授权提示弹窗功能说明
  • 为了配合应用审核,聊天页面在用户上传图片或文件时将弹出授权提示弹窗。此功能旨在确保用户知晓并同意上传操作,从而提高应用的安全性和合规性。

  • 弹窗样式及文案如下:

  • 授权提示弹窗

  • 弹窗触发及用户交互逻辑:

  • 首次触发:当用户首次点击上传图片或文件时,系统会弹出授权提示弹窗。用户可以选择【继续】或【不允许】。

  • 用户选择【继续】:如果用户点击【继续】,则表示同意授权。在用户同意后,再次点击上传图片或文件时将不会再弹出授权提示弹窗。

  • 用户选择【不允许】:如果用户点击【不允许】,则表示拒绝授权。在用户拒绝后,下次点击上传图片或文件时,授权提示弹窗会再次弹出,直到用户同意授权为止。

示例代码

完整实现可参考本项目中的`WKWebViewController`和`TOSWebViewController`类。

版本更新记录

时间

版本

说明

2025.05.22

v2.0.0

初始版本

2.1.5. HarmonyOS嵌入H5

简介

本指南将介绍如何在 HarmonyOS 应用中集成和使用 WebView 组件,实现网页内容的加载、与 H5 页面的交互、文件下载等功能。本项目提供了一个完整的在线客服 WebView 示例。

项目结构
entry/src/main/ets/
├── pages/
│   ├── HomePage.ets          # 首页,提供 URL 输入和导航
│   └── LiveChatWebPage.ets   # WebView 实现页面
└── common/
    └── utils/
        └── ToastUtil.ets     # 工具类
示例代码

完整的示例代码可以参考项目中的 HomePage.etsLiveChatWebPage.ets 文件。示例演示了:

  1. 输入 URL 并加载网页

  2. 与 H5 页面交互

  3. 处理文件下载

  4. 权限管理

  5. 返回事件处理

这个示例项目展示了如何在 HarmonyOS 应用中集成 WebView 并实现常见的功能需求。您可以基于此示例进行扩展,实现更多自定义功能。

基本使用

1、创建 WebView 页面

@Entry
@Component
struct LiveChatWebPage {
  @State url: string = ''
  webController: WebviewController = new webview.WebviewController()

  build() {
    Column() {
      Web({
        src: this.url,
        controller: this.webController
      })
        .javaScriptAccess(true)  // 启用 JavaScript
        .fileAccess(true)        // 启用文件访问
        .onlineImageAccess(true) // 启用在线图片访问
        .domStorageAccess(true)  // 启用 DOM 存储
        .imageAccess(true)       // 启用图片访问
        .zoomAccess(true)        // 启用缩放
        .geolocationAccess(true) // 启用地理位置
        .mixedMode(MixedMode.All)
    }
  }
}

2、配置 WebView 权限

module.json5 中添加必要的权限:

{
  "requestPermissions": [
    {
      "name": "ohos.permission.INTERNET"
    },
    {
      "name": "ohos.permission.MICROPHONE",
      "reason": "$string:permission_MICROPHONE",
      "usedScene": {
        "abilities": ["LiveChatPage"],
        "when": "inuse"
      }
    }
  ]
}
高级功能

1、与 H5 页面交互

定义交互接口类:

class WebviewApiClass {
  constructor() {}

  // H5 调用鸿蒙 back 方法
  back(): void {
    router.back()
  }

  // H5 调用鸿蒙 redirect 方法
  redirect(cardInfo: string): void {
    ToastUtil.show(cardInfo)
    console.info("receive redirect:" + cardInfo)
  }
}

注册交互接口:

const JAVA_SCRIPT_PROXY_NAME: string = "tinetWebviewApi"

// 在 WebView 控制器附加时注册
.onControllerAttached(() => {
  this.webController.registerJavaScriptProxy(
    this.webviewApiClass,
    JAVA_SCRIPT_PROXY_NAME,
    ["back", "redirect"]
  );
})

在 H5 中调用接口:

// H5 页面中调用鸿蒙接口
tinetWebviewApi.back();
tinetWebviewApi.redirect('some data');

2、文件下载功能

创建下载代理:

downloadDelegate: webview.WebDownloadDelegate = new webview.WebDownloadDelegate()

// 配置下载代理
this.downloadDelegate.onBeforeDownload((webDownloadItem: webview.WebDownloadItem) => {
  let localFilePath = getContext(this).cacheDir + "/" + webDownloadItem.getSuggestedFileName()
  webDownloadItem.start(localFilePath);
})

// 监听下载进度
this.downloadDelegate.onDownloadUpdated((webDownloadItem: webview.WebDownloadItem) => {
  console.info("download progress: " + webDownloadItem.getPercentComplete());
})

// 下载完成处理
this.downloadDelegate.onDownloadFinish((webDownloadItem: webview.WebDownloadItem) => {
  this.saveFileToDownload(webDownloadItem.getFullPath(), webDownloadItem.getSuggestedFileName());
})

注册下载代理:

.onControllerAttached(() => {
  this.webController.setDownloadDelegate(this.downloadDelegate);
})

3、自定义 UserAgent

// 自定义 UserAgent
@State customUserAgent: string = ' native_android native_harmonyos';

// 设置自定义 UserAgent
.onControllerAttached(() => {
  let userAgent = this.webController.getUserAgent() + this.customUserAgent;
  this.webController.setCustomUserAgent(userAgent);
})

4、处理返回事件

onBackPress() {
  if (this.webController.accessStep(-1)) {
    this.webController.backward();
    return true
  } else {
    return false
  }
}
权限处理

处理 WebView 权限请求:

.onPermissionRequest((event) => {
  if (event) {
    let requestResource = event.request.getAccessibleResource();
    for (let i = 0; i < requestResource.length; i++) {
      let request = requestResource[i];
      // 处理麦克风权限
      if (request === 'TYPE_AUDIO_CAPTURE') {
        let atManager = abilityAccessCtrl.createAtManager();
        atManager.requestPermissionsFromUser(getContext(this), ['ohos.permission.MICROPHONE'])
          .then((data) => {
            if (data.authResults.length > 0 && data.authResults[0] == 0) {
              event.request.grant(event.request.getAccessibleResource());
            } else {
              event.request.deny();
            }
          })
      }
    }
  }
})
注意事项

1、在组件销毁时注销 JavaScript 接口:

aboutToDisappear(): void {
  if (this.webController) {
    this.webController.deleteJavaScriptRegister(JAVA_SCRIPT_PROXY_NAME);
  }
}

2、确保添加必要的权限声明

3、处理文件下载时注意文件路径和权限

4、自定义 UserAgent 时保留原有 UserAgent 信息

5、合理处理返回事件,优先考虑 WebView 历史记录

2.1.6. ReactNative嵌入H5

项目概述

本文档提供了将客服聊天H5接入React Native应用的详细步骤,提供了完整的 H5 页面展示和通信能力。该项目包含完整的UI界面(包含导航栏和返回按钮),主要用于在 React Native 应用中集成 H5 页面,并实现 H5 页面与 RN 的双向通信。

功能特性
🎯 核心功能
  • 完整UI界面: 包含导航栏、标题和返回按钮的完整WebView界面

  • 双向通信: H5 与 RN 的消息传递

  • 返回功能: 支持多种返回方式(按钮返回和H5调用返回)

  • 错误处理: 完善的加载状态、错误处理和重试功能

  • 下载支持: 自动检测并处理文件下载链接

  • 加载状态: 显示加载指示器和错误重试界面

🔗 通信桥接
  • webkit.messageHandlers 兼容: 模拟 iOS 原生 WebView 接口

  • 自动重注入: 防止 messageHandler 被清除

  • 保护机制: 定时检查和恢复通信通道

  • 错误恢复: 完善的错误处理和恢复机制

依赖要求
必需依赖
{
  "react": "19.0.0",
  "react-native": "0.79.2",
  "react-native-webview": "^13.14.1"
}
安装命令
npm install react-native-webview
# 或
yarn add react-native-webview
基本用法
1. 导入组件
import WebViewPage from './WebViewPage';
2. 基本使用
import React, { Component } from 'react';
import WebViewPage from './WebViewPage';

interface AppState {
  showWebView: boolean;
  webUrl: string;
}

class App extends Component<{}, AppState> {
  state = {
    showWebView: false,
    webUrl: 'https://webchat-bj.clink.cn/chat.html?accessId=d410bbf0-5ce8-4c03-a595-820872ffb45b&language=zh_CN'
  };

  render() {
    if (this.state.showWebView) {
      return (
        <WebViewPage
          url={this.state.webUrl}
          onBack={() => this.setState({ showWebView: false })}
        />
      );
    }

    // 其他页面内容...
  }
}
3. 组件API

Props

属性名 类型 必填 描述

url

string

H5 页面的 URL 地址

onBack

() ⇒ void

返回回调函数,用于处理页面返回

State

状态名 类型 描述

isLoading

boolean

页面加载状态

hasError

boolean

是否发生错误

errorMessage

string

错误信息

高级配置
自定义 UserAgent

组件根据平台自动设置 UserAgent:

  • iOS: native_ios_wkweb

  • Android: native_android native_flutter

文件下载处理

自动检测并处理以下类型的下载链接:

  • 文档: .pdf, .docx, .xlsx

  • 压缩包: .zip, .rar

  • 媒体: .mp4, .jpg, .png

  • 应用: .apk, .exe, .dmg, .iso, .img, .bin

  • Data URLs: data:, blob:

  • 包含 download 关键字的链接

WebView 配置
// 默认启用的功能
{
  javaScriptEnabled: true,          // JavaScript 支持
  domStorageEnabled: true,          // DOM 存储
  allowFileAccess: true,            // 文件访问
  startInLoadingState: true,        // 显示加载状态
  allowsBackForwardNavigationGestures: true  // 手势导航
}
Injected JavaScript 详解
注入的核心JavaScript代码

WebViewPage 组件通过 injectedJavaScript 属性向 H5 页面注入完整的通信功能:

console.log('JavaScript通道初始化开始');

// 主初始化逻辑
function initializeJavaScriptBridge() {
    console.log('开始JavaScript桥接初始化');

    try {
        // 确保webkit结构存在
        if (typeof window.webkit === 'undefined') {
            window.webkit = {};
        }
        if (typeof window.webkit.messageHandlers === 'undefined') {
            window.webkit.messageHandlers = {};
        }

        // 注入back messageHandler
        window.webkit.messageHandlers.back = {
            postMessage: function(message) {
                console.log('back messageHandler被调用');
                try {
                    if (!window.ReactNativeWebView || typeof window.ReactNativeWebView.postMessage !== 'function') {
                        throw new Error('ReactNativeWebView不可用');
                    }

                    const messageToSend = JSON.stringify({
                        type: 'back',
                        data: message
                    });
                    window.ReactNativeWebView.postMessage(messageToSend);
                } catch (e) {
                    console.error('发送back消息失败:', e.message);
                }
            }
        };

        console.log('JavaScript桥接初始化完成');

    } catch (e) {
        console.error('messageHandlers注入失败:', e.message);
    }
}

// 开始初始化
try {
    initializeJavaScriptBridge();
} catch (e) {
    console.error('初始化失败:', e.message);
}

true; // 注入脚本必须返回true
注入机制说明
  1. 环境检测: 检查 window.ReactNativeWebView 确认运行在 React Native 环境中

  2. 结构创建: 确保 webkit.messageHandlers 对象结构存在

  3. Handler 注入: 创建三个主要的 messageHandler(back、tinetWebviewApi、tinetWebviewInterface)

  4. 全局函数: 提供便捷的全局访问接口

  5. 保护机制: 每2秒检查一次防止 handler 被页面清除

  6. 调试支持: 提供测试和强制重注入函数

  7. 错误处理: 完善的异常捕获和日志记录

错误处理
1. 返回功能失效
// 检查 messageHandler 是否存在
console.log('back handler:', !!window.webkit?.messageHandlers?.back);

// 手动重新注入
window.forceInjectBackHandler();

// 测试功能
window.testBackFeature();
2. 消息发送失败
// 检查 ReactNativeWebView 是否可用
console.log('RN WebView:', typeof window.ReactNativeWebView);

// 直接发送测试消息
window.ReactNativeWebView.postMessage('test-message');
3. 页面加载失败
  • 检查网络连接

  • 验证 URL 是否正确

  • 查看控制台错误信息

  • 使用组件提供的重试功能

最佳实践
1. 集成建议
  • 在 App 组件中管理 WebView 的显示状态

  • 提供明确的 onBack 回调处理

  • 处理网络错误和加载失败情况

  • 使用URL验证确保输入的地址有效

2. React Native 开发
  • onMessage 方法中正确解析 JSON 消息

  • 为不同类型的消息提供相应的处理逻辑

  • 添加错误边界和异常处理

  • 使用SafeAreaView确保界面适配

3. 调试技巧
  • 使用 React Native 调试器查看消息传递

  • 通过 console.log 跟踪消息流

  • 利用提供的测试函数验证功能

  • 检查注入的JavaScript是否正确执行

React Native 集成示例
完整的应用示例(基于实际项目)
import React, { Component } from 'react';
import { StyleSheet, Text, TextInput, View, TouchableOpacity, Alert, SafeAreaView } from 'react-native';
import WebViewPage from './WebViewPage';

interface AppState {
  text: string;
  showWebView: boolean;
}

export default class App extends Component<{}, AppState> {
  constructor(props: {}) {
    super(props);
    this.state = {
      text: 'https://webchat-bj.clink.cn/chat.html?accessId=d410bbf0-5ce8-4c03-a595-820872ffb45b&language=zh_CN',
      showWebView: false,
    };
  }

  validateUrl = (url: string): boolean => {
    try {
      // 检查是否为空
      if (!url.trim()) {
        Alert.alert('错误', '请输入网址');
        return false;
      }

      // 检查是否包含协议
      if (!url.startsWith('http://') && !url.startsWith('https://')) {
        Alert.alert('错误', '请输入有效的网址,包含 http:// 或 https://');
        return false;
      }

      // 尝试创建 URL 对象来验证
      new URL(url);
      return true;
    } catch (error) {
      Alert.alert('错误', '请输入有效的网址');
      return false;
    }
  }

  handleNextPage = () => {
    if (this.validateUrl(this.state.text)) {
      this.setState({ showWebView: true });
    }
  }

  render() {
    if (this.state.showWebView) {
      return (
        <WebViewPage
          url={this.state.text}
          onBack={() => this.setState({ showWebView: false })}
        />
      );
    }

    return (
      <SafeAreaView style={styles.container}>
        <View style={styles.content}>
          <TextInput
            style={styles.input}
            onChangeText={(text) => this.setState({ text })}
            value={this.state.text}
            placeholder="请输入网址"
            placeholderTextColor="#999"
            clearButtonMode="while-editing"
            keyboardType="url"
            autoCapitalize="none"
          />
          <TouchableOpacity
            style={styles.button}
            onPress={this.handleNextPage}
          >
            <Text style={styles.buttonText}>进入</Text>
          </TouchableOpacity>
        </View>
      </SafeAreaView>
    );
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#fff',
  },
  content: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    padding: 20,
  },
  input: {
    height: 40,
    width: '80%',
    borderWidth: 1,
    borderColor: '#ddd',
    padding: 10,
    marginBottom: 20,
    borderRadius: 5,
    backgroundColor: '#fff',
  },
  button: {
    backgroundColor: '#007AFF',
    padding: 15,
    borderRadius: 5,
    width: '80%',
    alignItems: 'center',
  },
  buttonText: {
    color: 'white',
    fontSize: 16,
    fontWeight: 'bold',
  }
});
技术支持

如有问题,请检查:

  1. 依赖版本是否正确 (react-native-webview: ^13.14.1)

  2. 网络连接是否正常

  3. H5 页面是否支持通信协议

  4. 控制台是否有错误信息

  5. 注入的JavaScript是否正确执行

项目配置
package.json配置

确保项目中包含正确的依赖版本:

{
  "name": "WebViewDemo",
  "version": "0.0.1",
  "dependencies": {
    "react": "19.0.0",
    "react-native": "0.79.2",
    "react-native-webview": "^13.14.1"
  }
}
TypeScript配置

项目使用TypeScript,确保类型定义正确。

示例代码

完整实现可参考本项目中的`WebViewPage.tsx`。

版本更新记录

时间

版本

说明

2025.06.09

v1.0.0

初始版本

2.1.7. UniApp嵌入H5

项目概述

本指南将介绍如何在 UniApp 应用中集成和使用 WebView 组件,实现网页内容的加载、与 H5 页面的交互、文件下载等功能。本项目提供了一个完整的在线客服 WebView 示例。

功能特性
1. WebView 基本功能
  • 支持 JavaScript 执行

  • 支持自定义 UserAgent

  • 支持页面返回功能

2. 权限支持
  • 网络访问权限

  • 文件存储权限

  • 相机权限

  • 录音权限

  • 音频设置修改权限

使用方法
1. 页面跳转
// 跳转到 WebView 页面
uni.navigateTo({
  url: '/pages/index/index?url=' + encodeURIComponent('您的网页URL')
})
2. 网页交互
// 处理网页消息
handleMessage(event) {
    console.log('handleMessage handleMessage:' + JSON.stringify(event.detail))
    const data = event.detail.data[0]
    if (data.type === 'back') {
        // 调用返回功能
        uni.navigateBack()
    } else if (data.type === 'redirect') {
        // 重定向功能
        uni.showToast({
            title: data.msg,
            icon: 'none'
        })
    }
}
权限说明
1. Android 权限
```xml
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.CAMERA"/>
<uses-permission android:name="android.permission.RECORD_AUDIO"/>
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
<uses-permission android:name="android.permission.READ_PHONE_STATE"/>
```
2. iOS 权限
```json
{
  "NSCameraUsageDescription": "需要访问相机用于拍照上传图片",
  "NSPhotoLibraryUsageDescription": "需要访问相册用于选择上传图片",
  "NSMicrophoneUsageDescription": "需要访问麦克风用于录音功能"
}
```
注意事项
1. 文件权限
  • Android 10 及以上版本需要注意文件访问权限的适配

  • iOS 需要在 info.plist 中配置相应的权限说明

2. 网页交互
  • 建议在网页加载完成后再进行 JS 交互

  • 文件上传时注意文件大小限制

3. 跨域问题
  • 需要确保网页支持跨域访问

  • 建议使用 HTTPS 协议

项目结构
WebViewDemo/
├── pages/
│   └── index/
│       └── index.vue      # WebView 主页面
├── manifest.json          # 应用配置文件
├── pages.json            # 页面配置文件
└── README.md             # 项目说明文档
与原生 Android 实现的对比
1. WebView 配置
  • Android 需要手动配置 WebSettings

  • UniApp 已经内置了基本配置,无需手动设置

2. 返回功能
  • Android 需要手动实现 JavascriptInterface

  • UniApp 通过 message 事件实现

3. 权限处理
  • Android 需要在 AndroidManifest.xml 中声明

  • UniApp 在 manifest.json 中统一配置

2.1.8. Flutter嵌入H5

项目概述

本说明面向需要在全新Flutter项目中集成H5页面(WebView)的开发者,详细介绍如何在Flutter项目中安全、完整地集成H5页面,并对原生部分的关键改动进行说明。文档结构参考Android嵌入H5说明,结合demo实现,便于快速对接。

1. 支持的WebView内核

Flutter端采用官方`webview_flutter`插件,底层分别调用: - Android:系统原生WebView - iOS:WKWebView

2. 集成准备与依赖配置
2.1 pubspec.yaml依赖

请在`pubspec.yaml`中添加如下依赖(已在demo中配置):

permission_handler: ^11.0.0
webview_flutter: ^4.4.2
image_picker: ^1.0.4
file_picker: ^10.1.9
flutter_downloader: ^1.11.6
path_provider: ^2.1.1
url_launcher: ^6.2.4
open_filex: ^4.3.4

执行:

flutter pub get
2.2 平台原生配置
  • Android 权限与配置

  • 在`android/app/src/main/AndroidManifest.xml`中添加:

    ```xml
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.CAMERA" />
    <uses-permission android:name="android.permission.RECORD_AUDIO" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    ```
  • 配置FileProvider以支持文件选择和下载。

  • 若需支持下载,需配置`provider_paths.xml`。

  • 详见demo AndroidManifest.xml。

  • iOS 权限与配置

  • 在`ios/Runner/Info.plist`中添加:

    ```xml
    <key>NSCameraUsageDescription</key>
    <string>需要使用相机进行拍照或扫码</string>
    <key>NSMicrophoneUsageDescription</key>
    <string>需要使用麦克风进行录音</string>
    <key>NSPhotoLibraryUsageDescription</key>
    <string>需要访问相册选择图片</string>
    <key>NSPhotoLibraryAddUsageDescription</key>
    <string>需要保存图片到相册</string>
    <key>NSAppTransportSecurity</key>
    <dict>
      <key>NSAllowsArbitraryLoads</key>
      <true/>
    </dict>
    <key>UIBackgroundModes</key>
    <array>
      <string>background-fetch</string>
      <string>background-processing</string>
    </array>
    ```
  • 若需后台下载,需配置entitlements文件并在Xcode打开后台模式。

  • 详见demo Info.plist。

3. WebView使用与功能实现
3.1 基本用法
  • 推荐封装WebViewPage,支持传入url、title。

  • 权限请求建议在页面初始化时统一处理(见demo PermissionUtil)。

  • 支持自定义UserAgent、注入JS、文件选择、下载、H5调用原生等。

3.2 权限处理
  • 使用`permission_handler`统一申请相机、麦克风、存储权限。

  • 权限未授予时,需弹窗提示并引导用户前往设置。

  • 详见demo permission_util.dart

3.3 文件上传/图片选择
  • 通过`webview_flutter`的文件选择回调,结合`image_picker`、`file_picker`实现图片/文件选择与相机拍照。

  • 详见demo webview_file_handler.dart

3.4 文件下载与打开
  • 通过`flutter_downloader`实现下载,`open_filex`实现文件打开。

  • Android需配置FileProvider,iOS需配置后台模式。

  • 详见demo download_util.dart

3.5 WebView调试与开发者工具
  • 支持Chrome DevTools远程调试(Android),支持Eruda/vConsole注入。

  • 可通过demo的调试模式入口体验。

3.6 H5与原生交互
  • 支持JS调用原生(如关闭页面、获取用户信息等),可通过`JavascriptChannel`实现。

  • 详见demo custom_webview.dart


4. 原生部分关键改动说明
4.1 Android
  • AndroidManifest.xml权限声明

  • FileProvider配置

  • provider_paths.xml配置

  • 下载与文件选择相关的intent处理

4.2 iOS
  • Info.plist权限声明

  • Runner.entitlements文件(如需后台下载)

  • AppDelegate.swift支持后台下载


5. 常见问题与注意事项
  • 权限未授予时,H5调用相机/麦克风/文件选择会失败,需做好用户引导。

  • Android 13+存储权限需适配新版API。

  • iOS需在真机测试相机/麦克风等能力。

  • 下载功能需在真机测试,iOS需配置后台模式。

  • WebView调试工具仅限开发环境使用。


6. Demo入口与参考
  • 入口:lib/main.dart,主页可输入URL并打开WebView。

  • 主要页面:lib/pages/webview_page.dartlib/pages/custom_webview.dart

  • 工具类:lib/utils/permission_util.dartlib/utils/webview_file_handler.dartlib/utils/download_util.dart


如需进一步定制或遇到集成问题,请参考demo代码或联系技术支持。

2.2. 商品卡片

商品卡片是一种特殊的消息类型,通过传入linkCard参数,允许访客在会话中将正在查看的商品信息发送给座席,以便座席快速了解访客需要咨询的内容。

商品卡片待发送效果: image

商品卡片发送成功效果: image

2.2.1. 参数说明

linkCard参数支持以下扩展参数:

名称 是否必须 类型 说明

autoSendOnSessionStart

number

访客进线后系统是否自动发送商品卡片,0表示否,1表示是

productList

Product[]

商品列表数组,Product表示单个商品,说明如下

Product

单个商品的数据格式

名称 是否必须 类型 说明

title

string

商品标题
示例: xxx商品

subtitle

string

商品副标题
示例: 商品说明

remark

string

描述,展示为标签样式
示例: 商品描述

amount

string

商品数量,非数字,可携带单位
示例: x5

price

string

商品价格,非数字,可携带单位
示例: $2999

img

string

商品图片url
示例: https://xxx.com/image/xxx.jpeg

imgTag

string

商品标签,对应商品图片左上角标签文字,建议小于等于4个中文
示例: 敬请期待

imgTagStyle

StyleType

商品标签样式,StyleType见其他格式说明
示例: { "color": "red" }

agentNavigateTabName

string

只在在线客服工作台座席端才起作用,客服点击则定位到工作台右侧边栏同名的 tab 打开
示例: 预览页面

visitorUrl

string

访客点击后打开的链接
示例: https://xxx.com/

agentUrl

string

客服点击后打开的链接
示例: https://xxx.com/

appId

string

访客点击跳转目标小程序的appId,适用于小程序嵌入H5和公众号嵌入H5,只在访客端生效,和pagePath一起传才会触发小程序跳转,优先级高于url
示例: 3qwrq***o43op

pagePath

string

访客点击跳转目标小程序的路由地址,适用于小程序嵌入H5和公众号嵌入H5,和appId一起传才会触发小程序跳转,优先级高于url
示例: /xxx/path

status

string

商品状态
示例: 待收货

2.2.2. 代码示例

web纯链接方式

如果您使用web纯链接方式接入,请参考以下代码:

<script type="text/javascript">
const path = 'https://xxxxx.xx/chat.html';
const accessId = 'd51d8423-**-**-8ced-6bf09b814040';
const linkCard = {
        "productList": [
          {
            "title": "xxx商品",
            "subtitle": "商品说明",
            "remark": "商品描述",
            "amount": "x5",
            "price": "$2999",
            "img": "https://xxx.com/image/xxx.jpeg",
            "imgTag": "敬请期待",
            "agentNavigateTabName": "预览页面",
            "visitorUrl": "https://xxx.com/",
            "agentUrl": "https://xxx.com/",
            "appId": "3qwrq***o43op",
            "pagePath": "/xxx/path",
            "status": "待收货",
          }
        ]
    };


const URL = `${path}?accessId=${accessId}&linkCard=${encodeURIComponent(JSON.stringify(linkCard))}`
</script>
web-js方式

如果您使用web-js方式接入,请参考以下代码:

<script>
    (function(win, doc, src, opt) {
      win[opt] = win[opt] || function () {
      win[opt].options = arguments[0]};
      var script = doc.createElement("script");
      script.async = 1;
      script.src = src;
      doc.body.appendChild(script);
    })(window, document, "https://xxxx.xx/webchat.js?v="+Date.now(), "clinkWebchatOptions");
      clinkWebchatOptions({
        accessId: "d51d8423-**-**-8ced-6bf09b814040",
        linkCard:{
          "productList": [
            {
              "title": "xxx商品",
              "subtitle": "商品说明",
              "remark": "商品描述",
              "amount": "x5",
              "price": "$2999",
              "img": "https://xxx.com/image/xxx.jpeg",
              "imgTag": "敬请期待",
              "agentNavigateTabName": "预览页面",
              "visitorUrl": "https://xxx.com/",
              "agentUrl": "https://xxx.com/",
              "appId": "3qwrq***o43op",
              "pagePath": "/xxx/path",
              "status": "待收货",
            }
          ]
        }
      });
</script>
iframe方式

如果您使用iframe嵌入在线客服,可以使用api发送卡片信息,请参考以下代码:

<!-- 引入AICC在线客服api -->
<script src="https://webchat-bj.clink.cn/clink-chat-api.js"></script>

<!-- 调用发送卡片接口示例(需要在iframe加载完成, onload之后调用) -->
<script>
    ClinkChatApi.setIframeCard("tinet-chat-iframe", {
          "productList": [
            {
              "title": "xxx商品",
              "subtitle": "商品说明",
              "remark": "商品描述",
              "amount": "x5",
              "price": "$2999",
              "img": "https://xxx.com/image/xxx.jpeg",
              "imgTag": "敬请期待",
              "agentNavigateTabName": "预览页面",
              "visitorUrl": "https://xxx.com/",
              "agentUrl": "https://xxx.com/",
              "appId": "3qwrq***o43op",
              "pagePath": "/xxx/path",
              "status": "待收货",
            }
          ]
        }
    );
</script>

2.3. 订单卡片

订单卡片是一种特殊的消息类型,通过传入linkCard参数,允许访客在会话中将正在查看的订单信息发送给座席,以便座席快速了解访客需要咨询的内容。

订单卡片待发送效果: image

订单卡片发送成功效果: image

2.3.1. 参数说明

linkCard参数支持以下扩展参数:

名称

是否必须

类型

说明

title

string

店铺名称
示例: XX专营店

logo

string

店铺头像
示例: https://xxx.com/image/xx.jpeg

time

string

订单时间,具体时间
示例: xx年xx月xx日 00:00:00

number

string

订单编号
示例: 1234**2457000

amount

string

商品数量,非数字,可携带单位
示例: x5

price

string

商品价格,非数字,可携带单位
示例: $2999

status

string

订单状态,具体文字状态
示例: 待收货

tag

TagConfig

订单标签 ,TagConfig见其他格式说明

autoSendOnSessionStart

number

访客进线后系统是否自动发送订单卡片,0表示否,1表示是

productList

Product[]

商品列表数组,Product表示单个商品,说明如下

Product

单个商品的数据格式

名称 是否必须 类型 说明

title

string

商品标题
示例: xxx商品

subtitle

string

商品副标题
示例: 商品说明

remark

string

描述,展示为标签样式
示例: 商品描述

amount

string

商品数量,非数字,可携带单位
示例: x5

price

string

商品价格,非数字,可携带单位
示例: $2999

img

string

商品图片url
示例: https://xxx.com/image/xxx.jpeg

imgTag

string

商品标签,对应商品图片左上角标签文字,建议小于等于4个中文
示例: 敬请期待

imgTagStyle

StyleType

商品标签样式,StyleType见其他格式说明
示例: { "color": "red" }

agentNavigateTabName

string

只在在线客服工作台座席端才起作用,客服点击则定位到工作台右侧边栏同名的 tab 打开
示例: 预览页面

visitorUrl

string

访客点击后打开的链接
示例: https://xxx.com/

agentUrl

string

客服点击后打开的链接
示例: https://xxx.com/

appId

string

访客点击跳转目标小程序的appId,适用于小程序嵌入H5和公众号嵌入H5,只在访客端生效,和pagePath一起传才会触发小程序跳转,优先级高于url
示例: 3qwrq***o43op

pagePath

string

访客点击跳转目标小程序的路由地址,适用于小程序嵌入H5和公众号嵌入H5,和appId一起传才会触发小程序跳转,优先级高于url
示例: /xxx/path

status

string

商品状态
示例: 待收货

2.3.2. 代码示例

web纯链接方式

如果您使用web纯链接方式接入,请参考以下代码:

<script type="text/javascript">
const path = 'https://xxxxx.xx/chat.html';
const accessId = 'd51d8423-**-**-8ced-6bf09b814040';
const linkCard = {
        "title": "XX专营店",
        "logo": "https://xxx.com/image/xx.jpeg",
        "time": "xx年xx月xx日 00:00:00",
        "number": "1234**2457000",
        "status": "待收货",
        "amount": "x5",
        "price": "$2999",
        "tag": {
          "text": "已发货",
          "style": { "color": "red" },
        },
        "productList": [
          {
            "title": "xxx商品",
            "subtitle": "商品说明",
            "remark": "商品描述",
            "amount": "x5",
            "price": "$2999",
            "img": "https://xxx.com/image/xxx.jpeg",
            "imgTag": "敬请期待",
            "agentNavigateTabName": "预览页面",
            "visitorUrl": "https://xxx.com/",
            "agentUrl": "https://xxx.com/",
            "appId": "3qwrq***o43op",
            "pagePath": "/xxx/path",
            "status": "待收货",
          }
        ]
    };


const URL = `${path}?accessId=${accessId}&linkCard=${encodeURIComponent(JSON.stringify(linkCard))}`
</script>
web-js方式

如果您使用web-js方式接入,请参考以下代码:

<script>
    (function(win, doc, src, opt) {
      win[opt] = win[opt] || function () {
      win[opt].options = arguments[0]};
      var script = doc.createElement("script");
      script.async = 1;
      script.src = src;
      doc.body.appendChild(script);
    })(window, document, "https://xxxx.xx/webchat.js?v="+Date.now(), "clinkWebchatOptions");
      clinkWebchatOptions({
        accessId: "d51d8423-**-**-8ced-6bf09b814040",
        linkCard:{
          "title": "XX专营店",
          "logo": "https://xxx.com/image/xx.jpeg",
          "time": "xx年xx月xx日 00:00:00",
          "number": "1234**2457000",
          "status": "待收货",
          "amount": "x5",
          "price": "$2999",
          "tag": {
            "text": "已发货",
            "style": { "color": "red" },
          },
          productList: [
            {
              "title": "xxx商品",
              "subtitle": "商品说明",
              "remark": "商品描述",
              "amount": "x5",
              "price": "$2999",
              "img": "https://xxx.com/image/xxx.jpeg",
              "imgTag": "敬请期待",
              "agentNavigateTabName": "预览页面",
              "visitorUrl": "https://xxx.com/",
              "agentUrl": "https://xxx.com/",
              "appId": "3qwrq***o43op",
              "pagePath": "/xxx/path",
              "status": "待收货",
            }
          ]
        }
      });
</script>
iframe方式

如果您使用iframe嵌入在线客服,可以使用api发送卡片信息,请参考以下代码:

<!-- 引入AICC在线客服api -->
<script src="https://webchat-bj.clink.cn/clink-chat-api.js"></script>

<!-- 调用发送卡片接口示例(需要在iframe加载完成, onload之后调用) -->
<script>
    ClinkChatApi.setIframeCard("tinet-chat-iframe", {
          "title": "XX专营店",
          "logo": "https://xxx.com/image/xx.jpeg",
          "time": "xx年xx月xx日 00:00:00",
          "number": "1234**2457000",
          "status": "待收货",
          "amount": "x5",
          "price": "$2999",
          "tag": {
            "text": "已发货",
            "style": { "color": "red" },
          },
          productList: [
            {
              "title": "xxx商品",
              "subtitle": "商品说明",
              "remark": "商品描述",
              "amount": "x5",
              "price": "$2999",
              "img": "https://xxx.com/image/xxx.jpeg",
              "imgTag": "敬请期待",
              "agentNavigateTabName": "预览页面",
              "visitorUrl": "https://xxx.com/",
              "agentUrl": "https://xxx.com/",
              "appId": "3qwrq***o43op",
              "pagePath": "/xxx/path",
              "status": "待收货",
            }
          ]
        }
    );
</script>

2.4. 自定义参数

2.4.1. 脚本嵌入

参照示例代码,定义自定义参数。

<script>
  (function(win, doc, src, opt) {
    win[opt] = win[opt] || function () {
    win[opt].options = arguments[0]};
    var script = doc.createElement("script");
    script.async = 1;
    script.src = src;
    doc.body.appendChild(script);
  })(window, document, "https://webchat-bj.clink.cn/webchat.js?v="+Date.now(), "clinkWebchatOptions");
    clinkWebchatOptions({
      accessId: '接入号标识。必需。',
      visitorExtraInfo: encodeURIComponent(JSON.stringify({
         "自定义字段1":"值1",
         "自定义字段2":"值2"
        }
      )
     )
    });
</script>

参数说明:

字段名

描述

visitorExtraInfo

访客自定义字段

注:参数为json。

2.4.2. H5链接

参照示例代码,将自定义参数包装为json对象。

var visitorExtraInfo= { "自定义字段1":"值1", "自定义字段2":"值2" };
var url = "https://webchat-bj.clink.cn/chat.html?accessId=" + paramObj.accessId
                + "&language=" + (paramObj['language'] || paramObj['locale'])
                + "&visitorExtraInfo=" + encodeURIComponent(JSON.stringify(visitorExtraInfo));

2.5. 敏感信息加密

接入时敏感信息加解密传参,技术方案基于AES加解密算法实现。

当前支持加密的参数如下:

参数名

描述

tel

访客手机号码

externalId

外部企业客户ID

customerFields

客户资料字段

visitorExtraInfo

访客自定义参数

timestamp

当前时间戳,单位:毫秒

Java示例代码

package com.tinet.clink.chat.web;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.security.SecureRandom;
import java.util.HashMap;
import java.util.Map;

public class CustomerFieldsEncryptTest {

    public static void main(String[] args) throws JsonProcessingException, UnsupportedEncodingException {
        String tel = "18866668888";
        String telText = "tel=" + tel;

        String externalId = "12345";
        String externalIdText = "externalId=" + externalId;

        Map<String, String> customerFields = new HashMap<String, String>(2);
        customerFields.put("客户名称", "张三");

        ObjectMapper objectMapper = new ObjectMapper();
        String customerFieldsJsonText = objectMapper.writeValueAsString(customerFields);
        customerFieldsJsonText = URLEncoder.encode(customerFieldsJsonText, "UTF-8");
        String customerFieldsEncryptingText = "customerFields=" + customerFieldsJsonText;

        Map<String, String> visitorExtraInfo = new HashMap<String, String>(2);
        visitorExtraInfo.put("访客自定义参数", "值1");
        String visitorExtraInfoJsonText = objectMapper.writeValueAsString(visitorExtraInfo);
        visitorExtraInfoJsonText = URLEncoder.encode(visitorExtraInfoJsonText, "UTF-8");
        String visitorExtraInfoEncryptingText = "visitorExtraInfo=" + visitorExtraInfoJsonText;

        long now = System.currentTimeMillis();
        String timestampText = "timestamp=" + now;

        String aesKey = "xxx";
        String encryptingText = String.join("&", telText, externalIdText, customerFieldsEncryptingText, timestampText, visitorExtraInfoEncryptingText);
        System.out.println("encrypting text is " + encryptingText);
        String encryptedText = encrypt(encryptingText, aesKey);
        System.out.println("encrypted text is " + encryptedText);
    }

    public static String encrypt(String encryptingText, String aesKey) {
        String encryptedText = null;
        try {
            KeyGenerator kgen = KeyGenerator.getInstance("AES");
            SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG");
            secureRandom.setSeed(aesKey.getBytes());
            kgen.init(128, secureRandom);
            SecretKey secretKey = kgen.generateKey();
            SecretKeySpec secretKeySpec = new SecretKeySpec(secretKey.getEncoded(), "AES");

            Cipher cipher = Cipher.getInstance("AES");
            cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec);
            byte[] result = cipher.doFinal(encryptingText.getBytes("utf-8"));
            encryptedText = parseByte2HexStr(result);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return encryptedText;
    }

    private static String parseByte2HexStr(byte buf[]) {
        StringBuffer sb = new StringBuffer();
        for (int i = 0; i < buf.length; i++) {
            String hex = Integer.toHexString(buf[i] & 0xFF);
            if (hex.length() == 1) {
                hex = '0' + hex;
            }
            sb.append(hex.toLowerCase());
        }
        return sb.toString();
    }
}

AESKey:即参数加解密的 Key,长度固定为43个字符,从 a-z,A-Z,0-9 共62个字符中选取。在企业后台配置。

为加强安全性,参数中增加 timestamp,精度为毫秒。

企业后台配置和获取 AESKey 如下图: image

2.5.1. 脚本嵌入

<script>
  (function(win, doc, src, opt) {
    win[opt] = win[opt] || function () {
    win[opt].options = arguments[0]};
    var script = doc.createElement("script");
    script.async = 1;
    script.src = src;
    doc.body.appendChild(script);
  })(window, document, "https://webchat-bj.clink.cn/webchat.js?v="+Date.now(), "clinkWebchatOptions");
    clinkWebchatOptions({
      accessId: '接入号标识。必需。',
      encryptedParams: '加密后的字符串',
      timestamp: 1616673070260
    });
</script>

参数说明:

字段名

描述

encryptedParams

加密后的字符串

timestamp

毫秒级时间戳

注:参数为json。

2.5.2. H5链接

参照示例代码,定义。

var url = "https://webchat-bj.clink.cn/chat.html?accessId=" + paramObj.accessId
    + "&encryptedParams=" + encodeURIComponent(encryptedParams)
    + "&timestamp=" + 1616673070260;

2.6. 访客身份签名

为了保证安全,建议企业在接入自定义 visitorId 时对访客身份进行签名。

签名参数说明:

noncestr:随机字符串

sign_token:签名 token,企业后台配置,服务端保存

timestamp:时间戳

visitor_id:自定义 visitorId

企业后台配置和获取 Token 如下图: image

签名生成步骤:

将上述参数按照 URL 键值对的格式(即 key1=value1&key2=value2…)拼接成字符串;示例如下(注意不可变动字段顺序):

noncestr=NONCESTR&sign_token=SIGN_TOKEN&timestamp=TIMESTAMP&visitor_id=VISITOR_ID

签名计算:

对拼接后的字符串进行 SHA-1 加密,生成签名。

安全建议:

签名 token (sign_token) 的保护:确保 sign_token 仅在服务端存储,不泄露给客户端。

以 sign_token=601F1889667EFAEBB33B8C125728 为例,脚本、H5集成示例如下:

2.6.1. 脚本嵌入

<script>
  (function(win, doc, src, opt) {
    win[opt] = win[opt] || function () {
    win[opt].options = arguments[0]};
    var script = doc.createElement("script");
    script.async = 1;
    script.src = src;
    doc.body.appendChild(script);
  })(window, document, "https://webchat-bj.clink.cn/webchat.js?v="+Date.now(), "clinkWebchatOptions");
    clinkWebchatOptions({
      accessId: 'ACCESS_ID',
      visitorId: 'xiaoming',
      timestamp: 1635132690283,
      noncestr: '1635132690',
      sign: '20791af57739f4e63f00f016d2279489518a7c48'
    });
</script>

2.6.2. H5链接

https://webchat-bj.clink.cn/chat.html?accessId=ACCESS_ID&visitorId=xiaoming&timestamp=1635132690283&noncestr=1635132690&sign=20791af57739f4e63f00f016d2279489518a7c48

2.7. 询前消息

接入时支持自定义固定的话术消息,在接入座席时会自动推送给访客。

字段名

类型

描述

initMsg

TEXT

询前消息内容,中文支持100字

2.8. 访客端发送商品卡片

1.在管理员系统中,企业配置⇒在线客服⇒渠道接入⇒网页⇒编辑⇒快捷入口⇒添加快捷入口, 弹出框里事件类型选择【卡片消息】,配置好获取卡片消息的url,参数,请求方式等

2.在自己的系统里按照刚刚配置的url,和请求方式,编写获取卡片消息的接口

字段名

类型

描述

status

Integer

状态码 注:请求成功默认返回200

message

String

描述

result

Object

卡片信息集合

接口返回结果示例:

{
	"status": 200,
	"message": "OK",
	"result": [
		{
			"title": "我是标题0",
			"subTitle": "我是副标题0",
			"description": "我是描述0",
			"price": 4999,
            "priceStyle": {'font-size': '14px', 'color': '#4385ff', 'font-weight': 700},
			"time": "2022-04-20T08:03:49.990+0000",
			"img": "https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fempic.dfcfw",
			"url": "https://detail.zol.com.cn/cell_phone/index1344163.shtml",
			"subUrl": "https://product.pconline.com.cn/mobile/huawei/1131794.html",
			"buttonText": null,
			"tabMenuName": null,
			"status": 0,
            "statusStyle": {'font-size': '14px', 'color': '#4385ff', 'font-weight': 700},
			"extraInfo": null,
			"extraData":
                [{"name":"orderType","value":"sellOrder"},
                {"name":"orderNo","value":"No0001"},
                {"name":"cardType","value":"sellCard"}]
		},
		{
			"title": "我是标题9",
			"subTitle": "我是副标题9",
			"description": "我是描述9",
			"price": 4999,
            "priceStyle": {'font-size': '14px', 'color': '#4385ff', 'font-weight': 700},
			"time": "2022-04-20T08:03:49.990+0000",
			"img": "https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fempic.dfc",
			"url": "https://detail.zol.com.cn/cell_phone/index1344163.shtml",
			"subUrl": "https://product.pconline.com.cn/mobile/huawei/1131794.html",
			"buttonText": null,
            "tabMenuName": null,
			"status": 9,
            "statusStyle": {'font-size': '14px', 'color': '#4385ff', 'font-weight': 700},
			"extraInfo": null,
			"extraData":
                [{"name":"orderType","value":"sellOrder"},
                {"name":"orderNo","value":"No0001"},
                {"name":"cardType","value":"sellCard"}]
		}
	]
}

返回卡片消息列表即可,如果接口异常返回[]空列表即可。

2.9. 访客端发送订单

2.9.1. 接入方式说明

访客端发送订单,可以根据配置的接口获取数据定制对应的订单列表,供访客查看发送给客服,使客服能快速明白访客的需求

2.9.2. 接入步骤

使用管理员账号登录-系统设置-在线客服-渠道接入-网页,寻找到列表中的一个接入配置点击编辑(如果您的列表中没有可用的接入配置,请联系售后支持帮您进行配置)。详细路径见下图: image

进入编辑页面后点击 基础设置-页面设置-编辑区,有以下两种方式可在访客页面新增对应的订单列表入口:
1、pc移动端通用: 快捷入口-添加快捷入口
2、移动端: 移动端工具栏-添加自定义按钮

详细路径见下图: image

两种方式点击后都会出现弹出框,弹出框内事件类型选择【发送订单】,配置好获取卡片消息的url请求方式等以保证可以成功请求接口,如果配置的接口支持搜索也可开启支持搜索配置以达到访客更好的体验

请按照刚刚配置的url,和请求方式,编写获取订单列表消息的接口,并保证能接收如以下 接口请求参数 说明的参数,和返回同 接口返回数据格式 说明的数据

正常配置成功后,可在访客端的快捷入口/移动端工具栏点击对应的发送订单入口,查看到对应的订单列表并操作

2.9.3. 接口请求参数

这里列举的是我们调用配置的接口时会携带上的参数

名称 是否必传 类型 说明

pageSize

number

分页的一页展示数量,现默认支持一页数量为10,

pageIndex

number

分页请求第几页,默认1,会根据当前页数动态传参

searchInput

string

当选择支持搜索时,访客点击搜索,传入搜索框输入的内容。具体是需要搜索名称还是订单号等等由您内部判断(搜索引导文案可配)

category

string

支持分类搜索,配置的分类名称,比如食品分类

2.9.4. 接口返回数据格式

这里是订单列表展示所需数据,用来定制订单列表的显示

当订单Order的非必须字段都不返回,只传productList商品列表数据的时候显示为商品列表

返回说明

名称

是否必须

类型

说明

status

number

状态码 注:请求成功默认返回200

message

String

描述

result

Result

返回数据,Result说明如下

Result

名称

是否必须

类型

说明

pageSize

number

分页的一页展示数量,应该与请求参数的pageSize同步

pageIndex

number

第几页,应该与请求参数的pageIndex同步

list

Order[]

订单列表数组,Order表示单个订单,说明如下

Order

单个订单的数据格式

名称

是否必须

类型

说明

title

string

店铺名称
示例: XX专营店

logo

string

店铺头像
示例: https://xxx.com/image/xx.jpeg

time

string

订单时间,具体时间
示例: xx年xx月xx日 00:00:00

number

string

订单编号
示例: 1234**2457000

status

string

订单状态,具体文字状态
示例: 待收货

tag

TagConfig

订单标签 ,TagConfig见其他格式说明

buttonConfigList

ButtonConfig[]

订单按钮配置,支持三种类型,链接,发送文本,发送订单,可传多个。发送出去的订单消息不展示此按钮,ButtonConfig见其他格式说明

bottomButtonConfig

ButtonConfig

订单底部按钮,只支持链接模式,建议一个订单包含商品较多的情况,这里只传递有限的几个商品数据,其他商品采用此按钮配置链接跳转到查看该订单所有商品的页面。ButtonConfig见其他格式说明
示例: { "text": "共计120件,查看更多", "type": "link", "linkUrl": "https://xxx.com/", "target": "", "appId": "", "pagePath": "", "style": {} }

extraInfoArr

ExtraInfo[]

自定义参数,只在订单列表显示,且显示在该订单底部,发送出去的消息不显示,ExtraInfo见其他格式说明
示例: [ { "name": "金额", "value": "899" }, { "name": "备注", "value": "快递放门口" } ]

extraData

any[] | null

您的自定字段,一般来说是个数组或null,会携带给机器人,不展示在页面上,我们不做处理

productList

Product[]

商品列表数组,Product表示单个商品,说明如下

Product

单个商品的数据格式

名称 是否必须 类型 说明

title

string

商品标题
示例: xxx商品

subtitle

string

商品副标题
示例: 商品说明

remark

string

描述,展示为标签样式
示例: 商品描述

amount

string

商品数量,非数字,可携带单位
示例: x5

price

string

商品价格,非数字,可携带单位
示例: $2999

img

string

商品图片url
示例: https://xxx.com/image/xxx.jpeg

imgTag

string

商品标签,对应商品图片左上角标签文字,建议小于等于4个中文
示例: 敬请期待

imgTagStyle

StyleType

商品标签样式,StyleType见其他格式说明
示例: { "color": "red" }

agentNavigateTabName

string

只在在线客服工作台座席端才起作用,客服点击则定位到工作台右侧边栏同名的 tab 打开
示例: 预览页面

visitorUrl

string

访客点击后打开的链接
示例: https://xxx.com/

agentUrl

string

客服点击后打开的链接
示例: https://xxx.com/

appId

string

访客点击跳转目标小程序的appId,适用于小程序嵌入H5和公众号嵌入H5,只在访客端生效,和pagePath一起传才会触发小程序跳转,优先级高于url
示例: 3qwrq***o43op

pagePath

string

访客点击跳转目标小程序的路由地址,适用于小程序嵌入H5和公众号嵌入H5,和appId一起传才会触发小程序跳转,优先级高于url
示例: /xxx/path

status

string

商品状态
示例: 待收货

buttonConfigList

ButtonConfig[]

商品按钮配置,支持三种类型,链接,发送文本,发送订单(发送单个商品也是作为订单发送出去,只是不传递订单的数据信息显示为一个商品)。发送出去的消息不展示此按钮,ButtonType见其他格式说明
示例:
[
{
"text": "发送订单",
"type": "sendCard",
"style": {}
},
{
"text": "发送消息",
"type": "sendContent",
"content": "亲,请稍等,我们帮您查询订单情况",
"style": {}
},
{
"text": "跳转官网",
"type": "link",
"linkUrl": "https://xxx.com/",
"target": "_blank",
"appId": "",
"pagePath": "",
"style": {}
}
]

extraData

any[] | null

您的自定字段,一般来说是个数组或null,会携带给机器人,不展示在页面上,我们不做处理

其他格式说明

以下是具体的ts类型

export type ExtraDataType = any[] | null;
export enum ButtonType {
    SEND_CONTENT = 'sendContent',
    SEND_CARD = 'sendCard',
    LINK = 'link',
}
export interface NameValue {
    label: string;
    value: string;
}
export interface StyleType {
    fontWeight?: string; // 字重
    color?: string; //字体颜色 (16进制色值,例如 #000000)
    border?: string; //边框
    background?: string; //背景
}
export interface TagStyleType {
    fontWeight?: string; // 字重
    color?: string; //字体颜色 (16进制色值,例如 #000000)
    border?: string; //边框
}
export interface TagConfig {
    text: string;
    style?: TagStyleType;
}
export interface ButtonConfig {
    type: ButtonType;
    text: string;
    style?: StyleType;
    // ------sendContent类型必要字段----------
    content?: string;
    // ------------link类型字段--------------
    linkUrl?: string;
    target?: string; // a标签跳转的类型
    // link类型需要跳转小程序字段,需要跳转小程序时以下两个字段都必须传
    appId?: string;
    pagePath?: string;
}
export interface Product {
    title: string;
    subtitle?: string;
    remark?: string;
    amount?: string;
    price?: string;
    img: string;
    imgTag?: string;
    imgTagStyle?: StyleType;
    agentNavigateTabName?: string;
    visitorUrl?: string;
    agentUrl?: string;
    appId?: string;
    pagePath?: string;
    status?: string;
    buttonConfigList?: ButtonConfig[];
    extraData?: ExtraDataType;
}

export interface Order {
    title?: string;
    logo?: string;
    time?: string;
    number?: string;
    status?: string;
    buttonConfigList?: ButtonConfig[];
    bottomButtonConfig?: ButtonConfig;
    extraInfoArr?: NameValue[];
    extraData?: ExtraDataType;
    productList: Product[];
}

2.9.5. 接口返回结果示例

{
  "status": 200,
  "message": "OK",
  "result": {
    "pageIndex": 1,
    "pageSize": 10,
    "list": [
      {
        "title": "XX专营店",
        "logo": "https://xxx.com/image/xx.jpeg",
        "time": "xx年xx月xx日 00:00:00",
        "number": "1234**2457000",
        "status": "待收货",
        "tag": {
          "text": "标签",
          "style": {
            "fontWeight": "bold",
            "color": "red",
            "border": "1px solid red",
          }
        },
        "buttonConfigList": [
          {
            "text": "发送订单",
            "type": "sendCard",
            "style": {}
          },
          {
            "text": "发送消息",
            "type": "sendContent",
            "content": "亲,请稍等,我们帮您查询订单情况",
            "style": {}
          },
          {
            "text": "跳转官网",
            "type": "link",
            "linkUrl": "https://xxx.com/",
            "target": "_blank",
            "appId": "",
            "pagePath": "",
            "style": {}
          }
        ],
        "bottomButtonConfig": {
          "text": "共计120件,查看更多",
          "type": "link",
          "linkUrl": "https://xxx.com/",
          "target": "",
          "appId": "",
          "pagePath": "",
          "style": {}
        },
        "extraInfoArr": [
          {
            "name": "金额",
            "value": "899"
          },
          {
            "name": "备注",
            "value": "快递放门口"
          }
        ],
        "extraData":
                [{"name":"orderType","value":"sellOrder"},
                {"name":"orderNo","value":"No0001"},
                {"name":"cardType","value":"sellCard"}],
        "productList": [
          {
            "title": "xxx商品",
            "subtitle": "商品说明",
            "remark": "商品描述",
            "amount": "x5",
            "price": "$2999",
            "img": "https://xxx.com/image/xxx.jpeg",
            "imgTag": "敬请期待",
            "imgTagStyle": {
            },
            "agentNavigateTabName": "预览页面",
            "visitorUrl": "https://xxx.com/",
            "agentUrl": "https://xxx.com/",
            "appId": "3qwrq***o43op",
            "pagePath": "/xxx/path",
            "status": "待收货",
            "buttonConfigList": [
              {
                "text": "发送",
                "type": "sendCard",
                "style": {
                }
              }
            ],
            "extraData":
                [{"name":"orderType","value":"sellOrder"},
                {"name":"orderNo","value":"No0001"},
                {"name":"cardType","value":"sellCard"}]
          },
          {
            "title": "xxx商品",
            "subtitle": "商品说明",
            "remark": "商品描述",
            "amount": "x5",
            "price": "$1866",
            "img": "https://xxx.com/image/xxx.jpeg",
            "imgTag": "敬请期待",
            "imgTagStyle": {
            },
            "agentNavigateTabName": "预览页面",
            "visitorUrl": "https://xxx.com/",
            "agentUrl": "https://xxx.com/",
            "appId": "3qwrq***o43op",
            "pagePath": "/xxx/path",
            "status": "待收货",
            "buttonConfigList": [
              {
                "text": "发送",
                "type": "sendCard",
                "style": {
                }
              }
            ],
            "extraData":
                [{"name":"orderType","value":"sellOrder"},
                {"name":"orderNo","value":"No0001"},
                {"name":"cardType","value":"sellCard"}]
          },
        ]
      },
    ]
  }
}

效果如图
image

2.10. 无按钮唤起访客端方法

对于按钮不展示的情况,可以直接打开会话窗口方法

<script>
    ClinkChatWeb.openSessionWindow()
</script>

接口代码Java版示例:

package com.tinet.clink.chat.web.controller;
import com.alibaba.fastjson.JSONObject;
import com.tinet.clink.chat.web.model.CardMessageWithType;
import org.springframework.util.DigestUtils;
import org.springframework.web.bind.annotation.*;

import java.util.ArrayList;
import java.util.List;
/**
 * 访客发送卡片示例代码
 *
 * @author dengsx
 * @create 2022/04/06
 **/
@RestController
@RequestMapping("/test/card_msg")
public class TestCardController {

    private static final String TOKEN = "CARD_MSG_TOKEN";

    @GetMapping
    public JSONObject getCard(@RequestParam String timestamp,
                                             @RequestParam String sign,
                                             @RequestParam(required = false) String enterpriseId,
                                             @RequestParam(required = false) String mainUniqueId,
                                             @RequestParam(required = false) String visitorId,
                                             @RequestParam(required = false) String inquiryTel,
                                             @RequestParam(required = false) String varTel) {
        // token校验
        String signParamString = TOKEN + timestamp;
        if (!DigestUtils.md5DigestAsHex(signParamString.getBytes()).equals(sign)) {
            // token校验不通过
            return new ArrayList<>();
        }
        // todo  根据传过来的访客相关参数,查询对应的卡片信息
        List<CardMessageWithType> result = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            CardMessageWithType type = new CardMessageWithType();
            type.setDescription("我是描述" + i);
            type.setTitle("我是标题" + i);
            type.setSubTitle("我是副标题" + i);
            type.setImg("https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fempic.dfcfw.com%2F753804994276458510%2Fw1012h705%2Fart&refer=http%3A%2F%2Fempic.dfcfw.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1651829391&t=d50fc22d46a2064e87172ce5d319a256");
            type.setUrl("https://detail.zol.com.cn/cell_phone/index1344163.shtml");
            type.setStatus(i);
            type.setSubUrl("https://product.pconline.com.cn/mobile/huawei/1131794.html");
            result.add(type);
        }
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("result", result);
        jsonObject.put("status", 200);
        jsonObject.put("message", "OK");
        return jsonObject;
    }
}


// 返回结构model示例
package com.tinet.clink.chat.web.model;

import lombok.Data;

import java.util.Date;
import java.util.List;
/**
 * 卡片消息model示例
 *
 * @author dengsx
 * @create 2022/04/06
 **/
@Data
public class CardMessageWithType {
    // 标题
    private String title;
    // 副标题
    private String subTitle;
    // 描述
    private String description;
    // 价格
    private Double price = 4999d;
    // 时间
    private Date time = new Date();
    // 图片地址
    private String img;
    //  商品地址
    private String url;
    // 商品地址2
    private String subUrl;
    // 按钮
    private String buttonText;
    // 状态
    private Integer status;
    // 其他信息
    private String extraInfo;
    // 其他信息
    private List<String> extraData;
}

2.11. 发送会话内容

// 调用ClinkChatWeb.sendVisitorMsg此api,会将传入信息转换并在聊天窗口中以访客身份发送出去,只支持第三方嵌入的形式, 目前只支持文本类型,想换行可通过\n在需要换行的区域换行
// ClinkChatWeb.sendVisitorMsg(params)
// params参数说明:
//  {
//      type: 'text'   --- 传入信息类型,支持扩展,可选值 text
//      content: '',   --- 展示的内容
//  }
//

3. 座席端

3.1. 发送商品卡片

通过自定义菜单嵌入在线客服,使用api发送图文消息

<!-- 引入AICC在线客服api -->
<script src="https://chat-ws-bj.clink.cn/chat-commodity-card.js"></script>
<script>
    // 参数说明:
    // card: 卡片信息
          // {
          //     title: "", 标题
          //     subTitle: "", 子标题,一般用作商品名
          //     description: "", 描述,一般用作对商品的描述
          //     price: "", 价格信息
          //     img: "", 图片链接
          //     url: "", 点击卡片跳转url
          //     status:"", 状态
          //     subUrl:"",座席端点击卡片跳转链接
          //     tabMenuName:"",商品卡片详情所展示到的自定义菜单名称
          //     time: "", 时间信息
          //     extraInfo: [
          //           { name: "", value: "" }, 扩展信息,可多条
          //           ...
          //     ]
          // };
    var params = {
             title: '订单号:#2112323123',
             subTitle: '华为P40 麒麟990 5G SoC芯片 5000万超感知徕卡三摄 30倍数字变焦',
             description: '麒麟990芯片;5000万超感知徕卡三摄;30倍数字变焦;华为手机至高6期免息',
             price: '¥6999',
             time: '2021/12/21 21:50',
             img: '',
             status: '完成',
             tabMenuName: '自定义菜单',
             url: 'https://item.jd.com/100006607505.html',
             subUrl: 'https://www.baidu.com',
             extraInfo: [
                 { name: '自定义字段1', value: '' },
                 { name: '自定义字段2', value: '' },
                 { name: '自定义字段3', value: '' },
                 { name: '自定义字段4', value: '' },
                 { name: '自定义字段5', value: '' },
             ],
        };
    ClinkChatApi.setIframeMsg(params);
</script>

3.2. 发送订单

3.2.1. 接入方式说明

在线客服工作台发送订单,需要通过配置工作台右侧边栏的自定义菜单嵌入对应的页面,页面内部引入我们的api,调用api触发发送订单事件,即可给当前选择的访客会话发送订单

3.2.2. 接入步骤

使用管理员账号登录-系统设置-在线客服-系统对接-自定义tab页,点击新建创建一个自定义tab,地址指到需要触发发送订单效果页面的url。(如果您的列表中没有可用的自定义菜单,请联系售后支持帮您进行配置) image

再在所配置的页面接入我们在线的api,调用setIframeMsg方法传入订单数据,即可

如下示例:

<!-- 引入AICC在线客服api -->
<script src="https://chat-ws-bj.clink.cn/chat-commodity-card.js"></script>
<script>
  // 发送订单
  var params = {
    cardType: '3',
    orderInfo: {
      "title": "XX专营店",
      "logo": "https://xxx.com/image/xx.jpeg",
      "time": "xx年xx月xx日 00:00:00",
      "number": "1234**2457000",
      "status": "待收货",
      "tag": {
        "text": "标签",
        "style": {
          "fontWeight": "bold",
          "color": "red",
          "border": "1px solid red",
        }
      },
      "bottomButtonConfig": {
        "text": "共计120件,查看更多",
        "type": "link",
        "linkUrl": "https://xxx.com/",
        "target": "",
        "appId": "",
        "pagePath": "",
        "style": {}
      },
      "productList": [
        {
          "title": "xxx商品",
          "subtitle": "商品说明",
          "remark": "商品描述",
          "amount": "x5",
          "price": "$2999",
          "img": "https://xxx.com/image/xxx.jpeg",
          "imgTag": "敬请期待",
          "imgTagStyle": {
            "color": "red"
          },
          "agentNavigateTabName": "预览页面",
          "visitorUrl": "https://xxx.com/",
          "agentUrl": "https://xxx.com/",
          "appId": "3qwrq***o43op",
          "pagePath": "/xxx/path",
          "status": "待收货",
        }
      ]
    }
  };
  ClinkChatApi.setIframeMsg(params);
</script>

以上代码触发时效果如下
image

3.2.3. 传入数据说明

若订单数据只传productList,则显示为商品列表。productList内只放入一个商品数据,则显示为一个商品

OrderInfo订单数据
名称 是否必须 类型 说明

title

string

店铺名称
示例: XX专营店

logo

string

店铺头像
示例: https://xxx.com/image/xx.jpeg

time

string

订单时间,具体时间
示例: xx年xx月xx日 00:00:00

number

string

订单编号
示例: 1234**2457000

status

订单状态

订单状态,具体文字状态
示例: 待收货

tag

TagConfig

订单标签,TagConfig见其他格式说明

bottomButtonConfig

ButtonConfig

订单底部按钮,只支持链接模式,建议一个订单包含商品较多的情况,这里只传递有限的几个商品数据,其他商品采用此按钮配置链接跳转到查看该订单所有商品的页面。ButtonConfig见其他格式说明
示例: { "text": "共计120件,查看更多", "type": "link", "linkUrl": "https://xxx.com/", "target": "", "appId": "", "pagePath": "", "style": {} }

productList

Product[]

商品列表数组,Product表示单个商品,说明Product

Product

单个商品的数据格式

名称 是否必须 类型 说明

title

string

商品标题
示例: xxx商品

subtitle

string

商品副标题
示例: 商品说明

remark

string

描述,展示为标签样式
示例: 商品描述

amount

string

商品数量,非数字,可携带单位
示例: x5

price

string

商品价格,非数字,可携带单位
示例: $2999

img

string

商品图片url
示例: https://xxx.com/image/xxx.jpeg

imgTag

string

商品标签,对应商品图片左上角标签文字,建议小于等于4个中文
示例: 敬请期待

imgTagStyle

StyleType

商品标签样式,StyleType见其他格式说明
示例: { "color": "red" }

agentNavigateTabName

string

只在在线客服工作台座席端才起作用,客服点击则定位到工作台右侧边栏同名的 tab 打开
示例: 预览页面

visitorUrl

string

访客点击后打开的链接
示例: https://xxx.com/

agentUrl

string

客服点击后打开的链接
示例: https://xxx.com/

appId

string

访客点击跳转目标小程序的appId,适用于小程序嵌入H5和公众号嵌入H5,只在访客端生效,和pagePath一起传才会触发小程序跳转,优先级高于url
示例: 3qwrq***o43op

pagePath

string

访客点击跳转目标小程序的路由地址,适用于小程序嵌入H5和公众号嵌入H5,和appId一起传才会触发小程序跳转,优先级高于url
示例: /xxx/path

status

string

商品状态
示例: 待收货

其他格式说明

以下是具体的ts类型

export type ExtraDataType = string; // json
export enum ButtonType {
    LINK = 'link',
}
export interface NameValue {
    label: string;
    value: string;
}
export interface StyleType {
    fontWeight?: string; // 字重
    color?: string; //字体颜色
    border?: string; //边框
    background?: string; //背景
}
export interface TagStyleType {
    fontWeight?: string; // 字重
    color?: string; //字体颜色
    border?: string; //边框
}
export interface TagConfig {
    text: string;
    style?: TagStyleType;
}
export interface ButtonConfig {
    type: ButtonType;
    text: string;
    style?: StyleType;
    // ------------link类型字段--------------
    linkUrl?: string;
    target?: string; // a标签跳转的类型
    // link类型需要跳转小程序字段,需要跳转小程序时以下两个字段都必须传
    appId?: string;
    pagePath?: string;
}
export interface Product {
    title: string;
    subtitle?: string;
    remark?: string;
    amount?: string;
    price?: string;
    img: string;
    imgTag?: string;
    imgTagStyle?: StyleType;
    agentNavigateTabName?: string;
    visitorUrl?: string;
    agentUrl?: string;
    appId?: string;
    pagePath?: string;
    status?: string;
    extraData?: ExtraDataType;
}

export interface Order {
    title?: string;
    logo?: string;
    time?: string;
    number?: string;
    status?: string;
    bottomButtonConfig?: ButtonConfig;
    extraData?: ExtraDataType;
    productList: Product[];
}

3.3. 发送物流卡片

通过自定义菜单嵌入在线客服,使用api发送卡片消息

<!-- 引入AICC在线客服api -->
<script src="https://chat-ws-bj.clink.cn/chat-commodity-card.js"></script>
<script>
    // 参数说明:
    // card: 卡片信息
          // {
          //     cardType:  1, 物流卡片type值  (必填)
          //     createTime: "", 下单时间 (必填)
          //     goodsName: "", 商品名称 (必填)
          //     senderName: "", 寄件人姓名 (必填)
          //     recipientName: "", 收件人姓名 (必填)
          //     goodsAmount: "", 商品金额 (必填)
          //     goodsQuantity:"", 商品数量 (必填)
          //     orderName:"",订单链接 (必填)
          //     orderNumber: "", 订单编号 (非必填)
          // };
    var params = {
            cardType: "1",
            createTime: "承运日期:4月11日",
            goodsName: "商品名称:石子",
            senderName: "梅州蕉岭-文化厂",
            recipientName: "汕头潮阳-朝阳区梅花站",
            goodsAmount: "58.00元/吨",
            goodsQuantity: "可接一车",
            orderLink: "https://www.baidu.com",
            orderNumber: "订单号:800-89741258",
        };
    ClinkChatApi.setIframeMsg(params);
</script>

例图:

image

3.4. 发送小程序卡片

通过自定义菜单嵌入在线客服,使用api发送卡片消息

注意事项:发送小程序卡片必须微信公众号和小程序关联(若想通过复制链接获取小程序链接需开启小程序链接管理权限)

<!-- 引入在线客服api -->

<script src="https://chat-ws-bj.clink.cn/chat-commodity-card.js"></script>
<script>
// 参数说明:
// card: 卡片信息
    // {
    //     cardType:  2, 卡片type值  (必填)
    //     title: "",  小程序标题(必填)
    //     pagepath: "", 点击卡片跳转的路由 (必填)
    //     query: "", 点击卡片跳转的路由需要携带的参数 (选填)
    //     miniProgramUrl: "", 点击卡片跳转的完整链接 (选填,例如 https://wxaurl.cn/09qdSUvAaaa)
    //     picurl: "", 小程序展示图 (必填,图片推荐大小为810px * 410px)
    //     appid: "", 小程序appId (必填)
    //     appSecret: "", 小程序appSecret (选填,会刷新小程序token,有冲突慎用)
    //     appLogo: "", 小程序logo (必填,推荐图片长宽比例是5:4,推荐的logo大小可以是50px40px或100px80px)
    //     appName:"", 小程序名称 (必填)
    // };
    //访客端URL点击执行优先级
    //网页:miniProgramUrl > appSecret获取的URL > pagepath+query
    //微信小程序、公众号、小程序嵌入H5、公众号嵌入H5:pagepath+query
    //访客端URL复制执行优先级
    //网页、小程序嵌入H5、公众号嵌入H5:miniProgramUrl > appSecret获取的URL > pagepath+query
    var params = {
        cardType: '2',
        title: '欢迎访问',
        pagepath: 'pages/index/index',
        query: 'id=1&name=zs',
        miniProgramUrl: 'https://wxaurl.cn/FX8cifcl49',
        picurl: 'https://img2.baidu.com/it/u=1229468480,2938819374&fm=253&fmt=auto&app=120&f=JPEG?w=500&h=500',
        appid: 'appid',
        appSecret: 'appSecret',
        appLogo:'https://img2.baidu.com/it/u=1229468480,2938819374&fm=253&fmt=auto&app=120&f=JPEG?w=500&h=500',
        appName: '小程序测试'
    };
    ClinkChatApi.setIframeMsg(params);
</script>

3.5. 发送文本消息

通过自定义菜单嵌入在线客服,使用api发送文本消息

<!-- 引入在线客服api -->

<script src="https://chat-ws-bj.clink.cn/chat-commodity-card.js"></script>
<script>
    // 参数说明:
    // text: 文本信息
          // {
          //     type:  "sendText", 消息类型:文本消息 (必填)
          //     content: "", 消息内容 (必填)
          // };
    var params = {
            type: "sendText",
            content: "文本消息",
        };
    ClinkChatApi.setIframeMsg(params);
</script>

4. 小程序

4.1. 小程序自定义参数

需要将button组件open-type的值设置为contact,设置session-from属性进行自定义传参

<button session-from="{{sessionForm}}" open-type="contact">
        在线咨询
</button>

对sessionForm进行赋值(JSON字符串格式)

data: {
    sessionForm: JSON.stringify({
        "city":"成都",
        "customerFields":{"电话":"18408246666","autoUpdate":1}, //非必需。如果参数中有autoUpdate=1的传值,则在打开自动创建客户的前提下会根据该字段传参更新客户资料。
        "externalId":"12345678765",
        "visitorName":"test123",
        "headImgUrl": "https://www.ti-net.com.cn/uploadfile/2022/0330/20220330053334129.png",
        "province":"四川",
        "tel":"18408249897",
        "visitorExtraInfo":{"拓展信息key":"拓展信息value"},
        "visitorTag":["标签"]
    })
}

4.2. 访客发送小程序卡片

需要将button组件open-type的值设置为contact,设置send-message-title、send-message-img、show-message-card属性进行发送小程序卡片

<button send-message-title="{{appletInfo.title}}"
        send-message-img="{{appletInfo.img}}"
        show-message-card="{{true}}"
        open-type="contact">
        在线咨询
</button>

对send-message-title、send-message-img进行赋值

data: {
    appletInfo: {
      title: "测试",
      img: "https://img2.baidu.com/it/u=1229468480,2938819374&fm=253&fmt=auto&app=120&f=JPEG?w=500&h=500"
    }
}

5. 机器人

5.1. 机器人商品列表参数说明

将卡片信息填入对应属性,即可通过机器人发送商品列表消息体,参照示例代码如下:

// 参数说明:
// card: 卡片信息,机器人参数,由于机器人限制  卡片配置项在data内,选传,位置固定在data尾部,可不传
//   {
//        text: '',       引导语文本
//        data: [         订单列表
//           {
//             orderNumberId: '',        订单编号
//             orderStatus: '未付款',     订单状态 -- 付款、未付款
//             orderTime: '2023-11-01',  订单时间
//             orderTimeStyle: {'font-size': '14px', 'color': '#4385ff', 'font-weight': 700},  订单时间样式
//             statusColor: '#666',      订单状态颜色
//             productPic:  '',          商品图片
//             productName: '',          商品名称
//             productNumber: '',        商品数量
//             productNumberStyle: '',   商品数量样式
//             productPrice: '',         商品价格
//             productPriceStyle: '',    商品价格样式
//             productBgColor: '',       商品背景色
//             productDescription: '',   商品描述
//             clickInteraction: '1',    点击交互效果 1-- 跳转  2-- 发送文本
//             subUrl: '',               座席端商品卡片跳转链接
//             productLink: '',          商品跳转链接
//             replyText: '',            回复文本
//           },
//           {
//             timeReplyText: '',        触发订单时间查询关键词
//             showTimeSelect: '1'       是否显示时间选择  1-- 显示  0-- 不显示
//           }
//       ]
//     }

5.2. 发送图文消息

将图文消息信息填入对应属性,即可通过机器人发送商品列表消息体,参照示例代码如下:

// 参数说明:
// messageType:32         32代表机器人图文消息
// content:  JSON.stringify({
//              title: '',      图文消息标题
//              description: '',图文消息描述
//              url:'',         图文消息跳转链接
//              picurl:'',      图文消息图片链接
//            })

5.3. 发送清单

5.3.1. 天润后台机器人

使用管理员账号登录,选择 机器人 菜单,寻找到列表中您使用的机器人(如果您的列表中没有可用的机器人或有疑问,请联系售后支持帮您进行配置)。
以下两种方式都可以配置发送清单消息

1、选择 FAQ管理 并点击新增,如下图:

image

2、选择 多轮会话 ,并点击新增,新增后点击配置进入配置页面,节点选择文本话术,如下图

image image image

两种方式都需要在选中纯文本的情况下再填入清单数据格式的json字符串(即需要通过JSON.stringify转义后的json字符串,但写入文本输入框时注意不要带入字符串的前后引号)
保存后,训练机器人并发布即可根据配置的问题触发对应的回答

5.3.2. 第三方机器人

对接详见:机器人API开发指南

机器人需要推送过来的数据格式如下,text具体内容data见清单数据格式

{
	"data": [
    {
      "reply": {
        "type": "TEXT",
        "text": JSON.stringify(data) // 转json字符串
      }
    }
  ]
}

5.3.3. 清单数据格式

注意:

在传递以下参数时,应该将以下对象通过JSON.stringify转化为json字符串再进行传递

名称 是否必须 类型 说明

type

string

'checklist' ,标识清单卡片

title

string

顶部富文本,自行控制显示内容
示例: '<div><p>您当前未结清合同总结:<span style="color: orange">*</span>笔</p><p>剩余未还金额为:<span style="color: orange">****元</span></p></div>'

subTitle

string

小标题,纯文本
示例: '请选择订单进行咨询:'

background

string

背景设置,同css样式background属性,不建议配置图片
示例: 'linear-gradient(210.2deg, #FFF2E7 0%, #FFFFFF 100%);'

pageSize

number

一页展示数量,默认3,传0则不分页
示例: 3

cardList

cardType[]

卡片列表 , 具体格式见cardType

cardType
名称 是否必须 类型 说明

number

string

序号
示例: '1'

style

string

卡片独立样式,同css样式,支持设置字体大小、颜色、字体加粗等,默认根据主题配置样式来
示例: 'color: red'

extraData

string

json数据,不做处理,可用于发送卡片携带给机器人消息识别
示例: ''

infoList

infoType[]

卡片信息列表数据,具体格式见infoType

infoType
名称 是否必须 类型 说明

name

string

名称
示例: '编号'

value

string

值或内容
示例: 'BH00xxx02'

示例:

转化前的数据

  {
    type: "checklist",
    title: `<div><p>您当前未结清合同总结:<span style="color: orange">*</span>笔</p><p>剩余未还金额为:<span style="color: orange">****元</span></p></div>`,
    subTitle: "请选择订单进行咨询:",
    background: "linear-gradient(210.2deg, #FFF2E7 0%, #FFFFFF 100%);",
    pageSize: 3,
    cardList: [
      {
        number: "1",
        style: "color: red",
        extraData: "",
        infoList: [
          {
            name: "编号",
            value: "BH00xxx01"
          },
          {
            name: "金额",
            value: "xx元"
          },
          {
            name: "日期",
            value: "****年**月*日"
          }
        ]
      },
      {
        number: "2",
        style: "",
        extraData: "",
        infoList: [
          {
            name: "编号",
            value: "BH00xxx02",
          },
          {
            name: "金额",
            value: "xx元"
          },
          {
            name: "日期",
            value: "****年**月*日"
          }
        ]
      }
    ]
  }

写入文本输入框的JSON.stringify转化后的数据

{"type":"checklist","title":"<div><p>您当前未结清合同总结:<span style=\"color: orange\">*</span>笔</p><p>剩余未还金额为:<span style=\"color: orange\">****元</span></p></div>","subTitle":"请选择订单进行咨询:","background":"linear-gradient(210.2deg, #FFF2E7 0%, #FFFFFF 100%);","pageSize":3,"cardList":[{"number":"1","style":"color: red","extraData":"","infoList":[{"name":"编号","value":"BH00xxx01"},{"name":"金额","value":"xx元"},{"name":"日期","value":"****年**月*日"}]},{"number":"2","style":"","extraData":"","infoList":[{"name":"编号","value":"BH00xxx02"},{"name":"金额","value":"xx元"},{"name":"日期","value":"****年**月*日"}]}]}

以上代码触发时效果如下

image

点击其中一个卡片即可发送单独的卡片消息

image