• 浅谈tornado项目应用设计


    一.预备知识  

     最近开始尝试做一些tornado商城项目,在开始之前需要引入一些项目设计知识,如接口,抽象方法抽象类,组合,程序设计原则等,个人理解项目的合理设计可增加其灵活性,降低数据之间的耦合性,提高稳定性,下面介绍一些预备知识 

    1.接口

      其实py中没有接口这个概念。要想实现接口的功能,可以通过主动抛出异常来实现

      接口作用:对派生类起到限制的作用

    例:

    #!/usr/bin/env python
    # -*- coding: utf-8 -*-
    """
    接口,python中的接口,通过在父类中主动抛出异常实现
    接口的作用:起到了限制的作用
    """
    
    class IFoo:
        def fun1(self):
            pass
            raise Exception("----")
    
    class Bar(IFoo):
        def fun1(self):
            #方法名必须和父类中的方法名相同,不然没办法正常执行,会抛出异常
            print("子类中如果想要调用父类中的方法,子类中必须要有父类中的方法名")
        def fun2(self):
            print("test")
    
    obj = Bar()
    obj.fun2()

    2.抽象方法抽象类

      抽象类,抽象方法是普通类和接口的综合,即可以继承也可以起到限制作用

      由于python 本身没有抽象类、接口的概念,所以要实现这种功能得abc.py 这个类库,具体实现方法如下 :

    #!/usr/bin/env python
    # -*- coding: utf-8 -*-
    """
    抽象类,抽象方法
    抽象类,抽象方法是普通类和接口的综合,即可以继承也可以起到限制作用
    """
    
    import abc
    class Foo(metaclass=abc.ABCMeta):
        def fun1(self):
            print("fun1")
    
        def fun2(self):
            print("fun2")
    
        @abc.abstractclassmethod
        def fun3(self):
            pass
    
    
    class Bar(Foo):
        def fun3(self):
            print("子类必须有父类的抽象方法名,不然会抛出异常")
    
    
    obj = Bar()
    obj.fun1()
    obj.fun2()
    obj.fun3()

     3.组合

      python中“多用组合少用继承”,因为继承的偶合性太强,可以把基类,当做参数传入派生类中,用于解偶

    如;

    #!/usr/bin/env python
    # -*- coding: utf-8 -*-
    #继承
    
    class Animals:
        def eat(self):
            print(self.Name + " eat")
        def drink(self):
            print(self.Name + " drink")
    
    class Person(Animals):
        def __init__(self, name):
            self.Name = name
    
        def think(self):
            print(self.Name + " think")
    obj = Person("user1")
    obj.drink()
    obj.eat()
    obj.think()
    继承
    class Animals:
        def __init__(self,name):
            self.Name = name
    
        def eat(self):
            print(self.Name + " eat")
    
        def drink(self):
            print(self.Name + " drink")
    
    class Person:
        def __init__(self, obj):
            self.obj = obj
    
        def eat(self):
            self.obj.eat()
    
        def think(self,name):
            print(name + " think")
    
    animals = Animals("animals")
    obj = Person(animals)
    obj.think("person")
    obj.eat()
    组合

    4.依赖注入

      刚接触理解的比较浅显

      像上一例中,如果有多层关系时,需要传入多个对象,为了解决这个问题就引入了依赖注入,如上例在Person类实例化时自动传入Animals对象

      那么,在引入依赖注入时先了解一下python类实例化过程中背后做了什么事情

    class Foo:
        def __init__(self):
            self.name = 111
        
        
        def fun(self)
            print(self.name)
            
    obj = Foo() #obj是Foo的实例化对象

    在python中一切皆对象,Foo是通过type类创建的

    例:

    #!/usr/bin/env python
    # -*- coding:utf-8 -*-
    
    class MyType(type):
    
        def __call__(cls, *args, **kwargs):
            obj = cls.__new__(cls, *args, **kwargs)
            obj.__init__(*args, **kwargs)
            return obj
    
    
    class Foo(metaclass=MyType):
    
        def __init__(self, name):
            self.name = name
    
        def f1(self):
            print(self.name)
     解释器解释:
        1.遇到 class Foo,执行type的__init__方法
        1.Type的init的方法里做什么么呢?不知道
            obj = Foo(123)
        3.执行Type的 __call__方法
            执行Foo类的 __new__方法
            执行Foo类的 __init__ 方法

    先来了解几个概念

    new 和 __init()和__metaclass__:

    • __new__函数是实例一个类所要调用的函数,每当我们调用obj = Foo()来实例一个类时,都是先调用__new__()

    • 然后再调用__init__()函数初始化实例. __init__()在__new__()执行后执行,

    • 类中还有一个属性 __metaclass__,其用来表示该类由 谁 来实例化创建,所以,我们可以为 __metaclass__ 设置一个type类的派生类,从而查看 类 创建的过程。

    那么依赖注入的实现方法,自定义一个type方法,实例化类的时候指定由自定义的type方法创建,具体实现方法如下:

    #!/usr/bin/env python
    # -*- coding: utf-8 -*-
    # 依赖注入应用
    #DI
    class Mapper:
        __mapper_relation ={}
    
        @staticmethod
        def register(cls,value):
            Mapper.__mapper_relation[cls] = value
    
        @staticmethod
        def exist(cls):
            if cls in Mapper.__mapper_relation:
                return True
            return False
    
        @staticmethod
        def value(cls):
            return Mapper.__mapper_relation[cls]
    
    
    class MyType(type):
        def __call__(self, *args, **kwargs):
            obj = self.__new__(self, *args, **kwargs)
            arg_list = list(args)
            if Mapper.exist(self):
                value=Mapper.value(self)
                arg_list.append(value)
            obj.__init__(*arg_list, **kwargs)
            return obj
    
    
    #定义由谁来实例化
    class Foo(metaclass=MyType):
        def __init__(self,name):
            self.name = name
    
        def f1(self):
            print(self.name)
    
    class Bar(metaclass=MyType):
        def __init__(self,name):
            self.name = name
    
        def f1(self):
            print(self.name)
    
    Mapper.register(Foo,"test1")
    Mapper.register(Bar,"test12")
    f=Foo()
    print(f.name)
    依赖注入应用

    5.程序的设计原则

      1. 单一责任原则       

    一个对象只对一个元素负责

    优点;

      消除耦合,减小因需求变化引起代码僵化

      2.开放封闭原则

        对扩展开放,对修改关闭

        优点:

          按照OCP原则设计出来的系统,降低了程序各部分之间的耦合性,其适应性、灵活性、稳定性都比较好。当已有软件系统需要增加新的功能时,

          不需要对作为系统基础的抽象层进行修改,只需要在原有基础上附加新的模块就能实现所需要添加的功能。增加的新模块对原有的模块完全没有影响或影响很小,

          这样就无须为原有模块进行重新测试

        如何实现 ? 

          在面向对象设计中,不允许更必的是系统的抽象层,面允许扩展的是系统的实现层,所以解决问题的关键是在于抽象化。

          在面向对象编程中,通过抽象类及接口,规定具体类的特征作为抽象层,相对稳定,不需要做更改的从面可以满足“对修改关闭”的原则;而从抽象类导出的具体 类可以

          改变系统 的行为,从而满足“对扩展开放的原则"

      3.里氏替换原则  

        可以使用任何派生类替换基类
        优点:
          可以很容易的实现同一父类下各个子类的互换,而客户端可以毫不察觉
      4.接口分享原则
        对于接口进行分类避免一个接口的方法过多,避免”胖接口"
        优点:
          会使一个软件系统功能扩展时,修改的压力不会传到别的对象那里
        如何实现 ?
          得用委托分离接口
          利用多继承分离接口

      5.依赖倒置原则    

    隔离关系,使用接口或抽象类代指
    高层次的模块不应该依赖于低层次的模块,而是,都应该依赖于抽象
        优点:
          使用传统过程化程序设计所创建的依赖关系,策略依赖于细节,这是糟糕的,因为策略受到细节改变的影响。
          依赖倒置原则使细节和策略都依赖于抽象,抽象的稳定性决定了系统的稳定性
      6.依赖注入和控制反转原则
        
        使用钩子再原来执行流程中注入其他对象

    二.tornado项目设计实例

      此实例只包含登录,写此实例目的在于更好的理解及应用以上的内容

    1.目录规划

      

    注:

      Infrastructure 目录:公共组件目录

      Model:业务逻辑处理目录

      Repository: 数据仓库及数据处理目录

      Statics:静态文件目录如(css,js,images等)

      UIAdmin: UI层

      Views:模板文件目录

      Application.py : 服务启动文件

    2.业务访问流程

       介绍完目录规划,那就来讲讲业务访问流程及数据走向

    1. 启动服务后,客户端访问URL,根据tornado路由找到相对的handler进行处理
    2. 找到handler后其相对方法(get/post/delete/put)中调用Model逻辑处理层方法进行处理并接收处理结果
    3. Model逻辑处理层需
      • 创建接口
      • 建模
      • 创建协调层

       创建完之后 ,由协调层(这里通用Services)调用数据层方法并接收处理结果返回给handler

      4.数据处理层接收到Model调用后,处理数据并将数据返回给Model业务逻辑处理层

      5.最终handler接收到最终结果,进行判断处理,并将处理结果返回给用户

    3.具体实施

      按照以上的访问流程来看配置文件

    1.启动文件,路由关系配置

    #!/usr/bin/env python
    # -*- coding:utf-8 -*-
    import tornado.ioloop
    import tornado.web
    from UIAdmin.Controllers import Account
    from UIAdmin.Controllers import Region
    from UIAdmin.Controllers import Customer
    from UIAdmin.Controllers import Merchant
    from UIAdmin import mapper
    
    settings = {
        'template_path': 'Views',
        'static_path': 'Statics',
        'static_url_prefix': '/statics/',
    }
    application = tornado.web.Application([
        (r"/login", Account.LoginHandler),
        (r"/check", Account.CheckCodeHandler),
    ],**settings)
    
    if __name__ == "__main__":
        application.listen(8000)
        tornado.ioloop.IOLoop.instance().start()
    Application.py

    注:

    settings 中指定配置,如模板文件路径,静态文件路径等

    application :路由配置,那个路径由那个handler进行处理

    2.handler配置 

    #!/usr/bin/env python
    # -*- coding: utf-8 -*-
    import io
    from Infrastructure.Core.HttpRequest import BaseRequestHandler
    from Infrastructure.utils import check_code
    from Model.User import UserService
    
    
    class LoginHandler(BaseRequestHandler):
        def get(self, *args, **kwargs):
            self.render("Admin/Account/login.html")
    
        def post(self, *args, **kwargs):
            username = self.get_argument("username",None)
            email = self.get_argument("email",None)
            pwd = self.get_argument("pwd",None)
            code = self.get_argument("checkcode",None)
            service = UserService()
            result = service.check_login(user=username,email=email,pwd=pwd)
            #obj封装了所有的用户信息,UserModel对象
            if result and code.upper() == self.session["CheckCode"].upper():
                self.session['username'] = result.username
                self.redirect("/ProvinceManager.html")
            else:
                self.write("alert('error')")
    hanler.py

    handler中主要是针对数据访问方式的不同,给出不同的处理方法,并将结果返回给客户端

    3.Model 逻辑处理层

      逻辑处理层中,着重看的有三点
    1. 建模
    2. 接口
    3. 协调

     建模  

    #!/usr/bin/env python
    # -*- coding: utf-8 -*-
    #建模
    from Infrastructure.DI.Meta import DIMetaClass
    
    class VipType:
    
        VIP_TYPE = (
            {'nid': 1, 'caption': '铜牌'},
            {'nid': 2, 'caption': '银牌'},
            {'nid': 3, 'caption': '金牌'},
            {'nid': 4, 'caption': '铂金'},
        )
    
        def __init__(self, nid):
            self.nid = nid
    
        def get_caption(self):
            caption = None
    
            for item in VipType.VIP_TYPE:
                if item['nid'] == self.nid:
                    caption = item['caption']
                    break
            return caption
    
        caption = property(get_caption)
    
    
    class UserType:
    
        USER_TYPE = (
            {'nid': 1, 'caption': '用户'},
            {'nid': 2, 'caption': '商户'},
            {'nid': 3, 'caption': '管理员'},
        )
    
        def __init__(self, nid):
            self.nid = nid
    
        def get_caption(self):
            caption = None
    
            for item in UserType.USER_TYPE:
                if item['nid'] == self.nid:
                    caption = item['caption']
                    break
            return caption
    
        caption = property(get_caption)
    
    
    class UserModel:
        def __init__(self, nid, username,password, email, last_login, user_type_obj, vip_type_obj):
            self.nid = nid
            self.username = username
            self.email = email
            self.password = password
            self.last_login = last_login
            self.user_type_obj = user_type_obj
            self.vip_type_obj = vip_type_obj
    建模

     接口

    IUseRepository类:接口类,用于约束数据库访问类的方法

    class IUserRepository:
    
        def fetch_one_by_user(self,user,pwd):
            """
            根据用户名和密码获取对象
            :param user:
            :param pwd:
            :return:
            """
    
        def fetch_one_by_email(self, user, pwd):
            """
            根据邮箱和密码获取对象
            :param user:
            :param pwd:
            :return:
            """
    接口

    协调

    协调作用主要是调用数据处理层的方法,并将数据处理层处理后的结果返回给它的上一层的调度者

    class UserService(metaclass=DIMetaClass):
        def __init__(self, user_repository):
            """
            :param user_repository: 数据仓库对象
            """
            self.userRepository = user_repository
    
        def check_login(self,user,email,pwd):
            if user:
                #数据仓库执行SQL后返回的字典
                #{"nid":1,username:xxx,vip:2,usertype:1}
                ret = self.userRepository.fetch_one_by_user(user,pwd)
            else:
                ret = self.userRepository.fetch_one_by_email(email,pwd)
            return ret
    协调层UserService

    4.Repository数据处理层

      将处理后结果(usermodel对象)返回给上一层调度者(UserService)

    #!/usr/bin/env python
    # -*- coding:utf-8 -*-
    #数据表创建 
    from sqlalchemy.ext.declarative import declarative_base
    from sqlalchemy import Column
    from sqlalchemy import Integer, Integer, CHAR, VARCHAR, ForeignKey, Index, DateTime, DECIMAL, TEXT
    from sqlalchemy.orm import sessionmaker, relationship
    from sqlalchemy import create_engine
    
    engine = create_engine("mysql+pymysql://root:123@127.0.0.1:3306/ShoppingDb?charset=utf8", max_overflow=5)
    
    Base = declarative_base()
    
    
    class Province(Base):
        """
        省
        """
        __tablename__ = 'province'
        nid = Column(Integer, primary_key=True)
        caption = Column(VARCHAR(16), index=True)
    
    
    class City(Base):
        """
        市
        """
        __tablename__ = 'city'
        nid = Column(Integer, primary_key=True)
        caption = Column(VARCHAR(16), index=True)
        province_id = Column(Integer, ForeignKey('province.nid'))
    
    
    class County(Base):
        """
        县(区)
        """
        __tablename__ = 'county'
        nid = Column(Integer, primary_key=True)
        caption = Column(VARCHAR(16), index=True)
        city_id = Column(Integer, ForeignKey('city.nid'))
    
    
    class UserInfo(Base):
        """
        用户信息
        """
    
        __tablename__ = 'userinfo'
    
        nid = Column(Integer, primary_key=True)
    
        USER_TYPE = (
            {'nid': 1, 'caption': '用户'},
            {'nid': 2, 'caption': '商户'},
            {'nid': 3, 'caption': '管理员'},
        )
        user_type = Column(Integer)
    
        VIP_TYPE = (
            {'nid': 1, 'caption': '铜牌'},
            {'nid': 2, 'caption': '银牌'},
            {'nid': 3, 'caption': '金牌'},
            {'nid': 4, 'caption': '铂金'},
        )
        vip = Column(Integer)
    
        username = Column(VARCHAR(32))
        password = Column(VARCHAR(64))
        email = Column(VARCHAR(64))
    
        last_login = Column(DateTime)
        ctime = Column(DateTime)
    
        __table_args__ = (
            Index('ix_user_pwd', 'username', 'password'),
            Index('ix_email_pwd', 'email', 'password'),
        )
    SqlAchemyOrm.py
    #!/usr/bin/env python
    # -*- coding: utf-8 -*-
    from Model.User import IUserRepository
    from Model.User import UserModel
    from Model.User import UserType
    from Model.User import VipType
    from Repository.Admin.DbConnection import DbConnection
    
    
    class UserRepository(IUserRepository):
    
        def __init__(self):
            self.db_conn = DbConnection()
    
        def fetch_one_by_email(self, email, password):
            ret = None
    
            cursor = self.db_conn.connect()
            sql = """select nid,username,password,email,last_login,vip,user_type from userinfo where email=%s and password=%s"""
            cursor.execute(sql, (email, password))
            db_result = cursor.fetchone()
            self.db_conn.close()
            print(type(db_result), db_result)
            if db_result:
                ret = UserModel(nid=db_result['nid'],
                                username=db_result['username'],
                                password=db_result['password'],
                                email=db_result['email'],
                                last_login=db_result['last_login'],
                                user_type_obj=UserType(nid=db_result['user_type']),
                                vip_type_obj=VipType(nid=db_result['vip']),)
                return ret
            return db_result
    
        def fetch_one_by_user(self, username, password):
            ret = None
            cursor = self.db_conn.connect()
            sql = """select nid,username,password,email,last_login,vip,user_type from userinfo where username=%s and password=%s"""
            cursor.execute(sql, (username, password))
            db_result = cursor.fetchone()
            self.db_conn.close()
    
            if db_result:
        #建模,将usermodel对象返回给上一层调用者,因为要向用户展示的user_type不可能为1,2这些数据而应该是相对的caption
                ret = UserModel(nid=db_result['nid'],
                                username=db_result['username'],
                                password=db_result['password'],
                                email=db_result['email'],
                                last_login=db_result['last_login'],
                                user_type_obj=UserType(nid=db_result['user_type']),
                                vip_type_obj=VipType(nid=db_result['vip']),)
                return ret
            return db_result
    数据处理层

    5.Handler最终处理

      接收到最终处理结果后判断,并返回数据给用户

    注:

      有没有注意到UserService是怎么和数据处理层建立联系的?

      这里我们用到了依赖注入,具体配置如下:  

    #!/usr/bin/env python
    # -*- coding:utf-8 -*-
    #依赖注入
    
    class DIMapper:
    
        __mapper_dict = {}
    
        @staticmethod
        def inject(cls, arg):
            if cls not in DIMapper.__mapper_dict:
                DIMapper.__mapper_dict[cls] = arg
    
        @staticmethod
        def get_mappers():
            return DIMapper.__mapper_dict
    
    
    class DIMetaClass(type):
    
        def __call__(cls, *args, **kwargs):
            # 获取配置的对应的对象,携带进入
            obj = cls.__new__(cls, *args, **kwargs)
    
            mapper_dict = DIMapper.get_mappers()
            if cls in mapper_dict:
                cls.__init__(obj, mapper_dict[cls])
            else:
                cls.__init__(obj, *args, **kwargs)
            return obj
    Meta.py

    #!/usr/bin/env python
    # -*- coding: utf-8 -*-
    # 依赖注入绑定
    from Infrastructure.DI import Meta
    from Model.User import UserService
    from Repository.Admin.UserRepository import UserRepository
    
    
    Meta.DIMapper.inject(UserService,UserRepository())
    mapper.py

    6.静态文件代码

    <!DOCTYPE html>
    <html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
        <meta name="viewport" content="width=device-width" />
        <meta http-equiv="X-UA-Compatible" content="IE=8" />
        <title>购物商城</title>
        <link href="/statics/Admin/Css/common.css" rel="stylesheet" />
        <link href="/statics/Admin/Css/account.css" rel="stylesheet" />
    </head>
    <body>
    
        <div class="account-container bg mt10">
            <div class='header clearfix'>
                    <div>
                        <a href="/home/index">
                            <img src="/statics/Admin/Images/mll_logo.gif">
                        </a>
                    </div>
                </div>
        </div>
    
        <div class='account-container mt30'>
            
            <div class='body clearfix pd10' style='position: relative;'>
                <div class='logo left'>
                    <img style='height:350px;' src="/statics/Admin/Images/login_logo.png" />
                </div>
                <div class='login left mt30'>
                    <form id='Form' action='/login' method='POST'>
                        
                        <div class='group mt10'>
                            <label class='tip'><span class="red">*</span>用户名:</label>
                            <input type='text' require='true' label='用户名' Field='string' range='4-40' name='username' />
                            <i class='i-name'></i>
                        </div>
                      
                        <div class='group'>
                            <label class='tip'><span class="red">*</span>密码:</label>
                            <input  type='password' require='true'  label='密码' min-len='6' name='pwd' />
                            <i class='i-pwd'></i>
                        </div>
                       
                        <div class='group'>
                            <label class='tip'><span class="red">*</span>验证码:</label>
                            <input  type='text' require='true' label='验证码' style='80px;' name='checkcode' />
                            <a style='125px;display:inline-block;'><img class='checkcode' onclick='ChangeCode();' id='imgCode' src='/check' /></a>
                        </div>
                        <div class='group font12 mb0'>
                            <label class='tip'></label>
                            <label style='246px;display: inline-block;'>
                                <input id='protocol' name='protocol' type='checkbox' checked='checked' />
                                <span>自动登录</span>
                                <span class='ml10'><a href='#'>忘记密码?</a></span>
                            </label>
                        </div>
                        <div class='group mt0'>
                            <label class='tip'></label>
                            <input type='submit' class='submit' value='登    录' />
                        </div>
                    </form>
                    
                    <div class='go-register'><a href='#'>免费注册 >> </a></div>
                </div>
            </div>
            
        </div>
        
        <div class='account-container mt20' style='text-align:center;color:#555;'>
            © 2004-2015 www.xxxxx.com.cn All Rights Reserved. xxxxx 版权所有
        </div>
        <script src="/statics/Admin/js/jquery-1.8.2.min.js"></script>
        <script src="/statics/Admin/js/treebiao.js"></script>
        <script type="text/javascript">
            
            $(function(){
                $.login('#Form','');
            });
        
            function ChangeCode() {
                var code = document.getElementById('imgCode');
                code.src += '?';
            }
        </script>
    </body>
    </html>
    login.html
    .header{
        padding:15px 0px;
    }
    
    .body{
        border: 1px solid #d7d7d7;
        padding: 40px;
        padding-right: 0;
    }
    .body .logo{
        width:50%;
    }
    .body .login{
        width:50%;    
        color: #555;
    }
    
    .body .register{
        width: 630px;
        border-right: 1px dashed #e5e5e5;
        color: #555;
    }
    .body .register .group,.body .login .group{
        margin:15px 0px;
        height:38px;
        font-size:14px;
        position:relative;
        line-height:38px;
    }
    .body .register .group .tip,.body .login .group .tip{
        width: 100px;
        display: inline-block;
        text-align: right;
        font-size: 14px;
    }
    .body .register .group label .red,.body .login .group label .red{
        margin:0 5px;
    }
    
    .body .register .group input[type='text'],.body .register .group input[type='password'],
    .body .login .group input[type='text'],.body .login .group input[type='password']{
        width:210px;
        height:32px;
        padding:0 30px 0 4px;
        border: 1px solid #cccccc;
        
    }
    .body .register .group i,.body .login .group i{
        position: absolute;
        left: 330px;
    }
    .body .register .group .i-name,.body .login .group .i-name{
        background: url(../Images/i_name.jpg) no-repeat scroll 0 0 transparent;
        height: 16px;
        top: 10px;
        width: 16px;
        
    }
    .body .register .group .i-pwd,.body .login .group .i-pwd{
        background: url(../Images/i_pwd.jpg) no-repeat scroll 0 0 transparent;
        height: 19px;
        top: 10px;
        width: 14px;
    }
    .body .register .group .i-phone,.body .register .login .i-phone{
        background: url(../Images/i_phone.jpg) no-repeat scroll 0 0 transparent;
        height: 21px;
        top: 10px;
        width: 14px;
    }
    
    .body .register .group .input-error{
        font-size:12px;
        color: #e4393c;
        display: inline-block;
        line-height: 32px;
        height: 32px;
        width: 260px;
        padding: 0 5px;
        background: #FFEBEB;
        border: 1px solid #ffbdbe;
    }
    
    .body .login .group .input-error{
        font-size:10px;
        position: absolute;
        color: #e4393c;
        background: #FFEBEB;
        border: 1px solid #ffbdbe;
        display: block;
        z-index: 10;
        height: 15px;
        width: 244px;
        line-height: 15px;
        left: 104px;
    }
    
    .body .register .group .checkcode,.body .login .group .checkcode{
        position:absolute;
        margin:-20px 0 0 5px;
    }
    .body .register .group .submit,.body .login .group .submit{
        background-color: #e4393c;
        padding:8px 20px;
        width:246px;
        color: white;
        text-align: center;
        border:1px solid #e4393c;
    }
    
    
    .body .more{
        padding:20px;
    }
    .body .login .go-register{
        position: absolute;
        right:0px;
        bottom:0px;
    }
    .body .login .go-register a{
        line-height: 32px;
        text-align: center;
        font-size: 14px;
        background: #7cbe56;
        width: 115px;
        height: 32px;
        display: block;
        color: #FFF;
    }
    
    
    .pg-footer{
        margin:20px 0;
        color: #555;
    }
    account.css
    /*公共开始*/
    body {
        margin: 0 auto;
        font-family: Arial;
        _font-family: 宋体,Arial;
        font-size: 12px;
    }
    body, dl, dt, dd, ul, ol, li, h1, h2, h3, h4, h5, h6, pre, code, form, fieldset, legend, input, button, textarea, p, blockquote, th, td, figure, div {
        margin: 0;
        padding: 0;
    }
    
    ol, ul, li {
        list-style: none;
    }
    a{
        cursor:pointer;
        text-decoration:none;
    }
    /*a:hover{
        color: #F60 !important;
        text-decoration: underline;
    }*/
    img{
        border:none;
        border-width:0px;
    }
    table{
        border-collapse: collapse;
        border-spacing: 0;
    }
    
    .red{
        color: #c00 !important;
    }
    
    .m8{
        margin:8px;
    }
    .mg20{
        margin:20px;
    }
    .mt0{
        margin-top:0px !important;
    }
    .mt10{
        margin-top:10px;
    }
    .mt20{
        margin-top:20px;
    }
    .mt30{
        margin-top:30px !important;
    }
    .mr5{
        margin-right:5px;
    }
    .ml5{
        margin-left:5px;
    }
    
    .ml10{
        margin-left:10px;
    }
    .mb0{
        margin-bottom:0px !important;
    }
    .mb20{
        margin-bottom:20px;
    }
    .mb10{
        margin-bottom:10px;
    }
    .pd10{
        padding:10px !important;
    }
    .pt18{
        padding-top:18px;
    }
    .pt20{
        padding-top:20px;
    }
    .pb20{
        padding-bottom:20px;
    }
    .nbr{
        border-right:0px;
    }
    .font12{
        font-size:12px !important;
    }
    .font13{
        font-size:13px !important;
    }
    .font14{
        font-size:14px;
    }
    .font16{
        font-size:16px;
    }
    .bold{
        font-weight:bold;
    }
    .left{
        float:left;
    }
    .right{
        float:right;
    }
    .hide{
        display:none;
    }
    .show{
         display:table;
    }
    .clearfix{
        clear:both;
    }
    .clearfix:after {
        content: ".";
        display: block;
        height: 0;
        clear: both;
        visibility: hidden;
    }
    * html .clearfix {zoom: 1;}
    
    .container{
        width:1190px;
        margin-left:auto;
        margin-right:auto;
    }
    .narrow{
        width:980px !important;
        margin-left:auto;
        margin-right:auto;
    }
    
    .account-container{
        width:980px;
        margin-left:auto;
        margin-right:auto;
    }
    
    .group-box-1 .title{
        height: 33px;
        line-height: 33px;
        border: 1px solid #DDD;
        background: #f5f5f5;
        padding-top: 0;
        padding-left: 0;
                    
    }
    .group-box-1 .title .title-font{
        display: inline-block;
        font-size: 14px;
        font-family: 'Microsoft Yahei','SimHei';
        font-weight: bold;
        color: #333;
        padding-left: 10px;
    }
    .group-box-1 .body {
        border: 1px solid #e4e4e4;
        border-top: none;
    }
    
    .tab-menu-box1 {
        border: 1px solid #ddd;
        margin-bottom: 20px;
    }
    
    .tab-menu-box1 .menu {
        line-height: 33px;
        height: 33px;
        background-color: #f5f5f5;
    }
    
    .tab-menu-box1 .content {
        min-height: 100px;
        border-top: 1px solid #ddd;
        background-color: white;
    }
    
    .tab-menu-box1 .menu ul {
        padding: 0;
        margin: 0;
        list-style: none;
        /*position: absolute;*/
    }
    
    .tab-menu-box1 .menu ul li {
        position: relative;
        float: left;
        font-size: 14px;
        font-family: 'Microsoft Yahei','SimHei';
        text-align: center;
        font-size: 14px;
        font-weight: bold;
        border-right: 1px solid #ddd;
        padding: 0 18px;
        cursor: pointer;
    }
    
    .tab-menu-box1 .menu ul li:hover {
        color: #c9033b;
    }
    
    .tab-menu-box1 .menu .more {
        float: right;
        font-size: 12px;
        padding-right: 10px;
        font-family: "宋体";
        color: #666;
        text-decoration: none;
    }
    
    .tab-menu-box1 .menu a:hover {
        color: #f60 !important;
        text-decoration: underline;
    }
    
    .tab-menu-box1 .menu .current {
        margin-top: -1px;
        color: #c9033b;
        background: #fff;
        height: 33px;
        border-top: 2px solid #c9033b;
        z-index: 10;
    }
    
    .tab-menu-box-2 .float-title {
        display: none;
        top: 0px;
        position: fixed;
        z-index: 50;
    }
    
    .tab-menu-box-2 .title {
        width: 890px;
        border-bottom: 2px solid #b20101;
        border-left: 1px solid #e1e1e1;
        clear: both;
        height: 32px;
    }
    
    .tab-menu-box-2 .title a {
        float: left;
        width: 107px;
        height: 31px;
        line-height: 31px;
        font-size: 14px;
        font-weight: bold;
        text-align: center;
        border-top: 1px solid #e1e1e1;
        border-right: 1px solid #e1e1e1;
        background: url(../images/bg4.png?3) 0 -308px repeat-x;
        text-decoration: none;
        color: #333;
        cursor: pointer;
    }
    
    .tab-menu-box-2 .title a:hover {
        background-position: -26px -271px;
        text-decoration: none;
        color: #fff;
    }
    
    .tab-menu-box-2 .content {
        min-height: 100px;
        background-color: white;
    }
    
    
    .tab-menu-box3 {
        border: 1px solid #ddd;
    }
    
    .tab-menu-box3 .menu {
        line-height: 33px;
        height: 33px;
        background-color: #f5f5f5;
    }
    
    .tab-menu-box3 .content {
        height: 214px;
        border-top: 1px solid #ddd;
        background-color: white;
    }
    
    .tab-menu-box3 .menu ul {
        padding: 0;
        margin: 0;
        list-style: none;
        /*position: absolute;*/
    }
    
    .tab-menu-box3 .menu ul li {
        position: relative;
        float: left;
        font-size: 14px;
        font-family: 'Microsoft Yahei','SimHei';
        text-align: center;
        font-size: 14px;
        width:50%;
        cursor: pointer;
    }
     
    .tab-menu-box3 .menu ul li:hover {
        color: #c9033b;
    }
    
    .tab-menu-box3 .menu .more {
        float: right;
        font-size: 12px;
        padding-right: 10px;
        font-family: "宋体";
        color: #666;
        text-decoration: none;
    }
    
    .tab-menu-box3 .menu a:hover {
        color: #f60 !important;
        text-decoration: underline;
        font-weight: bold;
    }
    
    .tab-menu-box3 .menu .current {
    
        margin-top: -1px;
        color: #c9033b;
        background: #fff;
        height: 33px;
        border-top: 2px solid #c9033b;
        z-index: 10;
        font-weight: bold;
        
    }
    
    .quantity-bg{
        height:20px;
        width: 77px; 
        border: 1px solid #999;
    }
     
    .quantity-bg  .minus,.quantity-bg  .plus{
      height:20px;
      width:20px; 
      line-height:20px;
      text-align:center;
      vertical-align:middle;
    }
    
    .quantity-bg input{
        height:20px;
        width:35px;
        border:0px;
        border-left:1px solid #999;
        border-right:1px solid #999;
     }
     
    /*公共结束*/
    common.css

    以上为tornado项目设计的小例,有很多地方法可以再去扩展优化,只是举例所以代码比较粗糙。只是大概介绍,后期会继续优化

  • 相关阅读:
    JS jQuery显示隐藏div的几种方法
    PHP 二维数组去重(保留指定键值的同时去除重复的项)
    Java面试题解析(一)
    Java :面向对象
    使用 Spring Framework 时常犯的十大错误
    Spring Boot 面试的十个问题
    《深入理解 Java 内存模型》读书笔记
    Spring Boot 2.0 迁移指南
    MaidSafe区块链项目白皮书解读
    20190712共学问题归纳
  • 原文地址:https://www.cnblogs.com/jl-bai/p/5865481.html
Copyright © 2020-2023  润新知