Tornado实战项目(伪JD商城)

预备知识  

在之前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对象

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. 单一责任原则(SRP)

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

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

  2.开放封闭原则(OCP)

     例如装饰器,可以对独立的功能实现扩展,但对源码不能进行修改
对扩展开放,对修改封闭

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

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

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

    如何实现 ? 

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

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

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

  3.里氏替换原则(LSP)  

子类可以替换父类,父类出现的地方都可以用子类替换
可以使用任何派生类(子类)替换基类

    优点:
      可以很容易的实现同一父类下各个子类的互换,而客户端可以毫不察觉

  4.接口分享原则(ISP)    

对于接口进行分类避免一个接口的方法过多,避免”胖接口"
    优点:
      会使一个软件系统功能扩展时,修改的压力不会传到别的对象那里
    如何实现 ?
      得用委托分离接口
      利用多继承分离接口

  5.依赖倒置原则(DIP)

高层模块不应该依赖低层模块,二者都应该依赖其抽象(理解为接口);抽象不应该依赖细节;细节应该依赖抽象   

隔离关系,使用接口或抽象类代指
高层次的模块不应该依赖于低层次的模块,而是,都应该依赖于抽象
    优点:
      使用传统过程化程序设计所创建的依赖关系,策略依赖于细节,这是糟糕的,因为策略受到细节改变的影响。
      依赖倒置原则使细节和策略都依赖于抽象,抽象的稳定性决定了系统的稳定性

  6.依赖注入(DI)和控制反转原则(ICO)        

使用钩子再原来执行流程中注入其他对象

tornado项目设计实例

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

  1、目录规划

  

说明:

  Infrastructure 目录:公共组件目录

  Model:业务逻辑处理目录

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

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

  UIAdmin: UI层

  Views:模板文件目录

  Application.py : 服务启动文件

  2.业务访问流程

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

启动服务后,客户端访问URL,根据tornado路由找到相对的handler进行处理
找到handler后其相对方法(get/post/delete/put)中调用Model逻辑处理层方法进行处理并接收处理结果
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 逻辑处理层

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

#!/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

协调

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:[email protected]: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=‘width:80px;‘ name=‘checkcode‘ />
                        <a style=‘width: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=‘width: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.py

.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

后续还有更新版本...

时间: 2024-11-05 22:39:39

Tornado实战项目(伪JD商城)的相关文章

android经典实战项目视频教程下载

注:这是一篇转载的文章,原文具体链接地址找不到了,将原文分享如下,希望能对看到的朋友有所帮助! 最近在学习android应用方面的技术,自己在网上搜集了一些实战项目的资料,感觉挺好的,发布出来跟大伙分享一下,希望对大家有用. 1.基于Android平台实战无线点餐系统(客户端(Client)和服务端(Server)) ①http://kuai.xunlei.com/d/xmBrDwI8CAAyXVFRa3d ②http://kuai.xunlei.com/d/xmBrDwJKCABPXVFR0d

C# RabbitMQ延迟队列功能实战项目演练

一.需求背景 当用户在商城上进行下单支付,我们假设如果8小时没有进行支付,那么就后台自动对该笔交易的状态修改为订单关闭取消,同时给用户发送一份邮件提醒.那么我们应用程序如何实现这样的需求场景呢?在之前的<C# Redis缓存过期实现延迟通知实战演练>分享课程中阿笨最后总结的时候说过Redis Pub/Sub是一种并不可靠地消息机制,他不会做信息的存储,只是在线转发,那么肯定也没有ack确认机制,另外只有订阅段监听时才会转发!我们是否有更好的方式去实现呢?今天给大家分享的比较好的解决方案就是通过

强烈推荐 GitHub 上值得前端学习的开源实战项目

