提交 1d7c0b0b authored 作者: GOD_ZYX's avatar GOD_ZYX

完成 视频加载

上级 d954e8c7
...@@ -89,11 +89,16 @@ $GLOBAL.BaseConfig = { ...@@ -89,11 +89,16 @@ $GLOBAL.BaseConfig = {
plugins: [ plugins: [
new webpack.DefinePlugin({ new webpack.DefinePlugin({
'webConf': JSON.stringify($GLOBAL.webConf) 'webConf': JSON.stringify($GLOBAL.webConf)
}),
new webpack.ProvidePlugin({
$: 'jquery',
jQuery: 'jquery',
'window.jQuery': 'jquery'
}) })
], ],
externals: { externals: {
'CKEDITOR': 'window.CKEDITOR' 'CKEDITOR': 'window.CKEDITOR',
'VideoJs': 'window.swfobject'
} }
} }
module.exports = $GLOBAL module.exports = $GLOBAL
*{margin: 0;padding: 0;list-style: none;} /* Logo 字体 */
/* @font-face {
KISSY CSS Reset font-family: "iconfont logo";
理念:1. reset 的目的不是清除浏览器的默认样式,这仅是部分工作。清除和重置是紧密不可分的。 src: url('https://at.alicdn.com/t/font_985780_km7mi63cihi.eot?t=1545807318834');
2. reset 的目的不是让默认样式在所有浏览器下一致,而是减少默认样式有可能带来的问题。 src: url('https://at.alicdn.com/t/font_985780_km7mi63cihi.eot?t=1545807318834#iefix') format('embedded-opentype'),
3. reset 期望提供一套普适通用的基础样式。但没有银弹,推荐根据具体需求,裁剪和修改后再使用。 url('https://at.alicdn.com/t/font_985780_km7mi63cihi.woff?t=1545807318834') format('woff'),
特色:1. 适应中文;2. 基于最新主流浏览器。 url('https://at.alicdn.com/t/font_985780_km7mi63cihi.ttf?t=1545807318834') format('truetype'),
维护:玉伯<lifesinger@gmail.com>, 正淳<ragecarrier@gmail.com> url('https://at.alicdn.com/t/font_985780_km7mi63cihi.svg?t=1545807318834#iconfont') format('svg');
*/ }
/** 清除内外边距 **/ .logo {
body, h1, h2, h3, h4, h5, h6, hr, p, blockquote, /* structural elements 结构元素 */ font-family: "iconfont logo";
dl, dt, dd, ul, ol, li, /* list elements 列表元素 */ font-size: 160px;
pre, /* text formatting elements 文本格式元素 */ font-style: normal;
form, fieldset, legend, button, input, textarea, /* form elements 表单元素 */ -webkit-font-smoothing: antialiased;
th, td /* table elements 表格元素 */ { -moz-osx-font-smoothing: grayscale;
margin: 0; }
padding: 0;
/* tabs */
.nav-tabs {
position: relative;
}
.nav-tabs .nav-more {
position: absolute;
right: 0;
bottom: 0;
height: 42px;
line-height: 42px;
color: #666;
} }
/** 设置默认字体 **/ #tabs {
body, border-bottom: 1px solid #eee;
button, input, select, textarea /* for ie */ {
font: 12px/1.5 tahoma, arial, \5b8b\4f53, sans-serif;
} }
h1, h2, h3, h4, h5, h6 { font-size: 100%; }
address, cite, dfn, em, var { font-style: normal; } /* 将斜体扶正 */
code, kbd, pre, samp { font-family: courier new, courier, monospace; } /* 统一等宽字体 */
small { font-size: 12px; } /* 小于 12px 的中文很难阅读,让 small 正常化 */
/** 重置列表元素 **/
ul, ol { list-style: none; }
/** 重置文本格式元素 **/ #tabs li {
a { text-decoration: none; } cursor: pointer;
a:hover { text-decoration: underline; } width: 100px;
height: 40px;
line-height: 40px;
text-align: center;
font-size: 16px;
border-bottom: 2px solid transparent;
position: relative;
z-index: 1;
margin-bottom: -1px;
color: #666;
}
/** 重置表单元素 **/ #tabs .active {
legend { color: #000; } /* for ie6 */ border-bottom-color: #f00;
fieldset, img { border: 0; } /* img 搭车:让链接里的 img 无边框 */ color: #222;
button, input, select, textarea { font-size: 100%; } /* 使得表单元素在 ie 下能继承字体大小 */ }
/* 注:optgroup 无法扶正 */
/** 重置表格元素 **/ .tab-container .content {
table { border-collapse: collapse; border-spacing: 0; } display: none;
}
/* 清除浮动 */ /* 页面布局 */
.ks-clear:after, .clear:after { .main {
content: '\20'; padding: 30px 100px;
display: block; width: 960px;
height: 0; margin: 0 auto;
clear: both;
} }
.ks-clear, .clear {
.main .logo {
color: #333;
text-align: left;
margin-bottom: 30px;
line-height: 1;
height: 110px;
margin-top: -50px;
overflow: hidden;
*zoom: 1; *zoom: 1;
} }
.main { .main .logo a {
padding: 30px 100px; font-size: 160px;
width: 960px; color: #333;
margin: 0 auto;
} }
.main h1{font-size:36px; color:#333; text-align:left;margin-bottom:30px; border-bottom: 1px solid #eee;}
.helps{margin-top:40px;} .helps {
.helps pre{ margin-top: 40px;
padding:20px; }
margin:10px 0;
border:solid 1px #e7e1cd; .helps pre {
padding: 20px;
margin: 10px 0;
border: solid 1px #e7e1cd;
background-color: #fffdef; background-color: #fffdef;
overflow: auto; overflow: auto;
} }
.icon_lists{ .icon_lists {
width: 100% !important; width: 100% !important;
overflow: hidden;
*zoom: 1;
} }
.icon_lists li{ .icon_lists li {
float:left;
width: 100px; width: 100px;
height:180px; margin-bottom: 10px;
margin-right: 20px;
text-align: center; text-align: center;
list-style: none !important; list-style: none !important;
cursor: default;
}
.icon_lists li .code-name {
line-height: 1.2;
} }
.icon_lists .icon{
font-size: 42px;
line-height: 100px;
margin: 10px 0;
color:#333;
-webkit-transition: font-size 0.25s ease-out 0s;
-moz-transition: font-size 0.25s ease-out 0s;
transition: font-size 0.25s ease-out 0s;
.icon_lists .icon {
display: block;
height: 100px;
line-height: 100px;
font-size: 42px;
margin: 10px auto;
color: #333;
-webkit-transition: font-size 0.25s linear, width 0.25s linear;
-moz-transition: font-size 0.25s linear, width 0.25s linear;
transition: font-size 0.25s linear, width 0.25s linear;
} }
.icon_lists .icon:hover{
.icon_lists .icon:hover {
font-size: 100px; font-size: 100px;
} }
.icon_lists .svg-icon {
/* 通过设置 font-size 来改变图标大小 */
width: 1em;
/* 图标和文字相邻时,垂直对齐 */
vertical-align: -0.15em;
/* 通过设置 color 来改变 SVG 的颜色/fill */
fill: currentColor;
/* path 和 stroke 溢出 viewBox 部分在 IE 下会显示
normalize.css 中也包含这行 */
overflow: hidden;
}
.icon_lists li .name,
.icon_lists li .code-name {
color: #666;
}
/* markdown 样式 */
.markdown { .markdown {
color: #666; color: #666;
font-size: 14px; font-size: 14px;
...@@ -165,40 +211,39 @@ margin: 0 auto; ...@@ -165,40 +211,39 @@ margin: 0 auto;
clear: both; clear: both;
} }
.markdown p, .markdown p {
.markdown pre {
margin: 1em 0; margin: 1em 0;
} }
.markdown > p, .markdown>p,
.markdown > blockquote, .markdown>blockquote,
.markdown > .highlight, .markdown>.highlight,
.markdown > ol, .markdown>ol,
.markdown > ul { .markdown>ul {
width: 80%; width: 80%;
} }
.markdown ul > li { .markdown ul>li {
list-style: circle; list-style: circle;
} }
.markdown > ul li, .markdown>ul li,
.markdown blockquote ul > li { .markdown blockquote ul>li {
margin-left: 20px; margin-left: 20px;
padding-left: 4px; padding-left: 4px;
} }
.markdown > ul li p, .markdown>ul li p,
.markdown > ol li p { .markdown>ol li p {
margin: 0.6em 0; margin: 0.6em 0;
} }
.markdown ol > li { .markdown ol>li {
list-style: decimal; list-style: decimal;
} }
.markdown > ol li, .markdown>ol li,
.markdown blockquote ol > li { .markdown blockquote ol>li {
margin-left: 20px; margin-left: 20px;
padding-left: 4px; padding-left: 4px;
} }
...@@ -210,24 +255,12 @@ margin: 0 auto; ...@@ -210,24 +255,12 @@ margin: 0 auto;
border-radius: 3px; border-radius: 3px;
} }
.markdown pre {
border-radius: 6px;
background: #f7f7f7;
padding: 20px;
}
.markdown pre code {
border: none;
background: #f7f7f7;
margin: 0;
}
.markdown strong, .markdown strong,
.markdown b { .markdown b {
font-weight: 600; font-weight: 600;
} }
.markdown > table { .markdown>table {
border-collapse: collapse; border-collapse: collapse;
border-spacing: 0px; border-spacing: 0px;
empty-cells: show; empty-cells: show;
...@@ -236,21 +269,20 @@ margin: 0 auto; ...@@ -236,21 +269,20 @@ margin: 0 auto;
margin-bottom: 24px; margin-bottom: 24px;
} }
.markdown > table th { .markdown>table th {
white-space: nowrap; white-space: nowrap;
color: #333; color: #333;
font-weight: 600; font-weight: 600;
} }
.markdown > table th, .markdown>table th,
.markdown > table td { .markdown>table td {
border: 1px solid #e9e9e9; border: 1px solid #e9e9e9;
padding: 8px 16px; padding: 8px 16px;
text-align: left; text-align: left;
} }
.markdown > table th { .markdown>table th {
background: #F7F7F7; background: #F7F7F7;
} }
...@@ -260,7 +292,6 @@ margin: 0 auto; ...@@ -260,7 +292,6 @@ margin: 0 auto;
border-left: 4px solid #e9e9e9; border-left: 4px solid #e9e9e9;
padding-left: 0.8em; padding-left: 0.8em;
margin: 1em 0; margin: 1em 0;
font-style: italic;
} }
.markdown blockquote p { .markdown blockquote p {
...@@ -287,8 +318,8 @@ margin: 0 auto; ...@@ -287,8 +318,8 @@ margin: 0 auto;
display: inline-block; display: inline-block;
} }
.markdown > br, .markdown>br,
.markdown > p > br { .markdown>p>br {
clear: both; clear: both;
} }
...@@ -360,11 +391,149 @@ margin: 0 auto; ...@@ -360,11 +391,149 @@ margin: 0 auto;
text-decoration: underline; text-decoration: underline;
} }
pre{ /* 代码高亮 */
background: #fff; /* PrismJS 1.15.0
https://prismjs.com/download.html#themes=prism&languages=markup+css+clike+javascript */
/**
* prism.js default theme for JavaScript, CSS and HTML
* Based on dabblet (http://dabblet.com)
* @author Lea Verou
*/
code[class*="language-"],
pre[class*="language-"] {
color: black;
background: none;
text-shadow: 0 1px white;
font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace;
text-align: left;
white-space: pre;
word-spacing: normal;
word-break: normal;
word-wrap: normal;
line-height: 1.5;
-moz-tab-size: 4;
-o-tab-size: 4;
tab-size: 4;
-webkit-hyphens: none;
-moz-hyphens: none;
-ms-hyphens: none;
hyphens: none;
} }
pre[class*="language-"]::-moz-selection,
pre[class*="language-"] ::-moz-selection,
code[class*="language-"]::-moz-selection,
code[class*="language-"] ::-moz-selection {
text-shadow: none;
background: #b3d4fc;
}
pre[class*="language-"]::selection,
pre[class*="language-"] ::selection,
code[class*="language-"]::selection,
code[class*="language-"] ::selection {
text-shadow: none;
background: #b3d4fc;
}
@media print {
code[class*="language-"],
pre[class*="language-"] {
text-shadow: none;
}
}
/* Code blocks */
pre[class*="language-"] {
padding: 1em;
margin: .5em 0;
overflow: auto;
}
:not(pre)>code[class*="language-"],
pre[class*="language-"] {
background: #f5f2f0;
}
/* Inline code */
:not(pre)>code[class*="language-"] {
padding: .1em;
border-radius: .3em;
white-space: normal;
}
.token.comment,
.token.prolog,
.token.doctype,
.token.cdata {
color: slategray;
}
.token.punctuation {
color: #999;
}
.namespace {
opacity: .7;
}
.token.property,
.token.tag,
.token.boolean,
.token.number,
.token.constant,
.token.symbol,
.token.deleted {
color: #905;
}
.token.selector,
.token.attr-name,
.token.string,
.token.char,
.token.builtin,
.token.inserted {
color: #690;
}
.token.operator,
.token.entity,
.token.url,
.language-css .token.string,
.style .token.string {
color: #9a6e3a;
background: hsla(0, 0%, 100%, .5);
}
.token.atrule,
.token.attr-value,
.token.keyword {
color: #07a;
}
.token.function,
.token.class-name {
color: #DD4A68;
}
.token.regex,
.token.important,
.token.variable {
color: #e90;
}
.token.important,
.token.bold {
font-weight: bold;
}
.token.italic {
font-style: italic;
}
.token.entity {
cursor: help;
}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
<title>IconFont Demo</title>
<link rel="shortcut icon" href="https://gtms04.alicdn.com/tps/i4/TB1_oz6GVXXXXaFXpXXJDFnIXXX-64-64.ico" type="image/x-icon"/>
<link rel="stylesheet" href="https://g.alicdn.com/thx/cube/1.3.2/cube.min.css">
<link rel="stylesheet" href="demo.css">
<link rel="stylesheet" href="iconfont.css">
<script src="iconfont.js"></script>
<!-- jQuery -->
<script src="https://a1.alicdn.com/oss/uploads/2018/12/26/7bfddb60-08e8-11e9-9b04-53e73bb6408b.js"></script>
<!-- 代码高亮 -->
<script src="https://a1.alicdn.com/oss/uploads/2018/12/26/a3f714d0-08e6-11e9-8a15-ebf944d7534c.js"></script>
</head>
<body>
<div class="main">
<h1 class="logo"><a href="https://www.iconfont.cn/" title="iconfont 首页" target="_blank">&#xe86b;</a></h1>
<div class="nav-tabs">
<ul id="tabs" class="dib-box">
<li class="dib active"><span>Unicode</span></li>
<li class="dib"><span>Font class</span></li>
<li class="dib"><span>Symbol</span></li>
</ul>
<a href="https://www.iconfont.cn/manage/index?manage_type=myprojects&projectId=948269" target="_blank" class="nav-more">查看项目</a>
</div>
<div class="tab-container">
<div class="content unicode" style="display: block;">
<ul class="icon_lists dib-box">
<li class="dib">
<span class="icon selfAllIcon">&#xe67e;</span>
<div class="name">discover</div>
<div class="code-name">&amp;#xe67e;</div>
</li>
<li class="dib">
<span class="icon selfAllIcon">&#xe68a;</span>
<div class="name">settings</div>
<div class="code-name">&amp;#xe68a;</div>
</li>
<li class="dib">
<span class="icon selfAllIcon">&#xe71e;</span>
<div class="name">new</div>
<div class="code-name">&amp;#xe71e;</div>
</li>
<li class="dib">
<span class="icon selfAllIcon">&#xe734;</span>
<div class="name">album</div>
<div class="code-name">&amp;#xe734;</div>
</li>
<li class="dib">
<span class="icon selfAllIcon">&#xe615;</span>
<div class="name">cc-book</div>
<div class="code-name">&amp;#xe615;</div>
</li>
<li class="dib">
<span class="icon selfAllIcon">&#xe768;</span>
<div class="name">播放</div>
<div class="code-name">&amp;#xe768;</div>
</li>
<li class="dib">
<span class="icon selfAllIcon">&#xe611;</span>
<div class="name">关闭</div>
<div class="code-name">&amp;#xe611;</div>
</li>
<li class="dib">
<span class="icon selfAllIcon">&#xe632;</span>
<div class="name">刷新</div>
<div class="code-name">&amp;#xe632;</div>
</li>
<li class="dib">
<span class="icon selfAllIcon">&#xe6ce;</span>
<div class="name">image-o</div>
<div class="code-name">&amp;#xe6ce;</div>
</li>
<li class="dib">
<span class="icon selfAllIcon">&#xe66c;</span>
<div class="name">Grade</div>
<div class="code-name">&amp;#xe66c;</div>
</li>
<li class="dib">
<span class="icon selfAllIcon">&#xe743;</span>
<div class="name">全屏</div>
<div class="code-name">&amp;#xe743;</div>
</li>
<li class="dib">
<span class="icon selfAllIcon">&#xe66b;</span>
<div class="name">nav</div>
<div class="code-name">&amp;#xe66b;</div>
</li>
<li class="dib">
<span class="icon selfAllIcon">&#xe607;</span>
<div class="name">密码</div>
<div class="code-name">&amp;#xe607;</div>
</li>
<li class="dib">
<span class="icon selfAllIcon">&#xe60e;</span>
<div class="name">视频</div>
<div class="code-name">&amp;#xe60e;</div>
</li>
<li class="dib">
<span class="icon selfAllIcon">&#xe62e;</span>
<div class="name">人物</div>
<div class="code-name">&amp;#xe62e;</div>
</li>
<li class="dib">
<span class="icon selfAllIcon">&#xe650;</span>
<div class="name">文件</div>
<div class="code-name">&amp;#xe650;</div>
</li>
<li class="dib">
<span class="icon selfAllIcon">&#xe609;</span>
<div class="name">学习</div>
<div class="code-name">&amp;#xe609;</div>
</li>
<li class="dib">
<span class="icon selfAllIcon">&#xe602;</span>
<div class="name">PPT</div>
<div class="code-name">&amp;#xe602;</div>
</li>
<li class="dib">
<span class="icon selfAllIcon">&#xe601;</span>
<div class="name">statistic</div>
<div class="code-name">&amp;#xe601;</div>
</li>
</ul>
<div class="article markdown">
<h2 id="unicode-">Unicode 引用</h2>
<hr>
<p>Unicode 是字体在网页端最原始的应用方式,特点是:</p>
<ul>
<li>兼容性最好,支持 IE6+,及所有现代浏览器。</li>
<li>支持按字体的方式去动态调整图标大小,颜色等等。</li>
<li>但是因为是字体,所以不支持多色。只能使用平台里单色的图标,就算项目里有多色图标也会自动去色。</li>
</ul>
<blockquote>
<p>注意:新版 iconfont 支持多色图标,这些多色图标在 Unicode 模式下将不能使用,如果有需求建议使用symbol 的引用方式</p>
</blockquote>
<p>Unicode 使用步骤如下:</p>
<h3 id="-font-face">第一步:拷贝项目下面生成的 <code>@font-face</code></h3>
<pre><code class="language-css"
>@font-face {
font-family: 'selfAllIcon';
src: url('iconfont.eot');
src: url('iconfont.eot?#iefix') format('embedded-opentype'),
url('iconfont.woff2') format('woff2'),
url('iconfont.woff') format('woff'),
url('iconfont.ttf') format('truetype'),
url('iconfont.svg#selfAllIcon') format('svg');
}
</code></pre>
<h3 id="-iconfont-">第二步:定义使用 iconfont 的样式</h3>
<pre><code class="language-css"
>.selfAllIcon {
font-family: "selfAllIcon" !important;
font-size: 16px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
</code></pre>
<h3 id="-">第三步:挑选相应图标并获取字体编码,应用于页面</h3>
<pre>
<code class="language-html"
>&lt;span class="selfAllIcon"&gt;&amp;#x33;&lt;/span&gt;
</code></pre>
<blockquote>
<p>"selfAllIcon" 是你项目下的 font-family。可以通过编辑项目查看,默认是 "iconfont"。</p>
</blockquote>
</div>
</div>
<div class="content font-class">
<ul class="icon_lists dib-box">
<li class="dib">
<span class="icon selfAllIcon el-icon-self-discover"></span>
<div class="name">
discover
</div>
<div class="code-name">.el-icon-self-discover
</div>
</li>
<li class="dib">
<span class="icon selfAllIcon el-icon-self-settings"></span>
<div class="name">
settings
</div>
<div class="code-name">.el-icon-self-settings
</div>
</li>
<li class="dib">
<span class="icon selfAllIcon el-icon-self-new"></span>
<div class="name">
new
</div>
<div class="code-name">.el-icon-self-new
</div>
</li>
<li class="dib">
<span class="icon selfAllIcon el-icon-self-album"></span>
<div class="name">
album
</div>
<div class="code-name">.el-icon-self-album
</div>
</li>
<li class="dib">
<span class="icon selfAllIcon el-icon-self-cc-book"></span>
<div class="name">
cc-book
</div>
<div class="code-name">.el-icon-self-cc-book
</div>
</li>
<li class="dib">
<span class="icon selfAllIcon el-icon-self-iconset0481"></span>
<div class="name">
播放
</div>
<div class="code-name">.el-icon-self-iconset0481
</div>
</li>
<li class="dib">
<span class="icon selfAllIcon el-icon-self-guanbi"></span>
<div class="name">
关闭
</div>
<div class="code-name">.el-icon-self-guanbi
</div>
</li>
<li class="dib">
<span class="icon selfAllIcon el-icon-self-xuexiao"></span>
<div class="name">
刷新
</div>
<div class="code-name">.el-icon-self-xuexiao
</div>
</li>
<li class="dib">
<span class="icon selfAllIcon el-icon-self-13"></span>
<div class="name">
image-o
</div>
<div class="code-name">.el-icon-self-13
</div>
</li>
<li class="dib">
<span class="icon selfAllIcon el-icon-self-grade"></span>
<div class="name">
Grade
</div>
<div class="code-name">.el-icon-self-grade
</div>
</li>
<li class="dib">
<span class="icon selfAllIcon el-icon-self-quanping"></span>
<div class="name">
全屏
</div>
<div class="code-name">.el-icon-self-quanping
</div>
</li>
<li class="dib">
<span class="icon selfAllIcon el-icon-self-nav"></span>
<div class="name">
nav
</div>
<div class="code-name">.el-icon-self-nav
</div>
</li>
<li class="dib">
<span class="icon selfAllIcon el-icon-self-mima"></span>
<div class="name">
密码
</div>
<div class="code-name">.el-icon-self-mima
</div>
</li>
<li class="dib">
<span class="icon selfAllIcon el-icon-self-shipin"></span>
<div class="name">
视频
</div>
<div class="code-name">.el-icon-self-shipin
</div>
</li>
<li class="dib">
<span class="icon selfAllIcon el-icon-self-character"></span>
<div class="name">
人物
</div>
<div class="code-name">.el-icon-self-character
</div>
</li>
<li class="dib">
<span class="icon selfAllIcon el-icon-self-wenjian"></span>
<div class="name">
文件
</div>
<div class="code-name">.el-icon-self-wenjian
</div>
</li>
<li class="dib">
<span class="icon selfAllIcon el-icon-self-xuexi-"></span>
<div class="name">
学习
</div>
<div class="code-name">.el-icon-self-xuexi-
</div>
</li>
<li class="dib">
<span class="icon selfAllIcon el-icon-self-PPT"></span>
<div class="name">
PPT
</div>
<div class="code-name">.el-icon-self-PPT
</div>
</li>
<li class="dib">
<span class="icon selfAllIcon el-icon-self-statistic"></span>
<div class="name">
statistic
</div>
<div class="code-name">.el-icon-self-statistic
</div>
</li>
</ul>
<div class="article markdown">
<h2 id="font-class-">font-class 引用</h2>
<hr>
<p>font-class 是 Unicode 使用方式的一种变种,主要是解决 Unicode 书写不直观,语意不明确的问题。</p>
<p>与 Unicode 使用方式相比,具有如下特点:</p>
<ul>
<li>兼容性良好,支持 IE8+,及所有现代浏览器。</li>
<li>相比于 Unicode 语意明确,书写更直观。可以很容易分辨这个 icon 是什么。</li>
<li>因为使用 class 来定义图标,所以当要替换图标时,只需要修改 class 里面的 Unicode 引用。</li>
<li>不过因为本质上还是使用的字体,所以多色图标还是不支持的。</li>
</ul>
<p>使用步骤如下:</p>
<h3 id="-fontclass-">第一步:引入项目下面生成的 fontclass 代码:</h3>
<pre><code class="language-html">&lt;link rel="stylesheet" href="./iconfont.css"&gt;
</code></pre>
<h3 id="-">第二步:挑选相应图标并获取类名,应用于页面:</h3>
<pre><code class="language-html">&lt;span class="selfAllIcon el-icon-self--xxx"&gt;&lt;/span&gt;
</code></pre>
<blockquote>
<p>"
selfAllIcon" 是你项目下的 font-family。可以通过编辑项目查看,默认是 "iconfont"。</p>
</blockquote>
</div>
</div>
<div class="content symbol">
<ul class="icon_lists dib-box">
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#el-icon-self-discover"></use>
</svg>
<div class="name">discover</div>
<div class="code-name">#el-icon-self-discover</div>
</li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#el-icon-self-settings"></use>
</svg>
<div class="name">settings</div>
<div class="code-name">#el-icon-self-settings</div>
</li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#el-icon-self-new"></use>
</svg>
<div class="name">new</div>
<div class="code-name">#el-icon-self-new</div>
</li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#el-icon-self-album"></use>
</svg>
<div class="name">album</div>
<div class="code-name">#el-icon-self-album</div>
</li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#el-icon-self-cc-book"></use>
</svg>
<div class="name">cc-book</div>
<div class="code-name">#el-icon-self-cc-book</div>
</li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#el-icon-self-iconset0481"></use>
</svg>
<div class="name">播放</div>
<div class="code-name">#el-icon-self-iconset0481</div>
</li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#el-icon-self-guanbi"></use>
</svg>
<div class="name">关闭</div>
<div class="code-name">#el-icon-self-guanbi</div>
</li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#el-icon-self-xuexiao"></use>
</svg>
<div class="name">刷新</div>
<div class="code-name">#el-icon-self-xuexiao</div>
</li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#el-icon-self-13"></use>
</svg>
<div class="name">image-o</div>
<div class="code-name">#el-icon-self-13</div>
</li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#el-icon-self-grade"></use>
</svg>
<div class="name">Grade</div>
<div class="code-name">#el-icon-self-grade</div>
</li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#el-icon-self-quanping"></use>
</svg>
<div class="name">全屏</div>
<div class="code-name">#el-icon-self-quanping</div>
</li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#el-icon-self-nav"></use>
</svg>
<div class="name">nav</div>
<div class="code-name">#el-icon-self-nav</div>
</li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#el-icon-self-mima"></use>
</svg>
<div class="name">密码</div>
<div class="code-name">#el-icon-self-mima</div>
</li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#el-icon-self-shipin"></use>
</svg>
<div class="name">视频</div>
<div class="code-name">#el-icon-self-shipin</div>
</li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#el-icon-self-character"></use>
</svg>
<div class="name">人物</div>
<div class="code-name">#el-icon-self-character</div>
</li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#el-icon-self-wenjian"></use>
</svg>
<div class="name">文件</div>
<div class="code-name">#el-icon-self-wenjian</div>
</li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#el-icon-self-xuexi-"></use>
</svg>
<div class="name">学习</div>
<div class="code-name">#el-icon-self-xuexi-</div>
</li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#el-icon-self-PPT"></use>
</svg>
<div class="name">PPT</div>
<div class="code-name">#el-icon-self-PPT</div>
</li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#el-icon-self-statistic"></use>
</svg>
<div class="name">statistic</div>
<div class="code-name">#el-icon-self-statistic</div>
</li>
</ul>
<div class="article markdown">
<h2 id="symbol-">Symbol 引用</h2>
<hr>
<p>这是一种全新的使用方式,应该说这才是未来的主流,也是平台目前推荐的用法。相关介绍可以参考这篇<a href="">文章</a>
这种用法其实是做了一个 SVG 的集合,与另外两种相比具有如下特点:</p>
<ul>
<li>支持多色图标了,不再受单色限制。</li>
<li>通过一些技巧,支持像字体那样,通过 <code>font-size</code>, <code>color</code> 来调整样式。</li>
<li>兼容性较差,支持 IE9+,及现代浏览器。</li>
<li>浏览器渲染 SVG 的性能一般,还不如 png。</li>
</ul>
<p>使用步骤如下:</p>
<h3 id="-symbol-">第一步:引入项目下面生成的 symbol 代码:</h3>
<pre><code class="language-html">&lt;script src="./iconfont.js"&gt;&lt;/script&gt;
</code></pre>
<h3 id="-css-">第二步:加入通用 CSS 代码(引入一次就行):</h3>
<pre><code class="language-html">&lt;style&gt;
.icon {
width: 1em;
height: 1em;
vertical-align: -0.15em;
fill: currentColor;
overflow: hidden;
}
&lt;/style&gt;
</code></pre>
<h3 id="-">第三步:挑选相应图标并获取类名,应用于页面:</h3>
<pre><code class="language-html">&lt;svg class="icon" aria-hidden="true"&gt;
&lt;use xlink:href="#icon-xxx"&gt;&lt;/use&gt;
&lt;/svg&gt;
</code></pre>
</div>
</div>
</div>
</div>
<script>
$(document).ready(function () {
$('.tab-container .content:first').show()
$('#tabs li').click(function (e) {
var tabContent = $('.tab-container .content')
var index = $(this).index()
if ($(this).hasClass('active')) {
return
} else {
$('#tabs li').removeClass('active')
$(this).addClass('active')
tabContent.hide().eq(index).fadeIn()
}
})
})
</script>
</body>
</html>
@font-face {font-family: "selfAllIcon"; @font-face {font-family: "selfAllIcon";
src: url('iconfont.eot?t=1546396965871'); /* IE9*/ src: url('iconfont.eot?t=1546593114550'); /* IE9 */
src: url('iconfont.eot?t=1546396965871#iefix') format('embedded-opentype'), /* IE6-IE8 */ src: url('iconfont.eot?t=1546593114550#iefix') format('embedded-opentype'), /* IE6-IE8 */
url('data:application/x-font-woff;charset=utf-8;base64,d09GRgABAAAAAA8oAAsAAAAAFyQAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAABHU1VCAAABCAAAADMAAABCsP6z7U9TLzIAAAE8AAAARAAAAFY8d0laY21hcAAAAYAAAADPAAAClLcRvpFnbHlmAAACUAAAClMAAA8U4TWXlWhlYWQAAAykAAAALwAAADYTw9BHaGhlYQAADNQAAAAcAAAAJAfeA5FobXR4AAAM8AAAAA8AAABAQAAAAGxvY2EAAA0AAAAAIgAAACIk2CEobWF4cAAADSQAAAAfAAAAIAEiANNuYW1lAAANRAAAAVIAAAKR7FrMk3Bvc3QAAA6YAAAAjQAAAK5mXzbdeJxjYGRgYOBikGPQYWB0cfMJYeBgYGGAAJAMY05meiJQDMoDyrGAaQ4gZoOIAgCKIwNPAHicY2BkYWCcwMDKwMHUyXSGgYGhH0IzvmYwYuRgYGBiYGVmwAoC0lxTGByeMT7PYG7438AQw9zA0AAUZgTJAQDlBgxNeJzlktENgkAMhv8DBFEUX3gzxAEMTwzCGCQwAwMwhNP45ANxiv/GwJb6QEx0AXv5Lnfttde0BbABEApXIQLcEw4qD9G6RR9it+gj3OR+xkk0GQMmTFmwYsOeA0dOvvS1b+cZoFtZu0/rV3ESvVyty2oBOY6SyUEy3iNDiq28D5BINhFicY5/RP4XyZb9/r7l2gljOQeG1BFMDO0tU0NqCxaGzgUrQ+oNNob+wM6A+vWGdAMcDJ0bjgb0z8mQXsGXhs6crw2dO98aiF8ZtEzZAHicjVd7jFTVGT/fOfc1d+6dO+87d5edneedgd0ddh57B6Tsyi6gG0SRVQSBuqg8itXQ+kQpXRofVAlpsDVKG22BbSttYmK7MVYk4DaVBJo2JlWpTUXT9g/aWEy0pZZ76Xfm7tJtMbWbu+f7zne+851zfud7nCEKIRePsWPsOqKQCGkjeVIgPeQesosQqEBOiidrTqnh1JJxKWfLIeD9hi3nbF8m5vvBH4N+6GvYJVtCpTQ0+6Ge5Gy+4TSdARwsVaCvH5o1M9kPqJYPgRxPmqhhJps1p5mGOo42bBzr57ObSTMENAPOSge/r0ZnRfHbluvpWdzTk5MDgVAgAMNXQsEp4AfNdC4opdeWg7JuacWSBN954cd/zicLlFKZirNmCacPrlmhpTRFW7/lqCt2tEM9ZUXa0su75u1cjIKit3HL6tu/l1y8sz4XhNN0dDZf1Zkd4qtG9wFfdXHPLXzVUOBYO1+zAB9UrmmPJCWrPSWnUrIc1zPvv/CD55R5TcYUJR6obOw7eFooSYFUSqqI7tH5d9Vg+QYrDNlZdgn7gw+5k1AffjI5tHdo1e0HTxMi4F0cZa+xwdZd3EN+0boDH+iSfxUNO5abC1IJcSvZCOkA4iVLiGfJRpTtRhNlTQfx6wSniTLknSTyJorMpJkGA5ImipBPSsjHZZTJ/KLQqowy5KUiV8FrcXwj3MY01yjZ/pJ8xWlOlvzJfO40FzeT/lJ8JZmS/wD0C2F1OB4flmJqo6IEIG7oO9SI2GZQSdkIsFGRqNEmRtQduhGHgFJpqDHpkn5A+Wx90XszYS4Nxjo7Y8GlZiJGQ1ZMi96vKhvQpyiENijq/VEtZoVo7P/Ug//yRC+jb6T003eqxtVP3+pnTpiJBdzU2oYgXL5dI2Ncvt/LFbUoKka1fysS/KPoY8fZz9HHriA3tPyrhJGYBj8s/RjFXggdpRXKssODNSfJcZTJEl5mvdZsRWgRuQHuVJw4FUB3wug3QEqySXRvjCpYMxJsM8Tg0mUYfxgL2IpaXJAaBU0QgzE9CgKN6kyQA7osZbbnXznvPiPLMfWqdYEIhNVdsmBaysZRtahskBR4zbew5tvzW/LesasfmxCEicd2TzAAVdPL8TkZ0YiGFNloptWoHKChJV3yW4xF9uwNQejVoBUWg9/6pkHDR/QUkTgYmPc4CRCLzCYLyRbE41I+K2bxvBGEIounjVQght0sohXBbhbFzSyPyDpnIzwy+1CHoQ5HqUIRVNMHy2liBqzXeOz5uGGewzRHpnMXVd2z1SGAoSpNtqgA6aR7NpkGpDSJdJk7gWETosuQR9eDoXc+P4+JQd1KJQqd65fgqrrAglE6x6GReCBSKc3tZELnPD0ahalU9Q5a97b6q8BT1SH3b755eMqnH3Oz6aR3L18HnqgOecfY8oIVpoJk4HkeCYUSDEJBea2kSEFNUue3deYg2s4dCgG8OMFuZQ0SJL1kgJBilqesepajyDGLxKV8S9ScArKEwCamgO2LNBDoNPa5liRHGg79kXdVQNcD8HJA055SddBV95N0uZyGH6ZLAKW0NztdhsEaldJl3d0Hb6m6rnqry2k9BVpMw4++yucMqCEofw5nlNO0gTOQugO1oVb/uKr3cp1TqJOkt6m6n3sPszfZKjyHSSqkir4gETlJTIc0bcJw39OREauWevNFGzMybjtZd5rF3jpmRjFiUvK693dRhMDrJ0ARRe/8iYkPRfHDiVabEpj3O0nGUwVkCQro9t5bZ9mNXOuEdx5nKSdw1oVHpvWxhZfo8wy0gHc4oAF7nsrxb4D2KBFxr++xd+k5EiZZ0kUaZB0hsZYnznDSSGYG4NyTY2ngBQRjuT5d0EUTK0BVwpJi5zByL9FS1W5gUNc6IcmDu/YkfTvTDdCdccs+hTvhjuoigEVVt9yiW6CypIIf2FYxlSpY8OJJSZIM6WQwEzyU6k+1IR3CviQNTfUOpPrpSejKum9nugC6MrSc7QLXRJO0PGX67d5B+CSPZufOzUUtbnjhSTkkS9IpVT1gmm2quljCJCUPIR8MHjDRH2XE5ml2lm3Ft0yR1MgCMkxWkZvwNtHb/GSHcckTHo/hOq+LTRHl9tzW7TZ5LXX8ey6Z/MpNrGrgp0jsoWY1JtmXfCFfZaPRoHDy2edO4kVF9rAvL0cf29X3+FeuuZu5D1AnW6tUR0v3Xnnj1X8Iw8sPP3xcCH48PD4yOFYerVZq3oW12yndvvZm3t4s9WzXYNuiwVsE4e4KrCpc3/XsG4LwxrNd1xc+oHb4irXs/NnHwzbdxhblQ2pwvL4yXBZ3TqJNYfJrRx4WXns6fEN9PKiG6Jcu2UT73i42ePfgBsruWjSykvgxi804Pdbynxpiw32mz09sHRDHOLxckLO5gMc1F+TZdjcSSwGkYvSveDXR/9WjVyJbjloAVrSMA95HU8yU2DvDu5bFO9M16j22hHWSMpmHuyva/EXTbN1ZvfWSzLdqE389zkzRjCeRnL0Q/H2azPT6GC1ay7oXrUtJ4VBOVXI71y8cKTgJ1h7j24nRSpzTnoThXWsksPrCi1iYYQJ6H6zM6gJ6/cJIDOrxm9ZDtjz0/Saf4j3qT4UHY5a330gkjAvheDw8te8xtp+NEZujKva1kkbpktdhm8jzWsF3yI+TSFMHpl1JxgM8Q5+4b90Kq+PAHVsPdFgr1t33dWDR9pgwue/JSQGibTH3XrZ70+bdjO3evGn3LfmxU73OkS2bd1C6Y/OWI0711Fiep/D8vkl0iX0FPQwx+vImro6TKN1NeN27+Gu2jfXiCzOPuaNJhsh13AOmoeTxzmsZTBe6CH+qJ9AhREnmLlGHLE8j+Vb9i+V5u6CVWEoz5PScl3GGAYYduLVe3lmEYfcwFNrhTHsBRnHAw8oEv5RllC3zboQM37V3Btu9Y5zlzd4ZUtaLltzv4sTizqID7zvDbpaOogmc7/GxFVCIysLVXDLmv9AuEp+O+mTK7y++JBA2jJmhj9cq9KycLPIfNZ2Ah/ezHa9KzQTPlQt5ucZaLU2lxlLE5Aoy5sceTIqmyP7ojbRf0e7doBuG/g5Y5njKSY2bbTvASBmzNcPw3oPouDZLG4/CflTc/xN8kXnr4BASOEQ3/FbXT8upDlPe5v2qbY42ouE3pw2qA9twrrYnzsc8PaGOyPKImoDEtbp+bYeivPkb/Cd+zXqXjbI0iZIURnIZT1RqXUCrMGX5o6RRYtlINlGHfCIfqeOLpJRPYMmy2epziubdLCn4Ij0kKYGG+wo8cMr7U3Xwwa0CPNTdXcffTvY5HL7w+5ZSQVNcl750yvvHJixea27bsxq655BWLTrOfsrK+IrqJN1kPlmKbwA/EPtYtpioc6+qy3XMtfUS/40XqfchxguBVyJMJUlxZkf23wT+CdjSDss9Y3UA3etKDiyoHrxz5Z0O/h+sLgDHPRwMh03DgJUIlhkOP40kZRh/sTo6LCo6jvtP1DqKfzgR++4ZMLQvclyxAeQ/4vxHPg9Lecf7mWYgrP8ClWypYAB4nGNgZGBgAOJVsqK34vltvjJwszCAwI0gN1UE/b+BhYG5AcjlYGACiQIA/IoIoQB4nGNgZGBgbvjfwBDDwgACQJKRARUIAABHFgJ5eJxjYWBgYKEAAwAIwABBAAAAAAAAxgH4AnwDIAOEA9oEaAT6BUoFpgX4BnYG3gcgB4oAAHicY2BkYGAQYDjOwMEAAkxAzAWEDAz/wXwGAB4hAfYAeJx1kMtKw0AUhv/0JibgQrHrcaOgkF42QsFFKbR2W6H7NJ20KUmmJNNCN76BC5/Hp/AF9Cnc+5seoUjNMIfvfHPOmSEAzvEBB/vvknvPDjxme67gBEq4Sn8jXCN3hevkB+EG+VHYxR2ehD1c4JkTnNops1u8Cjto4k24gjO8C1fpP4Vr5C/hOpqOJ9wgXwm7mDr3wh6unRd3kOvA6rma7VQcmiwymXULnUT9JBkzn+jFJgnyA3OAU50XsclUx28f2JHOdP47s9guutZGKspNqoYcrpPEqHVuVjq0/tLada/VisT7oUn5xAFyaASwjHP+1hl2jDFCGGSIymhZV/A8Yd5nTDCW8wntAhuagHOO1xy3U9qfjrjMFDrw0f6ndkSblfV/31lgy/u7tJZdijtnR0oayst1OcnQrMuzFU1I72NZdq3RQ4sr+lPvl3en31PddlsAAHicbclLDoIwFAXQXsofP7gQEokOXAYDN/AoDT6VNmnLZ/maOPVMj4jETyn+qxFBIkaCFBlyFChRYYc9DjiixknkA3tlF+1yr0NgM3pp9JrQu5+nTKmmt/ZVsbLm2+frrY3aSzI6GrQ0tMQTT1SoBzlSQbts1ebJZNJt1hs3suvuhQ8U2AdWQnwAFOUkvwAAAA==') format('woff'), url('data:application/x-font-woff2;charset=utf-8;base64,d09GMgABAAAAAA9cAAsAAAAAG8wAAA8OAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHEIGVgCFbAqmJJ4yATYCJANQCyoABCAFhREHgVUb5RZFBu4VJ2VC9v/hQIvDiuuxRVqNOHY7NtaA4+g0s9Sx4uMswaZn7Ok2SG3ja1NFgNAw6PuPj1gd/RqNnauqxJtYarxqQ0X6yHd7Pjxt7t3/xN0RRywSmYn2WpuxwkaswhpWg87A2tAZzIhgVs9KhtELI3s5QHARAFM5U8lwoDUcCI4pQPzraLUBZBekX0dqecGQsOnsEICg336pzc8H/qMgm114V3mkBb53W6cURilskAkVAjE4e/fdv2RTrXj5RZ2W4Hl8PCX8uvE3/hVXTlTjZJX+/7lXm7Qdg80QhdqZIineTVJ4ySvccn4xv6OU8/8ovwMi+fXsdnxeAUkNUY/dhJ5QE3JG+VmIV52TU3lLJ32wfWAA8NCphklJa9YGLER4aQz2G9jP3gOsJwGimBkM2BgJUa8IjAdisGQ68QOYY3x55g1iKgMIxBSvai0Zqb1hfM7Ud3qp3kUToJZnADjwAAqgGkB0iP5Jxn2GztxmUfCB4RyA8vRt+2M/J6Q4SmqBaohhMdAEM1x+oSuu5Pr+z5kUmxlweq/7MyivItvU8F95gJaGAmpKSCGGABYqcvBgQMCBQgQZJAAjAXT5dszXfC08ZxTFYQQIDZgUCAUYB4QaTAkNBlNDQzoWCIQYrAYQAlgMECyYBQgV2AAg5GADgeDBJgDBgM2Ahjaxy1gFHPBCh6AGiwNCBJYMhAysLxhmPDSgYgOgDSApB0hvSGOW+7MQgUDCK3kMCMScZFCQQwSFYu6VzXDpCm2Yelnr1fHlKpXYH+oUQvJgaqMNNTWVlUJ5Tz0Il0fCfFNTYwNfifG9FbPC4ZA96u8OVVWVWS5dFUJ1SXmpIIfx2R068cX8pH+2vmpG0RrU82ZBvXZPv289NB6oJjWYRXS8Rkg9tO7r98wHmy56vJGUC/Z4HE9klLs3QcJWbQTj6iymKCwlX3B5z8iCJJdKym6yUXVnhGjTm+m2vEnvBWIchrvZXZ777RF3ps2mG1+Mr9Zn/ZNm5vVBq2gUlpiylu5ScVp+0WlSQVRWcVa+0EZTe8NQtKD97DCWkyADQfbdeSwDI9eJLus/byQhqkRC5vlWEoehi3CLpwaKQg1v7HHx1my8NpPU7Mx05zLV3K0llWI/64Speo3WM0OujpesU9NyoY/Wo2wjjK5sR8YWpDtgQsz34C4tXgNJDdalaviJWpSTBntJTcwNratJjvqtDAJ4INhzcWFaxO/Gn0zKVDDzoi7hSzU4IRFgKTlvmuedllWPIOl8uCCWzKZFVaPX1CycWDbqo7Ar8owW32lnTvSl2/1n3BXxfEckW6jzp4rd0cxgLYqMnAZszTeClI+vb7FNQxyL6Wz2RpeNVF1LcorhzQNWRspcH3fzan3qImDDbewK0YmOB+Fjb1ZhLAZ0LESyD4yH3eHcw/pg8kFXKH2PE9VdWu4hHzTozjAhXIpWha71BA0Wsx/VeaH3I/c77E6t2u9uLeNlQ1hLZRNzRCeAKesMoDBjtR/u26Tzs6a4fiM107q0YT1ucc3Kpg3EShlybPgiV6Ouml46PT4/jHoIMAfCOWgxsVUBu5o2VwWjiAMdK4SXOBdWRTP3GwPxex3NddQFTz7sDvwcHiIhqM0dsrzAcWfZ8eW8y7zXAHgc18BlgTzmswBqimre7OszwEt5/vJFRau3l1gbGuN3dV7NY84QtcYBfJaEAESele3UnfqizkWPNQfZobkdRg32fa9ZcPa12Vu5AUYCct/DL7k2KJbA70PqaHCAxRRkUEHQRtyPIwRAdRQSVquE08RcG0yAApNM4Kk/YkbBFPc4EEHRcDOvS3Cj5ISiPPJRlJAaKybDxFKIqWiQiJVH1mILJox/AA56vsPnCzVngag1kkJs7QlvWtEb52hEIgFunq1zQ3jBZJvWjnS+xpcY7IzmivX+ZGFxQnvILPA8K3JRywelI5EbX5qCJ7+Oi17/3Bg49ulg+l5NIHH/ri6nEvsplYOMWYvovB2u+L3C+w40Gu7nq844ern74u07TZnyvLHCYZ79VYw4dITnTKbNpm6vzQROWC3hwnoARzKZ4kHB9boRC/EPLPvcsDUNPRQqY38wJRRW5n6tTwh3cKHsgzVY5IMesDH85gUjAIIIEwIa3CBwrkaNXmxt1uxYjeN6QoK+xPlOmLG6Ue4i70ccIRbj+UksZSTXxzsWYo6WrfNHyi7n2nXMktbENFqIhTmJTuaHspXIl5dHgmEOrBc4wQ9EwrEEDrzMJ2JzIUZ6sDK6kwOB05/nABUgAIEGXl7tQcDvZxWhQYsRcN4al6iG+gWS17yqDH7DFn7fo6xsWLjr94p3Y2FZWfpjZJX5D6cLo8a+o//frYvq1Rn4165zZDe9PG7M2l4STf194UIx+8+J9jI0VlOl2INdaz4a3jueri0sXEs7tqy4z3nAsa9L9b5HRX//drROxKA1iGkWbHMLHl0zfd6o8RVTyFabyo9dWt+O9jG9zKvntk02jZ8VY6rRFr0K2YnSMnpDpxomHNT3gWq5uXd0tNKd9hVyGxaNdHHazLeXM7UeNn3Fk1DrqN5mLoBTf/Moa+iTFWapJ1N7+a02k3ONLGqYW6F9pu2dOnSLdNbdWLldY3uqbZv+yRP9NqO59saa0eqSyYaj6tGa+aNtqcVEp9eR4v1XPXr+aI36qGFyiVozOvGEEmYLpsQF9aNPidb+LTxKGdGaLVqLDh2WWmtvnPfcvCm1ZpOJZppVuHctBYFJs2i287cgxp116nVJjD3UKGW7VqFWsAm6oEuVozPcCmmrf85R5bDkx+0M0lOq9aTldJ41C4xqhetI/9pDpo7tPb/7vJVJE235XRwoneEArjgIJhMWqpFWBZQT37MEWe6JFxqeJl+bTgIqmzp6zpbmJHlVixt1JYnNypl3nJwyqkWDh10kKVMV1W+eqkVlkWHSyp3aEnXiNL6kz6uXlRs+qn1CHa6GN/iQq0BbwB3i8rX5C454/NxJ/hIvueqoP/rq5qt2RZzCfvWOx1d3kkkyqSSmk/cPqnnH4+fj0lS7eeitXrwfetr2nIiVis4zotFze13qkM1B8imZe0UfP6ZG7QUg0qnh/YgGLbfZauPyuXZKnM16bRZodEaAYY781TQkeGbPdgQLQQ49ao45I0gIJi9fZxCCHHNmh4/8xyAn6jzuhyqzD2+rGTVS1kIx/MnM6hulqX57732H0/fvvDgyZpKqlaA6be1j7733sDkwa1SMq4dRUKzzzY9Ilo/nk66+9dTYKE3DXF/hx8MwCIc/GtMFAwJeDRoAoxEGVS+0DhKMSfZf5F4qE+73mKmocVdwVnCUz95V3+drp2yHtkJbX8EW/ZZ1a0Y3HL3ERFOynyI83mS6mF69dw2qRQLTTstbqtIVvmsr6EqqPUKygpdZaM67qGEVmgmhpk7E9SN++qtx05sKwytEZQ/RxBtJZBHffX3Pka1K23Sni56MF05hysETUyjr7olTdBKf/3WKma4/aV2UJExsPbz7er4okg7txDidjBrVcQOQHYKoTiDoOcQTTjmTZMORNMRmZJJIt0HUSK3DW9UAFuGff4npMzECSu4QC/e9nKwAKb8rhPq4acipQHbHPvsC7gSCIHAb9vK41X708tkWZn3ulBnZ3f27xB5iBS6Pm/GlO+BwnsX5QoXnoXe4PDZmT6NpuHFcRt/LFkkPcUlHjMX5ZXYwFPdnqNHEucYPegcxME79B5VbY9Bmq+Qf9E7GQBzXYtlag8atMjUtUp3aDy4ZT0GD6GH7fKjJjGme06TKTiPT2TLO8lmpmjsiqt34Ll1PWPWJ8aaADRVX691kLHHra9QybNX6CCnCbsX4K8SCoCbE72Q3PLqnI7r7Dzc8vI+K7t3Yd9WhIArH1Q3SL405MNQrPSj1qr3sQdabAk9qux9Zqk2dx2Zxec+85VlsnsoFmmdn1tAEmMXh9n3N7sOuiBMnLZB/l9edfzNzQdCCm2NNyhe7F3dAHU/3cszEGQWjanaOb5kfYo/IWJPZn+nHIC4iw0MChHAfVctdgInlqFIdHqXUi+eeOpVL59Jd9TS3vLyMp+yMg6RPdnYfkkEkSkAwP4Ppw7ihJq310GI2i21i1WnpGTty/PiUhMdOqW3v2/jY1Q9Xx/JXbXFxFuPF8qszepmJnKbeifp3IUVLm1Wqu0Jd53Ynu4OG1h5bi7bW3ZpXeO5F5PNzxUOiuekD1lszEzLVmgYsCYvObDhU9j7kvXzmlP1xB+s+i0LXW/dH7IcVKgpc0jwTQcndES/0L9bdWndT5yN+csUPHxzyjRKHpKwirq+hvDw7lqQecPr9oZLojE+2FvYjtAN6js2p1qGaPjwUcba3of5QPaqDaPav+IQEMbxYN/CoVdYTtJrXwQtxQkLbMqv/ywfK/we/l7Lft8JOQ/iKwf8R/3+RJz5N3k7YuGBi6UcApScw2tKQjjlFTTNPOoMAJcdJ+/SkcZLXbdrOuu4KtSDuOEYbQHrkAfkoLfaa9s/b6hytlzdtAzkRNyOVlpF+Uh8ptFyWUhekq4bSWtKBQBvnPWCjuroz9lLDP1HlYdCOF0J3of43VvaHVVAhTOlkeEtdoZxcsCB4pTPAv0xqynNE94htYoDo+m8oSukZATj/n4ZFAPDsGuCfpSwN7rchOjC1rFGZRz0sJpBCv5hChkqiaEyMKB7TQJSMMS/mURWtG5ZBiwmgYEQcgCrIlRYzCEL+YgIlri2mCMIDUTTmmyCewshEyZioxTzaMYnNlKEis/Ss9MgIgkdV3BXpGIzMkUFGZw7OvXvhkyz47IDvx9p7/pAzIVjNxlOi8BUDcnk15Es5FzHKcPTqpeCOoXNRJY4n1DKuRNJ+MjFUjWMdfW3gkRFEW/6oFPeNScdg+DKQ0ZmDlOHe476eLyzPDjj+FAjxT06t0+bhKDNj03TiK4Gc9oRtJLBfyjlhhlHK4Ry98tJquSx/F5VEl3VCLWNVDivtTdiFTFp9XG+0FxMdC/Skr7cuINLQ2NTc0trW3tHZ1d3TmzpN2gIK1Psp63hB7mUUoVDmZsBrG1xx9l2tR0WMdkgT3lOZLrezjv0MoaDu7Yw3gtiYfW07wxF7/xRNefnNAJeWJw+dXJGvLX1dAYMW5O4Vw4kgdPK5jZqWj59+FhBSvaRrNQA=') format('woff2'),
url('iconfont.ttf?t=1546396965871') format('truetype'), /* chrome, firefox, opera, Safari, Android, iOS 4.2+*/ url('iconfont.woff?t=1546593114550') format('woff'),
url('iconfont.svg?t=1546396965871#selfAllIcon') format('svg'); /* iOS 4.1- */ url('iconfont.ttf?t=1546593114550') format('truetype'), /* chrome, firefox, opera, Safari, Android, iOS 4.2+ */
url('iconfont.svg?t=1546593114550#selfAllIcon') format('svg'); /* iOS 4.1- */
} }
.selfAllIcon { .selfAllIcon {
font-family:"selfAllIcon" !important; font-family: "selfAllIcon" !important;
font-size:16px; font-size: 16px;
font-style:normal; font-style: normal;
-webkit-font-smoothing: antialiased; -webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale; -moz-osx-font-smoothing: grayscale;
} }
.el-icon-self-discover:before { content: "\e67e"; } .el-icon-self-discover:before {
content: "\e67e";
}
.el-icon-self-settings:before {
content: "\e68a";
}
.el-icon-self-new:before {
content: "\e71e";
}
.el-icon-self-album:before {
content: "\e734";
}
.el-icon-self-cc-book:before {
content: "\e615";
}
.el-icon-self-settings:before { content: "\e68a"; } .el-icon-self-iconset0481:before {
content: "\e768";
}
.el-icon-self-new:before { content: "\e71e"; } .el-icon-self-guanbi:before {
content: "\e611";
}
.el-icon-self-album:before { content: "\e734"; } .el-icon-self-xuexiao:before {
content: "\e632";
}
.el-icon-self-cc-book:before { content: "\e615"; } .el-icon-self-13:before {
content: "\e6ce";
}
.el-icon-self-iconset0481:before { content: "\e768"; } .el-icon-self-grade:before {
content: "\e66c";
}
.el-icon-self-13:before { content: "\e6ce"; } .el-icon-self-quanping:before {
content: "\e743";
}
.el-icon-self-grade:before { content: "\e66c"; } .el-icon-self-nav:before {
content: "\e66b";
}
.el-icon-self-nav:before { content: "\e66b"; } .el-icon-self-mima:before {
content: "\e607";
}
.el-icon-self-mima:before { content: "\e607"; } .el-icon-self-shipin:before {
content: "\e60e";
}
.el-icon-self-character:before { content: "\e62e"; } .el-icon-self-character:before {
content: "\e62e";
}
.el-icon-self-wenjian:before { content: "\e650"; } .el-icon-self-wenjian:before {
content: "\e650";
}
.el-icon-self-xuexi-:before { content: "\e609"; } .el-icon-self-xuexi-:before {
content: "\e609";
}
.el-icon-self-PPT:before { content: "\e602"; } .el-icon-self-PPT:before {
content: "\e602";
}
.el-icon-self-statistic:before { content: "\e601"; } .el-icon-self-statistic:before {
content: "\e601";
}
!function(a){var c,i='<svg><symbol id="el-icon-self-discover" viewBox="0 0 1024 1024"><path d="M544 288c0-52.928-43.072-96-96-96s-96 43.072-96 96 43.072 96 96 96S544 340.928 544 288zM416 288c0-17.632 14.368-32 32-32s32 14.368 32 32-14.368 32-32 32S416 305.632 416 288z" ></path><path d="M304 448C259.904 448 224 483.904 224 528 224 572.128 259.904 608 304 608s80-35.872 80-80C384 483.904 348.096 448 304 448zM304 544C295.168 544 288 536.832 288 528S295.168 512 304 512s16 7.168 16 16S312.832 544 304 544z" ></path><path d="M640 736m-64 0a2 2 0 1 0 128 0 2 2 0 1 0-128 0Z" ></path><path d="M887.84 268.672c39.744-60.672 64.256-113.184 64.224-148.96 0-22.976-9.312-36.928-17.12-44.576-38.08-37.376-110.016-3.2-160.768 25.664-15.328 8.736-20.704 28.288-11.968 43.648 8.736 15.36 28.32 20.704 43.648 11.968 49.408-28.16 73.248-33.088 82.496-33.696-1.504 41.376-95.328 202.432-330.208 433.504-226.368 222.72-398.656 324.672-442.24 325.376 0.96-11.36 8.768-44.448 58.528-118.688 1.312-1.888 2.4-3.968 3.232-6.144l2.304-5.92c0.256-0.64 0.288-1.344 0.512-2.016 8.8-10.24 10.816-25.088 3.392-37.248C147.328 651.616 128 582.592 128 512 128 300.256 300.256 128 512 128c59.296 0 116.096 13.12 168.864 39.04 15.936 7.776 35.04 1.184 42.816-14.656 7.776-15.84 1.248-35.008-14.624-42.816C647.424 79.328 581.12 64 512 64 264.96 64 64 264.96 64 512c0 76.064 19.488 150.496 56.064 216.64-0.192 0.576-0.64 1.056-0.8 1.632C51.168 832.608 35.2 895.68 68.928 928.768c11.008 10.784 25.632 15.584 43.072 15.584 39.328 0 92.864-24.928 150.08-60.896C336.096 933.408 421.952 960 512 960c247.04 0 448-200.96 448-448C960 424.864 934.912 341.248 887.84 268.672zM512 896c-68.288 0-133.728-17.888-191.84-51.552 128.288-92.256 258.432-218.56 282.848-242.624 89.152-87.712 178.56-187.04 244.64-275.968C879.2 382.592 896 446.112 896 512 896 723.744 723.744 896 512 896z" ></path></symbol><symbol id="el-icon-self-settings" viewBox="0 0 1024 1024"><path d="M512 608c-52.928 0-96-43.072-96-96s43.072-96 96-96 96 43.072 96 96S564.928 608 512 608zM512 480c-17.632 0-32 14.368-32 32s14.368 32 32 32 32-14.368 32-32S529.632 480 512 480z" ></path><path d="M631.04 960c-10.016 0-19.712-4.736-25.856-13.12l-73.408-100.48c-13.664 0.832-25.92 0.864-39.584 0l-73.376 100.48c-7.904 10.816-21.728 15.616-34.656 11.872-34.592-9.888-67.488-23.456-97.76-40.32-11.712-6.528-18.112-19.648-16.032-32.896l19.2-123.264c-9.824-8.704-19.136-18.016-27.84-27.808l-123.264 19.2c-13.056 1.984-26.336-4.352-32.896-16.064-16.896-30.368-30.464-63.296-40.32-97.824-3.68-12.864 1.056-26.72 11.904-34.624l100.448-73.376C177.152 524.96 176.928 518.4 176.928 512s0.224-12.96 0.672-19.776L77.12 418.816c-10.816-7.936-15.584-21.728-11.904-34.656 9.856-34.496 23.424-67.392 40.32-97.792 6.56-11.712 19.712-18.048 32.896-16.064l123.264 19.2c8.736-9.824 18.016-19.104 27.84-27.84l-19.2-123.264C268.288 125.184 274.72 112.064 286.432 105.536c30.4-16.896 63.296-30.464 97.76-40.32 12.928-3.648 26.72 1.056 34.656 11.904l73.376 100.448c13.568-0.896 25.824-0.896 39.584 0l73.408-100.48c7.904-10.848 21.664-15.52 34.656-11.904 34.464 9.856 67.328 23.424 97.76 40.32 11.712 6.528 18.112 19.648 16.064 32.896L734.432 261.76c9.824 8.736 19.104 18.016 27.808 27.776l123.328-19.2c13.152-2.016 26.336 4.384 32.864 16.032 16.896 30.4 30.496 63.296 40.352 97.824 3.648 12.896-1.088 26.688-11.904 34.624l-100.48 73.408c0.448 6.816 0.672 13.376 0.672 19.776s-0.224 12.96-0.672 19.776l100.48 73.376c10.816 7.904 15.552 21.728 11.904 34.624-9.856 34.496-23.424 67.424-40.352 97.824-6.528 11.712-19.488 18.048-32.896 16.064l-123.296-19.2c-8.672 9.76-17.984 19.072-27.808 27.776l19.232 123.296c2.048 13.248-4.352 26.368-16.064 32.896-30.24 16.832-63.136 30.4-97.76 40.32C636.928 959.584 633.984 960 631.04 960zM337.152 872.672c13.984 6.752 28.544 12.704 43.552 17.92l70.528-96.576c6.784-9.28 17.984-14.272 29.472-12.896 20.576 2.336 42.08 2.336 62.624 0 11.84-1.376 22.72 3.616 29.472 12.896l70.528 96.576c15.04-5.184 29.6-11.168 43.552-17.92l-18.432-118.368c-1.76-11.36 2.72-22.848 11.712-30.016 16.384-12.992 31.264-27.872 44.16-44.128 7.168-8.992 18.4-13.504 30.016-11.744l118.368 18.432c6.752-14.016 12.736-28.576 17.92-43.552l-96.576-70.528c-9.28-6.784-14.208-18.016-12.896-29.472 1.152-10.272 1.952-20.704 1.952-31.296s-0.768-21.024-1.952-31.296c-1.312-11.424 3.616-22.688 12.896-29.472l96.576-70.56c-5.152-15.008-11.168-29.568-17.92-43.552l-118.4 18.432c-11.328 1.696-22.816-2.688-29.984-11.744-12.896-16.256-27.776-31.104-44.16-44.128-8.992-7.168-13.472-18.592-11.712-29.984l18.432-118.4c-14.016-6.752-28.576-12.736-43.52-17.92l-70.56 96.576c-6.784 9.28-17.696 14.368-29.472 12.928-20.544-2.368-42.016-2.4-62.56 0-11.52 1.344-22.752-3.616-29.536-12.896l-70.528-96.576c-14.976 5.152-29.536 11.136-43.552 17.92l18.432 118.368c1.76 11.392-2.72 22.848-11.744 30.016-16.32 12.928-31.2 27.808-44.128 44.128-7.168 9.024-18.56 13.472-30.016 11.744l-118.368-18.432c-6.752 14.016-12.736 28.576-17.92 43.552l96.576 70.56c9.312 6.784 14.24 18.08 12.896 29.536C241.696 491.008 240.928 501.408 240.928 512s0.768 20.992 1.952 31.264c1.344 11.456-3.616 22.688-12.896 29.504l-96.576 70.528c5.184 15.008 11.168 29.568 17.92 43.552l118.368-18.432c11.392-1.76 22.848 2.752 30.016 11.744 12.896 16.32 27.744 31.168 44.128 44.128 9.024 7.168 13.504 18.624 11.744 30.016L337.152 872.672z" ></path></symbol><symbol id="el-icon-self-new" viewBox="0 0 1024 1024"><path d="M963.072 446.336c0-211.744-200.96-384-448-384s-448 172.256-448 384c0 116.48 63.008 226.048 172.896 300.672 14.656 9.984 34.528 6.144 44.448-8.512 9.952-14.624 6.112-34.528-8.512-44.448-92.032-62.496-144.832-152.768-144.832-247.712 0-176.448 172.256-320 384-320 211.744 0 384 143.552 384 320 0 176.448-172.256 320-384 320-1.984 0-3.68 0.768-5.568 1.12-15.104-2.688-30.464 5.216-35.776 20.192-6.144 17.376-46.368 46.656-94.144 73.792 17.472-58.208 9.088-70.688 3.52-78.976-6.72-9.984-17.92-15.936-29.92-15.936-17.664 0-32 14.304-32 32 0 5.824 1.536 11.264 4.256 15.936-3.232 18.24-17.216 60.864-33.088 99.872-4.928 12.096-1.984 25.984 7.36 35.072 6.112 5.888 14.112 8.992 22.272 8.992 4.384 0 8.8-0.896 12.992-2.752 36.48-16.256 147.68-69.12 187.616-125.664C766.144 826.496 963.072 655.904 963.072 446.336z" ></path><path d="M342.624 604.544c4.672 2.4 9.664 3.52 14.592 3.52 11.616 0 22.816-6.336 28.512-17.408l71.584-139.488 91.584 142.208c5.824 9.024 15.744 14.528 26.464 14.688 0.16 0 0.32 0 0.448 0 10.56 0 20.48-5.216 26.432-13.984l128.8-188.864c9.984-14.624 6.176-34.528-8.416-44.48-14.624-9.952-34.528-6.208-44.48 8.416l-101.632 148.992-95.456-148.288c-6.176-9.6-17.152-14.752-28.48-14.656-11.424 0.576-21.696 7.2-26.912 17.344l-96.896 188.896C320.672 577.184 326.88 596.48 342.624 604.544z" ></path></symbol><symbol id="el-icon-self-album" viewBox="0 0 1024 1024"><path d="M256 448m-64 0a2 2 0 1 0 128 0 2 2 0 1 0-128 0Z" ></path><path d="M714.688 256 181.312 256C116.64 256 64 308.64 64 373.312l0 405.376C64 843.36 116.64 896 181.312 896l533.376 0C779.36 896 832 843.36 832 778.688L832 373.312C832 308.64 779.36 256 714.688 256zM768 778.688C768 808.096 744.096 832 714.688 832L181.312 832C151.936 832 128 808.096 128 778.688L128 373.312C128 343.936 151.936 320 181.312 320l533.376 0C744.096 320 768 343.936 768 373.312L768 778.688z" ></path><path d="M842.688 128 256 128C238.336 128 224 142.336 224 160s14.336 32 32 32l586.688 0C872.096 192 896 215.936 896 245.312L896 640c0 17.696 14.304 32 32 32s32-14.304 32-32L960 245.312C960 180.64 907.36 128 842.688 128z" ></path><path d="M672.64 448.736c-129.056 0-143.936 72.672-152.832 116.096-7.2 35.168-10.4 39.968-28.64 42.88-34.976 5.568-44 0.192-58.912-8.8-15.456-9.312-36.672-22.08-77.952-23.168C227.616 573.12 194.112 723.84 192.704 730.24c-3.712 17.28 7.264 34.304 24.544 38.016 2.272 0.512 4.544 0.704 6.752 0.704 14.752 0 28-10.24 31.264-25.248 0.224-1.056 23.232-104.032 95.488-104.032 0.64 0 1.28 0 1.92 0.032 24.352 0.608 33.632 6.208 46.496 13.984 24.032 14.464 48.352 25.76 102.08 17.184 64.192-10.272 74.432-60.192 81.184-93.216 8.32-40.512 13.312-64.928 90.144-64.928 17.696 0 32-14.336 32-32S690.304 448.736 672.64 448.736z" ></path></symbol><symbol id="el-icon-self-cc-book" viewBox="0 0 1024 1024"><path d="M682.697195 859.707167 242.160807 859.707167c-12.152784 0-22.025643-9.893324-22.025643-22.026666 0-12.130272 9.872858-22.025643 22.025643-22.025643l440.537412 0c12.130272 0 22.025643 9.895371 22.025643 22.025643C704.723861 849.813842 694.82849 859.707167 682.697195 859.707167z" ></path><path d="M841.773214 158.324912c-12.955056 0-23.51967 10.543124-23.51967 23.520693l0 705.638185c0 12.95608-10.56666 23.520693-23.521716 23.520693L277.262256 911.004483c-38.911218 0-70.564126-31.650861-70.564126-70.564126 0-38.910195 31.652907-70.563102 70.564126-70.563102l423.382707 0c38.911218 0 70.565149-31.652907 70.565149-70.563102L771.210112 134.803195c0-38.911218-31.653931-70.564126-70.565149-70.564126L230.218824 64.23907c-38.911218 0-70.563102 31.652907-70.563102 70.564126l0 705.637162c0 64.823378 52.785204 117.608581 117.606535 117.608581l517.468548 0c38.909172 0 70.563102-31.653931 70.563102-70.565149L865.293907 181.845605C865.293907 168.868035 854.727247 158.324912 841.773214 158.324912zM230.218824 111.281479l470.426139 0c12.955056 0 23.521716 10.543124 23.521716 23.521716l0 564.510958c0 12.955056-10.56666 23.520693-23.521716 23.520693l-423.382707 0c-26.439162 0-50.90232 8.820899-70.564126 23.566742L206.698131 134.803195C206.698131 121.824603 217.241254 111.281479 230.218824 111.281479z" ></path></symbol><symbol id="el-icon-self-iconset0481" viewBox="0 0 1024 1024"><path d="M512 42.666667C253.866667 42.666667 42.666667 251.733333 42.666667 512s211.2 469.333333 469.333333 469.333333c260.266667 0 469.333333-209.066667 469.333333-469.333333S772.266667 42.666667 512 42.666667zM512 938.666667C277.333333 938.666667 85.333333 746.666667 85.333333 512 85.333333 277.333333 277.333333 85.333333 512 85.333333c234.666667 0 426.666667 192 426.666667 426.666667C938.666667 746.666667 746.666667 938.666667 512 938.666667zM716.8 494.933333 716.8 494.933333l-4.266667-2.133333c0 0 0 0-2.133333 0l-292.266667-168.533333 0 0C413.866667 322.133333 409.6 320 405.333333 320c-12.8 0-21.333333 8.533333-21.333333 21.333333l0 341.333333c0 12.8 8.533333 21.333333 21.333333 21.333333 4.266667 0 8.533333-2.133333 12.8-4.266667l0 0 292.266667-168.533333c0 0 0 0 2.133333 0l4.266667-2.133333 0 0c4.266667-4.266667 8.533333-10.666667 8.533333-17.066667S721.066667 499.2 716.8 494.933333zM426.666667 646.4 426.666667 377.6 661.333333 512 426.666667 646.4z" ></path></symbol><symbol id="el-icon-self-13" viewBox="0 0 1024 1024"><path d="M147.2 180.8h728c41.6 0 76.8 35.2 76.8 76.8v552c0 41.6-35.2 76.8-76.8 76.8h-728c-41.6 0-76.8-35.2-76.8-76.8v-552c0-41.6 35.2-76.8 76.8-76.8v0zM147.2 926.4c243.2 0 486.4 0 728 0 64 0 115.2-51.2 115.2-115.2 0-184 0-368 0-552 0-64-51.2-115.2-115.2-115.2-243.2 0-486.4 0-728 0-64 0-115.2 51.2-115.2 115.2 0 184 0 368 0 552 0 62.4 51.2 115.2 115.2 115.2z" ></path><path d="M201.6 419.2c0 44.8 36.8 81.6 81.6 81.6s81.6-36.8 81.6-81.6c0-44.8-36.8-81.6-81.6-81.6-44.8 0-81.6 36.8-81.6 81.6v0zM240 419.2c0-24 19.2-43.2 43.2-43.2s43.2 19.2 43.2 43.2c0 24-19.2 43.2-43.2 43.2-22.4 0-43.2-19.2-43.2-43.2z" ></path><path d="M675.2 499.2l203.2 203.2c8 8 8 19.2 0 27.2v0c-8 8-19.2 8-27.2 0l-203.2-203.2c-14.4-14.4-40-14.4-54.4 0l-163.2 161.6c-30.4 30.4-80 30.4-110.4 0l-25.6-25.6c-14.4-14.4-40-14.4-54.4 0l-67.2 67.2c-8 8-19.2 8-27.2 0v0c-8-8-8-19.2 0-27.2l67.2-67.2c30.4-30.4 80-30.4 110.4 0l25.6 25.6c14.4 14.4 40 14.4 54.4 0l161.6-161.6c30.4-30.4 80-30.4 110.4 0z" ></path></symbol><symbol id="el-icon-self-grade" viewBox="0 0 1024 1024"><path d="M863.721739 554.295652c24.486957-48.973913 28.93913-102.4 28.93913-160.278261C892.66087 189.217391 734.608696 22.26087 527.582609 22.26087 320.556522 22.26087 162.504348 189.217391 162.504348 394.017391c0 57.878261 4.452174 111.304348 28.93913 160.278261L51.2 792.486957c0 0 95.721739 35.617391 193.669565 55.652174 64.556522 73.46087 117.982609 151.373913 117.982609 151.373913l129.113043-247.095652c8.904348 0 20.034783 0 26.713043 0 8.904348 0 20.034783 0 26.713043 0l138.017391 233.73913c0 0 57.878261-64.556522 124.66087-138.017391 97.947826-20.034783 193.669565-55.652174 193.669565-55.652174L863.721739 554.295652 863.721739 554.295652 863.721739 554.295652zM353.947826 903.791304c0 0-46.747826-60.104348-89.043478-102.4-62.330435-17.808696-144.695652-35.617391-144.695652-35.617391L215.930435 601.043478c51.2 66.782609 135.791304 129.113043 220.382609 149.147826L353.947826 903.791304 353.947826 903.791304zM520.904348 710.121739c-80.13913 0-309.426087-48.973913-316.104348-316.104348C198.121739 224.834783 351.721739 64.556522 520.904348 64.556522c169.182609 0 329.46087 162.504348 329.46087 329.46087C852.591304 650.017391 601.043478 710.121739 520.904348 710.121739L520.904348 710.121739zM685.634783 903.791304l-82.365217-151.373913c84.591304-20.034783 182.53913-84.591304 233.73913-151.373913l95.721739 164.730435c0 0-82.365217 17.808696-144.695652 35.617391L685.634783 903.791304 685.634783 903.791304zM527.582609 175.86087c-117.982609 0-213.704348 95.721739-213.704348 213.704348s95.721739 213.704348 213.704348 213.704348c117.982609 0 213.704348-95.721739 213.704348-213.704348S645.565217 175.86087 527.582609 175.86087L527.582609 175.86087 527.582609 175.86087zM534.26087 558.747826c-53.426087 0-169.182609-20.034783-178.086957-178.086957-4.452174-84.591304 95.721739-164.730435 178.086957-164.730435 84.591304 0 162.504348 95.721739 164.730435 178.086957C701.217391 505.321739 587.686957 558.747826 534.26087 558.747826L534.26087 558.747826zM534.26087 558.747826" ></path></symbol><symbol id="el-icon-self-nav" viewBox="0 0 1024 1024"><path d="M888.838 319.775l-751.1 0c-23.944 0-43.339-19.553-43.339-43.646l0-40.012c0-24.093 19.395-43.645 43.339-43.645l751.1 0c23.923 0 43.334 19.554 43.334 43.645l0 40.012c-0.001 24.093-19.411 43.646-43.333 43.646l0 0zM888.838 587.509l-751.1 0c-23.944 0-43.339-19.533-43.339-43.64l0-39.998c0-24.115 19.395-43.647 43.339-43.647l751.1 0c23.923 0 43.334 19.533 43.334 43.647l0 39.998c-0.001 24.107-19.411 43.64-43.333 43.64l0 0zM888.838 876.17l-751.1 0c-23.944 0-43.339-19.532-43.339-43.627l0-40.017c0-24.093 19.395-43.641 43.339-43.641l751.1 0c23.923 0 43.334 19.548 43.334 43.641l0 40.017c-0.001 24.094-19.411 43.627-43.333 43.627l0 0z" ></path></symbol><symbol id="el-icon-self-mima" viewBox="0 0 1024 1024"><path d="M791.366 459.07l-459.174-0.791c-0.186-61.936-16.847-157.926 36.027-216.192 32.142-35.52 86.326-72.068 144.127-72.068 85.395 0 149.246 64.412 180.166 144.126 9.009 22.992 16.936 35.38 36.037 36.037 47.899 1.45 44.986-49.075 36.029-72.067C719.914 163.03 636.118 97.953 512.346 97.953c-84.12 0-130.475 17.265-180.154 72.065-76.009 83.746-72.259 219.151-72.067 288.26l-26.795 0.791c-25.045 0-45.273 20.034-45.273 44.667v378.249c0 24.774 20.269 44.755 45.273 44.755h557.99c25.009 0 45.273-19.989 45.273-44.755V503.737c0.046-24.678-20.218-44.667-45.227-44.667zM765.68 818.35c0 9.966-3.476 18.514-10.58 25.52-7.045 7.051-15.56 10.58-25.517 10.58H296.362c-9.966 0-18.472-3.478-25.475-10.58-7.051-7.049-10.624-15.553-10.624-25.52V565.635c0-19.878 16.166-36.054 36.098-36.054h433.212c19.932 0 36.107 16.176 36.107 36.054V818.35z" fill="" ></path></symbol><symbol id="el-icon-self-character" viewBox="0 0 1024 1024"><path d="M921.087359 990.72v-35.2A410.24 410.24 0 0 0 661.887359 576a307.2 307.2 0 1 0-300.8 0 410.24 410.24 0 0 0-256 380.8v35.2H101.887359a32 32 0 0 0 32 32 32 32 0 0 0 32-32v-34.56a345.6 345.6 0 0 1 691.2 0v35.2a31.36 31.36 0 0 0 31.36 30.08 32 32 0 0 0 32.64-32zM270.207359 307.2A241.28 241.28 0 1 1 511.487359 548.48 241.28 241.28 0 0 1 270.207359 307.2z" ></path></symbol><symbol id="el-icon-self-wenjian" viewBox="0 0 1024 1024"><path d="M752 80H272c-70.4 0-128 57.6-128 128v608c0 70.4 57.6 128 128 128h353.6c33.6 0 65.6-12.8 91.2-36.8l126.4-126.4c24-24 36.8-56 36.8-91.2V208c0-70.4-57.6-128-128-128zM208 816V208c0-35.2 28.8-64 64-64h480c35.2 0 64 28.8 64 64v464h-96c-70.4 0-128 57.6-128 128v80H272c-35.2 0-64-28.8-64-64z m462.4 44.8c-4.8 4.8-9.6 8-14.4 11.2V800c0-35.2 28.8-64 64-64h75.2l-124.8 124.8z" fill="#4A576A" ></path><path d="M368 352h288c17.6 0 32-14.4 32-32s-14.4-32-32-32H368c-17.6 0-32 14.4-32 32s14.4 32 32 32zM496 608h-128c-17.6 0-32 14.4-32 32s14.4 32 32 32h128c17.6 0 32-14.4 32-32s-14.4-32-32-32zM368 512h288c17.6 0 32-14.4 32-32s-14.4-32-32-32H368c-17.6 0-32 14.4-32 32s14.4 32 32 32z" fill="#4A576A" ></path></symbol><symbol id="el-icon-self-xuexi-" viewBox="0 0 1024 1024"><path d="M996.5 287.1L567.6 68.8c-34.8-17.6-76.4-17.6-111.2 0L27.5 286.4C10.6 295 0 312.1 0 331.2c0 19 10.5 36.2 27.5 44.8l219.8 111.8v305c0 33.9 18.8 64.4 49 79.7l164.9 83.6c15.9 8.1 33.4 12.1 50.9 12.1s35-4 50.9-12.1l164.9-83.6c30.2-15.3 49-45.9 49-79.7V488.2L901.4 425v111.9c0 15.2 12.3 27.5 27.5 27.5s27.5-12.3 27.5-27.5V401.6c0-1.5-0.1-2.9-0.3-4.3l40.4-20.5c17-8.6 27.5-25.8 27.5-44.8 0-19.1-10.5-36.3-27.5-44.9zM721.8 792.9c0 13.1-7.2 24.8-18.9 30.8L538 907.3c-16.3 8.3-35.8 8.3-52.1 0L321 823.7c-11.6-5.9-18.9-17.7-18.9-30.8V515.8l154.2 78.5c17.4 8.8 36.5 13.2 55.6 13.2s38.2-4.4 55.6-13.2l154.2-78.2v276.8z m-179-247.6c-19.3 9.8-42.3 9.8-61.5 0L60.5 331.2l420.7-213.4c19.3-9.8 42.3-9.8 61.5 0l420.7 214.1-420.6 213.4z" fill="" ></path></symbol><symbol id="el-icon-self-PPT" viewBox="0 0 1024 1024"><path d="M855.5 345.9L616 106.5c-5.3-5.3-12.4-8.2-19.9-8.2H174.3c-7.8 0-14.1 6.3-14.1 14.1v801.9c0 7.8 6.3 14.1 14.1 14.1h675.3c7.8 0 14.1-6.3 14.1-14.1V365.8c0-7.5-2.9-14.7-8.2-19.9z m-53 514.8h-581v-695h376.6v204.2h204.3l0.1 490.8z" ></path><path d="M520.2 632.7h-49v101.9H405V443.5h121.7c70.6 1.8 107.2 32.2 109.8 91.3 1 66.1-37.8 98.8-116.3 97.9z m-5.3-138.9h-43.7v88.6h43.7c34.4-0.9 52-15.9 52.9-45-0.9-28.2-18.5-42.7-52.9-43.6z" ></path></symbol><symbol id="el-icon-self-statistic" viewBox="0 0 1024 1024"><path d="M838.33 659.732c0 33.422-21.592 54.388-54.39 54.388H240.059c-32.796 0-54.388-20.966-54.388-54.388 0 0-0.409-482.507-0.409-516.961h653.477c0 54.85-0.409 516.96-0.409 516.96zM76.892 88.655v54.389h54.389v516.689c0 64.45 44.707 108.776 108.776 108.776h163.166L294.447 958.87l81.909-0.353 108.45-190.006h54.388l108.45 190.006 81.909 0.353-108.777-190.36h163.166c64.069 0 108.776-44.326 108.776-108.776v-516.69h54.389v-54.39H76.893v0.001z m271.942 543.883c15.038 0 27.194-12.156 27.194-27.194V496.568c0-15.011-12.156-27.195-27.194-27.195s-27.195 12.183-27.195 27.195v108.776c0 15.038 12.156 27.194 27.195 27.194z m326.33 0c15.039 0 27.195-12.156 27.195-27.194V360.597c0-15.011-12.156-27.195-27.195-27.195s-27.194 12.183-27.194 27.195v244.747c0 15.038 12.156 27.194 27.194 27.194z m-163.165 0c15.039 0 27.194-12.156 27.194-27.194v-326.33c0-15.011-12.155-27.195-27.194-27.195s-27.194 12.183-27.194 27.195v326.33c0 15.038 12.155 27.194 27.194 27.194z" ></path></symbol></svg>',l=(c=document.getElementsByTagName("script"))[c.length-1].getAttribute("data-injectcss");if(l&&!a.__iconfont__svg__cssinject__){a.__iconfont__svg__cssinject__=!0;try{document.write("<style>.svgfont {display: inline-block;width: 1em;height: 1em;fill: currentColor;vertical-align: -0.1em;font-size:16px;}</style>")}catch(c){console&&console.log(c)}}!function(c){if(document.addEventListener)if(~["complete","loaded","interactive"].indexOf(document.readyState))setTimeout(c,0);else{var l=function(){document.removeEventListener("DOMContentLoaded",l,!1),c()};document.addEventListener("DOMContentLoaded",l,!1)}else document.attachEvent&&(t=c,e=a.document,o=!1,s=function(){o||(o=!0,t())},(i=function(){try{e.documentElement.doScroll("left")}catch(c){return void setTimeout(i,50)}s()})(),e.onreadystatechange=function(){"complete"==e.readyState&&(e.onreadystatechange=null,s())});var t,e,o,s,i}(function(){var c,l,t,e,o,s;(c=document.createElement("div")).innerHTML=i,i=null,(l=c.getElementsByTagName("svg")[0])&&(l.setAttribute("aria-hidden","true"),l.style.position="absolute",l.style.width=0,l.style.height=0,l.style.overflow="hidden",t=l,(e=document.body).firstChild?(o=t,(s=e.firstChild).parentNode.insertBefore(o,s)):e.appendChild(t))})}(window); !function(i){var c,o='<svg><symbol id="el-icon-self-discover" viewBox="0 0 1024 1024"><path d="M544 288c0-52.928-43.072-96-96-96s-96 43.072-96 96 43.072 96 96 96S544 340.928 544 288zM416 288c0-17.632 14.368-32 32-32s32 14.368 32 32-14.368 32-32 32S416 305.632 416 288z" ></path><path d="M304 448C259.904 448 224 483.904 224 528 224 572.128 259.904 608 304 608s80-35.872 80-80C384 483.904 348.096 448 304 448zM304 544C295.168 544 288 536.832 288 528S295.168 512 304 512s16 7.168 16 16S312.832 544 304 544z" ></path><path d="M640 736m-64 0a2 2 0 1 0 128 0 2 2 0 1 0-128 0Z" ></path><path d="M887.84 268.672c39.744-60.672 64.256-113.184 64.224-148.96 0-22.976-9.312-36.928-17.12-44.576-38.08-37.376-110.016-3.2-160.768 25.664-15.328 8.736-20.704 28.288-11.968 43.648 8.736 15.36 28.32 20.704 43.648 11.968 49.408-28.16 73.248-33.088 82.496-33.696-1.504 41.376-95.328 202.432-330.208 433.504-226.368 222.72-398.656 324.672-442.24 325.376 0.96-11.36 8.768-44.448 58.528-118.688 1.312-1.888 2.4-3.968 3.232-6.144l2.304-5.92c0.256-0.64 0.288-1.344 0.512-2.016 8.8-10.24 10.816-25.088 3.392-37.248C147.328 651.616 128 582.592 128 512 128 300.256 300.256 128 512 128c59.296 0 116.096 13.12 168.864 39.04 15.936 7.776 35.04 1.184 42.816-14.656 7.776-15.84 1.248-35.008-14.624-42.816C647.424 79.328 581.12 64 512 64 264.96 64 64 264.96 64 512c0 76.064 19.488 150.496 56.064 216.64-0.192 0.576-0.64 1.056-0.8 1.632C51.168 832.608 35.2 895.68 68.928 928.768c11.008 10.784 25.632 15.584 43.072 15.584 39.328 0 92.864-24.928 150.08-60.896C336.096 933.408 421.952 960 512 960c247.04 0 448-200.96 448-448C960 424.864 934.912 341.248 887.84 268.672zM512 896c-68.288 0-133.728-17.888-191.84-51.552 128.288-92.256 258.432-218.56 282.848-242.624 89.152-87.712 178.56-187.04 244.64-275.968C879.2 382.592 896 446.112 896 512 896 723.744 723.744 896 512 896z" ></path></symbol><symbol id="el-icon-self-settings" viewBox="0 0 1024 1024"><path d="M512 608c-52.928 0-96-43.072-96-96s43.072-96 96-96 96 43.072 96 96S564.928 608 512 608zM512 480c-17.632 0-32 14.368-32 32s14.368 32 32 32 32-14.368 32-32S529.632 480 512 480z" ></path><path d="M631.04 960c-10.016 0-19.712-4.736-25.856-13.12l-73.408-100.48c-13.664 0.832-25.92 0.864-39.584 0l-73.376 100.48c-7.904 10.816-21.728 15.616-34.656 11.872-34.592-9.888-67.488-23.456-97.76-40.32-11.712-6.528-18.112-19.648-16.032-32.896l19.2-123.264c-9.824-8.704-19.136-18.016-27.84-27.808l-123.264 19.2c-13.056 1.984-26.336-4.352-32.896-16.064-16.896-30.368-30.464-63.296-40.32-97.824-3.68-12.864 1.056-26.72 11.904-34.624l100.448-73.376C177.152 524.96 176.928 518.4 176.928 512s0.224-12.96 0.672-19.776L77.12 418.816c-10.816-7.936-15.584-21.728-11.904-34.656 9.856-34.496 23.424-67.392 40.32-97.792 6.56-11.712 19.712-18.048 32.896-16.064l123.264 19.2c8.736-9.824 18.016-19.104 27.84-27.84l-19.2-123.264C268.288 125.184 274.72 112.064 286.432 105.536c30.4-16.896 63.296-30.464 97.76-40.32 12.928-3.648 26.72 1.056 34.656 11.904l73.376 100.448c13.568-0.896 25.824-0.896 39.584 0l73.408-100.48c7.904-10.848 21.664-15.52 34.656-11.904 34.464 9.856 67.328 23.424 97.76 40.32 11.712 6.528 18.112 19.648 16.064 32.896L734.432 261.76c9.824 8.736 19.104 18.016 27.808 27.776l123.328-19.2c13.152-2.016 26.336 4.384 32.864 16.032 16.896 30.4 30.496 63.296 40.352 97.824 3.648 12.896-1.088 26.688-11.904 34.624l-100.48 73.408c0.448 6.816 0.672 13.376 0.672 19.776s-0.224 12.96-0.672 19.776l100.48 73.376c10.816 7.904 15.552 21.728 11.904 34.624-9.856 34.496-23.424 67.424-40.352 97.824-6.528 11.712-19.488 18.048-32.896 16.064l-123.296-19.2c-8.672 9.76-17.984 19.072-27.808 27.776l19.232 123.296c2.048 13.248-4.352 26.368-16.064 32.896-30.24 16.832-63.136 30.4-97.76 40.32C636.928 959.584 633.984 960 631.04 960zM337.152 872.672c13.984 6.752 28.544 12.704 43.552 17.92l70.528-96.576c6.784-9.28 17.984-14.272 29.472-12.896 20.576 2.336 42.08 2.336 62.624 0 11.84-1.376 22.72 3.616 29.472 12.896l70.528 96.576c15.04-5.184 29.6-11.168 43.552-17.92l-18.432-118.368c-1.76-11.36 2.72-22.848 11.712-30.016 16.384-12.992 31.264-27.872 44.16-44.128 7.168-8.992 18.4-13.504 30.016-11.744l118.368 18.432c6.752-14.016 12.736-28.576 17.92-43.552l-96.576-70.528c-9.28-6.784-14.208-18.016-12.896-29.472 1.152-10.272 1.952-20.704 1.952-31.296s-0.768-21.024-1.952-31.296c-1.312-11.424 3.616-22.688 12.896-29.472l96.576-70.56c-5.152-15.008-11.168-29.568-17.92-43.552l-118.4 18.432c-11.328 1.696-22.816-2.688-29.984-11.744-12.896-16.256-27.776-31.104-44.16-44.128-8.992-7.168-13.472-18.592-11.712-29.984l18.432-118.4c-14.016-6.752-28.576-12.736-43.52-17.92l-70.56 96.576c-6.784 9.28-17.696 14.368-29.472 12.928-20.544-2.368-42.016-2.4-62.56 0-11.52 1.344-22.752-3.616-29.536-12.896l-70.528-96.576c-14.976 5.152-29.536 11.136-43.552 17.92l18.432 118.368c1.76 11.392-2.72 22.848-11.744 30.016-16.32 12.928-31.2 27.808-44.128 44.128-7.168 9.024-18.56 13.472-30.016 11.744l-118.368-18.432c-6.752 14.016-12.736 28.576-17.92 43.552l96.576 70.56c9.312 6.784 14.24 18.08 12.896 29.536C241.696 491.008 240.928 501.408 240.928 512s0.768 20.992 1.952 31.264c1.344 11.456-3.616 22.688-12.896 29.504l-96.576 70.528c5.184 15.008 11.168 29.568 17.92 43.552l118.368-18.432c11.392-1.76 22.848 2.752 30.016 11.744 12.896 16.32 27.744 31.168 44.128 44.128 9.024 7.168 13.504 18.624 11.744 30.016L337.152 872.672z" ></path></symbol><symbol id="el-icon-self-new" viewBox="0 0 1024 1024"><path d="M963.072 446.336c0-211.744-200.96-384-448-384s-448 172.256-448 384c0 116.48 63.008 226.048 172.896 300.672 14.656 9.984 34.528 6.144 44.448-8.512 9.952-14.624 6.112-34.528-8.512-44.448-92.032-62.496-144.832-152.768-144.832-247.712 0-176.448 172.256-320 384-320 211.744 0 384 143.552 384 320 0 176.448-172.256 320-384 320-1.984 0-3.68 0.768-5.568 1.12-15.104-2.688-30.464 5.216-35.776 20.192-6.144 17.376-46.368 46.656-94.144 73.792 17.472-58.208 9.088-70.688 3.52-78.976-6.72-9.984-17.92-15.936-29.92-15.936-17.664 0-32 14.304-32 32 0 5.824 1.536 11.264 4.256 15.936-3.232 18.24-17.216 60.864-33.088 99.872-4.928 12.096-1.984 25.984 7.36 35.072 6.112 5.888 14.112 8.992 22.272 8.992 4.384 0 8.8-0.896 12.992-2.752 36.48-16.256 147.68-69.12 187.616-125.664C766.144 826.496 963.072 655.904 963.072 446.336z" ></path><path d="M342.624 604.544c4.672 2.4 9.664 3.52 14.592 3.52 11.616 0 22.816-6.336 28.512-17.408l71.584-139.488 91.584 142.208c5.824 9.024 15.744 14.528 26.464 14.688 0.16 0 0.32 0 0.448 0 10.56 0 20.48-5.216 26.432-13.984l128.8-188.864c9.984-14.624 6.176-34.528-8.416-44.48-14.624-9.952-34.528-6.208-44.48 8.416l-101.632 148.992-95.456-148.288c-6.176-9.6-17.152-14.752-28.48-14.656-11.424 0.576-21.696 7.2-26.912 17.344l-96.896 188.896C320.672 577.184 326.88 596.48 342.624 604.544z" ></path></symbol><symbol id="el-icon-self-album" viewBox="0 0 1024 1024"><path d="M256 448m-64 0a2 2 0 1 0 128 0 2 2 0 1 0-128 0Z" ></path><path d="M714.688 256 181.312 256C116.64 256 64 308.64 64 373.312l0 405.376C64 843.36 116.64 896 181.312 896l533.376 0C779.36 896 832 843.36 832 778.688L832 373.312C832 308.64 779.36 256 714.688 256zM768 778.688C768 808.096 744.096 832 714.688 832L181.312 832C151.936 832 128 808.096 128 778.688L128 373.312C128 343.936 151.936 320 181.312 320l533.376 0C744.096 320 768 343.936 768 373.312L768 778.688z" ></path><path d="M842.688 128 256 128C238.336 128 224 142.336 224 160s14.336 32 32 32l586.688 0C872.096 192 896 215.936 896 245.312L896 640c0 17.696 14.304 32 32 32s32-14.304 32-32L960 245.312C960 180.64 907.36 128 842.688 128z" ></path><path d="M672.64 448.736c-129.056 0-143.936 72.672-152.832 116.096-7.2 35.168-10.4 39.968-28.64 42.88-34.976 5.568-44 0.192-58.912-8.8-15.456-9.312-36.672-22.08-77.952-23.168C227.616 573.12 194.112 723.84 192.704 730.24c-3.712 17.28 7.264 34.304 24.544 38.016 2.272 0.512 4.544 0.704 6.752 0.704 14.752 0 28-10.24 31.264-25.248 0.224-1.056 23.232-104.032 95.488-104.032 0.64 0 1.28 0 1.92 0.032 24.352 0.608 33.632 6.208 46.496 13.984 24.032 14.464 48.352 25.76 102.08 17.184 64.192-10.272 74.432-60.192 81.184-93.216 8.32-40.512 13.312-64.928 90.144-64.928 17.696 0 32-14.336 32-32S690.304 448.736 672.64 448.736z" ></path></symbol><symbol id="el-icon-self-cc-book" viewBox="0 0 1024 1024"><path d="M682.697195 859.707167 242.160807 859.707167c-12.152784 0-22.025643-9.893324-22.025643-22.026666 0-12.130272 9.872858-22.025643 22.025643-22.025643l440.537412 0c12.130272 0 22.025643 9.895371 22.025643 22.025643C704.723861 849.813842 694.82849 859.707167 682.697195 859.707167z" ></path><path d="M841.773214 158.324912c-12.955056 0-23.51967 10.543124-23.51967 23.520693l0 705.638185c0 12.95608-10.56666 23.520693-23.521716 23.520693L277.262256 911.004483c-38.911218 0-70.564126-31.650861-70.564126-70.564126 0-38.910195 31.652907-70.563102 70.564126-70.563102l423.382707 0c38.911218 0 70.565149-31.652907 70.565149-70.563102L771.210112 134.803195c0-38.911218-31.653931-70.564126-70.565149-70.564126L230.218824 64.23907c-38.911218 0-70.563102 31.652907-70.563102 70.564126l0 705.637162c0 64.823378 52.785204 117.608581 117.606535 117.608581l517.468548 0c38.909172 0 70.563102-31.653931 70.563102-70.565149L865.293907 181.845605C865.293907 168.868035 854.727247 158.324912 841.773214 158.324912zM230.218824 111.281479l470.426139 0c12.955056 0 23.521716 10.543124 23.521716 23.521716l0 564.510958c0 12.955056-10.56666 23.520693-23.521716 23.520693l-423.382707 0c-26.439162 0-50.90232 8.820899-70.564126 23.566742L206.698131 134.803195C206.698131 121.824603 217.241254 111.281479 230.218824 111.281479z" ></path></symbol><symbol id="el-icon-self-iconset0481" viewBox="0 0 1024 1024"><path d="M512 42.666667C253.866667 42.666667 42.666667 251.733333 42.666667 512s211.2 469.333333 469.333333 469.333333c260.266667 0 469.333333-209.066667 469.333333-469.333333S772.266667 42.666667 512 42.666667zM512 938.666667C277.333333 938.666667 85.333333 746.666667 85.333333 512 85.333333 277.333333 277.333333 85.333333 512 85.333333c234.666667 0 426.666667 192 426.666667 426.666667C938.666667 746.666667 746.666667 938.666667 512 938.666667zM716.8 494.933333 716.8 494.933333l-4.266667-2.133333c0 0 0 0-2.133333 0l-292.266667-168.533333 0 0C413.866667 322.133333 409.6 320 405.333333 320c-12.8 0-21.333333 8.533333-21.333333 21.333333l0 341.333333c0 12.8 8.533333 21.333333 21.333333 21.333333 4.266667 0 8.533333-2.133333 12.8-4.266667l0 0 292.266667-168.533333c0 0 0 0 2.133333 0l4.266667-2.133333 0 0c4.266667-4.266667 8.533333-10.666667 8.533333-17.066667S721.066667 499.2 716.8 494.933333zM426.666667 646.4 426.666667 377.6 661.333333 512 426.666667 646.4z" ></path></symbol><symbol id="el-icon-self-guanbi" viewBox="0 0 1024 1024"><path d="M567.001395 495.286374 966.070272 96.236851c10.904781-10.905395 10.904781-28.573798 0-39.481242-10.907853-10.905395-28.575949-10.905395-39.482778 0L527.519539 455.806157 128.45271 56.75561c-10.905805-10.905395-28.574925-10.905395-39.481754 0-10.906829 10.906419-10.906829 28.574822 0 39.481242l399.067955 399.049523L88.970957 894.337946c-10.906829 10.906419-10.906829 28.573798 0 39.480218 5.45239 5.452186 12.652646 8.177254 19.741389 8.177254 7.197184 0 14.285926-2.725069 19.740365-8.177254l399.065907-399.052595L926.587494 933.818061c5.454438 5.452186 12.652646 8.177254 19.740365 8.177254 7.198208 0 14.28695-2.725069 19.742413-8.177254 10.904781-10.906419 10.904781-28.573798 0-39.480218L567.001395 495.286374z" ></path></symbol><symbol id="el-icon-self-xuexiao" viewBox="0 0 1024 1024"><path d="M925.76107 278.522458l-16.542768 49.136094c0 0-79.438237-196.570981-314.467111-245.720378 0 0-96.866165-21.942755-198.602244 0-13.964036 7.977695-254.891248 85.180008-297.91718 311.248813 0 0 9.928117 68.815295 49.646724 0 0 0 23.170722-176.919409 281.368272-262.098393l182.061522 0c0 0 211.844849 72.074525 264.812201 245.720378l16.556071 81.906451 49.640584 16.392342 16.556071-196.584284L925.76107 278.523481z" ></path><path d="M876.120486 636.506688c0 0-23.170722 176.919409-281.369296 262.098393L412.690692 898.605082c0 0-211.844849-72.074525-264.813225-245.720378l-16.548908-81.906451-49.653887-16.392342L65.126788 751.170195l33.104979 0 16.548908-49.136094c0 0 116.961852 225.768977 314.467111 245.720378 83.788312 19.951401 198.61657 0 198.61657 0s254.884085-85.180008 297.896714-311.24779C925.76107 636.506688 915.832953 567.691393 876.120486 636.506688z" ></path></symbol><symbol id="el-icon-self-13" viewBox="0 0 1024 1024"><path d="M147.2 180.8h728c41.6 0 76.8 35.2 76.8 76.8v552c0 41.6-35.2 76.8-76.8 76.8h-728c-41.6 0-76.8-35.2-76.8-76.8v-552c0-41.6 35.2-76.8 76.8-76.8v0zM147.2 926.4c243.2 0 486.4 0 728 0 64 0 115.2-51.2 115.2-115.2 0-184 0-368 0-552 0-64-51.2-115.2-115.2-115.2-243.2 0-486.4 0-728 0-64 0-115.2 51.2-115.2 115.2 0 184 0 368 0 552 0 62.4 51.2 115.2 115.2 115.2z" ></path><path d="M201.6 419.2c0 44.8 36.8 81.6 81.6 81.6s81.6-36.8 81.6-81.6c0-44.8-36.8-81.6-81.6-81.6-44.8 0-81.6 36.8-81.6 81.6v0zM240 419.2c0-24 19.2-43.2 43.2-43.2s43.2 19.2 43.2 43.2c0 24-19.2 43.2-43.2 43.2-22.4 0-43.2-19.2-43.2-43.2z" ></path><path d="M675.2 499.2l203.2 203.2c8 8 8 19.2 0 27.2v0c-8 8-19.2 8-27.2 0l-203.2-203.2c-14.4-14.4-40-14.4-54.4 0l-163.2 161.6c-30.4 30.4-80 30.4-110.4 0l-25.6-25.6c-14.4-14.4-40-14.4-54.4 0l-67.2 67.2c-8 8-19.2 8-27.2 0v0c-8-8-8-19.2 0-27.2l67.2-67.2c30.4-30.4 80-30.4 110.4 0l25.6 25.6c14.4 14.4 40 14.4 54.4 0l161.6-161.6c30.4-30.4 80-30.4 110.4 0z" ></path></symbol><symbol id="el-icon-self-grade" viewBox="0 0 1024 1024"><path d="M863.721739 554.295652c24.486957-48.973913 28.93913-102.4 28.93913-160.278261C892.66087 189.217391 734.608696 22.26087 527.582609 22.26087 320.556522 22.26087 162.504348 189.217391 162.504348 394.017391c0 57.878261 4.452174 111.304348 28.93913 160.278261L51.2 792.486957c0 0 95.721739 35.617391 193.669565 55.652174 64.556522 73.46087 117.982609 151.373913 117.982609 151.373913l129.113043-247.095652c8.904348 0 20.034783 0 26.713043 0 8.904348 0 20.034783 0 26.713043 0l138.017391 233.73913c0 0 57.878261-64.556522 124.66087-138.017391 97.947826-20.034783 193.669565-55.652174 193.669565-55.652174L863.721739 554.295652 863.721739 554.295652 863.721739 554.295652zM353.947826 903.791304c0 0-46.747826-60.104348-89.043478-102.4-62.330435-17.808696-144.695652-35.617391-144.695652-35.617391L215.930435 601.043478c51.2 66.782609 135.791304 129.113043 220.382609 149.147826L353.947826 903.791304 353.947826 903.791304zM520.904348 710.121739c-80.13913 0-309.426087-48.973913-316.104348-316.104348C198.121739 224.834783 351.721739 64.556522 520.904348 64.556522c169.182609 0 329.46087 162.504348 329.46087 329.46087C852.591304 650.017391 601.043478 710.121739 520.904348 710.121739L520.904348 710.121739zM685.634783 903.791304l-82.365217-151.373913c84.591304-20.034783 182.53913-84.591304 233.73913-151.373913l95.721739 164.730435c0 0-82.365217 17.808696-144.695652 35.617391L685.634783 903.791304 685.634783 903.791304zM527.582609 175.86087c-117.982609 0-213.704348 95.721739-213.704348 213.704348s95.721739 213.704348 213.704348 213.704348c117.982609 0 213.704348-95.721739 213.704348-213.704348S645.565217 175.86087 527.582609 175.86087L527.582609 175.86087 527.582609 175.86087zM534.26087 558.747826c-53.426087 0-169.182609-20.034783-178.086957-178.086957-4.452174-84.591304 95.721739-164.730435 178.086957-164.730435 84.591304 0 162.504348 95.721739 164.730435 178.086957C701.217391 505.321739 587.686957 558.747826 534.26087 558.747826L534.26087 558.747826zM534.26087 558.747826" ></path></symbol><symbol id="el-icon-self-quanping" viewBox="0 0 1024 1024"><path d="M664.035 409.382l244.223-243.378-0.837 137.922c-0.335 11.437 8.912 21.527 20.351 21.191h14.63c11.438-0.335 21.025-7.065 21.193-18.67l0.672-205.367c0-0.171 0.335-10.934 0.335-10.934 0.17-5.716-1.179-10.934-4.879-14.63-3.7-3.702-8.742-6.055-14.633-5.886l-10.428 0.171c-0.169 0-0.331 0-0.501 0.171l-203.69-0.846c-11.438 0.336-21.025 9.76-21.193 21.361v14.633c1.685 13.625 12.446 21.529 23.884 21.193l134.219 0.335-243.551 242.542c-11.101 11.104-11.101 29.095 0 40.197 11.104 11.269 29.101 11.269 40.203 0h-0.003M356.233 638.806l-244.393 242.544 0.842-137.253c0.336-11.435-8.918-21.523-20.355-21.193h-15.47c-11.438 0.335-21.025 7.065-21.193 18.672l-0.672 205.536c0 0.171-0.335 10.934-0.335 10.934-0.171 5.722 1.18 10.934 4.875 14.63 3.7 3.7 8.746 6.055 14.633 5.886l10.427-0.17c0.171 0 0.336 0 0.507-0.171l204.532 0.843c11.435-0.336 21.025-9.756 21.193-21.361v-14.63c-1.683-13.625-12.451-21.529-23.887-21.193l-134.222-0.335 243.38-242.539c11.104-11.104 11.104-29.101 0-40.203-10.929-11.268-28.757-11.268-39.857 0.001v0M964.271 947.109l-0.507-205.536c-0.336-11.438-9.76-18.335-21.194-18.672h-14.629c-11.439-0.331-20.521 9.759-20.356 21.193l0.843 137.927-244.393-243.214c-11.104-11.104-29.099-11.104-40.203 0-11.097 11.101-11.097 29.099 0 40.203l243.386 242.538-134.22 0.335c-11.438-0.335-22.034 7.739-23.887 21.193v14.633c0.335 11.437 9.76 21.025 21.193 21.361l203.69-0.843c0.17 0 0.335 0.17 0.507 0.17l10.428 0.171c5.716 0.17 10.934-2.016 14.63-5.886 3.698-3.7 5.042-8.912 4.875-14.63 0 0-0.169-10.764-0.169-10.934l0.004-0.002M152.885 126.645l134.22-0.335c11.435 0.335 22.031-7.739 23.883-21.193v-14.636c-0.336-11.437-9.753-21.025-21.193-21.36l-204.694 0.842c-0.17 0-0.336-0.171-0.509-0.171l-10.426-0.169c-5.722-0.17-10.934 2.015-14.633 5.885-3.698 3.7-5.050 8.912-4.875 14.633 0 0 0.335 10.763 0.335 10.933l0.502 205.536c0.171 11.435 9.759 18.335 21.193 18.667h15.475c11.437 0.335 20.519-9.753 20.349-21.191l-0.671-137.248 244.224 242.709c11.097 11.104 29.094 11.104 40.196 0 11.104-11.101 11.104-29.099 0-40.203l-243.38-242.703M152.885 126.645v0z" ></path></symbol><symbol id="el-icon-self-nav" viewBox="0 0 1024 1024"><path d="M888.838 319.775l-751.1 0c-23.944 0-43.339-19.553-43.339-43.646l0-40.012c0-24.093 19.395-43.645 43.339-43.645l751.1 0c23.923 0 43.334 19.554 43.334 43.645l0 40.012c-0.001 24.093-19.411 43.646-43.333 43.646l0 0zM888.838 587.509l-751.1 0c-23.944 0-43.339-19.533-43.339-43.64l0-39.998c0-24.115 19.395-43.647 43.339-43.647l751.1 0c23.923 0 43.334 19.533 43.334 43.647l0 39.998c-0.001 24.107-19.411 43.64-43.333 43.64l0 0zM888.838 876.17l-751.1 0c-23.944 0-43.339-19.532-43.339-43.627l0-40.017c0-24.093 19.395-43.641 43.339-43.641l751.1 0c23.923 0 43.334 19.548 43.334 43.641l0 40.017c-0.001 24.094-19.411 43.627-43.333 43.627l0 0z" ></path></symbol><symbol id="el-icon-self-mima" viewBox="0 0 1024 1024"><path d="M791.366 459.07l-459.174-0.791c-0.186-61.936-16.847-157.926 36.027-216.192 32.142-35.52 86.326-72.068 144.127-72.068 85.395 0 149.246 64.412 180.166 144.126 9.009 22.992 16.936 35.38 36.037 36.037 47.899 1.45 44.986-49.075 36.029-72.067C719.914 163.03 636.118 97.953 512.346 97.953c-84.12 0-130.475 17.265-180.154 72.065-76.009 83.746-72.259 219.151-72.067 288.26l-26.795 0.791c-25.045 0-45.273 20.034-45.273 44.667v378.249c0 24.774 20.269 44.755 45.273 44.755h557.99c25.009 0 45.273-19.989 45.273-44.755V503.737c0.046-24.678-20.218-44.667-45.227-44.667zM765.68 818.35c0 9.966-3.476 18.514-10.58 25.52-7.045 7.051-15.56 10.58-25.517 10.58H296.362c-9.966 0-18.472-3.478-25.475-10.58-7.051-7.049-10.624-15.553-10.624-25.52V565.635c0-19.878 16.166-36.054 36.098-36.054h433.212c19.932 0 36.107 16.176 36.107 36.054V818.35z" fill="" ></path></symbol><symbol id="el-icon-self-shipin" viewBox="0 0 1024 1024"><path d="M858.8 903.6H182.2c-61 0-110.8-49.6-110.8-110.8V279c0-61 49.6-110.8 110.8-110.8h676.6c61 0 110.8 49.6 110.8 110.8v513.8c-0.2 61-49.8 110.8-110.8 110.8zM182.2 208.4c-39 0-70.8 31.8-70.8 70.8v513.8c0 39 31.8 70.8 70.8 70.8h676.6c39 0 70.8-31.8 70.8-70.8v-514c0-39-31.8-70.8-70.8-70.8H182.2z" fill="#262435" ></path><path d="M225.4 903.6c-11 0-20-9-20-20V188.4c0-11 9-20 20-20s20 9 20 20v695.2c0 11-8.8 20-20 20zM815.4 903.6c-11 0-20-9-20-20V188.4c0-11 9-20 20-20s20 9 20 20v695.2c0 11-9 20-20 20z" fill="#262435" ></path><path d="M225.4 399.2h-134c-11 0-20-9-20-20s9-20 20-20h134c11 0 20 9 20 20s-8.8 20-20 20zM225.4 556h-134c-11 0-20-9-20-20s9-20 20-20h134c11 0 20 9 20 20s-8.8 20-20 20zM225.4 732.6h-134c-11 0-20-9-20-20s9-20 20-20h134c11 0 20 9 20 20s-8.8 20-20 20z" fill="#262435" ></path><path d="M949.4 399.2h-134c-11 0-20-9-20-20s9-20 20-20h134c11 0 20 9 20 20s-9 20-20 20zM949.4 556h-134c-11 0-20-9-20-20s9-20 20-20h134c11 0 20 9 20 20s-9 20-20 20zM949.4 732.6h-134c-11 0-20-9-20-20s9-20 20-20h134c11 0 20 9 20 20s-9 20-20 20z" fill="#262435" ></path><path d="M432 679.2c-3.4 0-7-0.8-10-2.6-6.2-3.6-10-10.2-10-17.4V412.8c0-7.2 3.8-13.8 10-17.4 6.2-3.6 13.8-3.6 20 0l213.4 123.2c6.2 3.6 10 10.2 10 17.4s-3.8 13.8-10 17.4L442 676.4c-3.2 1.8-6.6 2.8-10 2.8z m20-231.8v177.2l153.4-88.6-153.4-88.6z" fill="#262435" ></path></symbol><symbol id="el-icon-self-character" viewBox="0 0 1024 1024"><path d="M921.087359 990.72v-35.2A410.24 410.24 0 0 0 661.887359 576a307.2 307.2 0 1 0-300.8 0 410.24 410.24 0 0 0-256 380.8v35.2H101.887359a32 32 0 0 0 32 32 32 32 0 0 0 32-32v-34.56a345.6 345.6 0 0 1 691.2 0v35.2a31.36 31.36 0 0 0 31.36 30.08 32 32 0 0 0 32.64-32zM270.207359 307.2A241.28 241.28 0 1 1 511.487359 548.48 241.28 241.28 0 0 1 270.207359 307.2z" ></path></symbol><symbol id="el-icon-self-wenjian" viewBox="0 0 1024 1024"><path d="M752 80H272c-70.4 0-128 57.6-128 128v608c0 70.4 57.6 128 128 128h353.6c33.6 0 65.6-12.8 91.2-36.8l126.4-126.4c24-24 36.8-56 36.8-91.2V208c0-70.4-57.6-128-128-128zM208 816V208c0-35.2 28.8-64 64-64h480c35.2 0 64 28.8 64 64v464h-96c-70.4 0-128 57.6-128 128v80H272c-35.2 0-64-28.8-64-64z m462.4 44.8c-4.8 4.8-9.6 8-14.4 11.2V800c0-35.2 28.8-64 64-64h75.2l-124.8 124.8z" fill="#4A576A" ></path><path d="M368 352h288c17.6 0 32-14.4 32-32s-14.4-32-32-32H368c-17.6 0-32 14.4-32 32s14.4 32 32 32zM496 608h-128c-17.6 0-32 14.4-32 32s14.4 32 32 32h128c17.6 0 32-14.4 32-32s-14.4-32-32-32zM368 512h288c17.6 0 32-14.4 32-32s-14.4-32-32-32H368c-17.6 0-32 14.4-32 32s14.4 32 32 32z" fill="#4A576A" ></path></symbol><symbol id="el-icon-self-xuexi-" viewBox="0 0 1024 1024"><path d="M996.5 287.1L567.6 68.8c-34.8-17.6-76.4-17.6-111.2 0L27.5 286.4C10.6 295 0 312.1 0 331.2c0 19 10.5 36.2 27.5 44.8l219.8 111.8v305c0 33.9 18.8 64.4 49 79.7l164.9 83.6c15.9 8.1 33.4 12.1 50.9 12.1s35-4 50.9-12.1l164.9-83.6c30.2-15.3 49-45.9 49-79.7V488.2L901.4 425v111.9c0 15.2 12.3 27.5 27.5 27.5s27.5-12.3 27.5-27.5V401.6c0-1.5-0.1-2.9-0.3-4.3l40.4-20.5c17-8.6 27.5-25.8 27.5-44.8 0-19.1-10.5-36.3-27.5-44.9zM721.8 792.9c0 13.1-7.2 24.8-18.9 30.8L538 907.3c-16.3 8.3-35.8 8.3-52.1 0L321 823.7c-11.6-5.9-18.9-17.7-18.9-30.8V515.8l154.2 78.5c17.4 8.8 36.5 13.2 55.6 13.2s38.2-4.4 55.6-13.2l154.2-78.2v276.8z m-179-247.6c-19.3 9.8-42.3 9.8-61.5 0L60.5 331.2l420.7-213.4c19.3-9.8 42.3-9.8 61.5 0l420.7 214.1-420.6 213.4z" fill="" ></path></symbol><symbol id="el-icon-self-PPT" viewBox="0 0 1024 1024"><path d="M855.5 345.9L616 106.5c-5.3-5.3-12.4-8.2-19.9-8.2H174.3c-7.8 0-14.1 6.3-14.1 14.1v801.9c0 7.8 6.3 14.1 14.1 14.1h675.3c7.8 0 14.1-6.3 14.1-14.1V365.8c0-7.5-2.9-14.7-8.2-19.9z m-53 514.8h-581v-695h376.6v204.2h204.3l0.1 490.8z" ></path><path d="M520.2 632.7h-49v101.9H405V443.5h121.7c70.6 1.8 107.2 32.2 109.8 91.3 1 66.1-37.8 98.8-116.3 97.9z m-5.3-138.9h-43.7v88.6h43.7c34.4-0.9 52-15.9 52.9-45-0.9-28.2-18.5-42.7-52.9-43.6z" ></path></symbol><symbol id="el-icon-self-statistic" viewBox="0 0 1024 1024"><path d="M838.33 659.732c0 33.422-21.592 54.388-54.39 54.388H240.059c-32.796 0-54.388-20.966-54.388-54.388 0 0-0.409-482.507-0.409-516.961h653.477c0 54.85-0.409 516.96-0.409 516.96zM76.892 88.655v54.389h54.389v516.689c0 64.45 44.707 108.776 108.776 108.776h163.166L294.447 958.87l81.909-0.353 108.45-190.006h54.388l108.45 190.006 81.909 0.353-108.777-190.36h163.166c64.069 0 108.776-44.326 108.776-108.776v-516.69h54.389v-54.39H76.893v0.001z m271.942 543.883c15.038 0 27.194-12.156 27.194-27.194V496.568c0-15.011-12.156-27.195-27.194-27.195s-27.195 12.183-27.195 27.195v108.776c0 15.038 12.156 27.194 27.195 27.194z m326.33 0c15.039 0 27.195-12.156 27.195-27.194V360.597c0-15.011-12.156-27.195-27.195-27.195s-27.194 12.183-27.194 27.195v244.747c0 15.038 12.156 27.194 27.194 27.194z m-163.165 0c15.039 0 27.194-12.156 27.194-27.194v-326.33c0-15.011-12.155-27.195-27.194-27.195s-27.194 12.183-27.194 27.195v326.33c0 15.038 12.155 27.194 27.194 27.194z" ></path></symbol></svg>',l=(c=document.getElementsByTagName("script"))[c.length-1].getAttribute("data-injectcss");if(l&&!i.__iconfont__svg__cssinject__){i.__iconfont__svg__cssinject__=!0;try{document.write("<style>.svgfont {display: inline-block;width: 1em;height: 1em;fill: currentColor;vertical-align: -0.1em;font-size:16px;}</style>")}catch(c){console&&console.log(c)}}!function(c){if(document.addEventListener)if(~["complete","loaded","interactive"].indexOf(document.readyState))setTimeout(c,0);else{var l=function(){document.removeEventListener("DOMContentLoaded",l,!1),c()};document.addEventListener("DOMContentLoaded",l,!1)}else document.attachEvent&&(t=c,e=i.document,s=!1,h=function(){s||(s=!0,t())},(o=function(){try{e.documentElement.doScroll("left")}catch(c){return void setTimeout(o,50)}h()})(),e.onreadystatechange=function(){"complete"==e.readyState&&(e.onreadystatechange=null,h())});var t,e,s,h,o}(function(){var c,l,t,e,s,h;(c=document.createElement("div")).innerHTML=o,o=null,(l=c.getElementsByTagName("svg")[0])&&(l.setAttribute("aria-hidden","true"),l.style.position="absolute",l.style.width=0,l.style.height=0,l.style.overflow="hidden",t=l,(e=document.body).firstChild?(s=t,(h=e.firstChild).parentNode.insertBefore(s,h)):e.appendChild(t))})}(window);
\ No newline at end of file \ No newline at end of file
...@@ -38,18 +38,30 @@ Created by iconfont ...@@ -38,18 +38,30 @@ Created by iconfont
<glyph glyph-name="iconset0481" unicode="&#59240;" d="M512 853.333333C253.866667 853.333333 42.666667 644.266667 42.666667 384s211.2-469.333333 469.333333-469.333333c260.266667 0 469.333333 209.066667 469.333333 469.333333S772.266667 853.333333 512 853.333333zM512-42.666667C277.333333-42.666667 85.333333 149.333333 85.333333 384 85.333333 618.666667 277.333333 810.666667 512 810.666667c234.666667 0 426.666667-192 426.666667-426.666667C938.666667 149.333333 746.666667-42.666667 512-42.666667zM716.8 401.066667 716.8 401.066667l-4.266667 2.133333c0 0 0 0-2.133333 0l-292.266667 168.533333 0 0C413.866667 573.866667 409.6 576 405.333333 576c-12.8 0-21.333333-8.533333-21.333333-21.333333l0-341.333333c0-12.8 8.533333-21.333333 21.333333-21.333333 4.266667 0 8.533333 2.133333 12.8 4.266667l0 0 292.266667 168.533333c0 0 0 0 2.133333 0l4.266667 2.133333 0 0c4.266667 4.266667 8.533333 10.666667 8.533333 17.066667S721.066667 396.8 716.8 401.066667zM426.666667 249.6 426.666667 518.4 661.333333 384 426.666667 249.6z" horiz-adv-x="1024" /> <glyph glyph-name="iconset0481" unicode="&#59240;" d="M512 853.333333C253.866667 853.333333 42.666667 644.266667 42.666667 384s211.2-469.333333 469.333333-469.333333c260.266667 0 469.333333 209.066667 469.333333 469.333333S772.266667 853.333333 512 853.333333zM512-42.666667C277.333333-42.666667 85.333333 149.333333 85.333333 384 85.333333 618.666667 277.333333 810.666667 512 810.666667c234.666667 0 426.666667-192 426.666667-426.666667C938.666667 149.333333 746.666667-42.666667 512-42.666667zM716.8 401.066667 716.8 401.066667l-4.266667 2.133333c0 0 0 0-2.133333 0l-292.266667 168.533333 0 0C413.866667 573.866667 409.6 576 405.333333 576c-12.8 0-21.333333-8.533333-21.333333-21.333333l0-341.333333c0-12.8 8.533333-21.333333 21.333333-21.333333 4.266667 0 8.533333 2.133333 12.8 4.266667l0 0 292.266667 168.533333c0 0 0 0 2.133333 0l4.266667 2.133333 0 0c4.266667 4.266667 8.533333 10.666667 8.533333 17.066667S721.066667 396.8 716.8 401.066667zM426.666667 249.6 426.666667 518.4 661.333333 384 426.666667 249.6z" horiz-adv-x="1024" />
<glyph glyph-name="guanbi" unicode="&#58897;" d="M567.001395 400.713626 966.070272 799.763149c10.904781 10.905395 10.904781 28.573798 0 39.481242-10.907853 10.905395-28.575949 10.905395-39.482778 0L527.519539 440.193843 128.45271 839.24439c-10.905805 10.905395-28.574925 10.905395-39.481754 0-10.906829-10.906419-10.906829-28.574822 0-39.481242l399.067955-399.049523L88.970957 1.662054c-10.906829-10.906419-10.906829-28.573798 0-39.480218 5.45239-5.452186 12.652646-8.177254 19.741389-8.177254 7.197184 0 14.285926 2.725069 19.740365 8.177254l399.065907 399.052595L926.587494-37.818061c5.454438-5.452186 12.652646-8.177254 19.740365-8.177254 7.198208 0 14.28695 2.725069 19.742413 8.177254 10.904781 10.906419 10.904781 28.573798 0 39.480218L567.001395 400.713626z" horiz-adv-x="1024" />
<glyph glyph-name="xuexiao" unicode="&#58930;" d="M925.761 617.478l-16.543-49.136c0 0-79.438 196.571-314.467 245.72 0 0-96.866 21.943-198.602 0-13.964-7.978-254.891-85.18-297.917-311.249 0 0 9.928-68.815 49.647 0 0 0 23.171 176.919 281.368 262.098h182.062c0 0 211.845-72.075 264.812-245.72l16.556-81.906 49.641-16.392 16.556 196.584h-33.112zM876.12 259.493c0 0-23.171-176.919-281.369-262.098l-182.060-0c0 0-211.845 72.075-264.813 245.72l-16.549 81.906-49.654 16.392-16.548-196.584h33.105l16.549 49.136c0 0 116.962-225.769 314.467-245.72 83.788-19.951 198.617 0 198.617 0s254.884 85.18 297.897 311.248c0 0-9.928 68.815-49.641 0z" horiz-adv-x="1024" />
<glyph glyph-name="13" unicode="&#59086;" d="M147.2 715.2h728c41.6 0 76.8-35.2 76.8-76.8v-552c0-41.6-35.2-76.8-76.8-76.8h-728c-41.6 0-76.8 35.2-76.8 76.8v552c0 41.6 35.2 76.8 76.8 76.8v0zM147.2-30.4c243.2 0 486.4 0 728 0 64 0 115.2 51.2 115.2 115.2 0 184 0 368 0 552 0 64-51.2 115.2-115.2 115.2-243.2 0-486.4 0-728 0-64 0-115.2-51.2-115.2-115.2 0-184 0-368 0-552 0-62.4 51.2-115.2 115.2-115.2zM201.6 476.8c0-44.8 36.8-81.6 81.6-81.6s81.6 36.8 81.6 81.6c0 44.8-36.8 81.6-81.6 81.6-44.8 0-81.6-36.8-81.6-81.6v0zM240 476.8c0 24 19.2 43.2 43.2 43.2s43.2-19.2 43.2-43.2c0-24-19.2-43.2-43.2-43.2-22.4 0-43.2 19.2-43.2 43.2zM675.2 396.8l203.2-203.2c8-8 8-19.2 0-27.2v0c-8-8-19.2-8-27.2 0l-203.2 203.2c-14.4 14.4-40 14.4-54.4 0l-163.2-161.6c-30.4-30.4-80-30.4-110.4 0l-25.6 25.6c-14.4 14.4-40 14.4-54.4 0l-67.2-67.2c-8-8-19.2-8-27.2 0v0c-8 8-8 19.2 0 27.2l67.2 67.2c30.4 30.4 80 30.4 110.4 0l25.6-25.6c14.4-14.4 40-14.4 54.4 0l161.6 161.6c30.4 30.4 80 30.4 110.4 0z" horiz-adv-x="1024" /> <glyph glyph-name="13" unicode="&#59086;" d="M147.2 715.2h728c41.6 0 76.8-35.2 76.8-76.8v-552c0-41.6-35.2-76.8-76.8-76.8h-728c-41.6 0-76.8 35.2-76.8 76.8v552c0 41.6 35.2 76.8 76.8 76.8v0zM147.2-30.4c243.2 0 486.4 0 728 0 64 0 115.2 51.2 115.2 115.2 0 184 0 368 0 552 0 64-51.2 115.2-115.2 115.2-243.2 0-486.4 0-728 0-64 0-115.2-51.2-115.2-115.2 0-184 0-368 0-552 0-62.4 51.2-115.2 115.2-115.2zM201.6 476.8c0-44.8 36.8-81.6 81.6-81.6s81.6 36.8 81.6 81.6c0 44.8-36.8 81.6-81.6 81.6-44.8 0-81.6-36.8-81.6-81.6v0zM240 476.8c0 24 19.2 43.2 43.2 43.2s43.2-19.2 43.2-43.2c0-24-19.2-43.2-43.2-43.2-22.4 0-43.2 19.2-43.2 43.2zM675.2 396.8l203.2-203.2c8-8 8-19.2 0-27.2v0c-8-8-19.2-8-27.2 0l-203.2 203.2c-14.4 14.4-40 14.4-54.4 0l-163.2-161.6c-30.4-30.4-80-30.4-110.4 0l-25.6 25.6c-14.4 14.4-40 14.4-54.4 0l-67.2-67.2c-8-8-19.2-8-27.2 0v0c-8 8-8 19.2 0 27.2l67.2 67.2c30.4 30.4 80 30.4 110.4 0l25.6-25.6c14.4-14.4 40-14.4 54.4 0l161.6 161.6c30.4 30.4 80 30.4 110.4 0z" horiz-adv-x="1024" />
<glyph glyph-name="grade" unicode="&#58988;" d="M863.721739 341.704348c24.486957 48.973913 28.93913 102.4 28.93913 160.278261C892.66087 706.782609 734.608696 873.73913 527.582609 873.73913 320.556522 873.73913 162.504348 706.782609 162.504348 501.982609c0-57.878261 4.452174-111.304348 28.93913-160.278261L51.2 103.513043c0 0 95.721739-35.617391 193.669565-55.652174 64.556522-73.46087 117.982609-151.373913 117.982609-151.373913l129.113043 247.095652c8.904348 0 20.034783 0 26.713043 0 8.904348 0 20.034783 0 26.713043 0l138.017391-233.73913c0 0 57.878261 64.556522 124.66087 138.017391 97.947826 20.034783 193.669565 55.652174 193.669565 55.652174L863.721739 341.704348 863.721739 341.704348 863.721739 341.704348zM353.947826-7.791304c0 0-46.747826 60.104348-89.043478 102.4-62.330435 17.808696-144.695652 35.617391-144.695652 35.617391L215.930435 294.956522c51.2-66.782609 135.791304-129.113043 220.382609-149.147826L353.947826-7.791304 353.947826-7.791304zM520.904348 185.878261c-80.13913 0-309.426087 48.973913-316.104348 316.104348C198.121739 671.165217 351.721739 831.443478 520.904348 831.443478c169.182609 0 329.46087-162.504348 329.46087-329.46087C852.591304 245.982609 601.043478 185.878261 520.904348 185.878261L520.904348 185.878261zM685.634783-7.791304l-82.365217 151.373913c84.591304 20.034783 182.53913 84.591304 233.73913 151.373913l95.721739-164.730435c0 0-82.365217-17.808696-144.695652-35.617391L685.634783-7.791304 685.634783-7.791304zM527.582609 720.13913c-117.982609 0-213.704348-95.721739-213.704348-213.704348s95.721739-213.704348 213.704348-213.704348c117.982609 0 213.704348 95.721739 213.704348 213.704348S645.565217 720.13913 527.582609 720.13913L527.582609 720.13913 527.582609 720.13913zM534.26087 337.252174c-53.426087 0-169.182609 20.034783-178.086957 178.086957-4.452174 84.591304 95.721739 164.730435 178.086957 164.730435 84.591304 0 162.504348-95.721739 164.730435-178.086957C701.217391 390.678261 587.686957 337.252174 534.26087 337.252174L534.26087 337.252174zM534.26087 337.252174" horiz-adv-x="1024" /> <glyph glyph-name="grade" unicode="&#58988;" d="M863.721739 341.704348c24.486957 48.973913 28.93913 102.4 28.93913 160.278261C892.66087 706.782609 734.608696 873.73913 527.582609 873.73913 320.556522 873.73913 162.504348 706.782609 162.504348 501.982609c0-57.878261 4.452174-111.304348 28.93913-160.278261L51.2 103.513043c0 0 95.721739-35.617391 193.669565-55.652174 64.556522-73.46087 117.982609-151.373913 117.982609-151.373913l129.113043 247.095652c8.904348 0 20.034783 0 26.713043 0 8.904348 0 20.034783 0 26.713043 0l138.017391-233.73913c0 0 57.878261 64.556522 124.66087 138.017391 97.947826 20.034783 193.669565 55.652174 193.669565 55.652174L863.721739 341.704348 863.721739 341.704348 863.721739 341.704348zM353.947826-7.791304c0 0-46.747826 60.104348-89.043478 102.4-62.330435 17.808696-144.695652 35.617391-144.695652 35.617391L215.930435 294.956522c51.2-66.782609 135.791304-129.113043 220.382609-149.147826L353.947826-7.791304 353.947826-7.791304zM520.904348 185.878261c-80.13913 0-309.426087 48.973913-316.104348 316.104348C198.121739 671.165217 351.721739 831.443478 520.904348 831.443478c169.182609 0 329.46087-162.504348 329.46087-329.46087C852.591304 245.982609 601.043478 185.878261 520.904348 185.878261L520.904348 185.878261zM685.634783-7.791304l-82.365217 151.373913c84.591304 20.034783 182.53913 84.591304 233.73913 151.373913l95.721739-164.730435c0 0-82.365217-17.808696-144.695652-35.617391L685.634783-7.791304 685.634783-7.791304zM527.582609 720.13913c-117.982609 0-213.704348-95.721739-213.704348-213.704348s95.721739-213.704348 213.704348-213.704348c117.982609 0 213.704348 95.721739 213.704348 213.704348S645.565217 720.13913 527.582609 720.13913L527.582609 720.13913 527.582609 720.13913zM534.26087 337.252174c-53.426087 0-169.182609 20.034783-178.086957 178.086957-4.452174 84.591304 95.721739 164.730435 178.086957 164.730435 84.591304 0 162.504348-95.721739 164.730435-178.086957C701.217391 390.678261 587.686957 337.252174 534.26087 337.252174L534.26087 337.252174zM534.26087 337.252174" horiz-adv-x="1024" />
<glyph glyph-name="quanping" unicode="&#59203;" d="M664.035 486.618l244.223 243.378-0.837-137.922c-0.335-11.437 8.912-21.527 20.351-21.191h14.63c11.438 0.335 21.025 7.065 21.193 18.67l0.672 205.367c0 0.171 0.335 10.934 0.335 10.934 0.17 5.716-1.179 10.934-4.879 14.63-3.7 3.702-8.742 6.055-14.633 5.886l-10.428-0.171c-0.169 0-0.331 0-0.501-0.171l-203.69 0.846c-11.438-0.336-21.025-9.76-21.193-21.361v-14.633c1.685-13.625 12.446-21.529 23.884-21.193l134.219-0.335-243.551-242.542c-11.101-11.104-11.101-29.095 0-40.197 11.104-11.269 29.101-11.269 40.203 0h-0.003M356.233 257.194l-244.393-242.544 0.842 137.253c0.336 11.435-8.918 21.523-20.355 21.193h-15.47c-11.438-0.335-21.025-7.065-21.193-18.672l-0.672-205.536c0-0.171-0.335-10.934-0.335-10.934-0.171-5.722 1.18-10.934 4.875-14.63 3.7-3.7 8.746-6.055 14.633-5.886l10.427 0.17c0.171 0 0.336 0 0.507 0.171l204.532-0.843c11.435 0.336 21.025 9.756 21.193 21.361v14.63c-1.683 13.625-12.451 21.529-23.887 21.193l-134.222 0.335 243.38 242.539c11.104 11.104 11.104 29.101 0 40.203-10.929 11.268-28.757 11.268-39.857-0.001v0M964.271-51.109l-0.507 205.536c-0.336 11.438-9.76 18.335-21.194 18.672h-14.629c-11.439 0.331-20.521-9.759-20.356-21.193l0.843-137.927-244.393 243.214c-11.104 11.104-29.099 11.104-40.203 0-11.097-11.101-11.097-29.099 0-40.203l243.386-242.538-134.22-0.335c-11.438 0.335-22.034-7.739-23.887-21.193v-14.633c0.335-11.437 9.76-21.025 21.193-21.361l203.69 0.843c0.17 0 0.335-0.17 0.507-0.17l10.428-0.171c5.716-0.17 10.934 2.016 14.63 5.886 3.698 3.7 5.042 8.912 4.875 14.63 0 0-0.169 10.764-0.169 10.934l0.004 0.002M152.885 769.355l134.22 0.335c11.435-0.335 22.031 7.739 23.883 21.193v14.636c-0.336 11.437-9.753 21.025-21.193 21.36l-204.694-0.842c-0.17 0-0.336 0.171-0.509 0.171l-10.426 0.169c-5.722 0.17-10.934-2.015-14.633-5.885-3.698-3.7-5.050-8.912-4.875-14.633 0 0 0.335-10.763 0.335-10.933l0.502-205.536c0.171-11.435 9.759-18.335 21.193-18.667h15.475c11.437-0.335 20.519 9.753 20.349 21.191l-0.671 137.248 244.224-242.709c11.097-11.104 29.094-11.104 40.196 0 11.104 11.101 11.104 29.099 0 40.203l-243.38 242.703M152.885 769.355v0z" horiz-adv-x="1024" />
<glyph glyph-name="nav" unicode="&#58987;" d="M888.838 576.225l-751.1 0c-23.944 0-43.339 19.553-43.339 43.646l0 40.012c0 24.093 19.395 43.645 43.339 43.645l751.1 0c23.923 0 43.334-19.554 43.334-43.645l0-40.012c-0.001-24.093-19.411-43.646-43.333-43.646l0 0zM888.838 308.491l-751.1 0c-23.944 0-43.339 19.533-43.339 43.64l0 39.998c0 24.115 19.395 43.647 43.339 43.647l751.1 0c23.923 0 43.334-19.533 43.334-43.647l0-39.998c-0.001-24.107-19.411-43.64-43.333-43.64l0 0zM888.838 19.83l-751.1 0c-23.944 0-43.339 19.532-43.339 43.627l0 40.017c0 24.093 19.395 43.641 43.339 43.641l751.1 0c23.923 0 43.334-19.548 43.334-43.641l0-40.017c-0.001-24.094-19.411-43.627-43.333-43.627l0 0z" horiz-adv-x="1024" /> <glyph glyph-name="nav" unicode="&#58987;" d="M888.838 576.225l-751.1 0c-23.944 0-43.339 19.553-43.339 43.646l0 40.012c0 24.093 19.395 43.645 43.339 43.645l751.1 0c23.923 0 43.334-19.554 43.334-43.645l0-40.012c-0.001-24.093-19.411-43.646-43.333-43.646l0 0zM888.838 308.491l-751.1 0c-23.944 0-43.339 19.533-43.339 43.64l0 39.998c0 24.115 19.395 43.647 43.339 43.647l751.1 0c23.923 0 43.334-19.533 43.334-43.647l0-39.998c-0.001-24.107-19.411-43.64-43.333-43.64l0 0zM888.838 19.83l-751.1 0c-23.944 0-43.339 19.532-43.339 43.627l0 40.017c0 24.093 19.395 43.641 43.339 43.641l751.1 0c23.923 0 43.334-19.548 43.334-43.641l0-40.017c-0.001-24.094-19.411-43.627-43.333-43.627l0 0z" horiz-adv-x="1024" />
<glyph glyph-name="mima" unicode="&#58887;" d="M791.366 436.93l-459.174 0.791c-0.186 61.936-16.847 157.926 36.027 216.192 32.142 35.52 86.326 72.068 144.127 72.068 85.395 0 149.246-64.412 180.166-144.126 9.009-22.992 16.936-35.38 36.037-36.037 47.899-1.45 44.986 49.075 36.029 72.067C719.914 732.97 636.118 798.047 512.346 798.047c-84.12 0-130.475-17.265-180.154-72.065-76.009-83.746-72.259-219.151-72.067-288.26l-26.795-0.791c-25.045 0-45.273-20.034-45.273-44.667v-378.249c0-24.774 20.269-44.755 45.273-44.755h557.99c25.009 0 45.273 19.989 45.273 44.755V392.263c0.046 24.678-20.218 44.667-45.227 44.667zM765.68 77.65c0-9.966-3.476-18.514-10.58-25.52-7.045-7.051-15.56-10.58-25.517-10.58H296.362c-9.966 0-18.472 3.478-25.475 10.58-7.051 7.049-10.624 15.553-10.624 25.52V330.365c0 19.878 16.166 36.054 36.098 36.054h433.212c19.932 0 36.107-16.176 36.107-36.054V77.65z" horiz-adv-x="1024" /> <glyph glyph-name="mima" unicode="&#58887;" d="M791.366 436.93l-459.174 0.791c-0.186 61.936-16.847 157.926 36.027 216.192 32.142 35.52 86.326 72.068 144.127 72.068 85.395 0 149.246-64.412 180.166-144.126 9.009-22.992 16.936-35.38 36.037-36.037 47.899-1.45 44.986 49.075 36.029 72.067C719.914 732.97 636.118 798.047 512.346 798.047c-84.12 0-130.475-17.265-180.154-72.065-76.009-83.746-72.259-219.151-72.067-288.26l-26.795-0.791c-25.045 0-45.273-20.034-45.273-44.667v-378.249c0-24.774 20.269-44.755 45.273-44.755h557.99c25.009 0 45.273 19.989 45.273 44.755V392.263c0.046 24.678-20.218 44.667-45.227 44.667zM765.68 77.65c0-9.966-3.476-18.514-10.58-25.52-7.045-7.051-15.56-10.58-25.517-10.58H296.362c-9.966 0-18.472 3.478-25.475 10.58-7.051 7.049-10.624 15.553-10.624 25.52V330.365c0 19.878 16.166 36.054 36.098 36.054h433.212c19.932 0 36.107-16.176 36.107-36.054V77.65z" horiz-adv-x="1024" />
<glyph glyph-name="shipin" unicode="&#58894;" d="M858.8-7.6H182.2c-61 0-110.8 49.6-110.8 110.8V617c0 61 49.6 110.8 110.8 110.8h676.6c61 0 110.8-49.6 110.8-110.8v-513.8c-0.2-61-49.8-110.8-110.8-110.8zM182.2 687.6c-39 0-70.8-31.8-70.8-70.8v-513.8c0-39 31.8-70.8 70.8-70.8h676.6c39 0 70.8 31.8 70.8 70.8v514c0 39-31.8 70.8-70.8 70.8H182.2zM225.4-7.6c-11 0-20 9-20 20V707.6c0 11 9 20 20 20s20-9 20-20v-695.2c0-11-8.8-20-20-20zM815.4-7.6c-11 0-20 9-20 20V707.6c0 11 9 20 20 20s20-9 20-20v-695.2c0-11-9-20-20-20zM225.4 496.8h-134c-11 0-20 9-20 20s9 20 20 20h134c11 0 20-9 20-20s-8.8-20-20-20zM225.4 340h-134c-11 0-20 9-20 20s9 20 20 20h134c11 0 20-9 20-20s-8.8-20-20-20zM225.4 163.4h-134c-11 0-20 9-20 20s9 20 20 20h134c11 0 20-9 20-20s-8.8-20-20-20zM949.4 496.8h-134c-11 0-20 9-20 20s9 20 20 20h134c11 0 20-9 20-20s-9-20-20-20zM949.4 340h-134c-11 0-20 9-20 20s9 20 20 20h134c11 0 20-9 20-20s-9-20-20-20zM949.4 163.4h-134c-11 0-20 9-20 20s9 20 20 20h134c11 0 20-9 20-20s-9-20-20-20zM432 216.8c-3.4 0-7 0.8-10 2.6-6.2 3.6-10 10.2-10 17.4V483.2c0 7.2 3.8 13.8 10 17.4 6.2 3.6 13.8 3.6 20 0l213.4-123.2c6.2-3.6 10-10.2 10-17.4s-3.8-13.8-10-17.4L442 219.6c-3.2-1.8-6.6-2.8-10-2.8z m20 231.8v-177.2l153.4 88.6-153.4 88.6z" horiz-adv-x="1024" />
<glyph glyph-name="character" unicode="&#58926;" d="M921.087359-94.72000000000003v35.2A410.24 410.24 0 0 1 661.887359 320a307.2 307.2 0 1 1-300.8 0 410.24 410.24 0 0 1-256-380.8v-35.2H101.887359a32 32 0 0 1 32-32 32 32 0 0 1 32 32v34.56a345.6 345.6 0 0 0 691.2 0v-35.2a31.36 31.36 0 0 1 31.36-30.08 32 32 0 0 1 32.64 32zM270.207359 588.8A241.28 241.28 0 1 0 511.487359 347.52 241.28 241.28 0 0 0 270.207359 588.8z" horiz-adv-x="1024" /> <glyph glyph-name="character" unicode="&#58926;" d="M921.087359-94.72000000000003v35.2A410.24 410.24 0 0 1 661.887359 320a307.2 307.2 0 1 1-300.8 0 410.24 410.24 0 0 1-256-380.8v-35.2H101.887359a32 32 0 0 1 32-32 32 32 0 0 1 32 32v34.56a345.6 345.6 0 0 0 691.2 0v-35.2a31.36 31.36 0 0 1 31.36-30.08 32 32 0 0 1 32.64 32zM270.207359 588.8A241.28 241.28 0 1 0 511.487359 347.52 241.28 241.28 0 0 0 270.207359 588.8z" horiz-adv-x="1024" />
......
import React from 'react'
/**
* 音频基础组件类
* props:
* src: 音频源地址
* lastTime: xx, 上次播放时间
* skipBegin: 忽略片头,true/false
* onmeta: 加载资源,可获取如视频长度等信息 父级方法实现
* onloaded: 加载完音频,可播放方法 父级方法实现
* onloading: 加载中音频 父级方法实现
* onplay: 播放音频 父级方法实现
* onpause: 暂停播放 父级方法实现
* ontime:
* 外部可用:
* duration
* seek
* play
* pause
*
*
* 问题:
* 1. 华为 P7;小米 自带浏览器,不支持 倍速
* 2. 音频 跳跃时,可能刷新音频,导致currentTime重新开始
* 3. 外观不够美化,进度条 不能拖动
* 4. 图片放大 这个插件也需要集成化
* 5.
*/
class Audio extends React.Component {
static SKIP_SECS = 7; // 片头7s
/**
* 构造函数
*/
constructor(props) {
/* 继承属性 */
super(props);
/* 缓存音频 音频列表 */
this.playList = [];
/* 音频列表中,播放第几个 */
this.audioIndex = 0;
// console.log('diaoyong', this.playList);
/* 当前所在播放时间 */
this.time = 0;
this.stepTimer = null; //time计时器
// this.lastSeekTime = 0; // 记录上一次 seek时,存储的时间值
}
/**
* 组件渲染前
*/
componentWillMount() {
/* 组件渲染前 加载Audio对象并加载音频 */
this.getAudioObject();
this.skipBeginAndPlay();
}
/**
* 组件渲染后
*/
componentDidMount() {
// 组件创建完成 执行 音频播放
// this.audioPlay(0);
}
componentWillReceiveProps(nextProps) {
// console.log(12222);
// if (nextProps.src !== this.props.src) {
// // 暂停当前sound
// let sound = this.getAudioObject();
// if (sound && sound.playing()) {
// sound.pause();
// }
// // 检查是否已存在
// let existIndex = findIndex(this.playList, {src: nextProps.src});
// if (existIndex < 0) {
// existIndex = this.playList.length;
// this.playList.push({src: nextProps.src, howl: null, time: 0});
// }
// this.audioPlay(existIndex);
// }
}
componentWillUnmount() {
this.getAudioObject().pause();
}
shouldComponentUpdate(nextProps, nextState) {
return false;
}
// 第一播放,跳过片头,同时考虑lastTime
skipBeginAndPlay() {
let lastTime = this.props.lastTime || 0;
if (this.props.skipBegin) { lastTime = Math.max(lastTime, Audio.SKIP_SECS); }
if (lastTime) {
// if (lastTime > sound.duration() - 10) { lastTime = 0; } // 最后10s不执行跳转lastTime
// if (cur < lastTime) { // 多次调用,如果当前播放时间大于lastTime时,应该终止播放
this.seek(lastTime);
// }
}
}
// 无参数时,播放当前index音频到对应位置。
// index 要播放的序号
audioPlay (index) {
this.audioIndex = index;
this.time = 0;
this.skipBeginAndPlay();
}
step() {
clearTimeout(this.stepTimer);
let sound = this.getAudioObject();
if (!sound) {return;}
let seek = Math.round(sound.seek() || 0);
let playing = sound.playing();
if (seek !== this.time) {
this.time = seek;
this.props.ontime(seek);
}
if (playing) {
this.stepTimer = setTimeout(this.step, 300);
}
}
stepPause() {
clearTimeout(this.stepTimer);
this.props.onpause();
}
/**
* 搜寻点击进度条某个位置 跳播方式
* @param {number} s 传入当前需要改变的时间
*/
seek(s) {
/**
* 设置时,需要判断是否在seekable的范围内,不在,等待缓冲
* 如果在了,就可以设置 -- 华为手机 就是不行,设置 可能会断,重新加载音频
*/
let _audio = this.getAudioObject(), i = 0;
// if (!/android/gi.test(navigator.userAgent)) {
if ('fastSeek' in _audio) {
_audio.fastSeek(s); // 改变audio.currentTime的值
return ;
}
this.pause();
_audio.currentTime = s;
this.play();
// } else {
// for (i=0; i<_audio.seekable.length; i++) {
// if (_audio.seekable.start(i) <= s && s >= _audio.seekable.end(i)) {
// break;
// }
// }
// if (i < _audio.seekable.length) {
// _audio.currentTime = s;
// } else {
// _audio.buffered.end(_audio.buffered.length - 1);
// }
// }
}
/**
* 返回视频长度
*/
getDuration() {
return this.getAudioObject().duration || 0;
}
/**
* 开始播放
*/
play() {
this.getAudioObject().play();
}
/**
* 播放暂停
*/
pause() {
this.getAudioObject().pause();
}
/**
* 播放速率改变
* @param {float} n 速率值,每次加0.5
*/
rate(n) {
this.getAudioObject().defaultPlaybackRate = n
this.getAudioObject().playbackRate = n
}
/**
* render渲染
*/
render () {
return null
}
/**
* 获取 Audio对象
* 内部创建一个 音频对象
*/
getAudioObject() {
/* 在缓存音频列表里,获取对应要播放的音频对象 */
let playItem = this.playList[this.audioIndex];
/* 如果音频列表里没有 */
if (!playItem) {
this.playList[this.audioIndex] = {src: this.props.src, obj: null, time: 0};
playItem = this.playList[this.audioIndex];
}
let _mediaAudio = playItem.obj;
/* 如果缓存中没有音频对象 */
if (!_mediaAudio) {
/* 创建一个Audio对象 */
_mediaAudio = document.createElement("audio");
_mediaAudio.src = playItem.src; // 资源src加载
_mediaAudio.preload = 'auto'; // none:不预载; metadata:仅仅缓冲文件的元数据; auto:缓冲音频文件
_mediaAudio.autoPlay = true;
_mediaAudio.load(); // 重新加载src指定的资源
_mediaAudio.loadAll = false; // 可能存在缓存,如果从缓存中获取,那么歌曲就全部加载完成了,就不再需要监听progress事件,虽然它还会继续加载
/* 开始请求 顺序 1 */
_mediaAudio.addEventListener('loadstart', () => {
}, false);
/* 正在请求 顺序 2, 这个比较特殊,有缓存时,它还会请求 */
_mediaAudio.addEventListener('progress', () => {
/* 父级事件,告诉父级音频在加载中 */
!_mediaAudio.loadAll && this.props.onloading();
// console.log('loading', !_mediaAudio.loadAll);
}, false);
/* 资源长度改 顺序 3 */
_mediaAudio.addEventListener('durationchange', () => {
/* 父级事件,告诉父级一些资源信息 华为原生浏览器上,播放前loadedmetadata事件监听,获取不到duration信息 */
this.props.onmeta();
}, false);
/* 可以播放,但中途可能因为加载而暂 顺序 4 */
_mediaAudio.addEventListener('canplay', () => {
/* 父级事件,告诉父级音频加载完成 */
this.props.onloaded();
_mediaAudio.play();
}, false);
/* 可以播放,歌曲全部加载完毕 顺序 5 */
_mediaAudio.addEventListener('canplaythrough', () => {
/* 歌曲全部加载 */
_mediaAudio.loadAll = true;
/* 父级事件,告诉父级音频加载完成 */
this.props.onloaded();
_mediaAudio.play();
}, false);
/* play()和autoplay 开始播放时,触发 但并未真正开始播放 */
_mediaAudio.addEventListener('play', () => {
/* 父级事件,告诉父级音频正在播放 */
this.props.onplay();
/* 父级事件,告诉父级音频在加载中 */
this.props.onloading();
}, false);
/* playing 开始播放时,触发,真正开始播放 */
_mediaAudio.addEventListener('playing', () => {
/**
* 0 HAVE_NOTHING 没有关于音频/视频是否就绪的信息
* 1 HAVE_METADATA 关于音频/视频就绪的元数据
* 2 HAVE_CURRENT_DATA 关于当前播放位置的数据是可用的,但没有足够的数据来播放下一帧/毫秒
* 3 HAVE_FUTURE_DATA 当前及至少下一帧的数据是可用的
* 4 HAVE_ENOUGH_DATA 可用数据足以开始播放
*/
if (_mediaAudio.readyState >= 2) {
/* 父级事件,告诉父级音频加载完成 */
this.props.onloaded();
}
}, false);
/* pause() 暂停播放时,触发 */
_mediaAudio.addEventListener('pause', () => {
/* 父级事件,告诉父级音频暂停 */
this.props.onpause();
}, false);
/* 获取一些预设信息,事件监听 */
_mediaAudio.addEventListener('loadedmetadata', () => {
/* 父级事件,告诉父级一些资源信息 */
this.props.onmeta();
}, false);
// waiting error abort
/* 播放时间改变时,实时改变进度条 */
_mediaAudio.addEventListener('timeupdate', () => {
/* 父级事件,告诉父级当前播放位置 */
this.props.ontime(_mediaAudio.currentTime);
// console.log('play');
}, false);
/* 设置playbackRate,触发事件ratechange,播放速率改变 */
_mediaAudio.addEventListener('ratechange', () => {
// alert(this.getAudioObject().playbackRate)
}, false);
/* 设置currentTime,触发事件seeking,寻找中 */
_mediaAudio.addEventListener('seeking', () => {
/* 父级事件,告诉父级音频在加载中 */
this.props.onloading();
}, false);
/* 设置currentTime,触发事件seeked,寻找完成 */
_mediaAudio.addEventListener('seeked', () => {
/* 父级事件,告诉父级音频加载完成 */
this.props.onloaded();
}, false);
playItem.obj = _mediaAudio;
}
return _mediaAudio;
}
}
module.exports = Audio;
import React from 'react'
import { Link } from 'react-router'
import sortedIndex from 'lodash/sortedIndex'
import {toTimeString} from '../../libs/utils'
import Audio from './Audio.jsx'
import ProgressCircle from '../../components/ProgressCircle.jsx'
import Loading from '../../components/Loading.jsx'
import {chapterType} from '../../libs/const'
let pagePlayMap = {};
const RATES = [1.0,1.2,1.5,2.0,2.5,3.0]
let isSkipBegin = false;
if (process.env.BROWSER) {
require('css/audioPlay.css');
// require('howler/dist/howler.min.js');
require('previewImage');
isSkipBegin = /skip=1/.test(document.cookie); // 浏览器下根据cookie判断是否跳过片头
}
class AudioPanel extends React.Component {
constructor(props) {
super(props);
this.state = {
pptIndex: 0,
loading: true, // 是否加载中
playing: false, // 是否播放中
time: 0, // 当前播放时间
duration: 0, // 总的播放时间
rateIndex: 0, //是否加倍
skipBegin: isSkipBegin, // 跳过片头
showChapter: false, // 显示章节弹出层
};
this.pptTimes = this.props.ppts.map(ppt => ppt.ppt_point);
this.pagePlayMap = pagePlayMap; // 播放期间记录本次页面进度 {[time, progress]}
this._setState = this._setState.bind(this);
this.getAudio = this.getAudio.bind(this);
this.getProgress = this.getProgress.bind(this);
this.onAudioMeta = this.onAudioMeta.bind(this);
this.onAudioLoad = this.onAudioLoad.bind(this);
this.onAudioLoading = this.onAudioLoading.bind(this);
this.onAudioPlay = this.onAudioPlay.bind(this);
this.onAudioPause = this.onAudioPause.bind(this);
this.onAudioCurrentTime = this.onAudioCurrentTime.bind(this);
this.toggleChapter = this.toggleChapter.bind(this);
this.toggleSet = this.toggleSet.bind(this);
this.handlePlay = this.handlePlay.bind(this);
this.handleForward = this.handleForward.bind(this);
this.handleBackward = this.handleBackward.bind(this);
this.handleTouchBar = this.handleTouchBar.bind(this);
this.handleOver = this.handleOver.bind(this);
this.handleSkip = this.handleSkip.bind(this);
this.handleRate = this.handleRate.bind(this);
this.handleClickList = this.handleClickList.bind(this);
this.handlePptView = this.handlePptView.bind(this);
}
componentDidMount() {
}
componentWillReceiveProps(nextProps) {
// 切换index,重置audio相关state
//if (nextProps.index !== this.props.index) {
// this._setState({time: 0})
//}
// 更新pptTimes
if (nextProps.ppts !== this.props.ppts) {
this.pptTimes = nextProps.ppts.map(ppt => ppt.ppt_point);
}
}
componentWillUnmount() {
this.unmounted = true; // 标记unmount状态,防止音频停止事件触发setState
let preview = document.getElementById('__previewImage-container');
if (preview) {
preview.style.display = 'none';
}
}
shouldComponentUpdate(nextProps, nextState) {
return this.state !== nextState || this.props !== nextProps;
}
_setState(obj) {
this.setState((prevState, props) => Object.assign({}, prevState, obj || {}))
}
getAudio() {
return this.refs.audio;
}
// 获取进度,综合接口返回上次播放进度和本页播放进度记录
getProgress(id) {
let p = 0;
let pageProg = (this.pagePlayMap[id] || [])[1] || 0;
let dataProg = ((this.props.chapterData.progressMap || {})[id] || {}).chapter_progress || 0;
p = Math.max(pageProg, dataProg);
return p;
}
/**
* 章节层显示与否
*/
toggleChapter() {
this._setState({ showChapter: !this.state.showChapter });
}
toggleSet() {
this._setState({ showSet: !this.state.showSet });
}
// 点击章节列表外面隐藏
handleClickList(e) {
if (e.target === e.currentTarget) {
this._setState({showChapter: false});
}
}
// seek
handleTouchBar(e) {
if (this.state.loading) return;
e = e.nativeEvent;
let x = e.pageX || e.clientX;
const $ = require('jquery');
let bar = $('#audio-bar');
let toX = x - bar.offset().left;
if (toX >= 0) {
let audio = this.getAudio();
let time = toX / bar.width() * this.state.duration;
audio.seek(time);
/* 跳播时,实时改变进度条 */
this.onAudioCurrentTime(time);
}
}
// 前15s
handleBackward() {
let audio = this.getAudio();
audio.seek(Math.max(0, this.state.time - 15));
}
// 后15s
handleForward() {
let audio = this.getAudio();
audio.seek(Math.min(this.state.duration - (+this.state.playing), this.state.time + 15));
}
// 标记完成
handleOver() {
this.props.handleOver(this.state.time);
}
// 通过previewImage预览图片
handlePptView(e) {
if (typeof previewImage !== 'object') return;
let list = this.props.ppts.map(ppt => ppt.ppt_url);
if(!list.length) {
list.push(this.props.course.curriculum && this.props.course.curriculum.curriculum_picture || '')
}
previewImage.start({
urls: list,
current: list[this.state.pptIndex]
});
}
formatTime(seconds) {
return toTimeString(Math.round(seconds), seconds >= 3600 ? 'h:m:s' : 'm:s');
}
/**
* 点击播放 click方法
*/
handlePlay() {
if (this.state.loading) return;
let audio = this.getAudio();
if (this.state.playing) {
audio.pause();
} else {
audio.play();
}
}
/**
* 跳过片头 click方法
*/
handleSkip() {
let skip = +(!this.state.skipBegin);
let d = new Date();
d.setMonth(d.getMonth() + 1);
document.cookie = 'skip=' + skip + ';path=/;domain=.ezijing.com;expires=' + d.toGMTString();
this._setState({ skipBegin: skip });
}
/**
* 控制倍速 click方法
*/
handleRate() {
let audio = this.getAudio();
if (audio) {
let l = RATES.length;
let from = this.state.rateIndex;
let to = (from + 1 + l) % l;
this._setState({ rateIndex: to});
audio.rate(RATES[to]);
}
}
// 处理Audio事件, 向下组件传递内容
/**
* 获取资源信息
*/
onAudioMeta() {
this._setState({duration: this.refs.audio.getDuration()});
}
/**
* 音频加载中
*/
onAudioLoading() {
this._setState({loading: true});
}
/**
* 音频加载完成,可以播放
*/
onAudioLoad() {
this._setState({loading: false});
}
/**
* 音频播放事件
*/
onAudioPlay() {
this._setState({playing: true});
}
/**
* 音频停止事件
*/
onAudioPause() {
if (this.unmounted) return;
this._setState({playing: false});
}
/**
* 实时改变当前时间
* @param {number} time 当前时间
*/
onAudioCurrentTime(time) {
if (this.unmounted) return;
// 更新当前播放时间
this._setState({time: time});
// 更新当前进度
const cid = this.props.chapterData.leaves[this.props.index];
this.pagePlayMap[cid] = [time, Math.round(time/this.state.duration * 100)];
// 检查ppt是否需要更新
if (this.pptTimes.length) {
let pptIndex = this.pptTimes.indexOf(time);
if (pptIndex < 0) {
pptIndex = sortedIndex(this.pptTimes, time);
pptIndex = Math.max(0, pptIndex - 1);
}
if (this.state.pptIndex !== pptIndex) {
this._setState({pptIndex});
}
}
this.props.handleAudioTime(time);
}
render() {
const {pptIndex, time, duration, playing, loading} = this.state;
const {index, chapterData, ppts, course, video_curm3u8} = this.props;
const chapter = chapterData.map[chapterData.leaves[index]];
if (!course || !course.course_id || !chapter) {
return <div></div>
}
const ppt = ppts[pptIndex] || {};
const isFirst = index === 0, isLast = index === chapterData.leaves.length - 1;
const timeStr = this.formatTime(time);
const durationStr = this.formatTime(duration);
// 上次进度时间
let lastTime = 0;
if (this.pagePlayMap[chapter.id]) {
lastTime = this.pagePlayMap[chapter.id][0] || 0;
} else {
lastTime = (chapterData.progressMap[chapter.id] || {}).last_position || 0;
}
const HEADER = <header>
<h1>{course.course_name || ''}</h1>
<Link to={`/courses/${course.course_id}`} className="play-back-phone">&nbsp;</Link>
</header>
// 是否支持播放音频
chapter.video = chapter.video ? chapter.video : {}
chapter.video.video_voice = chapter.video.video_voice ? chapter.video.video_voice : (video_curm3u8.audio && video_curm3u8.audio[0].url || '')
if (!chapter.video.video_voice) {
return <div className="page-audio">
{HEADER}
<p className="text-error" style={{color:'#f00'}}>本课程尚未添加对应手机端音频文件</p>
</div>
}
return (
<div className="page-audio">
{HEADER}
<section className="play-wrap rel">
<img className="ppt-inbox" onClick={this.handlePptView} src={ppt.ppt_url || (course.curriculum && course.curriculum.curriculum_picture || '')} alt=""/>
<button type="button" onTouchEnd={this.toggleSet} className={`play-set-btn ${this.state.showSet ? 'disable' : ''}`}></button>
<div className={`play-set ${this.state.showSet ? '' : 'hide' }` } >
<div>
<div className="play-set-time"><em>{timeStr}</em>{durationStr}</div>
<div style={{display: "flex"}}>
<button onTouchEnd={this.handleSkip} className={`play-jump ${this.state.skipBegin ? 'disable' : ''}`}><em></em><br/>跳过片头</button>
<button onTouchEnd={this.handleBackward} className="play-set-button play-minus"></button>
<button onTouchEnd={this.handleForward} className="play-set-button play-add"></button>
<button onTouchEnd={this.handleRate} className="play-set-button play-double">{RATES[this.state.rateIndex].toFixed(1)}X<br/>倍速播放</button>
</div>
</div>
</div>
</section>
<footer>
<Audio
ref="audio"
src={chapter.video.video_voice}
lastTime={lastTime}
skipBegin={this.state.skipBegin}
onmeta={this.onAudioMeta}
onloading={this.onAudioLoading}
onloaded={this.onAudioLoad}
onplay={this.onAudioPlay}
onpause={this.onAudioPause}
ontime={this.onAudioCurrentTime}
/>
<div className="play-timer">
<div className="audio-bar" onClick={this.handleTouchBar} id="audio-bar"><span className="audio-progress" style={{width: ''+(duration ? time / duration * 100 : 0)+'%'}}></span></div>
<div className="show-time">
<span className="cur-time">{timeStr}</span>
<span className="total-time">{durationStr}</span>
</div>
</div>
<div className="play-ctrls">
<div className="ctrl-items play-ctrl-chapter" onTouchEnd={this.toggleChapter}><em></em><br/>章节</div>
<div className="ctrl-items btns">
<Link to={`/courses/${course.semester_id}/${course.course_id}/chapters/${isFirst ? chapter.id : chapterData.leaves[index - 1]}`} type={`button ${isFirst ? 'disable' : ''}`} className={`button play-prev ${isFirst ? 'disable' : ''}`}>
</Link>
<button type="button" onClick={this.handlePlay} className={`button play-btn ${loading ? 'play-load' : playing ? 'play-pause' : 'play-play'}`}>{loading ? <Loading/> : ''}</button>
<Link to={`/courses/${course.semester_id}/${course.course_id}/chapters/${isLast ? chapter.id : chapterData.leaves[index + 1]}`} type={`button ${isLast ? 'disable' : ''}`} className={`button play-next ${isLast ? 'disable' : ''}`}>
</Link>
</div>
{ /* <div className={`ctrl-items play-ctrl-mark ${this.getProgress(chapter.id) === 100 ? 'disable' : ''}`} onTouchEnd={this.handleOver}><em></em><br/>标记为已学完</div>*/ }
</div>
<div className={`play-chapter ${this.state.showChapter ? '' : 'hide' }` } onClick={this.handleClickList}>
<div className="play-chapter-inner">
{chapterData.tree.map((tree, i) => {
let root = chapterData.map[tree[0]];
return (
<dl key={i}>
<dt>{root.name}</dt>
{tree[1].map((leaf, j) => {
leaf = chapterData.map[leaf];
return (
<dd key={j} className={index === chapterData.leaves.indexOf(leaf.id) ? 'on' : ''}>
<div className="cl">
<ProgressCircle percent={this.getProgress(leaf.id)} className="play-prog" />
<Link to={`/courses/${course.semester_id}/${course.course_id}/chapters/${leaf.id}`}>{leaf.name}</Link>
{leaf.type === chapterType.VIDEO ?
<span className="fr">{this.formatTime(leaf.video && leaf.video.video_length || 0)}</span>
: null
}
</div>
</dd>
);
})}
</dl>
);
})}
</div>
</div>
</footer>
</div>
)
}
}
module.exports = AudioPanel;
/**
* 测验类章节
* chapter
* courseId
* action
* dispatch
*/
import React from 'react'
import Formsy from 'formsy-react';
import FormsyComponent from '../../components/formsy/Component.jsx'
import FormItem from '../../components/formsy/FormItem.jsx'
import CoursesAction from '../../actions/CoursesAction'
import OperateAction from '../../actions/OperateAction'
import { getRequestTypes } from '../../libs/utils';
class ChapterExam extends FormsyComponent {
constructor(props) {
super(props);
this.state = { error: '' };
}
componentDidMount () {
this.courseAction = new CoursesAction();
this.operateAction = new OperateAction();
this.startTime = (new Date()).getTime();
// 加载回答
this.props.dispatch(this.courseAction.loadChapterWork(this.props.semesterId, this.props.chapter.homework.id));
}
componentWillReceiveProps (nextProps) {
let submitType = getRequestTypes(CoursesAction.SUBMIT_CHAPTER_WORK);
switch (nextProps.action.type) {
case submitType.success:
this.disableSubmitButton();
// 提交成功,重新获取答案
nextProps.dispatch(this.courseAction.loadChapterWork(this.props.semesterId, nextProps.chapter.homework.id));
nextProps.handleSubmited(nextProps.chapter.id);
break;
case submitType.failure:
this.enableSubmitButton();
nextProps.dispatch(this.operateAction.showErrorMessage(nextProps.action.error.message || '提交章节测验失败'));
break;
}
}
/**
* 提交登录
*/
onSubmit = model => {
this.loadingSubmitButton();
const {courseId, chapter, semesterId} = this.props;
const questions = chapter.homework && chapter.homework.questions || [];
let data = {
course_id: courseId,
chapter_id:chapter.id,
work_id: chapter.homework.id,
semester_id: semesterId,
work_contents: '',
//url: '',
duration: Math.ceil(((new Date()).getTime() - this.startTime) / 1000),
score: 0
}
// 组织返回答案结构
let correctNumber = 0;
let answers = [];
questions.forEach(q => {
let opts = [],
isCorrect = 0, seledNum = 0, correctCount = 0,
curSels = q.question_type === 2 ? model[q.id] || [] : [model[q.id] || ''],
options;
try {
options = JSON.parse(q.question_options);
} catch (e) {
options = [];
console.log('Try parse question_options JSON string fail. in play/ChapterExam');
}
options.forEach(o => {
let isSelected = (curSels.indexOf(o.id) >= 0) - 0;
if (o.checked) { correctCount++; }
if (o.checked && isSelected) { seledNum++; }
opts.push({
id: o.id,
option: o.option,
checked: o.checked,
selected: isSelected
});
});
isCorrect = (seledNum && seledNum === correctCount) - 0;
answers.push({
question_id: q.id,
options: opts,
is_correct: isCorrect
});
if (isCorrect) { correctNumber++; }
});
data.score = Math.round(correctNumber / questions.length * 1000) / 10; // 百分制
data.work_contents = JSON.stringify(answers);
this.props.dispatch(this.courseAction.submitChapterWork(data));
}
// 表单变更时,取消掉全局错误消息
onFormChange = () => {
this._setState({ error: '' });
}
// 将答案解析出来
parseAnswers = (strAnswers) => {
if (strAnswers === this._lastParseString && this._lastParseAnswers) {
return this._lastParseAnswers;
}
let r = {};
try {
let answers = JSON.parse(strAnswers);
this._lastParseString = strAnswers; // 记录上次成功解析的串
(answers || []).forEach(q => {
let opts = {};
(q.options || []).forEach(o => {
opts[o.id] = o;
});
r[q.question_id] = opts;
});
this._lastParseAnswers = r; // 记录上次的解析结果
} catch (e) { console.log('parse answer string to json failed.', e) }
return r;
}
render () {
const {chapter, chapter_work} = this.props;
const questions = chapter.homework && chapter.homework.questions;
let work = null;
let answers = {}
if (chapter_work.data && chapter_work.data.work_contents) {
work = chapter_work.data;
answers = this.parseAnswers(work.work_contents);
}
return (
<div className="play-paper">
<div className="play-paper-body">
<div className="play-paper-title"><div><h3>{chapter.name}</h3></div></div>
<div className="play-paper-content play-chapter-exam">
{work ?
<div className="result">正确率:{work.score || 0}%</div>
: null
}
<Formsy.Form
onValid={this.enableSubmitButton}
onInvalid={this.disableSubmitButton}
onValidSubmit={this.onSubmit}
onChange={this.onFormChange}
>
{questions.length ?
<ul>
{questions.map((q, qi) => {
let options;
let formOpts = {};
let rights = [];
let isCheckbox = q.question_type === 2;
let answer = answers[q.id] || {};
try {
options = JSON.parse(q.question_options);
options.forEach((o, i) => {
let k = String.fromCharCode(65 + i);
formOpts[o.id] = k + '. ' + o.option;
if (o.checked) rights.push(k);
})
} catch (e) {}
return (
<li key={qi}>
<div className="exam-number">{qi + 1}.</div>
<div className="exam-title"><div className="edit_html" dangerouslySetInnerHTML={{__html: (isCheckbox ? '(多选题)' : '(单选题)') + q.question_content}}></div></div>
{!work ?
<FormItem
name={q.id}
itemType={isCheckbox ? FormItem.CHECKBOX : FormItem.RADIO}
options={formOpts}
required
/>
:
<div>
{options.map((o, oi) => {
let selected = answer[o.id] && answer[o.id].selected;
let rclass = o.checked ? 'correct' : selected ? 'wrong' : '';
let tclass = isCheckbox ? 'checkbox' : 'radio';
return (
<div className={`${tclass} ${rclass}`} key={oi}>
<label><input name={q.id} type={tclass} defaultChecked={selected} readOnly/>&nbsp;{String.fromCharCode(65 + oi)}. {o.option}</label>
</div>
);
})}
<div className="answer">正确答案:{rights.join(',')}</div>
</div>
}
</li>
)
})}
</ul>
:
<p className="no-data">暂无问题数据</p>
}
<p className="text-danger">{this.state.error}</p>
<div className="area-btns">
<button type="submit" disabled={!this.canSubmit() || work} className="btn btn-primary" >{this.isSubmitLoading() ? '保存中...' : work ? '已提交' : '提交'}</button>
<span className="help-info">&emsp;&emsp;注意:测试只有一次提交机会</span>
</div>
</Formsy.Form>
</div>
</div>
</div>
);
}
}
module.exports = ChapterExam;
/**
* 阅读类章节
* chapter
* handleReaded
*/
import React, { Component } from 'react'
class ChapterRead extends Component {
componentDidMount() {
const {chapter} = this.props;
if (chapter && chapter.reading) {
this.props.handleReaded();
}
}
componentWillReceiveProps(nextProps) {
if (this.props.chapter && nextProps.chapter &&
this.props.chapter.id !== nextProps.chapter.id) {
nextProps.handleReaded();
}
}
render () {
const {chapter} = this.props;
const reads = chapter.reading ? [chapter.reading] : [];
return (
<div className="play-paper">
<div className="play-paper-body">
<div className="play-paper-title"><div><h3>{chapter.name}</h3></div></div>
<div className="play-paper-content">
{reads.length ?
<ul className="play-read-files">
{reads.map((o,k) => {
return <li key={k}><a href={o.reading_attachment} target="_blank">{o.reading_content}</a></li>
})}
</ul>
:
<p className="no-data">暂无阅读材料</p>
}
</div>
</div>
</div>
);
}
}
module.exports = ChapterRead;
/**
* 视频类章节
* props:
* username
* courseId
* chapter 当前章节
* ppts
* prevChapterId 上一节、下一节对应的章节ID
* nextChapterId
* curProgress
*
* handlePlayTime 播放时间变化
* handleOver 标记已完成
*/
import React, {Component} from 'react'
import { Link } from 'react-router';
import debounce from 'lodash/debounce'
import Video from '../../components/Video.jsx';
import Ppt from '../../components/Ppt.jsx';
let isSkipBegin = false;
if (process.env.BROWSER) {
isSkipBegin = /skip=1/.test(document.cookie); // 浏览器下根据cookie判断是否跳过片头
}
const PLAY_SPACE_WIDTH = 15;
const PLAY_SPACE_HEIGHT = 10;
const VIDEO_DEFAULT_WIDTH = 550;
const VIDEO_DEFAULT_HEIGHT = 360;
class ChapterVideo extends Component {
constructor (props) {
super(props)
this.state = {
pptIndex: 0,
pptBoxOnly: false, // 仅展示ppt框
pptBoxShow: false, // 展示ppt框
skipBegin: isSkipBegin, // 跳过片头
calculatedSize: false // 是否以计算过播放位置的尺寸
}
this.videoWidth = VIDEO_DEFAULT_WIDTH;
this.videoHeight = VIDEO_DEFAULT_HEIGHT;
this.lastTime = null
}
componentDidMount () {
let size = this.getCalculateSize();
if (size.video.w) {
this.videoWidth = size.video.w;
this.videoHeight= size.video.h;
}
// 延迟计算尺寸,因flash初始化需要时间
setTimeout(this.jdugeSize, 300);
// 窗口resize,重置大小
const $ = require('jquery');
$(window).on('resize.chaptervideo', debounce(this.jdugeSize.bind(this), 200));
}
componentWillReceiveProps (nextProps) {
// 章节变化时,重置位置,并在之后计算大小
if (nextProps.chapter.id !== this.props.chapter.id) {
this.videoWidth = VIDEO_DEFAULT_WIDTH;
this.videoHeight = VIDEO_DEFAULT_HEIGHT;
this.lastTime = null;
this._setState({calculatedSize: false})
setTimeout(this.jdugeSize, 300)
}
}
componentWillUnmount () {
const $ = require('jquery');
$(window).off('resize.chaptervideo');
}
// 根据时间设置ppt位置
setPptIndexByTime = time => {
const ppts = this.props.ppts || []
const len = ppts.length
let i = 0;
for(; i < len; i++) {
if (time < ppts[i].ppt_point) {
break;
}
}
if (this.state.pptIndex !== i - 1) {
this._setState({ pptIndex: i - 1 });
}
}
// 设置视频播放时间点
setVideoTime = time => {
if (this.refs.video) {
this.refs.video.setTimeTo(time);
}
};
// 播放时间变更
onVideoTimeChange = (e, data) => {
let time = parseFloat(data.time)
// 因视频播放完成后也会不断触发playing,因此比对上次时间
if (this.lastTime === time) { return }
this.lastTime = time
// 判断ppt位置
this.setPptIndexByTime(time)
this.props.handlePlayTime(time)
}
// toggle PPT播放框
togglePptBox = e => {
e.preventDefault();
let isShow = !this.state.pptBoxShow;
this._setState({
pptBoxShow: isShow,
pptBoxOnly: false
});
setTimeout(this.jdugeSize, 0)
};
// 仅显示ppt播放框
togglePptBoxOnly = e => {
e.preventDefault();
this._setState({ pptBoxOnly: !this.state.pptBoxOnly });
setTimeout(this.jdugeSize, 0)
};
// 跳过片头
toggleSkipBegin = e => {
e.preventDefault();
let skip = !this.state.skipBegin;
let d = new Date();
d.setMonth(d.getMonth() + 1);
document.cookie = 'skip=' + (+skip) + ';path=/;domain=.ezijing.com;expires=' + d.toGMTString();
if (skip && this.refs.video) {
this.refs.video.skipBegin();
}
this._setState({ skipBegin: skip });
};
// 标记完成状态
handleOver = e => { // 标记完成toggle
e.preventDefault();
let t = this.refs.video.getTime();
this.props.handleOver(t);
};
/**
* 判断并设置尺寸位置
*/
jdugeSize = () => {
let box = this.refs.box;
let size = this.getCalculateSize();
let uh = 0, uw = 0;
if (this.state.pptBoxOnly) { // 仅ppt
uw = size.ppt.w;
uh = size.ppt.h;
if (this.refs.ppt) {
this.refs.ppt.setSize(uw, uh);
}
} else if (this.state.pptBoxShow) { // 同时显示video、ppt
if (this.refs.ppt) {
this.refs.ppt.setSize(size.ppt.w, size.ppt.h);
}
if (this.refs.video) {
this.refs.video.setSize(size.video.w, size.video.h);
}
uw = size.ppt.w + size.video.w;
uh = size.video.h;
} else { // 只显示video
uw = size.video.w;
uh = size.video.h;
if (this.refs.video) {
this.refs.video.setSize(uw, uh);
}
}
this._setState({calculatedSize: true});
box.style.paddingLeft = '' + ((size.space.w - uw) / 2 + PLAY_SPACE_WIDTH) + 'px';
box.style.paddingTop = '' + ((size.space.h - uh) / 2 + PLAY_SPACE_HEIGHT) + 'px';
};
// 获取各个元素计算后的尺寸
getCalculateSize = () => {
let container = this.refs.container;
let w = container.offsetWidth - PLAY_SPACE_WIDTH * 2;
let h = container.offsetHeight - 53 - PLAY_SPACE_HEIGHT * 2; // 减去底部操作条高度
let videoRatio = 550 / 363;
let pptRatio = 336 / 236;
let r = {
space: {w: w, h: h}, // 可容纳play的容器总大小
video: {w: 0, h: 0}, // 视频大小
ppt: {w: 0, h: 0}, // ppt容器大小
};
if (this.state.pptBoxOnly) { // 仅ppt
r.ppt.w = w < h * pptRatio ? w : h * pptRatio;
r.ppt.h = h < w / pptRatio ? h : w / pptRatio;
} else if (this.state.pptBoxShow) { // 同时显示video、ppt
let halfW = w / 2;
let vw = halfW < h * videoRatio ? halfW : h * videoRatio;
let vh = h < halfW / videoRatio ? h : halfW / videoRatio;
let ph = vh;
let pw = ph * pptRatio;
r.video.w = vw; r.video.h = vh;
r.ppt.w = pw; r.ppt.h = ph;
} else { // 只显示video
r.video.w = w < h * videoRatio ? w : h * videoRatio;
r.video.h = h < w / videoRatio ? h : w / videoRatio;
}
return r;
}
_setState = obj => {
this.setState(Object.assign({}, this.state, obj || {}));
};
render () {
const {chapter, semesterId, courseId, username,
prevChapterId, nextChapterId,
curProgress,
ppts, lastTime} = this.props
const video = chapter.video;
const chapterId = chapter.id;
return (
<div className="play-content-video" ref="container">
<div className="play-center cl" ref="box">
{video ?
<div>
<div className={`play-video fl ${this.state.pptBoxOnly ? 'play-video-hide' : ''} ${this.state.calculatedSize ? '' : 'play-video-init-center'}`}>
<Video
ref="video"
videoId = {video.video_origionalID}
videoSrt = {video.video_subtitle || ''}
username = {username}
width = {this.videoWidth}
height = {this.videoHeight}
handlePlayTime = {this.onVideoTimeChange}
lastTime={lastTime}
/>
</div>
<div className={`play-jiangyi fl ${this.state.pptBoxShow ? '' : 'hide'}`}>
<Ppt
ref="ppt"
ppts = {ppts}
currentIndex = {this.state.pptIndex}
onVideoSyncTime = {this.setVideoTime}
onPptOnly = {this.togglePptBoxOnly}
onClose = {this.togglePptBox}
/>
</div>
</div>
:
<p>课程视频数据不存在</p>
}
</div>
<div className="play-footer">
<div className="fl">
<Link to={`/courses/${semesterId}/${courseId}/chapters/${prevChapterId || chapterId}`} className={`play-state play-state-prev ${prevChapterId?'': 'play-state-prev-disable'}`}>上一节</Link>
<Link to={`/courses/${semesterId}/${courseId}/chapters/${nextChapterId || chapterId}`} className={`play-state play-state-next ${nextChapterId?'': 'play-state-next-disable'}`}>下一节</Link>
</div>
<div className="fr">
<em className={`play-state play-state-check${curProgress == 100 ? '-active' : ''}`} onClick={this.handleOver} data-vid={video && video.id || ''} data-progress={curProgress} data-total={video && video.video_length || 1}>{curProgress == 100 ? '已学完' : '标记为已学完'}</em>
{ppts.length ?
<em className={`play-state play-state-ppt${this.state.pptBoxShow ? '-active' : ''}`} onClick={this.togglePptBox}>同步显示PPT</em>
: null
}
<em className={`play-state play-state-check${this.state.skipBegin ? '-active' : ''}`} onClick={this.toggleSkipBegin}>始终跳过片头</em>
</div>
</div>
</div>
);
}
}
module.exports = ChapterVideo
/**
* 作业类章节
*/
import React from 'react'
import Formsy from 'formsy-react';
import FormsyComponent from '../../components/formsy/Component.jsx'
import FormItem from '../../components/formsy/FormItem.jsx'
import CoursesAction from '../../actions/CoursesAction'
import OperateAction from '../../actions/OperateAction'
import { getRequestTypes } from '../../libs/utils';
class ChapterWork extends FormsyComponent {
constructor(props) {
super(props);
this.state = { error: '' };
}
componentDidMount () {
this.courseAction = new CoursesAction();
this.operateAction = new OperateAction();
this.startTime = (new Date()).getTime();
// 加载回答
this.props.dispatch(this.courseAction.loadChapterWork(this.props.semesterId, this.props.chapter.homework.id));
}
componentWillReceiveProps (nextProps) {
let submitType = getRequestTypes(CoursesAction.SUBMIT_CHAPTER_WORK);
switch (nextProps.action.type) {
case submitType.success:
this.enableSubmitButton();
// 提交成功,重新获取答案
nextProps.dispatch(this.courseAction.loadChapterWork(this.props.semesterId, nextProps.chapter.homework.id));
nextProps.handleSubmited(nextProps.chapter.id);
break;
case submitType.failure:
this.enableSubmitButton();
nextProps.dispatch(this.operateAction.showErrorMessage(nextProps.action.error.message || '提交章节作业失败'));
break;
}
}
/**
* 提交登录
*/
onSubmit = model => {
this.loadingSubmitButton();
const {courseId, chapter, semesterId} = this.props;
const questions = chapter.homework && chapter.homework.questions || [];
let data = {
course_id: courseId,
chapter_id:chapter.id,
work_id: chapter.homework.id,
semester_id: semesterId,
work_contents: '',
duration: Math.ceil(((new Date()).getTime() - this.startTime) / 1000),
}
// 组织返回答案结构
let answers = questions.map(q => {
return {
question_id: q.id,
descreption: model['desc_' + q.id],
file_url: model['file_' + q.id]
};
});
data.work_contents = JSON.stringify(answers);
this.props.dispatch(this.courseAction.submitChapterWork(data));
}
// 表单变更时,取消掉全局错误消息
onFormChange = () => {
this._setState({ error: '' });
}
handleUploadError = msg => {
this.props.dispatch(this.operateAction.showErrorMessage(msg || '上传文件失败'));
}
render () {
const {chapter, chapter_work} = this.props;
const questions = chapter.homework && chapter.homework.questions || [];
let work = null;
let answers = {}
if (chapter_work.data && chapter_work.data.work_contents) {
work = chapter_work.data;
try {
let answerObject = JSON.parse(work.work_contents);
answerObject.forEach(a => {
answers[a.question_id] = a;
})
} catch(e) {console.log('parse work_contents to json failed.')}
}
return (
<div className="play-paper">
<div className="play-paper-body">
<div className="play-paper-title"><div><h3>{chapter.name}</h3></div></div>
<div className="play-paper-content play-chapter-work">
<Formsy.Form
onValid={this.enableSubmitButton}
onInvalid={this.disableSubmitButton}
onValidSubmit={this.onSubmit}
onChange={this.onFormChange}
>
{questions.length ?
<ul>
{questions.map((q, qi) => {
let answer = answers[q.id] || {};
return (
<li key={qi}>
<div className="work-number">{qi + 1}.</div>
<div className="work-title">
<div className="edit_html" dangerouslySetInnerHTML={{__html: q.question_content}}></div>
</div>
<FormItem
name={'desc_' + q.id}
itemType={FormItem.TEXTRICH}
value={answer.descreption || ''}
required
/>
<FormItem
name={'file_' + q.id}
fileVal="file"
value={answer.file_url || ''}
btnClassName="upbtn"
label="请上传对应的文件附件:"
itemType={FormItem.FILE}
text="上传附件"
server="/api/tenant/util/upload-file"
fileSingleSizeLimit={1024*1024*10}
accept={[{extensions: 'docx'}]}
onFileError={this.handleUploadError}
/>
<p className="help help-file">只支持docx格式的文件,文件小于10M</p>
{answer.file_url && <a style={{display: 'block', marginBottom: '20px', color: 'blue'}} href={answer.file_url} >下载附件</a> }
</li>
);
})}
</ul>
:
<p className="no-data">暂无数据</p>
}
<p className="text-danger">{this.state.error}</p>
<div className="area-btns">
<button type="submit" disabled={!this.canSubmit() || (work && work.checker_id)} className="btn btn-primary" >{this.isSubmitLoading() ? '保存中...' : (work && work.checker_id) ? '已批改' : '提交'}</button>
<span className="help-info">&emsp;&emsp;在获老师批改之前,可以多次提交,将以最后一次提交为准</span>
{work ?
!work.checker_id ?
<p className="help">已于 {work.created_time} 提交,等待批改中</p>
:
<div className="play-paper-check">
<h4>已获批改 <small>批改于{work.check_date}</small></h4>
<div className="play-paper-check-item"><b>评分:</b>{work.score}</div>
<div className="play-paper-check-item">
<b>评语:</b>
<div className="edit_html" dangerouslySetInnerHTML={{__html:work.check_comments}}></div>
</div>
</div>
: null
}
</div>
</Formsy.Form>
</div>
</div>
</div>
);
}
}
module.exports = ChapterWork;
/**
* 课程资料
*
* courseId
* files
*/
import React, { Component } from 'react'
class CourseInfo extends Component {
render () {
const files = this.props.files || [];
return (
<div className="play-paper">
<div className="play-paper-body">
<div className="play-paper-title"><div><h3>课程资料</h3></div></div>
<div className="play-paper-content">
{files.length ?
<ul className="play-read-files">
{files.map((f, i) => {
return <li key={i}><a href={f.file_url} target="_blank">{f.file_name}</a></li>
})}
</ul>
:
<p className="no-data">暂无课程资料</p>
}
</div>
</div>
</div>
);
}
}
module.exports = CourseInfo;
/**
* 课程大作业
* porps:
* course
* course_work
* action
* dispatch
*/
import React, { Component } from 'react'
import { Link } from 'react-router';
import Formsy from 'formsy-react';
import FormsyComponent from '../../components/formsy/Component.jsx'
import FormItem from '../../components/formsy/FormItem.jsx'
import CoursesAction from '../../actions/CoursesAction'
import OperateAction from '../../actions/OperateAction'
import { getRequestTypes } from '../../libs/utils';
class CourseWork extends FormsyComponent {
constructor(props) {
super(props);
this.state = { error: '' };
}
componentDidMount () {
this.courseAction = new CoursesAction();
this.operateAction = new OperateAction();
this.courseId = this.props.course.course_id;
this.semesterId = this.props.semesterId;
// 加载回答
this.props.dispatch(this.courseAction.loadCourseWork(this.semesterId, this.courseId));
}
componentWillReceiveProps (nextProps) {
let submitType = getRequestTypes(CoursesAction.SUBMIT_COURSE_WORK);
switch (nextProps.action.type) {
case submitType.success:
this.enableSubmitButton();
if (nextProps.action.response.data.status == 0) {
nextProps.dispatch(this.operateAction.showErrorMessage('账号出错,请联系管理员'));
console.log(JSON.stringify(nextProps.action.response.data.error))
break;
}
// 提交成功,重新获取答案
nextProps.dispatch(this.courseAction.loadCourseWork(this.semesterId, this.courseId));
nextProps.handleSubmited('work');
break;
case submitType.failure:
this.enableSubmitButton();
nextProps.dispatch(this.operateAction.showErrorMessage(nextProps.action.error.message || '提交课程大作业失败'));
break;
}
}
/**
* 提交
*/
onSubmit = model => {
this.loadingSubmitButton();
model.course_id = this.props.course.course_id;
model.semester_id = this.props.semesterId;
this.props.dispatch(this.courseAction.submitCourseWork(model));
}
// 表单变更时,取消掉全局错误消息
onFormChange = () => {
this._setState({ error: '' });
}
handleUploadError = msg => {
this.props.dispatch(this.operateAction.showErrorMessage(msg || '上传文件失败'));
}
render () {
const {course, course_work} = this.props;
const info = course_work.data || {};
return (
<div className="play-paper">
<div className="play-paper-body">
<div className="play-paper-title"><div><h3>课程大作业</h3></div></div>
<div className="play-paper-content">
<div className="play-paper-step">&#9312; 阅读大作业要求</div>
<div className="edit_html" dangerouslySetInnerHTML={{__html: course.curriculum && course.curriculum.curriculum_essay || ''}}></div>
<p>截止日期:{course.essay_date || ''}</p>
<div className="play-paper-step">&#9313; 填写作业主题、摘要,上传附件(点击“提交”保存)</div>
<Formsy.Form
onValid={this.enableSubmitButton}
onInvalid={this.disableSubmitButton}
onValidSubmit={this.onSubmit}
onChange={this.onFormChange}
>
<input type="hidden" name="id" value={info.id || ''} />
<div>
<FormItem
name="essay_name"
value={info.essay_name || ''}
placeholder="主题"
required
/>
<FormItem
name="essay_description"
value={info.essay_description || ''}
label="摘要"
itemType={FormItem.TEXTRICH}
required
/>
<FormItem
name="url"
fileVal="file"
value ={info.file_url || ''}
btnClassName="upbtn"
label="大作业附件:"
itemType={FormItem.FILE}
text="选择附件"
server="/api/tenant/util/upload-file"
fileSingleSizeLimit={1024*1024*10}
accept={[{extensions: 'docx'}]}
onFileError={this.handleUploadError}
required
/>
<p className="help help-file">只支持docx格式的文件,文件小于10M</p>
{info.file_url && <a style={{display: 'block', marginBottom: '20px', color: 'blue'}} href={info.file_url} >下载附件</a> }
</div>
<p className="text-danger">{this.state.error}</p>
<div className="area-btns">
<div className="play-paper-step">&#9314; 截止日期前提交</div>
<button type="submit" disabled={!this.canSubmit() || info.checker_id} className="btn btn-primary" >{this.isSubmitLoading() ? '保存中...' : info.checker_id ? '已批改' : '提交'}</button>
<span className="help-info">&emsp;&emsp;在获老师批改之前,可以多次提交,将以最后一次提交为准</span>
{info.id ?
!info.checker_id ?
<p className="help">已于 {info.created_time} 提交,等待批改中</p>
:
<div className="play-paper-check">
<h4>已获批改 <small>批改于{info.check_date}</small></h4>
<div className="play-paper-check-item"><b>评分:</b>{info.score}</div>
<div className="play-paper-check-item">
<b>评语:</b>
<div className="edit_html" dangerouslySetInnerHTML={{__html:info.check_comments}}></div>
</div>
</div>
: null
}
</div>
</Formsy.Form>
</div>
</div>
</div>
);
}
}
module.exports = CourseWork
import React, { Component } from 'react';
import { connect } from 'react-redux'
import { Link } from 'react-router';
import { chapterType } from '../../libs/const';
import { getIdt, getRequestTypes, paramify } from '../../libs/utils';
import CoursesAction from '../../actions/CoursesAction';
import OperateAction from '../../actions/OperateAction';
import Loading from '../../components/Loading.jsx';
// 页面组成部分
import SideList from './SideList.jsx'
import SidePpt from './SidePpt.jsx'
import ChapterVideo from './ChapterVideo.jsx'
import ChapterRead from './ChapterRead.jsx'
import ChapterExam from './ChapterExam.jsx'
import ChapterWork from './ChapterWork.jsx'
import CourseWork from './CourseWork.jsx'
import CourseInfo from './CourseInfo.jsx'
import AudioPanel from './AudioPanel.jsx'
import VideoPhonePanel from './VideoPhonePanel.jsx'
if (process.env.BROWSER) {
require('css/course.css');
require('css/play.css');
}
const SIDEBAR_CHAPTER = 'sidebar_chapter';
const SIDEBAR_PPT = 'sidebar_ppt';
const IS_PHONE = /Android|iPhone|iPad/i.test(navigator && navigator.userAgent || '');
class Play extends Component {
// 初始加载数据
// static fetchData({dispatch, params={}, location={}, apiClient}) {
// const courseAction = new CoursesAction({ apiClient });
// let arr = [
// dispatch( courseAction.loadCourseDetail(params.courseId) ),
// dispatch( courseAction.loadChapterProgress(params.courseId) )
// ];
// return Promise.all(arr);
// }
constructor(props) {
super(props);
this.state = {
sidebar: IS_PHONE ? false : SIDEBAR_CHAPTER, // 控制侧边栏显示与否,及显示chapter还是ppt
pptBoxOnly: false, // 仅展示ppt框
pptBoxShow: false, // 展示ppt框
curProgress: 0, // 当前播放章节的进度,仅为更新this.pageProgressMap时,触发页面刷新
};
this.pageProgressMap = {}; // 播放期间记录本次页面进度
// 需上传数据
this.pushData = {
timer: null, // 定期发送timer
currentChapterId: null,
currentVideoId: null, // 当前播放视频id
total: 0, // 播放累计总时间
max: 0, // 最大播放时间点
arr: [], // 播放数组
last: 0, // 上次播放时间点,用来与当前第一个点比较是否重复
};
this.courseAction = new CoursesAction();
this.operateAction = new OperateAction();
this.getChapterMap = this.getChapterMap.bind(this);
this.getCurrentChapter = this.getCurrentChapter.bind(this);
this.getProgress = this.getProgress.bind(this);
}
componentDidMount() {
this._chapterMap = null;
const {params, course} = this.props;
// 如果没有指定章节ID,并且又取得了章节数据,跳转到第一节
if (!params.chapterId &&
(params.courseId === (course._req && course._req.courseId) && course.data)) {
let chapters = course.data && course.data.chapters || [];
let children = chapters[0] && chapters[0].children || [];
let firstId = children[0] && children[0].id;
this.props.history.push(`/courses/${params.semesterId}/${params.courseId}/chapters/${firstId}`);
return;
}
this.loadNeededData(this.props);
// 数据采集
this.pushData.timer = setInterval(this.sendPlayerProgress.bind(this), 5000);
}
componentDidUpdate() {
//if (this.refs.chapterVideo) {
// this.refs.chapterVideo.jdugeSize()
//}
}
componentWillReceiveProps(nextProps) {
this.loadNeededData(nextProps);
// 如果章节变化,需要再次获取最新的私密信息,以便保证每次播放都检查权限
if (this.props.location.pathname !== nextProps.location.pathname) {
// this.sendPlayerProgress();
}
if (this.props.video_progress.isFetching !== nextProps.video_progress.isFetching &&
nextProps.video_progress.data) {
let cur = this.getCurrentChapter(nextProps.params.chapterId);
if (cur) {
let video_progress = nextProps.video_progress.data;
Object.assign(this.pushData, {
currentVideoId: cur.video.id,
total: (video_progress.pt || 0) - 0,
max: (video_progress.mpt || 0) - 0,
last: (video_progress.cpt || 0) - 0,
})
}
}
}
componentWillUnmount() {
let operateAction = new OperateAction();
this.props.dispatch(operateAction.cleanData(CoursesAction.LOAD_COURSE_DETAIL));
this.props.dispatch(operateAction.cleanData(CoursesAction.LOAD_CHAPTER_PROGRESS));
clearInterval(this.pushData.timer);
this.sendPlayerProgress();
}
// 获取章节map
getChapterMap() {
if (!this._chapterMap) {
let ret = null;
let info = this.props.course.data || {};
let list = info.chapters || [];
list.forEach(chapter => {
(chapter.children || []).forEach(item => {
ret = ret || {};
ret[item.id] = item;
});
});
this._chapterMap = ret;
}
return this._chapterMap;
}
// 获取当前章节
getCurrentChapter(chapterId) {
let cur = null;
let id = chapterId || this.props.params.chapterId;
let map = this.getChapterMap();
if (map && id) {
cur = map[id];
}
return cur;
}
/**
* 加载需要的数据:已有数据与props中参数不一致时,加载对应的数据
*/
loadNeededData = props => {
const {params, course, ppts, user} = props;
const courseAction = new CoursesAction();
// 课程详情
if (params.courseId != (course._req && course._req.courseId)) {
props.dispatch( courseAction.loadCourseDetail(params.semesterId, params.courseId) );
props.dispatch( courseAction.loadChapterProgress(params.semesterId, params.courseId) );
}
// 课程章节
if (params.chapterId !== this.pushData.currentChapterId && course.data) {
// 只有章节数据存在时,才加载当前视频进度
let curChapter = this.getCurrentChapter(params.chapterId);
if (curChapter && curChapter.video) {
this.pushData.currentChapterId = params.chapterId;
props.dispatch( courseAction.loadPlayerProgress({
device_id: getIdt(),
video_id: curChapter.video.id,
semester_id: params.semesterId
//_idt: getIdt(),
//_uid: user.data && user.data.uid || null,
//_cid: params.courseId,
//_vid: curChapter.video.id,
}) );
// 存在章节时,加载ppt
if (curChapter.video &&
curChapter.video.id &&
curChapter.video.id != (ppts._req && ppts._req.videoId)) {
props.dispatch( courseAction.loadVideoPpts(curChapter.video.id) );
}
/* 读取对应视频 - 手机端 m3u8源 */
props.dispatch( courseAction.loadVideoPhone({ vid: curChapter.video.id }) )
}
}
};
// 发送到数据采集
sendPlayerProgress = () => {
if (this.pushData.arr.length && this.pushData.currentVideoId) {
const courseAction = new CoursesAction();
const len = this.pushData.arr.length;
this.pushData.total += len - (this.pushData.last === this.pushData.arr[0] ? 1 : 0);
this.pushData.last = this.pushData.arr[len - 1];
this.pushData.max = Math.max.apply(null, [this.pushData.max].concat(this.pushData.arr))
// 更新pageProgressMap
let chapter = this.getCurrentChapter();
if (chapter) {
let v = Math.ceil(this.pushData.max / chapter.video.video_length * 100);
this.pageProgressMap[chapter.id] = v;
if (this.state.curProgress != v) {
this.setState({curProgress: v});
}
}
let params = {
d: getIdt(),
i: getIdt(),
c: this.props.params.courseId,
s: this.props.params.semesterId,
v: this.pushData.currentVideoId,
//_ts: Math.round(new Date().getTime() / 1000),
_p: this.pushData.total,
_m: this.pushData.max > chapter && chapter.video.video_length ? chapter.video.video_length : this.pushData.max,
_c: this.pushData.last > chapter && chapter.video.video_length ? chapter.video.video_length : this.pushData.last,
};
this.props.dispatch( courseAction.playerProgress(params) );
// 再次推送另一个系统
delete params._dt_0;
const imgId = '_tj_pt_gif_';
let img = document.getElementById(imgId);
if (!img) {
img = new Image();
img.id = imgId;
img.style.display = 'none';
document.body.appendChild(img);
}
img.src = 'https://tj.ezijing.com/pt.gif?' + paramify(params);
this.pushData.arr = [];
}
};
// 阅读材料统计进度
sendReaded = () => {
let chapter = this.getCurrentChapter();
if (chapter && chapter.reading) {
let params = {
d: getIdt(),
i: getIdt(),
c: this.props.params.courseId,
s: this.props.params.semesterId,
r: chapter.reading.id
};
const courseAction = new CoursesAction();
this.props.dispatch( courseAction.chapterReaded(params) );
this.pageProgressMap[chapter.id] = 100;
}
};
chapterSubmited = id => {
this.pageProgressMap[id] = 100;
};
// 点击返回详情时,重新加载课程的private信息,以便更新latest_play字段数据
onClickToDetail = e => {
const courseAction = new CoursesAction();
this.props.dispatch( courseAction.loadCoursePrivate(this.props.params.courseId) );
};
changeSidebar = (v) => {
this.setState({sidebar: v});
if (v != this.state.sidebar && (v === false || this.state.sidebar === false)) {
if (this.refs.chapterVideo) {
setTimeout(() => {
this.refs.chapterVideo.jdugeSize()
}, 0)
}
}
}
// 侧边点击ppt,根据ppt播放时间点同步video
// @param index 被点击的ppt序列号
handleClickSidePpt = index => {
const ppts = this.props.ppts.data || []
if (ppts.length > index && this.refs.chapterVideo) {
const ppt = ppts[index]
this.refs.chapterVideo.setVideoTime(ppt.ppt_point)
}
};
handlePlayTime = time => { // 视频播放时间变更调用此方法
// 侧边ppt对应位置
if (this.refs.sidePpt) {
this.refs.sidePpt.setIndexByPoint(time)
}
// 存储到要上传到大数据的数据集中
time = Math.round(time);
if (this.pushData.arr.indexOf(time) < 0) {
this.pushData.arr.push(time);
}
};
// 标记完成
// @param time 当前播放到的时间点
handleOver = time => {
let cur = this.getCurrentChapter();
let progress = this.getProgress(cur.id);
if (cur) {
this._signOver = progress != 100;
this.props.dispatch( this.courseAction[progress == 100 ? 'playerUnover': 'playerOver']({
video_id: cur.video.id,
chapter_id: cur.id,
current_progress: Math.round(time/cur.video.video_length * 100)
}) );
}
};
// 获取进度,综合接口返回上次播放进度和本页播放进度记录
getProgress(id) {
const progressData = this.props.chapter_progress.data || {};
if (id === 'work') { // 大作业
return Math.max(
progressData.essay && progressData.essay.progress || 0,
this.pageProgressMap.essay || 0);
}
let p = 0;
let chapterProgressMap = progressData.chapters || {};
let pobj = chapterProgressMap[id] || {};
if (id === this.props.params.chapterId) {
p = (this._signOver || (pobj.sign && this._signOver === undefined)) ? 100 : pobj.progress || 0;
} else {
p = pobj.sign ? 100 : pobj.progress || 0;
}
p = Math.max(this.pageProgressMap[id] || 0, p);
return p;
}
render() {
let {course, ppts, params, user, video_curm3u8, video_progress} = this.props;
if (course.isFetching) {
return <div className="play"><Loading /></div>
} else if (!(course.data && course.data.course_id)) {
return <div className="play"><p className="no-data">课程不存在</p></div>
}
let cpt = (video_progress.data || {}).cpt || 0;
course = course.data || {};
let chapters = course.chapters || [];
ppts = ppts.data || [];
video_curm3u8 = video_curm3u8.data || {};
// 构造chapterMap,方便查找;顺便在同一个遍历中创建层级结构
let chapterMap = this.getChapterMap() || {}; // map
// 当前章节
let chapter = params.chapterId && chapterMap[params.chapterId] || {};
// 当前章节进度
let curProgress = this.getProgress(params.chapterId);
this.pushData.currentVideoId = chapter.video && chapter.video.id || null;
const isVideo = chapter.type === chapterType.VIDEO;
// 前一节、后一节
let leafIds = [];
chapters.forEach(c => {
(c.children || []).forEach(o => {
leafIds.push(o.id);
});
});
let prevChapterId = null;
let nextChapterId = null;
let curIndex = leafIds.indexOf(params.chapterId);
prevChapterId = curIndex > 0 ? leafIds[curIndex - 1] : null;
nextChapterId = curIndex < leafIds.length - 1 ? leafIds[curIndex + 1] : null;
if (isVideo && !video_progress.data) {
return <div className="play"><Loading /></div>
}
/* 手机端 没用 device接口传的 lastTime值 */
if (IS_PHONE && !video_curm3u8.video && !video_curm3u8.audio) {
return <div><Loading/></div>
}
/* 手机播放页面单独展示 - 音频播放 */
if (isVideo && IS_PHONE && video_curm3u8.audio) {
if (!this.audioChapterData) {
let map = {}; // {chapterId: chapterObj}
let tree = []; // [[cid, [cid, cid]]]
let leaves = [];
chapters.forEach(c => {
map[c.id] = c;
tree.push([c.id, []]);
c.children.forEach(o => {
map[o.id] = o;
leaves.push(o.id)
tree[tree.length - 1][1].push(o.id);
})
})
this.audioChapterData = {map, tree, leaves,
progressMap: this.props.chapter_progress.data || {}};
}
return <div className="play">
<AudioPanel
index={curIndex}
chapterData={this.audioChapterData}
course={course}
ppts={ppts}
skipBegin={false}
handleOver={this.handleOver}
handleAudioTime = {this.handlePlayTime}
video_curm3u8={video_curm3u8}
/>
</div>
} else
/* 有视频时,先播放视频, 并且存在手机端 m3u8时 */
if (isVideo && IS_PHONE && !chapter.video.video_voice) {
if (!this.audioChapterData) {
let map = {}; // {chapterId: chapterObj}
let tree = []; // [[cid, [cid, cid]]]
let leaves = [];
chapters.forEach(c => {
map[c.id] = c;
tree.push([c.id, []]);
c.children.forEach(o => {
map[o.id] = o;
leaves.push(o.id)
tree[tree.length - 1][1].push(o.id);
})
})
this.audioChapterData = {map, tree, leaves,
progressMap: this.props.chapter_progress.data || {}
};
}
return (<div className="play">
<VideoPhonePanel
index={curIndex}
chapterData={this.audioChapterData}
course={course}
ppts={ppts}
video_curm3u8={video_curm3u8}
skipBegin={false}
handleOver={this.handleOver}
handleVideoTime = {this.handlePlayTime}
/>
</div>)
}
return (
<div className={`play ${this.state.sidebar ? '' : 'sidebar-hide'}`}>
<div className="left-content">
<div className="play-top cl" style={this.state.sidebar ? {} : {marginRight: 0}}>
<Link className="play-back" to={`/courses/${params.semesterId}/${params.courseId}/cont`} onClick={this.onClickToDetail}>&lt;</Link>
<p>{course.course_name}</p>
<p>{chapter.chapter_name}</p>
</div>
<div className="play-content">
{params.chapterId === 'work' ?
<CourseWork
course={course}
semesterId={course.semester_id}
course_work={this.props.course_work}
dispatch={this.props.dispatch}
action={this.props.action}
handleSubmited={this.chapterSubmited}
/>
: params.chapterId === 'info' ?
<CourseInfo
courseId={params.courseId}
files={course.files}
/>
: chapter.type === chapterType.VIDEO ?
<ChapterVideo
ref="chapterVideo"
courseId={params.courseId}
semesterId={course.semester_id}
ppts={ppts}
chapter={chapter}
username={user.data && user.data.username || ''}
prevChapterId={prevChapterId}
nextChapterId={nextChapterId}
curProgress={curProgress}
handlePlayTime={this.handlePlayTime}
handleOver={this.handleOver}
lastTime={cpt}
/>
: chapter.type === chapterType.WORKOREXAM ?
(chapter.homework && chapter.homework.work_type === chapterType.WORK_HOME ?
<ChapterWork
chapter={chapter}
chapter_work={this.props.chapter_work}
courseId={params.courseId}
semesterId={course.semester_id}
action={this.props.action}
dispatch={this.props.dispatch}
handleSubmited={this.chapterSubmited}
/>
:
<ChapterExam
chapter={chapter}
chapter_work={this.props.chapter_work}
courseId={params.courseId}
semesterId={course.semester_id}
action={this.props.action}
dispatch={this.props.dispatch}
handleSubmited={this.chapterSubmited}
/>
)
:
<ChapterRead
chapter={chapter}
handleReaded={this.sendReaded}
/>
}
</div>
</div>
<div className="right-content" style={{ right: this.state.sidebar ? 0 : -388 }}>
<p className="right-arrow" onClick={this.handleHideSidebar}><span>&gt;</span></p>
<div className="control-panel">
<ul className="nav-tabs play-nav cl">
<li className={`videoChapter ${this.state.sidebar === SIDEBAR_CHAPTER ? 'current' : ''}`}><a href="#sidebar_chapter" onClick={this.handleShowSidebarChapter}>章节</a></li>
{ppts.length ?
<li className={`videoJy ${this.state.sidebar === SIDEBAR_PPT ? 'current' : ''}`}><a href="#sidebar_ppt" onClick={this.handleShowSidebarPpt}>讲义</a></li>
: null
}
</ul>
<div className="tab-content">
{this.state.sidebar === SIDEBAR_CHAPTER ?
<SideList
chapters={chapters}
getProgressByChapterId={this.getProgress}
courseId={params.courseId}
semesterId={params.semesterId}
chapterId={params.chapterId}
progress={curProgress}
handleClickItem={IS_PHONE ? this.handleHideSidebar1 : this.reloadPages}
/>
:
<SidePpt
ref="sidePpt"
ppts={ppts}
handleClickPpt={this.handleClickSidePpt}
/>
}
</div>
</div>
</div>
{this.state.sidebar ? null :
<div className="switch">
<a href="#sidebar_chapter" className="switch-chapter" onClick={this.handleShowSidebarChapter}></a>
{isVideo && ppts.length ?
<a href="#sidebar_ppt" className="switch-handout" onClick={this.handleShowSidebarPpt}></a>
: null
}
</div>
}
</div>
);
}
}
module.exports = connect( state => ({
action: state.action,
user: state.user,
course : state.course,
course_work: state.course_work,
chapter_work: state.chapter_work,
ppts: state.ppts,
chapter_progress: state.chapter_progress,
video_progress: state.video_progress, // 接口device
video_curm3u8: state.video_m3u8
}) )(Play);
/**
* 侧边章节导航列表
* props:
* chapters []
* getProgressByChapterId func
* courseId
* chapterId string
* progress number
*/
import React, { Component } from 'react'
import { Link } from 'react-router';
import ProgressCircle from '../../components/ProgressCircle.jsx'
import {chapterType} from '../../libs/const'
const NAV_COURSE = {
name: '大作业及资料',
children: [{
id: 'work',
type: chapterType.WORKOREXAM,
name: '课程大作业',
homework: {work_type: chapterType.WORK_HOME}
}, {
id: 'info',
type: chapterType.READ,
name: '课程资料'
}]
};
class SideList extends Component {
reloadPages () {
window.location.reload()
}
render () {
const {chapters, getProgressByChapterId, semesterId, courseId, chapterId, progress} = this.props;
let list = (chapters || []).concat([NAV_COURSE]);
return (
<div className="tab-pane current">
<ul className="under-control chapter-list current">
{list.map((item, index) => {
return (
<li className="chapter-item" key={index}>
<span className="cpt">{item.name}</span>
<div className="knob-list-wrap">
<ul className="knob-list">
{(item.children || []).map((o, i) => {
let isCur = chapterId === o.id;
let prog = isCur ? progress : getProgressByChapterId(o.id);
let part = Math.max(Math.round(prog / 25), 0);
let type;
switch (o.type) {
case chapterType.VIDEO:
type = 'video'; break;
case chapterType.READ:
type = 'read'; break;
default:
type = 'exam';
if (o.homework && o.homework.work_type === chapterType.WORK_HOME) {
type = 'work';
}
}
return (
<li className={`knob-item ${isCur ? 'current' : ''} ${part ? ['one', 'two', 'three', 'four'][part - 1] + '-four' : ''}`} key={i}>
<ProgressCircle percent={prog} className="play-chapter-progress"/>
<i className="icon icon-pro" title={`学习进度 ${prog}%`}></i>
{(type == 'work') ?
<Link to={`/courses/${semesterId}/${courseId}/chapters/${o.id}`} className="knob-name" onClick={this.reloadPages}>{o.name}</Link>
:
<Link to={`/courses/${semesterId}/${courseId}/chapters/${o.id}`} className="knob-name" onClick={this.props.handleClickItem}>{o.name}</Link>
}
<i className={`icon-play-chapter icon-play-${type}`}></i>
</li>
);
})}
</ul>
</div>
</li>
);
})}
</ul>
</div>
);
}
}
module.exports = SideList;
/**
* 侧边显示讲义ppt
* props:
* ppts: []
* handleClickPpt: func
*/
import React, { Component } from 'react'
class SidePpt extends Component {
constructor (props) {
super(props)
this.state = { index: 0 }
}
// 根据播放时间同步展示ppt
// @param time 播放时间
setIndexByPoint = time => {
const ppts = this.props.ppts || []
const len = ppts.length
let i = 0;
for(; i < len; i++) {
if (time < ppts[i].ppt_point) {
break;
}
}
if (this.state.index !== i - 1) {
this.setState({ index: i - 1 });
}
}
//handleSyncVideoTime = e => { // 点击ppt跳转对应的播放时间
// 点击某个ppt
onClickPpt = e => {
let toIndex = e.currentTarget.getAttribute('data-index') - 0
if (this.state.index === toIndex) { return }
this.setState({ index: toIndex })
this.props.handleClickPpt(toIndex)
}
render () {
const {ppts} = this.props
return (
<div className="tab-pane current">
<div className="jiangyi-list">
<div className="jy-list">
{(ppts || []).length ?
ppts.map((item, index) => {
return (
<div key={index} onClick={this.onClickPpt} data-index={index} className={index === this.state.index ? 'current' : ''}>
<img src={item.ppt_url} alt=""/>
</div>
);
})
:
<div className="no-data">暂无讲义</div>
}
</div>
</div>
</div>
);
}
}
module.exports = SidePpt;
import React from 'react'
import { ReactDom } from 'react-dom';
/**
* 视频基础组件类
* props:
* src: 视频源地址
* lastTime: xx, 上次播放时间
* skipBegin: 忽略片头,true/false
* onmeta: 加载资源,可获取如视频长度等信息 父级方法实现
* onloaded: 加载完视频,可播放方法 父级方法实现
* onloading: 加载中视频 父级方法实现
* onplay: 播放视频 父级方法实现
* onpause: 暂停播放 父级方法实现
* ontime:
* 外部可用:
* duration
* seek
* play
* pause
*
*
* 问题:
* 1. 华为 P7;小米 自带浏览器,不支持 倍速
* 2. 视频 跳跃时,可能刷新视频,导致currentTime重新开始
* 3. 外观不够美化,进度条 不能拖动
* 4. 图片放大 这个插件也需要集成化
* 5.
*/
class VideoPhone extends React.Component {
static SKIP_SECS = 7; // 片头7s
/**
* 构造函数
*/
constructor(props) {
/* 继承属性 */
super(props);
/* 缓存视频 视频列表 */
this.playList = [];
/* 视频列表中,播放第几个 */
this.videoIndex = 0;
// console.log('diaoyong', this.playList);
/* 当前所在播放时间 */
this.time = 0;
this.stepTimer = null; //time计时器
// this.lastSeekTime = 0; // 记录上一次 seek时,存储的时间值
}
/**
* 组件渲染前
*/
componentWillMount() {
}
/**
* 组件渲染后
*/
componentDidMount() {
this.refs.video_obj.setAttribute('x5-video-arientation', 'portrait');
this.refs.video_obj.setAttribute('x-webkit-airplay', 'true');
this.refs.video_obj.setAttribute('webkit-playsinline', '');
this.refs.video_obj.setAttribute('playsinline', 'true');
this.refs.video_obj.setAttribute('x5-video-player-typ', 'h5');
/* 跟audio组件唯一不同点是 video对象是组件渲染完成后添加 加载Video对象并加载视频 */
this.getVideoObject()
}
componentWillReceiveProps(nextProps) {
// console.log(12222);
// if (nextProps.src !== this.props.src) {
// // 暂停当前sound
// let sound = this.getVideoObject();
// if (sound && sound.playing()) {
// sound.pause();
// }
// // 检查是否已存在
// let existIndex = findIndex(this.playList, {src: nextProps.src});
// if (existIndex < 0) {
// existIndex = this.playList.length;
// this.playList.push({src: nextProps.src, howl: null, time: 0});
// }
// this.audioPlay(existIndex);
// }
}
componentWillUnmount() {
this.getVideoObject().pause();
}
shouldComponentUpdate(nextProps, nextState) {
return false;
}
// 第一播放,跳过片头,同时考虑lastTime
skipBeginAndPlay() {
let sound = this.getVideoObject();
if (sound && sound.state() === 'loaded') {
let lastTime = this.props.lastTime || 0;
if (this.props.skipBegin) { lastTime = Math.max(lastTime, Video.SKIP_SECS); }
if (lastTime) {
if (lastTime > sound.duration() - 10) { lastTime = 0; } // 最后10s不执行跳转lastTime
let cur = sound.seek();
if (cur < lastTime) {
sound.seek(lastTime);
}
}
sound.play();
}
}
// 无参数时,播放当前index视频到对应位置。
// index 要播放的序号
audioPlay (index) {
this.videoIndex = index;
this.time = 0;
this.skipBeginAndPlay();
}
step() {
clearTimeout(this.stepTimer);
let sound = this.getVideoObject();
if (!sound) {return;}
let seek = Math.round(sound.seek() || 0);
let playing = sound.playing();
if (seek !== this.time) {
this.time = seek;
this.props.ontime(seek);
}
if (playing) {
this.stepTimer = setTimeout(this.step, 300);
}
}
stepPause() {
clearTimeout(this.stepTimer);
this.props.onpause();
}
/**
* 搜寻点击进度条某个位置 跳播方式
* @param {number} s 传入当前需要改变的时间
*/
seek(s) {
/**
* 设置时,需要判断是否在seekable的范围内,不在,等待缓冲
* 如果在了,就可以设置 -- 华为手机 就是不行,设置 可能会断,重新加载视频
*/
let _video = this.getVideoObject(), i = 0;
// if (!/android/gi.test(navigator.userAgent)) {
if ('fastSeek' in _video) {
_video.fastSeek(s); // 改变audio.currentTime的值
return ;
}
this.pause();
_video.currentTime = s;
this.play();
// } else {
// for (i=0; i<_video.seekable.length; i++) {
// if (_video.seekable.start(i) <= s && s >= _video.seekable.end(i)) {
// break;
// }
// }
// if (i < _video.seekable.length) {
// _video.currentTime = s;
// } else {
// _video.buffered.end(_video.buffered.length - 1);
// }
// }
}
/**
* 返回视频长度
*/
getDuration() {
return this.getVideoObject().duration || 0;
}
/**
* 开始播放
*/
play() {
this.getVideoObject().play();
}
/**
* 播放暂停
*/
pause() {
this.getVideoObject().pause();
}
/**
* 播放速率改变
* @param {float} n 速率值,每次加0.5
*/
rate(n) {
this.getVideoObject().defaultPlaybackRate = n
this.getVideoObject().playbackRate = n
}
/**
* render渲染
*/
render () {
let videoEl = document.createElement("video");
// 是否支持 MP4
let isSupportMP4 = videoEl.canPlayType('video/mp4') !== '';
// 是否支持 HLS 的 m3u8
let isSupportHLSm3u8 = /like Mac OS X/gi.test(navigator.userAgent);// 判别是否为手机
// 移动端 支持 m3u8 -
// 安卓只支持 application/vnd.apple.mpegurl,而ios对于application/x-mpegURL,application/vnd.apple.mpegURL均支持
let isSupportm3u8 = videoEl.canPlayType('application/vnd.apple.mpegURL') !== '';
// alert(isSupportMP4+'1')
// alert(isSupportHLSm3u8+'2')
// alert(isSupportm3u8+'3')
return (<video ref="video_obj" poster={this.props.poster} width="100%" height="100%" videoWidth="" videoHeight=""
preload="auto" x5-video-arientation="portrait"
x-webkit-airplay="true"
webkit-playsinline playsinline="true"
x5-video-player-typ="h5">
{
isSupportHLSm3u8 ?
<source type="video/mp4" src="https://img.zai-art.com/video1/a264203d6f683194f8bfe7d698beee36.mp4" />
: isSupportm3u8 ?
<source src={this.props.src} />
: <source type="video/mp4" src="https://img.zai-art.com/video1/a264203d6f683194f8bfe7d698beee36.mp4" />
}
</video>)
}
//
/**
* 获取 Video对象
* 内部创建一个 视频对象
*/
getVideoObject() {
/* 在缓存视频列表里,获取对应要播放的视频对象 */
let playItem = this.playList[this.videoIndex];
/* 如果视频列表里没有 */
if (!playItem) {
this.playList[this.videoIndex] = {src: this.props.src, obj: null, time: 0};
playItem = this.playList[this.videoIndex];
}
let _mediaVideo = playItem.obj;
/* 如果缓存中没有视频对象 */
if (!_mediaVideo) {
/* 创建一个Video对象 */
_mediaVideo = this.refs.video_obj;
_mediaVideo.src = this.props.src;
_mediaVideo.load();
_mediaVideo.loadAll = false; // 可能存在缓存,如果从缓存中获取,那么歌曲就全部加载完成了,就不再需要监听progress事件,虽然它还会继续加载
/* 开始请求 顺序 1 */
_mediaVideo.addEventListener('loadstart', () => {
}, false);
/* 正在请求 顺序 2, 这个比较特殊,有缓存时,它还会请求 */
_mediaVideo.addEventListener('progress', () => {
/* 父级事件,告诉父级视频在加载中 */
!_mediaVideo.loadAll && this.props.onloading();
// console.log('loading', !_mediaVideo.loadAll);
}, false);
/* 资源长度改 顺序 3 */
_mediaVideo.addEventListener('durationchange', () => {
/* 父级事件,告诉父级一些资源信息 华为原生浏览器上,播放前loadedmetadata事件监听,获取不到duration信息 */
this.props.onmeta();
}, false);
/* 可以播放,但中途可能因为加载而暂 顺序 4 */
_mediaVideo.addEventListener('canplay', () => {
/* 歌曲可播放,就代表加载完 */
_mediaVideo.loadAll = true;
/* 父级事件,告诉父级视频加载完成 */
this.props.onloaded();
}, false);
/* 可以播放,歌曲全部加载完毕 顺序 5 */
_mediaVideo.addEventListener('canplaythrough', () => {
/* 父级事件,告诉父级视频加载完成 */
this.props.onloaded();
}, false);
/* play()和autoplay 开始播放时,触发 但并未真正开始播放 */
_mediaVideo.addEventListener('play', () => {
/* 父级事件,告诉父级视频正在播放 */
this.props.onplay();
/* 父级事件,告诉父级视频在加载中 */
this.props.onloading();
}, false);
/* playing 开始播放时,触发,真正开始播放 */
_mediaVideo.addEventListener('playing', () => {
/**
* 0 HAVE_NOTHING 没有关于视频/视频是否就绪的信息
* 1 HAVE_METADATA 关于视频/视频就绪的元数据
* 2 HAVE_CURRENT_DATA 关于当前播放位置的数据是可用的,但没有足够的数据来播放下一帧/毫秒
* 3 HAVE_FUTURE_DATA 当前及至少下一帧的数据是可用的
* 4 HAVE_ENOUGH_DATA 可用数据足以开始播放
*/
if (_mediaVideo.readyState >= 2) {
/* 父级事件,告诉父级视频加载完成 */
this.props.onloaded();
}
}, false);
/* pause() 暂停播放时,触发 */
_mediaVideo.addEventListener('pause', () => {
/* 父级事件,告诉父级视频暂停 */
this.props.onpause();
}, false);
/* 获取一些预设信息,事件监听 */
_mediaVideo.addEventListener('loadedmetadata', () => {
/* 父级事件,告诉父级一些资源信息 */
this.props.onmeta();
}, false);
// waiting error abort
/* 播放时间改变时,实时改变进度条 */
_mediaVideo.addEventListener('timeupdate', () => {
/* 父级事件,告诉父级当前播放位置 */
this.props.ontime(_mediaVideo.currentTime);
// console.log('play');
}, false);
/* 设置playbackRate,触发事件ratechange,播放速率改变 */
_mediaVideo.addEventListener('ratechange', () => {
// alert(this.getVideoObject().playbackRate)
}, false);
/* 设置currentTime,触发事件seeking,寻找中 */
_mediaVideo.addEventListener('seeking', () => {
/* 父级事件,告诉父级视频在加载中 */
this.props.onloading();
}, false);
/* 设置currentTime,触发事件seeked,寻找完成 */
_mediaVideo.addEventListener('seeked', () => {
/* 父级事件,告诉父级视频加载完成 */
this.props.onloaded();
}, false);
playItem.obj = _mediaVideo;
}
return _mediaVideo;
}
}
module.exports = VideoPhone;
import React from 'react'
import { Link } from 'react-router'
import sortedIndex from 'lodash/sortedIndex'
import {toTimeString} from '../../libs/utils'
import VideoPhone from './VideoPhone.jsx'
import ProgressCircle from '../../components/ProgressCircle.jsx'
import Loading from '../../components/Loading.jsx'
import {chapterType} from '../../libs/const'
let pagePlayMap = {};
const RATES = [1.0,1.2,1.5,2.0,2.5,3.0]
let isSkipBegin = false;
if (process.env.BROWSER) {
require('css/videoPlay.css');
// require('howler/dist/howler.min.js');
// require('previewImage');
isSkipBegin = /skip=1/.test(document.cookie); // 浏览器下根据cookie判断是否跳过片头
}
class VideoPhonePanel extends React.Component {
constructor(props) {
super(props);
this.state = {
pptIndex: 0,
loading: true, // 是否加载中
playing: false, // 是否播放中
time: 0, // 当前播放时间
duration: 0, // 总的播放时间
rateIndex: 0, //是否加倍
skipBegin: isSkipBegin, // 跳过片头
showChapter: false, // 显示章节弹出层
};
this.pptTimes = this.props.ppts.map(ppt => ppt.ppt_point);
this.pagePlayMap = pagePlayMap; // 播放期间记录本次页面进度 {[time, progress]}
this._setState = this._setState.bind(this);
this.getAudio = this.getAudio.bind(this);
this.getProgress = this.getProgress.bind(this);
this.onAudioMeta = this.onAudioMeta.bind(this);
this.onAudioLoad = this.onAudioLoad.bind(this);
this.onAudioLoading = this.onAudioLoading.bind(this);
this.onAudioPlay = this.onAudioPlay.bind(this);
this.onAudioPause = this.onAudioPause.bind(this);
this.onAudioCurrentTime = this.onAudioCurrentTime.bind(this);
this.toggleChapter = this.toggleChapter.bind(this);
this.toggleSet = this.toggleSet.bind(this);
this.handlePlay = this.handlePlay.bind(this);
this.handleForward = this.handleForward.bind(this);
this.handleBackward = this.handleBackward.bind(this);
this.handleTouchBar = this.handleTouchBar.bind(this);
this.handleOver = this.handleOver.bind(this);
this.handleSkip = this.handleSkip.bind(this);
this.handleRate = this.handleRate.bind(this);
this.handleClickList = this.handleClickList.bind(this);
this.handlePptView = this.handlePptView.bind(this);
}
componentDidMount() {
}
componentWillReceiveProps(nextProps) {
// 切换index,重置audio相关state
//if (nextProps.index !== this.props.index) {
// this._setState({time: 0})
//}
// 更新pptTimes
if (nextProps.ppts !== this.props.ppts) {
this.pptTimes = nextProps.ppts.map(ppt => ppt.ppt_point);
}
}
componentWillUnmount() {
this.unmounted = true; // 标记unmount状态,防止音频停止事件触发setState
let preview = document.getElementById('__previewImage-container');
if (preview) {
preview.style.display = 'none';
}
}
shouldComponentUpdate(nextProps, nextState) {
return this.state !== nextState || this.props !== nextProps;
}
_setState(obj) {
this.setState((prevState, props) => Object.assign({}, prevState, obj || {}))
}
getAudio() {
return this.refs.video;
}
// 获取进度,综合接口返回上次播放进度和本页播放进度记录
getProgress(id) {
let p = 0;
let pageProg = (this.pagePlayMap[id] || [])[1] || 0;
let dataProg = ((this.props.chapterData.progressMap || {})[id] || {}).chapter_progress || 0;
p = Math.max(pageProg, dataProg);
return p;
}
/**
* 章节层显示与否
*/
toggleChapter() {
this._setState({ showChapter: !this.state.showChapter });
}
toggleSet() {
this._setState({ showSet: !this.state.showSet });
}
// 点击章节列表外面隐藏
handleClickList(e) {
if (e.target === e.currentTarget) {
this._setState({showChapter: false});
}
}
// seek
handleTouchBar(e) {
if (this.state.loading) return;
e = e.nativeEvent;
let x = e.pageX || e.clientX;
const $ = require('jquery');
let bar = $('#video-bar');
let toX = x - bar.offset().left;
if (toX >= 0) {
let audio = this.getAudio();
let time = toX / bar.width() * this.state.duration;
audio.seek(time);
/* 跳播时,实时改变进度条 */
this.onAudioCurrentTime(time);
}
}
// 前15s
handleBackward() {
let audio = this.getAudio();
audio.seek(Math.max(0, this.state.time - 15));
}
// 后15s
handleForward() {
let audio = this.getAudio();
audio.seek(Math.min(this.state.duration - (+this.state.playing), this.state.time + 15));
}
// 标记完成
handleOver() {
this.props.handleOver(this.state.time);
}
// 通过previewImage预览图片
handlePptView(e) {
if (typeof previewImage !== 'object') return;
let list = this.props.ppts.map(ppt => ppt.ppt_url);
if(!list.length) {
list.push(this.props.course.curriculum && this.props.course.curriculum.curriculum_picture || '')
}
previewImage.start({
urls: list,
current: list[this.state.pptIndex]
});
}
formatTime(seconds) {
return toTimeString(Math.round(seconds), seconds >= 3600 ? 'h:m:s' : 'm:s');
}
/**
* 点击播放 click方法
*/
handlePlay() {
if (this.state.loading) return;
let audio = this.getAudio();
if (this.state.playing) {
audio.pause();
} else {
audio.play();
}
}
/**
* 跳过片头 click方法
*/
handleSkip() {
let skip = +(!this.state.skipBegin);
let d = new Date();
d.setMonth(d.getMonth() + 1);
document.cookie = 'skip=' + skip + ';path=/;domain=.ezijing.com;expires=' + d.toGMTString();
this._setState({ skipBegin: skip });
}
/**
* 控制倍速 click方法
*/
handleRate() {
let audio = this.getAudio();
if (audio) {
let l = RATES.length;
let from = this.state.rateIndex;
let to = (from + 1 + l) % l;
this._setState({ rateIndex: to});
audio.rate(RATES[to]);
}
}
// 处理Audio事件, 向下组件传递内容
/**
* 获取资源信息
*/
onAudioMeta() {
this._setState({duration: this.refs.video.getDuration()});
}
/**
* 音频加载中
*/
onAudioLoading() {
this._setState({loading: true});
}
/**
* 音频加载完成,可以播放
*/
onAudioLoad() {
this._setState({loading: false});
}
/**
* 音频播放事件
*/
onAudioPlay() {
this._setState({playing: true});
}
/**
* 音频停止事件
*/
onAudioPause() {
if (this.unmounted) return;
this._setState({playing: false});
}
/**
* 实时改变当前时间
* @param {number} time 当前时间
*/
onAudioCurrentTime(time) {
if (this.unmounted) return;
// 更新当前播放时间
this._setState({time: time});
// 更新当前进度
const cid = this.props.chapterData.leaves[this.props.index];
this.pagePlayMap[cid] = [time, Math.round(time/this.state.duration * 100)];
// 检查ppt是否需要更新
if (this.pptTimes.length) {
let pptIndex = this.pptTimes.indexOf(time);
if (pptIndex < 0) {
pptIndex = sortedIndex(this.pptTimes, time);
pptIndex = Math.max(0, pptIndex - 1);
}
if (this.state.pptIndex !== pptIndex) {
this._setState({pptIndex});
}
}
this.props.handleVideoTime(time);
}
render() {
const {pptIndex, time, duration, playing, loading} = this.state;
const {index, chapterData, ppts, course, video_curm3u8} = this.props;
const chapter = chapterData.map[chapterData.leaves[index]];
if (!course || !course.course_id || !chapter) {
return <div></div>
}
const ppt = ppts[pptIndex] || {};
const isFirst = index === 0, isLast = index === chapterData.leaves.length - 1;
const timeStr = this.formatTime(time);
const durationStr = this.formatTime(duration);
// 上次进度时间
let lastTime = 0;
if (this.pagePlayMap[chapter.id]) {
lastTime = this.pagePlayMap[chapter.id][0] || 0;
} else {
lastTime = (chapterData.progressMap[chapter.id] || {}).last_position || 0;
}
const HEADER = <header>
<h1>{course.course_name || ''}</h1>
<Link to={`/courses/${course.course_id}`} className="play-back-phone">&nbsp;</Link>
</header>
/* 视频还没加载完成 */
if(!video_curm3u8.video) {
return <div><Loading/></div>
}
// 是否支持播放音频
return <div className="page-video">
{HEADER}
<p className="text-error" style={{color:'#f00'}}>暂不支持手机端播放视频文件</p>
</div>
return (
<div className="page-video">
{HEADER}
<section className="play-wrap rel">
<VideoPhone
ref="video"
src={video_curm3u8.streaming[0].playurl}
lastTime={lastTime}
skipBegin={this.state.skipBegin}
onmeta={this.onAudioMeta}
onloading={this.onAudioLoading}
onloaded={this.onAudioLoad}
onplay={this.onAudioPlay}
onpause={this.onAudioPause}
ontime={this.onAudioCurrentTime}
poster={course.curriculum && course.curriculum.curriculum_picture || ''}
/>
<button type="button" onTouchEnd={this.toggleSet} className={`play-set-btn ${this.state.showSet ? 'disable' : ''}`}></button>
<div className={`play-set ${this.state.showSet ? '' : 'hide' }` } >
<div>
<div className="play-set-time"><em>{timeStr}</em>{durationStr}</div>
<div style={{display: "flex"}}>
<button onTouchEnd={this.handleSkip} className={`play-jump ${this.state.skipBegin ? 'disable' : ''}`}><em></em><br/>跳过片头</button>
<button onTouchEnd={this.handleBackward} className="play-set-button play-minus"></button>
<button onTouchEnd={this.handleForward} className="play-set-button play-add"></button>
<button onTouchEnd={this.handleRate} className="play-set-button play-double">{RATES[this.state.rateIndex].toFixed(1)}X<br/>倍速播放</button>
</div>
</div>
</div>
</section>
<footer>
<div className="play-timer">
<div className="video-bar" onClick={this.handleTouchBar} id="video-bar"><span className="video-progress" style={{width: ''+(duration ? time / duration * 100 : 0)+'%'}}></span></div>
<div className="show-time">
<span className="cur-time">{timeStr}</span>
<span className="total-time">{durationStr}</span>
</div>
</div>
<div className="play-ctrls">
<div className="ctrl-items play-ctrl-chapter" onTouchEnd={this.toggleChapter}><em></em><br/>章节</div>
<div className="ctrl-items btns">
<Link to={`/courses/${course.course_id}/chapters/${isFirst ? chapter.id : chapterData.leaves[index - 1]}`} type={`button ${isFirst ? 'disable' : ''}`} className={`button play-prev ${isFirst ? 'disable' : ''}`}>
</Link>
<button type="button" onClick={this.handlePlay} className={`button play-btn ${loading ? 'play-load' : playing ? 'play-pause' : 'play-play'}`}>{loading ? <Loading/> : ''}</button>
<Link to={`/courses/${course.course_id}/chapters/${isLast ? chapter.id : chapterData.leaves[index + 1]}`} type={`button ${isLast ? 'disable' : ''}`} className={`button play-next ${isLast ? 'disable' : ''}`}>
</Link>
</div>
<div className={`ctrl-items play-ctrl-mark ${this.getProgress(chapter.id) === 100 ? 'disable' : ''}`} onTouchEnd={this.handleOver}><em></em><br/>标记为已学完</div>
</div>
<div className={`play-chapter ${this.state.showChapter ? '' : 'hide' }` } onClick={this.handleClickList}>
<div className="play-chapter-inner">
{chapterData.tree.map((tree, i) => {
let root = chapterData.map[tree[0]];
return (
<dl key={i}>
<dt>{root.name}</dt>
{tree[1].map((leaf, j) => {
leaf = chapterData.map[leaf];
return (
<dd key={j} className={index === chapterData.leaves.indexOf(leaf.id) ? 'on' : ''}>
<div className="cl">
<ProgressCircle percent={this.getProgress(leaf.id)} className="play-prog" />
<Link to={`/courses/${course.course_id}/chapters/${leaf.id}`}>{leaf.name}</Link>
{leaf.type === chapterType.VIDEO ?
<span className="fr">{this.formatTime(leaf.video && leaf.video.video_length || 0)}</span>
: null
}
</div>
</dd>
);
})}
</dl>
);
})}
</div>
</div>
</footer>
</div>
)
}
}
module.exports = VideoPhonePanel;
<template> <template>
<div>考试</div> <div class="play-paper">
<div class="play-paper-body">
<div class="play-paper-title"><div><h3>{{chapterName}}</h3></div></div>
<div class="play-paper-content play-chapter-exam">
<div class='exam'>
<div style='text-align: center;'>
<div class='topic'>
<!-- <div class='tit'>{{exam.title}}</div> -->
<template v-if='exam.score'><div class='cur'>正确率:{{exam.score}}%</div></template>
</div>
</div>
<div style=''>
<template v-for='(item, index) in exam.group'>
<template v-if='item.type == 1'>
<div v-bind:key="index" class='q-group' @click="radioClick" :data-index='index'>
<div class='q-num'>{{index+1}}.</div><div class='q-title' v-html='item.title'></div><div class='q-type'>(单选题)</div>
<el-radio-group class='radio-group' @change='radioChange' v-model="item.sel">
<template v-for='(item1, index1) in item.arr'>
<!-- checked='{{item1.selected}}' -->
<el-radio v-bind:key="index1" :label='index1' :disabled='!!item.cur' :class='["radio", (item.cur && item1.checked && "success"), (item.cur && !item1.checked && item1.selected && "error")]'>{{ index1 == 0 ? "A" : (index1 == 1 ? "B" : (index1 == 2 ? "C" : (index1 == 3 ? "D" : (index1 == 4 ? "E" : (index1 == 5 ? "F" : "G"))))) }}. {{item1.option}}</el-radio>
</template>
</el-radio-group>
<template v-if='item.cur'><div class='result'>正确答案:{{item.cur}}</div></template>
</div>
</template>
<template v-if='item.type == 2'>
<div v-bind:key="index" class='q-group' @click="checkboxClick" :data-index='index'>
<div class='q-num'>{{index+1}}.</div><div class='q-title' v-html='item.title'></div><div class='q-type'>(多选题)</div>
<el-checkbox-group class='checkbox-group' @change='checkboxChange' v-model="item.arrSel">
<template v-for='(item1, index1) in item.arr'>
<!-- value='{{index1}}' -->
<el-checkbox v-bind:key="index1" :label='index1' :checked='!!item1.selected' :disabled='!!item.cur' :class='["checkbox", (item.cur && item1.checked && "success"), (item.cur && !item1.checked && item1.selected && "error")]'>{{ index1 == 0 ? "A" : (index1 == 1 ? "B" : (index1 == 2 ? "C" : (index1 == 3 ? "D" : (index1 == 4 ? "E" : (index1 == 5 ? "F" : "G"))))) }}. {{item1.option}}</el-checkbox>
</template>
</el-checkbox-group>
<template v-if='item.cur'><div class='result'>正确答案:{{item.cur}}</div></template>
</div>
</template>
</template>
</div>
<div :class='["btn", (exam.work_contents && "on")]' @click='submitExam' :data-submit='!!exam.work_contents'>{{exam.work_contents ? "已提交" : "提交"}}</div>
<div class='care'>(注意:测试只有一次提交机会)</div>
</div>
</div>
</div>
</div>
</template> </template>
<script>
import cAction from '@actions'
// import CKEDITOR from 'CKEDITOR'
export default {
props: {
chapterId: { type: String, require: false },
chapterName: { type: String, require: false },
chapterExam: { type: Object, require: false },
sid: { type: String, require: false },
cid: { type: String, require: false },
id: { type: String, require: false }
},
data () {
return {
exam: {
// id: '',
// semester_id: '',
// title: '家族财富传承第一章试题',
// score: '100',
// work_contents: '',
// group: [{
// id: '',
// type: 1, // 判别是 单选还是多选
// title: '<p>一般延续好几代,采取防御战略维持企业的家族企业类型是( )。</p>',
// arr: [
// { id: '', checked: true, option: '闪电型', selected: 0 },
// { id: '', checked: true, option: '不老型', selected: 1 },
// { id: '', checked: false, option: '长存型', selected: 1 },
// { id: '', checked: false, option: '持续创业型', selected: 0 }
// ],
// cur: '1',
// arrSel: [],
// sel: ''
// }]
},
// 提交时,传入的参数值
param: {
course_id: '',
chapter_id: '',
work_id: '',
semester_id: '',
work_contents: '',
duration: 0,
score: 0
},
/* 进入页面记录初试时间 */
startTime: '',
/* 记录所有题目是否都答过 */
recordAll: [],
setTime: null,
/* 当前 radio点击 */
radioCur: '',
/* 当前 checkbox点击 */
checkCur: ''
}
},
mounted () {
this.loadAjax()
},
methods: {
/**
* 将返回值 - 对照到对应 data上
*/
updateData (json) {
this.param = {
course_id: json.course_id,
chapter_id: json.chapter_id,
work_id: json.work_id,
semester_id: json.semester_id,
work_contents: '',
duration: 0,
score: 0
}
this.startTime = new Date().getTime()
let _data = {
id: json.id,
semester_id: json.semester_id,
title: json.work_title,
score: json.score || '',
work_contents: json.work_contents || '',
group: json.questions.map(function (_, i) {
let str = ''
let _json = JSON.parse(_.question_options)
_json.forEach(function (__, j) {
if (__.checked) {
switch (j) {
case 0: str += 'A,'; break
case 1: str += 'B,'; break
case 2: str += 'C,'; break
case 3: str += 'D,'; break
}
}
})
let sel = '' // 单选选中
/* 答过题目时 */
if (json.work_contents) {
let a = JSON.parse(json.work_contents)
for (let i = 0; i < a.length; i++) {
if (a[i].question_id === _.id) {
_json = a[i].options
break
}
}
/* 单选选中 */
if (_.question_type === 1) {
for (let i = 0; i < _json.length; i++) {
if (_json[i].selected) {
sel = i
break
}
}
}
}
return {
id: _.id,
type: _.question_type,
title: _.question_content,
arr: _json,
cur: (json.work_contents && str.substr(0, str.length - 1)) || '',
arrSel: [],
sel: sel
}
})
}
return _data
},
/**
* 生命周期函数--监听页面加载
*/
loadAjax () {
const loading = this.$loading({ lock: true, text: '', spinner: '', background: 'rgba(255, 255, 255, 0.9)' })
cAction.chapterAction.getExamDetail(this.sid, this.id).then(_data => {
let json = _data.homework
if (json) {
json.score = _data.score
json.work_contents = _data.work_contents
this.exam = this.updateData(json)
} else {
this.exam = {}
}
}).catch(e => { this.$message.error(e.message) }).finally(() => {
this.setTime = setInterval(() => {
// console.log(this.chapterExam.work_id, this.id)
if (this.chapterExam.work_id && this.chapterExam.work_id === this.id) {
if (!this.exam.id) {
this.exam = this.updateData(this.chapterExam)
/* 滚动到头部 */
document.querySelector('.play-paper').scrollTop = 0
}
clearInterval(this.setTime)
}
}, 50)
loading.close()
})
},
/**
* 辅助 radio选择
*/
radioClick (e) {
this.radioCur = e.currentTarget.dataset.index
},
/**
* radio选择
* PC端 值会 返回 选择的 值
*/
radioChange (val) {
let i = this.radioCur
let arr = this.exam.group[i].arr
let value = val
for (let j = 0; j < arr.length; j++) {
arr[j]['selected'] = 0
}
arr[value].selected = 1
this.exam.group[i].arr = arr
this.recordAll.push(i)
},
/**
* 辅助 checkbox选择
*/
checkboxClick (e) {
this.checkboxCur = e.currentTarget.dataset.index
},
/**
* checkbox选择
*/
checkboxChange (val) {
let i = this.checkboxCur
let arr = this.exam.group[i].arr
let value = val
for (let j = 0; j < arr.length; j++) {
arr[j]['selected'] = 0
}
for (let j = 0; j < value.length; j++) {
arr[value[j]].selected = 1
}
this.exam.group[i].arr = arr
if (!value.length) {
this.recordAll = this.recordAll.filter(function (_, m) { return _ !== i })
} else {
this.recordAll.push(i)
}
},
/**
* 提交试题
*/
submitExam (e) {
if (e.currentTarget.dataset.submit) {
this.$message.error('已做过,不能再提交')
return
}
// /* 计算答题时间 */
this.param.duration = Math.floor((new Date().getTime() - this.startTime) / 1000)
let group = this.exam.group
let total = group.length
let arr = []
let score = 0
let correct = 0
for (let m = 0; m < total; m++) {
let n = 0
for (; n < this.recordAll.length; n++) {
if (this.recordAll[n] === m + '') { break }
}
if (n === this.recordAll.length) {
console.log(this.recordAll)
this.$message.error('还有题目未做,不能提交')
return
}
}
/* 重新定义 - 上传字段 并计算分数 */
// is_correct
for (let i = 0; i < group.length; i++) {
correct = 0
if (group[i].type === 1) { // 单选的正确 判断
group[i].arr.forEach(function (_, i) {
if (_.checked && _.selected) { correct = 1; score += 1 }
})
}
if (group[i].type === 2) { // 多选的正确 判断
let flag = true
group[i].arr.forEach(function (_, i) {
if (_.checked !== _.selected) { flag = false }
})
if (flag) { correct = 1; score += 1 }
}
arr.push({
question_id: group[i].id,
is_correct: correct,
options: group[i].arr
})
}
this.param.work_contents = JSON.stringify(arr)
this.param.score = (score / total * 100).toFixed(1)
const loading = this.$loading({ lock: true, text: '', spinner: '', background: 'rgba(255, 255, 255, 0.9)' })
cAction.chapterAction.submitExamDetail(this.param).then(_res => {
if (_res.status) {
cAction.chapterAction.getExamDetail(this.sid, this.id).then(_data => {
let json = _data.homework
if (json) {
json.score = _data.score
json.work_contents = _data.work_contents
this.exam = this.updateData(json)
}
}).catch(e => { this.$message.error(e.message) })
} else {
this.$message.error(_res.data.error)
}
}).catch(e => { this.$message.error(e.message) }).finally(() => { loading.close() })
}
},
watch: {
id: {
handler () {
this.loadAjax()
}
}
}
}
</script>
<style lang="scss" scoped>
.play {
.exam { padding: 0; }
.exam .topic { display: inline-block; margin-bottom: 0.1rem; }
.exam .topic .tit { margin: 0 auto; padding: 0 0.2rem; text-align: center; font-size: 0.24rem; color: #313131; background: #fff; box-sizing: border-box; -webkit-box-sizing: border-box; }
.exam .topic .cur { text-align: center; font-size: 0.18rem; color: #313131; line-height: 0.4rem; }
/* 循环 所有选择题 */
.exam .q-group { padding: 0.1rem 0.1rem; border-bottom: 1px solid #c9c9c97a; overflow: hidden; }
.exam .q-group .q-num { float: left; margin-right: 0.1rem; font-size: 0.16rem; color: #676a6c; }
.exam .q-group .q-title { float: left; width: 90%; font-size: 0.16rem; color: #676a6c; text-align: justify; }
.exam .q-group .q-type { float: right; font-size: 0.16rem; color: #676a6c; }
.exam .q-group .radio-group { float: left; margin-top: 0.1rem; width: 100%; }
.exam .q-group .radio-group .radio { display: block; font-size: 0.18rem; color: #3f3b3a; line-height: 0.3rem; margin-bottom: 0.1rem; }
.exam .q-group .checkbox-group { float: left; margin-top: 0.1rem; width: 100%; }
.exam .q-group .checkbox-group .checkbox { display: block; font-size: 0.18rem; color: #3f3b3a; line-height: 0.3rem; margin-bottom: 0.1rem; }
.exam .q-group .radio-group .radio.error, .exam .q-group .checkbox-group .checkbox.error { color: #d80000; }
.exam .q-group .radio-group .radio.success, .exam .q-group .checkbox-group .checkbox.success { color: #090; }
.exam .q-group .result { float: right; font-size: 0.18rem; color: #3f3b3a; margin-right: 0; }
.exam .q-group:last-child { border-bottom: none; }
.exam .btn { margin: 0.2rem auto; width: 60%; height: 0.5rem; line-height: 0.5rem; font-size: 0.16rem; text-align: center; font-weight: 300; color: #fff; border-radius: 0.1rem; background: #b49441; cursor: pointer; }
.exam .btn.on { opacity: 0.5; }
.exam .care { font-size: 0.16rem; color: #d80000; text-align: center; }
}
</style>
<template> <template>
<div>视频</div> <div class="play-content-video" ref="container">
<div class="play-center" ref="box">
<template v-if="chapterVideo">
<div :class="['play-video', (state.pptBoxOnly ? 'play-video-hide' : ''), (state.calculatedSize ? '' : 'play-video-init-center')]">
<e-video
ref="video"
:lastTime="parseFloat((chapterVideo.progress && chapterVideo.progress.cpt) || 0)"
:width="videoFlash.videoWidth"
:height="videoFlash.videoHeight"
:username="videoFlash.username"
:videoId="chapterVideo.video_origionalID"
:videoSrt="(chapterVideo.video_subtitle || '')"
@handlePlayTime="onVideoTimeChange"
:chapterVideo="chapterVideo"
/>
</div>
<div :class="['play-jiangyi', (state.pptBoxShow ? '' : 'hide')]">
<template v-if="ppts.length">
<e-ppt
ref="ppt"
:ppts="ppts"
:currentIndex="state.pptIndex"
@onVideoSyncTime="setVideoTime"
@onPptOnly="togglePptBoxOnly"
@onClose="togglePptBox"
/>
</template>
</div>
</template>
<template v-else>
<p>课程视频数据不存在</p>
</template>
</div>
<!-- 底部控制 -->
<div class="play-footer">
<div class="fl">
<!-- <Link to={`/courses/${semesterId}/${courseId}/chapters/${prevChapterId || chapterId}`} class={`play-state play-state-prev ${prevChapterId?'': 'play-state-prev-disable'}`}>上一节</Link> -->
<!-- <Link to={`/courses/${semesterId}/${courseId}/chapters/${nextChapterId || chapterId}`} class={`play-state play-state-next ${nextChapterId?'': 'play-state-next-disable'}`}>下一节</Link> -->
</div>
<div class="fr">
<template v-if="chapterVideo.pdf">
<em class="play-state play-state-ppt" ><a :href="chapterVideo.pdf" target="_blank">下载PPT</a></em>
</template>
<!-- <em class={`play-state play-state-check${curProgress == 100 ? '-active' : ''}`} onClick={this.handleOver} data-vid={video && video.id || ''} data-progress={curProgress} data-total={video && video.video_length || 1}>{curProgress == 100 ? '已学完' : '标记为已学完'}</em> -->
<template v-if="ppts.length">
<em :class="['play-state', ('play-state-ppt' + (state.pptBoxShow ? '-active' : ''))]" @click="togglePptBox">同步显示PPT</em>
</template>
<em :class='["play-state", ("play-state-check" + (state.skipBegin ? "-active" : ""))]' @click="toggleSkipBegin">始终跳过片头</em>
</div>
</div>
</div>
</template> </template>
<script> <script>
import eVideo from './video.vue'
import ePpt from './ppt.vue'
import _ from 'lodash'
const PLAY_SPACE_WIDTH = 15
const PLAY_SPACE_HEIGHT = 10
/* 初始视频 宽、高 */
const VIDEO_DEFAULT_WIDTH = 550
const VIDEO_DEFAULT_HEIGHT = 360
export default { export default {
components: { eVideo, ePpt },
props: { props: {
chapterId: { type: String, require: false },
chapterName: { type: String, require: false },
chapterVideo: { type: Object, require: false },
ppts: { type: Array, require: false, default: [] },
sid: { type: String, require: false }, sid: { type: String, require: false },
cid: { type: String, require: false }, cid: { type: String, require: false },
id: { type: String, require: false } id: { type: String, require: false }
}, },
data () {
return {
state: {
pptIndex: 0, // ppt 所在当前位置
pptBoxOnly: false, // 仅展示ppt框
pptBoxShow: false, // 展示ppt框
skipBegin: /skip=1/.test(document.cookie), // 跳过片头
calculatedSize: false // 是否以计算过播放位置的尺寸
},
/* video flash 参数传递 */
videoFlash: {
lastTime: null,
videoWidth: VIDEO_DEFAULT_WIDTH,
videoHeight: VIDEO_DEFAULT_HEIGHT,
username: (window.G.UserInfo && window.G.UserInfo.username) || ''
},
/* 增加节流函数 对应 执行 */
resizeVideo: null,
/* 视频进度 统计 和 计算 */
hearBeat: null,
_rProgress: {}
}
},
mounted () { mounted () {
console.log(this.sid, this.cid, this.id) let size = this.getCalculateSize()
if (size.video.w) {
this.videoFlash.videoWidth = size.video.w
this.videoFlash.videoHeight = size.video.h
}
/* 如果不存在 改变视频尺寸大小 节流执行函数 定义如下 */
if (!this.resizeVideo) {
this.resizeVideo = _.debounce(this.jdugeSize.bind(this), 200)
}
/* 延迟计算尺寸,因flash初始化需要时间 */
setTimeout(this.jdugeSize, 300)
/* 窗口resize,重置大小 */
window.addEventListener('resize', this.resizeVideo)
/* 增加 心跳 记录 视频学习时间 */
this.hearBeat = setInterval(() => {
if (this.chapterVideo) {
if (this._rProgress && this._rProgress.id) {
let _rProgress = this._rProgress
let tempTime = this.videoFlash.lastTime
_rProgress.pt = parseInt(_rProgress.pt) + parseInt((tempTime - _rProgress.cpt > 0) && (tempTime - _rProgress.cpt < 8) ? (tempTime - _rProgress.cpt) : 0)
_rProgress.cpt = tempTime
_rProgress.mpt = tempTime > _rProgress.mpt ? tempTime : _rProgress.mpt
this.$emit('updateProgress', this._rProgress)
} else {
this._rProgress = _.assignIn({}, this.chapterVideo.progress)
}
}
}, 5000)
},
destroyed () {
window.removeEventListener('resize', this.resizeVideo)
clearInterval(this.hearBeat)
},
watch: {
id: {
handler () {
/* 重新点击时,刷新左侧,隐藏pptbox */
/* 这里 理论上 应该 把所有 组件全部初始化,否则会出现部分数据是 上次同一个组件 但不同vid的 视频内容值 */
if (this.id) {
this.state.pptBoxShow = false
this.state.pptBoxOnly = false
this._rProgress = {}
// this.videoFlash.lastTime = 0 // 这里 修改 最后时间不生效
setTimeout(this.jdugeSize, 0)
}
}
}
},
methods: {
// toggle PPT播放框
togglePptBox () {
this.state.pptBoxShow = !this.state.pptBoxShow
this.state.pptBoxOnly = false
setTimeout(this.jdugeSize, 0)
},
// 仅显示ppt播放框
togglePptBoxOnly () {
this.state.pptBoxOnly = !this.state.pptBoxOnly
setTimeout(this.jdugeSize, 0)
},
// 跳过片头
toggleSkipBegin () {
let skip = !this.state.skipBegin
let d = new Date()
d.setMonth(d.getMonth() + 1)
document.cookie = 'skip=' + (+skip) + ';path=/;domain=.ezijing.com;expires=' + d.toGMTString()
if (skip && this.$refs.video) {
this.$refs.video.skipBegin()
}
this.state.skipBegin = skip
},
// 播放时间变更
onVideoTimeChange (data) {
let time = data.time
// 因视频播放完成后也会不断触发playing,因此比对上次时间
if (this.videoFlash.lastTime === time) { return }
this.videoFlash.lastTime = time
// 判断ppt位置
this.setPptIndexByTime(time)
this.$emit('handlePlayTime', time) // 调用 父级方法,改变 sidebar ppt 对应 位置
},
/**
* 根据时间设置ppt位置 - PPT 样式 控制
*/
setPptIndexByTime (time) {
const ppts = this.ppts || []
let i = 0
for (; i < ppts.length; i++) {
if (time < ppts[i].ppt_point) { break }
}
if (this.state.pptIndex !== i - 1) {
this.state.pptIndex = i - 1
}
},
/**
* 设置视频播放时间点 - video flash 控制
*/
setVideoTime (time) {
if (this.$refs.video) {
this.$refs.video.setTimeTo(time)
}
},
/**
* 判断并设置尺寸位置
*/
jdugeSize () {
let box = this.$refs.box
let size = this.getCalculateSize()
let uh = 0
let uw = 0
if (this.state.pptBoxOnly) { // 仅ppt
uw = size.ppt.w
uh = size.ppt.h
if (this.$refs.ppt) {
this.$refs.ppt.setSize(uw, uh)
}
} else if (this.state.pptBoxShow) { // 同时显示video、ppt
if (this.$refs.ppt) {
this.$refs.ppt.setSize(size.ppt.w, size.ppt.h)
}
if (this.$refs.video) {
this.$refs.video.setSize(size.video.w, size.video.h)
}
uw = size.ppt.w + size.video.w
uh = size.video.h
} else { // 只显示video
uw = size.video.w
uh = size.video.h
if (this.$refs.video) {
this.$refs.video.setSize(uw, uh)
}
}
this.state.calculatedSize = true
box.style.paddingLeft = '' + ((size.space.w - uw) / 2 + PLAY_SPACE_WIDTH) + 'px'
box.style.paddingTop = '' + ((size.space.h - uh) / 2 + PLAY_SPACE_HEIGHT) + 'px'
},
/* 获取各个元素计算后的尺寸 */
getCalculateSize () {
let container = this.$refs.container
let w = container.offsetWidth - PLAY_SPACE_WIDTH * 2
let h = container.offsetHeight - 53 - PLAY_SPACE_HEIGHT * 2 // 减去底部操作条高度
let videoRatio = 550 / 363
let pptRatio = 336 / 236
let r = {
space: { w: w, h: h }, // 可容纳play的容器总大小
video: { w: 0, h: 0 }, // 视频大小
ppt: { w: 0, h: 0 } // ppt容器大小
}
if (this.state.pptBoxOnly) { // 仅ppt
r.ppt.w = w < h * pptRatio ? w : h * pptRatio
r.ppt.h = h < w / pptRatio ? h : w / pptRatio
} else if (this.state.pptBoxShow) { // 同时显示video、ppt
let halfW = w / 2
let vw = halfW < h * videoRatio ? halfW : h * videoRatio
let vh = h < halfW / videoRatio ? h : halfW / videoRatio
let ph = vh
let pw = ph * pptRatio
r.video.w = vw
r.video.h = vh
r.ppt.w = pw
r.ppt.h = ph
} else { // 只显示video
r.video.w = w < h * videoRatio ? w : h * videoRatio
r.video.h = h < w / videoRatio ? h : w / videoRatio
}
return r
}
} }
} }
</script> </script>
<template>
<div class="play-ppt" ref="wrap">
<div class="play-preview" ref="preview">
<img :src="ppts[state.index].ppt_url" class="play-ppt-img" style='vertical-align: middle' />
</div>
<div class="play-controls cl">
<div style="float: left;">
<template v-if="state.index >= 0">
<a href="#" @click="prev" style='margin: 0 20px 0 0; color: #fff;'><i class="el-icon-arrow-left"></i></a>
</template>
<template v-if="state.index + 1 < ppts.length">
<a href="#" @click="next"><i class="el-icon-arrow-right" style="color: #fff;"></i></a>
</template>
</div>
<div class="play-page">
<span class="play-now">{{state.index + 1}}</span>
/
<span class="play-total">{{ppts.length}}</span>
</div>
<div class="play-amazing">
<i :class="['el-icon-self-xuexiao', (state.sync ? 'active' : '')]" @click="onToggleSync"></i>
<i class="el-icon-self-quanping" @click="() => { this.$emit('onPptOnly') }"></i>
<i class="el-icon-self-shipin" @click="onSetVideoTime"></i>
<i class="el-icon-self-guanbi" @click="() => { this.$emit('onClose') }"></i>
</div>
</div>
</div>
</template>
<script>
/**
* ppt播放框
* 如果同步显示,则同步props.currentIndex到state.index,否则,仅使用state.index
*/
export default {
props: {
ppts: { type: Array, require: false },
currentIndex: { type: Number, require: false, default: 0 }
},
data () {
return {
state: {
index: this.currentIndex, // ppt展示序号
sync: true // 视频播放时,同步调整ppt展示
}
}
},
watch: {
currentIndex: {
handler () {
if (this.state.sync) {
this.state.index = this.currentIndex
}
}
}
},
methods: {
gotoIndex (index) {
this.state.index = index
},
getIndex (index) {
return Math.min(this.ppts.length - 1, Math.max(0, index))
},
prev (e) {
this.state.index = this.getIndex(this.state.index - 1)
this.state.sync = false
},
next (e) {
this.state.index = this.getIndex(this.state.index + 1)
this.state.sync = false
},
onToggleSync (e) {
this.state.sync = !this.state.sync
this.state.index = this.state.sync ? this.currentIndex : this.state.index
},
onSetVideoTime (e) {
this.$emit('onVideoSyncTime', this.ppts[this.state.index].ppt_point)
},
setSize (w, h) {
this.$refs.wrap.style.width = w + 'px'
this.$refs.wrap.style.height = h + 'px'
this.$refs.preview.style.lineHeight = (h - 44) + 'px'
}
}
}
</script>
<template>
<div id="playerWrap">
<div id="player">
<p>您还没有安装flash播放器,请 <a href="http://www.adobe.com/go/getflash" target="_blank">点击这里安装</a></p>
</div>
</div>
</template>
<style lang="scss" scoped>
#player p { color: #fff; text-align: center; padding: 50px 0; }
#player p a { color: #b01c40; text-decoration: underline; }
</style>
<script>
import swfobject from 'VideoJs'
// 播放器ID
const PLAYER_WRAP_ID = 'playerWrap'
const PLAYER_ID = 'player'
const SKIP_BEGIN_TIME = 7 // 跳过片头设置片头时间
let continueStart = 0 // 继续学习初始值
export default {
props: {
lastTime: { type: Number, require: false },
videoId: { type: String, require: false },
width: { type: Number, require: false },
height: { type: Number, require: false },
username: { type: String, require: false },
videoSrt: { type: String, require: false },
autoPlay: { type: Boolean, require: false, default: true },
chapterVideo: { type: Object, require: false }
},
mounted () {
this.definWindowFun()
// console.log(PLAYER_ID, this.videoId, this.autoPlay, this.videoSrt, this.username, this.width, this.height)
// this.renderPlayer(PLAYER_ID, this.videoId, this.autoPlay, this.videoSrt, this.username, this.width, this.height)
},
watch: {
videoId: {
handler () {
if (this.videoId) {
/* 注意 flash 初始化时,需要页面DOM存在 + videoId存在 */
continueStart = this.lastTime || 0 // 如果传递有上次播放时间,则记录缓存,以便player.start时使用
this.renderPlayer(PLAYER_ID, this.videoId, this.autoPlay, this.videoSrt, this.username, this.width, this.height)
}
}
}
},
methods: {
/* 定义windows下,播放事件和初始化回调 */
definWindowFun () {
let that = this
// 开始播放,如果设置了跳过片头则设置播放时间
window._playerStart = function () {
if (/skip=1/.test(document.cookie)) {
that.getPlayer().callAction('setCurrentTime', Math.max(continueStart, SKIP_BEGIN_TIME)) // 跳到第6秒开始播放
} else if (continueStart) {
that.getPlayer().callAction('setCurrentTime', continueStart)
}
}
// 播放过程中不断触发,传递当前播放到的时间
window._playerIng = function (time) {
$('#' + PLAYER_WRAP_ID).trigger('player.time', { time })
}
// 拖动播放进度条
window._playerSeek = function () {
$('#' + PLAYER_WRAP_ID).trigger('player.seek', { time: that.getPlayer().callAction('getCurrentTime') })
}
// 播放控件 - 初始化完成时,注册播放事件
window._playerCallback = function () {
let player = that.getPlayer()
if (player) {
// player.register('onLoadStart', '') // 开始loading加载
player.callAction('register', 'onCanplay', '_playerStart') // 开始播放视频内容
player.callAction('register', 'onPlaying', '_playerIng') // 播放中触发,300ms一次
// player.register('onPause', '') // 暂停
// player.register('onResume', '') // 恢复播放
player.callAction('register', 'onSeekComplete', '_playerSeek') // 拖动进度条
// player.register('onEnded', '') // 结束
}
}
},
/* flash swf视频 对象渲染 采用 VideoJs插件渲染 */
renderPlayer (domId, vid, autoPlay, srt, username, width, height) {
autoPlay = typeof autoPlay === 'undefined' ? 1 : autoPlay - 0
// For version detection, set to min. required Flash Player version, or 0 (or 0.0.0), for no version detection.
let swfVersionStr = '11.1.0'
// To use express install, set to playerProductInstall.swf, otherwise the empty string.
let xiSwfUrlStr = 'playerProductInstall.swf'
let flashvars = {
autoStart: autoPlay,
vid: vid,
isShowSpeeder: 1,
videoType: 1, // 0为mp4模式 1为cc模式
callback: '_playerCallback'
}
if (srt) { flashvars.srtUrl = srt }
if (username) { flashvars.username = username }
// flashvars.videoType = 1; // 0为mp4模式 1为cc模式
let params = {
quality: 'high',
bgcolor: '#000000',
allowscriptaccess: 'always',
allowfullscreen: 'true'
}
let attributes = {
id: domId,
name: domId,
align: 'middle',
wmode: 'opaque'
}
// render
swfobject.embedSWF(
// require('player'),
'/static/videoJs/swf/Player1705192.swf',
domId,
parseInt(width),
parseInt(height),
swfVersionStr,
xiSwfUrlStr,
flashvars,
params,
attributes
)
// 绑定事件监听
this.listenPlayerEvents()
},
listenPlayerEvents () {
$('#' + PLAYER_WRAP_ID).off('player.time player.seek').on('player.time player.seek', (e, data) => {
this.$emit('handlePlayTime', data)
})
},
// ========= 提供播放后,其他组件可使用控制播放的方法 ===========
// 获取视频对象
getPlayer () {
return document.getElementById(PLAYER_ID)
},
getTime () {
let player = this.getPlayer()
if (player) {
return player.callAction('getCurrentTime')
} else {
return 0
}
},
// 设置视频跳转时间
setTimeTo (time) {
let player = this.getPlayer()
if (player) {
player.callAction('setCurrentTime', time + 2) // flash实际播放值会大概小个一两秒,因此添加偏移
}
},
// 执行“跳过片头”操作
skipBegin () {
let player = this.getPlayer()
if (player && player.callAction('getCurrentTime') < SKIP_BEGIN_TIME) {
player.callAction('setCurrentTime', SKIP_BEGIN_TIME)
}
},
// 设置视频尺寸
setSize (w, h) {
let player = this.getPlayer()
if (player) {
player.width = w
player.height = h
}
}
}
}
</script>
.play{overflow:hidden; position: fixed;top:0; z-index: 800; width: 100%; height: 100%; background-color:#3f3f3f;color:#a0a0a0;} .play{overflow:hidden; position: fixed;top:0; z-index: 800; width: 100%; height: 100%; background-color:#3f3f3f;color:#a0a0a0;}
.play .left-content{ position: absolute; right: 350px; top: 0; left: 0; bottom: 0; min-width: 705px;height:100%;} .play .left-content{ position: absolute; right: 350px; top: 0; left: 0; bottom: 0; min-width: 705px;height:100%;}
.play .sidebar-hide .left-content{right:0;} .play.sidebar-hide .left-content{right:0;}
.play .play-top{ line-height: 56px;} .play .play-top{ line-height: 56px;}
.play .play-top p{font-size:1.5em;text-align:center;margin:0;} .play .play-top p{font-size:1.5em;text-align:center;margin:0;}
.play .play-back{position:absolute;top:6px;left:10px;width:40px;height:40px;overflow:hidden;color: #fff;font-size: 24px;line-height: 40px;text-align: center;}
.play .play-content{position:absolute;top:56px;bottom:0;left:0;right:0;} .play .play-content{position:absolute;top:56px;bottom:0;left:0;right:0;}
.play .play-content-video{height:100%;} .play .play-content-video{height:100%;}
...@@ -12,95 +13,42 @@ ...@@ -12,95 +13,42 @@
.play .play-video-hide{visible:hidden;overflow:hidden;width:0} .play .play-video-hide{visible:hidden;overflow:hidden;width:0}
.play .play-video-init-center{position:absolute;top:50%;left:50%;margin:-180px 0 0 -275px;} .play .play-video-init-center{position:absolute;top:50%;left:50%;margin:-180px 0 0 -275px;}
/* ppt控制 样式 */
.play .play-ppt { position: relative;width: 550px; height: 363.375px;background-color:#000;} .play .play-ppt { position: relative;width: 550px; height: 363.375px;background-color:#000;}
.play .play-ppt-img { width: 100%; height: 100%;} .play .play-ppt-img { width: 100%; height: 100%;}
.play .play-controls {position: absolute;bottom:0;left:0;right:0;height: 44px; line-height: 42px; padding: 0 14px;background-color: #000;} .play .play-controls {position: absolute;bottom:0;left:0;right:0;height: 44px; line-height: 42px; padding: 0 14px;background-color: #000;}
.play .play-controls .fl i{ color: #8c8c8b} .play .play-controls .fl i{ color: #8c8c8b}
.play .play-page{ position: absolute; left: 50%; margin-left: -75px; color: #fff; width: 150px; text-align: center; font-size: 0.875em;} .play .play-page{ position: absolute; left: 50%; margin-left: -75px; color: #fff; width: 150px; text-align: center; font-size: 0.875em;}
.play .play-page .play-now { color: #d29f29;} .play .play-page .play-now { color: #d29f29;}
.play .play-amazing i{ color: #fff; margin: 0 10px;} .play .play-amazing { float: right; }
.play .play-amazing i{ color: #fff; margin: 0 10px; cursor: pointer; }
.play .play-amazing i.active, .play .play-amazing i.active,
.play .play-amazing i:hover{ color: #d29f29;} .play .play-amazing i:hover{ color: #d29f29;}
.play .play-amazing .icon-rotate{ font-size: 1.125em;} .play .play-amazing .icon-rotate{ font-size: 1.125em;}
/* 整个视频 底部控制 上一章、下一章 同步、下载、跳过 */
.play .play-footer{ position: absolute; bottom: 0; left: 0;right:0; z-index: 200;padding:18px 20px 15px;} .play .play-footer{ position: absolute; bottom: 0; left: 0;right:0; z-index: 200;padding:18px 20px 15px;}
.play .play-footer a:hover{color:#a0a0a0;} .play .play-footer a:hover{color:#a0a0a0;}
/* .play-state, .play-back{display:inline-block;color:#a0a0a0;padding-left: 25px;font-size:14px;line-height:18px;margin:0 20px;background:url(../img/play-icons.png) no-repeat 0 0;cursor:pointer} */ /* 底部控制 按钮样式 */
.play .play-back{position:absolute;top:6px;left:10px;width:40px;height:40px;overflow:hidden;color: #fff;font-size: 24px;line-height: 40px;text-align: center;} .play .play-state{display:inline-block;color:#a0a0a0;padding-left: 25px;font-size:14px;line-height:18px;margin:0 20px;background:url(./play-icons.png) no-repeat 0 0;cursor:pointer}
.play .play-state a { color: #a0a0a0; text-decoration: none; }
.play .play-state-prev{background-position:0 2px;} .play .play-state-prev{background-position:0 2px;}
.play .play-state-prev-disable{background-position:0 -78px;color:#666;} .play .play-state-prev-disable{background-position:0 -78px;color:#666;}
.play .play-state-next{background-position:0 -38px;} .play .play-state-next{background-position:0 -38px;}
.play .play-state-next-disable{background-position:0 -118px;color:#666;} .play .play-state-next-disable{background-position:0 -118px;color:#666;}
.play .play-state-check{background-position:0 -160px;} .play .play-state-check{background-position:0 -160px;}
.play .play-state-check-active{background-position:0 -200px;color: #b19241;} .play .play-state-check-active{background-position:0 -200px;color: #b19241; }
.play .play-state-ppt{background-position:0 -240px;} .play .play-state-ppt{background-position:0 -240px;}
.play .play-state-ppt-active{background-position:0 -280px;} .play .play-state-ppt-active{background-position:0 -280px;color: #b19241; }
.play .play-state-prev-disable:hover, .play .play-state-prev-disable:hover,
.play .play-state-next-disable:hover {color:#666!important;} .play .play-state-next-disable:hover {color:#666!important;}
/* 收起右侧,出现的 按钮样式 */
.play .switch { display: block; width: 36px; height: 125px; position: absolute; right: 0px; top: 50%; text-align: center; margin: -63px 0 0; z-index: 998; cursor: pointer;} .play .switch { display: block; width: 36px; height: 125px; position: absolute; right: 0px; top: 50%; text-align: center; margin: -63px 0 0; z-index: 998; cursor: pointer;}
.play .switch a { color: #a0a0a0; text-decoration: none; font-size: 14px; line-height: 2; margin-bottom: 20px; } .play .switch a { color: #a0a0a0; text-decoration: none; font-size: 14px; line-height: 2; margin-bottom: 20px; }
.play .switch a i { font-size: 24px; padding: 5px; background: #666; border-radius: 4px; } .play .switch a i { font-size: 24px; padding: 5px; background: #666; border-radius: 4px; }
.play .right-content { position: absolute; right: 0px; width: 350px; z-index: 200; background: #212121; bottom: 0; top: 0px; border-left:19px solid #1b1b1b;}
.play .right-content .right-arrow{ position: absolute; left:-19px; top:0;bottom:0;width:19px;height:100%;cursor: pointer; color: #969696; font-size: 0.875em;}
.play .right-content .right-arrow>span{position:absolute;top: 50%;left:0;width:19px;margin-top:-10px;text-align:center}
.play .tab-content { width: 100%; position: absolute; top: 50px; bottom: 0px;}
.play .tab-content .tab-pane { display: none;}
.play .tab-content .tab-pane.current { display: block; height: 100%; overflow: auto;}
.play .control-panel { height: 100%; position: relative;}
.play .control-panel ul { margin: 0px; padding: 0px; line-height: 1.6; overflow: hidden;}
.play .control-panel ul.nav-tabs li { float: left; text-align: center; list-style: none; }
.play .control-panel ul.nav-tabs li a { text-decoration: none; }
.play .control-panel .play-nav{padding:15px 0; border-bottom: 0;background-color:#232323;}
.play .play-nav li{ width: 50%;padding:0; font-size:16px; }
.play .play-nav li.current:after{display:none;}
.play .play-nav li a{ color: #909090; border:none;}
.play .play-nav li:first-child a{border: 0;}
.play .play-nav li.current a{ color: #b49441;}
.play .play-nav li.current:after{ width:75px; height:3px; margin: 0 0 0 -37px;}
.play .play-nav .videoJy{border-left:1px solid #3f3f3f;}
.play .control-panel span { padding: 8px 14px; color: #909090; background: #242424; display: block; padding: 14px 22px;}
.play .control-panel .knob-list li { position: relative; padding: 0 0px 0 23px;}
.play .control-panel .knob-list li a { color: #909090; font-size: 0.875em; padding: 16px 35px 16px 20px; text-decoration: none; display: block; cursor: pointer;}
.play .control-panel .knob-item.current { background: #232323;}
.play .control-panel .knob-item.current a {color:#b49441;}
.play .control-panel .knob-item:after { content: ""; position: absolute; left: 22px; top: 0; width: 1px; height: 100px; background: #616161; z-index: 5 }
.play .control-panel .knob-item:before {content: ""; width: 18px; height: 18px; border-radius:50%; border:2px solid #5b5b5b; background: #5b5b5b; position: absolute; left: 13px; top: 18px; content: ""; display: block; z-index: 10;}
.play .control-panel .knob-item .icon-pro{top:16px;left:23px;z-index:15;}
.play .control-panel .four-four .icon-pro{left:13px;}
.play .control-panel .jiangyi-list { line-height: 0;}
.play .control-panel .jiangyi-list div { cursor: pointer; padding: 8px 16px;}
.play .control-panel .jiangyi-list div.current { background: #888;}
.play .control-panel .jy-list img { width: 100%;}
.play #player p{color:#fff;text-align:center;padding:50px 0;}
.play #player p a{color:#b01c40;text-decoration:underline;}
.play .play-chapter-progress{position:absolute;top:16px;left:13px;z-index:20;display:none\0;}
.play .play-chapter-progress .circle-progress:after{background-color:#5b5b5b;}
.play .play-chapter-progress .circle-progress{background-color:#5b5b5b;}
.play .right-content .chapter-item:last-child{border-bottom:none;margin-top:10px;}
.play .right-content .chapter-item .cpt{color:#b0b0b0;padding:10px 22px;background-color:#2f2f2f;}
.play .right-content .chapter-item-active a{text-decoration:none;}
.play .right-content .chapter-item-active .cpt{color:#b49441;}
/* .icon-play-chapter{display:inline-block;vertical-align:middle;width:14px;height:14px;background:url(../img/icon-play-chapter.png) no-repeat;position:absolute;right:13px;top:19px;} */
.play .icon-play-chapter { position: absolute; font-size: 16px; right: 10px; top: 50%; -webkit-transform: translateY(-50%); -moz-transform: translateY(-50%); -o-transform: translateY(-50%); -ms-transform: translateY(-50%); transform: translateY(-50%); }
.play .icon-play-video{background-position:0 0;}
.play .icon-play-work{background-position:0 -37px;}
.play .icon-play-exam{background-position:0 -73px;}
.play .icon-play-read{background-position:0 -110px;}
/*作业类页*/ /*作业类页*/
.play .play-paper{position:absolute;top:0;bottom:0;left:0;right:0;overflow:auto;background-color:#e5e5e5;} .play .play-paper{position:absolute;top:0;bottom:0;left:0;right:0;overflow:auto;background-color:#e5e5e5;}
...@@ -145,11 +93,14 @@ ...@@ -145,11 +93,14 @@
.play .play-chapter-exam .answer{position:absolute;right:25px;bottom:10px;} .play .play-chapter-exam .answer{position:absolute;right:25px;bottom:10px;}
.play .play-chapter-exam .result{float:right;margin-top:-20px;margin-right:-20px;font-size:16px;font-weight:bold;} .play .play-chapter-exam .result{float:right;margin-top:-20px;margin-right:-20px;font-size:16px;font-weight:bold;}
.play dd, .play dl, .play dt, .play li, .play ol, .play ul { /* 统一默认 common样式 */
margin: 0; .play dd,
padding: 0; .play dl,
list-style: none; .play dt,
} .play li,
.play ol,
.play ul { margin: 0; padding: 0; list-style: none; }
.play .edit_html p {padding: 0; margin: 0; margin-bottom: 15px;} .play .edit_html p {padding: 0; margin: 0; margin-bottom: 15px;}
.play .play-paper-step{font-weight:bold;font-size:16px;margin:30px -20px 15px;padding-bottom:10px;border-bottom:1px dashed #cecece;} .play .play-paper-step{font-weight:bold;font-size:16px;margin:30px -20px 15px;padding-bottom:10px;border-bottom:1px dashed #cecece;}
...@@ -160,6 +111,12 @@ ...@@ -160,6 +111,12 @@
padding: 160px 0; padding: 160px 0;
text-align: center; text-align: center;
} }
/* 视频播放样式 */
.play .hide { display: none; }
.play .play-video { float: left }
.play .play-jiangyi { float: left }
/* 视频播放 是 flash */
@media (max-width:768px){ @media (max-width:768px){
.play .left-content {min-width:0;right:0;} .play .left-content {min-width:0;right:0;}
...@@ -170,3 +127,10 @@ ...@@ -170,3 +127,10 @@
.play .play-paper-body{ padding: 15px 25px 25px; } .play .play-paper-body{ padding: 15px 25px 25px; }
.play .play-paper-step{ margin: 30px 0 15px; } .play .play-paper-step{ margin: 30px 0 15px; }
} }
/* OK 样式 */
.exam .q-group .q-title p { padding: 0; margin: 0; }
.exam .el-radio + .el-radio { margin-left: 0; }
.exam .el-radio__input.is-disabled + span.el-radio__label { color: inherit; }
.exam .el-checkbox + .el-checkbox { margin-left: 0; }
.exam .el-checkbox__input.is-disabled + span.el-checkbox__label { color: inherit; }
...@@ -7,23 +7,24 @@ ...@@ -7,23 +7,24 @@
</div> </div>
<div class="play-content"> <div class="play-content">
<router-view <router-view
ref="comTotalChapter"
:chapterName="curChapterName" :chapterName="curChapterName"
:chapterId="chapterId" :chapterId="chapterId"
:courseInfo="courseInfo" :courseInfo="courseInfo"
:courseWork="courseWork" :courseWork="courseWork"
:chapterRead="chapterRead" :chapterRead="chapterRead"
:chapterWork="chapterWork" :chapterWork="chapterWork"
:chapterExam="chapterExam"
:chapterVideo="chapterVideo"
:ppts="chapterPpts"
@handlePlayTime="handlePlayTime"
@updateProgress="updateProgress"
></router-view> ></router-view>
<!-- {params.chapterId === 'work' ? <!-- {params.chapterId === 'work' ?
: chapter.type === chapterType.VIDEO ? : chapter.type === chapterType.VIDEO ?
<ChapterVideo <ChapterVideo
ref="chapterVideo" ref="chapterVideo"
courseId={params.courseId}
semesterId={course.semester_id}
ppts={ppts}
chapter={chapter}
username={user.data && user.data.username || ''}
prevChapterId={prevChapterId} prevChapterId={prevChapterId}
nextChapterId={nextChapterId} nextChapterId={nextChapterId}
curProgress={curProgress} curProgress={curProgress}
...@@ -34,31 +35,30 @@ ...@@ -34,31 +35,30 @@
} --> } -->
</div> </div>
</div> </div>
<div class="right-content" :style="{ right: (state.sideBar ? 0 : -388) + 'px' }">
<p class="right-arrow" @click="changeSideBar('')"><span>&gt;</span></p> <div class="right-ctrl" :style="{ right: (state.sideBar ? 0 : -388) + 'px' }">
<div class="control-panel"> <p class="ctrl-arrow" @click="changeSideBar('')"><span>&gt;</span></p>
<ul class="nav-tabs play-nav cl"> <div class="ctrl-pl">
<li :class="['videoChapter', (state.sideBar === SIDEBAR_CHAPTER ? 'current' : '')]"><a :href="('#' + SIDEBAR_CHAPTER)" @click="changeSideBar(SIDEBAR_CHAPTER)">章节</a></li> <ul class="pl-tab-hd">
<li :class="[(state.sideBar === SIDEBAR_CHAPTER ? 'on' : '')]"><a :href="('#' + SIDEBAR_CHAPTER)" @click="changeSideBar(SIDEBAR_CHAPTER)">章节</a></li>
<template v-if="state.isChapterVideo"> <template v-if="state.isChapterVideo">
<li :class="['videoJy', (state.sideBar === SIDEBAR_PPT ? 'current' : '')]"><a :href="('#' + SIDEBAR_PPT)" @click="changeSideBar(SIDEBAR_PPT)">讲义</a></li> <li :class="['br-l-line', (state.sideBar === SIDEBAR_PPT ? 'on' : '')]"><a :href="('#' + SIDEBAR_PPT)" @click="changeSideBar(SIDEBAR_PPT)">讲义</a></li>
</template> </template>
</ul> </ul>
<div class="tab-content"> <div class="pl-tab-bd">
<template v-if="state.sideBar === SIDEBAR_CHAPTER"> <template v-if="state.sideBar === SIDEBAR_CHAPTER">
<side-chapter-list <side-chapter-list
:list="chapterList" :list="chapterList"
:sid="sid" :sid="sid"
:cid="cid" :cid="cid"
/> />
<!--
handleClickItem={IS_PHONE ? this.handleHideSidebar1 : this.reloadPages} -->
</template> </template>
<template v-if="state.sideBar === SIDEBAR_PPT"> <template v-if="state.sideBar === SIDEBAR_PPT">
<side-chapter-ppt <side-chapter-ppt
ref="sidePpt"
:ppt="pptList" :ppt="pptList"
@handleClickPpt="handleClickSidePpt"
/> />
<!-- ref="sidePpt"
handleClickPpt={this.handleClickSidePpt} -->
</template> </template>
</div> </div>
</div> </div>
...@@ -82,6 +82,7 @@ ...@@ -82,6 +82,7 @@
<script> <script>
import cAction from '@actions' import cAction from '@actions'
import cTool from '@tools'
import sideChapterList from './rightSide/sideChapterList.vue' import sideChapterList from './rightSide/sideChapterList.vue'
import sideChapterPpt from './rightSide/sideChapterPpt.vue' import sideChapterPpt from './rightSide/sideChapterPpt.vue'
...@@ -143,7 +144,12 @@ export default { ...@@ -143,7 +144,12 @@ export default {
/* 章节阅读 */ /* 章节阅读 */
chapterRead: {}, chapterRead: {},
/* 章节问题、作业提交 */ /* 章节问题、作业提交 */
chapterWork: {} chapterWork: {},
/* 章节测试 */
chapterExam: {},
/* 章节视频 */
chapterVideo: {},
chapterPpts: []
} }
}, },
beforeRouteUpdate (to, from, next) { beforeRouteUpdate (to, from, next) {
...@@ -153,7 +159,10 @@ export default { ...@@ -153,7 +159,10 @@ export default {
const loading = this.$loading({ lock: true, text: '', spinner: '', background: 'rgba(255, 255, 255, 0.9)' }) const loading = this.$loading({ lock: true, text: '', spinner: '', background: 'rgba(255, 255, 255, 0.9)' })
cAction.chapterAction.getCurrentChapterDetail(to.params.id).then(json => { cAction.chapterAction.getCurrentChapterDetail(to.params.id).then(json => {
this.pptList = json.image this.pptList = json.image
this.chapterPpts = json.rData.ppts
}).catch(e => { this.$message.error(e.message) }).finally(() => { loading.close() }) }).catch(e => { this.$message.error(e.message) }).finally(() => { loading.close() })
} else {
this.state.isChapterVideo = false
} }
const loading = this.$loading({ lock: true, text: '', spinner: '', background: 'rgba(255, 255, 255, 0.9)' }) const loading = this.$loading({ lock: true, text: '', spinner: '', background: 'rgba(255, 255, 255, 0.9)' })
cAction.chapterAction.getChapterList(to.params.cid, to.params.sid, to.params.id).then(json => { cAction.chapterAction.getChapterList(to.params.cid, to.params.sid, to.params.id).then(json => {
...@@ -163,7 +172,15 @@ export default { ...@@ -163,7 +172,15 @@ export default {
/* 章节选择 */ /* 章节选择 */
this.curChapterName = json.curJson.name this.curChapterName = json.curJson.name
this.chapterId = json.curJson.chapterId this.chapterId = json.curJson.chapterId
/* 如果 是 视频 再调用 进度信息接口 */
if (json.curJson.type === 2) {
cAction.chapterAction.getProgress(this.id, cTool.other.getIdt(), this.sid).then(_d => {
this.chapterVideo = ((json.curJson.type === 2) && json.curJson.chapterVideo) || {}
this.chapterVideo.progress = _d
}).catch(e => { this.$message.error(e.message) })
}
this.chapterRead = ((json.curJson.type === 4) && json.curJson.chapterRead) || {} this.chapterRead = ((json.curJson.type === 4) && json.curJson.chapterRead) || {}
this.chapterExam = ((json.curJson.type === 3 && json.curJson.work_type === 1) && json.curJson.homework) || {}
this.chapterWork = ((json.curJson.type === 3 && json.curJson.work_type === 2) && json.curJson.chapterWork) || {} this.chapterWork = ((json.curJson.type === 3 && json.curJson.work_type === 2) && json.curJson.chapterWork) || {}
}).catch(e => { this.$message.error(e.message) }).finally(() => { loading.close() }) }).catch(e => { this.$message.error(e.message) }).finally(() => { loading.close() })
next() next()
...@@ -175,6 +192,7 @@ export default { ...@@ -175,6 +192,7 @@ export default {
const loading = this.$loading({ lock: true, text: '', spinner: '', background: 'rgba(255, 255, 255, 0.9)' }) const loading = this.$loading({ lock: true, text: '', spinner: '', background: 'rgba(255, 255, 255, 0.9)' })
cAction.chapterAction.getCurrentChapterDetail(this.id).then(json => { cAction.chapterAction.getCurrentChapterDetail(this.id).then(json => {
this.pptList = json.image this.pptList = json.image
this.chapterPpts = json.rData.ppts
}).catch(e => { this.$message.error(e.message) }).finally(() => { loading.close() }) }).catch(e => { this.$message.error(e.message) }).finally(() => { loading.close() })
} }
const loading = this.$loading({ lock: true, text: '', spinner: '', background: 'rgba(255, 255, 255, 0.9)' }) const loading = this.$loading({ lock: true, text: '', spinner: '', background: 'rgba(255, 255, 255, 0.9)' })
...@@ -185,12 +203,49 @@ export default { ...@@ -185,12 +203,49 @@ export default {
/* 章节选择 */ /* 章节选择 */
this.curChapterName = json.curJson.name this.curChapterName = json.curJson.name
this.chapterId = json.curJson.chapterId this.chapterId = json.curJson.chapterId
/* 如果 是 视频 再调用 进度信息接口 */
if (json.curJson.type === 2) {
cAction.chapterAction.getProgress(this.id, cTool.other.getIdt(), this.sid).then(_d => {
this.chapterVideo = ((json.curJson.type === 2) && json.curJson.chapterVideo) || {}
this.chapterVideo.progress = _d
}).catch(e => { this.$message.error(e.message) })
}
this.chapterRead = ((json.curJson.type === 4) && json.curJson.chapterRead) || {} this.chapterRead = ((json.curJson.type === 4) && json.curJson.chapterRead) || {}
this.chapterExam = ((json.curJson.type === 3 && json.curJson.work_type === 1) && json.curJson.homework) || {}
this.chapterWork = ((json.curJson.type === 3 && json.curJson.work_type === 2) && json.curJson.chapterWork) || {} this.chapterWork = ((json.curJson.type === 3 && json.curJson.work_type === 2) && json.curJson.chapterWork) || {}
}).catch(e => { this.$message.error(e.message) }).finally(() => { loading.close() }) }).catch(e => { this.$message.error(e.message) }).finally(() => { loading.close() })
}, },
methods: { methods: {
changeSideBar (str) { this.state.sideBar = str } changeSideBar (str) { this.state.sideBar = str },
// 视频播放时间变更调用此方法
handlePlayTime (time) {
// 侧边ppt对应位置
if (this.$refs.sidePpt) {
this.$refs.sidePpt.setIndexByPoint(time)
}
},
/* 通过 右侧 sider ppt 控制 左侧视频播放位置 */
handleClickSidePpt (index) {
const ppts = this.chapterPpts || []
if (ppts.length > index && this.$refs.comTotalChapter) {
const ppt = ppts[index]
this.$refs.comTotalChapter.setVideoTime(ppt.ppt_point)
}
},
updateProgress (_rProgress) {
if (_rProgress.cpt) {
cAction.chapterAction.updateProgress({
d: cTool.other.getIdt(),
i: cTool.other.getIdt(),
c: this.cid,
s: this.sid,
v: this.id,
_p: parseInt(_rProgress.pt), // 累计时间
_m: parseInt(_rProgress.mpt), // 当前播放最大时间
_c: parseInt(_rProgress.cpt) // 当前播放位置
}).then(json => {}).catch(e => { this.$message.error(e.message) }).finally(() => { })
}
}
} }
} }
</script> </script>
...@@ -198,4 +253,77 @@ export default { ...@@ -198,4 +253,77 @@ export default {
<style lang="scss" scoped> <style lang="scss" scoped>
@import './index.css'; @import './index.css';
/* 右侧面板结构 */
.right-ctrl {
position: absolute;
top: 0;
bottom: 0;
right: 0;
z-index: 200;
width: 350px;
background: #212121;
border-left: 19px solid #1b1b1b;
/* 箭头 */
.ctrl-arrow {
position: absolute;
top: 0;
left: -19px;
bottom: 0;
width: 19px;
height: 100%;
font-size: 14px;
color: #969696;
cursor: pointer;
span {
position: absolute;
top: 50%;
left: 0;
width: 19px;
margin-top: -10px;
text-align: center;
color: #fff;
}
}
/* 面板 */
.ctrl-pl {
position: relative;
height: 100%;
/* 头部 - 选择项 */
.pl-tab-hd {
margin: 0;
padding: 15px 0;
line-height: 1.6;
background-color: #232323;
overflow: hidden;
li {
float: left;
position: relative;
width: 50%;
padding: 0;
font-size: 16px;
color: #909090;
text-align: center;
list-style: none;
cursor: pointer;
a {
color: #909090;
text-decoration: none;
}
&.on {
a {
color: #b49441;
}
}
}
.br-l-line {
border-left: 1px solid #3f3f3f;
}
}
/* 内部 - 列表项 */
.pl-tab-bd {
height: 94%;
}
}
}
</style> </style>
<template> <template>
<div class="tab-pane current"> <div class="tab-pane">
<ul class="under-control chapter-list current"> <ul class="chapter-list">
<template v-for="(item, index) in list.course"> <template v-for="(item, index) in list.course">
<li v-bind:key="index" class="chapter-item"> <li v-bind:key="index" class="chapter-item">
<span class="cpt">{{item.title}}</span> <h4>{{item.title}}</h4>
<div class="knob-list-wrap"> <ul class="knot-list">
<ul class="knob-list"> <template v-for="(_item, _index) in item.chapters">
<template v-for="(_item, _index) in item.chapters"> <li v-bind:key="_index" :class="['knob-item', (_item.id === list.currentChapterId ? 'on' : '')]">
<li v-bind:key="_index" :class="['knob-item', (_item.id === list.currentChapterId ? 'current' : '')]"> <a :data-vid="_item.id" :data-hasVA='_item.time' @click='jumpToOtherVA' :data-index='index' :data-index1='_index' class="knot-name">{{_item.name}}</a>
<!-- <ProgressCircle percent={prog} className="play-chapter-progress"/> --> <i :class="['el-icon', (_item.time ? 'el-icon-self-iconset0481' : (_item.type === 3 ? 'el-icon-edit-outline' : 'el-icon-self-cc-book'))]"></i>
<!-- <i className="icon icon-pro" title={`学习进度 ${prog}%`}></i> --> </li>
<!-- {(type == 'work') ? </template>
<Link to={`/courses/${semesterId}/${courseId}/chapters/${o.id}`} className="knob-name" onClick={this.reloadPages}>{o.name}</Link> </ul>
:
<Link to={`/courses/${semesterId}/${courseId}/chapters/${o.id}`} className="knob-name" onClick={this.props.handleClickItem}>{o.name}</Link>
} -->
<a :data-vid="_item.id" :data-hasVA='_item.time' class="knob-name" @click='jumpToOtherVA' :data-index='index' :data-index1='_index'>{{_item.name}}</a>
<i :class="['icon-play-chapter', (_item.time ? 'el-icon-self-iconset0481' : (_item.type === 3 ? 'el-icon-edit-outline' : 'el-icon-self-cc-book'))]"></i>
</li>
</template>
</ul>
</div>
</li> </li>
</template> </template>
</ul> </ul>
...@@ -74,3 +65,84 @@ export default { ...@@ -74,3 +65,84 @@ export default {
} }
} }
</script> </script>
<style lang="scss" scoped>
.tab-pane {
display: block;
height: 100%;
overflow: auto;
/* 章列表样式 */
.chapter-list {
margin: 0;
padding: 0;
line-height: 1.6;
overflow: hidden;
.chapter-item {
h4 {
padding: 10px 22px;
margin: 0;
font-size: 15px;
color: #b0b0b0;
background-color: #2f2f2f;
}
/* 节列表样式 */
.knot-list {
margin: 0;
padding: 0;
line-height: 1.6;
overflow: hidden;
li {
position: relative;
&.on, &:hover {
background: #3c3c3c;
a {
color: #b49441;
}
}
&:before {
display: block;
content: "";
position: absolute;
left: 13px;
top: 16px;
z-index: 10;
width: 18px;
height: 18px;
background: #5b5b5b;
border: 2px solid #5b5b5b;
border-radius: 50%;
}
&:after {
display: block;
content: "";
position: absolute;
left: 22px;
top: 0;
z-index: 5;
width: 1px;
height: 100px;
background: #616161;
}
}
.knot-name {
display: block;
padding: 15px 35px 15px 40px;
font-size: 14px;
color: #909090;
text-decoration: none;
cursor: pointer;
}
}
/* 章节后面小图标的样式 */
.el-icon {
position: absolute;
font-size: 16px;
right: 10px;
top: 50%;
transform: translateY(-50%);
}
}
}
}
</style>
<template> <template>
<div class="tab-pane current"> <div class="tab-pane">
<div class="jiangyi-list"> <ul class="lecture-list">
<div class="jy-list"> <template v-for="(item, index) in ppt.imgUrls">
<template v-for="(item, index) in ppt.imgUrls"> <li v-bind:key="index" @click="onClickPpt" :data-index="index" :class="[(index === ppt.selectIndex ? 'on' : '')]">
<div v-bind:key="index" onClick={this.onClickPpt} :data-index="index" :class="[(index === ppt.selectIndex ? 'current' : '')]"> <img :src="item" alt=""/>
<img :src="item" alt=""/> </li>
</div> </template>
</template> </ul>
<template v-if="!ppt.imgUrls.length">
<div className="no-data">暂无讲义</div>
</template>
</div>
</div>
</div> </div>
</template> </template>
...@@ -19,6 +14,55 @@ ...@@ -19,6 +14,55 @@
export default { export default {
props: { props: {
ppt: { type: Object, require: false } ppt: { type: Object, require: false }
},
methods: {
// 根据播放时间同步展示ppt
// @param time 播放时间
setIndexByPoint (time) {
const ppts = this.ppt.imgUrls || []
const len = ppts.length
let i = 0
for (; i < len; i++) {
if (time < this.ppt.timeArr[i]) {
break
}
}
if (this.ppt.selectIndex !== i - 1) {
this.ppt.selectIndex = i - 1
}
},
// 点击ppt跳转对应的播放时间
// 点击某个ppt
onClickPpt (e) {
let toIndex = e.currentTarget.dataset.index - 0
if (this.ppt.selectIndex === toIndex) { return }
this.ppt.selectIndex = toIndex
this.$emit('handleClickPpt', toIndex)
}
} }
} }
</script> </script>
<style lang="scss" scoped>
.tab-pane {
display: block;
height: 100%;
overflow: auto;
/* 讲义列表样式 */
.lecture-list {
padding: 8px 16px;
li {
padding: 8px 16px;
cursor: pointer;
list-style: none;
&.on {
background: #888;
}
img {
width: 100%;
}
}
}
}
</style>
...@@ -19,21 +19,31 @@ export default class ChapterAPI extends BaseAPI { ...@@ -19,21 +19,31 @@ export default class ChapterAPI extends BaseAPI {
* @param {[string]} did * @param {[string]} did
* @param {[string]} sid * @param {[string]} sid
*/ */
getProgress = (vid, did, sid) => this.get(`/v2/education/video/${sid}/${vid}/device`, { device_id: did }) getProgress = (vid, did, sid) => this.get(`/tenant/v2/education/video/${sid}/${vid}/device`, { device_id: did })
/** /**
* 提交进度信息 * 提交进度信息
* @param {[object]} obj * @param {[object]} obj
* d: obj.did,
i: obj.did,
c: obj.cid,
s: obj.sid,
v: obj.vid,
_p: obj.pt, // 累计时间
_m: obj.mpt, // 当前播放最大时间
_c: obj.cpt // 当前播放位置
*/ */
updateProgress = (obj = {}) => this.post('/v2/analytics/upload-video', { updateProgress = (obj = {}) => this.get('/tenant/v2/analytics/upload-video', obj)
d: obj.did, /**
i: obj.did, * 获取试题信息
c: obj.cid, * @param {[string]} eid
s: obj.sid, * @param {[string]} sid
v: obj.vid, */
_p: obj.pt, // 累计时间 getExamDetail = (sid, eid) => this.get(`/tenant/v2/education/homeworks/${sid}/${eid}`, {})
_m: obj.mpt, // 当前播放最大时间 /**
_c: obj.cpt // 当前播放位置 * 提交考试信息
}) * @param {[object]} param
*/
submitExamDetail = (param) => this.post(`/tenant/v2/education/homeworks`, param)
/** /**
* 获取对应 作业或问题 回答 * 获取对应 作业或问题 回答
* @param {[string]} sid * @param {[string]} sid
......
...@@ -23,17 +23,6 @@ export default class CourseAPI extends BaseAPI { ...@@ -23,17 +23,6 @@ export default class CourseAPI extends BaseAPI {
* @param {[string]} sid * @param {[string]} sid
*/ */
getCourseAssess = (cid, sid) => this.get(`/tenant/v2/analytics/courses/${sid}/${cid}/evaluation`, {}) getCourseAssess = (cid, sid) => this.get(`/tenant/v2/analytics/courses/${sid}/${cid}/evaluation`, {})
/**
* 获取试题信息
* @param {[string]} eid
* @param {[string]} sid
*/
getExamDetail = (eid, sid) => this.get(`/tenant/v2/education/homeworks/${sid}/${eid}`, {})
/**
* 提交考试信息
* @param {[object]} param
*/
submitExamDetail = (param) => this.post(`/tenant/v2/education/homeworks`, param)
/** /**
* 选课 * 选课
* @param {[string]} cid * @param {[string]} cid
......
import Cookies from './cookies' import Cookies from './cookies'
import ConvertTime from './convert_time' import ConvertTime from './convert_time'
import Other from './other'
let cookies = new Cookies() let cookies = new Cookies()
let convertTime = new ConvertTime() let convertTime = new ConvertTime()
let other = new Other()
export default { export default {
cookies, cookies,
convertTime convertTime,
other
} }
export default class Other {
/**
* 获取idt
*/
getIdt () {
let cookieName = '_idt'
// 尝试从cookie获取
let idt = /_idt=/.test(document.cookie) && document.cookie.replace(/.*_idt=([^;]+).*/, '$1')
if (!idt) {
idt = (new Date()).getTime().toString(36) + Math.random().toString(32) + Math.random().toString(32)
let d = new Date()
d.setMonth(d.getMonth() + 6)
document.cookie = cookieName + '=' + idt + ';path=/;domain=.ezijing.com;expires=' + d.toGMTString()
}
return idt
}
}
...@@ -65,6 +65,10 @@ export default class ChapterActon { ...@@ -65,6 +65,10 @@ export default class ChapterActon {
} }
let _chapterWork = (__.type === 3 && __.homework && __.homework.work_type === 2 && __.homework) || '' let _chapterWork = (__.type === 3 && __.homework && __.homework.work_type === 2 && __.homework) || ''
let _chapterRead = (__.type === 4 && __.reading) || '' let _chapterRead = (__.type === 4 && __.reading) || ''
let _chapterVideo = (__.type === 2 && __.video) || ''
if (_chapterVideo) {
_chapterVideo.pdf = __.pdf || ''
}
if (_id === __.resource_id) { if (_id === __.resource_id) {
curJson = { curJson = {
id: __.resource_id, id: __.resource_id,
...@@ -75,7 +79,8 @@ export default class ChapterActon { ...@@ -75,7 +79,8 @@ export default class ChapterActon {
work_type: (__.homework && __.homework.work_type) || '', work_type: (__.homework && __.homework.work_type) || '',
homework: _homework, homework: _homework,
chapterRead: _chapterRead, chapterRead: _chapterRead,
chapterWork: _chapterWork chapterWork: _chapterWork,
chapterVideo: _chapterVideo
} }
} }
return { return {
...@@ -86,7 +91,8 @@ export default class ChapterActon { ...@@ -86,7 +91,8 @@ export default class ChapterActon {
work_type: (__.homework && __.homework.work_type) || '', work_type: (__.homework && __.homework.work_type) || '',
homework: _homework, homework: _homework,
chapterRead: _chapterRead, chapterRead: _chapterRead,
chapterWork: _chapterWork chapterWork: _chapterWork,
chapterVideo: _chapterVideo
} }
}) })
} }
...@@ -133,23 +139,38 @@ export default class ChapterActon { ...@@ -133,23 +139,38 @@ export default class ChapterActon {
current: 0, current: 0,
selectIndex: 0, selectIndex: 0,
timeArr: (_res.ppts && _res.ppts.map(function (_, i) { return _.ppt_point })) || [] timeArr: (_res.ppts && _res.ppts.map(function (_, i) { return _.ppt_point })) || []
} },
rData: _res
} }
// callback(json) // 可以不使用callback 因为使用then // callback(json) // 可以不使用callback 因为使用then
return json return json
}) })
} }
/* 获取进度信息 */ /* 获取进度信息 */
getProgress (vid, did, sid, callback) { getProgress (vid, did, sid) {
return chapterApi.getProgress(vid, did, sid).then(res => { return chapterApi.getProgress(vid, did, sid).then(res => {
// callback(res.data) // 可以不使用callback 因为使用then // callback(res) // 可以不使用callback 因为使用then
return res.data return res
}) })
} }
/* 提交进度信息 */ /* 提交进度信息 */
updateProgress (obj) { updateProgress (obj) {
return chapterApi.updateProgress(obj).then(res => res) return chapterApi.updateProgress(obj).then(res => res)
} }
/* 获取试题信息 */
getExamDetail (eid, sid) {
return chapterApi.getExamDetail(eid, sid).then(res => {
// callback(res) // 可以不使用callback 因为使用then
return res
})
}
/* 提交考试信息 */
submitExamDetail (param) {
return chapterApi.submitExamDetail(param).then(res => {
// callback(res) // 可以不使用callback 因为使用then
return res
})
}
/* 获取对应 作业或问题 回答 */ /* 获取对应 作业或问题 回答 */
getHomework (sid, id) { getHomework (sid, id) {
return chapterApi.getHomework(sid, id).then(res => res) return chapterApi.getHomework(sid, id).then(res => res)
......
...@@ -194,20 +194,6 @@ export default class CourseAction { ...@@ -194,20 +194,6 @@ export default class CourseAction {
return json return json
}) })
} }
/* 获取试题信息 */
getExamDetail (eid, sid) {
return courseApi.getExamDetail(eid, sid).then(res => {
// callback(res) // 可以不使用callback 因为使用then
return res
})
}
/* 提交考试信息 */
submitExamDetail (param) {
return courseApi.submitExamDetail(param).then(res => {
// callback(res) // 可以不使用callback 因为使用then
return res
})
}
/* 选课 */ /* 选课 */
selectCourse (cid, sid) { selectCourse (cid, sid) {
return courseApi.selectCourse(cid, sid).then(res => { return courseApi.selectCourse(cid, sid).then(res => {
......
...@@ -10,5 +10,6 @@ ...@@ -10,5 +10,6 @@
<body> <body>
<div id="app"></div> <div id="app"></div>
<script type="text/javascript" src="/static/ckeditor/ckeditor.js"></script> <script type="text/javascript" src="/static/ckeditor/ckeditor.js"></script>
<script type="text/javascript" src="/static/videoJs/videoJs.js"></script>
</body> </body>
</html> </html>
...@@ -286,7 +286,7 @@ export default { ...@@ -286,7 +286,7 @@ export default {
}, { }, {
title: '课程考核', title: '课程考核',
isShow: false, isShow: false,
richText: "<div class='h1'>一、最终成绩计算</div> <div class='p'>课程表现得分*30%+每章试题得分*30%+结业大作业得分*40%=该门课程总得分,满分100分,低于60分为不及格,需重修此门课程。</div> <img class='b1' src='https://e-learning.ezijing.com/static/assets/img/course-check.png' mode='aspectFill' /> <div class='h1'>二、具体细则</div> <div class='h2'>课程表现:总分100分(占科目总成绩的30%)</div> <div class='p'>1、每个视频观看完成度50分:以后台数据统计为准,全部看完视频满分50分,其它酌情给分。</div> <div class='em'>注:视频观看考核的是实际播放时长,不是进度条的显示状态,进度条满格并不一定表示观看完这个视频。以下方每日学习时长为准。</div> <div class='p'>2、课程讨论及反馈建设性问题50分:课程讨论以后台数据为准,反馈问题以教务老师问题统计为准,满分50分,酌情给分。</div> <div class='h2'>每章试题:总分100分(占科目总成绩的30%)</div> <div class='p'>本课程所有试题的平均正确率*100分*占总成绩30%=此项得分。(比如正确度为80%,则此项得分:80%*100*30%=24分)</div> <div class='h2'>结业大作业:总分100分(占科目总成绩的40%)</div> <div class='p'>结业大作业满分为100分,以助教老师给分为准。</div>", richText: "<div class='h1'>一、最终成绩计算</div> <div class='p'>课程表现得分*30%+每章试题得分*30%+结业大作业得分*40%=该门课程总得分,满分100分,低于60分为不及格,需重修此门课程。</div> <img class='b1' src='https://e-learning.ezijing.com/static/assets/img/course-check.png' mode='aspectFill' /> <div class='h1'>二、具体细则</div> <div class='h2'>课程表现:总分100分(占科目总成绩的30%)</div> <div class='p'>1、每个视频观看完成度50分:以后台数据统计为准,全部看完视频满分50分,其它酌情给分。</div> <div class='em'>注:视频观看考核的是实际播放时长,不是进度条的显示状态,进度条满格并不一定表示观看完这个视频。以下方每日学习时长为准。</div> <div class='p'>2、课程反馈建设性问题及课程完成情况50分:反馈问题以教务老师统计为准,课程完成情况以后台数据为准,是否按时完成视频观看、测试、作业提交,满分50分,酌情给分。</div> <div class='h2'>每章试题:总分100分(占科目总成绩的30%)</div> <div class='p'>本课程所有试题的平均正确率*100分*占总成绩30%=此项得分。(比如正确度为80%,则此项得分:80%*100*30%=24分)</div> <div class='h2'>结业大作业:总分100分(占科目总成绩的40%)</div> <div class='p'>结业大作业满分为100分,以助教老师给分为准。</div>",
assess: { assess: {
score: '20', score: '20',
duration: '00:01:20', duration: '00:01:20',
......
<template> <template>
<div class="right-content"> <div class="right-ctrl">
<p class="right-arrow"> <p class="ctrl-arrow">
<i class="el-icon-arrow-right"></i> <i class="el-icon-arrow-right"></i>
</p> </p>
<div class="control-panel"> <div class="ctrl-pl">
<ul class="tab-nav cl"> <ul class="pl-tab-hd">
<li class="video-chapter current">章节</li> <li class="on">章节</li>
<li class="video-jy">讲义</li> <li class="br-l-line">讲义</li>
</ul> </ul>
<div class="tab-content"> <div class="pl-tab-bd">
<div class="tab-pane"> <div class="tab-pane">
<ul class="chapter-list"> <ul class="chapter-list">
<li class="chapter-item"> <li class="chapter-item">
<h4>第一章 战略管理与竞争力</h4> <h4>第一章 战略管理与竞争力</h4>
<ul class="knob-list"> <ul class="knot-list">
<li class="knob-item current"> <li class="on">
<div class="knob-progress"></div> <a class="knot-name" href="">1.1 战略管理概述</a>
<i class="icon icon-pro" title="学习进度 5%"></i>
<a class="knob-name" href="">1.1 战略管理概述</a>
<i class="el-icon el-icon-tickets"></i> <i class="el-icon el-icon-tickets"></i>
</li> </li>
<li class="knob-item"> <li>
<div class="knob-progress"></div> <a class="knot-name" href="">战略管理第一章课后试题</a>
<i class="icon icon-pro" title="学习进度 5%"></i>
<a class="knob-name" href="">战略管理第一章课后试题</a>
<i class="el-icon el-icon-menu"></i> <i class="el-icon el-icon-menu"></i>
</li> </li>
</ul> </ul>
</li> </li>
</ul> </ul>
<ul class="jiangyi-list"> <ul class="lecture-list">
<li class="current"> <li class="on">
<img src="https://img1.ezijing.com/curriculum/ppt/958dbedd5201f69068d8fe61e907d4dc.jpg" alt="" /> <img src="https://img1.ezijing.com/curriculum/ppt/958dbedd5201f69068d8fe61e907d4dc.jpg" alt="" />
</li> </li>
<li> <li>
...@@ -44,7 +40,7 @@ ...@@ -44,7 +40,7 @@
</template> </template>
<style lang="scss" scoped> <style lang="scss" scoped>
.right-content { .right-ctrl {
position: absolute; position: absolute;
top: 0; top: 0;
bottom: 0; bottom: 0;
...@@ -55,7 +51,7 @@ ...@@ -55,7 +51,7 @@
border-left: 19px solid #1b1b1b; border-left: 19px solid #1b1b1b;
/* 箭头 */ /* 箭头 */
.right-arrow { .ctrl-arrow {
position: absolute; position: absolute;
top: 0; top: 0;
left: -19px; left: -19px;
...@@ -75,16 +71,15 @@ ...@@ -75,16 +71,15 @@
color: #fff; color: #fff;
} }
} }
.control-panel { /* 面板 */
.ctrl-pl {
position: relative; position: relative;
height: 100%; height: 100%;
/* 头部 - 选择项 */
/* 选择项 */ .pl-tab-hd {
.tab-nav {
margin: 0; margin: 0;
padding: 15px 0; padding: 15px 0;
line-height: 1.6; line-height: 1.6;
border-bottom: 0;
background-color: #232323; background-color: #232323;
overflow: hidden; overflow: hidden;
li { li {
...@@ -93,111 +88,44 @@ ...@@ -93,111 +88,44 @@
width: 50%; width: 50%;
padding: 0; padding: 0;
font-size: 16px; font-size: 16px;
line-height: 28px;
color: #909090; color: #909090;
text-align: center; text-align: center;
list-style: none; list-style: none;
cursor: pointer; cursor: pointer;
&.current { &.on {
color: #b49441; color: #b49441;
} }
} }
.video-jy { .br-l-line {
border-left: 1px solid #3f3f3f; border-left: 1px solid #3f3f3f;
} }
} }
.tab-content { /* 内部 - 列表项 */
.pl-tab-bd {
.tab-pane { .tab-pane {
display: block; display: block;
height: 100%; height: 100%;
overflow: auto; overflow: auto;
} }
/* 讲义列表样式 */
/* 章列表样式 */ .lecture-list {
.chapter-list { padding: 8px 16px;
margin: 0; li {
padding: 0; padding: 8px 16px;
line-height: 1.6; cursor: pointer;
overflow: hidden; list-style: none;
.chapter-item { &.on {
h4 { background: #888;
padding: 10px 22px;
margin: 0;
color: #b0b0b0;
background-color: #2f2f2f;
} }
} img {
} width: 100%;
}
/* 节列表样式 */
.knob-list {
margin: 0;
padding: 0;
line-height: 1.6;
overflow: hidden;
li {
position: relative;
&.current {
background: #232323;
a {
color: #b49441;
} }
} }
&:before {
display: block;
content: "";
position: absolute;
left: 13px;
top: 16px;
z-index: 10;
width: 18px;
height: 18px;
background: #5b5b5b;
border: 2px solid #5b5b5b;
border-radius: 50%;
}
&:after {
display: block;
content: "";
position: absolute;
left: 22px;
top: 0;
z-index: 5;
width: 1px;
height: 100px;
background: #616161;
}
}
.knob-progress {
position: absolute;
top: 16px;
left: 13px;
z-index: 20;
display: none\0;
}
.icon-pro {
display: none;
position: absolute;
top: 16px;
left: 23px;
z-index: 15;
width: 19px;
height: 19px;
border-radius: 9px;
display: block\0;
}
.knob-name {
display: block;
padding: 15px 35px 15px 40px;
font-size: 14px;
color: #909090;
text-decoration: none;
cursor: pointer;
} }
} }
} }
/* 章节后面小图标的样式 */ /* 章节后面小图标的样式 */
.el-icon { .el-icon {
display: inline-block; display: inline-block;
...@@ -205,22 +133,6 @@ ...@@ -205,22 +133,6 @@
right: 13px; right: 13px;
top: 19px; top: 19px;
} }
/* 讲义列表样式 */
.jiangyi-list {
padding: 8px 16px;
li {
padding: 8px 16px;
cursor: pointer;
list-style: none;
&.current {
background: #888;
}
img {
width: 100%;
}
}
}
} }
</style> </style>
\ No newline at end of file
This source diff could not be displayed because it is too large. You can view the blob instead.
...@@ -24,7 +24,8 @@ ...@@ -24,7 +24,8 @@
"license": "ISC", "license": "ISC",
"eslintIgnore": [ "eslintIgnore": [
"client-dist/", "client-dist/",
"node_modules/" "node_modules/",
"static/"
], ],
"devDependencies": { "devDependencies": {
"acorn": "^6.0.4", "acorn": "^6.0.4",
...@@ -60,6 +61,7 @@ ...@@ -60,6 +61,7 @@
"gulp-util": "^3.0.8", "gulp-util": "^3.0.8",
"gulp-zip": "^4.2.0", "gulp-zip": "^4.2.0",
"html-webpack-plugin": "^3.2.0", "html-webpack-plugin": "^3.2.0",
"jquery": "^3.3.1",
"js-md5": "^0.7.3", "js-md5": "^0.7.3",
"mini-css-extract-plugin": "^0.4.5", "mini-css-extract-plugin": "^0.4.5",
"node-sass": "^4.10.0", "node-sass": "^4.10.0",
......
/*! SWFObject v2.2 <http://code.google.com/p/swfobject/>
is released under the MIT License <http://www.opensource.org/licenses/mit-license.php>
*/
var UNDEF = "undefined",
OBJECT = "object",
SHOCKWAVE_FLASH = "Shockwave Flash",
SHOCKWAVE_FLASH_AX = "ShockwaveFlash.ShockwaveFlash",
FLASH_MIME_TYPE = "application/x-shockwave-flash",
EXPRESS_INSTALL_ID = "SWFObjectExprInst",
ON_READY_STATE_CHANGE = "onreadystatechange",
win = window,
doc = document,
nav = navigator,
plugin = false,
domLoadFnArr = [main],
regObjArr = [],
objIdArr = [],
listenersArr = [],
storedAltContent,
storedAltContentId,
storedCallbackFn,
storedCallbackObj,
isDomLoaded = false,
isExpressInstallActive = false,
dynamicStylesheet,
dynamicStylesheetMedia,
autoHideShow = true,
/* Centralized function for browser feature detection
- User agent string detection is only used when no good alternative is possible
- Is executed directly for optimal performance
*/
ua = function() {
var w3cdom = typeof doc.getElementById != UNDEF && typeof doc.getElementsByTagName != UNDEF && typeof doc.createElement != UNDEF,
u = nav.userAgent.toLowerCase(),
p = nav.platform.toLowerCase(),
windows = p ? /win/.test(p) : /win/.test(u),
mac = p ? /mac/.test(p) : /mac/.test(u),
webkit = /webkit/.test(u) ? parseFloat(u.replace(/^.*webkit\/(\d+(\.\d+)?).*$/, "$1")) : false, // returns either the webkit version or false if not webkit
ie = !+"\v1", // feature detection based on Andrea Giammarchi's solution: http://webreflection.blogspot.com/2009/01/32-bytes-to-know-if-your-browser-is-ie.html
playerVersion = [0,0,0],
d = null;
if (typeof nav.plugins != UNDEF && typeof nav.plugins[SHOCKWAVE_FLASH] == OBJECT) {
d = nav.plugins[SHOCKWAVE_FLASH].description;
if (d && !(typeof nav.mimeTypes != UNDEF && nav.mimeTypes[FLASH_MIME_TYPE] && !nav.mimeTypes[FLASH_MIME_TYPE].enabledPlugin)) { // navigator.mimeTypes["application/x-shockwave-flash"].enabledPlugin indicates whether plug-ins are enabled or disabled in Safari 3+
plugin = true;
ie = false; // cascaded feature detection for Internet Explorer
d = d.replace(/^.*\s+(\S+\s+\S+$)/, "$1");
playerVersion[0] = parseInt(d.replace(/^(.*)\..*$/, "$1"), 10);
playerVersion[1] = parseInt(d.replace(/^.*\.(.*)\s.*$/, "$1"), 10);
playerVersion[2] = /[a-zA-Z]/.test(d) ? parseInt(d.replace(/^.*[a-zA-Z]+(.*)$/, "$1"), 10) : 0;
}
}
else if (typeof win.ActiveXObject != UNDEF) {
try {
var a = new ActiveXObject(SHOCKWAVE_FLASH_AX);
if (a) { // a will return null when ActiveX is disabled
d = a.GetVariable("$version");
if (d) {
ie = true; // cascaded feature detection for Internet Explorer
d = d.split(" ")[1].split(",");
playerVersion = [parseInt(d[0], 10), parseInt(d[1], 10), parseInt(d[2], 10)];
}
}
}
catch(e) {}
}
return { w3:w3cdom, pv:playerVersion, wk:webkit, ie:ie, win:windows, mac:mac };
}(),
/* Cross-browser onDomLoad
- Will fire an event as soon as the DOM of a web page is loaded
- Internet Explorer workaround based on Diego Perini's solution: http://javascript.nwbox.com/IEContentLoaded/
- Regular onload serves as fallback
*/
onDomLoad = function() {
if (!ua.w3) { return; }
if ((typeof doc.readyState != UNDEF && doc.readyState == "complete") || (typeof doc.readyState == UNDEF && (doc.getElementsByTagName("body")[0] || doc.body))) { // function is fired after onload, e.g. when script is inserted dynamically
callDomLoadFunctions();
}
if (!isDomLoaded) {
if (typeof doc.addEventListener != UNDEF) {
doc.addEventListener("DOMContentLoaded", callDomLoadFunctions, false);
}
if (ua.ie && ua.win) {
var a1 = function() {
if (doc.readyState == "complete") {
doc.detachEvent(ON_READY_STATE_CHANGE, a1);
callDomLoadFunctions();
}
}
doc.attachEvent(ON_READY_STATE_CHANGE, a1);
if (win == top) { // if not inside an iframe
var a2 = function(){
if (isDomLoaded) { return; }
try {
doc.documentElement.doScroll("left");
}
catch(e) {
setTimeout(a2, 0);
return;
}
callDomLoadFunctions();
};
a2();
}
}
if (ua.wk) {
var a3 = function(){
if (isDomLoaded) { return; }
if (!/loaded|complete/.test(doc.readyState)) {
setTimeout(a3, 0);
return;
}
callDomLoadFunctions();
};
a3();
}
addLoadEvent(callDomLoadFunctions);
}
}();
function callDomLoadFunctions() {
if (isDomLoaded) { return; }
try { // test if we can really add/remove elements to/from the DOM; we don't want to fire it too early
var t = doc.getElementsByTagName("body")[0].appendChild(createElement("span"));
t.parentNode.removeChild(t);
}
catch (e) { return; }
isDomLoaded = true;
var dl = domLoadFnArr.length;
for (var i = 0; i < dl; i++) {
domLoadFnArr[i]();
}
}
function addDomLoadEvent(fn) {
if (isDomLoaded) {
fn();
}
else {
domLoadFnArr[domLoadFnArr.length] = fn; // Array.push() is only available in IE5.5+
}
}
/* Cross-browser onload
- Based on James Edwards' solution: http://brothercake.com/site/resources/scripts/onload/
- Will fire an event as soon as a web page including all of its assets are loaded
*/
function addLoadEvent(fn) {
if (typeof win.addEventListener != UNDEF) {
win.addEventListener("load", fn, false);
}
else if (typeof doc.addEventListener != UNDEF) {
doc.addEventListener("load", fn, false);
}
else if (typeof win.attachEvent != UNDEF) {
addListener(win, "onload", fn);
}
else if (typeof win.onload == "function") {
var fnOld = win.onload;
win.onload = function() {
fnOld();
fn();
};
}
else {
win.onload = fn;
}
}
/* Main function
- Will preferably execute onDomLoad, otherwise onload (as a fallback)
*/
function main() {
if (plugin) {
testPlayerVersion();
}
else {
matchVersions();
}
}
/* Detect the Flash Player version for non-Internet Explorer browsers
- Detecting the plug-in version via the object element is more precise than using the plugins collection item's description:
a. Both release and build numbers can be detected
b. Avoid wrong descriptions by corrupt installers provided by Adobe
c. Avoid wrong descriptions by multiple Flash Player entries in the plugin Array, caused by incorrect browser imports
- Disadvantage of this method is that it depends on the availability of the DOM, while the plugins collection is immediately available
*/
function testPlayerVersion() {
var b = doc.getElementsByTagName("body")[0];
var o = createElement(OBJECT);
o.setAttribute("type", FLASH_MIME_TYPE);
var t = b.appendChild(o);
if (t) {
var counter = 0;
var a4 = function(){
if (typeof t.GetVariable != UNDEF) {
var d = t.GetVariable("$version");
if (d) {
d = d.split(" ")[1].split(",");
ua.pv = [parseInt(d[0], 10), parseInt(d[1], 10), parseInt(d[2], 10)];
}
}
else if (counter < 10) {
counter++;
setTimeout(a4, 10);
return;
}
b.removeChild(o);
t = null;
matchVersions();
};
a4();
}
else {
matchVersions();
}
}
/* Perform Flash Player and SWF version matching; static publishing only
*/
function matchVersions() {
var rl = regObjArr.length;
if (rl > 0) {
for (var i = 0; i < rl; i++) { // for each registered object element
var id = regObjArr[i].id;
var cb = regObjArr[i].callbackFn;
var cbObj = {success:false, id:id};
if (ua.pv[0] > 0) {
var obj = getElementById(id);
if (obj) {
if (hasPlayerVersion(regObjArr[i].swfVersion) && !(ua.wk && ua.wk < 312)) { // Flash Player version >= published SWF version: Houston, we have a match!
setVisibility(id, true);
if (cb) {
cbObj.success = true;
cbObj.ref = getObjectById(id);
cb(cbObj);
}
}
else if (regObjArr[i].expressInstall && canExpressInstall()) { // show the Adobe Express Install dialog if set by the web page author and if supported
var att = {};
att.data = regObjArr[i].expressInstall;
att.width = obj.getAttribute("width") || "0";
att.height = obj.getAttribute("height") || "0";
if (obj.getAttribute("class")) { att.styleclass = obj.getAttribute("class"); }
if (obj.getAttribute("align")) { att.align = obj.getAttribute("align"); }
// parse HTML object param element's name-value pairs
var par = {};
var p = obj.getElementsByTagName("param");
var pl = p.length;
for (var j = 0; j < pl; j++) {
if (p[j].getAttribute("name").toLowerCase() != "movie") {
par[p[j].getAttribute("name")] = p[j].getAttribute("value");
}
}
showExpressInstall(att, par, id, cb);
}
else { // Flash Player and SWF version mismatch or an older Webkit engine that ignores the HTML object element's nested param elements: display alternative content instead of SWF
displayAltContent(obj);
if (cb) { cb(cbObj); }
}
}
}
else { // if no Flash Player is installed or the fp version cannot be detected we let the HTML object element do its job (either show a SWF or alternative content)
setVisibility(id, true);
if (cb) {
var o = getObjectById(id); // test whether there is an HTML object element or not
if (o && typeof o.SetVariable != UNDEF) {
cbObj.success = true;
cbObj.ref = o;
}
cb(cbObj);
}
}
}
}
}
function getObjectById(objectIdStr) {
var r = null;
var o = getElementById(objectIdStr);
if (o && o.nodeName == "OBJECT") {
if (typeof o.SetVariable != UNDEF) {
r = o;
}
else {
var n = o.getElementsByTagName(OBJECT)[0];
if (n) {
r = n;
}
}
}
return r;
}
/* Requirements for Adobe Express Install
- only one instance can be active at a time
- fp 6.0.65 or higher
- Win/Mac OS only
- no Webkit engines older than version 312
*/
function canExpressInstall() {
return !isExpressInstallActive && hasPlayerVersion("6.0.65") && (ua.win || ua.mac) && !(ua.wk && ua.wk < 312);
}
/* Show the Adobe Express Install dialog
- Reference: http://www.adobe.com/cfusion/knowledgebase/index.cfm?id=6a253b75
*/
function showExpressInstall(att, par, replaceElemIdStr, callbackFn) {
isExpressInstallActive = true;
storedCallbackFn = callbackFn || null;
storedCallbackObj = {success:false, id:replaceElemIdStr};
var obj = getElementById(replaceElemIdStr);
if (obj) {
if (obj.nodeName == "OBJECT") { // static publishing
storedAltContent = abstractAltContent(obj);
storedAltContentId = null;
}
else { // dynamic publishing
storedAltContent = obj;
storedAltContentId = replaceElemIdStr;
}
att.id = EXPRESS_INSTALL_ID;
if (typeof att.width == UNDEF || (!/%$/.test(att.width) && parseInt(att.width, 10) < 310)) { att.width = "310"; }
if (typeof att.height == UNDEF || (!/%$/.test(att.height) && parseInt(att.height, 10) < 137)) { att.height = "137"; }
doc.title = doc.title.slice(0, 47) + " - Flash Player Installation";
var pt = ua.ie && ua.win ? "ActiveX" : "PlugIn",
fv = "MMredirectURL=" + encodeURI(window.location).toString().replace(/&/g,"%26") + "&MMplayerType=" + pt + "&MMdoctitle=" + doc.title;
if (typeof par.flashvars != UNDEF) {
par.flashvars += "&" + fv;
}
else {
par.flashvars = fv;
}
// IE only: when a SWF is loading (AND: not available in cache) wait for the readyState of the object element to become 4 before removing it,
// because you cannot properly cancel a loading SWF file without breaking browser load references, also obj.onreadystatechange doesn't work
if (ua.ie && ua.win && obj.readyState != 4) {
var newObj = createElement("div");
replaceElemIdStr += "SWFObjectNew";
newObj.setAttribute("id", replaceElemIdStr);
obj.parentNode.insertBefore(newObj, obj); // insert placeholder div that will be replaced by the object element that loads expressinstall.swf
obj.style.display = "none";
var a5 = function(){
if (obj.readyState == 4) {
obj.parentNode.removeChild(obj);
}
else {
setTimeout(a5, 10);
}
};
a5();
}
createSWF(att, par, replaceElemIdStr);
}
}
/* Functions to abstract and display alternative content
*/
function displayAltContent(obj) {
if (ua.ie && ua.win && obj.readyState != 4) {
// IE only: when a SWF is loading (AND: not available in cache) wait for the readyState of the object element to become 4 before removing it,
// because you cannot properly cancel a loading SWF file without breaking browser load references, also obj.onreadystatechange doesn't work
var el = createElement("div");
obj.parentNode.insertBefore(el, obj); // insert placeholder div that will be replaced by the alternative content
el.parentNode.replaceChild(abstractAltContent(obj), el);
obj.style.display = "none";
var a6 = function(){
if (obj.readyState == 4) {
obj.parentNode.removeChild(obj);
}
else {
setTimeout(a6, 10);
}
};
a6();
}
else {
obj.parentNode.replaceChild(abstractAltContent(obj), obj);
}
}
function abstractAltContent(obj) {
var ac = createElement("div");
if (ua.win && ua.ie) {
ac.innerHTML = obj.innerHTML;
}
else {
var nestedObj = obj.getElementsByTagName(OBJECT)[0];
if (nestedObj) {
var c = nestedObj.childNodes;
if (c) {
var cl = c.length;
for (var i = 0; i < cl; i++) {
if (!(c[i].nodeType == 1 && c[i].nodeName == "PARAM") && !(c[i].nodeType == 8)) {
ac.appendChild(c[i].cloneNode(true));
}
}
}
}
}
return ac;
}
/* Cross-browser dynamic SWF creation
*/
function createSWF(attObj, parObj, id) {
var r, el = getElementById(id);
if (ua.wk && ua.wk < 312) { return r; }
if (el) {
if (typeof attObj.id == UNDEF) { // if no 'id' is defined for the object element, it will inherit the 'id' from the alternative content
attObj.id = id;
}
if (ua.ie && ua.win) { // Internet Explorer + the HTML object element + W3C DOM methods do not combine: fall back to outerHTML
var att = "";
for (var i in attObj) {
if (attObj[i] != Object.prototype[i]) { // filter out prototype additions from other potential libraries
if (i.toLowerCase() == "data") {
parObj.movie = attObj[i];
}
else if (i.toLowerCase() == "styleclass") { // 'class' is an ECMA4 reserved keyword
att += ' class="' + attObj[i] + '"';
}
else if (i.toLowerCase() != "classid") {
att += ' ' + i + '="' + attObj[i] + '"';
}
}
}
var par = "";
for (var j in parObj) {
if (parObj[j] != Object.prototype[j]) { // filter out prototype additions from other potential libraries
par += '<param name="' + j + '" value="' + parObj[j] + '" />';
}
}
el.outerHTML = '<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"' + att + '>' + par + '</object>';
objIdArr[objIdArr.length] = attObj.id; // stored to fix object 'leaks' on unload (dynamic publishing only)
r = getElementById(attObj.id);
}
else { // well-behaving browsers
var o = createElement(OBJECT);
o.setAttribute("type", FLASH_MIME_TYPE);
for (var m in attObj) {
if (attObj[m] != Object.prototype[m]) { // filter out prototype additions from other potential libraries
if (m.toLowerCase() == "styleclass") { // 'class' is an ECMA4 reserved keyword
o.setAttribute("class", attObj[m]);
}
else if (m.toLowerCase() != "classid") { // filter out IE specific attribute
o.setAttribute(m, attObj[m]);
}
}
}
for (var n in parObj) {
if (parObj[n] != Object.prototype[n] && n.toLowerCase() != "movie") { // filter out prototype additions from other potential libraries and IE specific param element
createObjParam(o, n, parObj[n]);
}
}
el.parentNode.replaceChild(o, el);
r = o;
}
}
return r;
}
function createObjParam(el, pName, pValue) {
var p = createElement("param");
p.setAttribute("name", pName);
p.setAttribute("value", pValue);
el.appendChild(p);
}
/* Cross-browser SWF removal
- Especially needed to safely and completely remove a SWF in Internet Explorer
*/
function removeSWF(id) {
var obj = getElementById(id);
if (obj && obj.nodeName == "OBJECT") {
if (ua.ie && ua.win) {
obj.style.display = "none";
var a7 = function(){
if (obj.readyState == 4) {
removeObjectInIE(id);
}
else {
setTimeout(a7, 10);
}
};
a7();
}
else {
obj.parentNode.removeChild(obj);
}
}
}
function removeObjectInIE(id) {
var obj = getElementById(id);
if (obj) {
for (var i in obj) {
if (typeof obj[i] == "function") {
obj[i] = null;
}
}
obj.parentNode.removeChild(obj);
}
}
/* Functions to optimize JavaScript compression
*/
function getElementById(id) {
var el = null;
try {
el = doc.getElementById(id);
}
catch (e) {}
return el;
}
function createElement(el) {
return doc.createElement(el);
}
/* Updated attachEvent function for Internet Explorer
- Stores attachEvent information in an Array, so on unload the detachEvent functions can be called to avoid memory leaks
*/
function addListener(target, eventType, fn) {
target.attachEvent(eventType, fn);
listenersArr[listenersArr.length] = [target, eventType, fn];
}
/* Flash Player and SWF content version matching
*/
function hasPlayerVersion(rv) {
var pv = ua.pv, v = rv.split(".");
v[0] = parseInt(v[0], 10);
v[1] = parseInt(v[1], 10) || 0; // supports short notation, e.g. "9" instead of "9.0.0"
v[2] = parseInt(v[2], 10) || 0;
return (pv[0] > v[0] || (pv[0] == v[0] && pv[1] > v[1]) || (pv[0] == v[0] && pv[1] == v[1] && pv[2] >= v[2])) ? true : false;
}
/* Cross-browser dynamic CSS creation
- Based on Bobby van der Sluis' solution: http://www.bobbyvandersluis.com/articles/dynamicCSS.php
*/
function createCSS(sel, decl, media, newStyle) {
if (ua.ie && ua.mac) { return; }
var h = doc.getElementsByTagName("head")[0];
if (!h) { return; } // to also support badly authored HTML pages that lack a head element
var m = (media && typeof media == "string") ? media : "screen";
if (newStyle) {
dynamicStylesheet = null;
dynamicStylesheetMedia = null;
}
if (!dynamicStylesheet || dynamicStylesheetMedia != m) {
// create dynamic stylesheet + get a global reference to it
var s = createElement("style");
s.setAttribute("type", "text/css");
s.setAttribute("media", m);
dynamicStylesheet = h.appendChild(s);
if (ua.ie && ua.win && typeof doc.styleSheets != UNDEF && doc.styleSheets.length > 0) {
dynamicStylesheet = doc.styleSheets[doc.styleSheets.length - 1];
}
dynamicStylesheetMedia = m;
}
// add style rule
if (ua.ie && ua.win) {
if (dynamicStylesheet && typeof dynamicStylesheet.addRule == OBJECT) {
dynamicStylesheet.addRule(sel, decl);
}
}
else {
if (dynamicStylesheet && typeof doc.createTextNode != UNDEF) {
dynamicStylesheet.appendChild(doc.createTextNode(sel + " {" + decl + "}"));
}
}
}
function setVisibility(id, isVisible) {
if (!autoHideShow) { return; }
var v = isVisible ? "visible" : "hidden";
if (isDomLoaded && getElementById(id)) {
getElementById(id).style.visibility = v;
}
else {
createCSS("#" + id, "visibility:" + v);
}
}
/* Filter to avoid XSS attacks
*/
function urlEncodeIfNecessary(s) {
var regex = /[\\\"<>\.;]/;
var hasBadChars = regex.exec(s) != null;
return hasBadChars && typeof encodeURIComponent != UNDEF ? encodeURIComponent(s) : s;
}
/* Release memory to avoid memory leaks caused by closures, fix hanging audio/video threads and force open sockets/NetConnections to disconnect (Internet Explorer only)
*/
var cleanup = function() {
if (ua.ie && ua.win) {
window.attachEvent("onunload", function() {
// remove listeners to avoid memory leaks
var ll = listenersArr.length;
for (var i = 0; i < ll; i++) {
listenersArr[i][0].detachEvent(listenersArr[i][1], listenersArr[i][2]);
}
// cleanup dynamically embedded objects to fix audio/video threads and force open sockets and NetConnections to disconnect
var il = objIdArr.length;
for (var j = 0; j < il; j++) {
removeSWF(objIdArr[j]);
}
// cleanup library's main closures to avoid memory leaks
for (var k in ua) {
ua[k] = null;
}
ua = null;
for (var l in swfobject) {
swfobject[l] = null;
}
swfobject = null;
});
}
}();
var swfobject = {
/* Public API
- Reference: http://code.google.com/p/swfobject/wiki/documentation
*/
registerObject: function(objectIdStr, swfVersionStr, xiSwfUrlStr, callbackFn) {
if (ua.w3 && objectIdStr && swfVersionStr) {
var regObj = {};
regObj.id = objectIdStr;
regObj.swfVersion = swfVersionStr;
regObj.expressInstall = xiSwfUrlStr;
regObj.callbackFn = callbackFn;
regObjArr[regObjArr.length] = regObj;
setVisibility(objectIdStr, false);
}
else if (callbackFn) {
callbackFn({success:false, id:objectIdStr});
}
},
getObjectById: function(objectIdStr) {
if (ua.w3) {
return getObjectById(objectIdStr);
}
},
embedSWF: function(swfUrlStr, replaceElemIdStr, widthStr, heightStr, swfVersionStr, xiSwfUrlStr, flashvarsObj, parObj, attObj, callbackFn) {
var callbackObj = {success:false, id:replaceElemIdStr};
if (ua.w3 && !(ua.wk && ua.wk < 312) && swfUrlStr && replaceElemIdStr && widthStr && heightStr && swfVersionStr) {
setVisibility(replaceElemIdStr, false);
addDomLoadEvent(function() {
widthStr += ""; // auto-convert to string
heightStr += "";
var att = {};
if (attObj && typeof attObj === OBJECT) {
for (var i in attObj) { // copy object to avoid the use of references, because web authors often reuse attObj for multiple SWFs
att[i] = attObj[i];
}
}
att.data = swfUrlStr;
att.width = widthStr;
att.height = heightStr;
var par = {};
if (parObj && typeof parObj === OBJECT) {
for (var j in parObj) { // copy object to avoid the use of references, because web authors often reuse parObj for multiple SWFs
par[j] = parObj[j];
}
}
if (flashvarsObj && typeof flashvarsObj === OBJECT) {
for (var k in flashvarsObj) { // copy object to avoid the use of references, because web authors often reuse flashvarsObj for multiple SWFs
if (typeof par.flashvars != UNDEF) {
par.flashvars += "&" + k + "=" + flashvarsObj[k];
}
else {
par.flashvars = k + "=" + flashvarsObj[k];
}
}
}
if (hasPlayerVersion(swfVersionStr)) { // create SWF
var obj = createSWF(att, par, replaceElemIdStr);
if (att.id == replaceElemIdStr) {
setVisibility(replaceElemIdStr, true);
}
callbackObj.success = true;
callbackObj.ref = obj;
}
else if (xiSwfUrlStr && canExpressInstall()) { // show Adobe Express Install
att.data = xiSwfUrlStr;
showExpressInstall(att, par, replaceElemIdStr, callbackFn);
return;
}
else { // show alternative content
setVisibility(replaceElemIdStr, true);
}
if (callbackFn) { callbackFn(callbackObj); }
});
}
else if (callbackFn) { callbackFn(callbackObj); }
},
switchOffAutoHideShow: function() {
autoHideShow = false;
},
ua: ua,
getFlashPlayerVersion: function() {
return { major:ua.pv[0], minor:ua.pv[1], release:ua.pv[2] };
},
hasFlashPlayerVersion: hasPlayerVersion,
createSWF: function(attObj, parObj, replaceElemIdStr) {
if (ua.w3) {
return createSWF(attObj, parObj, replaceElemIdStr);
}
else {
return undefined;
}
},
showExpressInstall: function(att, par, replaceElemIdStr, callbackFn) {
if (ua.w3 && canExpressInstall()) {
showExpressInstall(att, par, replaceElemIdStr, callbackFn);
}
},
removeSWF: function(objElemIdStr) {
if (ua.w3) {
removeSWF(objElemIdStr);
}
},
createCSS: function(selStr, declStr, mediaStr, newStyleBoolean) {
if (ua.w3) {
createCSS(selStr, declStr, mediaStr, newStyleBoolean);
}
},
addDomLoadEvent: addDomLoadEvent,
addLoadEvent: addLoadEvent,
getQueryParamValue: function(param) {
var q = doc.location.search || doc.location.hash;
if (q) {
if (/\?/.test(q)) { q = q.split("?")[1]; } // strip question mark
if (param == null) {
return urlEncodeIfNecessary(q);
}
var pairs = q.split("&");
for (var i = 0; i < pairs.length; i++) {
if (pairs[i].substring(0, pairs[i].indexOf("=")) == param) {
return urlEncodeIfNecessary(pairs[i].substring((pairs[i].indexOf("=") + 1)));
}
}
}
return "";
},
// For internal usage only
expressInstallCallback: function() {
if (isExpressInstallActive) {
var obj = getElementById(EXPRESS_INSTALL_ID);
if (obj && storedAltContent) {
obj.parentNode.replaceChild(storedAltContent, obj);
if (storedAltContentId) {
setVisibility(storedAltContentId, true);
if (ua.ie && ua.win) { storedAltContent.style.display = "block"; }
}
if (storedCallbackFn) { storedCallbackFn(storedCallbackObj); }
}
isExpressInstallActive = false;
}
}
};
window.swfobject = swfobject;
// module.exports = swfobject;
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论