您好,欢迎接入Udesk WebIM网页插件, 以下提供基本用法、注意事项、参数说明、方法说明、事件说明、相关示例等。
基本用法
登录Udesk客服系统管理员账号,在 管理中心 -> 渠道管理 -> 即时通讯 -> 网页插件 -> (某一插件)管理,配置基本属性后复制代码到网页,即可启用Udesk IM。
注意事项
设备支持
WebIM目前支持PC/Android(含平板)/IOS(含平板)等设备。
浏览器兼容性
WebIM目前支持IE8+(含)的IE浏览器与基于它内核(360/qq/...)、其他主流浏览器(chrome/firefox/...)。
基本示例
内嵌代码示例
<body>
<!-- 网页内容 具体代码请在管理员即时通讯网页插件处复制 -->
<script>
(function(a,h,c,b,f,g){a["UdeskApiObject"]=f;a[f]=a[f]||function(){(a[f].d=a[f].d||[]).push(arguments)};g=h.createElement(c);g.async=1;g.src=b;c=h.getElementsByTagName(c)[0];c.parentNode.insertBefore(g,c)})(window,document,"script","https://xxx.xxxx.cn/im_client/js/udeskApi.js?1487931175918","ud");
ud({
"code": "xxxx",
"link": "https://xxx.xxxx.cn/im_client/?web_plugin_id=1",
"isInvite": true,
"mode": "inner",
"color": "#307AE8",
"pos_flag": "srb",
"language": "en-us", //语言-英文
"onlineText": "联系客服,在线咨询",
"offlineText": "客服下班,请留言",
"mobile": { //为响应式布局,提供pc、mobile自定制
"mode": "blank",
"color": "#307AE8",
"pos_flag": "crb",
"onlineText": "联系客服,在线咨询",
"offlineText": "客服下班,请留言"
}
});
</script>
</body>
链接示例
https://xxx.xxxx.cn/im_client/?web_plugin_id=xx&language=zh-cn&agent_id=xxx&group_id=xxx
自定义配置
开发者可以根据自己的需求自定义相关配置,所有属性、事件都在
ud({...})
里面进行配置,所有方法通过ud(methodName, ...arguments)
的方式调用。
配置方式
1. ud({key: val, key2: val2, ...});
2. ud(key, val);
参数说明
全局参数
参数名称 | 类型 | 值 | 是否必需 | 说明 |
---|---|---|---|---|
code | String | 系统生成 |
是 | 公司唯一标识 |
link | String | 系统生成 |
是 | 公司IM Client链接地址 |
isInvite | Boolean | true或false(默认) | 否 | 是否开启访客邀请功能管理台可配 |
agent_id | String | 指定客服ID,默认不指定 | 否 | 指定会话的客服人员 |
group_id | String | 指定客服组ID,默认不指定 | 否 | 指定会话的客服组 |
session_key | String | 6位随机字符串 | 否 | 同一个客户,不同的sesstion_key会触发新的会话. 仅支持字母、数字及下划线,禁用特殊字符 |
merchant_id | String | 商户ID | 否 | 若传递了merchant_id,那么session_key、merchant_id均需要传输同一个商户ID 会话逻辑:传了存相关商户参数,不打乱现有会话顺序,机器人、询前表单、留言等,仅在人工会话时保存参数并进行显示替换 |
merchant_name | String | 商户名称 | 否 | 与该客服的会话中【客服介绍】会替换显示为【商户名称】 |
merchant_avatar | String | 商户头像 | 否 | 与该客服的会话中【客服头像】会替换显示为【商户头像】 |
merchant_list | Boolean | true或false(默认) | 否 | 当网页插件链接中merchant_list=true时网页插件加载出当前客户历史会话中所有的商户,按照每个商户最新的会话时间排序 |
mode | String | inner:网页内嵌模式(默认) blank:独立窗口模式 |
否 | 按钮打开窗口显示模式管理台可配 |
color | String | 按钮颜色代码,默认为:#307AE8 | 否 | 配置IM按钮颜色管理台可配 |
pos_flag | String | 按钮形状+位置 1)形状: h(横向) v(纵向) c(圆形) i(图片); 2)位置: lt(左上) mt(中上) rt(右上) lm(左中) mm(正中) rm(右中) lb(左下) mb(中下) rb(右下) 默认值:crb(pc),hrb(mobile) |
否 | 按钮形状+位置 如:vrb(纵向右下角) 图片配置路径:管理中心-渠道管理-即时通讯-网页插件-管理-按钮设置 |
onlineText | String | 客服在线显示文本,默认为“联系客服,在线咨询” | 是 | 管理台可配 |
offlineText | String | 客服离线显示文本,默认为“客服下班,请留言” | 是 | 管理台可配 |
noBubble | Boolean | true或false(默认) | 否 | 不显示消息弹出气泡提醒 |
manualInit | Boolean | true或false(默认) | 否 | 手动初始化,设置为true时,嵌入代码不会自动初始化,需要主动调用ud('init')方法 |
language | String | "zh-cn"(表示中文)、"en-us"(表示英文)等 | 否 | 配置客户端多语言[详情] |
channel | String | 自定义渠道 | 否 | 管理台可配 |
targetSelector | String | 支持#id,.class,tagName,...默认系统生成 唯一 |
否 | 主目标按钮选择器 1) 用于触发聊天窗口 2) 用于 代替 系统默认提供的按钮,指定有效后默认按钮不再显示 3) 用于接收邀请 4) 用于接收新消息提醒 5) ... |
selector | String | 支持#id,.class,tagName,... 默认无 可多个 |
否 | 辅助按钮选择器 1) 用于触发聊天窗口 |
css | Object | 按钮自定义样式,如:css:{top:10%},默认无 | 否 | |
window | Object | 仅独立窗口模式 独立窗口默认大小为宽780,高560 例 window: {"width": "780", "height": "560"} |
否 | 配置打开独立窗口大小 |
panel | Object | 仅网页内嵌模式 |
否 | 会话面板 [详情] |
pop | Object | 否 | 消息提醒气泡 [详情] | |
customer | Object | 否 | 客户信息 [详情] | |
im_client_valid | {v_nonce:string,v_timestamp:string,v_signature:string} | 否 | 请求权限校验开通时必传 [详情] | |
product | Object | 否 | 咨询对象 [详情] | |
hide_product | Boolean | 否 | 参数为true时, 会隐藏客户端咨询对象的显示,客服端仍会显示 | |
mobile | Object | 否 | 移动端相关配置 [详情] 当网站为响应式布局,提供pc、mobile自定制 |
|
robot_modelKey | String | 机器人常见问题模板ID | 否 | 根据机器人常见问题模板ID调用常见问题模板,在‘Km-机器人-FAQ对话-知识库-常见问题-ID’可查看 |
c_cf_dialogueDesc | String | 自定义值,支持用逗号分隔多个值 | 否 | 此字段的作用是满足客户给机器人传任何自定义信息的需求 用法:首先要在客服系统里的客户自定义字段内,设置一个名字叫做c_cf_dialogueDesc的自定义字段。设置客户该字段值之后,打开web IM网页插件,将该字段添加到路径参数里,格式如下 https://xxx.udesk.cn/im_client/?web_plugin_id=41597&c_cf_dialogueDesc=* 这样系统会自动将c_cf_dialogueDesc的参数值传到机器人中,机器人的对话描述字段接收此值 |
uba | {app_key: string} | user behavior analysis 配置 | 否 | 例子:ud({uba:{app_key: 'xxxxxxx'}}) |
src_url | String | http URL地址 | 否 | 来源URL |
示例
ud({
"code": "xxxx",
"link": "https://xxx.xxxx.cn/im_client/",
"mode": "inner",
"color": "#307AE8",
"pos_flag": "srb",
"onlineText": "联系客服,在线咨询",
"offlineText": "客服下班,请留言",
"targetSelector": "#btn_udesk_im",
"selector": ".udesk_im",
"pop": {},
"panel": {},
"mobile": {},
"window": {}
});
> 移动端 - 参数说明
参数名称 | 类型 | 值 | 是否必选 | 说明 |
---|---|---|---|---|
mode | String | inner:网页内嵌模式(默认) blank:独立窗口模式 |
否 | 按钮打开窗口显示模式管理台可配 |
color | String | 按钮颜色代码,默认为:#307AE8 | 否 | 配置IM按钮颜色管理台可配 |
pos_flag | String | 按钮形状+位置 1)形状: h(横向) v(纵向) c(圆形) i(图片); 2)位置: lt(左上) mt(中上) rt(右上) lm(左中) mm(正中) rm(右中) lb(左下) mb(中下) rb(右下) 默认值:crb(pc),hrb(mobile) |
否 | 按钮形状+位置 如:vrb(纵向右下角) 图片配置路径:管理中心-渠道管理-即时通讯-网页插件-管理-按钮设置 |
onlineText | String | 客服在线显示文本,默认为“联系客服,在线咨询” | 是 | 管理台可配 |
offlineText | String | 客服离线显示文本,默认为“客服下班,请留言” | 是 | 管理台可配 |
noBubble | Boolean | true或false(默认) | 否 | 不显示消息弹出气泡提醒 |
language | String | "zh-cn"(中文) 或 "en-us"(英文) | 否 | 配置客户端多语言 |
targetSelector | String | 支持#id,.class,tagName,...默认系统生成 唯一 |
否 | 主目标按钮选择器 1) 用于触发聊天窗口 2) 用于 代替 系统默认提供的按钮,指定有效后默认按钮不再显示 3) 用于接收邀请 4) 用于接收新消息提醒 5) ... |
selector | String | 支持#id,.class,tagName,... 默认无 可多个 |
否 | 辅助按钮选择器 1) 用于触发聊天窗口 |
css | Object | 按钮自定义样式,如:css:{top:10%},默认无 | 否 | |
pop | Object | 否 | 消息提醒气泡 [详情] |
示例
ud({
"mobile", {
"mode": "blank",
"color": "#307AE8",
"pos_flag": "crb",
"onlineText": "联系客服,在线咨询",
"offlineText": "客服下班,请留言",
"pop": {
"direction": "top",
"arrow": {
"top": 0,
"left": "70%"
}
}
}
});
> 会话面板 - 参数说明
参数名称 | 类型 | 值 | 是否必选 | 说明 |
---|---|---|---|---|
css | Object | 自定义样式,如:css:{top:10%},默认无 | 否 | 面板位置只支持 top、left、right |
onToggle | Function | 自定义方法 参数:data.visible(true或false)}, |
否 | 检测当前会话面板是否打开,visible为true时则是打开状态 |
示例
ud({
"panel": {
"css": {
"top": "0",
"left": "0"
},
"onToggle": function(data) { //面板开关时间
if (data.visible) {
//console.log('窗口打开');
}
else{
//console.log('窗口关闭');
}
}
}
});
> 消息提醒气泡 - 参数说明
参数名称 | 类型 | 值 | 是否必选 | 说明 |
---|---|---|---|---|
css | Object | 自定义样式,如:css:{top:10%},默认无 | 否 | |
direction | String | 气泡弹出方向,"top"(默认);"right";"left";"bottom" | 否 | |
offset | Object | 气泡位移量,{top:0,left:0},默认相对居中显示 | 否 | |
arrow | Object | 气泡箭头配置,{top:0,left:0},默认相对居中显示 | 否 |
示例
ud({
"pop": {
"direction": "top",
"arrow": {
"top": 0,
"left": "70%"
}
}
});
> 客户信息 - 参数说明
概述
网站登录用户通过web im进行聊天对话时,可以将客户信息通过链接转给Udesk,这样客服在与客户对话时,就可以直接查看客户信息。通过客户信息辅助组件也可以进一步了解此客户的订单等其他信息。
使用方法
在web im对话窗口的链接(https://xxxx.udesk.cn/im_client/)按照以下规则增加参数和加密信息,即可传输客户信息。
其中邮箱和电话号码用于客户识别,即与Udesk系统内CRM中已有数据进行比对,如果为已有客户会更新信息,如果没有匹配则会新建客户。
> 客户身份认证 - 请求参数
传递客户信息、业务记录需要做身份认证 signature加密算法,如果不需要传入这些数据,就不需要做身份认证;
客户身份识别有效时间: 4天;
客户身份的识别顺序: web_token > customer_token > c_email > c_phone;
如果传入的web_token或customer_token在系统中没有;但是传入的c_email或c_phone已存在,系统仍然会识别为原有客户。 如果只传入web_token,不传入customer_token、c_email、c_phone等身份数据,则web_token只匹配客户的web_token一项数据,如未匹配到则创建新客户。
参数名称 | 类型 | 值 | 是否必选 | 说明 |
---|---|---|---|---|
nonce | String | 随机数 | 是 | 随机数,动态的比静态随机数安全系数更高 |
timestamp | String | 时间戳 | 是 | 当前时间戳(13位毫秒) |
web_token | String | 客户ID | 是 | 客户唯一标示,推荐使用邮箱、手机号等 仅支持字母、数字及下划线,禁用特殊字符 |
customer_token | String | 客户外部ID | 否 | 客户外部唯一标识 等同于open_api_token 仅支持字母、数字及下划线,禁用特殊字符 |
signature | String | 加密签名 | 是 | [signature加密算法] |
encryption_algorithm | String | SHA1 或者SHA256 |
否 | 加密算法,不填写默认为SHA1 推荐使用SHA256 |
signature加密算法(三步)
1、按后面参数及顺序拼接字符,以key=value&形式: nonce
、timestamp
、web_token
、im_user_key
im_user_key获取位置【管理中心-即时通讯-网页插件-管理/添加客户信息中的KEY】
sign_str = nonce=value×tamp=value&web_token=value&im_user_key
2、 使用加密算法计算出签名字符串
sign_str = sha1(sign_str)
或者 sign_str = sha256(sign_str)
3、 将字符串转换为大写
sign_str = sign_str.toUpperCase()
4、易错提醒
1、 im_user_key无需参数仅需要值即可 2、 signature字符串要转换为大写
SHA1示例:
sign_str = "nonce=9ca6fff5a509fb887ac72cf5c92010e7×tamp=1455675719000&web_token=test@udesk.cn&b476f9f8-5309-4d0a-a2d4-af08c4507a15";
sign_str = sha1(sign_str);
sign_str = sign_str.toUpperCase();
示例
// SHA1示例
ud({
"customer": {
"nonce": "9ca6fff5a509fb887ac72cf5c92010e7",
"signature": "9B2619225AA6476DC1EB80DBB8801E1575EBE39C",
"timestamp": "1455675719000",
"web_token": "test@udesk.cn"
}
});
// SHA256示例
ud({
"customer": {
"nonce": "9ca6fff5a509fb887ac72cf5c92010e7",
"signature": "4CC40FA2D762DAB1D4509750A7135123743D26F2552B7E23611AB8CB5D35825B",
"timestamp": "1455675719000",
"web_token": "test@udesk.cn",
"encryption_algorithm": "SHA256"
}
});
客户参数
参数名称 | 类型 | 值 | 是否必选 | 说明 |
---|---|---|---|---|
c_name | String | 客户姓名 | 否 | |
c_email | String | 客户邮箱唯一 |
否 | |
c_phone | String | 客户电话唯一 |
否 | |
customer_token | String | 客户外部标识唯一 |
否 | 与open_api_token同一字段 |
c_desc | String | 客户描述 | 否 | |
c_org | String | 公司名称 | 否 | 若开通多公司,则支持传多个,用英文逗号分隔 如:"帅气,漂亮";未开通则仅取第一个 |
c_tags | String | 客户标签 | 否 | 传入客户标签,用逗号分隔 如:"帅气,漂亮" |
c_owner | String | 客户负责人ID | 否 | |
c_vip | String | 'vip'(vip客户) 或者 'normal'(普通客户) | 否 | 客户vip识别 |
c_owner_group | String | 客户负责组ID | 否 | |
c_other_emails | String | 客户其他邮箱列表 | 否 | 逗号分隔 如:"a@xxx.cn,b@xxx.cn" |
c_cf_<自定义字段名称> | String | 客户自定义字段 | 否 | 客户自定义字段 如: c_cf_名字、c_cf_age、... |
> 业务记录 - 请求参数
参数名称 | 类型 | 值 | 是否必选 | 说明 |
---|---|---|---|---|
c_cn_title | String | 业务记录主题 | 否 | 传入业务记录主题 |
c_cn_<自定义字段名称> | String | 业务记录自定义字段 | 否 | 业务记录自定义字段 如: c_cn_名字、c_cn_age、... |
示例
ud({
"customer": {
"c_name": "客户姓名kimi",
"c_email": "newest3@udesk.cn",
"c_other_emails": '11@udesk.cn,22@udesk.cn',
"c_desc": "意向客户,潜力巨大",
"c_org": "desc",
"c_phone": "1100110012",
"c_tags": "英俊,新加的",
"c_owner_group": "62",
"c_owner": "3",
"c_vip": "vip",
"c_qq": "123123",
"c_cf_年龄": 10,
"c_cf_爱好": "极限挑战",
"c_cn_title":"业务记录主题",
"c_cn_名字":"业务记录的名字",
"nonce": "694db2645b3f69a8",
"signature": "315345C77C73A128CF9850EAD777F7A71D423A36",
"timestamp": "1465878579000",
"web_token": "newest3@udesk.cn"
}
});
链接示例
所传的参数请编码处理,否则会出现乱码!
https://xxx.udesk.cn/im_client/?c_desc=test123456789&c_email=jiangst%40udesio.com&c_phone=15888888888&echostr=hello&nonce=cdebd5d13f4d331e&signature=693140534BDDDAF002A46F0027E06244DF0B21AB×tamp=1457696747000&web_token=42f6607e-8892-4fcf-889b-f9babf627060
> 工单信息 - 请求参数
参数名称 | 类型 | 值 | 是否必选 | 说明 |
---|---|---|---|---|
t_subject | String | 工单主题 | 否 | 工单主题 |
t_content | String | 工单描述 | 否 | 传值后会带入页面,覆盖留言内容模板 |
t_priority_id | String | 工单优先级 | 否 | t_priority_id取值范围 1(紧急),2(高), 3(标准), 4(低) 详情 |
t_status_id | String | 工单状态 | 否 | t_status_id取值范围 1(开启),2(已解决), 3(已关闭), 4(解决中) 详情 |
t_tags | String | 工单标签 | 否 | 传入工单标签,用逗号分隔 如:"tag1,tag2,tag3" |
t_cf_<自定义字段名称> | String | 工单自定义字段 | 否 | 工单自定义字段,通过api获取字段详情列表 如: t_cf_TextField_213、t_cf_SelectField_225 详情 |
示例
ud({
"ticket": {
"t_subject": "工单主题",
"t_content": "工单描述",
"t_priority_id": '3',//标准
"t_status_id": "1",//开启
"t_tags": "隐约雷鸣,但盼风雨来,能留你在此",
"t_cf_TextField_213": "文本",
"t_cf_TextField_214": "12:00:00",//时间
"t_cf_TextField_215": "2018-10-26",//日期
"t_cf_TextField_215": "2018-10-20 12:00",//时间日期
"t_cf_SelectField_225": "3",//单选或下拉
"t_cf_SelectField_226": "1,2",//复选
"t_cf_SelectField_227": "0,0",//级联
}
});
链接示例
所传的参数请编码处理,否则会出现乱码!
https://xxx.udesk.cn/im_client/?t_priority_id=3&t_status_id=1&t_cf_TextField_124=%E6%96%87%E6%9C%AC%E6%A1%86&t_cf_TextField_135=2018-10-20&t_cf_TextField_136=12:00:00&t_cf_TextField_137=2018-10-20%2012:00&t_cf_TextField_138=3.14&t_cf_TextField_139=250&t_cf_TextField_140=https://www.baidu.com&t_cf_TextField_141=abc&t_cf_TextField_142=%E9%80%9A%E5%8E%BF&t_cf_SelectField_134=1&t_cf_SelectField_135=0,0&t_cf_SelectField_136=0&t_cf_SelectField_137=1,2&t_tags=%E6%B5%8B%E8%AF%951,%E5%BC%A0%E4%B8%89,%E6%9D%8E%E5%9B%9B,%E7%8E%8B%E4%BA%94&c_name=im_client%E6%B5%8B%E8%AF%95%E5%AE%A2%E6%88%B7&c_email=t_im_client@163.com&c_phone=13520361010
> 咨询对象 - 参数说明
提供两种接入方式:url传参方式、javascript接入方式
参数说明
名称 | 值 | 说明 |
---|---|---|
title | String | 标题 |
title_style | String | 标题的样式,例如:color:rgb(50,50,50);font-weight:bold |
url | String | 跳转页的链接地址 |
image | String | 显示图片地址 |
send | true或false | 为true时,咨询对象下方会出现发送按钮,点击发送会将当前咨询对象作为商品消息发送给客服 |
<自定义参数> | 可定义多个自定义参数,可为中文 | |
<自定义参数>_style | String | 自定义参数的样式 |
url传参方式
在所有的参数前面加前缀product_,所传的参数请编码处理,否则会出现乱码!
如:https://udeskdemo.udesk.cn/im_client/?product_send=true&product_title=Apple%20iPhone%207&product_url=http%3A%2F%2Fitem.jd.com%2F3133829.html%3Fcu%3Dtrue%26utm_source%E2%80%A6erm%3D9457752645_0_11333d2bdbd545f1839f020ae9b27f14&product_image=http%3A%2F%2Fimg14.360buyimg.com%2Fn1%2Fs450x450_jfs%2Ft3157%2F63%2F1645131029%2F112074%2Ff4f79169%2F57d0d44dN8cddf5c5.jpg%3Fv%3D1474635884816&product_%E4%BB%B7%E6%A0%BC=%EF%BF%A56189.00&product_%E4%BF%83%E9%94%80%E4%BF%A1%E6%81%AF=%E8%B4%AD%E4%B9%B0%E4%BA%AC%E4%B8%9C%E8%87%AA%E8%90%A5%E7%94%B5%E8%84%91%2C%E6%89%8B%E6%9C%BA%2C%E6%95%B0%E7%A0%81%E5%93%81%E7%B1%BB%E6%8C%87%E5%AE%9A%E4%BA%A7%E5%93%81%2C%E5%AE%9E%E9%99%85%E4%B8%8B%E5%8D%95%E7%BB%93%E7%AE%97%E9%87%91%E9%A2%9D%E6%BB%A1199%E5%85%83%2C%E8%BF%94%E4%BA%AC%E4%B8%9C%E8%87%AA%E8%90%A5%E5%A4%A7%E9%97%B8%E8%9F%B9%E4%B8%9C%E5%88%B8%E4%B8%80%E5%BC%A0.
javascript接入方式
ud({
product: {
title: "Apple iPhone 7",
url: "http://item.jd.com/3133829.html?cu=true&utm_source…erm=9457752645_0_11333d2bdbd545f1839f020ae9b27f14",
image: "http://img14.360buyimg.com/n1/s450x450_jfs/t3157/63/1645131029/112074/f4f79169/57d0d44dN8cddf5c5.jpg",
"价格": "¥6189.00",
"促销价": "¥6188.00"
}
});
> 配置客户端多语言 -详情
从多语言配置中查找已经配置的多语言语言码
多语言功能请单独联系Udesk开通
管理 -> 账户设置-> 多语言支持
语言名称 | 文件名称 | 语言码 | 更新日期 | 操作 |
---|---|---|---|---|
English | reocar_language_translation(en-us).xlsx | en-us | 2017/08/23 14:30 | 下载文件 上传选择文件 更新 |
选取配置的语言码 en-us
作为 language的参数。如果需要使用法语,需要先配置法语的多语言,然后使用多语言支持列表中的法语配置记录
的语言码
zh-cn(简体中文)已经是系统默认,可以直接使用,不需要在多语言支持
中配置
如果language传了一个多语言支持列表
中没有配置的语言码参数,会使用默认的简体中文
前端的静态资源中的显示内容现在支持简体中文和English,不支持自定义语言翻译。如果需要支持更多的语言,请联系我们
多语言语言码配置规定
语言码只能从以下列表中选择:
如果想配置的多语言语言码不在下面列表中, 请联系Udesk
语言名称 | 语言码 |
---|---|
阿拉伯语 | ar |
English | en-us |
西班牙语 | es |
法语 | fr |
日语 | ja |
朝鲜语/韩语 | ko |
泰语 | th |
印度尼西亚语 | id |
繁体中文 | zh-TW |
葡萄牙语 | pt |
俄罗斯语 | ru |
方法说明
方法名称 | 参数 | 说明 |
---|---|---|
init | 无 | 手动初始化,但manualInit 设置为true 有效 如:ud("init") |
hidePanel | 无 | 手动隐藏会话面板,如:ud("hidePanel") |
showPanel | 无 | 手动打开会话面板,如:ud("showPanel") |
dataTrace | type(数据类型), data(数据) | 数据跟踪:包括浏览轨迹、事件等 |
> 商品浏览轨迹上传
方法说明
ud("dataTrace", type, data);
参数说明
参数名称 | 类型 | 值 | 是否必选 | 说明 |
---|---|---|---|---|
type | String | product | 是 | 跟踪类型 |
data | Object | 是 | 跟踪数据 | |
data.name | String | 是 | 商品名称 | |
data.url | String | 否 | 商品跳转链接(新页显示),如果值为空,则不能点击 | |
data.imgUrl | String | 否 | 如果值为空,则不显示 | |
data.params | Object | 否 | 参数列表 | |
data.params.text | String | 否 | 参数文本 | |
data.params.color | String | 否 | 参数颜色值,规定为十六进制值的颜色(比如 #ff0000) | |
data.params.fold | Boolean | 否 | 是否粗体 | |
data.params.break | Boolean | 否 | 是否换行 | |
data.params.size | Number | 默认12 | 否 | 字体大小 |
示例
// 商品浏览轨迹
ud("dataTrace", "product", {
"name": " Apple iPhone X (A1903) 64GB 深空灰色 移动联通4G手机",
"url": "https://item.jd.com/6748052.html",
"imgUrl": "http://img12.360buyimg.com/n1/s450x450_jfs/t10675/253/1344769770/66891/92d54ca4/59df2e7fN86c99a27.jpg",
"params": [
{
"text": "¥6999.00",
"color": "#FF0000",
"fold": false,
"break": false, //break为关键字,ie8下需要引号包裹
"size": 12
}
]
});
> 订单事件上传
订单事件上传需要先进行客户身份认证,再通过以下方法调用
方法说明
ud("dataTrace", type, data);
参数说明
参数名称 | 类型 | 值 | 是否必选 | 说明 |
---|---|---|---|---|
type | String | order | 是 | 跟踪类型 |
data | Object | 是 | 跟踪数据 | |
data.order_no | String | 是 | 订单编号 | |
data.name | String | 是 | 订单名称 | |
data.url | String | 否 | 订单跳转链接 | |
data.price | Number | 是 | 订单价格 | |
data.order_at | Date | 是 | 下单时间 | |
data.pay_at | Date | 否 | 付款时间 | |
data.status | String | 是 | 订单状态: wait_pay(待付款)、paid(已付款)、closed(已关闭) | |
data.remark | String | 否 | 备注, 最大长度为1000字节(中文2个字节) | |
data.consignee_name | String | 否 | 收货人姓名 | |
data.consignee_phone | String | 否 | 收货人电话 | |
data.commodit_num | String | 否 | 商品总数量 | |
commodities | Array | 否 | 商品信息Array(Ojbect) |
commodities商品信息对象
参数名 | 类型 | 必填 | 说明 |
---|---|---|---|
commodit_name | string | 否 | 商品名称 |
commodit_no | string | 否 | 商品编号 |
commodit_count | numeric | 否 | 商品数量 |
commodit_fee | string | 否 | 商品价格 |
示例
ud("dataTrace", "order", {
order_no: '1',
name: '第一个订单',
url: 'http://xxx.xxxx.com/订单链接',
price: 16.8,
order_at: new Date(),
pay_at: new Date(),
status: 'wait_pay',
remark: '备注',
consignee_name: '张三',
consignee_phone: '01012345',
consignee_address: '北京市大兴区',
commodit_num: 88,
commodities: [
{
commodit_name: '牛奶A',
commodit_no: 'NO123456',
commodit_count: 1,
commodit_fee: '46.5'
}
]
});
> 营销会话事件跟踪
方法说明
ud("dataTrace", type, data);
参数说明
参数名称 | 类型 | 值 | 是否必选 | 说明 |
---|---|---|---|---|
type | String | marketing | 是 | 跟踪类型 |
data | Object | 是 | 跟踪数据 | |
data.name | String | 是 | 事件名称 |
示例
// 商品浏览轨迹
ud("dataTrace", "marketing", {
"name": "取消订单"
});
事件说明
参数名称 | 类型 | 值 | 是否必需 | 说明 |
---|---|---|---|---|
onReady | Function | 监听事件,无参数 | 否 | Udesk IM插件初始化完成事件回调 |
onUnread | Function | 监听事件,参数:data:{count:1 //未读消息数} | 否 | 未读消息事件回调 |
示例
ud({
"onReady": function() {
console.log('初始化完成');
},
"onUnread": function(data) {
console.log('未读消息数'+data.count);
}
});
> 按钮位置修改(拖拽)
概述
主要用于第三方修改按钮位置来实现自定义拖拽事件。 修改class为udesk-client-btn的css(top,right,bottom,left)
注意事项
为防止拖拽点击触发展开会话模版,接受邀请等事件,需要: 在document.onmousedown事件中调用ud('noDrag'); 在document.onmousemove事件中调用ud('isDrag')
示例
- 修改按钮位置: 客服系统后台->渠道管理->即时通讯->某网页插件(点击管理)->按钮设置(右上角)
- 拖拽修改方式: 用户自己实现,可使用原生的mousemove和mouseup方法,根据自身的实际业务逻辑实现
窗口设置 - 信息区自定义URL
当使用udesk网页插件,并且在网页插件的窗口设置中配置了自定义URL时,可以在配置的页面内给Udesk客服/机器人发送消息.
基本用法
自定义URL 页面通过 postMessage 和父级页面通信, 需遵循特定的消息格式
客服聊天界面: 可以发送文本消息和商品消息
机器人聊天界面: 只支持发送文本消息
当前窗口网页链接传入自定义的iframe
窗口会将当前网页链接的参数传入自定义URL的iframe.(其中这四个参数cur_url/cur_title/pre_url/src_url不传).
参数说明
参数名称 | 类型 | 值 | 是否必填 | 说明 |
---|---|---|---|---|
method | String | 'sendMsg' | 是 | 方法名称,目前只支持sendMsg |
type | String | 'text' or 'product' | 是 | 消息类型: 文本消息 或 商品消息 |
content | String | Object | 是 | 消息内容 |
示例
1. 文本消息
var data = JSON.stringify({
method: "sendMsg", // 固定method
type: "text", // 固定type
content: “xxx” // 需要发送的消息
});
var origin = "*";
window.parent.postMessage(data, origin);
2. 商品消息 参数详情
var data = JSON.stringify({
method: "sendMsg", // 固定method
type: "product", // 固定type
content: { // 商品消息格式
name: "Apple iPhone X (A1903) 64GB 深空灰色 移动联通4G手机",
url: "https://item.jd.com/6748052.html",
imgUrl: "http://img12.360buyimg.com/n1/s450x450_jfs/t10675/253/1344769770/66891/92d54ca4/59df2e7fN86c99a27.jpg",
params: [{
text: "¥6999.00",
color: "#FF0000",
fold: false,
break: false,
size: 12
}, {
text: "满1999元另加30元"
}]
}
});
var origin = "*";
window.parent.postMessage(data, origin);
注意事项
- 请限制发送频率
- 注意消息格式
自定义搜索关键词
参数名称 | 类型 | 说明 |
---|---|---|
udesk_wd | String | 搜索关键词,由第三方跳转过来如:百度推广 |
第三方网站链接示例(如:公司官网)
https://www.公司域名.com?udesk_wd=xxxx
网页插件链接示例
https://公司域名.udesk.cn/im_client/?udesk_wd=xxxx
向外部传递事件
网页插件通过window.postMessage向外部传递事件
第三方页面通过window.addEventListener('message',function (event) { //event })接收
示例
window.addEventListener('message',function (event) {
/*
以会话结束事件举例
通过接收到的type区分事件类型,具体事件类型详见参数说明
*/
let receiveEvent = JSON.parse(event.data)
if (receiveEvent.type === 'chatDone') {
//自定义事件
}
})
值 | 说明 |
---|---|
chatting | 会话开始事件 |
chatDone | 会话结束事件 |
queuing | 进入排队事件 |
hidePanel | 隐藏窗口事件 |
开通 "web IM请求权限校验"以后的校验方法
如果想要强制网页插件链接必须校验以后才允许访问,
可以在管理中心->网页插件->其他设置中打开“web IM请求权限校验”,
打开以后插件链接里必须传 v_signature、v_timestamp和v_nonce
v_signature加密算法(三步)
1、按后面参数及顺序拼接字符,以key=value&形式: v_nonce
、v_timestamp
、im_user_key
im_user_key获取位置【管理中心-即时通讯-网页插件-管理/添加客户信息中的KEY】
sign_str = v_nonce=value&v_timestamp=value&im_user_key
2、 使用加密算法计算出签名字符串
sign_str = sha1(sign_str)
3、 将字符串转换为大写
sign_str = sign_str.toUpperCase()
Java代码示例
1.安装依赖
commons-codec用于计算 sha1
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>1.17.0</version>
</dependency>
2.代码实现
import org.apache.commons.codec.digest.DigestUtils;
public class App {
public static void main(String[] args) {
// 插件专用链接
String baseURL = "[这里替换为您的插件专用链接]";
// 插件的身份认证Key
String im_user_key = "[这里替换为您的插件的 im_user_key]";
// 时间戳,单位是毫秒
long timestamp = System.currentTimeMillis();
// 随机字符串
String nonce = Long.toString(Double.valueOf(Math.ceil(Math.random() * Long.MAX_VALUE)).longValue(), 36);
// 合并为需要签名的字符串
String s = String.format("v_nonce=%s&v_timestamp=%s&%s", nonce, timestamp, im_user_key);
// 计算sha1,并将结果大写
String signature = DigestUtils.sha1Hex(s).toUpperCase();
// 拼接最终的网页插件链接
String url = String.format(
"%s&v_nonce=%s&v_timestamp=%s&v_signature=%s",
baseURL,
nonce,
timestamp,
signature
);
System.out.println(url);
// 如果是用代码对接方式
System.out.printf(
"ud({\n code: '%s',\n link: '%s',\n im_client_valid:{\n v_nonce:'%s',\n v_timestamp:'%s',\n v_signature:'%s'\n }\n})\n",
"[替换为您的 code]",
baseURL,
nonce,
timestamp,
signature
);
}
}
如何加密网页插件参数
如果使用 url 的方式对接网页插件,会把客户信息全部拼接到 url 里,而且是明文的,
为解决此问题,udesk 提供了一种方式,可以通过公钥加密 url 里所有 c_ 开头的参数,
然后将加密后的密文作为参数 customer_encrypt 的值。
下面是一个用 java 实现这个功能的例子
1.安装依赖
commons-lang3 是一个java里常用的工具类
commons-codec 用于计算签名
jackson-databind 用于转换 object 为 json 字符串
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.14.0</version>
</dependency>
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>1.16.0</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.16.2</version>
</dependency>
2.代码实现
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonRootName;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.RandomStringUtils;
import javax.crypto.Cipher;
import java.security.KeyFactory;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.X509EncodedKeySpec;
import java.util.Arrays;
import java.util.Base64;
public class DemoUdeskImClientApplication {
// 用于加密所有c_开头的参数
private static final String PUBLIC_KEY = "[替换为网页插件的public key]";
// 用于参数签名
private static final String IM_USER_KEY = "[替换为网页插件用于身份认证的 key]";
private static final String URL_BASE = "https://[这里替换为客服系统域名]/im_client/?web_plugin_id=[替换为网页插件id]";
public static void main(String[] args) throws Exception {
Customer customer = new Customer(
"李四",
"lisi@udesk.cn",
"18518518518",
"李四是一个测试客户",
"客户标签,客户标签2",
"normal",
"AAJD1710397772205@udesktest.com"
);
String params = String.format(
"nonce=%s×tamp=%s&web_token=%s",
RandomStringUtils.random(5, true, true),
System.currentTimeMillis(),
customer.getWeb_token()
);
String url = URL_BASE
+ "&customer_encrypt=" + encode(customer)
+ "&" + params
+ "&encryption_algorithm=SHA256"
+ "&signature=" + getSignature(params);
System.out.println(url);
}
public static String encode(Customer customer) throws Exception {
// 将 customer 转成 json 字符串
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.enable(SerializationFeature.WRAP_ROOT_VALUE);
String message = objectMapper.writer().writeValueAsString(customer);
// 获取 RSAPublicKey 对象
byte[] encoded = Base64.getDecoder().decode(PUBLIC_KEY);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(encoded);
RSAPublicKey publicKey = (RSAPublicKey) keyFactory.generatePublic(keySpec);
// 初始化 Cipher
Cipher encryptCipher = Cipher.getInstance("RSA");
encryptCipher.init(Cipher.ENCRYPT_MODE, publicKey);
// 对密文分段
int dataCount = (int) Math.ceil((double) message.getBytes().length / 117);
byte[][] datas = new byte[dataCount][];
for (int i = 0; i < dataCount; i++) {
int length = (i == dataCount - 1) ? (message.getBytes().length % 117) : 117;
byte[] item = Arrays.copyOfRange(message.getBytes(), i * 117, i * 117 + length);
datas[i] = item;
}
// 对分好段的密文加密,然后合成一个大的 byte 数组,最后将这个大数组使用 base64 编码
byte[] mi = new byte[]{};
for (byte[] s : datas) {
mi = ArrayUtils.addAll(mi, encryptCipher.doFinal(s));
}
return Base64.getEncoder().encodeToString(mi);
}
public static String getSignature(String paramString) {
return DigestUtils.sha256Hex(paramString + "&" + IM_USER_KEY).toUpperCase();
}
@JsonRootName("customer")
@JsonInclude(JsonInclude.Include.NON_NULL)
static class Customer {
String c_name;
String c_email;
String c_phone;
String c_desc;
String c_tags;
String c_vip;
String web_token;
public Customer(String c_name, String c_email, String c_phone, String c_desc, String c_tags, String c_vip, String web_token) {
this.c_name = c_name;
this.c_email = c_email;
this.c_phone = c_phone;
this.c_desc = c_desc;
this.c_tags = c_tags;
this.c_vip = c_vip;
this.web_token = web_token;
}
public Customer(String c_name) {
this.c_name = c_name;
}
public String getC_name() {
return c_name;
}
public void setC_name(String c_name) {
this.c_name = c_name;
}
public String getWeb_token() {
return web_token;
}
public void setWeb_token(String web_token) {
this.web_token = web_token;
}
public String getC_email() {
return c_email;
}
public void setC_email(String c_email) {
this.c_email = c_email;
}
public String getC_phone() {
return c_phone;
}
public void setC_phone(String c_phone) {
this.c_phone = c_phone;
}
public String getC_desc() {
return c_desc;
}
public void setC_desc(String c_desc) {
this.c_desc = c_desc;
}
public String getC_tags() {
return c_tags;
}
public void setC_tags(String c_tags) {
this.c_tags = c_tags;
}
public String getC_vip() {
return c_vip;
}
public void setC_vip(String c_vip) {
this.c_vip = c_vip;
}
}
}
常见问题
企业微信H5无法拍照?
请联系 udesk 客服配置企业微信代开发应用的可信域名
安卓webview中无法拍照或者录像?
网页插件的文件、拍照、录像都是普通的<input type='file'>标签
在安卓 webview 中需要写一些代码适配,
下面的代码实现了以下功能,打开视频查看效果
1. 点击拍照时直接打开相机,点击录像时直接打开相机录像,点击文件时可以选择所有类型的文件
2. 未授权过的 app 拍照或者录屏时申请权限
3. 拒绝相机权限的时候,弹出“去设置”的提示框
import android.Manifest;
import android.app.Activity;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Bundle;
import android.provider.MediaStore;
import android.provider.Settings;
import android.webkit.ValueCallback;
import android.webkit.WebChromeClient;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import androidx.core.content.FileProvider;
import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
public class MainActivity extends AppCompatActivity {
private static final int FILE_CHOOSER_REQUEST_CODE = 1;
private static final int CAMERA_PERMISSION_REQUEST_CODE = 2;
private static final int CAMERA_VIDEO_PERMISSION_REQUEST_CODE = 3;
private WebView webView;
private ValueCallback<Uri[]> filePathCallback;
private String cameraPhotoPath;
private String cameraVideoPath;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
webView = findViewById(R.id.webview);
WebSettings webSettings = webView.getSettings();
webSettings.setJavaScriptEnabled(true);
webView.setWebViewClient(new WebViewClient());
webView.setWebChromeClient(new WebChromeClient() {
@Override
public boolean onShowFileChooser(WebView webView, ValueCallback<Uri[]> filePathCallback, WebChromeClient.FileChooserParams fileChooserParams) {
MainActivity.this.filePathCallback = filePathCallback;
String[] acceptTypes = fileChooserParams.getAcceptTypes();
if (acceptTypes.length > 0) {
String acceptType = acceptTypes[0];
if (acceptType.equals("video/*") || acceptType.equals("image/*")) {
if (ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(
MainActivity.this,
new String[]{Manifest.permission.CAMERA},
acceptType.equals("video/*") ? CAMERA_VIDEO_PERMISSION_REQUEST_CODE : CAMERA_PERMISSION_REQUEST_CODE
);
return true;
} else {
if (acceptType.equals("image/*")) {
openImageChooser();
return true;
} else if (acceptType.equals("video/*")) {
openVideoChooser();
return true;
}
}
}
}
openFileChooser();
return true;
}
});
webView.loadUrl("https://[替换为您的专属域名]/im_client/");
}
private void openImageChooser() {
// 创建用于调用相机的Intent
Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
if (takePictureIntent.resolveActivity(getPackageManager()) != null) {
// 创建用于存储拍摄图片的文件
File photoFile = null;
try {
photoFile = createImageFile();
takePictureIntent.putExtra("PhotoPath", cameraPhotoPath);
} catch (IOException ex) {
// 错误处理
ex.printStackTrace();
}
if (photoFile != null) {
cameraPhotoPath = "file:" + photoFile.getAbsolutePath();
Uri photoURI = FileProvider.getUriForFile(this, "udesk.im.client.webview.demo.provider", photoFile);
takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoURI);
} else {
takePictureIntent = null;
}
}
startActivityForResult(takePictureIntent, FILE_CHOOSER_REQUEST_CODE);
}
private File createImageFile() throws IOException {
// 创建图片文件名
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
String imageFileName = "JPEG_" + timeStamp + "_";
File storageDir = getExternalFilesDir(null);
return File.createTempFile(imageFileName, ".jpg", storageDir);
}
private void openVideoChooser() {
// 创建用于调用相机录像的Intent
Intent takeVideoIntent = new Intent(MediaStore.ACTION_VIDEO_CAPTURE);
if (takeVideoIntent.resolveActivity(getPackageManager()) != null) {
// 创建用于存储录像文件
File videoFile = null;
try {
videoFile = createVideoFile();
takeVideoIntent.putExtra("VideoPath", cameraVideoPath);
} catch (IOException ex) {
// 错误处理
ex.printStackTrace();
}
if (videoFile != null) {
cameraVideoPath = "file:" + videoFile.getAbsolutePath();
Uri videoURI = FileProvider.getUriForFile(this, "udesk.im.client.webview.demo.provider", videoFile);
takeVideoIntent.putExtra(MediaStore.EXTRA_OUTPUT, videoURI);
} else {
takeVideoIntent = null;
}
}
startActivityForResult(takeVideoIntent, FILE_CHOOSER_REQUEST_CODE);
}
private File createVideoFile() throws IOException {
// 创建视频文件名
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
String videoFileName = "MP4_" + timeStamp + "_";
File storageDir = getExternalFilesDir(null);
return File.createTempFile(videoFileName, ".mp4", storageDir);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == FILE_CHOOSER_REQUEST_CODE && resultCode == Activity.RESULT_OK) {
Uri[] results = null;
if (resultCode == Activity.RESULT_OK) {
if (data == null || data.getData() == null) {
if (cameraPhotoPath != null) {
results = new Uri[]{Uri.parse(cameraPhotoPath)};
}
} else {
String dataString = data.getDataString();
if (dataString != null) {
results = new Uri[]{Uri.parse(dataString)};
}
}
}
filePathCallback.onReceiveValue(results);
filePathCallback = null;
} else {
if (filePathCallback != null) {
filePathCallback.onReceiveValue(null);
filePathCallback = null;
}
}
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == CAMERA_PERMISSION_REQUEST_CODE || requestCode == CAMERA_VIDEO_PERMISSION_REQUEST_CODE) {
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
if (requestCode == CAMERA_VIDEO_PERMISSION_REQUEST_CODE) {
openVideoChooser(); // 点击录像申请权限时同意授权
} else if (requestCode == CAMERA_PERMISSION_REQUEST_CODE) {
openImageChooser(); // 点击拍照申请权限时同意授权
}
} else {
if (!ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.CAMERA)) {
showPermissionDeniedDialog(); // 权限被拒绝,并且勾选了“不再询问”。
} else {
Toast.makeText(this, "需要相机权限才能拍照和录屏。", Toast.LENGTH_SHORT).show();
}
if (filePathCallback != null) {
filePathCallback.onReceiveValue(null);
filePathCallback = null;
}
}
}
}
private void openFileChooser() {
Intent takePictureIntent = new Intent(Intent.ACTION_GET_CONTENT);
takePictureIntent.setType("*/*");
startActivityForResult(takePictureIntent, FILE_CHOOSER_REQUEST_CODE);
}
private void showPermissionDeniedDialog() {
new AlertDialog.Builder(this)
.setTitle("需要权限")
.setMessage("拍照需要相机权限。请在设置中启用该权限。")
.setPositiveButton("去设置", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
openAppSettings();
}
})
.setNegativeButton("取消", null)
.show();
}
private void openAppSettings() {
Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
Uri uri = Uri.fromParts("package", getPackageName(), null);
intent.setData(uri);
startActivity(intent);
}
}
安卓webview中如何下载图片?
网页插件点击图片会显示专门的图片查看界面,界面的顶部有一个下载的按钮,
这个按钮是一个普通的 a 标签,当我们点击这个 a 标签的时候会在新的页面打开这个图片,
所有我们要在安卓 app 里拦截这个行为,改为下载。
下面是具体的代码实现
import android.app.DownloadManager;
import android.content.Context;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;
public class MainActivity extends AppCompatActivity {
private WebView myWebView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
myWebView = findViewById(R.id.webview);
myWebView.getSettings().setJavaScriptEnabled(true);
myWebView.setWebViewClient(new MyWebViewClient());
myWebView.loadUrl("https://[替换为您的专属域名]/im_client/");
}
private class MyWebViewClient extends WebViewClient {
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
if (isImageUrl(url)) {
// 如果是图片链接,调用下载方法
downloadImage(MainActivity.this, url);
return true;
} else {
// 如果不是图片链接,继续加载URL
return false;
}
}
private boolean isImageUrl(String url) {
// 检查URL是否为图片格式
return url.endsWith(".jpg")
|| url.endsWith(".jpeg")
|| url.endsWith(".png")
|| url.endsWith(".gif")
|| url.contains(".jpg?")
|| url.contains(".jpeg?")
|| url.contains(".png?")
|| url.contains(".gif?");
}
private void downloadImage(Context context, String url) {
DownloadManager.Request request = new DownloadManager.Request(Uri.parse(url));
request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, url.substring(url.lastIndexOf("/") + 1));
request.allowScanningByMediaScanner();
request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
DownloadManager downloadManager = (DownloadManager) context.getSystemService(Context.DOWNLOAD_SERVICE);
if (downloadManager != null) {
downloadManager.enqueue(request);
Toast.makeText(context, "正在下载...", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(context, "下载管理器不可用", Toast.LENGTH_SHORT).show();
}
}
}
}