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
  • 相关阅读:
    30个在线学习设计与开发的站点
    马云:你的一生到底该往哪个方向走?
    那些争议最大的编程观点
    Python 标识符
    Python 环境搭建
    Python 简介
    PyCharm 使用技巧
    Shell脚本———— /dev/null 2>&1详解
    linux 创建连接命令 ln -s 软链接
    scp命令详解
  • 原文地址:https://www.cnblogs.com/Erick-L/p/6866450.html
Copyright © 2011-2022 走看看