强烈推荐 GitHub 上值得前端学习的开源实战项目. Vue.js vue-element-admin 是一个后台前端解决方案,它基于和 element-ui 实现 基于 iView 的 Vue 2.0 管理系统模板 基于 vue2 + vuex 构建一个具有 45 个页面的大型单页面应用 基于 vue + element-ui 的后台管理系统 基于Vue.js + Element UI 的后台管理系统解决方案 基于 Vue(2.5) + vuex + vue-router + vue-axi

C#网络编程技术FastSocket实战项目演练

一.FastSocket课程介绍 .NET框架虽然微软提供了socket通信的类库,但是还有很多事情要自己处理,比如TCP协议需要处理分包.组包.粘包.维护连接列表等,UDP协议需要处理丢包.乱序,而且对于多连接并发,还要自己处理多线程等等.本期分享课程阿笨给大家带来的是来源于github开源Socket通信中间件:FastSocket,目的就是把大家从繁琐的网络编程技术中彻底地解放和释放出来. 阿笨只想安安静静的学习下网络编程技术Socket后,将学习的成果直接灵活的运用到自己的实际项目中去.

Python实战项目网络爬虫 之 爬取小说吧小说正文

本次实战项目适合,有一定Python语法知识的小白学员.本人也是根据一些网上的资料,自己摸索编写的内容.有不明白的童鞋,欢迎提问. 目的:爬取百度小说吧中的原创小说<猎奇师>部分小说内容 链接:http://tieba.baidu.com/p/4792877734 首先,自己定义一个类,方便使用.其实类就像一个"水果篮",这个"水果篮"里有很多的"水果",也就是我们类里面定义的变量啊,函数啊等等,各种各样的.每一种"水果&q

安卓入门实战项目-身份证信息查看器,精确解读身份证信息

本系列教程致力于可以快速的进行学习安卓开发,按照项目式的方法,通常一篇文章会做一个小程序.提高学习的兴趣. 一方面总结自己所得,另一方面可以通过自己的分享帮助更多学习的同仁. 因为知识的连贯性,推荐按照瞬息进行学习.目录链接:http://www.chengxiaoxiao.com/bozhu/1336.html 本教程由今日头条-做全栈攻城狮原创首发,转载请注明出处. 大学生求兼职:请联系wx:aiquanzhan 页尾提供github源代码下载地址. 一.项目描述 通过小程序实现精准解读身份

基于Python Spark的大数据分析_pyspark实战项目课程

基于Python Spark的大数据分析(第一期) 课程介绍地址:http://www.xuetuwuyou.com/course/173 课程出自学途无忧网:http://www.xuetuwuyou.com 讲师:轩宇老师 1.开课时间:小班化教学授课,第一期开课时间为5月20号(满30人开班,先报先学!): 2.学习方式:在线直播,共8次课,每次2小时,每周2次(周三.六,晚上20:30 - 22:30),提供在线视频,课后反复学习: 3.报名课程后,请联系客服申请加入班级答疑交流QQ群:

实战项目:EMOS集成邮件平台

实战项目:EMOS集成邮件平台用户邮箱系统:http://mailAnonymous.cn/邮件服务器管理平台http://mailAnonymous.cn/extman 项目需求:随着公司规模不断扩大,员工数已经迅速突破至300多人急需一致的,低成本邮件系统,员工计算机水平不一,除主持邮件客户端外,最好能通过web方式登陆收发邮件 项目分析:实施成本考虑.自主搭建邮件系统购买一台pc服务器(<5万),部署到公司支持多个邮件域:公司已有域名Anonymous.cn,163.com需向域名服务商申

RN实战项目网络请求封装(二)

RN实战项目网络请求封装(二) 网络相关学习文档,参考 RN中文网文档 RN中主要是用fetch来完成异步网络请求的,传统的ajax慢慢的会被fetch替代 RN中文网fetch API 案例 fetch('https://mywebsite.com/endpoint/', { method: 'POST', //请求方法 默认get headers: { 'Accept': 'application/json', 'Content-Type': 'application/json', //数据