zoukankan      html  css  js  c++  java
  • tornado web高级开发项目

    抽屉官网:http://dig.chouti.com/

    一、配置(settings)

    settings = {
        'template_path': 'views',    #模板文件路径
        'static_path': 'statics',        #静态文件路径
        'static_url_prefix': '/statics/',  #静态文件前缀
        'autoreload': True,
        'ui_methods': mt
    }

    二、路由配置

    application = tornado.web.Application([
        (r"/index", home.IndexHandler),    #主页
        (r"/check_code", account.CheckCodeHandler),  #验证码
        (r"/send_msg", account.SendMsgHandler),  #邮箱验证码
        (r"/register", account.RegisterHandler),  #注册
        (r"/login", account.LoginHandler),    #登陆
        (r"/upload_image", home.UploadImageHandler),  #上传图片
        (r"/comment", home.CommentHandler),  #评论
        (r"/favor", home.FavorHandler),   #点赞
    ], **settings)

    三、文件夹分类

    下面我们将根据上图文件目录由上到下做一一分析:

    四、准备工作

      本项目所有前端反馈均是通过BaseResponse类实现的:

    class BaseResponse:
     
        def __init__(self):
            self.status = False    #状态信息,是否注册成功,是否登陆成功,是否点赞成功、是否评论成功等
            self.code = StatusCodeEnum.Success
            self.data = None        #前端需要展示的数据
            self.summary = None    #错误信息
            self.message = {}    #字典类型的错误信息

    前端:

    • html
    • css基础(一)
    • css基础(二)
    • JavaScript基础
    • Dom基础和实例
    • jquary基础和实例大全
    • 偷偷发请求的ajax基础与实例大全

    后端:

    • web框架本质
    • web框架之tronado

    数据库:

    • mysql基础一
    • mysql基础二 
    • python操作mysql(pymysql 和ORM框架 SQLAchemy)

    缓存:

    • RabbitMQ、Redis、Memcache、SQLAlchemy

    五、core:业务处理类handler需要继承的父类

    import tornado.web
    from backend.session.session import SessionFactory
     
     
    class BaseRequestHandler(tornado.web.RequestHandler):
     
        def initialize(self):
     
            self.session = SessionFactory.get_session_obj(self)

    六、form:用于form验证的文件,这是一个自定义的tornado form验证模块

      fields:包含字符串、邮箱、数字、checkbox、文件类型验证

      forms:核心验证处理,返回验证是否成功self._valid_status、成功后的数据提self._value_dict、错误信息self._error_dict

    class Field:
    
        def __init__(self):
    
            self.is_valid = False
            self.name = None
            self.value = None
            self.error = None
    
        def match(self, name, value):
            self.name = name
    
            if not self.required:
                self.is_valid = True
                self.value = value
            else:
                if not value:
                    if self.custom_error_dict.get('required', None):
                        self.error = self.custom_error_dict['required']
                    else:
                        self.error = "%s is required" % name
                else:
                    ret = re.match(self.REGULAR, value)
                    if ret:
                        self.is_valid = True
                        self.value = value
                    else:
                        if self.custom_error_dict.get('valid', None):
                            self.error = self.custom_error_dict['valid']
                        else:
                            self.error = "%s is invalid" % name
    初始化
    class StringField(Field):
    
        REGULAR = "^.*$"
    
        def __init__(self, custom_error_dict=None, required=True):
    
            self.custom_error_dict = {}  # {'required': 'IP不能为空', 'valid': 'IP格式错误'}
            if custom_error_dict:
                self.custom_error_dict.update(custom_error_dict)
    
            self.required = required
    
            super(StringField, self).__init__()
    字符串匹配
    class IPField(Field):
    
        REGULAR = "^(25[0-5]|2[0-4]d|[0-1]?d?d)(.(25[0-5]|2[0-4]d|[0-1]?d?d)){3}$"
    
        def __init__(self, custom_error_dict=None, required=True):
    
            self.custom_error_dict = {}  # {'required': 'IP不能为空', 'valid': 'IP格式错误'}
            if custom_error_dict:
                self.custom_error_dict.update(custom_error_dict)
    
            self.required = required
            super(IPField, self).__init__()
    ip匹配
    class EmailField(Field):
    
        REGULAR = "^w+([-+.']w+)*@w+([-.]w+)*.w+([-.]w+)*$"
    
        def __init__(self, custom_error_dict=None, required=True):
    
            self.custom_error_dict = {}  # {'required': 'IP不能为空', 'valid': 'IP格式错误'}
            if custom_error_dict:
                self.custom_error_dict.update(custom_error_dict)
    
            self.required = required
            super(EmailField, self).__init__()
    邮箱匹配
    class IntegerField(Field):
    
        REGULAR = "^d+$"
    
        def __init__(self, custom_error_dict=None, required=True):
    
            self.custom_error_dict = {}  # {'required': 'IP不能为空', 'valid': 'IP格式错误'}
            if custom_error_dict:
                self.custom_error_dict.update(custom_error_dict)
    
            self.required = required
            super(IntegerField, self).__init__()
    数字匹配
    class CheckBoxField(Field):
    
        REGULAR = "^d+$"
    
        def __init__(self, custom_error_dict=None, required=True):
    
            self.custom_error_dict = {}  # {'required': 'IP不能为空', 'valid': 'IP格式错误'}
            if custom_error_dict:
                self.custom_error_dict.update(custom_error_dict)
    
            self.required = required
            super(CheckBoxField, self).__init__()
    
        def match(self, name, value):
            self.name = name
    
            if not self.required:
                self.is_valid = True
                self.value = value
            else:
                if not value:
                    if self.custom_error_dict.get('required', None):
                        self.error = self.custom_error_dict['required']
                    else:
                        self.error = "%s is required" % name
                else:
                    if isinstance(name, list):
                        self.is_valid = True
                        self.value = value
                    else:
                        if self.custom_error_dict.get('valid', None):
                            self.error = self.custom_error_dict['valid']
                        else:
                            self.error = "%s is invalid" % name
    checkbox匹配
    class FileField(Field):
    
        REGULAR = "^(w+.pdf)|(w+.mp3)|(w+.py)$"
    
        def __init__(self, custom_error_dict=None, required=True):
    
            self.custom_error_dict = {}  # {'required': 'IP不能为空', 'valid': 'IP格式错误'}
            if custom_error_dict:
                self.custom_error_dict.update(custom_error_dict)
    
            self.required = required
    
            super(FileField, self).__init__()
    
        def match(self, name, file_name_list):
            flag = True
            self.name = name
    
            if not self.required:
                self.is_valid = True
                self.value = file_name_list
            else:
                if not file_name_list:
                    if self.custom_error_dict.get('required', None):
                        self.error = self.custom_error_dict['required']
                    else:
                        self.error = "%s is required" % name
                    flag = False
                else:
                    for file_name in file_name_list:
                        if not file_name or not file_name.strip():
                            if self.custom_error_dict.get('required', None):
                                self.error = self.custom_error_dict['required']
                            else:
                                self.error = "%s is required" % name
                            flag = False
                            break
                        else:
                            ret = re.match(self.REGULAR, file_name)
                            if not ret:
                                if self.custom_error_dict.get('valid', None):
                                    self.error = self.custom_error_dict['valid']
                                else:
                                    self.error = "%s is invalid" % name
                                flag = False
                                break
    
                self.is_valid = flag
    
        def save(self, request, upload_to=""):
    
            file_metas = request.files[self.name]
            for meta in file_metas:
                file_name = meta['filename']
                file_path_name = os.path.join(upload_to, file_name)
                with open(file_path_name, 'wb') as up:
                    up.write(meta['body'])
    
            upload_file_path_list = map(lambda path: os.path.join(upload_to, path), self.value)
            self.value = list(upload_file_path_list)
    文件匹配

    核心验证处理:

    from backend.form import fields
     
     
    class BaseForm:
     
        def __init__(self):
            self._value_dict = {} #数据字典
            self._error_dict = {} #错误信息字典
            self._valid_status = True #是否验证成功
     
        def valid(self, handler):
     
            for field_name, field_obj in self.__dict__.items():
                if field_name.startswith('_'):  #过滤私有字段
                    continue
     
                if type(field_obj) == fields.CheckBoxField:  #checkbox处理
                    post_value = handler.get_arguments(field_name, None)
                elif type(field_obj) == fields.FileField:  #文件处理
                    post_value = []
                    file_list = handler.request.files.get(field_name, [])
                    for file_item in file_list:
                        post_value.append(file_item['filename'])
                else:
                    post_value = handler.get_argument(field_name, None)
     
                field_obj.match(field_name, post_value)  #匹配
                if field_obj.is_valid:                    #如果验证成功
                    self._value_dict[field_name] = field_obj.value  #提取数据
                else:
                    self._error_dict[field_name] = field_obj.error  #错误信息
                    self._valid_status = False
            return self._valid_status    #返回是否验证成功

    七、如何应用上述form验证模块:

    以注册为例:

    前端:

    <div class="header">
                    <span>注册</span>
                    <div class="dialog-close" onclick="CloseDialog('#accountDialog');">X</div>
                </div>
                <div class="content">
                    <div style="padding: 0 70px">
                        <div class="tips">
                            <span>输入注册信息</span>
                        </div>
                        <div id="register_error_summary" class="error-msg">
    
                        </div>
                        <div class="inp">
                            <input name="username" type="text" placeholder="请输入用户名" />
    
                        </div>
                        <div class="inp">
                            <input name="email" id="email" type="text" placeholder="请输入邮箱" />
                        </div>
                        <div class="inp">
                            <input name="email_code" class="email-code" type="text" placeholder="请输入邮箱验证码" />
                            <a id="fetch_code" class="fetch-code" href="javascript:void(0);">获取验证码</a>
                        </div>
                        <div class="inp">
                            <input name="password" type="password" placeholder="请输入密码" />
                        </div>
                        <div class="inp">
                            <div class="submit" onclick="SubmitRegister(this);">
                                <span>注册</span>
                                <span class="hide">
                                    <img src="/statics/images/loader.gif" style="height: 16px; 16px">
                                    <span>正在注册</span>
                                </span>
                            </div>
                        </div>
                    </div>
                </div>
    Html

    js:

    /*
            点击注册按钮
            */
            function SubmitRegister(ths){
                $('#register_error_summary').empty();
                $('#model_register .inp .error').remove();
     
                $(ths).children(':eq(0)').addClass('hide');
                $(ths).addClass('not-allow').children(':eq(1)').removeClass('hide');
     
                var post_dict = {};
                $('#model_register input').each(function(){
                    post_dict[$(this).attr("name")] = $(this).val();  #将所有input标签内容提取出来,以对应name为key,值为value放入post_dict字典
                });
     
                $.ajax({
                    url: '/register', #提交的url
                    type: 'POST',    #提交方式
                    data: post_dict,  #提交数据
                    dataType: 'json',  #数据格式
                    success: function(arg){
                        if(arg.status){
                            window.location.href = '/index'; #验证成功跳转至主页
                        }else{
                            $.each(arg.message, function(k,v){
                                //<span class="error">s</span>
                                var tag = document.createElement('span'); #验证失败创建标签
                                tag.className = 'error';
                                tag.innerText = v; #存入错误信息
                                $('#model_register input[name="'+ k +'"]').after(tag); #将标签加入html中
                            })
                        }
                    }
                });
     
                $(ths).removeClass('not-allow').children(':eq(1)').addClass('hide');
                $(ths).children(':eq(0)').removeClass('hide');
            }
    js

    后台处理:

    首先需要编写RegisterForm:

    class RegisterForm(BaseForm):  #需要继承上面的form验证核心处理类
     
        def __init__(self): #初始化每一个input标签的name
            self.username = StringField()  #input标签name=对应类型的类
            self.email = EmailField()
            self.password = StringField()
            self.email_code = StringField()
     
            super(RegisterForm, self).__init__()

    后台RegisterHandler:

    class RegisterHandler(BaseRequestHandler):
        def post(self, *args, **kwargs):
            rep = BaseResponse()   #总的返回前端的类,包含是否注册成功的状态、错误信息
            form = account.RegisterForm()  #实例化RegisterForm
            if form.valid(self):  #调用baseform核心验证处理函数valid,返回是否验证成功
                current_date = datetime.datetime.now()
                limit_day = current_date - datetime.timedelta(minutes=1)
                conn = ORM.session()  #获取数据库session对象<br>        #查看验证码是否过期
                is_valid_code = conn.query(ORM.SendMsg).filter(ORM.SendMsg.email == form._value_dict['email'],
                                                               ORM.SendMsg.code == form._value_dict['email_code'],
                                                               ORM.SendMsg.ctime > limit_day).count()
                if not is_valid_code:
                    rep.message['email_code'] = '邮箱验证码不正确或过期'
                    self.write(json.dumps(rep.__dict__))
                    return
                has_exists_email = conn.query(ORM.UserInfo).filter(ORM.UserInfo.email == form._value_dict['email']).count()#邮箱是否存在
                if has_exists_email:
                    rep.message['email'] = '邮箱已经存在'
                    self.write(json.dumps(rep.__dict__))
                    return
                has_exists_username = conn.query(ORM.UserInfo).filter(
                    ORM.UserInfo.username == form._value_dict['username']).count() #用户名是否存在
                if has_exists_username:
                    rep.message['email'] = '用户名已经存在'
                    self.write(json.dumps(rep.__dict__))
                    return<br>        #按数据库表的列订制form._value_dict
    form._value_dict['ctime'] = current_date form._value_dict.pop('email_code') obj = ORM.UserInfo(**form._value_dict) conn.add(obj)<br>
         conn.flush()
         conn.refresh(obj) #将自增id也提取出来
     
         user_info_dict = {'nid': obj.nid, 'email': obj.email, 'username': obj.username}
     
         conn.query(ORM.SendMsg).filter_by(email=form._value_dict['email']).delete()#删除本次邮箱验证码
         conn.commit()
         conn.close()
     
         self.session['is_login'] = True  #注册成功后定义登陆成功
         self.session['user_info'] = user_info_dict 用户信息写入session
         rep.status = True
     
     else:
         rep.message = form._error_dict #错误信息
     
     self.write(json.dumps(rep.__dict__))  #返回给前端,前端ajax success接收并处理,在前端页面展示

    八、session ,本session是基于tornado的自定义session  

    1.应用工厂方法模式定义session保存的位置,用户只需在配置文件修改即可

    class SessionFactory:
     
        @staticmethod
        def get_session_obj(handler):
            obj = None
     
            if config.SESSION_TYPE == "cache": #缓存
                obj = CacheSession(handler)
            elif config.SESSION_TYPE == "memcached": #memcached
    obj = MemcachedSession(handler) <br>    elif config.SESSION_TYPE == "redis": #radis<br>      obj = RedisSession(handler) <br>    return obj

    2.缓存session

    class CacheSession:
        session_container = {}
        session_id = "__sessionId__"
    
        def __init__(self, handler):
            self.handler = handler
            client_random_str = handler.get_cookie(CacheSession.session_id, None)
            if client_random_str and client_random_str in CacheSession.session_container:
                self.random_str = client_random_str
            else:
                self.random_str = create_session_id()
                CacheSession.session_container[self.random_str] = {}
    
            expires_time = time.time() + config.SESSION_EXPIRES
            handler.set_cookie(CacheSession.session_id, self.random_str, expires=expires_time)
    
        def __getitem__(self, key):
            ret = CacheSession.session_container[self.random_str].get(key, None)
            return ret
    
        def __setitem__(self, key, value):
            CacheSession.session_container[self.random_str][key] = value
    
        def __delitem__(self, key):
            if key in CacheSession.session_container[self.random_str]:
                del CacheSession.session_container[self.random_str][key]
    缓存Sesson

    3.memcache session

    import memcache
    
    conn = memcache.Client(['192.168.11.119:12000'], debug=True, cache_cas=True)
    
    class MemcachedSession:
        session_id = "__sessionId__"
    
        def __init__(self, handler):
            self.handler = handler
            # 从客户端获取随机字符串
            client_random_str = handler.get_cookie(CacheSession.session_id, None)
            # 如果从客户端获取到了随机字符串
            #
            if client_random_str and conn.get(client_random_str):
                self.random_str = client_random_str
            else:
                self.random_str = create_session_id()
                conn.set(self.random_str, json.dumps({}), config.SESSION_EXPIRES)
                #CacheSession.session_container[self.random_str] = {}
    
            conn.set(self.random_str, conn.get(self.random_str), config.SESSION_EXPIRES)
    
            expires_time = time.time() + config.SESSION_EXPIRES
            handler.set_cookie(MemcachedSession.session_id, self.random_str, expires=expires_time)
    
        def __getitem__(self, key):
            # ret = CacheSession.session_container[self.random_str].get(key, None)
            ret = conn.get(self.random_str)
            ret_dict = json.loads(ret)
            result = ret_dict.get(key,None)
            return result
    
        def __setitem__(self, key, value):
            ret = conn.get(self.random_str)
            ret_dict = json.loads(ret)
            ret_dict[key] = value
            conn.set(self.random_str, json.dumps(ret_dict), config.SESSION_EXPIRES)
    
            # CacheSession.session_container[self.random_str][key] = value
    
        def __delitem__(self, key):
            ret = conn.get(self.random_str)
            ret_dict = json.loads(ret)
            del ret_dict[key]
            conn.set(self.random_str, json.dumps(ret_dict), config.SESSION_EXPIRES)
    memcache session

    4.radis session

    import redis
    
    pool = redis.ConnectionPool(host='192.168.11.119', port=6379)
    r = redis.Redis(connection_pool=pool)
    
    class RedisSession:
        session_id = "__sessionId__"
    
        def __init__(self, handler):
            self.handler = handler
            # 从客户端获取随机字符串
            client_random_str = handler.get_cookie(CacheSession.session_id, None)
            # 如果从客户端获取到了随机字符串
            if client_random_str and r.exists(client_random_str):
                self.random_str = client_random_str
            else:
                self.random_str = create_session_id()
                r.hset(self.random_str,None,None)
    
                # conn.set(self.random_str, json.dumps({}), config.SESSION_EXPIRES)
                # CacheSession.session_container[self.random_str] = {}
            r.expire(self.random_str, config.SESSION_EXPIRES)
            # conn.set(self.random_str, conn.get(self.random_str), config.SESSION_EXPIRES)
    
    
            expires_time = time.time() + config.SESSION_EXPIRES
            handler.set_cookie(RedisSession.session_id, self.random_str, expires=expires_time)
    
        def __getitem__(self, key):
            # ret = CacheSession.session_container[self.random_str].get(key, None)
            result = r.hget(self.random_str,key)
            if result:
                ret_str = str(result, encoding='utf-8')
                try:
                    result = json.loads(ret_str)
                except:
                    result = ret_str
                return result
            else:
                return result
    
        def __setitem__(self, key, value):
            if type(value) == dict:
                r.hset(self.random_str, key, json.dumps(value))
            else:
                r.hset(self.random_str, key, value)
    
            # CacheSession.session_container[self.random_str][key] = value
    
        def __delitem__(self, key):
            r.hdel(self.random_str,key)
    radis session

    九、验证码:

    注:验证码需要依赖session。

    #!/usr/bin/env python
    #coding:utf-8
    
    import random
    from PIL import Image, ImageDraw, ImageFont, ImageFilter
    
    _letter_cases = "abcdefghjkmnpqrstuvwxy"  # 小写字母,去除可能干扰的i,l,o,z
    _upper_cases = _letter_cases.upper()  # 大写字母
    _numbers = ''.join(map(str, range(3, 10)))  # 数字
    init_chars = ''.join((_letter_cases, _upper_cases, _numbers))
    
    def create_validate_code(size=(120, 30),
                             chars=init_chars,
                             img_type="GIF",
                             mode="RGB",
                             bg_color=(255, 255, 255),
                             fg_color=(0, 0, 255),
                             font_size=18,
                             font_type="Monaco.ttf",
                             length=4,
                             draw_lines=True,
                             n_line=(1, 2),
                             draw_points=True,
                             point_chance = 2):
        '''
        @todo: 生成验证码图片
        @param size: 图片的大小,格式(宽,高),默认为(120, 30)
        @param chars: 允许的字符集合,格式字符串
        @param img_type: 图片保存的格式,默认为GIF,可选的为GIF,JPEG,TIFF,PNG
        @param mode: 图片模式,默认为RGB
        @param bg_color: 背景颜色,默认为白色
        @param fg_color: 前景色,验证码字符颜色,默认为蓝色#0000FF
        @param font_size: 验证码字体大小
        @param font_type: 验证码字体,默认为 ae_AlArabiya.ttf
        @param length: 验证码字符个数
        @param draw_lines: 是否划干扰线
        @param n_lines: 干扰线的条数范围,格式元组,默认为(1, 2),只有draw_lines为True时有效
        @param draw_points: 是否画干扰点
        @param point_chance: 干扰点出现的概率,大小范围[0, 100]
        @return: [0]: PIL Image实例
        @return: [1]: 验证码图片中的字符串
        '''
    
        width, height = size # 宽, 高
        img = Image.new(mode, size, bg_color) # 创建图形
        draw = ImageDraw.Draw(img) # 创建画笔
    
        def get_chars():
            '''生成给定长度的字符串,返回列表格式'''
            return random.sample(chars, length)
    
        def create_lines():
            '''绘制干扰线'''
            line_num = random.randint(*n_line) # 干扰线条数
    
            for i in range(line_num):
                # 起始点
                begin = (random.randint(0, size[0]), random.randint(0, size[1]))
                #结束点
                end = (random.randint(0, size[0]), random.randint(0, size[1]))
                draw.line([begin, end], fill=(0, 0, 0))
    
        def create_points():
            '''绘制干扰点'''
            chance = min(100, max(0, int(point_chance))) # 大小限制在[0, 100]
    
            for w in range(width):
                for h in range(height):
                    tmp = random.randint(0, 100)
                    if tmp > 100 - chance:
                        draw.point((w, h), fill=(0, 0, 0))
    
        def create_strs():
            '''绘制验证码字符'''
            c_chars = get_chars()
            strs = ' %s ' % ' '.join(c_chars) # 每个字符前后以空格隔开
    
            font = ImageFont.truetype(font_type, font_size)
            font_width, font_height = font.getsize(strs)
    
            draw.text(((width - font_width) / 3, (height - font_height) / 3),
                        strs, font=font, fill=fg_color)
    
            return ''.join(c_chars)
    
        if draw_lines:
            create_lines()
        if draw_points:
            create_points()
        strs = create_strs()
    
        # 图形扭曲参数
        params = [1 - float(random.randint(1, 2)) / 100,
                  0,
                  0,
                  0,
    - float(random.randint(1, 10)) / 100,
                  float(random.randint(1, 2)) / 500,
                  0.001,
                  float(random.randint(1, 2)) / 500
                  ]
        img = img.transform(size, Image.PERSPECTIVE, params) # 创建扭曲
    
        img = img.filter(ImageFilter.EDGE_ENHANCE_MORE) # 滤镜,边界加强(阈值更大)
    
        return img, strs
    check_code.py
    class CheckCodeHandler(BaseRequestHandler):
        def get(self, *args, **kwargs):
            stream = io.BytesIO()
            img, code = check_code.create_validate_code()
            img.save(stream, "png")
            self.session["CheckCode"] = code  #利用session保存验证码
            self.write(stream.getvalue())

    路由配置:(r"/check_code", account.CheckCodeHandler),

    前端:

    <img class="check-img" src="/check_code" alt="验证码" onclick="ChangeCode(this);">

    js:

    <script>
                        function ChangeCode(ths) {
                            ths.src += '?';
                        }
                    </script>

    十、发送邮箱验证码

    前端:

    <div class="inp">
            <input class="regiter-temp" name="code" class="email-code" type="text" placeholder="请输入邮箱验证码" />
            <a onclick="SendCode(this);" class="fetch-code" >获取验证码</a>
    </div>

    js:

    function SendCode(ths) {
    //            var email = $(ths).prev().val();
                var email = $('#email').val();
                $.ajax({
                    url: '/send_code',
                    type: 'POST',
                    data: {em: email},
                    success: function (arg) {
                        console.log(arg);
                    },
                    error: function () {
                         
                    }
                     
                });
            }

    路由配置:

    (r"/send_code", account.SendCodeHandler),

    后台handler:

    class SendCodeHandler(BaseRequestHandler):
        def post(self, *args, **kwargs):
            ret = {'status': True, "data": "", "error": ""}
            email = self.get_argument('em', None)
            if email:
                code = commons.random_code()  #获取随机验证码
                message.email([email,], code) #发送验证码到邮箱
                conn = chouti_orm.session()   #获取数据库session对象
                obj = chouti_orm.SendCode(email=email,code=code, stime=datetime.datetime.now()) #写入数据库
                conn.add(obj)
                conn.commit()
            else:
                ret['status'] = False
                ret['error'] = "邮箱格式错误"
     
            self.write(json.dumps(ret))

     发送邮箱验证码函数:

    import smtplib
    from email.mime.text import MIMEText
    from email.utils import formataddr
     
     
    def email(email_list, content, subject="抽屉新热榜-用户注册"):  #email_list邮件列表,content邮件内容,subject:发送标题
        msg = MIMEText(content, 'plain', 'utf-8')
        msg['From'] = formataddr(["抽屉新热榜",'wptawy@126.com'])
        msg['Subject'] = subject
     
        server = smtplib.SMTP("smtp.126.com", 25) 邮箱引擎
        server.login("youxiang@126.com", "mima") #邮箱名,密码
        server.sendmail('wptawy@126.com', email_list, msg.as_string())
        server.quit()

    十一、邮箱验证码之过期时间

    案例:

    html:

    <a id="fetch_code" class="fetch-code" href="javascript:void(0);">获取验证码</a>

    js:

    function BindSendMsg(){
               $("#fetch_code").click(function(){
                   $('#register_error_summary').empty();  #清空错误信息
                   var email = $('#email').val(); #获取邮箱地址
                   if(email.trim().length == 0){  #判断是否输入邮箱
                       $('#register_error_summary').text('请输入注册邮箱');
                       return;
                   }
                   if($(this).hasClass('sending')){  #判断是否已经发送
                       return;
                   }
                   var ths = $(this);
                   var time = 60; 设置倒计时时间为60s
     
                   $.ajax({
                       url: "/send_msg",
                       type: 'POST',
                       data: {email: email},
                       dataType: 'json',
                       success: function(arg){
                           if(!arg.status){  #是否发送成功
                               $('#register_error_summary').text(arg.summary); #不成功显示错误信息
                           }else{
                               ths.addClass('sending');      #成功后显示已发送状态
                               var interval = setInterval(function(){
                               ths.text("已发送(" + time + ")");
                               time -= 1;    #定时器每运行一次,计数器减1
                               if(time <= 0){
                                   clearInterval(interval); #一分钟过完,清除定时器
                                   ths.removeClass('sending');# 移除已发送状态
                                   ths.text("获取验证码");# 恢复未发送状态
                               }
                           }, 1000);#定时器每隔1s运行一次
                           }
                       }
                   });
     
               });
           }
    附:一些常见模块:
    1.随机验证码获取:
    def random_code():
        code = ''
        for i in range(4):
            current = random.randrange(0,4)
            if current != i:
                temp = chr(random.randint(65,90))
            else:
                temp = random.randint(0,9)
            code += str(temp)
        return code
    随机验证码

    2.md5加密

    def generate_md5(value):
        r = str(time.time())
        obj = hashlib.md5(r.encode('utf-8'))
        obj.update(value.encode('utf-8'))
        return obj.hexdigest()

    十二、分页功能,该功能是基于tornado的自定义分页功能

    案例:

    前端:

    <div class="pagination">
             {% raw str_page%}  #展示原生html
    </div>

    url配置:

    (r"/index/(?P<page>d*)", IndexHandler),

    分页模块:

    #!/usr/bin/env python
    # -*- coding:utf-8 -*-
    
    
    class Pagination:
        def __init__(self, current_page, all_item):
            try:
                page = int(current_page)
            except:
                page = 1
            if page < 1:
                page = 1
    
            all_pager, c = divmod(all_item, 10)
            if c > 0:
                all_pager += 1
    
            self.current_page = page
            self.all_pager = all_pager
    
        @property
        def start(self):
            return (self.current_page - 1) * 10
    
        @property
        def end(self):
            return self.current_page * 10
    
        def string_pager(self, base_url="/index/"):
            list_page = []
            if self.all_pager < 11:
                s = 1
                t = self.all_pager + 1
            else:  # 总页数大于11
                if self.current_page < 6:
                    s = 1
                    t = 12
                else:
                    if (self.current_page + 5) < self.all_pager:
                        s = self.current_page - 5
                        t = self.current_page + 5 + 1
                    else:
                        s = self.all_pager - 11
                        t = self.all_pager + 1
            # 首页
            # first = '<a href="%s1">首页</a>' % base_url
            # list_page.append(first)
            # 上一页
            # 当前页 page
            if self.current_page == 1:
                prev = '<a href="javascript:void(0);">上一页</a>'
            else:
                prev = '<a href="%s%s">上一页</a>' % (base_url, self.current_page - 1,)
            list_page.append(prev)
    
            for p in range(s, t):  # 1-11
                if p == self.current_page:
                    temp = '<a class="active" href="%s%s">%s</a>' % (base_url,p, p)
                else:
                    temp = '<a href="%s%s">%s</a>' % (base_url,p, p)
                list_page.append(temp)
            if self.current_page == self.all_pager:
                nex = '<a href="javascript:void(0);">下一页</a>'
            else:
                nex = '<a href="%s%s">下一页</a>' % (base_url, self.current_page + 1,)
    
            list_page.append(nex)
    
            # 尾页
            last = '<a href="%s%s">尾页</a>' % (base_url, self.all_pager,)
            list_page.append(last)
    
            # 跳转
            jump = """<input type='text' /><a onclick="Jump('%s',this);">GO</a>""" % ('/index/', )
            script = """<script>
                function Jump(baseUrl,ths){
                    var val = ths.previousElementSibling.value;
                    if(val.trim().length>0){
                        location.href = baseUrl + val;
                    }
                }
                </script>"""
            list_page.append(jump)
            list_page.append(script)
            str_page = "".join(list_page)
            return str_page
    分页模块

    注:Pagination实例化接收两个参数:当前页current_page、新闻总数all_item,其中current_page一般通过url分组元素直接获取

     后台handler:

    class IndexHandler(BaseRequestHandler):
        def get(self, page=1):
            conn = ORM.session()  #获取数据库session对象
            all_count = conn.query(ORM.News).count()#计算新闻总数
            obj = Pagination(page, all_count)  #实例化pagination对象
            current_user_id = self.session['user_info']['nid'] if self.session['is_login'] else 0  #如果登陆获取用户id,否则,用户id=0,下面的查询结果也为空
            result = conn.query(ORM.News.nid,
                                ORM.News.title,
                                ORM.News.url,
                                ORM.News.content,
                                ORM.News.ctime,
                                ORM.UserInfo.username,
                                ORM.NewsType.caption,
                                ORM.News.favor_count,
                                ORM.News.comment_count,
                                ORM.Favor.nid.label('has_favor')).join(ORM.NewsType, isouter=True).join(ORM.UserInfo, isouter=True).join(ORM.Favor, and_(ORM.Favor.user_info_id == current_user_id, ORM.News.nid == ORM.Favor.news_id), isouter=True)[obj.start:10] #从每页开始向下取10条,即每页显示10条新闻
            conn.close()
            str_page = obj.string_pager('/index/') 获取页码的字符串格式html
            self.render('home/index.html', str_page=str_page, news_list=result)

    十三、页面登陆验证(装饰器方式实现)

    1.普通登陆验证

    def auth_login_redirect(func):
     
        def inner(self, *args, **kwargs):
            if not self.session['is_login']:
                self.redirect(config.LOGIN_URL)
                return
            func(self, *args, **kwargs)
        return inner

    2.ajax提交数据的登陆验证

    def auth_login_json(func):
     
        def inner(self, *args, **kwargs):
            if not self.session['is_login']:
                rep = BaseResponse()
                rep.summary = "auth failed"
                self.write(json.dumps(rep.__dict__))
                return
            func(self, *args, **kwargs)
        return inner

    十四、上传文件

    前端:

    <form style="display: inline-block" id="upload_img_form" name="form" action="/upload_image" method="POST"  enctype="multipart/form-data" >
      <a id="fakeFile" class="fake-file">
        <span>上传图片</span>
        <input type="file" name="img" onchange="UploadImage(this);"/>
        <input type="text" name="url" class="hide" />
      </a>
      <iframe id='upload_img_iframe' name='upload_img_iframe' src=""  class="hide"></iframe>
    </form>

     js:

    function UploadImage(ths){
               document.getElementById('upload_img_iframe').onload = UploadImageComplete;  #页面加载完成后执行UploadImageComplete函数
               document.getElementById('upload_img_form').target = 'upload_img_iframe';  设置form提交到iframe
               document.getElementById('upload_img_form').submit(); #提交到iframe
           }
     
           /*
           上传图片之后回掉函数
           */
           function UploadImageComplete(){
               var origin = $("#upload_img_iframe").contents().find("body").text();#获取图片数据
               var obj = JSON.parse(origin); #转换成JavaScript对象
               if(obj.status){  #如果上传成功
                   var img = document.createElement('img'); #创建img标签
                   img.src = obj.data; 图片地址
                   img.style.width = "200px";
                   img.style.height = "180px";
                   $("#upload_img_form").append(img);添加图片
                   $('#fakeFile').addClass('hide');
                   $('#reUploadImage').removeClass('hide');
                   $('#fakeFile').find('input[type="text"]').val(obj.data);#保存图片地址到隐藏的input标签中
               }else{
                   alert(obj.summary);#否则显示错误信息
               }
           }
    js

    路由配置:

    (r"/upload_image", home.UploadImageHandler),

    后台handler:

    class UploadImageHandler(BaseRequestHandler):
        @decrator.auth_login_json  #上传前登陆验证
        def post(self, *args, **kwargs):
            rep = BaseResponse() 前端回应类
            try:
                file_metas = self.request.files["img"] 获取图片列表
                for meta in file_metas:
                    file_name = meta['filename'] #图片名
                    file_path = os.path.join('statics', 'upload', commons.generate_md5(file_name)) #保存地址
                    with open(file_path, 'wb') as up:
                        up.write(meta['body']) #在服务器写入图片
                rep.status = True #写入成功
                rep.data = file_path
            except Exception as ex:
                rep.summary = str(ex)#错误信息
            self.write(json.dumps(rep.__dict__)) #反馈给前端

    十五、文章发布

    1.定义需要验证的form类

    class IndexForm(BaseForm):
     
        def __init__(self):
            self.title = StringField()  #标题
            self.content = StringField(required=False) 内容
            self.url = StringField(required=False)  图片url
            self.news_type_id = IntegerField()   新闻类型
     
            super(IndexForm, self).__init__()

    2.前端html:

    <div class="f4">
      <a class="submit right" id="submit_img">提交</a>
      <span class="error-msg right"></span>
    </div>

    3.js

    function BindPublishSubmit(){
                $('#submit_link,#submit_text,#submit_img').click(function(){
                    // 获取输入内容并提交
                    var container = $(this).parent().parent();
                    var post_dict = {};
                    container.find('input[type="text"],textarea').each(function(){
                        post_dict[$(this).attr('name')] =$(this).val();
                    });
                    post_dict['news_type_id'] = container.find('.news-type .active').attr('value');
    
                    $.ajax({
                        url: '/index',
                        type: 'POST',
                        data: post_dict,
                        dataType: 'json',
                        success: function (arg) {
                            if(arg.status){
                                window.location.href = '/index';
                            }else{
                                console.log(arg);
                            }
                        }   
                    })
                });
            }
    View Code

    后台handler:

    @decrator.auth_login_json  #发布前登陆验证
        def post(self, *args, **kwargs):
            rep = BaseResponse()
     
            form = IndexForm()#实例化Indexform
            if form.valid(self):
                # title,content,href,news_type,user_info_id
           #写入数据库
                input_dict = copy.deepcopy(form._value_dict)
                input_dict['ctime'] = datetime.datetime.now()
                input_dict['user_info_id'] = self.session['user_info']['nid']
                conn = ORM.session()
                conn.add(ORM.News(**input_dict))
                conn.commit()
                conn.close()
                rep.status = True #写入成功
            else:
                rep.message = form._error_dict  #错误信息
     
            self.write(json.dumps(rep.__dict__))

    十六、点赞功能

    前端html:

    <a href="javascript:void(0);" class="digg-a" title="推荐" onclick="DoFavor(this,{{item[0]}});">
      {% if item[9] %}                       #是否已点过赞
        <span class="hand-icon icon-digg active"></span>
      {% else %}
        <span class="hand-icon icon-digg"></span>
      {% end %}
       <b id="favor_count_{{item[0]}}">{{item[7]}}</b>    #点赞数量<br></a>

    js:

    function DoFavor(ths, nid) {
     
                if($('#action_nav').attr('is-login') == 'true'){           #登陆状态才能点赞
                    $.ajax({
                        url: '/favor',
                        type: 'POST',
                        data: {news_id: nid},   #携带当前新闻id
                        dataType: 'json',
                        success: function(arg){
                            if(arg.status){
                                var $favorCount = $('#favor_count_'+nid);
                                var c = parseInt($favorCount.text());#获取当前点赞数量
                                if(arg.code == 2301){      #当前用户以前没点过赞
                                    $favorCount.text(c + 1);  #点赞数量加1
                                    $(ths).find('span').addClass('active'); 已经点过赞变深颜色
                                    AddFavorAnimation(ths); #+1动态效果
                                }else if(arg.code == 2302){ #该用户以前对该新闻点过赞
                                    $favorCount.text(c - 1); #点赞数量减1
                                    $(ths).find('span').removeClass('active'); 取消点赞颜色变浅
                                    MinusFavorAnimation(ths); #-1动态效果
                                }else{
     
                                }
     
                            }else{
     
                            }
                        }
                    })
                }else{
                    $('#accountDialog').removeClass('hide');
                    $('.shadow').removeClass('hide');
                }
            } 
    点赞

    后台handler:

    class FavorHandler(BaseRequestHandler):
     
        @decrator.auth_login_json #点赞前登陆验证
        def post(self, *args, **kwargs):
            rep = BaseResponse()
     
            news_id = self.get_argument('news_id', None) #获取新闻id
            if not news_id:
                rep.summary = "新闻ID不能为空."
            else:
                user_info_id = self.session['user_info']['nid']  #获取当前用户id
                conn = ORM.session()<br>      #查询当前用户是否对该新闻点过赞
                has_favor = conn.query(ORM.Favor).filter(ORM.Favor.user_info_id == user_info_id,
                                                         ORM.Favor.news_id == news_id).count()
                if has_favor:   #如果已经点过赞,删除数据库点赞表favor点赞数据,更新数据库新闻表news点赞数量-1
                    conn.query(ORM.Favor).filter(ORM.Favor.user_info_id == user_info_id,
                                                 ORM.Favor.news_id == news_id).delete()
                    conn.query(ORM.News).filter(ORM.News.nid == news_id).update(
                        {"favor_count": ORM.News.favor_count - 1}, synchronize_session="evaluate")
                    rep.code = StatusCodeEnum.FavorMinus    #返回已经点过赞的状态吗
                else:
                    conn.add(ORM.Favor(user_info_id=user_info_id, news_id=news_id, ctime=datetime.datetime.now()))
                    conn.query(ORM.News).filter(ORM.News.nid == news_id).update(
                        {"favor_count": ORM.News.favor_count + 1}, synchronize_session="evaluate")
                    rep.code = StatusCodeEnum.FavorPlus
                conn.commit()
                conn.close()
     
                rep.status = True   #操作成功
     
            self.write(json.dumps(rep.__dict__))  #返回给前端
    View Code

    点赞+1和-1动态效果js:

    /*
            点赞+1效果
             */
            function AddFavorAnimation(ths){
                var offsetTop = -10;
                var offsetLeft = 20;
                var fontSize = 24;
                var opacity = 1;
                var tag = document.createElement('i');
                tag.innerText = "+1";
                tag.style.position = 'absolute';
                tag.style.top = offsetTop + 'px';
                tag.style.left = offsetLeft + 'px';
                tag.style.fontSize = fontSize + "px";
                tag.style.color = "#5cb85c";
                $(ths).append(tag);
    
                var addInterval = setInterval(function(){
                        fontSize += 5;
                        offsetTop -= 15;
                        offsetLeft += 5;
                        opacity -= 0.1;
                        tag.style.top = offsetTop+ 'px';
                        tag.style.left = offsetLeft+ 'px';
                        tag.style.fontSize = fontSize + 'px';
                        tag.style.opacity = opacity;
                    if(opacity <= 0.5){
                        tag.remove();
                        clearInterval(addInterval);
                    }
                },40)
            }
    点赞+1效果
    /*
            点赞-1效果
             */
            function MinusFavorAnimation(ths){
                var offsetTop = -10;
                var offsetLeft = 20;
                var fontSize = 24;
                var opacity = 1;
                var tag = document.createElement('i');
                tag.innerText = "-1";
                tag.style.position = 'absolute';
                tag.style.top = offsetTop + 'px';
                tag.style.left = offsetLeft + 'px';
                tag.style.fontSize = fontSize + "px";
                tag.style.color = "#787878";
                $(ths).append(tag);
    
                var addInterval = setInterval(function(){
                        fontSize += 5;
                        offsetTop -= 15;
                        offsetLeft += 5 ;
                        opacity -= 0.1;
                        tag.style.top = offsetTop+ 'px';
                        tag.style.left = offsetLeft+ 'px';
                        tag.style.fontSize = fontSize + 'px';
                        tag.style.opacity = opacity;
                    if(opacity <= 0.5){
                        tag.remove();
                        clearInterval(addInterval);
                    }
                },40)
            }
    点赞-1效果

    十七、评论功能

    案例:

     前端html:

    <div class="box-r">
      <a href="javascript:void(0);" class="pub-icons add-pub-btn add-pub-btn-unvalid" onclick="DoComment({{item[0]}})">评论</a>  #携带新闻id
      <a href="javascript:void(0);" class="loading-ico loading-ico-top pub-loading-top hide">发布中...</a>
    </div> 
    {% raw tree(comment_tree) %}

    创建评论树字典函数:

    def build_tree(comment_list):
    
        comment_dic = collections.OrderedDict()
    
        for comment_obj in comment_list:
            if comment_obj[2] is None:
                # 如果是根评论,添加到comment_dic[评论对象] = {}
                comment_dic[comment_obj] = collections.OrderedDict()
            else:
                # 如果是回复的评论,则需要在 comment_dic 中找到其回复的评论
                tree_search(comment_dic, comment_obj)
        return comment_dic
    build_tree

    递归生成评论树函数:

    def tree_search(d_dic, comment_obj):
        # 在comment_dic中一个一个的寻找其回复的评论
        # 检查当前评论的 reply_id 和 comment_dic中已有评论的nid是否相同,
        #   如果相同,表示就是回复的此信息
        #   如果不同,则需要去 comment_dic 的所有子元素中寻找,一直找,如果一系列中未找,则继续向下找
        for k, v_dic in d_dic.items():
            # 找回复的评论,将自己添加到其对应的字典中,例如: {评论一: {回复一:{},回复二:{}}}
            if k[0] == comment_obj[2]:
                d_dic[k][comment_obj] = collections.OrderedDict()
                return
            else:
                # 在当前第一个跟元素中递归的去寻找父亲
                tree_search(d_dic[k], comment_obj)
    tree_search

    前端调用uimethod,生成评论html:

    TEMP1 = """
    <li class="items" style='padding:8px 0 0 %spx;'>
        <span class="folder" id='comment_folder_%s'>
            <div class="comment-L comment-L-top">
                <a href="#" class="icons zhan-ico"></a>
                <a href="/user/moyujian/submitted/1">
                    <img src="/statics/images/1.jpg">
                </a>
            </div>
            <div class="comment-R comment-R-top" style="background-color: rgb(246, 246, 246);">
                <div class="pp">
                    <a class="name" href="/user/moyujian/submitted/1">%s</a>
                    <span class="p3">%s</span>
                    <span class="into-time into-time-top">%s</span>
                </div>
                <div class="comment-line-top">
                    <div class="comment-state">
                        <a class="ding" href="javascript:void(0);">
                            <b></b>
                            <span class="ding-num">[0]</span>
                        </a>
                        <a class="cai" href="javascript:void(0);">
                            <b></b>
                            <span class="cai-num">[0]</span>
                        </a>
                        <span class="line-huifu">|</span>
                        <a class="see-a jubao" href="javascript:void(0);">举报</a>
                        <span class="line-huifu">|</span>
                        <a class="see-a huifu-a" href="javascript:void(0);" onclick="reply(%s,%s,'%s')"  id='comment_reply_%s' >回复</a>
                    </div>
                </div>
            </div>
        </span>
    
    """
    def tree(self, comment_dic):
        html = ''
        for k, v in comment_dic.items():
            html += TEMP1 %(0,k[0], k[3],k[1],k[4],k[7],k[0], k[3],k[0])
            html += generate_comment_html(v, 16)
            html += "</li>"
    
        return html
    
    def generate_comment_html(sub_comment_dic, margin_left_val):
        # html = '<ul style="background: url(&quot;/statics/images/pinglun_line.gif&quot;) 0px -10px no-repeat scroll transparent;margin-left:3px;">'
        html = '<ul>'
        for k, v_dic in sub_comment_dic.items():
            html += TEMP1 %(margin_left_val,k[0], k[3],k[1],k[4],k[7],k[0], k[3],k[0])
            if v_dic:
                html += generate_comment_html(v_dic, margin_left_val)
            html += "</li>"
        html += "</ul>"
        return html
    uimethod

    后台handler:

    class CommentHandler(BaseRequestHandler):
        def get(self, *args, **kwargs):  #展示评论信息
            # comment_list需要按照时间从小到大排列
            nid = self.get_argument('nid', 0)
            conn = ORM.session()
            comment_list = conn.query(
                ORM.Comment.nid,
                ORM.Comment.content,
                ORM.Comment.reply_id,
                ORM.UserInfo.username,
                ORM.Comment.ctime,
                ORM.Comment.up,
                ORM.Comment.down,
                ORM.Comment.news_id
            ).join(ORM.UserInfo, isouter=True).filter(ORM.Comment.news_id == nid).all()
    
            conn.close()
            """
            comment_list = [
                (1, '111',None),  #评论id,评论内容,回复id,如果是None,则代表回复新闻
                (2, '222',None),
                (3, '33',None),
                (9, '999',5),
                (4, '444',2),
                (5, '555',1),
                (6, '666',4),
                (7, '777',2),
                (8, '888',4),
            ]
            """
    
            comment_tree = commons.build_tree(comment_list)  #最终传递给前端的是一个字典
    
            self.render('include/comment.html', comment_tree=comment_tree)
    
    
        @decrator.auth_login_json  #提交评论前做登陆验证
        def post(self, *args, **kwargs):  #提交评论信息
            rep = BaseResponse() #前端反馈类
            form = CommentForm() #评论的form验证
    
            if form.valid(self):  #如果验证成功
                form._value_dict['ctime'] = datetime.datetime.now() #获取当前时间
    
                conn = ORM.session() #获取数据库session对象
           # 将评论写入数据库
                obj = ORM.Comment(user_info_id=self.session['user_info']['nid'],
                                  news_id=form._value_dict['news_id'],
                                  reply_id=form._value_dict['reply_id'],
                                  content=form._value_dict['content'],
                                  up=0,
                                  down=0,
                                  ctime=datetime.datetime.now())
                conn.add(obj)
                conn.flush()
                conn.refresh(obj)  #同时获取评论的自增id
    
                rep.data = {
                    'user_info_id': self.session['user_info']['nid'],
                    'username': self.session['user_info']['username'],
                    'nid': obj.nid,
                    'news_id': obj.news_id,
                    'ctime': obj.ctime.strftime("%Y-%m-%d %H:%M:%S"),
                    'reply_id': obj.reply_id,
                    'content': obj.content,
                }
          #更新数据库的评论数量
                conn.query(ORM.News).filter(ORM.News.nid == form._value_dict['news_id']).update(
                    {"comment_count": ORM.News.comment_count + 1}, synchronize_session="evaluate")
                conn.commit()
                conn.close()
    
                rep.status = True  #评论成功的状态信息
            else:
                rep.message = form._error_dict  #错误信息
            print(rep.__dict__)
            self.write(json.dumps(rep.__dict__)) #反馈给前端
    后台handler

    js:

    function DoComment(nid){
                var content = $("#comment_content_"+nid).val();
                var reply_id = $("#reply_id_"+nid).attr('target');
     
                if($('#action_nav').attr('is-login') == 'true'){  #已经登录才发ajax请求
                    $.ajax({
                        url: '/comment',
                        type: 'POST',
                        data: {content: content, reply_id:reply_id, news_id: nid},#发送评论内容,回复的评论id,新闻id
                        dataType: 'json',
                        success: function(arg){
                            // 获取评论信息,将内容添加到指定位置
                            console.log(arg);
                            if(arg.status){
                                $('#comment_content_' + arg.data.news_id).val('');
                                var a = '<ul><li class="items" style="padding:8px 0 0 16px;"><span class="folder" id="comment_folder_';
                                var b = arg.data.nid;
                                var c = '"><div class="comment-L comment-L-top"><a href="#" class="icons zhan-ico"></a><a href="/user/moyujian/submitted/1"><img src="/statics/images/1.jpg"></a></div><div class="comment-R comment-R-top" style=""><div class="pp"><a class="name" href="/user/moyujian/submitted/1">';
                                var d = arg.data.username;
                                var e = '</a><span class="p3">';
                                var f = arg.data.content;
                                var g= '</span><span class="into-time into-time-top">';
                                var h = arg.data.ctime;
                                var i = '</span></div><div class="comment-line-top"><div class="comment-state"><a class="ding" href="javascript:void(0);"><b>顶</b><span class="ding-num">[0]</span></a><a class="cai" href="javascript:void(0);"><b>踩</b><span class="cai-num">[0]</span></a><span class="line-huifu">|</span> <a class="see-a jubao" href="javascript:void(0);">举报</a> <span class="line-huifu">|</span> <a class="see-a huifu-a" href="javascript:void(0);" onclick="';
                                var j = "reply(" + arg.data.news_id + "," +arg.data.nid+",'"+arg.data.username+"')";
                                var k = '">回复</a></div></div></div></span></li></ul>';
                                var tag = a+b+c+d+e+f+g+h+i+j+k;
                                console.log(arg,tag);
                                if(arg.data.reply_id){
                                    $comment_folder = $('#comment_folder_' + arg.data.reply_id);
                                    $comment_folder.after(tag);
                                }else{
                                    $('#comment_list_'+arg.data.news_id).append(tag);
                                }
     
     
                            }else{
                                alert('error');
                            }
                        }
                    })
                }else{
                    $('#accountDialog').removeClass('hide');
                    $('.shadow').removeClass('hide');
                }
            }
    js
  • 相关阅读:
    不务正业系列-浅谈《过气堡垒》,一个RTS玩家的视角
    [LeetCode] 54. Spiral Matrix
    [LeetCode] 40. Combination Sum II
    138. Copy List with Random Pointer
    310. Minimum Height Trees
    4. Median of Two Sorted Arrays
    153. Find Minimum in Rotated Sorted Array
    33. Search in Rotated Sorted Array
    35. Search Insert Position
    278. First Bad Version
  • 原文地址:https://www.cnblogs.com/Erick-L/p/6866450.html
Copyright © 2011-2022 走看看