`
ruilin215
  • 浏览: 1096990 次
  • 性别: Icon_minigender_2
  • 来自: 成都
文章分类
社区版块
存档分类
最新评论

Qomolangma框架库(一):概述、工具、异常、调试与分析

阅读更多

================================================================================
Qomolangma OpenProject v1.0


类别 :Rich Web Client
关键词 :JS OOP,JS Framwork, Rich Web Client,RIA,Web Component,
DOM,DTHML,CSS,JavaScript,JScript

项目发起:aimingoo (aim@263.net)
项目团队:..\..\Qomo_team.txt
有贡献者:JingYu(zjy@cnpack.org)
================================================================================


一、概述:Qomolangma中的框架库(v0.1)
~~~~~~~~~~~~~~~~~~

在UI层方面,Qomo一直没有足够的进展,因此Qomo在beta 1之前公布的代码看起来就象是一个语言实验
工程,而不象是一个面向应用的项目。

其实Qomo的前身(WEUI)本身就是围绕UserInterface Library来提出的,因此WEUI的确有自己的UI层。此
外,它也有完整的DB和Graphics层(及一个VML的实现)。但是Qomo对UI层提出的目标与WEUI并不一致,因
此这直接导致了“Qomo需要一个新的UI库”的结果。

Qomo在beta 2中包含部分UI、DB层的代码,但是并不推荐将它归为Qomo的一个组成部分并应用。——尽管
这些的确可以在Qomo下运行得很好,开发人员可以从中得到很多的思想与技术实现。——事实上Qomo也从
WEUI的UI和Graphics框架的基础框架上借鉴了一些东西。

但这些都不是这一组文章要讨论的内容。

因为在Qomo的beta 2之后,除了一些底层语言体系的修补之外,Qomo团队将开始有关框架库(并不是UI库)
的开发工作。这些工作包括:
- 公共框架类库: Framework\Classes.js, Framework\Common\*
- 日志、调试、分析和单元测试框架:Framework\Debug\*
- DOM、CSS兼容层框架:Components\Compat\*

二、Qomo的基础库与基础类库
~~~~~~~~~~~~~~~~~~

Qomo的基础库是一些工具函数,或者原生(native)的JavaScript类。它的地位与RTL中的JSEnhance.js
是相同的,但基础库并不只是针对JavaScript进行增强。

Qomo的基础类库建立自Qomo的OOP框架。也就是说,至少是继承自TObjecct的类。基础类库表现为一些工
具类、容器类、全局(单例)类和一些其它抽象层次较低的类。

Qomo的基础库与基础类库位于:
Framework\Common\*
它通过一个包文件载入:
Framework\Classes.js


三、基础库中的工具函数
~~~~~~~~~~~~~~~~~~

基础库中的工具函数(目前)包括在:
- 系统实用工具: Framework\Common\SysUtils.js
- 对象实用工具: Framework\Common\ObjUtils.js
- 类型或数据结构定义及转换工具: Framework\Common\ConvUtils.js
三个文件中。

其中,ObjUtils.js和ConvUtils.js虽然包括在Qomo Beta2中,但事实上没有正式发布。——很多代码
已经移除;部分代码未经测试也没有相关的说明。因此本文先不讲述它们。

SysUtils.js中的目前只发布了4个工具函数。事实上他们在我以前的工程中大量使用过。下面做一些
简单地介绍。

1. createUniqueID()
----------
该函数产生一个唯一的标识符。一般来说,他在当前的Web页(包括多帧)中都是不重复的。它基于一
个随机数产生的算法规则:即使是在同一时刻调用两次随机数,也会因种子的变化而产生不同的值。因
此拼接随机数和日期值通常会得到一个唯一的标识。——如果对随机数的算法的设定出了问题,则另论。


2. createUniqueVar()
----------
UniqueID可以作为标识,但无法作为全局变量使用。而本函数则用声明一个全局变量,并且返回该变
量名。这个声明可以被delete删除以回收内存。例如:
------
function myFunc() {
// 创建并得到变量名
var name = createUniqueVar();

// 使用该变量
eval('name').value = 'abcdefgh';

// 删除该变量
eval('delete ' + name);
}
------

3. isVariant(varName)
----------
传入一个变量名,该函数会返回该变量名是否是一个变量。既可以是全局变量,也可以是局部变量。
例如:
------
function myFunc() {
alert(isVariant('myFunc'));
}
------

4. defined(aVariant)
----------
查看一个变量或值是否被声明过。例如:
------
var all = [1,,3,4];
function myFunc() {
for (var i=0; i<all.length; i++) {
if (defined(all[i])) {
alert(all[i]);
}
}
}
------


四、基础库中的异常与断言
~~~~~~~~~~~~~~~~~~

1. JavaScript中的异常基础
----------
在JavaScript语言中,创建异常对象的方法是:
e = new Error([number[, description]]);

你可以在创建时或者创建后抛出异常:
------
// 创建时抛出
throw new Error(100, 'this is exception - 100.');
// 创建后抛出
var e = new Error(101, 'this is exceptio - 101.');
throw e;
------

按照JavaScript的语言规范,你可以在try中捕获到异常后再次抛出。例如:
------
try {
// ...
}
catch (e) {
throw e; // 再次抛出
}
------


2. Qomo基础库中的异常
----------
我们发现标准JS中的异常很难管理。例如异常的编号,或者显示信息时大多使用直接声明
的字符串。因此,Qomo约定一个异常可以用两个成员的数组表示。例如:
EAccessInvaildClass = [8109, 'Class invaild: lost typeinfo!'];
在第二个成员(字符串)中允许使用%s通配符。例如:
EAttributeCantRead = [8112, 'The "%s" attribute can\'t read for %s.'];

Qomo认为这是一个标准的异常记录/对象的结构。这种定义是非常取巧的:
- 如果不使用Qomo的异常框架,那么标准JS中将会把数组转换成字符串,这时得到的信息
是可以阅读的。
- 如果使用Qomo异常框架,那么由于Qomo替换了Error()类,因此将生成更友好的信息。
- 在JSEnhance.js中,Qomo再次替换了Error()类,这使得%s可以被处理,从而使动态的
组织异常信息成为非常便利的事。

在RTL/Error.js中,Qomo重写了Error()类。这使得Error()有以下构造形式:
e = new Error();
e = new Error(number);
e = new Error(number, description);
e = new Error(number, description, instanceObj);
e = new Error(a_qomo_exp);
e = new Error(a_qomo_exp, instanceObj);

其中,instanceObj 表明一个关联到该异常的对象实例。但Qomo并不处理instanceObj, 只是
通过异常对象来传递它。这样可以使try .. catch捕获到的异常对象有一个instanceObj属性,
指向触发异常时送过来的一个“参考实例”。

而a_qomo_exp表是一个按qomo的规则声明的异常数组(参阅前面的内容)。这样我们就可以用下
面的代码来简单的触发一个异常:
throw new Error(EAccessInvaildClass);

如果系统未装载过Error.js,那么显示的信息将是:
---------------------------
错误: 8109,Class invaild: lost typeinfo!
---------------------------

如果系统已经装载过Error.js,那么显示的信息将是:
---------------------------
错误: Class invaild: lost typeinfo!
---------------------------

或者我们也可以这样使用带通配符的异常:
throw new Error(EAttributeCantRead.concat('get', 'Enumerator'));

如果我们装载过JSEnhance.js,那么显示的信息将是:
---------------------------
错误: The "get" attribute can't read for Enumerator.
---------------------------

这样,无论如何,我们都能给用户“相对友好”的错误信息。


3. Qomo基础库中的断言
----------
Qomo中的断言实现得非常简单。它其实就是一个Qomo的异常。如下:
---------------------------
var
EAssertFail = [8001, 'assert is failed.\n\n%s'];

$assert = function (isTrue, info) {
if (!isTrue) throw new Error(EAssertFail.concat([info]));
}
---------------------------
由于Qomo有自己的异常实现,因此断言的显示将非常友好。


五、基础库中的性能分析工具(Profiler)
~~~~~~~~~~~~~~~~~~

Qomo在Debug.js单元中载入了一个Profiler工具,是分析代码性能的利器。它的一个
使用示例是:
Framework/Debug/TestCase/T_profiler.html


Qomo中的profiler使用起来非常方便,也可以使用多组的profiler。系统中单独初始
化了一个全局的$profilers,以方便使用。

1. 核心结构
----------
如果不考虑输出的效果,那么直接调用Qomo的profiler就可以得到它的核心结构和
使用流程了:
---------------------------
<!-- 载入Profiler类 -->
<script src='Framework/Debug/Profilers.js'></script>

<script>
// 在用户代码中插入分析语句, 然后执行
function myFunc() {
$profilers('myFunc').begin()

// your code ...

$profilers('myFunc').end()
}
myFunc();

// 输出分析结果
document.writeln($profilers);
</script>
---------------------------
但是这样输入的东西根本没有办法看。因此,Qomo提供一组工具来辅助使用profiler。
当然,你也可以定制它。——这个后面再讲。


2. 基础Dbg.Utils的简单使用
----------
使用Dbg.Utils.js是非常便捷的做法:
---------------------------
<!-- 载入Profiler类和调试用工具单元 -->
<script src='Framework/Debug/Profilers.js'></script>
<script src='Framework/Debug/Dbg.Utils.js'></script>

<script>
// 在用户代码中插入分析语句, 然后执行
// (同上, 略... )

// 重写$debug()函数
$debug.resetTo(function() {
arguments.join = Array.prototype.join;
document.writeln(arguments.join(''));
});

// 显示profiler信息
showProfiler($profilers);
</script>
---------------------------

注意这里有两处关键的细节。一是要求载入Dbg.Utils.js,至于它位于Profilers.js之前或者
之后并没有关系。


3. 为分析对象指定精确的标签
----------
在开始的示例里,我们用一对
---------------------------
$profilers('myFunc').begin()
$profilers('myFunc').end()
---------------------------
来开始和结束分析。这里的myFunc可以是任意字符,也可以是任意多的参数。这样,你可以
指定:
---------------------------
$profilers('myFunc', 'build').begin();
$profilers('myFunc', 'build').end();

$profilers('myFunc', 'execute').begin();
$profilers('myFunc', 'execute').end();
---------------------------
这样对一个函数的多组分析。也可以指定:
---------------------------
$profilers('myFunc', '1').begin();
$profilers('myFunc', '1').end();

$profilers('myFunc', '2').begin();
$profilers('myFunc', '2').end();
---------------------------
这样来表明步骤。


4. 处理递归
----------
接下来,对于递归的函数,你可以采取两种方法来处理它。其一是加标签,例如:
---------------------------
function calc(n) {
$profilers('calc', n).begin();

var v = n;
if (v > 0) {
v = n + calc(n - 1);
}

$profilers('calc', n).end();

return v;
}

calc(10);
---------------------------

第二种方法,则是借用profiler返回标志,例如:
---------------------------
function calc(n) {
var tag = $profilers('calc').begin();

var v = n;
if (v > 0) {
v = n + calc(n - 1);
}

$profilers('calc').end(tag);

return v;
}

calc(10);
---------------------------

这两种方法中,第一种分另产生不同的profiler记录项,因此采用标准方法处理即可;第二种
则产生同一记录项的多个记录值,对于这种情况,在Dbg.Utils.js中的showProfiler()展示了
如何处理。


5. 显示结果: $debug()的重写
----------
$debug()最初被声明在system.js中,用于直接向document输出调试信息。但是这样必然会
破坏网页的显示效果。——尤其profiler的信息量非常大。

因此,在Dbg.Utils.js中,我们重写了它。使它的输出被放到缓存中:
---------------------------
$debug = function() {
// ...

arguments.callee['$cached$'] += arguments.join('');
}
---------------------------
其中,arguments.callee直接向$debug()函数本身。使用callee而不是$debug自身,是避免
$debug()被再次重写。

我们还为$debug添加了一个方法resetTo()。这个方法传入一个函数,新函数要能够输出这些
被缓存的信息。——它在被resetTo()时将被自动调用一次。然后该新函数将替代原$debug()
在系统中的作用。

例如我们在面第二节中讲到的:
---------------------------
// 重写$debug()函数
$debug.resetTo(function() {
arguments.join = Array.prototype.join;
document.writeln(arguments.join(''));
});

showProfiler($profilers);
---------------------------

这个重写就是用于向document输出,它也被用在下面这个示例里:
Framework/Debug/TestCase/T_profiler.html

另外一处使用,则是在:
Components/QomoHierarchyPoster.html
它的写法是将信息显示到一个HTML元素中:
---------------------------
$debug.resetTo(function() {
arguments.join = Array.prototype.join;
document.getElementById('Qo_DBGINFO').insertAdjacentHTML(
'beforeEnd', arguments.join(''));
});
---------------------------


6. 显示结果: showProfiler()及其定制
----------
事实上,Dbg.Utils.js作为一个实用工具单元,可以完全不用载入到当前页面。这种情况下,
Profilers的功能依然是可用的。包括使用:
---------------------------
$profilers('your_tag').begin();
$profilers('your_tag').end();
---------------------------
这样的方法来记录profiler数据。因为$profilers这个全局对象是被创建在Profiles.js中的。

如果你不打算使用Dbg.Utils.js中的showProfiers()来显示结果。那么你可以自己写一个,例
如打开一个新窗口来显示。这时你只需要参考一下showProfiers()的代码即可:
---------------------------
function showProfiers(prof) {
var data = prof.toData();

// ...
}
---------------------------

这样从prof中得到的数据是如下的一种结构:
---------------------------
a_data_instance = {
'your_tag_1': [beginTime1, endTime1, beginTime2, endTime2, ...],
'your_tag_2': [...],
'your_tag_3': [...]
// ...
}
---------------------------
你只需要写代码去循环处理即可。——注意beginTime/endTime的值是Number类型的。如果你
要转换为Date()对象,那么可以“new Date(beginTime)”这样即可。

7. 在系统中处理多个Profilers()对象
----------
只要你愿意,你可以创建多个Profilers()对象,他们之间是没有干扰的。——当然,你也
可以只创建一个,并用不同的标签来分组显示它们。这一切只取决于你的选择。

如果你打算创建多个Profilers,那么大概的代码如下:
---------------------------
var prof1 = new Profilers();
var prof2 = new Profilers();

// prof1('tag').begin() ...
// ...

showProfiler(prof1);
showProfiler(prof2);
---------------------------

8. 清理profilers数据
----------
如果你要开始新一批的profilers分析。那么你应该清除一下原有的数据。但到目前为止,
Profilers()对象并没有提供clear()方法。——我认为不必须。

因此如果你需要清理数据,最好的方法就是重新创建一个。例如:
---------------------------
// 重新创建全局的分析器
$profilers = new Profilers();
---------------------------


六、Profilers与AOP的结合使用
~~~~~~~~~~~~~~~~~~
大概要为每一个函数去写.begin()和.end()会是一件让人痛苦的事,而且频繁地改动原先的
函数,也不是是一件什么好事。因此事实上在Profilers的使用示例中,我采用的都是AOP来
实现。在
Components/QomoHierarchyPoster.html
Framework/Debug/TestCase/T_profiler.html
这两个文件中,都可以看到AOP的用法。

其中,T_profiler.html载入了一个AOP_MyProf.js文件,这里的示例最为简单:
---------------------------
// 加入profiler相关的代码($profilers是全局对象)
var asp_import = new FunctionAspect($import, '$import', 'Function');

asp_import.OnBefore.add(function(o, n, p, a) {
with ($profilers(n, FN(a[0]))) {
set('url', a[0]);
a['$tag$'] = begin();
}
});

asp_import.OnAfter.add(function(o, n, p, a, v) {
$profilers(n, FN(a[0])).end(a['$tag$']);
});
---------------------------

我们看到asp_import就是一个为"$import()"创建的一个切面,这个切面的名字,就是"$import"。
所以我们在OnBefore和OnAfter中看到的参数n,值就是"$import"。

接下来,我们在OnBefore的事件处理函数中,添加了对每一次$import()调用的profiler分析。这
里传入的参数a,就是调用$import()时的arguments。所以a[0]就是$import()的文件名。——因为,
我们总是用“$import('a_js_url')”来导入文件的。

调用$profilers()时传入了两个标签。其中n总是"$import",而FN(a[0])则是取到URL未尾的文件名。
因此,相当于创建了一个名为"$import/a_js_filename"形式的标识。这在后面用showProfilers()
时就可以看到了。

接下来,由于我们还需要在showProfilers()时能显示一点数据,例如载入的实例的URL的完整径,
因此我们调用了set()方法。这段代码实际相当于:
---------------------------
asp_import.OnBefore.add(function(o, n, p, a) {
var prof = $profilers(n, FN(a[0]));

prof.set('url', a[0]);
a['$tag$'] = prof.begin();
});
---------------------------
prof.set('url', ...)这行代码可以为一个prof添加任意多的、任何名称的定制数据。这些数据
可以提供给showProfiler()来使用。——当然,你自己也可以写一个showProfiler()来处理这些
数据。

最后一行是为了处理在Qomo中将同一个.js文件导入多次。——"$import/a_js_filename"会重复,
从而看起来象是处理同一段代码(像对递归过程profiler)。——因此这里把prof.begin()返回的
标识放在a['$tag$']里。

这里用了一个取巧的方法:AOP事件中的参数a是调用被关注点时的参数arguments,因此对同一个
关注对象来说,OnBefore与OnAfter所使用的arguments也是同一个。所以当切面到达OnAfter时,
我们只需要处理成
---------------------------
$profilers(n, FN(a[0])).end(a['$tag$']);
---------------------------
就可以了。——a['$tag']就是我们需要使用的begin()返回值。而且,我们也不需要去delete这
个属性。因为当函数运行结束后,arguments将自动被javascript引擎销毁。

同样的技术也被用在
Components/QomoHierarchyPoster.html
这个文件中。不过QomoHierarchyPoster.html在profilers中值得言讲的,却在于它对combine()
的活用。

在QomoHierarchyPoster.html中,我们处理了五个切面,并试图对它们做性能分析:
---------------------------
var asp_getTopoString = new FunctionAspect(getTopoString, 'getTopoString', 'Function');

var asp_LinesString = new FunctionAspect(getLinesString, 'getLinesString', 'Function');
var asp_drawTopo = new FunctionAspect(drawTopo, 'drawTopo', 'Function');
var asp_active = new FunctionAspect(activeTopoInCanvas, 'activeTopoInCanvas', 'Function');
var asp_cache = new FunctionAspect(cacheTargetByNodes, 'cacheTargetByNodes', 'Function');
---------------------------

而这时,我们只为一个切面的OnBefore()和OnAfter()添加了事件处理句柄:
---------------------------
asp_getTopoString.OnBefore.add(function(o, n, p, a) {
a["$tag$"] = $profilers('$prof', p, n).begin();
});

asp_getTopoString.OnAfter.add(function(o, n, p, a, v) {
$profilers('$prof', p, n).end(a["$tag$"]);
});
---------------------------

为了让其它几个切面也得到相同的处理能力,我们使用了Qomo AOP中的“联合(combine)”,这
非常简单,一行代码就可以了:
---------------------------
asp_getTopoString.combine(asp_LinesString, asp_drawTopo, asp_active, asp_cache);
---------------------------

于是,上面的五个切面所处理的被观察者(函数)执行时,都会触发asp_getTopoString的OnBefore
和OnAfter事件了。

我相信Qomo AOP的价值远非如此。接下来的应用,看大家的吧。^.^


七、其它
~~~~~~~~~~~~~~~~~~

1. 可以独立于Qomo框架的内核模块
----------
在Qomo内核中,Profilers.js、Dbg.Utils.js与JSEnhance.js一样,都不需要加载Qomo的
框架,因此可以直接在其它的、第三方的代码框架中使用。这样的内核模块还包括:
------------
// 包括Profilers, Dbg.Utils和RepImport等
$import('Debug/Debug.js');

// 替换错误处理
$import('RTL/Error.js');

// 接口
$import('RTL/Interface.js');

// 协议(目前只包括URL分析)
$import('RTL/Protocol.js');

// 兼容层
$import('Compat/CompatLayer.js');

// 命名空间(依赖于system.js与Protocol.js)
$import('Names/NamedSystem.js');

// 脚本功能增强
$import('RTL/JSEnhance.js');
------------

由于Profilers被设计成可以针对整个Qomo做性能分析(包括最初的$import(),以及后续加载
的各个组件与类),因此它必须在最先载入。同样的原因,整个的Debug.js是第一个由system.js
载入的单元。


2. 框架库之其它内容
----------
在Qomo beta2的基础库中,还包括三个重要的成员:
- 队列/池,以及处理器类
- 时间序列、时间线与数据发生器
- ajax原型:HttpMachine()

这些内容在随后我将专门撰文讲解。

分享到:
评论

相关推荐

    英语人教八年级下册UnitaQomolangmaPPT课件.pptx

    英语人教八年级下册UnitaQomolangmaPPT课件.pptx

    2024职工群体户外交友拓展“躺进春天 趣野人生”活动策划方案ss.pptx

    2024职工群体户外交友拓展“躺进春天 趣野人生”活动策划方案ss.pptx

    pypy3.7-v7.3.4-osx64.tar.bz2

    Python库是一组预先编写的代码模块,旨在帮助开发者实现特定的编程任务,无需从零开始编写代码。这些库可以包括各种功能,如数学运算、文件操作、数据分析和网络编程等。Python社区提供了大量的第三方库,如NumPy、Pandas和Requests,极大地丰富了Python的应用领域,从数据科学到Web开发。Python库的丰富性是Python成为最受欢迎的编程语言之一的关键原因之一。这些库不仅为初学者提供了快速入门的途径,而且为经验丰富的开发者提供了强大的工具,以高效率、高质量地完成复杂任务。例如,Matplotlib和Seaborn库在数据可视化领域内非常受欢迎,它们提供了广泛的工具和技术,可以创建高度定制化的图表和图形,帮助数据科学家和分析师在数据探索和结果展示中更有效地传达信息。

    腾讯&阿里&携程面试题汇总(精华版).pdf

    腾讯&阿里&携程面试题汇总(精华版)

    pypy2-v6.0.0-s390x.tar.bz2

    Python库是一组预先编写的代码模块,旨在帮助开发者实现特定的编程任务,无需从零开始编写代码。这些库可以包括各种功能,如数学运算、文件操作、数据分析和网络编程等。Python社区提供了大量的第三方库,如NumPy、Pandas和Requests,极大地丰富了Python的应用领域,从数据科学到Web开发。Python库的丰富性是Python成为最受欢迎的编程语言之一的关键原因之一。这些库不仅为初学者提供了快速入门的途径,而且为经验丰富的开发者提供了强大的工具,以高效率、高质量地完成复杂任务。例如,Matplotlib和Seaborn库在数据可视化领域内非常受欢迎,它们提供了广泛的工具和技术,可以创建高度定制化的图表和图形,帮助数据科学家和分析师在数据探索和结果展示中更有效地传达信息。

    基于C语言实现列车车厢重排问题(源码)

    列车车厢重排问题是经典的组合优化问题,也称为车厢调度问题或车厢排序问题。它的问题描述如下:有一列火车,列车由多节车厢组成,每个车厢上都有一个唯一的标识号。现在需要将这些车厢按照指定的顺序重新排列,使得满足一定的条件,例如车厢编号的升序或降序排列,或者满足某些车厢之间的关系等。

    报告-《新范式 新时代 新机会》dr.pptx

    报告-《新范式 新时代 新机会》dr.pptx

    xx集团数字化转型方案ss.pptx

    xx集团数字化转型方案ss.pptx

    pypy3.7-v7.3.3rc2-osx64.tar.bz2

    Python库是一组预先编写的代码模块,旨在帮助开发者实现特定的编程任务,无需从零开始编写代码。这些库可以包括各种功能,如数学运算、文件操作、数据分析和网络编程等。Python社区提供了大量的第三方库,如NumPy、Pandas和Requests,极大地丰富了Python的应用领域,从数据科学到Web开发。Python库的丰富性是Python成为最受欢迎的编程语言之一的关键原因之一。这些库不仅为初学者提供了快速入门的途径,而且为经验丰富的开发者提供了强大的工具,以高效率、高质量地完成复杂任务。例如,Matplotlib和Seaborn库在数据可视化领域内非常受欢迎,它们提供了广泛的工具和技术,可以创建高度定制化的图表和图形,帮助数据科学家和分析师在数据探索和结果展示中更有效地传达信息。

    Qlik Sense Desktop 2024

    Qlik Sense Desktop 是一个 Windows 应用程序,让个人用户有机会使用 Qlik Sense 并通过多个数据源以简便的拖放操作创建个性化、交互式数据可视化、报告和仪表板。个人和内部业务使用时免费。 您可以创建自己可以重复使用的 Qlik Sense 应用程序,并且可以修改和与他人共享,而不需要部署和管理大量的商业应用程序。 Qlik Sense是一个强大的可视化BI报表开发平台,Qlik Sense Desktop是Windows桌面版的客户端,通常还有Enterprise HUB云端版。

    Java项目源码_ssm_高校学生选课系统项目源码.rar

    **Java项目源码_ssm_高校学生选课系统项目源码.rar** 本资源为一个基于Java技术栈的高校学生选课系统项目源码,采用了SSM(Spring、SpringMVC、MyBatis)框架进行开发。项目涵盖了学生选课、课程管理、教师管理、学生管理等功能模块,可以为高校教务管理提供有力支持。 项目结构清晰,模块化设计,便于二次开发和定制。主要包括以下几个模块: 1. 用户认证模块:包括用户登录、注册、权限验证等功能,支持多角色(学生、教师、管理员)登录和权限控制。 2. 学生选课模块:学生可以查看课程信息、选课、退课等操作,支持选课时间、人数限制等规则。 3. 课程管理模块:教师可以发布课程、编辑课程信息、上传课程资源等操作,支持课程分类、标签等功能。 4. 教师管理模块:管理员可以添加、编辑、删除教师信息,支持教师角色权限分配。 5. 学生管理模块:管理员可以添加、编辑、删除学生信息,支持学生角色权限分配。 6. 成绩管理模块:教师可以录入、编辑、查看学生成绩,支持成绩统计和导出功能。 7. 通知公告模块:管理员可以发布通知公告,支持公告分类、置顶等功能。 8. 系统设置模块:管理员可以配置系统参数、数据备份、日志查看等操作,保障系统稳定运行。 本项目源码可以为高校教务管理提供有力支持,同时也适用于企业内部培训、教育培训等场景。如有需要,可以根据实际需求进行二次开发和定制,以满足不同场景的需求。

    asp代码ASP.NET+SQLBS模式的计算机等级考试管理系统的设计与实现(论文+源代码+开题报告)

    asp代码ASP.NET+SQLBS模式的计算机等级考试管理系统的设计与实现(论文+源代码+开题报告)本资源系百度网盘分享地址

    pypy2.7-v7.3.6-s390x.tar.bz2

    Python库是一组预先编写的代码模块,旨在帮助开发者实现特定的编程任务,无需从零开始编写代码。这些库可以包括各种功能,如数学运算、文件操作、数据分析和网络编程等。Python社区提供了大量的第三方库,如NumPy、Pandas和Requests,极大地丰富了Python的应用领域,从数据科学到Web开发。Python库的丰富性是Python成为最受欢迎的编程语言之一的关键原因之一。这些库不仅为初学者提供了快速入门的途径,而且为经验丰富的开发者提供了强大的工具,以高效率、高质量地完成复杂任务。例如,Matplotlib和Seaborn库在数据可视化领域内非常受欢迎,它们提供了广泛的工具和技术,可以创建高度定制化的图表和图形,帮助数据科学家和分析师在数据探索和结果展示中更有效地传达信息。

    星闪技术介绍.pptx

    星闪技术介绍

    《情绪管理指南》培训课件ld.pptx

    《情绪管理指南》培训课件ld.pptx

    毕业设计,前端,前端毕业设计

    毕业设计,前端,前端毕业设计

    课件-咨询IT规划方法dr.pptx

    课件-咨询IT规划方法dr.pptx

    非暴力沟通生命的语言lg.pptx

    非暴力沟通生命的语言lg.pptx

    nacos2.3.1改造适配postgresql配置文件

    nacos2.3.1改造适配postgresql配置文件,无需自己再去做数据库适配,导入数据库即可;

    高级网络人才培训专家-X00070003 第30章 配置PPP

    高级网络人才培训专家_X00070003 第30章 配置PPP

Global site tag (gtag.js) - Google Analytics