找回密码
 FreeOZ用户注册
楼主: woodheadz
打印 上一主题 下一主题

[学习深造] 发起了一个Javascript开源MVVM框架

[复制链接]
61#
 楼主| 发表于 2-10-2013 20:29:52 | 只看该作者

从mvvm的角度来看现在的这些js mvvm框架,在m端和v都没啥太本质的区别。 关键就在对“vm”的看法上。

knotjs以外js框架倾向于把vm整个作为框架的职责,这样就必然要求在配置中提供足够灵活的语法来描述vm的工作逻辑。

knot.js基本是沿用WPF的哲学,逻辑都是代码的责任,标记干的事,就是把逻辑粘合。所以knot.js的vm完全是面对开放和与代码的整合设计的。这种方式我觉得要灵活得多也简单得多。实际做下来,因为js的动态特性,knot.js比wpf的绑定要容易使用很多。

knot.js里的controller,和你发的链接里skackoverflow上的次高票观点是一致的。knot.js实际上直接用vm替换了controller.
我把user input看作成两类,一类是直接的命令,比如按钮,菜单等;一类是间接的事件,例如文字改变,选择改变等等。
knot.js不处理第一类命令。这类命令由用户直接根据命令结果操作数据即可,因为这类命令在发动的时候意图非常明确,状态非常单纯。如果要在这方面完成重用,一个附加command模式就已经足够了,knot.js不该去处理这个问题。
第二类命令常常会跟随有非常复杂的后续状态变化,而这些状态的变化逻辑绝大多数在描述绑定的时候就已经完成描述了。所以由框架处理是最好。

我回头好好看看那个贴里提到的“ Sculpture code-gen framework ”,看看他们把controller从vm独立出来的动机是什么。没准又能学到些新想法。
回复  

使用道具 举报

62#
 楼主| 发表于 2-10-2013 20:31:35 | 只看该作者
woodheadz 发表于 2-10-2013 19:29
从mvvm的角度来看现在的这些js mvvm框架,在m端和v都没啥太本质的区别。 关键就在对“vm”的看法上。

...

郁闷,貌似Sculpture code-gen framework 已经被取消了
回复  

使用道具 举报

63#
 楼主| 发表于 2-10-2013 20:35:21 | 只看该作者
coredump 发表于 2-10-2013 19:25
黑虫的很多观点也是很有价值的,面对批评不能点菜,我要具体的意见,别来宏观的,切,给你吐槽就不错了,还 ...


其实我一开始也没点菜...   我还是老老实实的从宏观上解释了knotjs的变化了啊...
唉,算了,不聊这事了
回复  

使用道具 举报

64#
 楼主| 发表于 2-10-2013 20:44:01 | 只看该作者
本帖最后由 woodheadz 于 2-10-2013 19:47 编辑
coredump 发表于 2-10-2013 19:21
QML最初的原型也是用的XML, 后来放弃了。 XML不是human readable的语言。 QML有namespace的概念。


忽然想到,你说做这么个东西出来有没有价值:

做一个类似css的东西,把界面的绑定逻辑独立放在一个文件中,用类似qml的模式去描述,使用和css一样的selector和html页面元素关联。

----------------
再想想,觉得对于网页js应用而言,这么做可能太过于重型了
回复  

使用道具 举报

65#
发表于 2-10-2013 21:17:33 | 只看该作者
woodheadz 发表于 2-10-2013 19:44
忽然想到,你说做这么个东西出来有没有价值:

做一个类似css的东西,把界面的绑定逻辑独立放在一个文 ...

