*/ public static function defaults(): array { return [ 'v' => self::VERSION, 'enabled' => false, 'template' => 'bubble', 'position' => 'bottom-right', 'title' => '专属顾问在线', 'subtitle' => '点击添加企业微信,获取一对一服务', 'button_text' => '立即咨询', 'primary_color' => '#139A8C', 'bottom_offset' => 28, 'show_mobile' => true, ]; } /** * @return array * @throws InvalidArgumentException */ public static function fromInput(mixed $input): array { if (!is_array($input)) { throw new InvalidArgumentException('浮窗配置格式无效'); } $defaults = self::defaults(); $version = self::integerValue(self::inputValue($input, 'v', self::VERSION), '配置版本'); if ($version !== self::VERSION) { throw new InvalidArgumentException('不支持的浮窗配置版本'); } $template = self::textValue(self::inputValue($input, 'template', $defaults['template']), '模板', 1, 20); if (!in_array($template, self::TEMPLATES, true)) { throw new InvalidArgumentException('浮窗模板无效'); } $position = self::textValue(self::inputValue($input, 'position', $defaults['position']), '位置', 1, 20); if (!in_array($position, self::POSITIONS, true)) { throw new InvalidArgumentException('浮窗位置无效'); } $color = strtoupper(trim(self::stringValue( self::inputValue($input, 'primary_color', $defaults['primary_color']), '主题色' ))); if (preg_match('/^#[0-9A-F]{6}$/D', $color) !== 1) { throw new InvalidArgumentException('主题色必须是 #RRGGBB 格式'); } $bottomOffset = self::integerValue( self::inputValue($input, 'bottom_offset', $defaults['bottom_offset']), '底部距离' ); if ($bottomOffset < 16 || $bottomOffset > 160) { throw new InvalidArgumentException('底部距离必须在 16-160 之间'); } return [ 'v' => self::VERSION, 'enabled' => self::booleanValue(self::inputValue($input, 'enabled', $defaults['enabled']), '启用状态'), 'template' => $template, 'position' => $position, 'title' => self::textValue(self::inputValue($input, 'title', $defaults['title']), '标题', 1, 24), 'subtitle' => self::textValue(self::inputValue($input, 'subtitle', $defaults['subtitle']), '副标题', 0, 48), 'button_text' => self::textValue( self::inputValue($input, 'button_text', $defaults['button_text']), '按钮文案', 1, 12 ), 'primary_color' => $color, 'bottom_offset' => $bottomOffset, 'show_mobile' => self::booleanValue( self::inputValue($input, 'show_mobile', $defaults['show_mobile']), '移动端展示状态' ), ]; } /** @return array */ public static function decode(mixed $stored): array { if (!is_string($stored) || trim($stored) === '') { return self::defaults(); } try { $decoded = json_decode($stored, true, 16, JSON_THROW_ON_ERROR); if (!is_array($decoded)) { return self::defaults(); } foreach (array_keys(self::defaults()) as $key) { if (!array_key_exists($key, $decoded)) { return self::defaults(); } } return self::fromInput($decoded); } catch (\Throwable) { return self::defaults(); } } /** @param array $config */ public static function encode(array $config): string { return self::jsonForScript(self::fromInput($config)); } /** * 生成可直接跨站安装的完整脚本。真实获客链接始终只由跳转端点选择。 * * @param array $config */ public static function renderScript(string $key, string $goUrl, array $config, bool $poolEnabled = true): string { $config = self::fromInput($config); if (!$poolEnabled) { $config['enabled'] = false; } $jsonKey = self::jsonForScript($key); $jsonGo = self::jsonForScript($goUrl); $jsonConfig = self::jsonForScript($config); return <<=0;index--){ if((scripts[index].src||'').indexOf(marker)!==-1){scriptNode=scripts[index];return scriptNode;} } return null; } function resolveGoUrl(value){ if(/^https?:\/\//i.test(value)){return value;} var node=findScriptNode(); if(node&&node.src&&typeof w.URL==='function'){ try{return new w.URL(value,node.src).href;}catch(error){} } return value; } function sourceUrl(){ var location=w.location||{}; var origin=location.origin||((location.protocol&&location.host)?location.protocol+'//'+location.host:''); return origin+(location.pathname||'/'); } function openPromotion(){ w.location.assign(go+'?from='+encodeURIComponent(sourceUrl())); } function handleDocumentClick(event){ var path=typeof event.composedPath==='function'?event.composedPath():[]; var node=null; for(var index=0;index $max) { throw new InvalidArgumentException(sprintf('%s长度必须在 %d-%d 个字符之间', $label, $min, $max)); } return $value; } private static function jsonForScript(mixed $value): string { return json_encode( $value, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_THROW_ON_ERROR ); } }