精华内容
下载资源
问答
  • (1) 利用SQL Server Management Studio创建一个名为employees的数据库。该数据库的主数据库文件逻辑名称为employees,初始大小为10MB,最大尺寸为无限大,增长速度为10%;数据库的日志文件逻辑名称为employees_log...
  • Python—docx 批量生成 docx 文件

    千次阅读 2019-07-20 11:46:28
    、文档结构 二、方法 from docx import Document from docx.shared import Pt # 字体磅数 from docx.oxml.ns import qn # 中文格式 from docx.shared import Inches # 图片尺寸 from docx.enum.text import WD_...

    一、文档结构

    在这里插入图片描述

    二、方法

    在这里插入图片描述

    from docx import Document
    from docx.shared import Pt         # 字体磅数
    from docx.oxml.ns import qn        # 中文格式
    from docx.shared import Inches     # 图片尺寸
    from docx.enum.text import WD_PARAGRAPH_ALIGNMENT
    from docx.shared import RGBColor
    import time
    
    
    
    today = time.strftime("%Y{y}%m{m}%d{d}",time.localtime()).format(y='年',m='月',d='日')
    
    
    # 对客户或者领导的工作报告
    Customer_Leader = {
        '经理1':'9:20',
        '经理2':'9:30',
        '经理3':'10:40',
        '张三': '13:20',
        '李四': '14:20',
        '王五': '15:20',
    }
    
    
    for key,value in Customer_Leader.items():
        document = Document()
        document.styles['Normal'].font.name = u'微软雅黑'
        document.styles['Normal'].font.size = Pt(14)
        document.styles['Normal']._element.rPr.rFonts.set(qn('w:eastAsia'), '微软雅黑')
    
    
        p = document.add_heading('关于XXX'+Customer_Leader[key]+'今日的报告', 0)      # 添加标题
    
        # 自定义--段落样式(样式名, 样式类型),样式类型: 1为段落样式, 2为字符样式, 3为表格样式)
        UserStyle1 = document.styles.add_style('UserStyle1', 1)
        UserStyle1.font.size = Pt(30)                                          # 字体尺寸
        UserStyle1.font.color.rgb = RGBColor(0xff, 0xde, 0x00)                 # 字体颜色
        UserStyle1.paragraph_format.alignment = WD_PARAGRAPH_ALIGNMENT.CENTER  # 居中文本
    
        UserStyle1.paragraph_format.first_line_indent = Inches(0.3)            # 缩进
        UserStyle1.paragraph_format.space_before = Pt(18)                      # 段前间距
        UserStyle1.paragraph_format.space_after = Pt(12)                       # 段后间距
        UserStyle1.font.name = 'Times New Roman'                               # 西文字体
        UserStyle1._element.rPr.rFonts.set(qn('w:eastAsia'), '微软雅黑')        # 中文字体
    
    
        # 自定义--字符样式
        UserStyle2 = document.styles.add_style('UserStyle2', 2)
        UserStyle2.font.size = Pt(15)
        UserStyle2.font.color.rgb = RGBColor(0x0c, 0x8a, 0xc5)
        UserStyle2.font.name = '宋体'
    
        UserStyle2._element.rPr.rFonts.set(qn('w:eastAsia'), '宋体')
    
        # 段落-内容(依次添加段落1,段落2)
        txt_p1 = '由于天气原因导致产品价格上升%s的通知'%(today)
        p1 = document.add_paragraph(txt_p1, style=UserStyle1)
    
        txt_p2 = '预计2019年1-6月归属于上市公司股东的净利润盈利:7,419.218016万元至7,869.218016万元,同比上年增长约:50%至60%。'
        p2 = document.add_paragraph('').add_run(txt_p2,style=UserStyle2)
    
        # 添加分页符
        document.add_page_break()
    
        # 创建 有序列表
        document.add_paragraph('').add_run('微软财务分析人-有序列表内容').font.size = Pt(20)
        document.add_paragraph('UBS分析师', style='List Number')
        document.add_paragraph('纳德拉', style='List Number')
        document.add_paragraph('德意志银行分析师', style='List Number')
    
        # 创建 无序列表
        document.add_paragraph('').add_run('微软财务收入-无序列表内容').font.size = Pt(30)
        document.add_paragraph('游戏还是主要基于游戏机和个人电脑,微软对自己的定位', style='List Bullet')
        document.add_paragraph('微软会在Windows 10商务组件业务上保持两位数的增长', style='List Bullet')
        document.add_paragraph('最新的Office对Teams来说是一个全新的突破', style='List Bullet')
        document.add_paragraph('用户长期地表现出对产品的忠诚度,该产品越来越被认可了', style='List Bullet')
    
        # 添加分页符,插入图片(默认居左)
        document.add_page_break()
        document.add_paragraph('').add_run('详情价格单').font.size = Pt(30)
        document.add_picture('../gggg/001.png', width=Inches(1.25))
    
        records = (
                    ('浙江东日',
                    '预计2019年1-6月归属于上市公司股东的净利润盈利:7,419.218016万元至7,869.218016万元,同比上年增长约:50%至60%。',
                    '因雨雪天气较多,蔬菜批发价格上涨'),
                    ('长城汽车', '预计2019年1-6月归属于上市公司股东的净利润盈利:153,000万元,同比上年下降:58.6%。',
                    '公司提高产品优惠额度让利消费者,并继续加大品牌推广力度及研发投入'),
                    ('圣济堂', '预计2019年1-6月归属于上市公司股东的净利润亏损:1,500万元至2,500万元。',
                    '甲醇市场处于持续低迷状态,医药行业受集中带量采购政策所影响,药品价格有所下降')
                   )
    
        # 添加表
        table = document.add_table(rows=1, cols=3)
        hdr_cells = table.rows[0].cells            # 设置表首行标题
        hdr_cells[0].text = '股票简称'              # 表首行标题赋值
        hdr_cells[1].text = '业绩变动'
        hdr_cells[2].text = '业绩变动原因'
        for qty, id, desc in records:              # for循环将records内容赋值到单元格内
            row_cells = table.add_row().cells
            row_cells[0].text = str(qty)
            row_cells[1].text = id
            row_cells[2].text = desc
    
        document.save('./demo_'+str(key)+'.docx')  # 保存文件
    
    

    三、表格批量写入数据

    from docx import Document
    from docx.shared import Pt         # 字体磅数
    from docx.oxml.ns import qn        # 中文格式
    from docx.shared import RGBColor
    import time
    
    
    
    
    def read_txt(txt_path):
    	f = open(txt_path, "r", encoding="utf-8")
    	lines = f.readlines()
    	data_list = []
    	for line in lines:
    		data_list.append(line.strip('\n'))
    	f.close()
    	return data_list
    
    
    def write_docx(doc_path,index,text):
        document = Document()
        document.styles['Normal'].font.name = u'宋体'
        document.styles['Normal'].font.size = Pt(10)
        document.styles['Normal']._element.rPr.rFonts.set(qn('w:eastAsia'), '宋体')
    
        records = (
                    zip(tuple(text[:5000]),tuple(text[5000:10000]))
                   )
    
        # 添加表(n行两列)
        table = document.add_table(rows=0, cols=2)
        for text1, text2 in records:
            row_cells = table.add_row().cells
            row_cells[0].text = text1
            row_cells[1].text = text2
        document.save(doc_path+'/medical_'+str(index)+'.docx')  # 保存文件
    
    
    if __name__ == '__main__':
        txt_path = './text_local/medical.txt'
        doc_path = './test_doc'
        data_list = read_txt(txt_path)
        doc_num = len(data_list)//5000+1
        for index in range(doc_num):
            try:
                write_docx(doc_path, index,data_list[index*5000:(index+1)*10000])
            except:
                write_docx(doc_path, index,data_list[index*10000:])
    

    四、批量修改表格文字字体大小颜色

    import os
    from docx import Document
    from docx.shared import Pt         # 字体磅数
    from docx.oxml.ns import qn
    from docx.shared import RGBColor
    
    
    
    def get_docx(docx_paths):
        docx_list = []
        for paths,dirs,filenames in os.walk(docx_paths):
            for filename in filenames:
                if filename.endswith('.docx'):
                    docx_list.append(paths+'/'+filename)
        return docx_list
    
    '''
    增值税发票:深蓝色          #172194(23,33,148)
    增值税电子发票:深紫黑色    #1F1023(31,16,35)
    增值税发票:浅蓝色          #51557C(81,85,124)
    医疗票据: 深黄黑色        #3F4537(63,69,55)
    增值税电子发票: 浅黄色     #90867A(144,134,122)
    '''
    
    
    def rewrite_doc(docx_path,output_docx_paths):
        save_name = output_docx_paths+'/xieti_127_127_127_'+docx_path.split('/')[-1]
        document = Document(docx_path)
        table = document.tables[0]
        for row in table.rows:
            for cell in row.cells:
                paragraphs = cell.paragraphs
                paragraph = paragraphs[0]
                run_obj = paragraph.runs
                run = run_obj[0]
                # run.font.size = Pt(6.5)                                    # 字体大小
                # run.font.name = u'宋体'                                    # 字体
                # run._element.rPr.rFonts.set(qn('w:eastAsia'), '宋体')      # 字体
                # run.font.name = 'Times New Roman'
                run.font.color.rgb = RGBColor(23,33,148)                     # 字体颜色
                run.italic = True
        document.save(save_name)
    
    
    if __name__ == '__main__':
        input_docx_paths = './标准轮廓宋体大小6.5副本'
        output_docx_paths = './xieti_23_33_148'
        docx_list = get_docx(input_docx_paths)
        for docx_path in docx_list:
            rewrite_doc(docx_path, output_docx_paths)
    

    有关模板填充:https://www.cnblogs.com/cofear/p/10686299.html

    展开全文
  • DuckX DuckX是用于创建Office docx文件的库DuckX帮助您在C ++中快速使用docx文件。 状态文档(docx)[Word]读取/写入/编辑DuckX创建,读取和...它打开一个名为file.docxdocx文件,遍历各段并运行以打印它们:#include
  • 当进程创建一个新的线程时,系统会查看进程地址空间中所有的DLL文件映射,之后用DLL_THREAD_ATTACH来调用DLL中的DllMain函数。要注意的是,系统不会进程的主线程使用值DLL_THREAD_ATTACH来调用DLL中的DllMain函数...
  • UI界面元素 PAGE 3 目 录 TOC \o "1-3" \h \z \u...布局文件 在Android程序中界面是通过布局文件设定的在每个应用程序创建时会默认包含一个主界面布局该布局位于res/layout目录中如图1所示默认主布局的名字activity_m
  • 离散元软件中的Geometry系统允许用户创建、导入和输出几何体数据。一般地,这些几何体数据可用于range的过滤器功能,或用于视图的可视化辅助功能。当然,这些几何体数据也可以被用户分配相应的FISH外置参数和group组...
  • Python语言中的缩进在程序中长度统一且强制使用,只要统一即可,不一定是4空格(尽管这是惯例) IPO模型指:Input Process Output 字符串的正向递增和反向递减序号体系:正向是从左到右,0到n-1,反向是从右到左...

    测验1:Python基本语法元素

    知识点概要:

    • 普遍认为Python语言诞生于1991
    • Python语言中的缩进在程序中长度统一且强制使用,只要统一即可,不一定是4个空格(尽管这是惯例)
    • IPO模型指:Input Process Output
    • 字符串的正向递增和反向递减序号体系:正向是从左到右,0到n-1,反向是从右到左,-1到-n,举例
    str = "csdn" 
    #str[0]就表示字符串c, str[-1]表示"n"
    
    • Python的合法命名规则:命名包含字母,数字,下划线,但是首字符不能是数字
    • Python中获得用户输入的方式为:input()
    • Python中的保留字:type不是,是内置函数,def elif import 都是保留字
    • Python的数据类型有整数、列表、字符串等,但是不包括实数,实数是数学概念,在Python中对应着浮点数
    • 保留字if-elif-else用于表示分支结构,in用来进行成员判断
    • print()格式化输出,控制浮点数的小数点后两位输出应为:print("{:.2f}".format(XX)) :.2f哪一个都不能少

    编程测试:

    • Hello World 的条件输出:获得用户输入的一个整数,参考该整数值,打印输出"Hello World",要求:‪‬‪‬‪‬‪‬‪‬‮‬‪‬‫‬‪‬‪‬‪‬‪‬‪‬‮‬‭‬‫‬‪‬‪‬‪‬‪‬‪‬‮‬‫‬‫‬‪‬‪‬‪‬‪‬‪‬‮‬‫‬‮‬‪‬‪‬‪‬‪‬‪‬‮‬‫‬‫‬‪‬‪‬‪‬‪‬‪‬‮‬‫‬‮‬
      如果输入值是0,直接输出"Hello World"‪‬‪‬‪‬‪‬‪‬‮‬‪‬‫‬‪‬‪‬‪‬‪‬‪‬‮‬‭‬‫‬‪‬‪‬‪‬‪‬‪‬‮‬‫‬‫‬‪‬‪‬‪‬‪‬‪‬‮‬‫‬‮‬‪‬‪‬‪‬‪‬‪‬‮‬‫‬‫‬‪‬‪‬‪‬‪‬‪‬‮‬‫‬‮‬
      如果输入值大于0,以两个字符一行方式输出"Hello World"(空格也是字符)‪‬‪‬‪‬‪‬‪‬‮‬‪‬‫‬‪‬‪‬‪‬‪‬‪‬‮‬‭‬‫‬‪‬‪‬‪‬‪‬‪‬‮‬‫‬‫‬‪‬‪‬‪‬‪‬‪‬‮‬‫‬‮‬‪‬‪‬‪‬‪‬‪‬‮‬‫‬‫‬‪‬‪‬‪‬‪‬‪‬‮‬‫‬‮‬
      如果输入值小于0,以垂直方式输出"Hello World"
    # eval()函数可以将默认输入的字符串去掉双引号并进行表达式运算,如输入500+20,默认
    #得到的输入为一个字符串“500+20”,但是使用eval()函数我们得到的是一个整型数字:520
    Number = eval(input())
    if Number == 0:
        print("Hello World")
    elif Number > 0:
        print("He\nll\no \nWo\nrl\nd")
    else:
        for c in "Hello World":
            print(c)
    
    • 数值运算:获得用户输入的一个字符串,格式如下:M OP N ,其中,M和N是任何数字,OP代表一种操作,表示为如下四种:+, -, *, /(加减乘除)‪‬‪‬‪‬‪‬‪‬‮‬‪‬‫‬‪‬‪‬‪‬‪‬‪‬‮‬‭‬‫‬‪‬‪‬‪‬‪‬‪‬‮‬‫‬‫‬‪‬‪‬‪‬‪‬‪‬‮‬‫‬‮‬‪‬‪‬‪‬‪‬‪‬‮‬‫‬‫‬‪‬‪‬‪‬‪‬‪‬‮‬‫‬‮‬根据OP,输出M OP N的运算结果,统一保存小数点后2位。‪‬‪‬‪‬‪‬‪‬‮‬‪‬‫‬‪‬‪‬‪‬‪‬‪‬‮‬‭‬‫‬‪‬‪‬‪‬‪‬‪‬‮‬‫‬‫‬‪‬‪‬‪‬‪‬‪‬‮‬‫‬‮‬‪‬‪‬‪‬‪‬‪‬‮‬‫‬‫‬‪‬‪‬‪‬‪‬‪‬‮‬‫‬‮‬
      注意:M和OP、OP和N之间可以存在多个空格,不考虑输入错误情况。
    print("{:.2f}".format(eval(input())))
    

    测验2:Python基本图形绘制

    知识点概要:

    • 正确引用turtle库的方式:
    import turtle
    #t是别名,可以更换其他名称
    import turtle as t 
    from turtle import setup
    from turtle import *
    

    import setup from turtle是不正确的

    • turtle库是一个直观有趣的图形绘制函数库,最早成功应用于LOGO编程语言,turtle绘图体系以水平右侧为绝对方位的0度,turtle坐标系的原点****默认在屏幕正中间
    • turtle.circle(-90,90)表示绘制一个半径为90像素的弧形,圆心在小海龟当前行进的右侧
    # circle(x,y)表示以x长度为半径,y为角度,
    #当前方向左侧x处为圆心画圆,其中x,y都可以是负数,相应取反
    #当前方向是水平向右的,对应直角坐标系中的x轴正方向
    #x为正,则圆心在y轴正方向上,y为正,逆时针画圆,圆弧角度为y
    #x为负则相反,圆心在y轴负方向上,y为正,顺时针画圆,圆弧角度为y
    
    • turtle.seth(to_angle)函数的作用是设置小海龟当前行进方向为to_angle,to_angle是角度的整数值
    • turtle.fd(distance)函数的作用是向小海龟当前行进方向前进distance距离
    • turtle.pensize(size)函数的作用是改变画笔的宽度为size像素
    • turtle**.circle**()函数不能绘制椭圆形
    • turtle.circle(x,y)函数绘制半圆,第二个参数y是180的奇数倍
    • turtle.penup()的别名有turtle.pu(),turtle.up()
    • turtle.colormode()的作用是设置画笔RGB颜色的表示模式
    • turtle.width()和turtle.pensize()都可以用来设置画笔尺寸
    • turtle.pendown()只是放下画笔,并不绘制任何内容
    • 改变turtle画笔的运行方向有left()、right()和seth()函数,bk()只能后退,但是不改变方向
    • turtle.done()用来停止画笔绘制,但绘图窗体不关闭,建议在每个turtle绘图最后增加turtle.done()
    • 循环相关保留字是:for…in和while,def用于定义函数

    编程测试:

    • turtle八边形绘制:使用turtle库,绘制一个八边形
    import turtle as t
    t.pensize(2)
    for i in range(8):
        t.fd(100)
        t.left(45)
    
    • turtle八角图形绘制:使用turtle库,绘制一个八角图形
    import turtle as t
    t.pensize(2)
    for i in range(8):
        t.fd(150)
        t.left(135)
    

    测验3:基本数据类型

    知识点概要:

    • pow(x,0.5)能够计算x的平方根,计算负数的平方根将产生复数
    • 字符串.strip()方法的功能是去掉字符串两侧指定的字符
    • 字符串.split()方法的功能是按照指定字符分隔字符串为数组
    • 字符串.repalce()方法的功能是替换字符串中特定字符
    • +操作符用来连接两个字符串序列
    • 字符串是一个连续的字符序列,使用\n可以实现打印字符信息的换行
    • val = pow(2,1000)
      #返回val结果的长度值要使用 len(str(val)),因为整型没有len()方法,要通过str()函数
      #将数字类型转换为字符串
    • 正确引用time库的方式如下:
    import time
    from time import strftime
    from time import *
    
    • Python语言的整数类型表示十进制(一般表示)二进制(0b或0B开头)八进制(0o或0O开头)十六进制(0x或0X开头)
    • %运算符的意思是取余数
    • 字符串切片操作:s[N:M],从N到M,但是不包括M
    name="Python语言程序设计课程"
    print(name[0],name[2:-2],name[-1])
    #输出结果为:P thon语言程序设计 程
    
    • print("{0:3}".format('PYTHON'))代码执行的结果是PYTHON,{0:3}表示输出的宽度是3,但是如果字符串长度超过3就以字符串长度显示

    编程测试:

    • 平方根格式化:获得用户输入的一个整数a,计算a的平方根,保留小数点后3位,并打印输出。‪‬‪‬‪‬‪‬‪‬‮‬‪‬‫‬‪‬‪‬‪‬‪‬‪‬‮‬‭‬‫‬‪‬‪‬‪‬‪‬‪‬‮‬‫‬‫‬‪‬‪‬‪‬‪‬‪‬‮‬‫‬‮‬‪‬‪‬‪‬‪‬‪‬‮‬‫‬‫‬‪‬‪‬‪‬‪‬‪‬‮‬‫‬‮‬输出结果采用宽度30个字符、右对齐输出、多余字符采用加号(+)填充,‪‬‪‬‪‬‪‬‪‬‮‬‪‬‫‬‪‬‪‬‪‬‪‬‪‬‮‬‭‬‫‬‪‬‪‬‪‬‪‬‪‬‮‬‫‬‫‬‪‬‪‬‪‬‪‬‪‬‮‬‫‬‮‬‪‬‪‬‪‬‪‬‪‬‮‬‫‬‫‬‪‬‪‬‪‬‪‬‪‬‮‬‫‬‮‬如果结果超过30个字符,则以结果宽度为准
    a = eval(input())
    print("{:+>30.3f}".format(a**0.5)) 
    # +是填充字符 >是右对齐 30是宽度 .3f是保留小数点后3位
    #若平凡根后是一个复数,复数的实部和虚部都是浮点数,.3f可以将实部和虚部分别取三位小数
    
    • 字符串分段组合:获得输入的一个字符串s,以字符减号(-)分割s,将其中首尾两段用加号(+)组合后输出
    InputStr = input()
    strs = InputStr.split('-')
    print(strs[0]+'+'+strs[-1])
    print("{}+{}".format(strs[0], strs[-1]))
    #s.split(k)以k为标记分割s,产生一个列表
    #通过该题目,掌握split()方法的使用,注意:k可以是单字符,也可以是字符串
    

    测验4:程序的控制结构

    知识点概要:

    • for…in…中in的后面需要的是一个迭代类型(组合类型),{1;2;3;4;5}不是Python的有效数据类型
    • range(x,y)
    for i in range(0,2):
    	print(i)
    #输出结果为:0 1
    
    • 程序的三种基本结构:顺序结构,循环结构和分支结构
    • 循环是程序根据条件判断结果向后反复执行的一种运行方式,是一种程序的基本控制结构,条件循环和遍历循环结构都是基本的循环结构,死循环能够用于测试性能,形式上的死循环可以用break来退出,例如
    x = 10
    while True:
    	x = x -1
    	if x == 1:
    		break
    
    • p = -p #表示给p赋值为它的负数,Python中的=是赋值符号
    • 缩进表达层次关系,同时用来判断当前Python语句在分支结构
    • continue结束当次循环,但是不跳出循环
    • random库中用于生产随机小数的函数是random(),而randint()/getrandbits()/randrange()都产生随机整数
    • 程序错误是一个大的概念,不仅指代码运行错误,更代表功能逻辑错误。使用异常处理try-excepy,可以对程序的异常进行捕捉和处理,程序运行可能不会出错,但逻辑上可能会出错

    编程测试:

    • 四位玫瑰数:四位玫瑰数是4位数的自幂数。自幂数是指一个 n 位数,它的每个位上的数字的 n 次幂之和等于它本身‪‬‪‬‪‬‪‬‪‬‮‬‪‬‫‬‪‬‪‬‪‬‪‬‪‬‮‬‭‬‫‬‪‬‪‬‪‬‪‬‪‬‮‬‫‬‫‬‪‬‪‬‪‬‪‬‪‬‮‬‫‬‮‬‪‬‪‬‪‬‪‬‪‬‮‬‫‬‫‬‪‬‪‬‪‬‪‬‪‬‮‬‫‬
      例如:当n为3时,有1^3 + 5^3 + 3^3 = 153,153即是n为3时的一个自幂数,3位数的自幂数被称为水仙花数‪‬‪‬‪‬‪‬‪‬‮‬‪‬‫‬‪‬‪‬‪‬‪‬‪‬‮‬‭‬‫‬‪‬‪‬‪‬‪‬‪‬‮‬‫‬‫‬‪‬‪‬‪‬‪‬‪‬‮‬‫‬‮‬‪‬‪‬‪‬‪‬‪‬‮‬‫‬‫‬‪‬‪‬‪‬‪‬‪‮‬‫
      请输出所有4位数的四位玫瑰数,按照从小到大顺序,每个数字一行
    #个人思路:求四位数的各个位数abcd
    for i in range(1000,10000):
        a = i%10
        b = (i//10)%10
        c = (i//100)%10
        d = (i//1000)%10
        if a**4 + b**4 + c**4 + d**4 == i:
            print(i)
    #参考答案:字符串+eval()
    s = ""
    for i in range(1000, 10000):
        t = str(i)
        if pow(eval(t[0]),4) + pow(eval(t[1]),4) + pow(eval(t[2]),4) + pow(eval(t[3]),4) == i :
            print(i)
    
    • 100以内素数之和:求100以内所有素数之和并输出‪‬‪‬‪‬‪‬‪‬‮‬‪‬‫‬‪‬‪‬‪‬‪‬‪‬‮‬‭‬‫‬‪‬‪‬‪‬‪‬‪‬‮‬‫‬‫‬‪‬‪‬‪‬‪‬‪‬‮‬‫‬‮‬‪‬‪‬‪‬‪‬‪‬‮‬‫‬‫‬‪‬‪‬‪‬‪‬‪‬‮‬‫‬‮‬
      素数指从大于1,且仅能被1和自己整除的整数‪‬‪‬‪‬‪‬‪‬‮‬‪‬‫‬‪‬‪‬‪‬‪‬‪‬‮‬‭‬‫‬‪‬‪‬‪‬‪‬‪‬‮‬‫‬‫‬‪‬‪‬‪‬‪‬‪‬‮‬‫‬‮‬‪‬‪‬‪‬‪‬‪‬‮‬‫‬‫‬‪‬‪‬‪‬‪‬‪‬‮‬,提示:可以逐一判断100以内每个数是否为素数,然后求和
    sum = 0
    for i in range(2,100):
        isFlag = 1 #判断是否为素数
        for j in range(2,i): #遍历2-i-1,看是否能被i整除
            if i%j == 0: #被整除说明不是素数
                isFlag = 0
                break
        if isFlag == 1:
            sum += i
    print(sum)
    #参考答案:将判断是否为素数封装为一个函数,倾向于这种解题思路
    def is_prime(n):
        for i in range(2,n):
            if n%i == 0:
                return False
        return True
    sum = 0
    for i in range(2,100):
        if is_prime(i):
            sum += i
    print(sum)
    

    测验5:函数和代码复用

    知识点概要:

    • 函数作用:增强代码可读性、降低编程复杂度、复用代码,函数不能直接提高代码的执行速度
    • 全局变量与局部变量:函数的参数一般为局部变量,函数内使用global s 表示变量s为全局变量
    • 函数调用前必须已经存在函数定义,否则无法执行,Python内置函数直接使用,不需要引用任何模块
    • 模块内高耦合,模块间低耦合:高耦合的特点是复用较为困难,模块间关系应尽可能简单,模块之间耦合度低,尽可能合理划分功能块,功能块内部耦合度高
    • 递归不会提高程序的执行效率,任何递归程序都可以通过堆栈或队列变为非递归程序
    • 函数是一段具有特定功能的、可重用的语句组,可以看做是一段具有名字的程序,通过函数名来调用,同时不需要知道函数的内部实现原理,只需要知道调用方法(接口)即可
    • def func(*a,b):是错误的函数定义,*a表示可变参数,可变参数只能放在函数参数的最后,即def func(a,*b):
    • 函数可以包含0个或多个return语句
    • 每个递归函数至少存在一个基例,但可能存在多个基例,基例表示不再进行递归,同时决定了递归的深度

    编程测试:

    • 随机密码生成:以整数17为随机数种子,获取用户输入整数N为长度,产生3个长度为N位的密码,密码的每位是一个数字。每个密码单独一行输出,产生密码采用random.randint()函数
    import random
    def genpwd(length):
        a = 10**(length-1)
        b = 10**length - 1
        return "{}".format(random.randint(a, b))
    length = eval(input())
    random.seed(17)
    for i in range(3):
        print(genpwd(length))
    
    #思路类似,同样过了
    def genpwd(length):
        high = 10**length
        low = 10**(length-1)
        return random.randrange(low,high)
    
    • 连续质数计算:获得用户输入数字N,计算并输出从N开始的5个质数,单行输出,质数间用逗号,分割。
      注意:需要考虑用户输入的数字N可能是浮点数,应对输入取整数;最后一个输出后不用逗号
    def prime(m): #判断是否为质数
        for i in range(2,m):
            if m%i == 0:
                return False
        return True
        
    n = eval(input())
    if n != int(n): #考虑输入为浮点数的情况
        n = int(n) + 1
    else:
        n = int(n)   
        
    times = 0 #统计质数的次数
    res = [] #存放输出结果
    while times < 5:
        if prime(n):
            res.append(n)
            times += 1
        n += 1
    for i in res[:len(res)-1]:
        print(i,end=",")
    print(res[-1]) #最后一个不输出逗号
    
    #参考答案
    def prime(m):
        for i in range(2,m):
            if m % i == 0:
                return False
        return True
    #需要对输入小数情况进行判断,获取超过该输入的最小整数(这里没用floor()函数)
    n = eval(input())
    n_ = int(n)
    n_ = n_+1 if n_ < n else n_
    count = 5
    #对输出格式进行判断,最后一个输出后不增加逗号(这里没用.join()方法)
    while count > 0:
        if prime(n_):
            if count > 1:
                print(n_, end=",")
            else:
                print(n_, end="")
            count -= 1 
        n_ += 1
    

    测验6:组合数据类型

    知识点概要:

    • 列表ls,ls.append(x)表示只能向列表最后增加一个元素,如果x是一个列表,则该列表作为一个元素增加到ls中
    • 集合“交并差补”四种运算分别对应的运算符是:& | - ^
    • 字典d,d.values()返回的是dict_values类型,包括字典中的所有值,通常与for…in组合使用
    • Python的元组类型:元组采用逗号和圆括号(可选)来表示,一旦创建就不能修改,一个元组可以作为另一个元祖的元素,可用多级索引获取信息,序列类型(元组、列表)中的元素都可以是不同类型
    • 创建字典时,如果相同键对应不同值,字典采用最后一个"键值对"
    d= {'a': 1, 'b': 2, 'b': '3'}
    print(d['b'])
    #输出结果:3
    
    • 集合与字典类型最外侧都用{}表示,不同在于集合类型元素是普通元素,字典类型元素是键值对。字典在程序设计中非常常用,因此直接采用{}默认生成一个空字典
    • 对于字典d:x in d表示判断x是否是字典d中的键,键是值的序号,也是字典中值的索引方式
    • Python序列类型有:列表类型、元组类型、字符串类型(Python内置数据类型中没有数组类型)
    • 组合数据类型能够将多个相同类型或不同类型的数据组织起来,通过单一的表示使数据操作更有序、更容易
    • 组合数据类型可以分为3类:序列类型、集合类型和映射类型;
    • Python的字符串元组列表类型都属于序列类型,序列类型总体上可以看成一维向量,如果其元素都是序列,则可被当作二维向量
    • 对于序列s:s.index(x)返回序列s中元素x第一次出现的序号,并不返回全部序号

    编程测试:

    • 数字不同数之和:获得用户输入的一个整数N,输出N中所出现不同数字的和‪‬‪‬‪‬‪‬‪‬‮‬‪‬‫‬‪‬‪‬‪‬‪‬‪‬‮‬‭‬‫‬‪‬‪‬‪‬‪‬‪‬‮‬‫‬‫‬‪‬‪‬‪‬‪‬‪‬‮‬‫‬‮‬‪‬‪‬‪‬‪‬‪‬‮‬‫‬‫‬‪‬‪‬‪‬‪‬‪‬‮‬‫‬‮‬
      例如:用户输入 123123123,其中所出现的不同数字为:1、2、3,这几个数字和为6
    #参考答案:字符串可以通过list()直接变成列表,或通过set()直接变成集合
    n = input()
    ss = set(n)
    s = 0
    for i in ss:
        s += eval(i)
        #s += int(i) #同样可以
    print(s)
    
    • 人名最多数统计:给出了一个字符串,其中包含了含有重复的人名,请直接输出出现最多的人名
    #先使用字典建立"姓名与出现次数"的关系,然后找出现次数最多数对应的姓名
    s = '''双儿 洪七公 赵敏 赵敏 逍遥子 鳌拜 殷天正 金轮法王 乔峰 杨过 洪七公 郭靖 
           杨逍 鳌拜 殷天正 段誉 杨逍 慕容复 阿紫 慕容复 郭芙 乔峰 令狐冲 郭芙 
           金轮法王 小龙女 杨过 慕容复 梅超风 李莫愁 洪七公 张无忌 梅超风 杨逍 
           鳌拜 岳不群 黄药师 黄蓉 段誉 金轮法王 忽必烈 忽必烈 张三丰 乔峰 乔峰 
           阿紫 乔峰 金轮法王 袁冠南 张无忌 郭襄 黄蓉 李莫愁 赵敏 赵敏 郭芙 张三丰 
           乔峰 赵敏 梅超风 双儿 鳌拜 陈家洛 袁冠南 郭芙 郭芙 杨逍 赵敏 金轮法王 
           忽必烈 慕容复 张三丰 赵敏 杨逍 令狐冲 黄药师 袁冠南 杨逍 完颜洪烈 殷天正 
           李莫愁 阿紫 逍遥子 乔峰 逍遥子 完颜洪烈 郭芙 杨逍 张无忌 杨过 慕容复 
           逍遥子 虚竹 双儿 乔峰 郭芙 黄蓉 李莫愁 陈家洛 杨过 忽必烈 鳌拜 王语嫣 
           洪七公 韦小宝 阿朱 梅超风 段誉 岳灵珊 完颜洪烈 乔峰 段誉 杨过 杨过 慕容复 
           黄蓉 杨过 阿紫 杨逍 张三丰 张三丰 赵敏 张三丰 杨逍 黄蓉 金轮法王 郭襄 
           张三丰 令狐冲 赵敏 郭芙 韦小宝 黄药师 阿紫 韦小宝 金轮法王 杨逍 令狐冲 阿紫 
           洪七公 袁冠南 双儿 郭靖 鳌拜 谢逊 阿紫 郭襄 梅超风 张无忌 段誉 忽必烈 
           完颜洪烈 双儿 逍遥子 谢逊 完颜洪烈 殷天正 金轮法王 张三丰 双儿 郭襄 阿朱 
           郭襄 双儿 李莫愁 郭襄 忽必烈 金轮法王 张无忌 鳌拜 忽必烈 郭襄 令狐冲 
           谢逊 梅超风 殷天正 段誉 袁冠南 张三丰 王语嫣 阿紫 谢逊 杨过 郭靖 黄蓉 
           双儿 灭绝师太 段誉 张无忌 陈家洛 黄蓉 鳌拜 黄药师 逍遥子 忽必烈 赵敏 
           逍遥子 完颜洪烈 金轮法王 双儿 鳌拜 洪七公 郭芙 郭襄 赵敏'''
           
    names = s.split()
    d = {}
    for name in names:
        d[name] = d.get(name, 0) + 1
    Maxkey = ""
    MaxValue = 0
    for k in d:
        if d[k] > MaxValue:
            Maxkey = k
            MaxValue = d[k]
    print(Maxkey)
    #参考答案
    ls = s.split()
    d = {}
    for i in ls:
        d[i] = d.get(i, 0) + 1
    max_name, max_cnt = "", 0
    for k in d:
        if d[k] > max_cnt:
            max_name, max_cnt = k, d[k]
    print(max_name)
    

    测验7:文件和数据格式化

    知识点概要:

    • 数据组织纬度一维数据采用线性方式组织,对应于数学中的数组和集合等概念;二维数据采用表格方式组织,对应于数学中的矩阵;高维数据由键值对类型的数据构成,采用对象方式组织,字典就用来表示高维数据,一般不用来表示一二纬数据
    • Python对文件操作采用的统一步骤是:打开-操作-关闭(其中关闭可以省略)
    • CSV文件格式是一种通用的、相对简单的文件格式,应用于程序之间转移表格数据,CSV文件的每一行是一维数据,可以使用Python中的列表类型表示,整个CSV文件是一个二维数据,一般来说,CSV文件都是文本文件,由相同的编码字符组成
    • 二维列表切片ls = [[1,2,3],[4,5,6],[7,8,9]]获取其中的元素5要使用:ls[1][1]
    • 文件可以包含任何内容,是数据的集合和抽象,是存储在辅助存储器上的数据序列,而函数或类才是程序的集合和抽象
    • 打开文件后采用close()关闭文件是一个好习惯。如果不调用close(),当前Python程序完全运行退出时,该文件引用被释放,即程序退出时,相当于调用了close(),默认关闭
    • Python文件的"+"打开模式,与r/w/a/x一同使用,在原功能基础上同时增加了读写功能,同时赋予文件的读写权限
    • 同一个文件既可以用文本方式打卡,也可以用二进制方式打开
    • 列表元素如果都是列表,其可能表示二维数据,如[[1,2],[3,4],[5,6]],如果列表元素不都是列表,则它表示一维数据
    • Python文件读操作有:read()、readline()、readlines(),没有readtext()方法

    编程测试:

    • 文本的平均列数:打印输出附件文件的平均列数,计算方法如下:‪‬‪‬‪‬‪‬‪‬‮‬‪‬‫‬‪‬‪‬‪‬‪‬‪‬‮‬‭‬‫‬‪‬‪‬‪‬‪‬‪‬‮‬‫‬‫‬‪‬‪‬‪‬‪‬‪‬‮‬‫‬‮‬‪‬‪‬‪‬‪‬‪‬‮‬‫‬‫‬‪‬‪‬‪‬‪‬‪‬‮‬‫‬‮‬
      (1)有效行指包含至少一个字符的行,不计算空行‪‬‪‬‪‬‪‬‪‬‮‬‪‬‫‬‪‬‪‬‪‬‪‬‪‬‮‬‭‬‫‬‪‬‪‬‪‬‪‬‪‬‮‬‫‬‫‬‪‬‪‬‪‬‪‬‪‬‮‬‫‬‮‬‪‬‪‬‪‬‪‬‪‬‮‬‫‬‫‬‪‬‪‬‪‬‪‬‪‬‮‬‫‬‮‬
      (2)每行的列数为其有效字符数‪‬‪‬‪‬‪‬‪‬‮‬‪‬‫‬‪‬‪‬‪‬‪‬‪‬‮‬‭‬‫‬‪‬‪‬‪‬‪‬‪‬‮‬‫‬‫‬‪‬‪‬‪‬‪‬‪‬‮‬‫‬‮‬‪‬‪‬‪‬‪‬‪‬‮‬‫‬‫‬‪‬‪‬‪‬‪‬‪‬‮‬
      (3)平均列数为有效行的列数平均值,采用四舍五入方式取整数进位
    #for line in f 获取的line包含每行最后的换行符(\n),所以去掉该换行符再进行统计
    f = open("latex.log", "r", encoding="utf-8")
    lines = 0
    columns = 0
    for line in f:
        line = line.strip("\n")
        if len(line):
            lines += 1
            columns += len(line)
    print("{:.0f}".format(columns/lines))
    f.close()
    
    #参考答案如下:
    f = open("latex.log")
    s, c = 0, 0
    for line in f:
        line = line.strip("\n")
        if line == "":
            continue
        s += len(line)
        c += 1
    print(round(s/c))
    

    -CSV格式清洗与转换:附件是一个CSV格式文件,提取数据进行如下格式转换:‪‬‪‬‪‬‪‬‪‬‮‬‪‬‫‬‪‬‪‬‪‬‪‬‪‬‮‬‭‬‫‬‪‬‪‬‪‬‪‬‪‬‮‬‫‬‫‬‪‬‪‬‪‬‪‬‪‬‮‬‫‬‮‬‪‬‪‬‪‬‪‬‪‬‮‬‫‬‫‬‪‬‪‬‪‬‪‬‪‬‮‬‫‬‮‬
    (1)按行进行倒序排列‪‬‪‬‪‬‪‬‪‬‮‬‪‬‫‬‪‬‪‬‪‬‪‬‪‬‮‬‭‬‫‬‪‬‪‬‪‬‪‬‪‬‮‬‫‬‫‬‪‬‪‬‪‬‪‬‪‬‮‬‫‬‮‬‪‬‪‬‪‬‪‬‪‬‮‬‫‬‫‬‪‬‪‬‪‬‪‬‪‬‮‬‫‬‮
    (2)每行数据倒序排列‪‬‪‬‪‬‪‬‪‬‮‬‪‬‫‬‪‬‪‬‪‬‪‬‪‬‮‬‭‬‫‬‪‬‪‬‪‬‪‬‪‬‮‬‫‬‫‬‪‬‪‬‪‬‪‬‪‬‮‬‫‬‮‬‪‬‪‬‪‬‪‬‪‬‮‬‫‬‫‬‪‬‪‬‪‬‪‬‪‬‮‬‫‬‮‬
    (3)使用分号(;)代替逗号(,)分割数据,无空格‪‬‪‬‪‬‪‬‪‬‮‬‪‬‫‬‪‬‪‬‪‬‪‬‪‬‮‬‭‬‫‬‪‬‪‬‪‬‪‬‪‬‮‬‫‬‫‬‪‬‪‬‪‬‪‬‪‬‮‬‫‬‮‬‪‬‪‬‪‬‪‬‪‬‮‬‫‬‫‬‪‬‪‬‪‬‪‬‪‬‮‬‫‬‮‬
    按照上述要求转换后将数据输出

    f = open("data.csv", "r", encoding="utf-8")
    txt = f.readlines()
    txt.reverse() #按行进行倒序排列
    for line in txt:
        #line = line.strip("\n") #去除末尾换行符
        #line = line.replace(" ","") #去空格
        line = line.strip("\n").replace(" ", "")
        #ls = line.split(",") 
        #ls = ls[::-1]
        ls = line.split(",")[::-1] #逗号分隔并将分隔后的元素倒序
        print(";".join(ls)) #元素间插入分号
    f.close()
    
    #参考答案(使用strip()方法去掉每行最后的回车,使用replace()去掉每行元素两侧的空格)
    f = open("data.csv")
    ls = f.readlines()
    ls = ls[::-1]
    lt = []
    for item in ls:
        item = item.strip("\n")
        item = item.replace(" ", "")
        lt = item.split(",")
        lt = lt[::-1]
        print(";".join(lt))
    f.close()
    

    测验8:程序设计方法学

    知识点概要:

    • 用户体验:编程只是手段,程序最终为人类服务,用户体验很重要,一个提醒进度的进度条、一个永不抛出异常的程序、一个快速的响应、一个漂亮的图标、一个合适尺寸的界面等都是用户体验的组成部分。总的来说,用户体验是一切能够提升程序用户感受的组成
    • 计算思维是基于计算机的思维模式,计算机出现之前,由于没有快速计算装置,计算所反映的思维模式主要是数学思维,即通过公式来求解问题。当快速计算装置出现后,计算思维才真正形成
    • 软件产品 = 程序功能 + 用户体验 ;产品不仅需要功能,更需要更好的用户体验。往往,产品都需要综合考虑技术功能和人文设计,这源于产品的商业特性。即,商业竞争要求产品不能只关心技术功能,更要关心用户易用和喜好需求
    • os库os.system()可以启动进程执行程序
    • 函数自顶向下设计的关键元素,通过定义函数及其参数逐层开展程序设计
    • os.path子库os.path.relpath(path)用来计算相对路径
    • Python第三方库安装:使用pip命令、使用集成安装工具或访问UCI网站下载安装文件,请不要直接联系作者索要第三方库
    • 计算思维的本质是:抽象自动化
    • os库是Python重要的标准库之一,提供了路径操作、进程管理等几百个函数功能,覆盖与操作系统、文件操作等相关的众多功能;os库适合所有操作系统
    • 计算生态以竞争发展、相互依存和迅速更迭为特点,在开源项目间不存在顶层设计,以类自然界"适者生存"的方式形成技术演进路径

    编程测试:

    • 英文字符的鲁棒输入:获得用户的任何可能输入,将其中的英文字符进行打印输出,程序不出现错误
    inputStr = input()
    for i in inputStr:
        if i.islower() or i.isupper():
            print(i,end="")
         
    #参考答案:采用遍历字符的方式实现,通过约束字母表达到鲁棒效果
    alpha = []
    for i in range(26):
        alpha.append(chr(ord('a') + i))
        alpha.append(chr(ord('A') + i))
    s = input()
    for c in s:
        if c in alpha:
            print(c, end="")
    
    • 数字的鲁棒输入:获得用户输入的一个数字,可能是浮点数或复数,如果是整数仅接收十进制形式,且只能是数字。对输入数字进行平方运算,输出结果,要求:
      1)无论用户输入何种内容,程序无错误‪‬‪‬‪‬‪‬‪‬‮‬‪‬‫‬‪‬‪‬‪‬‪‬‪‬‮‬‭‬‫‬‪‬‪‬‪‬‪‬‪‬‮‬‫‬‫‬‪‬‪‬‪‬‪‬‪‬‮‬‫‬‮‬‪‬‪‬‪‬‪‬‪‬‮‬‫‬‫‬‪‬‪‬‪‬‪‬‪‬‮‬
      2)如果输入有误,请输出"输入有误"
    number = input()
    try:
        #complex()和complex(eval())之间的比较
        #将能够排除非数字类型的输入
        if complex(number) == complex(eval(number)):
            print(eval(number) ** 2)
    except:
        print("输入有误") 
        
    '''
    不能直接使用eval(),否则用户可以通过输入表达式(如100**2)输入数字
    与要求不同(在实际应用中会带来安全隐患)
    '''
    

    测验9:Python计算生态纵览

    知识点概要:

    • Python网络爬虫方向第三方库有:Requests、Scrapy、pyspider
    • Python数据可视化方向第三方库有:Mayavi、Matplotlib、Seaborn
    • Python Web信息提取方向第三方库有:Beautiful Soup、Python-Goose、Re
    • Python游戏开发第三方库有:Panda3D、cocos2d、PyGame
    • Python数据分析方向第三方库有:Numpy、Pandas、Scipy
    • Python图形用户界面方向(GUI)第三方库有:PyQt5、wxPython、PyGObject
    • Python网站开发框架方向第三方库有:Django、Pyramid、Flask
    • Python文本处理方向第三方库有:NLTK、python-docx、PyPDF2
    • Python网络应用开发方向第三方库有:aip、MyQR、WeRobot
    • aip是百度的人工智能功能Python访问接口
    • Python人工智能方向第三方库有:TensorFlow、Scikit-learn、MXNet
    • Vizard是虚拟现实第三方库
    • pyovr是增强现实开发库
    • redis-py是redis数据的Python访问接口

    编程测试:

    • 系统基本信息获取:获取系统的递归深度、当前执行文件路径、系统最大UNICODE编码值等3个信息,并打印输出;输出格式如下:‪‬‪‬‪‬‪‬‪‬‮‬‫‬‫‬‪‬‪‬‪‬‪‬‪‬‮‬‭‬‪‬‪‬‪‬‪‬‪‬‪‬‮‬‪‬‫‬‪‬‪‬‪‬‪‬‪‬‮‬‭‬‫‬‪‬‪‬‪‬‪‬‪‬‮‬‫‬‫‬‪‬‪‬‪‬‪‬‪‬‮‬‫‬‮‬‪‬‪‬‪‬‪‬‪‬‮‬‫‬‫‬‪‬‪‬‪‬‪‬‪‬‮‬‫‬‮‬
      RECLIMIT:<深度>, EXEPATH:<文件路径>, UNICODE:<最大编码值>‪‬‪‬‪‬‪‬‪‬‮‬‪‬‫‬‪‬‪‬‪‬‪‬‪‬‮‬‭‬‫‬‪‬‪‬‪‬‪‬‪‬‮‬‫‬‫‬‪‬‪‬‪‬‪‬‪‬‮‬‫‬‮‬‪‬‪‬‪‬‪‬‪‬‮‬‫‬‫‬‪‬‪‬‪‬‪‬‪‬‮‬‫‬‮
      提示:请在sys标准库中寻找上述功能
    import sys
    print("RECLIMIT:{}, EXEPATH:{}, UNICODE:{}".format(sys.getrecursionlimit(), sys.executable, sys.maxunicode))
    
    • 二维数据表格输出:tabulate能够对二维数据进行表格输出,是Python优秀的第三方计算生态。‪‬‪‬‪‬‪‬‪‬‮‬‫‬‫‬‪‬‪‬‪‬‪‬‪‬‮‬‭‬‪‬‪‬‪‬‪‬‪‬‪‬‮‬‪‬‫‬‪‬‪‬‪‬‪‬‪‬‮‬‭‬‫‬‪‬‪‬‪‬‪‬‪‬‮‬‫‬‫‬‪‬‪‬‪‬‪‬‪‬‮‬‫‬‮‬‪‬‪‬‪‬‪‬‪‬‮‬‫‬‫‬‪‬‪‬‪‬‪‬‪‬‮‬‫‬‮‬编写程序,能够输出如下风格效果的表格数据
      输出效果
    data = [ ["北京理工大学", "985", 2000], \
             ["清华大学", "985", 3000], \
             ["大连理工大学", "985", 4000], \
             ["深圳大学", "211", 2000], \
             ["沈阳大学", "省本", 2000], \
        ]
    from tabulate import tabulate
    print(tabulate(data, tablefmt="grid"))
    

    期末测验

    *编程测试:

    • 无空隙回声输出:获得用户输入,去掉其中全部空格,将其他字符按收入顺序打印输出
    print(input().replace(" ",""))
    
    • 文件关键行数:关键行指一个文件中包含的不重复行。关键行数指一个文件中包含的不重复行的数量。‪‬‪‬‪‬‪‬‪‬‮‬‪‬‫‬‪‬‪‬‪‬‪‬‪‬‮‬‭‬‫‬‪‬‪‬‪‬‪‬‪‬‮‬‫‬‫‬‪‬‪‬‪‬‪‬‪‬‮‬‫‬‮‬‪‬‪‬‪‬‪‬‪‬‮‬‫‬‫‬‪‬‪‬‪‬‪‬‪‬‮‬‫‬‮‬统计附件文件中关键行的数量
    f = open("latex.log", "r", encoding="utf-8")
    d = {}
    for line in f:
        d[line] = d.get(line, 0) + 1
    print("共{}关键行".format(len(d)))
    
    #参考答案:如果需要"去重"功能,请使用集合类型
    f = open("latex.log")
    ls = f.readlines()
    s = set(ls)
    print("共{}关键行".format(len(s)))
    
    • 剩余两题与测验九重复,不重复记录
    展开全文
  • Octavo v 1.0 ... 作为一名,我想要种在Markdown中编写统计研讨会手册的方法(在Latex中没有所有繁琐的事情),让我的计算机生成多版本,插入常用文本,以及生成诸如APA样式的“参考”部分。 并处理方程
  • 网上有很多类似的教程,我这里也是现学现卖,非常简单 批量新建文件夹: 1、新建一个Excel空白工作薄文件(这里我用的Excel 2016);...4、将D列内容复制到记事本中,保存类型为“所有文件”,扩展名为.
  • 定义主函数,创建元素3的Shape类型的维数组,分别为数组元素创建Circle、Square和Triangle类型的对象,最后分别调用各数组元素的Area方法,输出相关信息。 (3)编制程序,完成自定义异常。
  • Git.docx

    2019-08-22 11:23:47
    当使用 git commit 进行提交操作时,Git会先计算每一个文件的校验和,在Git仓库中保存blob对象,然后计算每一个子目录的校验和,保存树对象,随后,Git便会创建一个提交对象,它包含提交信息(比如提交者名字,...
  • 掌握使用SQL语言创建修改数据库 掌握数据库的分离和附加 实验内容 用Transact-SQL语句完成以下操作 1创建数据库 创建一个test数据库其主数据文件逻辑test_data1物理文件名test_data1.mdf初始大小10MB最大尺寸100...
  • 毕业设计

    千次阅读 多人点赞 2019-12-06 21:32:08
    今晚就设计了一个表 user_info用户表 字段名称 类型 是否主键 是否空 字段描述 userName varchar(10) ...

    测试地址:https://blog.csdn.net/weixin_42717928/article/details/105212776

    开始做毕业设计了,记录一下最后一次做项目了,唉

    时间:2019/12/06

     

    Day 06

    今晚就设计了一个表

    user_info用户表

    字段名称

    类型

    是否主键

    是否为空

    字段描述

    userName

    varchar(10)

    Y

    N

    用户昵称

    userPassword

    varchar(20)

    N

    N

    用户密码

    userPhone

    varchar(20)

    Y

    N

    用户手机号

    userPicPath

    varchar(20)

    N

    N

    用户头像虚拟路径

    userLocPath

    varchar(20)

    N

    N

    用户头像真实路径

    userPS

    varchar(20)

    N

    Y

    用户个性签名

    userOriginal

    bigint

    N

    Y

    用户原创文章数

    userFollowers

    bigint

    N

    Y

    用户被关注人数

    userFollowing

    bigint

    N

    Y

    用户关注人数

    userLoginTime

    datetime

    N

    Y

    用户登录时间

    userRole

    int

    N

    Y

    用户权限

    原创文章,关注人数,被关注人数初始值是0

    个性签名初始值:喜欢是淡淡的爱;爱是深深的喜欢

    权限:null是普通用户,1是VIP用户,2是管理员

    头像虚拟路径初始值:/statics/uploadfiles/tx.jpg

    头像真实路径初始值:

    F:\footprint\out\artifacts\GZLifeCircle_war_exploded\statics\uploadfiles\tx.jpg

     

     

     

    Day 09

    CREATE DATABASE GZLifeCircle
    
    CREATE TABLE user_info(
      userName VARCHAR(20) NOT NULL,
      userPassword VARCHAR(20) NOT NULL,
      userPhone VARCHAR(20) NOT NULL,
      userPicPath VARCHAR(500) NOT NULL DEFAULT '/statics/uploadfiles/tx.jpg',
      userLocPath VARCHAR(500) NOT NULL DEFAULT 'F:\\GZLifeCircle\\out\\artifacts\\GZLifeCircle_war_exploded\\statics\\uploadfiles\\tx.jpg',
      userPS VARCHAR(40) DEFAULT '喜欢是淡淡的爱;爱是深深的喜欢',
      userOriginal BIGINT(30) DEFAULT 0,
      userFollowers BIGINT(30) DEFAULT 0,
      userFollowing BIGINT(30) DEFAULT 0,
      userGoods BIGINT(30) DEFAULT 0,
      userLoginTime DATETIME,
      userRole INT,
      PRIMARY KEY (userPhone)
    ) ENGINE=INNODB DEFAULT CHARSET=utf8;
    
    
    
    
    注意:\是转义字符,所以为了显示\,要\\

    user_info用户表

    字段名称

    类型

    是否主键

    是否为空

    字段描述

    userName

    varchar(20)

    N

    N

    用户昵称

    userPassword

    varchar(20)

    N

    N

    用户密码

    userPhone

    varchar(20)

    Y

    N

    用户手机号

    userPicPath

    varchar(500)

    N

    N

    用户头像虚拟路径

    userLocPath

    varchar(500)

    N

    N

    用户头像真实路径

    userPS

    varchar(40)

    N

    Y

    用户个性签名

    userOriginal

    Bigint(30)

    N

    N

    用户原创文章数

    userFollowers

    Bigint(30)

    N

    N

    用户被关注人数

    userFollowing

    Bigint(30)

    N

    N

    用户关注人数

    userGoods

    Bigint(30)

    N

    N

    用户文章获取的点赞数

    userLoginTime

    datetime

    N

    Y

    用户登录时间

    userRole

    int

    N

    Y

    用户权限

     

    原创文章,关注人数,被关注人数初始值是0

    个性签名初始值:喜欢是淡淡的爱;爱是深深的喜欢

    权限:null是普通用户,1是VIP用户,2是管理员

    头像虚拟路径初始值:/statics/uploadfiles/tx.jpg

    头像真实路径初始值:

    F:\footprint\out\artifacts\GZLifeCircle_war_exploded\statics\uploadfiles\tx.jpg

    用户昵称:注册时候会验证唯一

     

     

    登录页面

     

    注册页面

     

     

     

    Day 10

    对注册的内容限制一下

    昵称:4-8位字符串

    密码:8-16位有字母和数字组成

    手机格式要符合

    // 注册-----------------------------------------------注册//
    // trim:去掉两端空格,中间的不会去掉;replace里面的写法去掉中间的空格
    $(document).ready(function() {
        $("#userName").blur(function () {
            if($("#userName").val().trim().replace(/\s/g,"")==''||$("#userName").val().trim()==null){
                $('#userName').css({
                    border: "1px solid red",
                    boxShadow: "0 0 2px red"
                });
                $('#userCue').html("<font color='red'><b>×用户昵称不能为空</b></font>");
                	userNameF=false;
            }else{
                if($("#userName").val().length<4||$("#userName").val().length>8){
                    userNameF=false;
                    $('#userName').css({
                        border: "1px solid red",
                        boxShadow: "0 0 2px red"
                    });
                    $('#userCue').html("<font color='red'><b>×用户名4-8位</b></font>");
                }else {
                    // userName();
                }
            }
        });
        $("#userPassword").blur(function () {
            if($("#userPassword").val()==null){
                $('#userPassword').focus().css({
                    border: "1px solid red",
                    boxShadow: "0 0 2px red"
                });
                $('#userCue').html("<font color='red'><b>×密码不能为空</b></font>");
                userPasswordF=false;
            }else if (!/^(?![0-9]+$)(?![a-zA-Z]+$)[0-9A-Za-z]{8,16}$/.test($('#userPassword').val())){
                $('#userPassword').css({
                    border: "1px solid red",
                    boxShadow: "0 0 2px red"
                });
                $('#userCue').html("<font color='red'><b>×密码8-16有字母和数字组成</b></font>");
                userPasswordF=false;
            }else {
                $('#userPassword').css({
                    border: "1px solid #D7D7D7",
                    boxShadow: "0 0 2px #D7D7D7"
                });
                $('#userCue').html("<font >注册注意:</font>");
                userPasswordF=true;
            }
        });
        $("#confirmUserP").blur(function () {
            if($('#confirmUserP').val() != $('#userPassword').val()){
                $('#confirmUserP').css({
                    border: "1px solid red",
                    boxShadow: "0 0 2px red"
                });
                $('#userCue').html("<font color='red'><b>×两次密码不一致</b></font>");
                userPasswordF=false;
            }else {
                $('#confirmUserP').css({
                    border: "1px solid #D7D7D7",
                    boxShadow: "0 0 2px #D7D7D7"
                });
                $('#userCue').html("<font >注册注意:</font>");
                userPasswordF=true;
            }
        });
        $("#userPhone").blur(function () {
            if($("#userPhone").val().trim().replace(/\s/g,"")==''||$("#userPhone").val().trim()==null){
                $('#userPhone').css({
                    border: "1px solid red",
                    boxShadow: "0 0 2px red"
                });
                $('#userCue').html("<font color='red'><b>×手机号不能为空</b></font>");
                userPhone=false;
            }else if(!/(^1[3|4|5|7|8]\d{9}$)|(^09\d{8}$)/.test($("#userPhone").val())){
                $('#userPhone').css({
                    border: "1px solid red",
                    boxShadow: "0 0 2px red"
                });
                $('#userCue').html("<font color='red'><b>×手机号格式不对</b></font>");
                userPhoneF=false;
            }else{
                // userPh();
            }
        })

     

     

     

    Day 12

    上次对注册的内容用js做了限制

    但是我还想,注册要保证昵称和手机号是唯一的

    于是在原来那个方法成功的地方加了方法

    // 注册时候判断用户昵称是否已存在
    function userNameExist() {
        $.ajax({
            type:"POST",//请求类型
            url:"userNameExist.json",//请求的url
            data:{userName:$("#userName").val()},//请求参数
            dataType:"json",//ajax接口(请求url)返回的数据类型
            success:function(data){//data:返回数据(json对象)
                if(data.userName == "exist"){//账号不可用,错误提示
                    $('#userName').css({
                        border: "1px solid red",
                        boxShadow: "0 0 2px red"
                    });
                    $('#userCue').html("<font color='red'><b>×用户名已存在</b></font>");
                    userNameF=false;
                }else{//账号可用,正确提示
                    $('#userName').css({
                        border: "1px solid #D7D7D7",
                        boxShadow: "0 0 2px #D7D7D7"
                    });
                    $('#userCue').html("<font >注册注意:昵称可用√</font>");
                    userNameF=true;
                }
            },
            error:function(data){//当访问时候,404,500 等非200的错误状态码
                alert("请求错误!");
            }
        });
    }

    因为昵称和手机号方式是一样的,我方法就写在了一起

     /**
         * 注册时候根据userName查找UserInfo是否存在
         * 注册时候根据userPhone查找UserInfo是否存在
         * @param userName
         * @return
         * @throws Exception
         * */
        public UserInfo getUserInfo(String userName,String userPhone)throws Exception;
    <select id="getUserInfo" resultType="UserInfo">
            select u.* from user_info u
            <trim prefix="where" prefixOverrides="and | or">
                <if test="userName != null">
                    and u.userName = #{userName}
                </if>
                <if test="userPhone != null">
                    and u.userPhone = #{userPhone}
                </if>
            </trim>
    </select>

    效果就出来了

     

     

     

     

    Day 16

    想了一下,不想给名字限制我要做的内容,于是换了个名字,改成了知援无境

     

    写验证码遇到一个问题,不知道为什么我写sendMessage没反应,加了个1却可以了,有点迷

     

    直接点击获取验证码会有提示

     

     

     

     

    Day 19

    注册的js验证写完了,中途时候出现了两个问题

    一个是获取验证码后手机号改变了没有验证

    一个是先点了确认密码再点密码,会出现提示密码不规范

     

     

    短信效果就是这样:

    主要的js和jsp页面代码

    jsp代码如下:

    js代码如下:

    // 提交时候判断
    function binding(){
        var userPhone = $.trim($('#userPhone').val());
        var code = $.trim($('#code').val());
        alert("userPhone"+userPhone)
        alert("code"+code)
        if (!phoneReg.test(userPhone)) {
            alert("请输入正确的手机号码")
        }
        else if (code.trim()==null||code.trim()==""){
            alert("请输入验证码")
        }
        else if(codeApi==null||codeApi==""){
            alert("验证码不规范,请点击获取验证码")
        }
        else{
            if (codeApi.toString()=="shibai") {
                alert("手机已经被注册")
                codeApi=null;
            }
            else if (phoneNum.toString()!=userPhone){
                alert("输入的手机号已经改变,请重新获得验证码")
                phoneNum=null;
                codeApi=null;
            }
            else if(codeApi.toString()!=code.toString()){
                alert("验证码错误")
            }else {
                if (!userNameF){
                    alert("昵称不规范")
                }
                else if (!userPasswordF){
                    alert("密码不规范")
                }
                else if (!userPasswordCF){
                    alert("密码不一致")
                }
                else if (!userPhoneF){
                    alert("手机号不规范")
                }
                else {
                    if (confirm("是否提交?")) {
                        $("#regUser").submit();
                    }
                }
            }
        }
    }
    function sendMessage1() {
        curCount1 = count;//60秒数
        var userPhone = $.trim($('#userPhone').val());
        if (!phoneReg.test(userPhone)) {
            alert("请输入有效的手机号码");
        }else {
            phoneNum=userPhone;//发送验证码的号码
            codeCheck();//获得验证码
            //设置button效果,开始计时
            $("#btnSendCode").attr("disabled", "true");
            $("#btnSendCode").val( + curCount1 + "秒再获取");
            InterValObj1 = window.setInterval(SetRemainTime, 1000); //启动计时器,1秒执行一次
            //向后台发送处理数据
        }
    }
    function SetRemainTime() {
        if (curCount1 == 0) {
            window.clearInterval(InterValObj1);//停止计时器
            $("#btnSendCode").removeAttr("disabled");//启用按钮
            $("#btnSendCode").val("重新发送");
        }
        else {
            curCount1--;
            $("#btnSendCode").val( + curCount1 + "秒再获取");
        }
    }
    function codeCheck() {
        var userPhone = $.trim($('#userPhone').val());
        $.ajax({
            type : "POST",
            url : "/qt/userCode.json",
            data : 'userPhone=' + userPhone,
            dataType : "json",
            success:function(data) {
                if (data.code=="shibai"){
                    alert("手机号已被注册,请更换手机号")
                }
                else {
                    codeApi=data.code;
                }
            },
            error:function(data) {//当访问时候,404,500 等非200的错误状态码
                alert("出错!")
            }
        })
    }
    // 注册-----------------------------------------------注册//
    // trim:去掉两端空格,中间的不会去掉;replace里面的写法去掉中间的空格
    $(document).ready(function() {
        $("#userName").blur(function () {
            if($("#userName").val().trim().replace(/\s/g,"")==''||$("#userName").val().trim()==null){
                $('#userName').css({
                    border: "1px solid red",
                    boxShadow: "0 0 2px red"
                });
                $('#userCue').html("<font color='red'><b>×用户昵称不能为空</b></font>");
                	userNameF=false;
            }else{
                if($("#userName").val().length<4||$("#userName").val().length>8){
                    userNameF=false;
                    $('#userName').css({
                        border: "1px solid red",
                        boxShadow: "0 0 2px red"
                    });
                    $('#userCue').html("<font color='red'><b>×用户名4-8位</b></font>");
                }else {
                    userNameExist();
                }
            }
        });
        $("#userPassword").blur(function () {
            if($("#userPassword").val()==null){
                $('#userPassword').focus().css({
                    border: "1px solid red",
                    boxShadow: "0 0 2px red"
                });
                $('#userCue').html("<font color='red'><b>×密码不能为空</b></font>");
                userPasswordF=false;
                userPasswordCF=false;
            }else if (!/^(?![0-9]+$)(?![a-zA-Z]+$)[0-9A-Za-z]{8,16}$/.test($('#userPassword').val())){
                $('#userPassword').css({
                    border: "1px solid red",
                    boxShadow: "0 0 2px red"
                });
                $('#userCue').html("<font color='red'><b>×密码8-16有字母和数字组成</b></font>");
                userPasswordF=false;
                userPasswordCF=false;
            }else {
                $('#userPassword').css({
                    border: "1px solid #D7D7D7",
                    boxShadow: "0 0 2px #D7D7D7"
                });
                $('#userCue').html("<font >注册注意:密码可用</font>");
                userPasswordF=true;
                userPasswordIs=true;
            }
        });
        $("#confirmUserP").blur(function () {
            if($('#confirmUserP').val() != $('#userPassword').val()){
                $('#confirmUserP').css({
                    border: "1px solid red",
                    boxShadow: "0 0 2px red"
                });
                $('#userCue').html("<font color='red'><b>×两次密码不一致</b></font>");
                userPasswordCF=false;
                userPasswordF=false;
            }else {
                $('#confirmUserP').css({
                    border: "1px solid #D7D7D7",
                    boxShadow: "0 0 2px #D7D7D7"
                });
                $('#userCue').html("<font >注册注意:密码一致</font>");
                userPasswordCF=true;
                if (userPasswordIs){
                    userPasswordF=true;
                }
            }
        });
        $("#userPhone").blur(function () {
            if($("#userPhone").val().trim().replace(/\s/g,"")==''||$("#userPhone").val().trim()==null){
                $('#userPhone').css({
                    border: "1px solid red",
                    boxShadow: "0 0 2px red"
                });
                $('#userCue').html("<font color='red'><b>×手机号不能为空</b></font>");
                userPhone=false;
            }else if(!/(^1[3|4|5|7|8]\d{9}$)|(^09\d{8}$)/.test($("#userPhone").val())){
                $('#userPhone').css({
                    border: "1px solid red",
                    boxShadow: "0 0 2px red"
                });
                $('#userCue').html("<font color='red'><b>×手机号格式不对</b></font>");
                userPhoneF=false;
            }else{
                userPhoneExist();
            }
        })
    
    
    
    });
    // 注册时候判断用户昵称是否已存在
    function userNameExist() {
        $.ajax({
            type:"POST",//请求类型
            url:"userNameExist.json",//请求的url
            data:{userName:$("#userName").val()},//请求参数
            dataType:"json",//ajax接口(请求url)返回的数据类型
            success:function(data){//data:返回数据(json对象)
                if(data.userName == "exist"){//账号不可用,错误提示
                    $('#userName').css({
                        border: "1px solid red",
                        boxShadow: "0 0 2px red"
                    });
                    $('#userCue').html("<font color='red'><b>×用户名已存在</b></font>");
                    userNameF=false;
                }else{//账号可用,正确提示
                    $('#userName').css({
                        border: "1px solid #D7D7D7",
                        boxShadow: "0 0 2px #D7D7D7"
                    });
                    $('#userCue').html("<font >注册注意:昵称可用√</font>");
                    userNameF=true;
                }
            },
            error:function(data){//当访问时候,404,500 等非200的错误状态码
                alert("请求错误!");
            }
        });
    }
    // 注册时候判断手机号是否已存在
    function userPhoneExist() {
        $.ajax({
            type:"GET",//请求类型
            url:"userPhoneExist.json",//请求的url
            data:{userPhone:$("#userPhone").val()},//请求参数
            dataType:"json",//ajax接口(请求url)返回的数据类型
            success:function(data){//data:返回数据(json对象)
                if(data.userPhone == "exist"){//账号不可用,错误提示
                    $('#userPhone').css({
                        border: "1px solid red",
                        boxShadow: "0 0 2px red"
                    });
                    $('#userCue').html("<font color='red'><b>×手机号已存在</b></font>");
                    userPhoneF=false;
                }else{//账号可用,正确提示
                    $('#userPhone').css({
                        border: "1px solid #D7D7D7",
                        boxShadow: "0 0 2px #D7D7D7"
                    });
                    $('#userCue').html("<font >注册注意:手机可用</font>");
                    userPhoneF=true;
                }
            },
            error:function(data){//当访问时候,404,500 等非200的错误状态码
                alert("请求错误!");
                return false;
            }
        });
    }

    数据库有了

    不过页面还没写

     

     

     

     

    Day  20

    解决注册成功不跳登录页面

    登录判断

    // 登录-------------------------------------------登录
    $(document).ready(function() {
        $('#doLogin').click(function() {
            if (($('#uPh').val() == "")||($('#uPh').val() == null)) {
                $('#uPh').focus().css({
                    border: "1px solid red",
                    boxShadow: "0 0 2px red"
                });
                $('#doLoginError').html("<font color='red'><b>×手机号不能为空</b></font>");
                return false;
            }
            else{
                $('#uPh').css({
                    border: "1px solid #D7D7D7",
                    boxShadow: "0 0 2px #D7D7D7"
                });
                $('#doLoginError').html("<font >注册注意:</font>");
            }
    
            if (($('#uPa').val() == "")||($('#uPa').val() == null)){
                $('#uPa').focus().css({
                    border: "1px solid red",
                    boxShadow: "0 0 2px red"
                });
                $('#doLoginError').html("<font color='red'><b>×密码不能为空</b></font>");
                return false;
            }
            else {
                $('#uPa').css({
                    border: "1px solid #D7D7D7",
                    boxShadow: "0 0 2px #D7D7D7"
                });
                $('#doLoginError').html("<font >注册注意:</font>");
            }
            $('#login_form').submit();
        });
    });

    效果:

    不过可惜要点击登录才进行判断,没有做成是聚焦那种效果,后续有空再改吧

     

    再通过修改距离,显得空地方不这么大

     

     

     

    主页没有做,随便写了个主页测试一下能不能登录进去以及数据对不对

    补充一个知识点:

    //转发,地址不变,共享数据,服务器直接访问目标地址的 url网址,把里面的东西读取出来,但是客户端并不知道
    return "forward:/qt/index";
    //重定向,地址变化,不共享数据,服务器根据逻辑,发送一个状态码,告诉浏览器重新去请求那个地址
    return "redirect:/qt/login";
    //普通的
    return "qt/index";

     

     

    (1)输入错误账号密码去登录,效果:

    地址发送了变化

     

    (2)输入正确的账号密码登录,效果:

    地址发送了变化

    主页也拿到了数据

    ok了,感觉好像没什么问题了

    这两个我也搞得乱乱的,不行在看吧

     

     

     

     

    Day 22

    今天有空就是写网页,好玩又很耗时间,而且审美也差哈哈

    主要是先做了文章这块(中间)

     

    好久没写了,写网页遇到几个问题:

    1.高度自适应

    2.内容超过后省略号表示

    3.标签中li横向排列(我写的丑)

    参考了https://blog.csdn.net/leewokan/article/details/6626774

     

    至于文章流加载,用的是layui

     

     

     

     

    Day 23

    写网页遇到的问题

    (1)

     

     

    (2)横线不用hr吧,用这个

    <div style="border-bottom: 1px solid #EEEEEE;"></div>

     

     

    (3)p标签单行或多行超出显示省略号

    // 单行显示省略号
    p {
    	overflow: hidden;
    	text-overflow:ellipsis;
    	white-space: nowrap;
    }
    
    
    // 多行显示省略号,数字3为超出3行显示
    //-webkit-line-clamp表示要展示的行数
    p {
    	display: -webkit-box;
    	-webkit-box-orient: vertical;
    	-webkit-line-clamp: 3;
    	overflow: hidden;
    }
    

    今晚就写了两个东西

    能折叠的公告栏

     

     

     

     

     

     

     

    Day 29

    谁看过我

    要让图片在li中垂直居中

    <div class="f_two">
         <ul>
          	<li><a href=""><img src="../img/pvip.jpg" width="60px" height="60px"/></a></li>
          	<li><a href=""><img src="../img/pvip.jpg" width="60px" height="60px"/></a></li>
          	<li><a href=""><img src="../img/pvip.jpg" width="60px" height="60px"/></a></li>
          	<li><a href=""><img src="../img/pvip.jpg" width="60px" height="60px"/></a></li>      				
         </ul>
    </div>
    .f_two>ul{
        width: 100%;
        margin: 0 auto;
        display: table;
    }
    
    .f_two>ul>li{
        border: solid 1px black;
        padding-top: 7px;
        float: left;
        width: 25%;
        padding-bottom: 5px;
    }
    
    
    .f_two>ul>li>a>img{
        margin: 0 auto;
        display: block;
    }

     

    花了点时间,做了个很丑的网页

     

     

     

     

     

     

    Day 02

    写了个两个表,还不完整

    article_info 文章表

    字段名称

    类型

    是否主键

    是否为空

    字段描述

    Id

    Bigint(30)

    Y

    N

    游记id

    userName

    varchar(20)

    N

    N

    用户昵称

    userPhone

    varchar(20)

    N

    N

    用户手机号

    userPicPath

    varchar(500)

    N

    N

    用户头像虚拟路径

    articleTitle

    varchar()

    N

    N

    文章题目

    articleContent

    MediumText

    N

    N

    文章内容

    articlePublishDate

    Date

    N

    N

    文章发表日期

    articleModifyDate

    Date

    N

    N

    文章修改日期

    articleCommentSum

    Bigint(30)

    N

    N

    文章评论总数

    articleType

    varchar(20)

    N

    N

    文章类型

    articleRole

    varchar(20)

    N

    N

    文章权限

    articleDateShow

    varchar(20)

    N

    N

    文章时间展示形式

    articleType:1:普通文章;2:悬赏文章

    articleRole:1:所有可见;2:VIP文章;3:私密文章

    articleDateShow:1:今天;2:昨天;3:日期(年月-日)

     

     

     

    dictionary_info 字典表

    字段名称

    类型

    是否主键

    是否为空

    字段描述

    Id

    int

    Y

    N

    字典id

    typeCode

    varchar(20)

    N

    N

    字典类型

    valueId

    int

    N

    N

    属性id

    valueName

    varchar(30)

    N

    N

    属性名字

    【typeCode】

    articleType:1:普通文章;2:悬赏文章;

     

     

     

    Day 05

    今天把个人中心的页面写好了

     

     

     

     

     

     

    Day 07

    重新定义了一下文章表

    文章权限:1:所有可见;2:VIP文章;3:私密文章

    文章时间展示形式:1:今天;2:昨天;3:日期(年月-日):逻辑去判断

    头像虚拟路径初始值:/statics/uploadfiles/tx.jpg

    头像真实路径初始值:

    F:\footprint\out\artifacts\GZLifeCircle_war_exploded\statics\uploadfiles\tx.jpg

    文章评论总数:0

    文章的悬赏金额:0

    文章类型:1:普通文章;2:悬赏文章

    文章的浏览量:0

    文章权重:1

     

    CREATE TABLE article_info(
      id BIGINT(30) NOT NULL,
      userName VARCHAR(20) NOT NULL,
      userPhone VARCHAR(20) NOT NULL,
      userPicPath VARCHAR(500) NOT NULL DEFAULT '/statics/uploadfiles/tx.jpg',
      userLocPath VARCHAR(500) NOT NULL DEFAULT 'F:\\GZLifeCircle\\out\\artifacts\\GZLifeCircle_war_exploded\\statics\\uploadfiles\\tx.jpg',
      articleTitle VARCHAR(20) NOT NULL,
      articleContent MEDIUMTEXT NOT NULL,
      articlePublishDate DATE NOT NULL,
      articleModifyDate DATE NOT NULL,
      articleCommentSum BIGINT(30) NOT NULL DEFAULT 0,
      articleType INT NOT NULL,
      articleRole INT NOT NULL,
      articleDateShow INT NOT NULL,
      articleOneSort INT NOT NULL,
      articleTwoSort INT NOT NULL,
      articleThreeSort INT NOT NULL,
      articleWeight INT NOT NULL DEFAULT 1,
      articleHits BIGINT(30) NOT NULL DEFAULT 0,
      articleEnclosure VARCHAR(30),
      articleReward BIGINT(30) NOT NULL DEFAULT 0,
      PRIMARY KEY (id)
    ) ENGINE=INNODB DEFAULT CHARSET=utf8;

     

     

     

     

    Day 08

    针对搜索功能,我又加了个样式

    然后把代码贴上idea,基本资料也出来了

    接下来是勋章和头像框,有空再写了

     

     

     

     

    Day  12

    发布文章要选分类

    $(function(){
        //动态加载一级分类列表
        $.ajax({
            type:"GET",//请求类型
            url:"categorylevellist.json",//请求的url
            data:{pid:null},//请求参数
            dataType:"json",//ajax接口(请求url)返回的数据类型
            success:function(data){//data:返回数据(json对象)
                $("#categoryLevel1").html("");
                var options = "<option value=\"\">--请选择--</option>";
                for(var i = 0; i < data.length; i++){
                    options += "<option value=\""+data[i].id+"\">"+data[i].categoryName+"</option>";
                }
                $("#categoryLevel1").html(options);
            },
            error:function(data){//当访问时候,404,500 等非200的错误状态码
                alert("加载一级分类列表失败!");
            }
        });
        //动态加载二级分类列表
        $("#categoryLevel1").change(function(){
            var categoryLevel1 = $("#categoryLevel1").val();
            if(categoryLevel1 != '' && categoryLevel1 != null){
                $.ajax({
                    type:"GET",//请求类型
                    url:"categorylevellist.json",//请求的url
                    data:{pid:categoryLevel1},//请求参数
                    dataType:"json",//ajax接口(请求url)返回的数据类型
                    success:function(data){//data:返回数据(json对象)
                        $("#categoryLevel2").html("");
                        var options = "<option value=\"\">--请选择--</option>";
                        for(var i = 0; i < data.length; i++){
                            options += "<option value=\""+data[i].id+"\">"+data[i].categoryName+"</option>";
                        }
                        $("#categoryLevel2").html(options);
                    },
                    error:function(data){//当访问时候,404,500 等非200的错误状态码
                        alert("加载二级分类失败!");
                    }
                });
            }else{
                $("#categoryLevel2").html("");
                var options = "<option value=\"\">--请选择--</option>";
                $("#categoryLevel2").html(options);
            }
            $("#categoryLevel3").html("");
            var options = "<option value=\"\">--请选择--</option>";
            $("#categoryLevel3").html(options);
        });
        //动态加载三级分类列表
        $("#categoryLevel2").change(function(){
            var categoryLevel2 = $("#categoryLevel2").val();
            if(categoryLevel2 != '' && categoryLevel2 != null){
                $.ajax({
                    type:"GET",//请求类型
                    url:"categorylevellist.json",//请求的url
                    data:{pid:categoryLevel2},//请求参数
                    dataType:"json",//ajax接口(请求url)返回的数据类型
                    success:function(data){//data:返回数据(json对象)
                        $("#categoryLevel3").html("");
                        var options = "<option value=\"\">--请选择--</option>";
                        for(var i = 0; i < data.length; i++){
                            options += "<option value=\""+data[i].id+"\">"+data[i].categoryName+"</option>";
                        }
                        $("#categoryLevel3").html(options);
                    },
                    error:function(data){//当访问时候,404,500 等非200的错误状态码
                        alert("加载三级分类失败!");
                    }
                });
            }else{
                $("#categoryLevel3").html("");
                var options = "<option value=\"\">--请选择--</option>";
                $("#categoryLevel3").html(options);
            }
        });
    
    });
    
    

     

     

     

     

    Day  1/20

    发文章,使用的是summernote编辑器

    <script>
        var i;
        $(document).ready(function () {
            $('#summernote').summernote({
                height: '250px',
                lang: 'zh-CN',
                focus: true,
                maxHeight: null,
                minHeight: null,
                callbacks:{
                    onImageUpload:function (files) {
                        //上传图片到服务器
                        var formData = new FormData();
                        formData.append('file',files[0]);
                        $.ajax({
                            url : '/qt/uploadEditorImg',//后台文件上传接口
                            type : 'POST',
                            data : formData,
                            processData : false,
                            contentType : false,
                            success : function(data) {
                                i = data.Timg;
                                $('#summernote').summernote('insertImage',i);
                                // 可以看到图片路径
                                // alert(i);
                            },
                            error:function(){
                                alert("文章图片上传失败");
                            }
                        });
                    }
                }
            })
        });
        function su() {
            // 获取编辑器中的值
            $('#articleContent').val($('#summernote').summernote('code'));
        }
    </script>
        //文章图片上传
        @RequestMapping(value = "uploadEditorImg", method = {RequestMethod.POST, RequestMethod.GET})
        @ResponseBody
        public Object uploadEditorImg(HttpSession session, HttpServletRequest request, HttpServletResponse response) throws Exception {
    //      转换request,解析出request中的文件
            MultipartHttpServletRequest mr = (MultipartHttpServletRequest) request;
            Map<String, Object> map = new HashMap<>();
    //      返回此请求中包含的多部分文件的参数名的字符串对象的迭代器。这些是表单的字段名(name属性)(与普通参数类似),而不是原始文件名
            Iterator<String> iterator = mr.getFileNames();
            String fileName = "";
    //        判断是否存在下一个元素
            while (iterator.hasNext()) {
    //          返回此请求中上载文件的内容和说明,如果不存在则返回空值;next:指针下移,返回该指针所指向的元素
                MultipartFile multipartFile = mr.getFile(iterator.next());
                String realPath = session.getServletContext().getRealPath("/statics/TextImg/");
    //          获取原始文件名字,包括后缀
                String originalFilename = multipartFile.getOriginalFilename();
    //          获得文件名的扩展名,不包括.
                String suffix = FilenameUtils.getExtension(originalFilename);
                String base = "${pageContext.request.contextPath }/";
    //          equalsIgnoreCase:忽略大小写
                if (suffix.equalsIgnoreCase("jpg") || suffix.equalsIgnoreCase("png")
                        || suffix.equalsIgnoreCase("jpeg") || suffix.equalsIgnoreCase("pneg")) {
                    fileName = System.currentTimeMillis() + new Random().nextInt(1000000) + "_wz.jpg";//新的文件名
    //              路径是directory,名字是filename
                    File targetFile = new File(realPath, fileName);//新建文件夹
    //              判断路径(文件夹)是否存在,如果不存在就创建一个
                    if (!targetFile.getParentFile().exists()) {
                        System.out.println("文件夹不存在:"+targetFile.getParentFile());
    //                    getParentFile()的作用是获得父目录
                        targetFile.getParentFile().mkdirs();
                    }
                    System.out.println("文件夹存在:"+targetFile.getParentFile());
                    try {
                        // 将上传文件存储到服务器中
                        //使用此方法保存必须要绝对路径且文件夹必须已存在,否则报错
                        multipartFile.transferTo(targetFile);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
                map.put("Timg", "../../statics/TextImg/" + fileName);
            }
            return JSONObject.toJSONString(map);
        }

    也可以插入视频,但是暂时只有这些

     

     

     

     

     

    Day 1/23

    改了一下,修改日期可以为空

    然后把昵称,头像从文章表删除,只通过手机号去查用户表,这样就不会因为修改了用户表导致还要重新更新全部文章,这样不合理,删掉后也符合三大范式的规则。

     

    然后发现网页的附件没有写样式,暂时不做

    文章发表做好了

    内容就以HTML的形式存储在数据库了

     

    看了下数据库,发现Date类型的数据我怎么转都没有时分秒,不知道什么原因,就改了类型

    //new日期对象
    Date date = new Date();
    //转换提日期输出格式
    SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    articleInfo.setArticlePublishDate(dateFormat.format(date));

    这样就行了

     

    然后再把二级和三级分类改为可以为空,就完成文章发布这个模块了。

     

     

     

     

     

    Day  1/30

    “躺尸”N天,不敢出门,忽然醒悟,打开电脑好好学习

    今天先做文章的流加载

     

    然后运行时候出现

    调式了一下,发现数据是有的,就是在最后显示出现了问题

    - (33019 ms) - 2020-1-30 13:20:11[DEBUG](AbstractMessageConverterMethodProcessor.java:185) Written [{"articleInfos":[{"articleCommentSum":0,"articleContent":"<p>内容</p>","articleDateShow":1,"articleHits":0,"articleOneSort":1,"articlePublishDate":"2020-01-23 00:00:00.0","articleReward":0,"articleRole":1,"articleThreeSort":11,"articleTitle":"标题","articleTwoSort":4,"articleType":2,"articleWeight":1,"id":1,"userPhone":"17820423525"},{"articleCommentSum":0,"articleContent":"<p><br></p>","articleDateShow":1,"articleHits":0,"articleOneSort":1,"articlePublishDate":"2020-01-23 00:00:00.0","articleReward":0,"articleRole":1,"articleThreeSort":11,"articleTitle":"1","articleTwoSort":4,"articleType":2,"articleWeight":1,"id":2,"userPhone":"17820423525"},{"articleCommentSum":0,"articleContent":"<p><iframe webkitallowfullscreen=\"\" mozallowfullscreen=\"\" allowfullscreen=\"\" frameborder=\"0\" height=\"498\" width=\"510\" src=\"//player.youku.com/embed/XNDE2Mjk3ODIyOA\" class=\"note-video-clip\"></iframe></p><p>这是文章的内容</p><p>123456789AZaz</p><p>/.,\\';][</p><p><img src=\"../../statics/TextImg/1579845198865_wz.jpg\" style=\"width: 804px;\"><br></p>","articleDateShow":1,"articleHits":0,"articleOneSort":1,"articlePublishDate":"2020-01-24 00:00:00.0","articleReward":0,"articleRole":1,"articleThreeSort":11,"articleTitle":"标题","articleTwoSort":4,"articleType":1,"articleWeight":1,"id":3,"userPhone":"17820423525"},{"articleCommentSum":0,"articleContent":"<p>AA</p>","articleDateShow":1,"articleHits":0,"articleOneSort":1,"articlePublishDate":"2020-01-24 00:00:00.0","articleReward":0,"articleRole":1,"articleThreeSort":12,"articleTitle":"标题","articleTwoSort":4,"articleType":1,"articleWeight":1,"id":4,"userPhone":"17820423525"},{"articleCommentSum":0,"articleContent":"<p>订单</p>","articleDateShow":1,"articleHits":0,"articleOneSort":1,"articlePublishDate":"2020-01-24 14:01:15.0","articleReward":0,"articleRole":1,"articleThreeSort":11,"articleTitle":"的","articleTwoSort":4,"articleType":2,"articleWeight":1,"id":5,"userPhone":"17820423525"}]}] as "application/json;charset=UTF-8" using [org.springframework.http.converter.StringHttpMessageConverter@3474a243]
    - (33019 ms) - 2020-1-30 13:20:11[DEBUG](DispatcherServlet.java:1034) Null ModelAndView returned to DispatcherServlet with name 'spring': assuming HandlerAdapter completed request handling
    - (33019 ms) - 2020-1-30 13:20:11[DEBUG](FrameworkServlet.java:1000) Successfully completed request

    后面发现

    //这样写是错误的 
    '<span class="tm">item.articleTitle</span>'+
    
    //这样也是错误的
     '<span class="tm">+item.articleTitle+</span>'+
    
    //这样才是对的
    '<span class="tm">'+item.articleTitle+'</span>'+

    点击加载更多,会刷新下一页的数据,没有数据就会这样显示

    原来想法是不用点,鼠标滚动更新,还有文章内容前面是有多个空格的,不知道为什么显示不了,后续再看看

     

     

    接下来是阅读全文,点击后能进入文章的详情,以及看到文章的评论,回复等内容

    静态页面写好了

     

     

    接下来就是评论和回复的功能了

    写数据表的时候忽然想起,不能用Integer,要用long,于是全改了

     

     

    commentReply_info 评论回复表

    字段名称

    类型

    是否主键

    是否为空

    字段描述

    commentId

    bigint(30)

    Y

    N

    评论id

    id

    bigint(30)

    N

    N

    文章id

    replyId

    bigint(30)

    N

    N

    回复id

    commentContent

    varchar(200)

    N

    N

    评论的内容

    commentTime

    varchar(25)

    N

    N

    评论时间

    commentUserName

    varchar(20)

    N

    N

    评论用户的昵称

    commentUserPhone

    varchar(20)

    N

    N

    评论用户的手机号

    commentUserPicPath

    varchar(500)

    N

    N

    评论用户的头像

    replyUserPhone

    varchar(20)

    N

    N

    回复用户的手机号

    replyUserName

    varchar(20)

    N

    N

    回复用户的昵称

    commentFabulous

    bigint(30)

    N

    N

    评论的点赞数量

    commentFlag

    enum

    N

    N

    评论的标识

     

     

    回复id:默认是0,代表是主评论

    回复id:拿的是一级评论的id

    回复人:是从一级评论拿的commentUserName(手机号也一样)

    评论时间:默认是0000-00-00 00:00:00

    评论用户的头像:默认是/statics/uploadfiles/tx.jpg

    回复用户的手机号:默认是空(当是主评论的时候没有回复者手机号)

    回复用户的昵称:默认是空(当是主评论的时候没有回复者昵称)

    评论的点赞数量:默认是0

    评论的标识:枚举类型(0,1),默认是0

    评论的标识:如果评论有点赞,控制层会将这个评论的标识从0改为1,但是不会去改数据库

    CREATE TABLE commentReply_info(
      commentId BIGINT(30) NOT NULL,
      id BIGINT(30) NOT NULL,
      replyId BIGINT(30) NOT NULL DEFAULT '0',
      commentContent VARCHAR(200) NOT NULL,
      commentTime VARCHAR(25) DEFAULT '0000-00-00 00:00:00',
      commentUserName VARCHAR(20) NOT NULL,
      commentUserPhone VARCHAR(20) NOT NULL,
      commentUserPicPath VARCHAR(500) DEFAULT '/statics/uploadfiles/tx.jpg',
      replyUserPhone VARCHAR(20) DEFAULT NULL,
      replyUserName VARCHAR(20) DEFAULT NULL,
      commentFabulous BIGINT(30) NOT NULL DEFAULT '0',
      commentFlag ENUM('0','1') NOT NULL DEFAULT '0',
      PRIMARY KEY (commentId)
    ) ENGINE=INNODB DEFAULT CHARSET=utf8;

     

    commentFabulous_info 评论点赞表

    字段名称

    类型

    是否主键

    是否为空

    字段描述

    Id

    bigint(30)

    Y

    N

    id

    commentId

    bigint(30)

    N

    N

    文章评论的id

    userPhone

    varchar(20)

    N

    N

    用户手机号

    CREATE TABLE commentFabulous_info(
      id BIGINT(30) NOT NULL AUTO_INCREMENT,
      commentId BIGINT(30) NOT NULL,
      userPhone VARCHAR(20) NOT NULL,
      PRIMARY KEY (id)
    ) ENGINE=INNODB DEFAULT CHARSET=utf8;

    代码写好后,没数据的样子

    数据库先插两条数据,效果:

     

    在做文章二级回复的时候出现了一个bug

    HandlerMethod details: 
    Controller [cn.controller.qt.ArticleController]
    Method [public java.lang.Object cn.controller.qt.ArticleController.getUserReply(java.lang.String,java.lang.String,java.lang.String,long,javax.servlet.http.HttpSession)]
    
    org.springframework.web.bind.MissingServletRequestParameterException: Required String parameter 'replyId' is not present
    @RequestParam(value = "replyId") String replyId, 

    我是这样写的,看报错是没有数据过来,去页面改一下就行了,字段写错了

     

    写完后简单测试了一下,发现一级评论可以正确发布,但是回复(二级评论)会报错(空指针异常),但是我如果是先发了一级评论,然后刷新,再回复, 是没问题的,同时偶尔出现一个问题,就是回复发布后,显示两条同样的回复,但是我刷新后又能正确显示了。

    经过一系列的调试,发现了问题,就是我在对回复进行回复的时候,会发现,我打印出来的id和这条回复在数据库的id不对,我拿了一个不存在的id去查肯定报空指针,但是我在刷新后,点哪个回复,居然发现打印正确,所以刷新后就能正确回复,但是这样明显不合理,代码如下:

    <!--点击回复动态创建回复块-->
    <!-- 创建回复框-->
    <script type="text/javascript">
        var firstCommentId;
        $('.comment-show').on('click','.pl-hf',function(){
            // 获取一级评论的commentId
            firstCommentId = $(this).parents('.date-dz-right').parents('.date-dz').siblings('.pl-text').find($("input[name='commentId']")).val();
    //
    //
    //问题出现在这里,对回复再回复打印的id是错误的,很奇怪
    //
    //
            alert("一级id:"+firstCommentId)
            //获取回复人的名字
            var fhName = $(this).parents('.date-dz-right').parents('.date-dz').siblings('.pl-text').find('.comment-size-name').html();
            //回复@
            var fhN = '回复@'+fhName;
            //var oInput = $(this).parents('.date-dz-right').parents('.date-dz').siblings('.hf-con');
            var fhHtml = '<div class="hf-con pull-left"> <textarea class="content comment-input hf-input" placeholder="" onkeyup="keyUP(this)"></textarea> <a href="javascript:;" class="hf-pl">评论</a></div>';
            // remove()方法移除被选元素,包括所有的文本和子节点,也会移除被选元素的数据和事件
            // 当点击了其他回复后,原来的回复回复初始状态
            // pl-hf没有样式(回复按钮的class),hf-con是点击回复弹出的文本框的div的class
            $('.pl-hf').addClass('hf-con-block') && $('.hf-con').remove();
            //显示回复
            if($(this).is('.hf-con-block')){
                // 在被选元素的结尾(仍然在内部)插入fhHtml的内容
                $(this).parents('.date-dz-right').parents('.date-dz').append(fhHtml);
                $(this).removeClass('hf-con-block');
                $('.content').flexText();
                $(this).parents('.date-dz-right').siblings('.hf-con').find('.pre').css('padding','6px 15px');
            }else {
                $(this).addClass('hf-con-block');
                $(this).parents('.date-dz-right').siblings('.hf-con').remove();
            }
        });
    </script>
    <!--评论回复块创建-->
    <!--评论回复提交-->
    <script type="text/javascript">
        $('.comment-show').on('click','.hf-pl',function(){
            var oThis = $(this);
            var myDate = new Date();
            //获取当前年
            var year=myDate.getFullYear();
            //获取当前月
            var month=myDate.getMonth()+1;
            //获取当前日
            var date=myDate.getDate();
            var h=myDate.getHours();       //获取当前小时数(0-23)
            var m=myDate.getMinutes();     //获取当前分钟数(0-59)
            if(m<10) m = '0' + m;
            var s=myDate.getSeconds();
            if(s<10) s = '0' + s;
            var now=year+'-'+month+"-"+date+" "+h+':'+m+":"+s;
            //获取输入内容
            var oHfVal = $(this).siblings('.flex-text-wrap').find('.hf-input').val();
            console.log(oHfVal)
            var oHfName = $(this).parents('.hf-con').parents('.date-dz').siblings('.pl-text').find('.comment-size-name').html();
            if(oHfVal.replace(/^ +| +$/g,'') == ''){
                oThis.parents('.hf-con').siblings('.date-dz-right').find('.pl-hf').addClass('hf-con-block') && oThis.parents('.hf-con').remove();
            }else {
                $.ajax({
                    type:'POST',
                    url : '/qt/getUserReply',
                    data:{commentContent:oHfVal,commentTime:now,replyId:firstCommentId,id:${requestScope.articleInfo.id}},
                    async:false,
                    success : function(data) {
                        if(data.flag){
                            var oHtml =
                                '<div class="all-pl-con">' +
                                '<div class="pl-text hfpl-text clearfix">' +
                                '<input type="hidden" name="commentId" value="'+data.commentReplyInfo.commentId+'">' +
                                '<a href="#" class="comment-size-name">'+data.commentReplyInfo.commentUserName+' : </a>' +
                                '<span class="my-pl-con">回复' +
                                '<a href="#" class="atName">@'+data.commentReplyInfo.replyUserName+'</a>&nbsp;'
                                + data.commentReplyInfo.commentContent+'</span></div>' +
                                '<div class="date-dz"> ' +
                                '<span class="date-dz-left pull-left comment-time">'+data.commentReplyInfo.commentTime+'</span>' +
                                '<div class="date-dz-right pull-right comment-pl-block">' +
                                '<a href="javascript:;" class="removeBlock">删除</a>' +
                                '<a href="javascript:;" class="date-dz-pl pl-hf hf-con-block pull-left">回复</a>' +
                                '<span class="pull-left date-dz-line">|</span>' +
                                '<a href="javascript:;" class="date-dz-z pull-left">' +
                                '<i class="date-dz-z-click-red"></i>赞 (<i class="z-num">0</i>)</a> ' +
                                '</div> </div></div>';
                            oThis.parents('.hf-con').parents('.comment-show-con-list').find('.hf-list-con').css('display','block').prepend(oHtml) && oThis.parents('.hf-con').siblings('.date-dz-right').find('.pl-hf').addClass('hf-con-block') && oThis.parents('.hf-con').remove();
                        }else {
                            alert("亲,请登录后再来评论!")
                        }
                    },
                    error:function(){
                        alert("网络异常,回复失败");
                    }
                });
            }
    
    
        });
    </script>
        //@人的回复评论(二级评论)
        @RequestMapping(value = "getUserReply")
        @ResponseBody
        public Object getUserReply(@RequestParam(value = "commentContent") String commentContent,//评论内容
                                      @RequestParam(value = "commentTime") String commentTime,//评论时间
                                      @RequestParam(value = "replyId") String replyId,           //一级评论id
                                      @RequestParam(value = "id") long id,//文章id
                                      HttpSession session) {
            UserInfo userInfo = (UserInfo) session.getAttribute(Constants.QT_USER_SESSION);
    //        一级评论的id
            long rid = Long.parseLong(replyId);
    //        由数据中心 ID 和机器 ID 作区分
            IdGenerator idWorker = new IdGenerator(0, 0);
    //        回复一级评论的id
            long commentId = idWorker.nextId();
            CommentReplyInfo commentReplyInfo1 = new CommentReplyInfo();
            Map<String, Object> map = new HashMap<>();
            if(session.getAttribute(Constants.QT_USER_SESSION)==null||session.getAttribute(Constants.QT_USER_SESSION)==""){
                map.put("flag",false);
            }else {
                try {
    //                查询要回复的那个一级评论的手机号和用户昵称
                    commentReplyInfo1 = articleInfoService.getCommentReplyInfo1(rid);        
                } catch (Exception e) {
                    e.printStackTrace();
                }
                CommentReplyInfo commentReplyInfo = new CommentReplyInfo(commentId,id,rid,commentContent,commentTime,userInfo.getUserName(),userInfo.getUserPhone(),userInfo.getUserPicPath(),commentReplyInfo1.getCommentUserPhone(),commentReplyInfo1.getCommentUserName(),0,"0");
                boolean flag = false;
                try {
                    flag = articleInfoService.addCommentReplyInfo2(commentReplyInfo);
                } catch (Exception e) {
                    e.printStackTrace();
                }
                    map.put("flag",true);
                    map.put("commentReplyInfo", commentReplyInfo);
    
            }
            return JSONObject.toJSONString(map);
        }

    后面我发现我应该是用了这个作为评论的id的原因(IdGenerator类),具体是什么原因我不知道,后续再看看,暂时使用这个

    long commentId = System.currentTimeMillis();(这个不合理,先暂时用着)
    package cn.util;
    
    /**
     * 用于生成唯一 ID
     * 关于如何在系统中生成唯一性 ID 的问题(如订单号、批次号等),一直困扰了许久。因为还要考虑并发的问题,所以时间戳 + 随机数的组合并不可取,Java 中的 UUID 是一种可取的方法,但它的缺点是序列号太长了,而且没有可读性,对用户来说这么一堆乱码是极不友好的。
       推特的工程师 snowflake 也提出了一个在分布式系统中生成唯一序列的方法。SnowFlake 的优点是,效率高,整体上按照时间自增排序,提高了序列号的可读性,对用户来说也比较友好,并且整个分布式系统内不会产生 ID 碰撞(由数据中心 ID 和机器 ID 作区分)。
     2.SnowFlake 算法的 Java 实现
     * Twitter_Snowflake
     * SnowFlake 的结构如下(每部分用-分开):
     * 0 - 0000000000 0000000000 0000000000 0000000000 0 - 00000 - 00000 - 000000000000
     * 1 位标识,由于 long 基本类型在 Java 中是带符号的,最高位是符号位,正数是 0,负数是 1,所以 id 一般是正数,最高位是 0
     * 41 位时间戳(毫秒级),注意,41 位时间戳不是存储当前时间的时间戳,而是存储时间戳的差值(当前时间戳 - 开始时间戳)
     * 得到的值),这里的的开始时间戳,一般是我们的 id 生成器开始使用的时间,由我们程序来指定的(如下面程序 SnowflakeIdWorker 类的 startTime 属性)。41 位的时间戳,可以使用 69 年,年 T = (1L << 41) / (1000L * 60 * 60 * 24 * 365) = 69
     * 10 位的数据机器位,可以部署在 1024 个节点,包括 5 位 datacenterId 和 5 位 workerId
     * 12 位序列,毫秒内的计数,12 位的计数顺序号支持每个节点每毫秒(同一机器,同一时间戳)产生 4096 个 ID 序号
     * 加起来刚好 64 位,为一个 Long 型。
     */
    public class IdGenerator {
        /* 开始时间戳 (2018-09-01) */
        private final long twepoch = 1535731200L;
        /**
         * 机器id所占的位数
         */
        private final long workerIdBits = 5L;
    
        /**
         * 数据标识id所占的位数
         */
        private final long datacenterIdBits = 5L;
    
        /**
         * 支持的最大机器id,结果是31 (这个移位算法可以很快的计算出几位二进制数所能表示的最大十进制数)
         */
        private final long maxWorkerId = -1L ^ (-1L << workerIdBits);
    
        /**
         * 支持的最大数据标识id,结果是31
         */
        private final long maxDatacenterId = -1L ^ (-1L << datacenterIdBits);
    
        /**
         * 序列在id中占的位数
         */
        private final long sequenceBits = 12L;
    
        /**
         * 机器ID向左移12位
         */
        private final long workerIdShift = sequenceBits;
    
        /**
         * 数据标识id向左移17位(12+5)
         */
        private final long datacenterIdShift = sequenceBits + workerIdBits;
    
        /**
         * 时间戳向左移22位(5+5+12)
         */
        private final long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;
    
        /**
         * 生成序列的掩码,这里为4095 (0b111111111111=0xfff=4095)
         */
        private final long sequenceMask = -1L ^ (-1L << sequenceBits);
    
        /**
         * 工作机器ID(0~31)
         */
        private long workerId;
    
        /**
         * 数据中心ID(0~31)
         */
        private long datacenterId;
    
        /**
         * 毫秒内序列(0~4095)
         */
        private long sequence = 0L;
    
        /**
         * 上次生成ID的时间戳
         */
        private long lastTimestamp = -1L;
    
    //==============================Constructors=====================================
    
        /**
         * 构造函数
         *
         * @param workerId     工作ID (0~31)
         * @param datacenterId 数据中心ID (0~31)
         */
        public IdGenerator(long workerId, long datacenterId) {
            if (workerId > maxWorkerId || workerId < 0) {
                throw new IllegalArgumentException(String.format("worker Id can't be greater than %d or less than 0", maxWorkerId));
            }
            if (datacenterId > maxDatacenterId || datacenterId < 0) {
                throw new IllegalArgumentException(String.format("datacenter Id can't be greater than %d or less than 0", maxDatacenterId));
            }
            this.workerId = workerId;
            this.datacenterId = datacenterId;
        }
    
    // ==============================Methods==========================================
    
        /**
         * 获得下一个ID (该方法是线程安全的)
         *
         * @return SnowflakeId
         */
        public synchronized long nextId() {
            long timestamp = timeGen();
    
            //如果当前时间小于上一次ID生成的时间戳,说明系统时钟回退过这个时候应当抛出异常
            if (timestamp < lastTimestamp) {
                throw new RuntimeException(
                        String.format("Clock moved backwards.  Refusing to generate id for %d milliseconds", lastTimestamp - timestamp));
            }
    
            //如果是同一时间生成的,则进行毫秒内序列
            if (lastTimestamp == timestamp) {
                sequence = (sequence + 1) & sequenceMask;
                //毫秒内序列溢出
                if (sequence == 0) {
                    //阻塞到下一个毫秒,获得新的时间戳
                    timestamp = tilNextMillis(lastTimestamp);
                }
            }
            //时间戳改变,毫秒内序列重置
            else {
                sequence = 0L;
            }
    
            //上次生成ID的时间戳
            lastTimestamp = timestamp;
    
            //移位并通过或运算拼到一起组成64位的ID
            return ((timestamp - twepoch) << timestampLeftShift) //
                    | (datacenterId << datacenterIdShift) //
                    | (workerId << workerIdShift) //
                    | sequence;
        }
    
        /**
         * 阻塞到下一个毫秒,直到获得新的时间戳
         *
         * @param lastTimestamp 上次生成ID的时间戳
         * @return 当前时间戳
         */
        protected long tilNextMillis(long lastTimestamp) {
            long timestamp = timeGen();
            while (timestamp <= lastTimestamp) {
                timestamp = timeGen();
            }
            return timestamp;
        }
    
        /**
         * 返回以毫秒为单位的当前时间
         *
         * @return 当前时间(毫秒)
         */
        protected long timeGen() {
            return System.currentTimeMillis();
        }
    
        /**
         * 测试
         */
    //    public static void main(String[] args) {
    //        IdGenerator idWorker = new IdGenerator(0, 0);
    //        long id = idWorker.nextId();
    //        System.out.println(id);
    //    }
    }
    

     

    接下来是评论的删除功能

    写完后发现当我删除一级评论时候,回复的回复删除不了;当我删除回复的时候,回复的回复也删除不了

    在页面效果没问题,但是在数据库有问题,后续再看

        //删除评论
        @RequestMapping(value = "deleteCommentById")
        @ResponseBody
        public Object deleteCommentById(@RequestParam(value = "commentId") String commentId,
                                    HttpSession session) {
            long cId = Long.parseLong(commentId);
            boolean flag = false;
    //        通过文章id查询评论的信息
            CommentReplyInfo commentReplyInfo = null;
            try {
                commentReplyInfo = articleInfoService.getCommentReplyInfo1(cId);
            } catch (Exception e) {
                e.printStackTrace();
            }
    //      等于0删除整块(一级和二级),否则就删除回复的评论(包括再回复),或者是再回复的评论
            if (commentReplyInfo.getReplyId()==0){
                try {
    //                删除文章一级评论
                    flag = articleInfoService.deleteCommentById1(cId);
                } catch (Exception e) {
                    e.printStackTrace();
                }
                try {
    //          删除这条评论的全部点赞(在这里是删除一级评论的全部点赞)    
                    flag = articleInfoService.deleteCommentFabulousByCommentId(cId);
                } catch (Exception e) {
                    e.printStackTrace();
                }
                try {
    //          暂时
    //          拿到一级评论的文章id,对这篇文章的评论总数减1
                    flag = articleInfoService.updateArticleCommentSum1(commentReplyInfo.getId());
                } catch (Exception e) {
                    e.printStackTrace();
                }
                try {
    //          拿到一级评论的id,然后作为回复id找到这条一级评论的全部回复,然后删除
    //          问题是:回复的回复删除不了   
                    flag =articleInfoService.deleteCommentByReplyId(commentReplyInfo.getCommentId());
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }else {
                try {
    //          不是0说明是回复,直接根据拿到的评论id删除这个评论就行了
                    flag = articleInfoService.deleteCommentById1(cId);
                } catch (Exception e) {
                    e.printStackTrace();
                }
                try {
    //            删除回复的全部点赞    
                    flag = articleInfoService.deleteCommentFabulousByCommentId(cId);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            return null;
        }

     

    接下来是评论的点赞,取消点赞

    <!--点赞-->
    <script type="text/javascript">
        $('.comment-show').on('click','.date-dz-z',function(){
            var zNum = $(this).find('.z-num').html();
    
            var isThis = $(this);
            // 拿到要点赞的这条评论的id
            var commentId = $(this).parents('.date-dz-right').parents('.date-dz').siblings('.pl-text').find($("input[name='commentId']")).val();
            if($(this).is('.date-dz-z-click')){//取消点赞
                zNum--;
                $.ajax({
                    type:'POST',
                    url : '/qt/disLikeCommentId',
                    data:{commentId:commentId},
                    async:false,
                    success : function(data) {
                        if(data.flag){
                            $(isThis).removeClass('date-dz-z-click red');
                            $(isThis).find('.z-num').html(zNum);
                            $(isThis).find('.date-dz-z-click-red').removeClass('red');
                        }else {
                            alert("亲,请登陆后再来点赞!")
                        }
    
                    },
                    error:function(){
                        alert("网络异常,取消点赞失败");
                    }
                });
            }else {//点赞
                zNum++;
                $.ajax({
                    type:'POST',
                    url : '/qt/likeCommentId',
                    data:{commentId:commentId},
                    async:false,
                    success : function(data) {
                        if(data.flag) {
                            $(isThis).addClass('date-dz-z-click');
                            $(isThis).find('.z-num').html(zNum);
                            $(isThis).find('.date-dz-z-click-red').addClass('red');
                        }else {
                            alert("亲,请登陆后再来点赞!")
                        }
                    },
                    error:function(){
                        alert("网络异常,点赞失败");
                    }
                });
            }
        })
    </script>

    至此,基本就出来了,其中一些bug后续有空再回来解决了

     

     

     

     

    Day 2/8

    回复功能:点击回复获取的这条评论的id是错误的

    //            long commentId = System.currentTimeMillis();

    我使用上面的方式去写id,功能就没有问题,但是不合理,因为可能会出现两个人在同一个时间评论,这样id就冲突了

    于是我找了个能生成uuid的类:https://blog.csdn.net/rhx_1989/article/details/82784991

    但是出现了像前面一系列的问题

    测试了很久,终于找到了回复那一系列的问题原因,因为我使用的long类型,数字太长了,在js获取丢失了精度

    解决方法:https://blog.csdn.net/weixin_42717928/article/details/104256759

    控制层这样就行了

    //            由数据中心 ID 和机器 ID 作区分
                IdGenerator idWorker = new IdGenerator(0, 0);
                String commentId = String.valueOf(idWorker.nextId());

     

     

    然后接下来把主页的头像区分一下,没有登录就进入主页的为游客,显示游客头像,登录过的人显示该用户头像

    <c:choose>
       <c:when test="${empty sessionScope.qtUserInfoSession.userPhone}">
           <img src="${pageContext.request.contextPath }/statics/img/youke.jpg" width="35px" height="35px" class="dropdown-toggle img-responsive img-circle" id="dropdownMenu1" data-toggle="dropdown" style="padding-top: 10px;"/>
       </c:when>
       <c:otherwise>
           <img src="${pageContext.request.contextPath }${sessionScope.qtUserInfoSession.userPicPath}" width="35px" height="35px" class="dropdown-toggle img-responsive img-circle" id="dropdownMenu1" data-toggle="dropdown" style="padding-top: 10px;"/>
       </c:otherwise>
    </c:choose>

    然后退出登录加个弹窗二次提示

    <a role="menuitem" tabindex="-1" href="logout" onclick="return confirm('亲,是否退出登录?')">退出登录</a>

    直接加个判断是否显示管理员入口

    然后是个人资料页面

     

     

     

     

    Day 2/9

    主页加些判断,第一部分就做好了

     

    然后第二部分

    这里显示我关注的用户当天发的文章(最新的),最大限制数10条

    待定:想实现类似这样的功能的,已读的不显示

     

    临时加了个表

    follow_info 关注表

    字段名称

    类型

    是否主键

    是否为空

    字段描述

    Id

    varchar(30)

    Y

    N

    id

    userPhone

    varchar(20)

    N

    N

    用户手机号

    followUserPhone

    varchar(20)

    N

    N

    关注的那个用户的手机号

    followTime

    Datetime

    N

    N

    关注时间

    CREATE TABLE follow_info(
      id VARCHAR(30) NOT NULL,
      userPhone VARCHAR(20) NOT NULL,
      followUserPhone VARCHAR(20) NOT NULL,
      followTime DATETIME NOT NULL,
      PRIMARY KEY (id)
    ) ENGINE=INNODB DEFAULT CHARSET=utf8;

    要根据一堆关注用户去查他们的文章,可以使用批量查询

    //关注的用户
    //这里使用String的原因是,这样我得到的对象可以放在List<String>定义的参数里面
            List<String> followInfos = null;
    //关注用户的文章
            List<ArticleInfo> articleInfos = null;
    //报了一个bug
    org.apache.ibatis.binding.BindingException: Parameter 'list' not found. Available parameters are [endStr, startStr, userPhone, param3, param1, param2]

    部分代码如下: 

    //              GregorianCalendar是Calendar的子类
                    Calendar calendar = new GregorianCalendar();
    //              -1是一天前,1是后一天
    //              指示一个月中的某天
                    calendar.add(Calendar.DAY_OF_MONTH,0);
    
    //一天的开始时间 yyyy:MM:dd 00:00:00
    //              指示一天中的小时
                    calendar.set(Calendar.HOUR_OF_DAY,0);
    //              指示一小时中的分钟
                    calendar.set(Calendar.MINUTE,0);
    //              指示一分钟中的秒
                    calendar.set(Calendar.SECOND,0);
    //              指示一秒中的毫秒
                    calendar.set(Calendar.MILLISECOND,0);
    //              得到的是这种类型的日期:Tue Feb 11 00:00:00 CST 2020
                    Date dayStart = calendar.getTime();
                    SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                    String startStr = simpleDateFormat.format(dayStart);
    
    //一天的结束时间 yyyy:MM:dd 23:59:59
                    calendar.set(Calendar.HOUR_OF_DAY,23);
                    calendar.set(Calendar.MINUTE,59);
                    calendar.set(Calendar.SECOND,59);
    //              1秒=1000毫秒
                    calendar.set(Calendar.MILLISECOND,999);
                    Date dayEnd = calendar.getTime();
                    String endStr = simpleDateFormat.format(dayEnd);
    
                    articleInfos = qtUserInfoService.batchGetFollowingsArticleInfos(startStr,endStr,followInfos);
    public List<ArticleInfo> batchGetFollowingsArticleInfos(String startStr,String endStr,List<String> userPhones) throws Exception;
    
    
    
    @Override
    public List<ArticleInfo> batchGetFollowingsArticleInfos(String startStr,String endStr,List<String> userPhones) throws Exception{
            return qtUserInfoMapper.batchGetFollowingsArticleInfos(startStr,endStr,userPhones);
        }
    
    
    
    public List<ArticleInfo> batchGetFollowingsArticleInfos(@Param(value = "startStr") String startStr,@Param(value = "endStr") String endStr,@Param(value = "userPhone") List<String> userPhones) throws Exception;
    
    
    
    <select id="batchGetFollowingsArticleInfos" resultType="ArticleInfo">
            select *from article_info where userPhone in
            <foreach collection="list" item="userPhone" index="no" open="(" separator="," close=")">
                #{userPhone}
            </foreach>
            and articlePublishDate between #{startStr} and #{endStr} order by articlePublishDate desc limit 10
    </select>
    
    
    
    

    经过调试我发现如果只是一个参数,刚刚开始我是只有List<String> userPhones一个参数,我使用list没问题

    但是我加入另外两个参数,程序就出错了,后面改为collection="userPhone",问题解决

     

    接下来,当用户关注的人当天没有发文章,会有一个默认的文案

     

    原来是这样写的

    request.setAttribute("articleInfosError","亲,关注的朋友今天没有发文章哦");

    然后一堆乱码,不知道什么原因,试了很多解决方法都不行

    最后只能这样写了

    String articleInfosError = "亲,关注的朋友今天没有发文章哦";
    request.setAttribute("articleInfosError",articleInfosError);

    随便插了些数据,这部分就做好了

     

     

     

    然后第三部分:热门悬赏榜单

    我自己定了些规则

    热度悬赏榜单:
    规则1:前提是悬赏文章
    规则2:每2小时更新一次,待定
    规则3:
    (1)金额1-50(+1)51-100(+2)101-500(+3)501-1000(+4)
    (2)浏览量100-500(+1)501-10000(+2),10001-100000(+3),100001>(+4)
    (3)文章权重:1>
    (4)互动量(评论数量):0-500(+1)501-10000(+2),10001-100000(+3),100001>(+4)
    (5)回答质量:至少有一个评论500赞以上
    规则4:
    加一个字段是0,后台可以调整为1,则不显示在热度榜单,以达到管控
    文章权重:文章都是1,这个是后台进行管控(买热度可以加权重)
    

    实现也是用了很笨的方法

    功能是实现了,但是代码写的很烂。。。

     

     

     

    接下来是第四部分:全部文章内容的显示

    然后写完了发现了个bug,文章,视频或者图片过多直接顶下去了,本来的想法是只能显示这么多,多余的省略

    .flow_top_center_center{
    	font-size: 18px;
    	overflow: hidden;
    	line-height:25px;
    	width: 100%;
    	height:80%;	
    }

     这就可以把超过div的内容隐藏掉,效果成下面图片那样

     

    .flow_top_center_center>a{
    	display: -webkit-box;
    	-webkit-box-orient: vertical;
    	-webkit-line-clamp: 9;
    	overflow: hidden;
    	height: auto;
    	cursor: pointer;
    }
    
    //这样的话这部分的样式好像就部分实现不了了,后续再看看

    还有这样就不是很好看,预期是多余的空白能挤压掉,后续再看看

     

    还有这个本想做一个判断的,不知道为什么拿不到值,于是普通文章也这样显示了,只不过悬赏金额为0

     

    然后我把文章的这个字段改为了varchar类型,作用是判断文章是以今天,昨天,还是日期来显示

    主要是通过这个类:参考:https://www.xuebuyuan.com/2006093.html

    package cn.util;
    
    /**
     * Created by 漫步云端 on 2020/2/13.
     */
    import java.text.ParseException;
    import java.text.SimpleDateFormat;
    import java.util.Calendar;
    import java.util.Date;
    
    public class CalendarTest {
    
        /**
         * @param args
         */
    //    public static void main(String[] args) {
    //
            String time = formatDateTime("2020-02-13 15:41:15");
            System.out.println("time:"+time);
            time = formatDateTime("2020-02-12 15:45");
            System.out.println("time:"+time);
            time = formatDateTime("2013-08-11 15:43");
            System.out.println("time:"+time);
    //    }
    
        /**
         * 格式化时间
         * @param time
         * @return
         */
        public String formatDateTime(String time) {
            SimpleDateFormat format = new java.text.SimpleDateFormat("yyyy-MM-dd HH:mm");
            System.out.println("format"+format);
            if(time==null ||"".equals(time)){
                return "";
            }
            Date date = null;
            try {
    //            parse表示将字符串转化为时间:Thu Feb 13 15:41:00 CST 2020
                date = format.parse(time);
            } catch (ParseException e) {
                e.printStackTrace();
            }
    
    //        现在的日期
            Calendar current = Calendar.getInstance();
    
            Calendar today = Calendar.getInstance();	//今天
    
            today.set(Calendar.YEAR, current.get(Calendar.YEAR));
            today.set(Calendar.MONTH, current.get(Calendar.MONTH));
            today.set(Calendar.DAY_OF_MONTH,current.get(Calendar.DAY_OF_MONTH));
            //  Calendar.HOUR——12小时制的小时数 Calendar.HOUR_OF_DAY——24小时制的小时数
            today.set( Calendar.HOUR_OF_DAY, 0);
            today.set( Calendar.MINUTE, 0);
            today.set(Calendar.SECOND, 0);
    
            Calendar yesterday = Calendar.getInstance();	//昨天
    
            yesterday.set(Calendar.YEAR, current.get(Calendar.YEAR));
            yesterday.set(Calendar.MONTH, current.get(Calendar.MONTH));
            yesterday.set(Calendar.DAY_OF_MONTH,current.get(Calendar.DAY_OF_MONTH)-1);
            yesterday.set( Calendar.HOUR_OF_DAY, 0);
            yesterday.set( Calendar.MINUTE, 0);
            yesterday.set(Calendar.SECOND, 0);
    
    //        setTime()方法以毫秒设置Date对象
            current.setTime(date);
    
    
    //        这里有个问题,超过今天都是今天,不过因为文章的发表不会超过当天,所以这里使用没问题
            if(current.after(today)){
    //            以空格分隔时间,取索引为1的时间
                return "今天 "+time.split(" ")[1];
            }else if(current.before(today) && current.after(yesterday)){
    
                return "昨天 "+time.split(" ")[1];
            }else{
    //            返回指定字符在字符串中第一次出现处的索引,这里是4
                int index = time.indexOf("-")+1;
    //            截取除了年的其他部分
                return time.substring(index, time.length());
            }
        }
    
    }
    
    

    也不用插入数据库,仅仅是一个过渡来显示而已

    //用来判断文章是否以今天,昨天还是日期来显示
    CalendarTest calendarTest = new CalendarTest();
    articleInfo1.setArticleDateShow(calendarTest.formatDateTime(articleInfo1.getArticlePublishDate()));

    效果就这样

     

     

    点击文章内容可以进入文章的详情

    '<a href="/qt/articleDetails?id='+item.id+'&&articleUserPhone='+item.userPhone+'&&userPhone=${sessionScope.qtUserInfoSession.userPhone}" style="text-decoration: none;">'+item.articleContent+'</a>'+

    在这里的话我想点了别人的文章进入详情,看的是别人的个人空间,而不是自己的,于是我加了两个参数,文章手机号和登录用户的手机号,如果一致,说明这个文章是这个用户发的,就进入该用户的个人中心,如果不一致,说明此文章不是该用户发的,就进入此文章作者的个人中心。

     

    因为同时也是进入了这篇文章的详情,浏览量要+1,但是这个要怎么实现呢,感觉有难度。。。

    百度看到一种简单的,放入session中的缺点是如果关闭浏览器再次打开该文章,还是会+1. 总比每次加1要好

    //可以在做一个标识放到session中,例如session.setAttribute("Flag","true") 
    String flag = (String)session.getAttribue("Flag"); 
    if(null!=flag&&"true".equals(flag)){ 
        break; 
    }else{ 
    //你更新的操作 
    session.setAttribute("Flag","true") 
    }
    
    //这样好像可以放一个map对象(用户手机号,文章id)

    但是不是我想要的,这个浏览量先暂时不做,后续再看看

     

    接下来是点击了自己的文章进入自己的个人中心,文章详情是这样

     

    然后回到这里,我发了三条评论,显示没问题,但是一刷新第三条评论显示不了了

    数据库数据插入成功,控制层也拿到数据,测试后发现三级以后的回复都显示不了了,心累

    控制台一堆乱码,解决方法:https://blog.csdn.net/m0_37893932/article/details/78280663(我用了第二个)

    经过调试后发现初始的显示状态只能显示一级和二级评论,三级以后的显示不了,但是直接评论能显示,只要不刷新

    然后发现评论总数也有问题,假如一级评论下有二级评论2条,那我删除一级评论,评论总数只是减一,应该是减3

    这个bug有点难搞,后续再调

     

    接下里是热门文章,就只显示浏览量最高的五篇文章而已

    然后这几部分暂时不做

     

     

     

     

    Day 20

    resource_info 资源表

    字段名称

    类型

    是否主键

    是否为空

    字段描述

    Id

    bigInt(30)

    Y

    N

    id

    resourceName

    varchar(30)

    N

    N

    资源名字

    resourcePicPath

    varchar(500)

    N

    N

    资源图片虚拟路径

    resourceDetails

    varchar(500)

    N

    N

    资源介绍

    resourceValue

    int

    N

    N

    资源的价格

    resourceUserName

    Varchar(20)

    N

    N

    资源上传者昵称

    resourcePublishDate

    datetime

    N

    N

    资源的发布时间

    resourceDownloadLink

    Varchar(500)

    N

    N

    资源的下载链接(虚拟路径)

    resourceLocPath

    Varchar(500)

    N

    N

    资源的真实下载路径

    resourceStatus

    int

    N

    N

    资源的状态

    resourceUserPhone

    Varchar(20)

    N

    N

    资源上传者手机号

    resourceOneSort

    int

    N

    N

    资源的一级分类

    resourceTwoSort

    int

    N

    Y

    资源的二级分类

    resourceThreeSort

    int

    N

    Y

    资源的三级分类

    resourceDownloadCount

    bigInt(30)

    N

    N

    资源的下载量(以第一次为准)

    资源的状态:1:待审核;2:审核不通过;3:审核通过;

    资源下载量:默认是0

     

    CREATE TABLE resource_info(
      id BIGINT(30) NOT NULL AUTO_INCREMENT,
      resourceName VARCHAR(30) NOT NULL,
      resourcePicPath VARCHAR(500) NOT NULL,
      resourceDetails VARCHAR(500) NOT NULL,
      resourceValue INT NOT NULL,
      resourceUserName VARCHAR(20) NOT NULL,
      resourcePublishDate DATETIME NOT NULL,
      resourceDownloadLink VARCHAR(500) NOT NULL,
      resourceLocPath VARCHAR(500) NOT NULL,
      resourceStatus INT NOT NULL,
      resourceUserPhone VARCHAR(20) NOT NULL,
      resourceOneSort INT NOT NULL,
      resourceTwoSort INT NOT NULL,
      resourceThreeSort INT NOT NULL,
      PRIMARY KEY (id)
    ) ENGINE=INNODB DEFAULT CHARSET=utf8;

    写了个很丑的页面

    - (87831 ms) - 2020-2-20 23:02:26[DEBUG](InvocableHandlerMethod.java:168) Error resolving argument [0] [type=cn.pojo.ResourceDownload]
    HandlerMethod details: 
    Controller [cn.controller.qt.ResourceDownloadController]
    Method [public java.lang.String cn.controller.qt.ResourceDownloadController.UploadResources(cn.pojo.ResourceDownload,javax.servlet.http.HttpSession,javax.servlet.http.HttpServletRequest,org.springframework.web.multipart.MultipartFile)]
    
    org.springframework.validation.BindException: org.springframework.validation.BeanPropertyBindingResult: 1 errors
    Field error in object 'resourceDownload' on field 'resourceDownloadLink': rejected value [org.springframework.web.multipart.commons.CommonsMultipartFile@2dbf939f]; codes [typeMismatch.resourceDownload.resourceDownloadLink,typeMismatch.resourceDownloadLink,typeMismatch.java.lang.String,typeMismatch]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [resourceDownload.resourceDownloadLink,resourceDownloadLink]; arguments []; default message [resourceDownloadLink]]; default message [Failed to convert property value of type 'org.springframework.web.multipart.commons.CommonsMultipartFile' to required type 'java.lang.String' for property 'resourceDownloadLink'; nested exception is java.lang.IllegalStateException: Cannot convert value of type [org.springframework.web.multipart.commons.CommonsMultipartFile] to required type [java.lang.String] for property 'resourceDownloadLink': no matching editors or conversion strategy found]
    

    报了个bug,把name的名字改一下就行了

     

     

    我的二级和三级分类是允许为空的,于是我上传资源三级分类为空,报了个bug

    - (46630 ms) - 2020-2-20 23:47:59[DEBUG](InvocableHandlerMethod.java:168) Error resolving argument [0] [type=cn.pojo.ResourceDownload]
    HandlerMethod details: 
    Controller [cn.controller.qt.ResourceDownloadController]
    Method [public java.lang.String cn.controller.qt.ResourceDownloadController.UploadResources(cn.pojo.ResourceDownload,javax.servlet.http.HttpSession,javax.servlet.http.HttpServletRequest,org.springframework.web.multipart.MultipartFile)]
    
    org.springframework.validation.BindException: org.springframework.validation.BeanPropertyBindingResult: 1 errors
    Field error in object 'resourceDownload' on field 'resourceThreeSort': rejected value []; codes [typeMismatch.resourceDownload.resourceThreeSort,typeMismatch.resourceThreeSort,typeMismatch.int,typeMismatch]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [resourceDownload.resourceThreeSort,resourceThreeSort]; arguments []; default message [resourceThreeSort]]; default message [Failed to convert property value of type 'java.lang.String' to required type 'int' for property 'resourceThreeSort'; nested exception is java.lang.NumberFormatException: For input string: ""]
    

    百度了一下:

    出现Field error in object ‘xxx’ on field ‘XXX’: rejected value [];错误
    在ssm框架中,前端没有没有传值或者搜索条件为空时,传值时无法转换为int,会导致加载controller之前就报错,故应把属性值得类型改为Integer,即可接收到null。

    于是我查看了我的pojo的类,发现我定义的是int,哎,短路了,改为Integer就行了

     

    上传资源的代码

        @RequestMapping(value="/UploadResources",method= RequestMethod.POST)
        public String UploadResources(ResourceDownload resourceDownload, HttpSession session, HttpServletRequest request,
                                      @RequestParam(value="a_downloadLink",required= false) MultipartFile attach ){
            String downloadLink =  null;   //下载链接
            String resourceLocPath = null;     //资源的服务器存储路径
            String resourceFileName = null;     //上传的资源文件名称
            String resourcePicPath = null;       //资源图片的虚拟路径
    
            if (session.getAttribute(Constants.QT_USER_SESSION)==null||session.getAttribute(Constants.QT_USER_SESSION)==""){
    //            没有登录跳到登录页面
                return "redirect:/qt/login";
            }
    
            if(!attach.isEmpty()){
                String path = request.getSession().getServletContext().getRealPath("statics"+ File.separator+"uploadfiles");
                logger.info("uploadFile path: " + path);
                //获取原始文件名字,包括后缀
                String oldFileName = attach.getOriginalFilename();
                //获得文件名的扩展名,不包括.
                String prefix = FilenameUtils.getExtension(oldFileName);
                //equalsIgnoreCase:忽略大小写
                if(prefix.equalsIgnoreCase("doc")||prefix.equalsIgnoreCase("docx")||prefix.equalsIgnoreCase("pdf")||prefix.equalsIgnoreCase("rar")||prefix.equalsIgnoreCase("zip")||prefix.equalsIgnoreCase("exe")||prefix.equalsIgnoreCase("ppt")||prefix.equalsIgnoreCase("txt")||prefix.equalsIgnoreCase("xlsx")){
                    String resourceName = null;
    //                资源展示的扩展名图片
                    resourcePicPath = "../../statics/img/" + prefix+".jpg";
                    try {
                        //资源文件名字
                        resourceName = resourceDownload.getResourceName();
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
    //                资源名字:上传时输入的资源名字.文件后缀
                    resourceFileName = resourceName + "."+prefix;
                    File targetFile = new File(path,resourceFileName);
                    //判断路径(文件夹)是否存在,如果不存在就创建一个
                    if(!targetFile.exists()){
                        targetFile.mkdirs();
                    }
                    try {
                        // 将上传文件存储到服务器中
                        //使用此方法保存必须要绝对路径且文件夹必须已存在,否则报错
                        attach.transferTo(targetFile);
                    } catch (Exception e) {
                        e.printStackTrace();
                        return "redirect:/scxz/rd?error=error1";
                    }
                    downloadLink = request.getContextPath()+"/statics/img/"+resourceFileName;
                    resourceLocPath = path+File.separator+resourceFileName;
                }else{
                    String resourceName = null;
                    resourcePicPath = "../../statics/img/" +"bzd.jpg";
                    try {
                        //资源文件名字
                        resourceName = resourceDownload.getResourceName();
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                    resourceFileName = resourceName + "."+prefix;
                    File targetFile = new File(path,resourceFileName);
                    if(!targetFile.exists()){
                        targetFile.mkdirs();
                    }
                    try {
                        attach.transferTo(targetFile);
                    } catch (Exception e) {
                        e.printStackTrace();
                        return "redirect:/scxz/rd?error=error1";
                    }
                    downloadLink = request.getContextPath()+"/statics/img/"+resourceFileName;
                    resourceLocPath = path+File.separator+resourceFileName;
                }
    
                //        发布者的手机号
                resourceDownload.setResourceUserPhone(((UserInfo)session.getAttribute(Constants.QT_USER_SESSION)).getUserPhone());
    //        发布者的昵称
                resourceDownload.setResourceUserName(((UserInfo)session.getAttribute(Constants.QT_USER_SESSION)).getUserName());
                //new日期对象
                Date date = new Date();
                //转换提日期输出格式
                SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    //        发布的日期
                resourceDownload.setResourcePublishDate(dateFormat.format(date));
    //        资源下载的链接(虚拟)
                resourceDownload.setResourceDownloadLink(downloadLink);
    //        资源的下载链接(真实)
                resourceDownload.setResourceLocPath(resourceLocPath);
    //        资源的名字
                resourceDownload.setResourceName(resourceFileName);
    //        资源的图片(虚拟)
                resourceDownload.setResourcePicPath(resourcePicPath);
    //        资源的状态(1:待审核)
                resourceDownload.setResourceStatus(1);
                try {
    //                上传资源
                    if(resourceDownloadService.uploadResources(resourceDownload)){
                        return "redirect:/scxz/rd";
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
                return "redirect:/scxz/rd";
            }
    
            return "redirect:/scxz/rd?error=error2";
        }

    测试一下,上传这些文件

    上传成功,没问题

    再测试一下这部分

    然后发现当我上传的是0KB的文件,!attach.isEmpty()为false,然后我就加了判断

    //        上传资源报错的提示
            if(null != fileUploadError && fileUploadError.equals("error1")){
                fileUploadError = Constants.FILEUPLOAD_ERROR_1;
            }else if(null != fileUploadError && fileUploadError.equals("error2")){
                fileUploadError	= Constants.FILEUPLOAD_ERROR_2;
            }
            model.addAttribute("fileUploadError",fileUploadError);
    
    
    
    	public final static String FILEUPLOAD_ERROR_1 = " * 上传失败!";
    	public final static String FILEUPLOAD_ERROR_2 = " * 上传文件要大于0KB!";

    上传基本没问题了

     

    接下来是翻页的功能,这里的form[2]是这个jsp的第三个form

    <ul class="pagination">
        <c:if test="${pages.currentPageNo > 1}">
            <li class="paginate_button previous"><a
                    href="javascript:page_nav(document.forms[2],1);"
                    aria-controls="datatable-responsive" data-dt-idx="0"
                    tabindex="0">首页</a>
            </li>
            <li class="paginate_button "><a
                    href="javascript:page_nav(document.forms[2],${pages.currentPageNo-1});"
                    aria-controls="datatable-responsive" data-dt-idx="1"
                    tabindex="0">上一页</a>
            </li>
        </c:if>
        <c:if test="${pages.currentPageNo < pages.totalPageCount }">
            <li class="paginate_button "><a
                    href="javascript:page_nav(document.forms[2],${pages.currentPageNo+1 });"
                    aria-controls="datatable-responsive" data-dt-idx="1"
                    tabindex="0">下一页</a>
            </li>
            <li class="paginate_button next"><a
                    href="javascript:page_nav(document.forms[2],${pages.totalPageCount });"
                    aria-controls="datatable-responsive" data-dt-idx="7"
                    tabindex="0">最后一页</a>
            </li>
        </c:if>
    </ul>
    function page_nav(frm,num){
    		frm.pageIndex.value = num;
    		frm.submit();
    }

    对应的form里面还有

    <form>
       <input type="hidden" name="pageIndex" value="1" />
    </form>

    这样分页就完成了

     

    然后是分类,在上面的form基础上加上条件

    <form method="post" action="rd">
        <input type="hidden" name="pageIndex" value="1" />
        <input type="text" name="resourceName" value="${resourceName}" class="form-control" placeholder="输入资源名字">
        <select id="queryCategoryLevel1" name="resourceOneSort" class="form-control">
            <c:if test="${categoryLevel1List != null }">
                <option value="">--请选择--</option>
                <c:forEach var="categoryLevel1List" items="${categoryLevel1List}">
                    <option <c:if test="${categoryLevel1List.id == resourceOneSort }">selected="selected"</c:if>
                            value="${categoryLevel1List.id}">${categoryLevel1List.categoryName}
                    </option>
                </c:forEach>
            </c:if>
        </select>
        <select id="queryCategoryLevel2" name="resourceTwoSort" class="form-control">
            <c:if test="${categoryLevel2List != null }">
                <option value="">--请选择--</option>
                <c:forEach var="categoryLevel2List" items="${categoryLevel2List}">
                    <option <c:if test="${categoryLevel2List.id == resourceTwoSort }">selected="selected"</c:if>
                            value="${categoryLevel2List.id}">${categoryLevel2List.categoryName}
                    </option>
                </c:forEach>
            </c:if>
        </select>
        <select id="queryCategoryLevel3" name="resourceThreeSort" class="form-control">
            <c:if test="${categoryLevel3List != null }">
                <option value="">--请选择--</option>
                <c:forEach var="categoryLevel3List" items="${categoryLevel3List}">
                    <option <c:if test="${categoryLevel3List.id == resourceThreeSort }">selected="selected"</c:if>
                            value="${categoryLevel3List.id}">${categoryLevel3List.categoryName}
                    </option>
                </c:forEach>
            </c:if>
        </select>
        <button type="submit" class="layui-btn layui-btn-warm layui-btn-radius">查询资源</button>
    </form>

    资源的显示这部分功能就完成了,控制层代码:

        //资源库
    //    这里的error是上传资源有错误的提示
        @RequestMapping(value="rd")
        public String rd(Model model, HttpSession session,
                         @RequestParam(value="resourceName",required=false) String resourceName,
                         @RequestParam(value="resourceOneSort",required=false) String _resourceOneSort,
                         @RequestParam(value="resourceTwoSort",required=false) String _resourceTwoSort,
                         @RequestParam(value="resourceThreeSort",required=false) String _resourceThreeSort,
                         @RequestParam(value="pageIndex",required=false) String pageIndex,
                         @RequestParam(value="error",required= false)String fileUploadError){
    
            List<ResourceDownload> resourceDownloads = null;           //资源信息列表
            //列出一级分类列表,注:二级和三级分类列表通过异步ajax获取(文章的分类和资源分类共用的)
            List<ArticleCategory> categoryLevel1List = null;
            List<ArticleCategory> categoryLevel2List = null;
            List<ArticleCategory> categoryLevel3List = null;
    
            //资源页面容量(5)
            int pageSize = Constants.pageSize;
            //当前页码(1)
            Integer currentPageNo = 1;
            //判断pageIndex是否为空
            if(pageIndex != null){
                try{
                    //Integer.valueOf(),返回一个int,毕竟前面定义pageIndex是一个String,而currentPageNo是Integer
                    //如果当前页有其他值,就把1改为那个值,没有就默认第一页开始
                    currentPageNo = Integer.valueOf(pageIndex);
                    //数字格式化错误,如果你的参数字符串不是数字的话,经过Integer.valueOf(argument)就会抛出
                    // NumberFormatException异常。表示将字符串解析成int类型数字出现异常。
                }catch (NumberFormatException e) {
                    e.printStackTrace();
                }
            }
    
            Integer resourceOneSort = null;
            if(_resourceOneSort != null && !_resourceOneSort.equals("")){
                resourceOneSort = Integer.parseInt(_resourceOneSort);
            }
            Integer resourceTwoSort = null;
            if(_resourceTwoSort != null && !_resourceTwoSort.equals("")){
                resourceTwoSort = Integer.parseInt(_resourceTwoSort);
            }
            Integer resourceThreeSort = null;
            if(_resourceThreeSort != null && !_resourceThreeSort.equals("")){
                resourceThreeSort = Integer.parseInt(_resourceThreeSort);
            }
    
            //总数量(表)
            int totalCount = 0;
            try {
                totalCount = resourceDownloadService.getResourceInfoCount(resourceName, resourceOneSort, resourceTwoSort, resourceThreeSort,3);
            } catch (Exception e) {
                e.printStackTrace();
            }
    
            //总页数
            PageSupport pages = new PageSupport();
            pages.setCurrentPageNo(currentPageNo);      //当前页码
            pages.setPageSize(pageSize);                 //页码容量
            pages.setTotalCount(totalCount);             //页码条数
            int totalPageCount = pages.getTotalPageCount();          //计算出一共多少页
            //控制首页和尾页
    //        if(currentPageNo < 1){
    //            currentPageNo = 1;
    //        }else if(currentPageNo > totalPageCount){
    //            currentPageNo = totalPageCount;
    //        }
            try {
                resourceDownloads = resourceDownloadService.getResourceInfoList(resourceName, resourceOneSort, resourceTwoSort,resourceThreeSort,3,currentPageNo, pageSize);
    
    
                /*拿到一级的id,分类编码,分类名称*/
                categoryLevel1List = articleCategoryService.getArticleCategoryListByParentId(null);
            } catch (Exception e) {
                e.printStackTrace();
            }
            model.addAttribute("resourceDownloads", resourceDownloads);
            model.addAttribute("categoryLevel1List", categoryLevel1List);
            model.addAttribute("pages", pages);
            model.addAttribute("resourceName", resourceName);
            model.addAttribute("resourceOneSort", resourceOneSort);
            model.addAttribute("resourceTwoSort", resourceTwoSort);
            model.addAttribute("resourceThreeSort", resourceThreeSort);
    
            //二级分类列表和三级分类列表---回显
            if(resourceTwoSort != null && !resourceTwoSort.equals("")){
                categoryLevel2List = getCategoryList(resourceOneSort.toString());
                model.addAttribute("categoryLevel2List", categoryLevel2List);
            }
            if(resourceThreeSort != null && !resourceThreeSort.equals("")){
                categoryLevel3List = getCategoryList(resourceTwoSort.toString());
                model.addAttribute("categoryLevel3List", categoryLevel3List);
            }
    
    
    //        上传资源报错的提示
            if(null != fileUploadError && fileUploadError.equals("error1")){
                fileUploadError = Constants.FILEUPLOAD_ERROR_1;
            }else if(null != fileUploadError && fileUploadError.equals("error2")){
                fileUploadError	= Constants.FILEUPLOAD_ERROR_2;
            }
            model.addAttribute("fileUploadError",fileUploadError);
    
    
            //跳到资源库
            return "qt/resourceDownload";
        }

     

     

     

     

    download_info 下载表

    字段名称

    类型

    是否主键

    是否为空

    字段描述

    Id

    bigInt(30)

    Y

    N

    id

    resourceId

    bigInt(30)

    N

    N

    资源id

    userPhone

    varchar(20)

    N

    N

    下载的用户

    resourceIdUserPhone

    varchar(20)

    N

    N

    资源的上传者

    resourceName

    varchar(30)

    N

    N

    资源名字

    resourcePicPath

    varchar(500)

    N

    N

    资源图片虚拟路径

    resourceDownloadDate

    datetime

    N

    N

    资源的下载时间

     

    CREATE TABLE download_info(
      id BIGINT(30) NOT NULL AUTO_INCREMENT,
      resourceId BIGINT(30) NOT NULL,
      userPhone VARCHAR(20) NOT NULL,
      resourceIdUserPhone VARCHAR(20) NOT NULL,
      resourceName VARCHAR(30) NOT NULL,
      resourcePicPath VARCHAR(500) NOT NULL,
      resourceDownloadDate DATETIME NOT NULL,
      PRIMARY KEY (id)
    ) ENGINE=INNODB DEFAULT CHARSET=utf8;

    下载资源使用的是模态框

    在这里我遇到一个问题,数据是通过</c:forEach>遍历出来的,按钮是在里面的,模态框内容也不能放出去,要不值拿不到

    于是就产生一个尴尬的问题,点击哪个按钮显示的都是第一个资源的信息

    我改成class,那点击就是五个模态框信息,这样是不合理的

    解决方法是不去设置具体的值,而是根据遍历出来的id去显示,这样就没问题了,因为id是唯一的

     

     

    接着,写js的时候出现了个bug

    org.springframework.web.bind.MissingServletRequestParameterException: Required String parameter 'resourcePicPath' is not present

     是因为我加了“”,导致后面几个参数没数据了,去掉就行

    在这还遇到一个暂时解决不了的问题,扣除积分后无法自动下载,js写的有问题,后续再看看

     

     

     

    后台页面>

    medal_info 勋章表

    字段名称

    类型

    是否主键

    是否为空

    字段描述

    Id

    bigInt(30)

    Y

    N

    id

    userPhone

    varchar(20)

    N

    N

    勋章上传者手机号

    medalPicPath

    varchar(500)

    N

    N

    勋章虚拟路径

    medalLocPath

    varchar(500)

    N

    N

    勋章真实路径

    medalTitle

    Varchar(30)

    N

    Y

    勋章提示语

    medalPublishDate

    datetime

    N

    N

    勋章的发布日期

    medalStatus

    int

    N

    N

    勋章的状态

    勋章的状态:1:使用中;2:停止使用

    CREATE TABLE medal_info(
      id BIGINT(30) NOT NULL AUTO_INCREMENT,
      userPhone VARCHAR(20) NOT NULL,
      medalPicPath VARCHAR(500) NOT NULL,
      medalLocPath VARCHAR(500) NOT NULL,
      medalTitle VARCHAR(30) NOT NULL,
      medalPublishDate DATETIME NOT NULL,
      medalStatus INT NOT NULL,
      PRIMARY KEY (id)
    ) ENGINE=INNODB DEFAULT CHARSET=utf8;

     

     

     

    现在写一下批量操作的功能,类似的都可以这样写

    <th class="table-check"><input type="checkbox" name="" value=""></th>
    
    <td><input type="checkbox" value="${medalInfos.id }" class="checkbox-box"></td>
    
    
    <script src="${pageContext.request.contextPath }/statics/layer/3.1.1/layer.js"></script>
    int batchDelMedal(Integer[] ids);
    /* 全选 */
    $("table thead th input:checkbox").on("click", function() {
        $(this).closest("table").find("tr > td:first-child input:checkbox").prop("checked", $("table thead th input:checkbox").prop("checked"));
    });
    <%--批量删除--%>
    <script type="text/javascript">
        function batchdel() {
            if ($(".checkbox-box:checked")) {
                if ($(".checkbox-box:checked").length > 0) {
                    var pars = '';
                    $(".checkbox-box:checked").each(function(i, v) {
                        pars += '&ids=' + $(v).val();
                    });
                    alert(pars)
                    layer.confirm('亲,确认要删除吗?', function(index) {
                        $.ajax({
                            type : 'POST',
                            url : "batchDelMedal.json",
                            data : pars,
                            success : function(data) {
                                commonreuslt(data);
                            }
                        });
                    });
                } else {
                    layer.msg("亲,请选择你要删除的内容!", {
                        icon : 5,
                        time : 1000
                    });
                }
            }
    
        }
    </script>
    function commonreuslt(data) {
        if (data.code == "1000") {
            // icon参数为1是成功的图标,2000是显示那个弹窗的毫秒数
            layer.msg(data.msg, {
                icon : 1,
                time : 2000
            }, function() {
                //获取窗口索引
                var index = parent.layer.getFrameIndex(window.name);
                if (index > 0) {
                    // 父页面刷新
                    //parent.location.reload();
    
                    // parent.location.href:上一层页面跳转;parent.location.replace:实现父页面刷新
                    //parent.location.href: http://localhost:8080/ht/medalList
                    parent.location.replace(parent.location.href)
                    //关闭弹出的子页面窗口
                    parent.layer.close(index);
                } else {
                    // index是undefined,在这里执行了代码刷新了页面
                    // location.href:当前页完整url
                    location.replace(location.href)
                    //location.reload();
                }
    
            });
        } else if (data.code == "1001") {
            // 操作失败
            layer.msg(data.msg, {
                icon : 2,
                time : 2000
            });
        } else if (data.code == "1002") {
            alert("亲,操作失败!")
        }
    
    }
    <delete id="batchDelMedal">
            delete from medal_info where id in
            <foreach collection="array" item="id" index="no" open="(" separator="," close=")">
                #{id}
            </foreach>
    </delete>

     

     

    写js的时候,报了个错误

    js的let报错Let definitions are not supported by current JavaScript version

     解决方法:在IDE中设置JavaScript版本。可以在Windows上使用ctrl + alt + s进行访问。至少需要使用ECMA Script 6才能使用let

     

     

     

    在写资源下载的时候,遇到一些问题,虽然解决了,但是写的很菜

    游客无法下载,要有登录提醒

    积分不够显示充值通道

    假如我下载资源后,后退连接再下载,无法下载,会提醒用户页面过期,要刷新

    假如我有12积分,去了一个1积分的资源,不下载,再去一个12积分的资源,下载后我就没有积分了,但是后退到那个1积分下载页面,点击下载,我积分就变成了11积分(不合理,要处理,无法下载,会提醒用户页面过期,要刷新)

    假如我有12积分,打开谷歌浏览器去了一个12积分的资源页面,又打开火狐浏览器,去了一个1积分资源的页面,先在谷歌下载,我就没积分了,又去火狐下载,我的积分就变成了11(不合理,账号禁止多开)--暂时写成禁止使用该功能,要去刷新或重新登录

    假如我在下载资源的页面清了session的信息,那这样是下载不了的,要提示用户重新登录

    还有就是用户在下载资源过程中,发布者也在删除资源,这个也要处理一下

    用户在下载资源之前(已经打开资源详情页),发布者先删除资源,用户下载,应该无法下载,有错误提示,不会扣除积分等操作

    用户在下载资源过程中,发布者删除资源,不能删除,提醒发布者有用户在下载他的资源,提示稍后删除

    效果大概就这样了:

     

     

    完成每日签到的功能

    按照自己理解写的,只能写成这样了

    <div class="qd">
        <span>每日签到领取积分:</span>
        <%--隐藏了,要通过js显示,这个是解决签到后的显示--%>
        <a onclick="return confirm('亲,您今天已经签到了,明天再来吧')" id="si2" class="layui-btn layui-btn-disabled layui-btn-sm">已签到</a>
        <c:choose>
            <c:when test="${!empty sessionScope.qtUserInfoSession.userPhone}">
                <c:choose>
                    <c:when test="${empty signInFlag}">
                        <a signInFlag=1 class="signIn si1 layui-btn layui-btn-warm layui-btn-sm">点击签到</a>
                    </c:when>
                    <c:otherwise>
                        <a signInFlag=0 class="signIn layui-btn layui-btn-disabled layui-btn-sm">已签到</a>
                    </c:otherwise>
                </c:choose>
            </c:when>
            <c:otherwise>
                <%--游客显示这个--%>
                <a href="login" onclick="return confirm('亲,你需要登录才能签到呀')" class="layui-btn layui-btn-warm layui-btn-sm">点击签到</a>
            </c:otherwise>
        </c:choose>
    </div>
    <script>
        $("#si2").hide();
    </script>
    $(document).ready(function() {
        // 签到
    $(".signIn").on("click",function(){
        var obj = $(this);
        // 签到检查:0已签到;1未签到
        var signInFlag = obj.attr("signInFlag");
        if(signInFlag != "0"){
            $.ajax({
                type:"GET",
                url:"signIn.json",
                data:{},
                dataType:"json",
                success:function(data){
                    if(data.result == "true"){//签到成功
                        // window.location.href="index";
                        $(".si1").hide();
                        $("#si2").show();
                        alert("亲,签到成功,获得1积分,明天继续加油呀");
                    }else if(data.result == "false2"){//已经签到
                        alert("亲,页面过时请刷新");
                    }else if(data.result == "false1"){
                        alert("亲,网络异常,无法签到");
                    }
                },
                error:function(data){
                    alert("亲,网络异常,无法签到");
                }
            });
        }else {
            alert("亲,您今天已经签到了,明天再来吧")
        }
    });
    
    })
    
    //签到
        @RequestMapping(value="/signIn.json")
        @ResponseBody
        public Object signIn(HttpSession session){
            HashMap<String, String> resultMap = new HashMap<String, String>();
    //      用户签到字段
            String signIn = ((UserInfo)session.getAttribute(Constants.QT_USER_SESSION)).getSignIn();
    //        用户手机
            String userPhone = ((UserInfo)session.getAttribute(Constants.QT_USER_SESSION)).getUserPhone();
            CalendarGet calendarGet = new CalendarGet();
            String webUrl="http://www.baidu.com";
            //获取当前网络时间(百度时间)
            String webTime=calendarGet.getNetworkTime(webUrl);
            if (webTime.equals("")){
    //          无法获取网络时间,网络异常
                resultMap.put("result", "false1");
                return JSONArray.toJSONString(resultMap);
            }
            if (signIn.equals(webTime)){
    //          时间一样,说明当天已经签到
                resultMap.put("result", "false2");
            }
            if (!signIn.equals(webTime)){
                boolean a = false;
                try {
    //              更新签到字段的日期,以年月日显示yyyy-MM-dd(同时也增加积分<if test="signIn != null">userValue=userValue+1,</if>)
                    a = qtUserInfoService.updateUserInfo(userPhone,null,webTime);
                    if (a){
                        UserInfo userInfo = null;
        //              更新sessiion(后面怎么刷新网页都是显示已签到),也防止倒退页面刷积分
                        userInfo = qtUserInfoService.getUserInfo(null,((UserInfo)session.getAttribute(Constants.QT_USER_SESSION)).getUserPhone());
        //              签到成功
                        resultMap.put("result", "true");
        //              传1回去显示已关注样式(因为第一次显示是通过js显示的)
                        session.setAttribute("signInFlag","1");
                        session.setAttribute(Constants.QT_USER_SESSION, userInfo);
                    }
                    else {
    //                  中途有其他问题显示网络异常
                        resultMap.put("result", "false1");
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            return JSONArray.toJSONString(resultMap);
        }
        /*
         * 获取当前网络时间
         */
        public static String getNetworkTime(String webUrl) {
            try {
                URL url=new URL(webUrl); // 取得资源对象
                URLConnection conn=url.openConnection(); // 生成连接对象
                conn.connect(); // 发出连接
                long dateL=conn.getDate();    // 读取网站日期时间
                Date date=new Date(dateL);   // 转换为标准时间对象
    
    
                SimpleDateFormat dateFormat=new SimpleDateFormat("yyyy-MM-dd");
                return dateFormat.format(date);
            }catch (MalformedURLException e) {
                e.printStackTrace();
            }catch (IOException e) {
                // TODO: handle exception
                e.printStackTrace();
                System.out.println("网络异常");
            }
            return "";
        }

     

     

    主态个人空间

    发布文章这一块太大了,显的好难看,于是用折叠把它隐藏掉

     点击文案可以打开,再点击可以收回去

     

     

    主态的文章详情页

    (1)在主页或者其他页面点击文章的评论按钮能进入文章详情页,因为评论输入框在文章详情底部,如果文章巨长,我还要不停下滑页面才能评论,这显的用户体验感很差,于是就要对地址做一个定位,很简单

    在文章详情页评论模块给个id,在主页的评论按钮a标签最后给个#评论模块的id,这样就可以了(如#pldw)

    <a href="/qt/articleDetails?id='+item.id+'&&articleUserPhone='+item.userPhone+'#pldw">

     

    (2)在文章详情页时候,图片样式有问题,改成了这样显示就行了(外面的部分只是简单模仿头像框,忽略吧...)

    <a href="#" style="background:url(${pageContext.request.contextPath }${sessionScope.qtUserInfoSession.userPicPath}); background-size: 100%;"><span>更换头像</span></a>

     这样鼠标指在头像部分就会出现更换头像的蒙层,移开就消失,只有主态的文章详情页才有更换头像蒙层

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

    https://blog.csdn.net/qq_36850813/article/details/89711784

     

     

     

     

     

     

     

     

     

    展开全文
  • Java实验-接口.docx

    2020-12-13 20:09:46
    创建一个Vehicle接口,在接口中定义2个抽象方法:启动start()和刹车stop()。 定义两个类Car和Bike,实现Vehicle接口,并具体实现接口start()和stop()方法 。 创建一个类Test_Vehicle,在main主方法中分别通过使用...
  • python_docx制作word文档

    千次阅读 2018-11-21 16:37:00
    docx模块 Python可以利用python-docx模块处理word文档,处理方式是面向对象的。也就是说python-docx模块会把word文档,文档中的段落、文本、字体等都看做对象,对对象进行处理就是对word文档的内容处理。 ...

    python_docx制作word文档

    一、docx模块

    Python可以利用python-docx模块处理word文档,处理方式是面向对象的。也就是说python-docx模块会把word文档,文档中的段落、文本、字体等都看做对象,对对象进行处理就是对word文档的内容处理。

    二、相关概念

    如果需要读取word文档中的文字(一般来说,程序也只需要认识word文档中的文字信息),需要先了解python-docx模块的几个概念。

    1. Document对象,表示一个word文档。
    2. Paragraph对象,表示word文档中的一个段落
    3. Paragraph对象的text属性,表示段落中的文本内容。

     

    三、模块的安装和导入

    安装模块

    pip3 install python-docx

     

    注意在导入模块时,用的是import docx。

    也真是奇了怪了,怎么安装和导入模块时,很多都不用一个名字。

     

    四、读取word文本

    在了解了上面的信息之后,就很简单了,下面先创建一个D:\temp\word.docx文件,并在其中输入如下内容。

     

     写一段python代码读取

    #!/usr/bin/env python
    # coding: utf-8
    
    import docx
    #获取文档对象
    file=docx.Document("test1.docx")
    print("段落数:"+str(len(file.paragraphs)))#段落数为6,每个回车隔离一段
    
    #输出每一段的内容
    for para in file.paragraphs:
     print(para.text)
    
    #输出段落编号及段落内容
    for i in range(len(file.paragraphs)):
     print(""+str(i)+"段的内容是:"+file.paragraphs[i].text)

     

    执行输出:

    段落数:6
    清平调·其一
    【朝代】唐
    
    云想衣裳花想容,春风拂槛露华浓。
    若非群玉山头见,会向瑶台月下逢。
    
    第0段的内容是:清平调·其一
    第1段的内容是:【朝代】唐
    第2段的内容是:
    第3段的内容是:云想衣裳花想容,春风拂槛露华浓。
    第4段的内容是:若非群玉山头见,会向瑶台月下逢。
    第5段的内容是:
    View Code

     

    本文参考链接:

    https://www.jb51.net/article/133405.htm

     

    五、制作word文档

    1. 创建文档

    from docx import Document
    document = Document()
    document.save('ceshi.docx')  #保存文档

     

    执行之后,它会创建一个ceshi.docx,打开之后,内容是空的。

     

    2. 添加标题

    document.add_heading('琅琊榜', 0)

     

    完整代码如下:

    from docx import Document
    document = Document()
    
    document.add_heading('琅琊榜', 0)  # 添加标题
    
    document.save('ceshi.docx')  #保存文档
    View Code

     

    执行程序,打开文档,效果如下

     

    但是,这个有个问题是标题下面有一条横线,对于重度强迫症的我是无法容忍的。所以我直接添加段落文字表示标题

     

    3. 添加段落文字

    document.add_paragraph('剧情简介')

     

    完整代码如下:

    from docx import Document
    document = Document()
    
    document.add_paragraph('剧情简介')  # 添加段落
    
    document.save('ceshi.docx')  #保存文档
    View Code

     

    执行程序,打开文档,效果如下

     

     但是,这只实现了默认格式的段落文字添加,且这里的文字只能是英文。如果要设置中文字体,且对文字设置对齐,颜色,大小等设置,则:

    需要使用add_run()方法添加文字。

     

    4. 设置文字大小

    字体是不是太小了,可以设置文字大小。

    关于word中的字体大小对应表,请参考链接:

    https://blog.csdn.net/zhushouchen/article/details/50236817

     

    现在需要设置字体大小为一号,那么对应的数字为26

    完整代码如下:

    from docx import Document
    from docx.shared import Pt
    
    document = Document()
    p = document.add_paragraph()
    run = p.add_run('琅琊榜')  # 使用add_run添加文字
    run.font.size = Pt(26) #字体大小设置,和word里面的字号相对应,小一
    
    document.save('ceshi.docx')  #保存文档
    View Code

     

    执行程序,打开文档,效果如下

     

    可以发现,它的大小就是一号!

     

    5. 设置对齐

    我需要将这些文件居中对齐,怎么办?需要导入一个类WD_ALIGN_PARAGRAPH

    完整代码如下:

    from docx import Document
    from docx.shared import Pt
    from docx.enum.text import WD_ALIGN_PARAGRAPH
    
    document = Document()
    p = document.add_paragraph()
    run = p.add_run('琅琊榜')  # 使用add_run添加文字
    run.font.size = Pt(26) #字体大小设置,和word里面的字号相对应,小一
    p.paragraph_format.alignment = WD_ALIGN_PARAGRAPH.CENTER    #段落文字居中设置
    
    document.save('ceshi.docx')  #保存文档
    View Code

     

    注意:这段代码,导入类方法WD_ALIGN_PARAGRAPH时,Pycharm会飘红,但是不要紧,执行不会报错!

    执行程序,打开文档,效果如下:

     

    左对齐,WD_ALIGN_PARAGRAPH.LEFT

    右对齐,WD_ALIGN_PARAGRAPH.RIGHT

    其他更多方式,请参考链接:

    https://python-docx.readthedocs.org/en/latest/api/enum/WdAlignParagraph.html#wdparagraphalignment

     

    6. 设置字体加粗

    设置run.bold = True 就可以实现加粗

    完整代码如下:

    from docx import Document
    from docx.shared import Pt
    from docx.enum.text import WD_ALIGN_PARAGRAPH
    
    document = Document()
    p = document.add_paragraph()
    run = p.add_run('琅琊榜')  # 使用add_run添加文字
    run.font.size = Pt(26) #字体大小设置,和word里面的字号相对应,小一
    p.paragraph_format.alignment = WD_ALIGN_PARAGRAPH.CENTER    #段落文字居中设置
    run.bold = True  # 字体加粗
    
    document.save('ceshi.docx')  #保存文档
    View Code

     

    执行程序,打开文档,效果如下:

    7. 设置字体

    我需要将文字设置为 "宋体",使用

    document.styles['Normal'].font.name = '宋体'  # 设置字体
    document.styles['Normal']._element.rPr.rFonts.set(qn('w:eastAsia'), '宋体')

    网上很多文章说,只需要上面一行就可以了。其实根本就没有效果,要2行代码一起设置才会有效果!

     

    完整代码如下:

    from docx import Document
    from docx.shared import Pt
    from docx.enum.text import WD_ALIGN_PARAGRAPH
    from docx.oxml.ns import qn
    
    document = Document()
    p = document.add_paragraph()
    run = p.add_run('琅琊榜')  # 使用add_run添加文字
    run.font.size = Pt(26) #字体大小设置,和word里面的字号相对应,小一
    p.paragraph_format.alignment = WD_ALIGN_PARAGRAPH.CENTER    #段落文字居中设置
    run.bold = True  # 字体加粗
    document.styles['Normal'].font.name = '宋体'  # 设置字体
    document.styles['Normal']._element.rPr.rFonts.set(qn('w:eastAsia'), '宋体')
    
    document.save('ceshi.docx')  #保存文档
    View Code

     

    执行程序,打开文档,效果如下:

    可以发现,它已经是宋体了!之前的字体是MS Mincho

     

    8. 设置文字颜色

    我需要将标题设置为红色,需要使用RGB颜色,关于RGB颜色对照表,请参考链接:

    http://tool.oschina.net/commons?type=3

     

    那么红色对应的RGB就是255.0.0,代码就是

    run.font.color.rgb = RGBColor(255,0,0)

     

    完整代码如下:

    from docx import Document
    from docx.shared import Pt
    from docx.enum.text import WD_ALIGN_PARAGRAPH
    from docx.oxml.ns import qn
    from docx.shared import RGBColor
    
    document = Document()
    p = document.add_paragraph()
    run = p.add_run('琅琊榜')  # 使用add_run添加文字
    run.font.size = Pt(26) #字体大小设置,和word里面的字号相对应,小一
    p.paragraph_format.alignment = WD_ALIGN_PARAGRAPH.CENTER    #段落文字居中设置
    run.bold = True  # 字体加粗
    document.styles['Normal'].font.name = '宋体'  # 设置字体
    document.styles['Normal']._element.rPr.rFonts.set(qn('w:eastAsia'), '宋体')
    run.font.color.rgb = RGBColor(255,0,0) #颜色设置,这里是用RGB颜色
    
    document.save('ceshi.docx')  #保存文档
    View Code

     

    执行程序,打开文档,效果如下:

     

     

    9. 添加图片

    先从网络上下载一张图片lyb.jpb,请确保图片和python程序在同一目录

    图像大小

    默认情况下,添加图像出现在本地的大小。这通常比你想要的更大。本机大小的计算方法。因此,具有300dpi分辨率的300×300像素图像出现在一平方英寸。问题是大多数图像不包含dpi属性,它默认为72 dpi。这将使同一图像在一边,在一半左右的某处出现4.167英寸。pixels dpi

    要获得所需的图像大小,您可以以方便的单位指定其宽度或高度,如英寸或厘米:

    from docx.shared import Inches
    document.add_picture('image-filename.png', width=Inches(1.0))

    你可以自由地指定宽度和高度,但通常你不想要。如果仅指定一个,python-docx用它来计算出其他的适当换算值。这样的高宽比是保留的,你的图像看起来不拉伸。

    InchesCm提供课程,让你指定派上用场单位进行测量。在内部,python-docx使用英语公制单位,914400为英寸。所以,如果你忘记了,只是把喜欢的东西width=2,你会得到一个非常小的图像:)。你需要从导入docx.shared 子包。你可以在算术中使用它们,就像它们是一个整数,事实上它们是。因此,像一个表达式的作品就好了。width Inches(3) /thing_count

     

    完整代码如下:

    from docx import Document
    from docx.shared import Pt
    from docx.enum.text import WD_ALIGN_PARAGRAPH
    from docx.oxml.ns import qn
    from docx.shared import RGBColor
    from docx.shared import Inches
    
    document = Document()
    p = document.add_paragraph()
    run = p.add_run('琅琊榜')  # 使用add_run添加文字
    run.font.size = Pt(26) #字体大小设置,和word里面的字号相对应,小一
    p.paragraph_format.alignment = WD_ALIGN_PARAGRAPH.CENTER    #段落文字居中设置
    run.bold = True  # 字体加粗
    document.styles['Normal'].font.name = '宋体'  # 设置字体
    document.styles['Normal']._element.rPr.rFonts.set(qn('w:eastAsia'), '宋体')
    run.font.color.rgb = RGBColor(255,0,0) #颜色设置,这里是用RGB颜色
    
    pic = document.add_picture('lyb.jpg',width = Inches(5))  # 添加图片
    
    document.save('ceshi.docx')  #保存文档
    View Code

     

    执行程序,打开文档,效果如下:

    图片居中

    默认情况下,图片在文档中是左对齐的,如果要对图片进行居中显示,在网上找了很多方法都不可行,最后找到一种方法是直接加入以下代码:

    last_paragraph = document.paragraphs[-1]
    last_paragraph.alignment = WD_ALIGN_PARAGRAPH.CENTER      #图片居中设置

     

    完整代码如下:

    from docx import Document
    from docx.shared import Pt
    from docx.enum.text import WD_ALIGN_PARAGRAPH
    from docx.oxml.ns import qn
    from docx.shared import RGBColor
    from docx.shared import Inches
    
    document = Document()
    p = document.add_paragraph()
    run = p.add_run('琅琊榜')  # 使用add_run添加文字
    run.font.size = Pt(26) #字体大小设置,和word里面的字号相对应,小一
    p.paragraph_format.alignment = WD_ALIGN_PARAGRAPH.CENTER    #段落文字居中设置
    run.bold = True  # 字体加粗
    document.styles['Normal'].font.name = '宋体'  # 设置字体
    document.styles['Normal']._element.rPr.rFonts.set(qn('w:eastAsia'), '宋体')
    run.font.color.rgb = RGBColor(255,0,0) #颜色设置,这里是用RGB颜色
    
    pic = document.add_picture('lyb.jpg',width = Inches(5))  # 添加图片
    last_paragraph = document.paragraphs[-1]
    last_paragraph.alignment = WD_ALIGN_PARAGRAPH.CENTER      #图片居中设置
    
    document.save('ceshi.docx')  #保存文档
    View Code

     

    执行程序,打开文档,效果如下:

     

    10. 文字缩进

    我需要实现以下效果,就需要用到缩进了

     

    关于缩进,有3中方式

    from docx.shared import Inches
    from docx.shared import Pt
    
    #设置段落从左开始缩进,使用Inches来衡量
    paragraph_format.left_indent = Inches(0.5)
    
    #设置段落从右开始缩进,使用Pt来衡量
    paragraph_format.right_indent = Pt(20)
    
    #设置段落第一行缩进,可以与上两个缩进叠加
    paragraph_format.first_line_indent = Inches(0.5)

     

    仔细观察,上面的段落只需要用到首行缩进!

    完整代码如下:

    #!/usr/bin/env python
    # coding: utf-8
    
    from docx import Document
    from docx.shared import Pt
    from docx.enum.text import WD_ALIGN_PARAGRAPH
    from docx.oxml.ns import qn
    from docx.shared import RGBColor
    from docx.shared import Inches
    
    document = Document()
    p = document.add_paragraph()
    run = p.add_run('琅琊榜')  # 使用add_run添加文字
    run.font.size = Pt(26) #字体大小设置,和word里面的字号相对应,小一
    p.paragraph_format.alignment = WD_ALIGN_PARAGRAPH.CENTER    #段落文字居中设置
    run.bold = True  # 字体加粗
    document.styles['Normal'].font.name = '宋体'  # 设置字体
    document.styles['Normal']._element.rPr.rFonts.set(qn('w:eastAsia'), '宋体')
    run.font.color.rgb = RGBColor(255,0,0) #颜色设置,这里是用RGB颜色
    
    pic = document.add_picture('lyb.jpg',width = Inches(5))  # 添加图片
    last_paragraph = document.paragraphs[-1]
    last_paragraph.alignment = WD_ALIGN_PARAGRAPH.CENTER      #图片居中设置
    
    p = document.add_paragraph()
    run = p.add_run('剧情简介')
    run.font.size = Pt(22)  # 二号
    run.bold = True
    
    p = document.add_paragraph()
    run = p.add_run('梅长苏(胡歌饰)本远在江湖,却名动帝辇。江湖传言:“江左梅郎,麒麟之才,得之可得天下。”作为天下第一大帮“江左盟”的首领,梅长苏“梅郎”之名响誉江湖。然而,有着江湖至尊地位的梅长苏,却是一个病弱青年、弱不禁风,背负着十多年前巨大的冤案与血海深仇,就连身世背后也隐藏着巨大的秘密。')
    p_format = p.paragraph_format
    p_format.first_line_indent = Inches(0.2)  # 首行缩进
    
    document.save('ceshi.docx')  #保存文档
    View Code

     

    执行程序,打开文档,效果如下:

     

    11. 表格

    我需要实现下面的表格

     

    换行

    在剧情简介和音乐原声之间,需要有一个换行,否则就太紧凑了,使用

    document.add_paragraph(text='\r', style=None)  # 换行

     

    表格

    表格坐标体系

    一张图,就可以看明白了

     

    完整代码如下:

    #!/usr/bin/env python
    # coding: utf-8
    
    from docx import Document
    from docx.shared import Pt
    from docx.enum.text import WD_ALIGN_PARAGRAPH
    from docx.oxml.ns import qn
    from docx.shared import RGBColor
    from docx.shared import Inches
    
    document = Document()
    p = document.add_paragraph()
    run = p.add_run('琅琊榜')  # 使用add_run添加文字
    run.font.size = Pt(26) #字体大小设置,和word里面的字号相对应,小一
    p.paragraph_format.alignment = WD_ALIGN_PARAGRAPH.CENTER    #段落文字居中设置
    run.bold = True  # 字体加粗
    document.styles['Normal'].font.name = '宋体'  # 设置字体
    document.styles['Normal']._element.rPr.rFonts.set(qn('w:eastAsia'), '宋体')
    run.font.color.rgb = RGBColor(255,0,0) #颜色设置,这里是用RGB颜色
    
    pic = document.add_picture('lyb.jpg',width = Inches(5))  # 添加图片
    last_paragraph = document.paragraphs[-1]
    last_paragraph.alignment = WD_ALIGN_PARAGRAPH.CENTER      #图片居中设置
    
    p = document.add_paragraph()
    run = p.add_run('剧情简介')
    run.font.size = Pt(22)  # 二号
    run.bold = True
    
    p = document.add_paragraph()
    run = p.add_run('梅长苏(胡歌饰)本远在江湖,却名动帝辇。江湖传言:“江左梅郎,麒麟之才,得之可得天下。”作为天下第一大帮“江左盟”的首领,梅长苏“梅郎”之名响誉江湖。然而,有着江湖至尊地位的梅长苏,却是一个病弱青年、弱不禁风,背负着十多年前巨大的冤案与血海深仇,就连身世背后也隐藏着巨大的秘密。')
    p_format = p.paragraph_format
    p_format.first_line_indent = Inches(0.2)  # 首行缩进
    
    document.add_paragraph(text='\r', style=None)  # 换行
    
    p = document.add_paragraph()
    run = p.add_run('音乐原声')
    run.font.size = Pt(22)  # 二号
    run.bold = True
    
    table = document.add_table(rows=4, cols=5)  # 4行5列的表格
    table.cell(0,0).text = "歌曲"
    table.cell(0,1).text = "演唱者"
    table.cell(0,2).text = "作曲"
    table.cell(0,3).text = "作词"
    table.cell(0,4).text = "类型"
    
    table.cell(1,0).text = "《风起时》"
    table.cell(1,1).text = "胡歌"
    table.cell(1,2).text = "孟可"
    table.cell(1,3).text = "海宴"
    table.cell(1,4).text = "主题曲、片尾曲"
    
    table.cell(2,0).text = "《红颜旧》"
    table.cell(2,1).text = "刘涛"
    table.cell(2,2).text = "赵佳霖"
    table.cell(2,3).text = "袁亮"
    table.cell(2,4).text = "插曲"
    
    table.cell(3,0).text = "《赤血长殷》"
    table.cell(3,1).text = "王凯"
    table.cell(3,2).text = "于海航"
    table.cell(3,3).text = "清彦、冰封"
    table.cell(3,4).text = "插曲"
    
    document.save('ceshi.docx')  #保存文档
    View Code

     

    执行程序,打开文档,效果如下:

     

    可以发现,默认的表格是没有线条的。需要设置表格样式

    表格样式

    关于表格样式,可以参考链接:

    https://blog.csdn.net/ibiao/article/details/78595295

     

    上面的链接,列举了所有的样式。注意:这些样式,都是隔行换色的!

     

    喜欢哪个,将样式名复制一下,使用以下代码实现!

    下面的代码,表示使用Table Grid样式

    table.style = document.styles['Table Grid']  # 表格样式

     

    完整代码如下:

    #!/usr/bin/env python
    # coding: utf-8
    
    from docx import Document
    from docx.shared import Pt
    from docx.enum.text import WD_ALIGN_PARAGRAPH
    from docx.oxml.ns import qn
    from docx.shared import RGBColor
    from docx.shared import Inches
    
    document = Document()
    p = document.add_paragraph()
    run = p.add_run('琅琊榜')  # 使用add_run添加文字
    run.font.size = Pt(26) #字体大小设置,和word里面的字号相对应,小一
    p.paragraph_format.alignment = WD_ALIGN_PARAGRAPH.CENTER    #段落文字居中设置
    run.bold = True  # 字体加粗
    document.styles['Normal'].font.name = '宋体'  # 设置字体
    document.styles['Normal']._element.rPr.rFonts.set(qn('w:eastAsia'), '宋体')
    run.font.color.rgb = RGBColor(255,0,0) #颜色设置,这里是用RGB颜色
    
    pic = document.add_picture('lyb.jpg',width = Inches(5))  # 添加图片
    last_paragraph = document.paragraphs[-1]
    last_paragraph.alignment = WD_ALIGN_PARAGRAPH.CENTER      #图片居中设置
    
    p = document.add_paragraph()
    run = p.add_run('剧情简介')
    run.font.size = Pt(22)  # 二号
    run.bold = True
    
    p = document.add_paragraph()
    run = p.add_run('梅长苏(胡歌饰)本远在江湖,却名动帝辇。江湖传言:“江左梅郎,麒麟之才,得之可得天下。”作为天下第一大帮“江左盟”的首领,梅长苏“梅郎”之名响誉江湖。然而,有着江湖至尊地位的梅长苏,却是一个病弱青年、弱不禁风,背负着十多年前巨大的冤案与血海深仇,就连身世背后也隐藏着巨大的秘密。')
    p_format = p.paragraph_format
    p_format.first_line_indent = Inches(0.2)  # 首行缩进
    
    document.add_paragraph(text='\r', style=None)  # 换行
    
    p = document.add_paragraph()
    run = p.add_run('音乐原声')
    run.font.size = Pt(22)  # 二号
    run.bold = True
    
    table = document.add_table(rows=4, cols=5)  # 4行5列的表格
    table.cell(0,0).text = "歌曲"
    table.cell(0,1).text = "演唱者"
    table.cell(0,2).text = "作曲"
    table.cell(0,3).text = "作词"
    table.cell(0,4).text = "类型"
    
    table.cell(1,0).text = "《风起时》"
    table.cell(1,1).text = "胡歌"
    table.cell(1,2).text = "孟可"
    table.cell(1,3).text = "海宴"
    table.cell(1,4).text = "主题曲、片尾曲"
    
    table.cell(2,0).text = "《红颜旧》"
    table.cell(2,1).text = "刘涛"
    table.cell(2,2).text = "赵佳霖"
    table.cell(2,3).text = "袁亮"
    table.cell(2,4).text = "插曲"
    
    table.cell(3,0).text = "《赤血长殷》"
    table.cell(3,1).text = "王凯"
    table.cell(3,2).text = "于海航"
    table.cell(3,3).text = "清彦、冰封"
    table.cell(3,4).text = "插曲"
    
    table.style = document.styles['Table Grid']  # 表格样式
    
    document.save('ceshi.docx')  #保存文档
    View Code

     

    执行程序,打开文档,效果如下:

     

    表格字体加粗

    上面的效果还有一点不足,哪里呢?第一行没有加粗,关于表格文字加粗,这个问题找了很久,终于找到一篇文章,链接如下:

    https://www.jb51.net/article/139691.htm

     

    在它的基础上,我做了一些改进。封装了一个函数th

    完整代码如下:

    #!/usr/bin/env python
    # coding: utf-8
    
    from docx import Document
    from docx.shared import Pt
    from docx.enum.text import WD_ALIGN_PARAGRAPH
    from docx.oxml.ns import qn
    from docx.shared import RGBColor
    from docx.shared import Inches
    
    document = Document()
    p = document.add_paragraph()
    run = p.add_run('琅琊榜')  # 使用add_run添加文字
    run.font.size = Pt(26) #字体大小设置,和word里面的字号相对应,小一
    p.paragraph_format.alignment = WD_ALIGN_PARAGRAPH.CENTER    #段落文字居中设置
    run.bold = True  # 字体加粗
    document.styles['Normal'].font.name = '宋体'  # 设置字体
    document.styles['Normal']._element.rPr.rFonts.set(qn('w:eastAsia'), '宋体')
    run.font.color.rgb = RGBColor(255,0,0) #颜色设置,这里是用RGB颜色
    
    pic = document.add_picture('lyb.jpg',width = Inches(5))  # 添加图片
    last_paragraph = document.paragraphs[-1]
    last_paragraph.alignment = WD_ALIGN_PARAGRAPH.CENTER      #图片居中设置
    
    p = document.add_paragraph()
    run = p.add_run('剧情简介')
    run.font.size = Pt(22)  # 二号
    run.bold = True
    
    p = document.add_paragraph()
    run = p.add_run('梅长苏(胡歌饰)本远在江湖,却名动帝辇。江湖传言:“江左梅郎,麒麟之才,得之可得天下。”作为天下第一大帮“江左盟”的首领,梅长苏“梅郎”之名响誉江湖。然而,有着江湖至尊地位的梅长苏,却是一个病弱青年、弱不禁风,背负着十多年前巨大的冤案与血海深仇,就连身世背后也隐藏着巨大的秘密。')
    p_format = p.paragraph_format
    p_format.first_line_indent = Inches(0.2)  # 首行缩进
    
    document.add_paragraph(text='\r', style=None)  # 换行
    
    p = document.add_paragraph()
    run = p.add_run('音乐原声')
    run.font.size = Pt(22)  # 二号
    run.bold = True
    
    table = document.add_table(rows=4, cols=5)  # 4行5列的表格
    # table.cell(0,0).text = "歌曲"
    # table.cell(0,1).text = "演唱者"
    # table.cell(0,2).text = "作曲"
    # table.cell(0,3).text = "作词"
    # table.cell(0,4).text = "类型"
    
    def th(x,y,content):
        """
        th样式
        :param x: x坐标
        :param y: y坐标
        :param content: 内容
        :return: None
        """
        # print(grid,content)
        run = table.cell(x,y).paragraphs[0].add_run(content)
        run.bold = True  # 加粗
    
    th(0,0,"歌曲")
    th(0,1,"演唱者")
    th(0,2,"作曲")
    th(0,3,"作词")
    th(0,4,"类型")
    
    table.cell(1,0).text = "《风起时》"
    table.cell(1,1).text = "胡歌"
    table.cell(1,2).text = "孟可"
    table.cell(1,3).text = "海宴"
    table.cell(1,4).text = "主题曲、片尾曲"
    
    table.cell(2,0).text = "《红颜旧》"
    table.cell(2,1).text = "刘涛"
    table.cell(2,2).text = "赵佳霖"
    table.cell(2,3).text = "袁亮"
    table.cell(2,4).text = "插曲"
    
    table.cell(3,0).text = "《赤血长殷》"
    table.cell(3,1).text = "王凯"
    table.cell(3,2).text = "于海航"
    table.cell(3,3).text = "清彦、冰封"
    table.cell(3,4).text = "插曲"
    
    table.style = document.styles['Table Grid']  # 表格样式
    
    document.save('ceshi.docx')  #保存文档
    View Code

     

    执行程序,打开文档,效果如下:

     

    那么第一行,能不能加一个背景颜色呢?不好意思,目前没有找到有效的方法。

    上面有很多隔行换色的样式,可以选择。如果不想要,那么就只能这样了!

     

    表格字体颜色

    我需要将表格中的 "胡歌" 变成红色,怎么办呢?

    加一个方法即可

    def td_red(table,x, y,content):
        """
        td红色字体
        :param table: 表格对象
        :param x: x坐标
        :param y: y坐标
        :param content: 内容
        :return: None
        """
        run = table.cell(x, y).paragraphs[0].add_run(content)
        run.font.size = Pt(11)
        run.font.color.rgb = RGBColor(255, 0, 0)

     

    完整代码如下:

    #!/usr/bin/env python
    # coding: utf-8
    
    from docx import Document
    from docx.shared import Pt
    from docx.enum.text import WD_ALIGN_PARAGRAPH
    from docx.oxml.ns import qn
    from docx.shared import RGBColor
    from docx.shared import Inches
    
    document = Document()
    p = document.add_paragraph()
    run = p.add_run('琅琊榜')  # 使用add_run添加文字
    run.font.size = Pt(26) #字体大小设置,和word里面的字号相对应,小一
    p.paragraph_format.alignment = WD_ALIGN_PARAGRAPH.CENTER    #段落文字居中设置
    run.bold = True  # 字体加粗
    document.styles['Normal'].font.name = '宋体'  # 设置字体
    document.styles['Normal']._element.rPr.rFonts.set(qn('w:eastAsia'), '宋体')
    run.font.color.rgb = RGBColor(255,0,0) #颜色设置,这里是用RGB颜色
    
    pic = document.add_picture('lyb.jpg',width = Inches(5))  # 添加图片
    last_paragraph = document.paragraphs[-1]
    last_paragraph.alignment = WD_ALIGN_PARAGRAPH.CENTER      #图片居中设置
    
    p = document.add_paragraph()
    run = p.add_run('剧情简介')
    run.font.size = Pt(22)  # 二号
    run.bold = True
    
    p = document.add_paragraph()
    run = p.add_run('梅长苏(胡歌饰)本远在江湖,却名动帝辇。江湖传言:“江左梅郎,麒麟之才,得之可得天下。”作为天下第一大帮“江左盟”的首领,梅长苏“梅郎”之名响誉江湖。然而,有着江湖至尊地位的梅长苏,却是一个病弱青年、弱不禁风,背负着十多年前巨大的冤案与血海深仇,就连身世背后也隐藏着巨大的秘密。')
    p_format = p.paragraph_format
    p_format.first_line_indent = Inches(0.2)  # 首行缩进
    
    document.add_paragraph(text='\r', style=None)  # 换行
    
    p = document.add_paragraph()
    run = p.add_run('音乐原声')
    run.font.size = Pt(22)  # 二号
    run.bold = True
    
    table = document.add_table(rows=4, cols=5)  # 4行5列的表格
    # table.cell(0,0).text = "歌曲"
    # table.cell(0,1).text = "演唱者"
    # table.cell(0,2).text = "作曲"
    # table.cell(0,3).text = "作词"
    # table.cell(0,4).text = "类型"
    
    def th(x,y,content):
        """
        th样式
        :param x: x坐标
        :param y: y坐标
        :param content: 内容
        :return: None
        """
        # print(grid,content)
        run = table.cell(x,y).paragraphs[0].add_run(content)
        run.bold = True  # 加粗
    
    def td_red(table,x, y,content):
        """
        td红色字体
        :param table: 表格对象
        :param x: x坐标
        :param y: y坐标
        :param content: 内容
        :return: None
        """
        run = table.cell(x, y).paragraphs[0].add_run(content)
        run.font.size = Pt(11)
        run.font.color.rgb = RGBColor(255, 0, 0)
    
    th(0,0,"歌曲")
    th(0,1,"演唱者")
    th(0,2,"作曲")
    th(0,3,"作词")
    th(0,4,"类型")
    
    table.cell(1,0).text = "《风起时》"
    # table.cell(1,1).text = "胡歌"
    td_red(table,1,1,"胡歌")
    table.cell(1,2).text = "孟可"
    table.cell(1,3).text = "海宴"
    table.cell(1,4).text = "主题曲、片尾曲"
    
    table.cell(2,0).text = "《红颜旧》"
    table.cell(2,1).text = "刘涛"
    table.cell(2,2).text = "赵佳霖"
    table.cell(2,3).text = "袁亮"
    table.cell(2,4).text = "插曲"
    
    table.cell(3,0).text = "《赤血长殷》"
    table.cell(3,1).text = "王凯"
    table.cell(3,2).text = "于海航"
    table.cell(3,3).text = "清彦、冰封"
    table.cell(3,4).text = "插曲"
    
    table.style = document.styles['Table Grid']  # 表格样式
    
    document.save('ceshi.docx')  #保存文档
    View Code

     

    执行程序,打开文档,效果如下:

     

     

    本文参考链接:

    https://blog.csdn.net/sinat_30711195/article/details/80725435

     

    https://www.cnblogs.com/ontheway703/p/5266041.html

     

    posted @ 2018-11-21 16:37 肖祥 阅读( ...) 评论( ...) 编辑 收藏
    展开全文
  • 先拖到IDA瞅眼,发现加壳了 用PEID查一下是什么壳,但是没有查出来。使用Strings看一下有没有什么可参考的信息,发现是UPX 3.91的壳, 但是在UPX官网上下载的脱壳工具并没有成功脱壳,那么手脱吧。 UPX的脱壳...
  • 二、基于docx格式文件创建模板: (1)替换数据占位符: (2)将替换数据占位符文件强制修改zip格式: (3)获取文本数据文件document.xml: (4)获取图片依赖文件document.xml.rels: (5)占位符修改...
  • 首先创建一个maven工程 在pom文件中配置依赖 fr.opensagres.xdocreport fr.opensagres.xdocreport.converter.docx.xwpf 1.0.6 fr.opensagres.xdocreport org.apache.poi.xwpf.converter....
  • 前面写了很多利用python-docx批量处理word文件的教程,但是python-docx不支持doc格式,因此如何将doc文件转为docx文件就是一个需要解决的问题,如果文件少可以另存,如何文件多呢,因此需要一个doc批量转docx的...
  • -.填空题(12题 19.2分) 1Java中不支持_继承 正确答案: 第空 多重;多 . 2Java仅支持类间的_重继承 正确答案 第空:单 3当子类中定义的成员变量和...子类 我的答案: 5面向对象语言的四特征是:抽象_ 封装多态 正确
  • -单选题(25题50.0分) 1下列程序 段执行后的结果是( ) String s= new String"abcdefg; for (int i=0; i; i+=2){ System.out.print(s.charAt (i;} A. aceg B. ACEG C. abcdefg D. abcd 正确答案: A 2class Foo{ public...
  • 二、基于docx格式文件创建模板: (1)替换数据占位符: (2)将替换数据占位符文件强制修改zip格式: (3)获取文本数据文件document.xml: (4)获取图片依赖文件document.xml.rels: (5)占位符修改...
  • word vba 创建一个新文档

    千次阅读 2019-03-21 16:16:49
    Sub CreateWord() Dim wordApp As word.Application Application.StatusBar = "正在创建。。。" Set wordApp = New word.Application With wordApp .Visible = True Application.St...
  • 让我们逐步完成一个示例创建文档的步骤,从您可以对文档执行的两个主要操作开始,打开和保存。 打开一个文档 最简单的就是打开一个文档,但是不指定文件路径: from docx import Document document = Document() ...
  • 在d盘创建一个文件夹aaa madir()创建文件夹目录 15 File file = new File("d:\\aaa" ); 16 boolean mkdir = file.mkdir(); 17 System.out.println(mkdir); 18 // 在d盘的文件夹aaa里面创建三个...
  • Vim命令合集.docx

    2018-01-06 10:12:29
    vim filename 打开vim并创建名为filename的文件 文件命令 打开单个文件 vim file 同时打开多个文件 vim file1 file2 file3 ... 在vim窗口中打开一个新文件 :open file 在新窗口中打开文件 :split file 切换到下一个...
  • 它是一个与LibreOffice Writer Online和一个文件管理器的集成,当你在线时处理你的所有文件。它提供从一开始就创建文档的直接访问权限,但是它也拦截使用您的Web浏览器时打开的文件。主要特点:- 创建,编辑和查看...
  • 批处理定义:顾名思义,批处理文件是将一系列命令按一定的顺序集合为一个可执行的文本文件,其扩展名为BAT或者CMD。这些命令统称批处理命令。
  • 1.台Windows Server 2016 DC,主机名为DC。 2.两台Windows Server 2016服务器并加入域,主机名为Server1和Server2。 3.台Windows 10客户端并加入域,主机名为Win10。 实训操作 假设你是一家公司的网站...
  • 上海电子信息职业技术学院 通信与信息工程学院软件技术系 网页制作课程任务单 姓 学 号 班级 成绩 学习情景编号... 能够利用HTML创建表单对象 任务实施步骤 1在网站中新建一个子页 以玩具公司网站创建职位详情子

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 7,586
精华内容 3,034
关键字:

创建一个名为docx