我觉得这个思路是蛮有意思的,HTML最有价值的核心就是DOM模型,除此之外的会统统逐渐分离,本来也没有CSS,Opera的工程师觉得这样啰哩啰唆的到处color, font, size太不好了,于是把这些单独拿出来弄了个CSS出来,Javascript更是无心插柳弄出来的玩意,如果现有CSS出来,Netscape可能就没动力弄Javascript了。 而且不管CSS还是JS都和HTML标准没关系,他们在一起成为整个Web Platform (http://www.webplatform.org/) 生态系统的组成部分。现在的整个Web架构基本上还只是大体上实现了View部分的规范化,Model或者数据层的API才刚刚起步,Model和View的交互离规范化还很远,现在就只能在JS上做文章hack。
回复  

使用道具 举报

66#
 楼主| 发表于 2-10-2013 21:31:36 | 只看该作者
coredump 发表于 2-10-2013 20:17
我觉得这个思路是蛮有意思的,HTML最有价值的核心就是DOM模型,除此之外的会统统逐渐分离,本来也没有CSS ...


我刚才又想了下,想法又有点改变。
之前觉得重型是因为老想着放在单独的文件里,其实不一定是这样,像css一样直接放在html文件里也没啥。 我会拥着个思路重新做一个knot.js试试

这个算是今天最大的收获了,哈哈,thx
回复  

使用道具 举报

67#
 楼主| 发表于 2-10-2013 21:36:55 | 只看该作者
本帖最后由 woodheadz 于 2-10-2013 20:41 编辑
coredump 发表于 2-10-2013 20:17
我觉得这个思路是蛮有意思的,HTML最有价值的核心就是DOM模型,除此之外的会统统逐渐分离,本来也没有CSS ...


QML对逻辑是怎么处理的? 允许在QML中内嵌写逻辑吗?  还是采用和WPF类似的做法?
--------------
用js的动态特性,应该容易把逻辑镶嵌到定义里。但我还有点不确定该不该这么做。
回复  

使用道具 举报

68#
发表于 2-10-2013 21:43:26 | 只看该作者
reactive programming对于Javascript来说就像OOP对于C的样子,实现起来肯定是doable的,而且不止一种思路,但是总归会有隔靴瘙痒之感,最终的出路肯定有或者JS语言扩充规范,或者有新的语言来实现,而且reactive programming总体上来说更适合用函数式语言来做(因为CSP,call cc对于函数式语言更加自然,而reactive programming在实现层面必然是一些callbacks),除了QML外,有一个elm (elm-lang.org)函数式语言号称FRP (Functional Reactive Programming), 刚才在github上一顿搜,发现也有人用Javascript来做FRP的实践, 比如这个(https://github.com/baconjs/bacon.js)。
回复  

使用道具 举报

69#
发表于 2-10-2013 21:50:47 | 只看该作者
woodheadz 发表于 2-10-2013 20:36
QML对逻辑是怎么处理的? 允许在QML中内嵌写逻辑吗?  还是采用和WPF类似的做法?
--------------
用 ...

QML和JS是完全mix的,任何binding都可以用JS表达式,这点和XAML正好相反,当然这也有缺点,很容易让View和逻辑混在一起,需要通过编程规范来约束,比如:

  1. import QtQuick 2.0

  2. Rectangle {
  3.    id: rect
  4.    width: 400  //bind to contant
  5.    height: width / 2   //js expression, height always binds to width/2
  6.    color: getColor()  //bind to js function

  7.    function getColor () {
  8.        //js code
  9.        return "red";
  10.    }
  11. }
复制代码
回复  

使用道具 举报

70#
 楼主| 发表于 2-10-2013 22:05:45 | 只看该作者
coredump 发表于 2-10-2013 20:43
reactive programming对于Javascript来说就像OOP对于C的样子,实现起来肯定是doable的,而且不止一种思路, ...

我不大喜欢这个思路。
markup/json/qml这类描述方式的一个优势就是在描述静态关系时特别方便,比在代码里描述舒服多了。用来描述UI和数据以及逻辑块之间的关系刚刚好。 我觉得bacon走到另一个极端上了。
-----------------------
我现在的整个思路是这样,现在knot.js提供的东西,相当于镶嵌在htmltag中的style样式,我在这个基础上增加一个我称为"cbs"的东西(Cascading Binding Sheets),可以独立于html tag定义数据绑定关系。
即便是在目前的技术限制下,这方面我觉得还是有很大的想象空间的。你觉得如何?
回复  

使用道具 举报

71#
发表于 2-10-2013 22:07:38 | 只看该作者
又找到一个http://www.reactjs.com/
回复  

使用道具 举报

72#
发表于 2-10-2013 22:08:29 | 只看该作者
woodheadz 发表于 2-10-2013 21:05
我不大喜欢这个思路。
markup/json/qml这类描述方式的一个优势就是在描述静态关系时特别方便,比在代码里 ...

嗯,值得探索一下,很有意思
回复  

使用道具 举报

73#
发表于 2-10-2013 22:12:08 | 只看该作者
回复  

使用道具 举报

74#
 楼主| 发表于 2-10-2013 22:14:41 | 只看该作者
coredump 发表于 2-10-2013 20:50
QML和JS是完全mix的,任何binding都可以用JS表达式,这点和XAML正好相反,当然这也有缺点,很容易让View和 ...

嗯。我觉得用编程规范解决这个问题要好些。
我不大喜欢限制太多的框架,没必要这么不相信使用者嘛 更何况在某些情况直接嵌入javascript可能简洁很多。

===============
thanks, 我觉得我好像又欠你一顿酒了。上次的还没兑现呢
回复  

使用道具 举报

75#
发表于 2-10-2013 22:20:27 | 只看该作者
本帖最后由 black_zerg 于 2-10-2013 21:37 编辑

这类CRUD数据库的网页都不是什么火箭科技,说老实话,有点经验的怎么写都能成功。所以最重要是团队合作和维护上,逻辑必须predictable。大家都按一个规矩写,就完了。追求代码最短没有意义。就好象英文通用,大家都说英文,跟英文是不是最简洁没冗余没什么关系。

reactive programming 我能看到的好处就是缩短代码,不然到处函数好像很恶心。 但是直觉上我就觉得这个让编程难度增加很多,容易有副作用。 a = b + a , 就变成了 类似
a = function(){ b() + c()}, 然后每次 a(); 这不是更晕么。反正我是想不过来

如果说到js应用框架,认真地说我觉得对于CRUD的话,老backbone大概是在功能和平衡上做的最好的。databing的话,
初看到了觉得挺稀奇的,但总有界面复杂了之后很不靠谱的感觉,就像老丐说的,Javascript 就没有reactive programming 支持,怎么做都是模拟。 那程序员就只能认了,自己写逻辑。现在为了绑定事件什么的,都是一堆getter setter 或者watcher之类的,反正看着也都有些别扭的。

我刚写了个例子(尝试做binding) http://glue.atwebpages.com/clip.html ,感觉还是只能自己玩玩,真用还是用backbone保险。

评分

参与人数 1威望 +30 收起 理由
coredump + 30 谢谢分享!

查看全部评分

回复  

使用道具 举报

76#
 楼主| 发表于 2-10-2013 22:36:56 | 只看该作者
coredump 发表于 2-10-2013 21:12
https://github.com/reactivemanifesto/reactivemanifesto
http://redmonk.com/jgovernor/2013/09/26/why ...

如果把Reactive programming这个思想放大点,不要局限在具体语言上,那么绑定实际上就算是reactive programming的实现。但具体到在语言级建立reactive programming,我觉得可能意义不大。
有两个原因:

首先,他在数据之间建立的联系并没有任何限制,这有点类似于当年的goto语句,太过于强大以至于很容易出现产生的破坏性远远大于利益的情况。所以这个思路直接用于建立复杂逻辑模型我是不大看好的。

其次,虽然这个思想天然就可以用来描述UI和数据之间的绑定情况,但用代码去做这样的描述始终不够直观。

评分

参与人数 1威望 +20 收起 理由
coredump + 20 谢谢分享!

查看全部评分

回复  

使用道具 举报

77#
发表于 2-10-2013 22:44:55 | 只看该作者
black_zerg 发表于 2-10-2013 21:20
这类CRUD数据库的网页都不是什么火箭科技,说老实话,有点经验的怎么写都能成功。所以最重要是团队合作和维 ...

reactive programming 我能看到的好处就是缩短代码,不然到处函数好像很恶心。 但是直觉上我就觉得这个让编程难度增加很多,容易有副作用。 a = b + a , 就变成了 类似
a = function(){ b() + c()}, 然后每次 a(); 这不是更晕么。反正我是想不过来


如果支持RP,赋值和binding就必须有区分,在QML中, binding 是用:, =还是赋值语意。
reactive最适合的领域是高度实时交互的UI和其它事件来源密集,依赖复杂的应用领域,比如Game, 大数据处理之类的地方。这个和OOP有点类似,OOP最成功的领域也是GUI。

这里有个paper: http://conal.net/fran/tutorial.htm

评分

参与人数 1威望 +50 收起 理由
black_zerg + 50 谢谢分享!

查看全部评分

回复  

使用道具 举报

78#
发表于 2-10-2013 22:59:46 | 只看该作者
black_zerg 发表于 2-10-2013 21:20
这类CRUD数据库的网页都不是什么火箭科技,说老实话,有点经验的怎么写都能成功。所以最重要是团队合作和维 ...
这类CRUD数据库的网页都不是什么火箭科技

话说整个软件开发界就没有什么rocket sience的东西,别说rocket,连那点勉强够sience的基础部分也都被发掘殆尽了, 最后一个改进的排序算法 (library sort http://en.wikipedia.org/wiki/Library_sort )是2004年发现,经常被做为栗子被大家用各种姿势举着来让人震惊居然这么晚还能在这么基础的领域有所发现。

剩下的全部都是工程实践,不管是Linux Kernel还是Google集群,新语言,新数据库,新的开发哲学,统统都是在实践过程中对基础理论的应用和微调,所以面对现实,做一个IT农场的码农是幸福的,做一个计算机科学家是痛苦的。

CRUD虽然基础得很,可是一个ORM整个软件开发界死啃了这么多年也没什么像样的东西出来,只好说这玩意本身就是evil的,然后发现OOP也evil了,SQL也evil了,搞IT的,虽然不是搞科学,可是科学精神那是一点都不缺的,得有足够的自我否定精神和大胆假设,小心求证,不断实践的热情。

虽然已经很久没人发明出钢琴了,可是能弹一首好曲子还是很让人赞叹的,编程上也类似。
回复  

使用道具 举报

79#
发表于 2-10-2013 23:14:19 | 只看该作者
black_zerg 发表于 2-10-2013 21:20
这类CRUD数据库的网页都不是什么火箭科技,说老实话,有点经验的怎么写都能成功。所以最重要是团队合作和维 ...

backbone的实现就的确挺不错的。
回复  

使用道具 举报

80#
 楼主| 发表于 2-10-2013 23:31:16 | 只看该作者
本帖最后由 woodheadz 于 2-10-2013 22:39 编辑
coredump 发表于 2-10-2013 22:14
backbone的实现就的确挺不错的。


在我现在做的NovaMind html5版本中就有一个和backbone很像的数据底层。为系统的数据层提供事件(包括自动生成的datachange)和序列化支持。和backbone不同的是我的底层序列化的结果是xml,因为NovaMind的数据文件格式基于XML。
javascript没有元数据,所以这个部分的实现无论我的数据底层和backbone都没办法做得像C#里的那么优雅。很期待anders在typescript里也创建类似的东西,到时候就很爽了。

但我觉得backbone这类的东西离UI还是远了点,把UI的繁琐工作拿出来交给一个前端框架还是很有必要的。

回复  

使用道具 举报

81#
 楼主| 发表于 3-10-2013 00:09:32 | 只看该作者
coredump 发表于 2-10-2013 20:50
QML和JS是完全mix的,任何binding都可以用JS表达式,这点和XAML正好相反,当然这也有缺点,很容易让View和 ...

还有个关于QML的问题请教一下:QML支持数据验证不? 怎么支持的?
回复  

使用道具 举报

82#
 楼主| 发表于 3-10-2013 01:21:57 | 只看该作者
本帖最后由 woodheadz 于 3-10-2013 00:24 编辑
coredump 发表于 2-10-2013 19:25
黑虫的很多观点也是很有价值的,面对批评不能点菜,我要具体的意见,别来宏观的,切,给你吐槽就不错了,还 ...


刚才好好想了下,又把帖子从头到尾看了一遍,我这次发火了,这是我的不对。
我只觉得黑虫的态度言语让我很不舒服,但实际上可能是我的那句“让设计师奔溃”先让他不舒服的。

看来我身上的确是有一点要不得的精英主义思想,另外也情绪和判断力也太容易受自己对人的成见影响了。

在此郑重向 @black_zerg 道歉,这次是我错了,请你原谅。收回"stay away from my thread",欢迎你在我的帖子里发言。

以后继续努力,以老丐的签名“言者所在意,得意而忘言”为目标,提高自己。 顺便谢谢 @coredump 提醒。

评分

参与人数 3威望 +150 收起 理由
我无聊 + 50 恭喜你!
black_zerg + 50 赞一个!
ubuntuhk + 50 赞一个!

查看全部评分

回复  

使用道具 举报

83#
发表于 3-10-2013 08:07:26 | 只看该作者
woodheadz 发表于 2-10-2013 23:09
还有个关于QML的问题请教一下:QML支持数据验证不? 怎么支持的?

TextInput专门有个validator的属性,内置IntValidator, DoubleValidator和RegExpValidator
在此基础上可以添加自定义的validator,比如DateValidator之类

http://qt-project.org/doc/qt-4.8/texthandling.html
http://qt-project.org/forums/viewthread/18044

评分

参与人数 1威望 +50 收起 理由
woodheadz + 50 thanks

查看全部评分

回复  

使用道具 举报

84#
发表于 3-10-2013 12:06:39 | 只看该作者
woodheadz 发表于 3-10-2013 00:21
刚才好好想了下,又把帖子从头到尾看了一遍,我这次发火了,这是我的不对。
我只觉得黑虫的态度言语让 ...
以后继续努力,以老丐的签名“言者所在意,得意而忘言”为目标,提高自己。

还借我签名用,三顿饭钱了
回复  

使用道具 举报

85#
 楼主| 发表于 3-10-2013 12:38:53 | 只看该作者
coredump 发表于 3-10-2013 11:06
还借我签名用,三顿饭钱了

回复  

使用道具 举报

86#
发表于 3-10-2013 15:07:32 | 只看该作者
my new demo for '2 way binding'
http://glue.atwebpages.com/clip/Clip.html

评分

参与人数 1威望 +50 收起 理由
coredump + 50 赞,talk is cheap

查看全部评分

回复  

使用道具 举报

87#
 楼主| 发表于 3-10-2013 16:28:32 | 只看该作者
刚才改了改,现在knot.js支持也cbs的形式嵌入配置了。感觉还不错
cbs语法基本和css差不多,在selector方面在css selector的基础上增加了一个新的,
例如: #dataList>div span [0]
最后的这个"[0]"表示选择结果中的第一个。 这和css selector的nth-child不一样,这表示是在选择结果集中德顺序。
现在还没开始支持函数,我仔细考虑和尝试了下,还是暂时不打算直接支持在cbs内部嵌入函数了。增加很多复杂性但得到的好处实在有限。
但我打算支持在 cbs script block中用js加入converter,validator的定义,这样比直接应用在javascript的好处是knot.js可以控制这些对象变量的命名空间,防止污染global scope. 当然用户还是可以像原来那样直接引用js中德对象

使用cbs的一个巨大的好处就是cbs使用和css一样的selector,意味着你很少需要为cbs而修改你的html。这直接把knotjs的“注入式”从代码级提升到了html级。现在使用knots.js的话要退出成本基本为0了。以前你还得清理下html中没用的tag,现在直接删掉cbs block就结了。

下面是使用cbs重新定义后的example html部分。代码部分和原来比基本没有变动。
  1. <script type="text/cbs">
  2.         body{
  3.             knotDataContext:/model
  4.         }

  5.         #dataList{
  6.             foreach:*list;
  7.             @itemCreated:onItemCreated;
  8.         }

  9.         #dataList>div{
  10.             class:*isSelected=>boolToClassConverter;
  11.         }

  12.         #dataList>div span [0]{
  13.             text:*sex=>sexToTitle
  14.         }
  15.         #spanName{
  16.             text:*name
  17.         }
  18.         #spanAge{
  19.             text:*age
  20.         }
  21.         #spanFrom{
  22.             text:*from
  23.         }


  24.         #editArea input[type="text"] [0]{
  25.             value:*selected.name =!mustNotNull & duplicatedName;
  26.             disabled:*selected=>enableControl
  27.         }
  28.         #editArea .errorMessage [0]{
  29.             style:{
  30.                 display:!selected.name=>visibleControl
  31.                 };
  32.             text:!selected.name
  33.         }

  34.         #editArea input[type="text"] [1]{
  35.             value:*selected.age =!mustNotNull & checkInteger&ageRange;
  36.             disabled:*selected=>enableControl
  37.         }
  38.         #editArea .errorMessage [1]{
  39.             style:
  40.                 {
  41.                     display:!selected.age=>visibleControl
  42.                 };
  43.             text:!selected.age
  44.         }

  45.         #editArea select [0]{
  46.             selected:*selected.sex;
  47.             disabled:*selected=>enableControl
  48.         }
  49.         #editArea select [1]{
  50.             selected:*selected.from;
  51.             disabled:*selected=>enableControl;
  52.             foreach:/countries
  53.         }

  54.         #selectFromOptionTemplate{
  55.             text:--self
  56.         }

  57.         #buttonRemove{
  58.             disabled:*selected=>enableControl
  59.         }
  60.     </script>


  61. <body onload="onLoad();">
  62. <h2>Knot.js Example</h2>
  63. <div>
  64.     <div id="dataList">
  65.         <div>
  66.             <span id="spanTitle"> </span><span id="spanName"></span>
  67.             <hr/>
  68.             Age:<span id="spanAge"></span>,
  69.             From:<span id="spanFrom"></span>
  70.         </div>
  71.     </div>
  72.     <div id="editArea">
  73.         <div>
  74.             <div class="fieldTitle">Name:</div>
  75.             <input type="text"/>
  76.             <span class="errorMessage"></span>
  77.         </div>
  78.         <div>
  79.             <div class="fieldTitle">Age:</div>
  80.             <input type="text"/>
  81.             <span class="errorMessage"></span>
  82.         </div>
  83.         <div>
  84.             <div class="fieldTitle">Sex:</div>
  85.             <select>
  86.                 <option>Male</option>
  87.                 <option>Female</option>
  88.             </select>
  89.         </div>
  90.         <div>
  91.             <div class="fieldTitle">From:</div>
  92.             <select>
  93.                 <option id="selectFromOptionTemplate"></option>
  94.             </select>
  95.         </div>

  96.         <p>
  97.             <button onclick="onNew();">New</button>

  98.             <button id="buttonRemove" onclick="onRemove()">Delete</button>
  99.         </p>
  100.     </div>
  101. </div>
  102. </body>
