`
yanghui628
  • 浏览: 8620 次
  • 性别: Icon_minigender_1
  • 来自: 广州
社区版块
存档分类
最新评论

发布基于JQuery一个考试题型编辑器插件,目前只做了几个功能,还请大牛仍砖!

阅读更多
注册javaeye两年了,一直没敢发过帖子,怕自己的技术太差。今天特意把自己目前做的一个考试题型编辑器插件发布上来,目前支持单选题,预览,上传附件,支持对视频音频文件的播放。

有同学问我,是基于一个什么业务的。其实是最近可能要做一个考试系统,需要这样的编辑器,然后找了很久都没有找到,就自己写了。后来想可能还会有人需要,就发布上来了。

希望各位大拿多多指教。本人文采不好就直接代码了:

/* -------------------------------------------------------------------
// 作者:yanghui527@126.com
// 时间:2011/05/06
// 描述:这是一个基于jquery的题型编辑器
// ------------------------------------------------------------------*/

(function($){
	//公共编辑ID
	var editorId = "editorHtml";
	//文件上传FormID
	var uploadFormId;
	//上传文件的格式
	var uploadFileFormat = "flv|swf|m4v|mp3|jpg|png|bmp|gif";
	//当前点击的菜单
	var clickmenu;
	//获取时间戳,用于一些元素ID的设置,避免命名冲突
	var timestamp = new Date().getTime();
	//用于截取题型选项顺序
	var questionsTitles = "ABCDEFGHIGKLMNOPQRSTUVWXYZ";
	//设置插件路径
	var pluginPath = getPluginPath();
	//加载相关联的插件
	initPluginFile ();
	var defaults = {};
	/**
	 * 初始化编辑器
	 */
	$.fn.editor = function(options){
		var object = $(this);
		if (object.attr("tagName") != "DIV") {
			return;
		}
		$.extend(defaults, options);
		delete options;
		object.addClass("Container");
		var header = $("<div class='Header'></div>");
		 $.each(plugins(), function (i, value){
				header.append(value); 
		 });
		object.append(header);
		object.append("<div class='Editor' id='" + editorId + "'></div>");
		//加载左键菜单
		object.append(getContextMenu());
		//加载文件上传控件
		uploadFormId = loadUploadFile();
	};
	/**
	 * 初始化编辑器
	 */
	$.fn.editorPlayer = function (isAuto){
		 $(this).flowplayer(pluginPath + "/plugins/flowplayer/flowplayer-3.2.7.swf", {
				clip:  {
			        autoPlay: isAuto,
			        autoBuffering: true
			    },
				onLoad : function() {
					this.setVolume(100);
				}
			});
	};
	
	/****************************************相关联的插件加载功能实现区***************************************************/
	
	/**
	 * 初始化插件js文件
	 */
	function initPluginFile () {
		$("script[src]:last").after("<script src='" + pluginPath + "/plugins/jquery.DOMWindow.js'></script>");
		$("script[src]:last").after("<script src='" + pluginPath + "/plugins/jquery.contextmenu.r2.js'></script>");
		$("script[src]:last").after("<script src='" + pluginPath + "/plugins/flowplayer/flowplayer-3.2.6.min.js'></script>");
		$("script[src]:last").after("<script src='" + pluginPath + "/plugins/jqueryform.js'></script>");

	} ;
	/**
	 * 获取插件路径
	 */
	function getPluginPath () {
		var filePath = $("link[href *= edtior/style.css]:first").attr("href");
		filePath = filePath.substring(0, filePath.lastIndexOf("\/"));
		return filePath;
	}
	/****************************************编辑器功能实现区***************************************************/
	/**
	 * 实例化编辑器插件
	 */
	plugins = function () {
		 var defaultPlugin = {
				 'radioSelect' : function (visible) {  return $.radioSelect(visible);},
				 'uploadFile' : function (visible) {  return $.uploadFile(visible); },
				 'preview' : function (visible) { return $.preview(visible); }
		 };
		 var array = new Array();
		 if ( defaults && defaults.controls )
	        {
	            var controls = defaults.controls;
	            delete defaults.controls;
		            $.each(controls, function (i, type) {
		            	var visible = typeof(type.visible) == "undefined" ? true :  type.visible;
		            	if (typeof(defaultPlugin[i]) != "undefined") 
		            	{
		            		array.push($(defaultPlugin[i](visible)));
		            		delete defaultPlugin[i];
		            	}
		            	delete visible;
		            });

	        }
		 	$.each(defaultPlugin, function(i, value) {
		 		array.push($(value(true)));
		 	});
		 return array;
	};
	/**
	 * 单选题
	 * @param isHide 是否显示
	 */
	$.radioSelect = function(isHide) {
		  var radio = $("<a href='javascript:void(0)' title ='单选题' class='HeaderButton1'>&nbsp;&nbsp;</a>");
		  if (!isHide) { 
			  radio.hide();
			  return radio;
		  }
		  radio.bind({
			  click: function() {
			  		addQuestion("radio");
		  		}
		  });
		  return radio;
	};
	/**
	 * 预览
	 * @param isHide 是否显示
	 */
	$.preview = function(isHide) {
		 var preview = $("<a href='javascript:void(0)' title ='预览' class='preview'>&nbsp;&nbsp;</a>");
		 if (!isHide) { 
			 preview.hide();
			  return preview;
		  }
		 preview.bind({
			 click: function() {
			 			var div = windowHead();
			 			cloneEditor().appendTo(div);
						openWindos (div, $(document).height() * 0.9, $(document).width() * 0.91);
		  		}
		  });
		 return preview;
	};
	
	/**
	 * 文件上传
	 * @param isHide 是否显示
	 */
	$.uploadFile = function(isHide) {
		 var uploadFile = $("<a href='javascript:void(0)' title ='上传文件' class='upload'>&nbsp;&nbsp;</a>");
		 if (!isHide) { 
			 uploadFile.hide();
		  }
		  return uploadFile;
	};
	
	/****************************************左键功能实现区***************************************************/
	/**
	 * 菜单
	 */
	getContextMenu = function () {
		var contextMenu = typeof(defaults.contextMenu) == "undefined" ? "": defaults.contextMenu;
		return $("<div class='contextMenu' id='myMenu'><ul>" + 
				"<li id='contextMenudelete'><img src='" + contextMenu.deleteImg + "' /> 删除</li>" + 
				"<li id='contextMenuadd'><img src='" + contextMenu.deleteImg + "' /> 添加选项</li>" + 
				"<li id='contextMenuslave'><img src='" + contextMenu.deleteImg + "' /> 添加附件</li>" + 
				"</ul></div>").hide();
	};
	/**
	 * 菜单事件绑定
	 */
	contextMenuEvent = function () {
		return {
			'contextMenudelete': function(t) {
				$(t).nextAll().each(function (i, value) {
					var questionRow = $(value).find("table:first tr:first span");
					questionRow.text(questionRow.text() - 1);
				});
			  	$(t).remove(); 
          },
          'contextMenuadd': function(t) {
        	  //获取最后一个选项
        	  var lastQuestion =$(t).find("input[name=answerQuestions" + timestamp + "]:last").parents("tr:first");
        	  var cloneQuestion = lastQuestion.clone();
        	  cloneQuestion.find("input:checked").removeAttr("checked");
        	  cloneQuestion.find("input:text").val("");
        	  cloneQuestion = $("<tr>" + cloneQuestion.html() +"</tr>");
        	  lastQuestion.after(cloneQuestion);
        	  var index = questionsTitleIndexOf(cloneQuestion.find("input:first").val());
        	  var questionsTitle = questionsTitles.substring(index + 1, index + 2);
        	  cloneQuestion.find("td:first input:first").val(questionsTitle);
        	  cloneQuestion.find("span").text(questionsTitle);
	        },
          'contextMenuslave': function(t) {
	        	showUploadFile();
	        	clickmenu = t;
	        }
		};
	};
	
	/****************************************文件上传功能实现区***************************************************/
	/**
	 * 加载文件上传Form
	 */
	loadUploadFile = function  () {
		if (defaults && defaults.uploadFile) {
			var uploadFile = defaults.uploadFile;
			if (typeof(uploadFile.formId) != 'undefined') {
					var uploadoptions = {
						type : 'post',
						beforeSubmit : function (obj) {
							var file = $(uploadFile.formId).find("input[type=file]:first");
							var fileName;
							$.each(obj, function (i, val) {
								if (val.name == file.attr("name")) {
									fileName = val.value;
									return false;
								}
							});
							if (isChinese(fileName.substring(fileName.lastIndexOf("\\") + 1))) {
								alert("文件名不能包含中文!");
								return false;
							}
 							if (uploadFileFormat.indexOf(fileName.substring(fileName.lastIndexOf(".") + 1)) == -1) {
								alert("格式不支持,仅仅支持" + uploadFileFormat);
								return false;
							}
							
						},
						success : function(response, status) {
							addPreviewslave(response.replace(/<.*?>/g,'').replace(/\\/g, "/"));
						},
						error : function() {
							aletr("文件上传失败!");
						}
					};
					$(uploadFile.formId).ajaxForm(uploadoptions);
					$(uploadFile.formId).hide();
					delete defaults.uploadFile;
					return uploadFile.formId;
			}
		}
		return null;
	};
	
	/**
	 * 显示上传窗口
	 */
	showUploadFile = function () {
		if (typeof(uploadFormId) == 'undefined' || uploadFormId == null) {
			alert("文件上传控件没有初始化");
			return;
		}
		openWindos ($("<div></div>")
				.append($(uploadFormId).clone(true).show()
					.append($("<p aling='center'></p>")
						.append($("<input type='submit' value='确定'>"))
						.append($("<input type='button' value='取消' class='closeDOMWindow'>")))
				), 100, 400);
	};
	
	/**
	 * 添加附件预览
	 */
	addPreviewslave = function (result) {
		if (typeof(clickmenu) != "undefined") {
		var fileName = result.substring(result.lastIndexOf("/") + 1, result.length);
    	var oldtr = $(clickmenu).find("table:last tr:last");
    	var slave = $("<tr></tr>").append($("<td colspan='2' align='left'></td>")
    			.append($("<a href='javascript:void(0);'></a>")
    					.attr("title", result)
    					.text(fileName)
	    	        	.bind({
	    	        		 click: function () {
	    	   				 var div = windowHead();
	    	   				 div.append(addPlayer(this.title, true));
	    	   				 openWindos(div, 350, 500);
	    	        		}
	    	        	})		
	    	        )		
    		);

    	oldtr.after(slave);
		}
    	$.closeDOMWindow();
	};
	
	/**
	 * 添加播放器
	 * @param href 播放文件路径
	 * @param isAuto 是否自动播放
	 */
	addPlayer = function (href, isAuto) {
		 var player = $("<a  id='player' style='text-align: center; display: block; width: 495px; height: 330px'></a>");
		 player.attr("href", href);
		 player.editorPlayer(isAuto);
		 return player;
	};
	/****************************************辅助功能实现区***************************************************/
	/**
	 * 添加选项
	 * @param type 选项类型
	 */
	addQuestion = function (type) {
		var div = $("<div class='Questions'></div>");
  		var table = $("<table class='wording'></table>");
  		table.append("<tr>" + 
  						"<td width='20'><span>" + getEditorAreaLength() + "</span>.</td>" + 
  						"<td><textarea></textarea></td></tr>"+ 
  					"<tr><td></td><td><table>" + 
  								"<tr><td width='44'>" + 
  								"<input type='" + type + "' name='answerQuestions" + timestamp + "' value='A' onclick='$.setAnswers(this);'>" + 
  								"<span>A</span></td><td><input type='text' size='80'></td>" +
  								"</tr>" + 
  								"<tr id='previewRemove" + timestamp + "'><td>答案:<span><B>&nbsp;&nbsp;</B></span></td><td></td></tr>" + 
  								"<tr id='previewRemove" + timestamp + "'><td>分数:</td><td><input type='text'></td></tr>" + 
  								"</table>" +
  					"</td></tr>");
  		div.append(table);
  		div.bind({
  			mouseover: function() {
  				$(this).addClass("Question");
  			},
  			mouseout: function(){
  				$(this).removeClass("Question");
  			}
  		});
  		div.contextMenu("myMenu", {
  			bindings: contextMenuEvent()
  			});
  		$("#editorHtml").append(div);
	};
	
	/**
	 * 克隆编辑器的内容并且优化
	 */
	cloneEditor = function () {
		 //克隆问题html
		 var previewHtml = $("#" + editorId).clone();
		 $.each(previewHtml.find("#previewRemove" + timestamp), function (i, value) {
			 $(value).remove();
		 });
		 if ($.browser.mozilla) {
			 $.each($("#" + editorId).find("textarea"), function (i, value) {
				 $(previewHtml.find("textarea")[i]).val($(value).val());
			 });
		 }
		 previewHtml.css("height" , $(document).height() * 0.85);
		 previewHtml.css("width" , $(document).width() * 0.89);
		 $.each(previewHtml.find("div"), function (i, div){
			 $.each($(div).find("a"), function(i, a) {
					$(div).find("td:last").append(addPlayer($(a).attr("title"), false));
					$(a).remove();
				 });
		 });
		 $.each(previewHtml.find("textarea, :text"), function (i, value) {
			 $(value).after($(value).val());
			 $(value).remove();
		 });

		 return previewHtml;
	};
	
	/**
	 * 获取在选项描述中的位置
	 */
	questionsTitleIndexOf = function (string) {
		return questionsTitles.indexOf(string);
	};
	/**
	 * 获取编辑区域长度
	 */
	getEditorAreaLength = function () {
		return ($("#" + editorId + " div").length + 1);
	};
	
	/**
	 * 封装打开窗口
	 * @param object 窗口内容对象
	 * @param height 高度
	 * @param width 宽度
	 */
	openWindos = function (object, height, width) {
		$.openDOMWindow({ 
	        windowSourceID:object, 
	        height:height,  
	        width:width,
	        borderSize:0.5,
	        overlay:1,
	        modal:1
	 });
	};
	
	/**
	 * 弹出层窗口的头部信息
	 */
	windowHead = function () {
		var p = $("<p class='closeplayer' title='关闭'>&nbsp;&nbsp;</p>");
		p.bind({
			 click: function() {
				   $.closeDOMWindow();
				}
		 });
		 return $("<div></div>").append(p);
	};
	
	/**
	 * 答案设置
	 */
	$.setAnswers = function (obj) {
		$(obj).parents("table").find("span:last b").html($(obj).val());
	};

	/**
	 * 验证是否存在中文
	 */ 
	isChinese = function (string) {
			var reg = /.*[\u4e00-\u9fa5]+.*$/;
			return reg.test(string);
	};
	
	/****************************************编辑器扩展功能实现区***************************************************/
	/**
	 * 扩展
	 */
	$.extend($.fn.editor, {
		/**
		 * 获取编辑器的内容
		 */
		geHtml : function(){
			return cloneEditor().html();
		}
	}); 

})(jQuery);



图片:














附近是图片以及源码:[/b]







  • 大小: 39.8 KB
  • 大小: 44 KB
  • 大小: 24.5 KB
  • 大小: 42 KB
  • 大小: 43.4 KB
  • 大小: 54.3 KB
分享到:
评论
16 楼 caoxiaoj2ee 2011-06-17  
共同研究 把这个控件做好
15 楼 yanghui628 2011-05-30  
jjq 写道
这个很简单的,就一个tab控件+kindeditor,你自己研究下,如果实在不行你再联系我吧。


其实我说能公布代码意思就是贡献出来,让大家都了解下。
14 楼 jjq 2011-05-30  
这个很简单的,就一个tab控件+kindeditor,你自己研究下,如果实在不行你再联系我吧。
13 楼 yanghui628 2011-05-30  
<div class="quote_title">jjq 写道</div>
<div class="quote_div">
<p>最近我也在写一个类似的东西,完全是练手,我是这样解决的,直接上图说话:</p>
<p><br><br> 因为我这里不要求上传视频,kindeditor我选用的是简单模式,而这个编辑器提供了视频和flash上传的功能,我认为这样可以符合你的要求,第一次在这里发帖,心里有点虚,不知道说得对不对,还请大家指教。</p>
</div>
<p>    这种也不错哦,能公布代码不</p>
12 楼 jjq 2011-05-29  
<p>最近我也在写一个类似的东西,完全是练手,我是这样解决的,直接上图说话:</p>
<p><br><img src="http://dl.iteye.com/upload/attachment/489987/e93d4564-b2a6-3e74-a1a7-cf0c82201ad3.jpg" alt=""><br> 因为我这里不要求上传视频,kindeditor我选用的是简单模式,而这个编辑器提供了视频和flash上传的功能,我认为这样可以符合你的要求,第一次在这里发帖,心里有点虚,不知道说得对不对,还请大家指教。</p>
11 楼 yanghui628 2011-05-28  
caoxiaoj2ee 写道
不错 有一个叫“考试酷” 的 http://www.examcoo.com/ 上面也有插件 也是基于jq做的;建议楼主参考一下

这个网站其实我看过,但是其不开源而且并不支持视频音频。不过作为参考还是可以,谢谢你的建议。
10 楼 caoxiaoj2ee 2011-05-28  
不错 有一个叫“考试酷” 的 http://www.examcoo.com/ 上面也有插件 也是基于jq做的;建议楼主参考一下
9 楼 yanghui628 2011-05-28  
shaomeng95 写道
建议楼主发到web技术论坛比较好


这是我的失误,不好意思。
8 楼 whaosoft 2011-05-28  
额 .... 写的不错啊 呵呵 ui高手
7 楼 shaomeng95 2011-05-28  
建议楼主发到web技术论坛比较好
6 楼 yanghui628 2011-05-27  
ry.china 写道
你这个用在考试系统上面的插件现在也不错,希望你最后弄完了可以给出来看看


谢谢支持,最后弄完我会继续发布上来。
5 楼 ry.china 2011-05-27  
你这个用在考试系统上面的插件现在也不错,希望你最后弄完了可以给出来看看
4 楼 tinalucky 2011-05-27  
恩,不错,支持一下
3 楼 yanghui628 2011-05-26  
chinacool_main 写道
问一下,你这个Projec想实现的业务逻辑是什么?


其实是因为最近需要做一个在线考试系统,需要一个这样的编辑器,然后找了很久都没有找到,就自己写了。
2 楼 chinacool_main 2011-05-25  
问一下,你这个Projec想实现的业务逻辑是什么?
1 楼 yanghui628 2011-05-25  
难道是我的方式错了?没有一个人发言吗?

相关推荐

    简单的jQuery富文本编辑器插件

    综上所述,这个富文本编辑器插件利用了jQuery的强大功能和CSS3的先进特性,为开发者提供了一个轻量级且易于集成的解决方案,以实现网页上的文本编辑需求。通过学习和使用这个插件,开发者可以进一步掌握jQuery的DOM...

    jQuery+Bootstarp富文本编辑器插件summernote

    `Summernote` 就是这样一款基于这两者的强大富文本编辑器插件,它结合了 jQuery 的便利性和 Bootstrap 的美观性,为用户提供了直观、易用的编辑体验。 **jQuery 插件与 Bootstrap 结合** jQuery 是一个轻量级的...

    jQuery富文本编辑器Notebook

    "jQuery富文本编辑器Notebook"是一个专为创建简洁、整洁且美观的所见即所得(WYSIWYG)编辑器而设计的工具。这款编辑器利用了流行的jQuery库,旨在提供一个用户友好的界面,使得内容创作者可以轻松地进行文字编辑、...

    JwySiwyg 基于jQuery插件的所见所得编辑器

    JwySiwyg就是这样一个基于jQuery的插件,提供了一种直观、易于使用的所见所得编辑器解决方案。 **一、jQuery框架** jQuery是一个轻量级的JavaScript库,简化了HTML文档遍历、事件处理、动画和Ajax交互。它的API...

    基于jQuery的轻量级js弹窗插件

    总结起来,这个基于jQuery的轻量级js弹窗插件是一个基础但具有潜力的项目,它的目标是提供一个简单易用的弹窗解决方案。虽然目前功能有限,但通过进一步开发和完善,有望成为一个功能全面、高度定制化的弹窗工具,...

    基于jQuery功能强大的图片查看器插件(5星级)

    "基于jQuery功能强大的图片查看器插件(5星级)"这个标题指出,我们正在讨论的是一款在jQuery框架下开发的高质量图片查看器插件。它获得了五星级的评价,意味着该插件在用户体验、性能、功能等方面表现出色,受到了...

    基于Bootstrap的网格式jQuery文本编辑器

    这个项目的标题"基于Bootstrap的网格式jQuery文本编辑器"表明我们正在讨论一个特定的jQuery插件,名为"grid-editor",它利用了Bootstrap的网格系统来创建一个独特的、所见即所得的编辑器体验。 Bootstrap的网格系统...

    基于jquery的日历日程插件

    "基于jQuery的日历日程插件"就是这样一个工具,它结合了jQuery库的强大功能,以实现高效且用户友好的日程管理功能。该插件不仅具有24节气的支持,还以美观大气的设计和简单的操作方式赢得了开发者和用户的青睐,尤其...

    jQuery+Bootstarp富文本编辑器插件summernote.js.zip

    jQuery与Bootstrap结合的Summernote.js插件就是一个高效且易用的富文本编辑器,尤其适合那些希望在网页中提供强大编辑功能的开发者。 一、jQuery与Bootstrap的结合 jQuery是一款轻量级的JavaScript库,它的主要...

    jquery插件集-web文本编辑器jwysiwyg

    `jWYSIWYG`是一个基于jQuery的轻量级插件,它提供了一个所见即所得(WYSIWYG)的编辑界面,使得用户在网页上可以像使用桌面应用那样进行文本编辑。这款插件以其小巧、易于集成和使用的特点受到开发者的青睐。 **一...

    MathquillBasedEditor是一款所见即所得的jQuery数学公式编辑器插件

    MathquillBasedEditor是一款专为JavaScript开发者设计的所见即所得的jQuery数学公式编辑器插件,它极大地简化了在网页上创建和编辑数学公式的流程。这款编辑器的亮点在于其直观易用的界面,用户只需通过点击预设的...

    jquery在线编辑器

    jQuery在线编辑器是一种基于jQuery库的富文本编辑器插件,它为用户在网页上提供了一种便捷的方式来创建、编辑和格式化文本内容,类似于Word等桌面文字处理软件的功能。此类编辑器通常用于博客、论坛、内容管理系统...

    基于JQuery开发的弹窗插件

    【标题】基于JQuery开发的弹窗插件 在Web开发中,弹窗是一种常见的交互方式,用于显示警告、确认信息或提供用户输入等。JQuery是一个轻量级的JavaScript库,它简化了HTML文档遍历、事件处理、动画以及Ajax交互等...

    jquery用户前端简洁大气在线编辑器插件

    本文将深入探讨一款基于jQuery的前端编辑器插件,该插件以其简洁大气的界面设计和强大的功能特性,深受开发者和用户的喜爱。 ### 一、jQuery简介 jQuery是一款轻量级、高性能的JavaScript库,它简化了HTML文档遍历...

    基于jquery的图片裁剪插件

    以下是一个简单的JQuery图片裁剪插件使用示例: ```html &lt;!DOCTYPE html&gt; &lt;script src="https://code.jquery.com/jquery.min.js"&gt;&lt;/script&gt; &lt;script src="cropper.min.js"&gt;&lt;/script&gt; &lt;button id="crop"&gt;...

    jQuery实现Markdown编辑器插件.zip

    总之,"jQuery实现Markdown编辑器插件.zip"提供的工具使开发者能够快速创建一个功能完备且用户友好的Markdown编辑界面,借助jQuery的便利性和Bootstrap的美观性,提升网站的交互体验。通过深入理解和灵活运用,可以...

    轻量级在线编辑器,基于jquery

    标题中的“轻量级在线编辑器,基于jquery”指的是一个使用jQuery库开发的、设计简洁且功能相对简单的网页文本编辑工具。这样的编辑器通常用于网站内容管理系统(CMS)或者论坛等,让用户可以在网页上直接编辑和格式...

    基于jquery.masonry插件开发的瀑布流ajax动态加载数据功能

    通过以上步骤,我们可以成功构建一个基于jQuery Masonry插件和Ajax技术的瀑布流动态加载数据功能。这个功能不仅提高了页面的可读性和互动性,还能有效减少服务器负载,因为只有在需要时才会请求新数据。

    jQuery富文本编辑器插件-wysiwyg.js

    `wysiwyg.js`是一款基于jQuery的富文本编辑器插件,为网页提供了一种高效、功能强大的文本编辑解决方案。这款插件以其优秀的性能和兼容性,被广泛应用于博客、论坛、内容管理系统等需要用户输入格式化文本的场景。 ...

    jquery编辑器

    jQuery编辑器,正如其名,是基于流行的JavaScript库jQuery构建的,旨在提供一个功能强大、界面美观的在线文本编辑解决方案。它不仅简化了开发过程,还增强了用户体验,特别是对于那些需要用户提交格式化内容的网站,...

Global site tag (gtag.js) - Google Analytics