复制代码

评分

参与人数 1威望 +50 收起 理由
coredump + 50 你太有才了!

查看全部评分

回复  

使用道具 举报

88#
发表于 3-10-2013 17:10:37 | 只看该作者
两位真是爱动手的好同学啊,就我抄着手光说不练 team里如果都是你们这样的淫才就享福了。
@woodheadz 现在的实现就漂亮多了, 你可以顺便看看Mozilla的XBL: http://www-archive.mozilla.org/projects/xbl/xbl2.html 找点灵感或者教训, XBL目前正在被W3C标准化中, 不过是用XML实现的,不如CSS风格来得简练。
回复  

使用道具 举报

89#
 楼主| 发表于 3-10-2013 17:17:47 | 只看该作者
coredump 发表于 3-10-2013 16:10
两位真是爱动手的好同学啊,就我抄着手光说不练 team里如果都是你们这样的淫才就享福了。
@woodheadz 现 ...

现在的实现是看起来舒服,但做的时候需要多费点功夫,你得通过selector找到element才能明白绑定的对象。这对于用户而言也是一个成本。

把绑定信息从html tag里提到cbs里的综合效果还是不能与把style提到css里的综合效果比。主要原因是css应用的时候selector的覆盖范围往往是成片覆盖,而cbs实用时多数时候一个cbs只会对应一个element。 这也是我为什么在css selector的基础上增加了个索引的缘故。

不过我觉得多花的这个成本还是值得的,我正在把公司项目里之前做的绑定配置一个个全部提出来
回复  

使用道具 举报

90#
 楼主| 发表于 3-10-2013 17:36:54 | 只看该作者
coredump 发表于 3-10-2013 16:10
两位真是爱动手的好同学啊,就我抄着手光说不练 team里如果都是你们这样的淫才就享福了。
@woodheadz 现 ...

在cbs里使用扫射也还是很爽  
比如以前我界面上好几个控件都有单独针对是否有当前选择项而进行的disable绑定,需要每个控件单独配置。现在就简单了,一个cbs搞定:
  1. .disableWhenNoSelection{
  2.             disabled:*selected=>enableControl;
  3.         }
复制代码
回复  

使用道具 举报

您需要登录后才可以回帖 登录 | FreeOZ用户注册

本版积分规则

小黑屋|手机版|Archiver|FreeOZ论坛

GMT+11, 27-10-2024 08:33 , Processed in 0.053989 second(s), 52 queries , Gzip On, Redis On.

Powered by Discuz! X3.2

© 2001-2013 Comsenz Inc.

快速回复 返回顶部 返回列表