热门标签 | HotTags
当前位置:  开发笔记 > 后端 > 正文

day52_BOS项目_04

今天内容安排:1、区域数据批量导入功能jQueryOCUpload(一键上传插件)使用apachePOI解析Excel文件使用Pinyin4J生成简码和城市编码2、实现区域的分页查
  • 今天内容安排:
    • 1、区域数据批量导入功能
      • jQuery OCUpload(一键上传插件)
      • 使用apache POI解析Excel文件
      • 使用Pinyin4J生成简码和城市编码
    • 2、实现区域的分页查询
    • 3、对分页代码重构
    • 4、添加分区(使用combobox下拉框)
    • 5、分区的组合条件分页查询
    • 6、分区数据导出功能

1、区域数据批量导入功能

1.1、jQuery OCUpload(一键上传插件)

  • ajax不能做文件上传。
    第一步:在jsp页面中引入插件的js文件

<script type="text/Javascript" 
    src="${pageContext.request.contextPath }/js/jquery.ocupload-1.1.2.js">
script>

第二步:在页面中提供任意一个元素

     <input id="but1" type="button" value="上传">

第三步:调用该插件提供的upload方法,动态修改页面html代码

    
    <script type="text/Javascript">
        $(function() {
            $("#but1").upload({
                action'abc',  
                name'myFile'
            });
        });
    
script>

动态修改页面html代码效果如下图所示:

1.2、使用apache POI解析Excel文件

  • Apache POI是Apache软件基金会的开放源码函式库,POI提供API给Java程序对Microsoft Office格式档案读和写的功能。
    第一步:导入poi-3.9-20121203.jar包
    第二步:测试代码如下:
package com.itheima.mytest;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;

import org.apache.poi.hssf.usermodel.HSSFSheet;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.Row;
import org.junit.Test;

public class POITest {
    /**
     * 使用POI解析Excel文件
     * @throws IOException 
     * @throws FileNotFoundException 
     */

    @Test
    public void test1() throws FileNotFoundException, IOException{
        HSSFWorkbook workbook = new HSSFWorkbook(new FileInputStream(new File("E:\\test\\abc.xls")));
        HSSFSheet sheet = workbook.getSheetAt(0);
        for (Row row : sheet) {
            String v1 = row.getCell(0).getStringCellValue();
            String v2 = row.getCell(1).getStringCellValue();
            String v3 = row.getCell(2).getStringCellValue();
            String v4 = row.getCell(3).getStringCellValue();
            String v5 = row.getCell(4).getStringCellValue();
            System.out.println(v1 + " " + v2 + " " + v3 + " " + v4 + " " + v5);
        }
    }
}

控制台输出结果为:

区域编号 省份 城市 区域 邮编
QY001 北京市 北京市 东城区 110101
QY002 北京市 北京市 西城区 110102
QY003 北京市 北京市 朝阳区 110105
QY004 北京市 北京市 丰台区 110106
QY005 北京市 北京市 石景山区 110107
QY006 北京市 北京市 海淀区 110108
QY007 北京市 北京市 门头沟区 110109
QY008 北京市 北京市 房山区 110111
QY009 北京市 北京市 通州区 110112
QY010 北京市 北京市 顺义区 110113
QY011 北京市 北京市 昌平区 110114
QY012 北京市 北京市 大兴区 110115
QY013 北京市 北京市 怀柔区 110116
QY014 北京市 北京市 平谷区 110117
QY015 北京市 北京市 密云县 110228
QY016 北京市 北京市 延庆县 110229
......

第三步:在RegionAction中提供批量导入方法

package com.itheima.bos.web.action;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import org.apache.poi.hssf.usermodel.HSSFSheet;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.Row;
import org.apache.struts2.ServletActionContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Controller;

import com.itheima.bos.domain.Region;
import com.itheima.bos.service.IRegionService;
import com.itheima.bos.web.action.base.BaseAction;

/**
 * 区域设置
 * @author Bruce
 *
 */

@Controller
@Scope("prototype")
public class RegionAction extends BaseAction<Region{

    @Autowired
    private IRegionService regionService;

    // 采用属性驱动的方式,接收上传过来的文件
    private File myFile;
    public void setMyFile(File myFile) {
        this.myFile = myFile;
    }

    /**
     * 批量导入Xls文件
     * @throws IOException 
     * @throws FileNotFoundException 
     */

    public String importXls() throws Exception {
        String flag = "1";
        try {
            // 使用POI解析Excel文件
            HSSFWorkbook workbook = new HSSFWorkbook(new FileInputStream(myFile));
            // 获得第一个sheet页
            HSSFSheet sheet = workbook.getSheetAt(0);
            List list = new ArrayList();
            for (Row row : sheet) {
                int rowNum = row.getRowNum();
                if (rowNum == 0) {
                    // 第一行,标题行,忽略
                    continue;
                }
                String id = row.getCell(0).getStringCellValue();
                String province = row.getCell(1).getStringCellValue();
                String city = row.getCell(2).getStringCellValue();
                String district = row.getCell(3).getStringCellValue();
                String postcode = row.getCell(4).getStringCellValue();

                Region region = new Region(id, province, city, district, postcode, nullnullnull);
                list.add(region);
            }
            regionService.saveBatch(list);          
        } catch (Exception e) {
            flag = "0";
        }
        // 服务器响应给浏览器一个状态码,这种手法常用
        ServletActionContext.getResponse().setContentType("text/html;charset=UTF-8");
        ServletActionContext.getResponse().getWriter().print(flag);

        return "none";
    }
}

第四步:浏览器根据服务器响应回来的状态码,进行判断并给出提示信息


<script type="text/Javascript">
    $(function() {
        $("#button-import").upload({
            action'${pageContext.request.contextPath}/regionAction_importXls.action',  
            name'myFile',
            // 浏览器根据服务器响应回来的状态码,进行判断并给出提示信息
            onComplete: function(data{
                // alert(data);
                if (data == '1') {
                    // 文件上传成功
                    $.messager.alert("提示信息""区域数据导入成功!""info");
                } else {
                    // 文件上传失败
                    $.messager.alert("提示信息""区域数据导入失败!""warning");
                } 
            }
        });
    });
script>

1.3、使用Pinyin4J生成简码和城市编码

第一步:导入pinyin4j-2.5.0.jar包,拷贝PinYin4jUtils.java工具类至utils包中
第二步:测试类代码如下:

package com.itheima.mytest;

import org.apache.commons.lang3.StringUtils;
import org.junit.Test;

import com.itheima.bos.utils.PinYin4jUtils;

public class Pinyin4JTest {
    @Test
    public void test1(){
        String province = "河北省";
        String city = "石家庄市";
        String district = "长安区";

        // 城市编码 --> shijiazhuang
        city  = city.substring(0, city.length() - 1);
        String[] stringToPinyin = PinYin4jUtils.stringToPinyin(city);
        String citycode = StringUtils.join(stringToPinyin, "");
        System.out.println(citycode);

        // 简码 --> HBSJZCA
        province  = province.substring(0, province.length() - 1);
        district  = district.substring(0, district.length() - 1);
        String info = province + city + district; // 河北石家庄长安
        String[] headByString = PinYin4jUtils.getHeadByString(info);
        String shortcode = StringUtils.join(headByString, "");
        System.out.println(shortcode);
    }
}

2、实现区域的分页查询

  • 代码同取派员的分页查询。

3、对分页代码重构

  • 在BaseAction中抽取PageBean对象,在BaseAction中提供setPage和setRows方法,并注入给PageBean对象
    // 采用属性驱动的方式,接收页面提交过来的参数
    protected PageBean pageBean = new PageBean();
    public void setPage(int page) {
        // 设置当前页码
        pageBean.setCurrentPage(page);
    }
    public void setRows(int rows) {
        // 设置每页显示记录数
        pageBean.setPageSize(rows);
    }
  • 在BaseAction中抽取条件查询对象
    // 设置离线条件查询对象,封装查询条件
    DetachedCriteria detachedCriteria = null;
  • 在BaseAction的构造方法中创建条件查询对象,并注入给PageBean对象
    如下图所示:
  • 在BaseAction中抽取将PageBean对象转为json的方法
    /**
     * 将PageBean对象转为JSON格式的数据的方法
     * @param pageBean
     * @param excludes
     * @throws IOException
     */

    public void writePageBean2Json(PageBean pageBean, String[] excludes) throws IOException {
        // 步骤:先导入json-lib的jar包+依赖包,步骤链接:https://www.cnblogs.com/chenmingjun/p/9513143.html
        // 将PageBean对象转为JSON格式的数据响应给客户端浏览器进行显示
        // 排除不需要的数据和排除关联对象
        JsonConfig jsonConfig = new JsonConfig();
        jsonConfig.setExcludes(new String[] {"currentPage""pageSize""detachedCriteria"});

        JSONObject jsonObject = JSONObject.fromObject(pageBean, jsonConfig);
        String json = jsonObject.toString();

        ServletActionContext.getResponse().setContentType("text/json;charset=UTF-8");
        ServletActionContext.getResponse().getWriter().print(json);
    }
  • 在RegionAction中使用分页方法
    /**
     * 分页查询
     * @throws IOException 
     */

    public String pageBean() throws IOException {
        // 调用方法,设置PageBean对象的其他属性
        regionService.pageBean(pageBean); // 注意:pageBean传的是对象的引用,调用该方法后,对象的属性就发生改变了
        // 调用将PageBean对象转为JSON格式的数据的方法
        this.writePageBean2Json(pageBean, new String[] {"currentPage""pageSize""detachedCriteria"}); // 实际做项目中,要把没用到的数据都给干掉,也就是说不需要显示的数据有很多
        return "none";
    }

4、使用jQuery EasyUI 下拉框combobox


第一步:在subarea.jsp中使用combobox下拉框展示区域数据到下拉框中
    <tr>
        <td>选择区域td>
        <td>
            <input class="easyui-combobox" name="region.id"  
                data-options="valueField:'id',textField:'name',
                url:'${pageContext.request.contextPath}/regionAction_listajax1.action'"
 />
  
        td>
    tr>

效果如下图所示:


第二步:在RegionAction中提供listajax()方法,查询所有的区域数据,返回json数据,并将该方法抽取至BaseAction中
RegionAction.java
    /**
     * 查询所有的区域数据,返回json
     * @throws IOException 
     */

    public String listajax() throws IOException {
        List list = regionService.findAll();
        String[] exclude = new String[]{"subareas"}; // 实际做项目中,要把没用到的数据都给排除掉,也就是说不需要显示的数据有很多,本例中只排除掉了一个
        this.writeList2Json(list, exclude);
        return null;
    }

BaseAction.java

    /**
     * 将List集合对象转为JSON格式的数据的方法
     * @param list
     * @param exclude
     * @throws IOException 
     */

    public void writeList2Json(List list, String[] exclude) throws IOException {
        JsonConfig jsonConfig = new JsonConfig();
        jsonConfig.setExcludes(new String[] {"currentPage""pageSize""detachedCriteria"});

        JSONArray jsonObject = JSONArray.fromObject(list, jsonConfig);
        String json = jsonObject.toString();

        ServletActionContext.getResponse().setContentType("text/json;charset=UTF-8");
        ServletActionContext.getResponse().getWriter().print(json);
    }

为了使返回的json中含有name字段,需要在Region类中提供getName()方法

    // 序列化一个对象的时候,找的是getter方法
    public String getName() {
        return province + city + district;
    }

浏览器返回的json数据效果如下图所示:


页面效果如下图所示:

5、添加分区

第一步:页面位置:/bos19/WebContent/WEB-INF/pages/base/subarea.jsp
为了便于处理,我们先将subarea.jsp中的分拣编码选项框删掉,该编号我们让其自动生成。
我们在Subarea.hbm.xml中更改主键生成策略,代码如下:

    <id name="id" type="java.lang.String">
        <column name="id" length="32" />
        
        <generator class="uuid" />
    id>

第二步:为添加窗口中的“保存按钮”绑定事件

    <div class="datagrid-toolbar">
        <a id="save" icon="icon-save" href="#" class="easyui-linkbutton" plain="true" >保存a>
        <script type="text/Javascript">
            $(function() {
                $("#save").click(function() {
                    var v = $("#addSubareaForm").form("validate");
                    if (v) {
                        $("#addSubareaForm").submit();
                    }
                });
            });
        
script>           
    div>

第三步:创建SubareaAction类,提供add方法,处理分区添加动作
SubareaAction.java

package com.itheima.bos.web.action;

import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Controller;

import com.itheima.bos.domain.Subarea;
import com.itheima.bos.web.action.base.BaseAction;

/**
 * 分区设置
 * @author Bruce
 *
 */

@Controller
@Scope("prototype")
public class SubareaAction extends BaseAction<Subarea>{

    /**
     * 添加分区的方法
     * @return
     */

    public String add() {
        subareaService.save(model);
        return "list";
    }
}

在第三步之前,我们将所有的注入service,抽取至BaseAction中,将修饰符public改为protected,使其子类能够访问
BaseAction.java

    // 注入service
    @Autowired
    protected IUserService userServie;
    @Autowired
    protected IStaffService staffService;
    @Autowired
    protected IRegionService regionService;
    @Autowired
    protected ISubareaService subareaService;

第四步:配置struts.xml

    
    <action name="subareaAction_*" class="subareaAction" method="{1}">
        <result name="list">/WEB-INF/pages/base/subarea.jspresult>
    action>

6、解决区域分页查询的bug

Method public java.lang.String org.apache.commons.lang.exception.NestableRuntimeException.getMessage(intthrew an exception when invoked on net.sf.json.JSONExceptionThere is a cycle in the hierarchy!
  • 延迟加载也称为懒加载,是Hibernate3关联关系对象默认的加载方式,所谓延迟加载就是当在真正需要数据的时候,才真正执行数据加载操作。简单理解为,只有在使用的时候,才会发出sql语句进行查询。
  • Hibernate中主要是通过代理(proxy)机制来实现延迟加载。我们在查询区域的时候,区域关联的分区没有立即查询,因为所有的关联查询默认都是延时加载(懒加载)。那么返回来的就是代理对象,而代理对象是不能被序列化的。
  • 如何解决呢?
    答:因为PageBean中的属性有集合list,此时的list集合中存放的是Region对象,而Region对象中又关联一个集合set,该set集合存放的Subareas对象,该Subareas对象默认是懒加载的,而此时我们没有用到Subareas对象的数据,所以我们就应该将其排除掉。
    this.writePageBean2Json(pageBean, new String[] {"currentPage""pageSize""detachedCriteria""subareas"}); 

7、实现分区分页查询(没有过滤条件)

  • 代码同区域的分页查询。
    小区别:当我们查询分区表的时候,需要立即去查询关联的区域表
    我们需要修改分区的Hibernate配置文件Subarea.hbm.xml中的加载时机,修改代码如下:
    
    <many-to-one lazy="false" name="region" class="com.itheima.bos.domain.Region" fetch="select">
        <column name="region_id" length="32" />
    many-to-one>
    <many-to-one name="decidedzone" class="com.itheima.bos.domain.Decidedzone" fetch="select">
        <column name="decidedzone_id" length="32" />
    many-to-one>

8、实现分区组合条件分页查询

EasyUI Datagrid 数据网格的load()方法:
  加载并显示第一页的行,如果指定 'param' 参数,它将替换 queryParams 属性。通常情况下,通过传递一些从参数进行查询,该方法被调用来从服务器加载新数据。
查询分区页面如下图所示:


第一步:为“查询按钮”绑定事件,调用datagrid的load()方法,重新发起ajax请求,并提交输入框参数,这里我们使用一个工具方法:将指定的表单中的输入项序列化为json对象
    // 工具方法:可以将指定的表单中的输入项序列化为json对象
    $.fn.serializeJson = function() {
        var serializeObj = {};
        var array = this.serializeArray();
        $(array).each(
            function() {
                if (serializeObj[this.name]) {
                    if ($.isArray(serializeObj[this.name])) {
                        serializeObj[this.name].push(this.value);
                    } else {
                        serializeObj[this.name] = [serializeObj[this.name], this.value];
                    }
                } else {
                    serializeObj[this.name] = this.value;
                }
            });
        return serializeObj;
    };

    // 绑定事件:执行查询
    $("#btn").click(function() {
        // 将表单序列化为json对象
        var p = $("#searchForm").serializeJson(); // json对象格式:{id:'xxx', name:'xxx', age:'xxx', ...}
        // 重新发起ajax请求,并提交新的参数(包括原来的参数)
        $("#grid").datagrid("load", p);
        // 关闭查询窗口
        $("#searchWindow").window("close");
    });

浏览器的调试截图:


第三步:修改SubareaAction中的分页查询方法,封装分页查询的条件
    /**
     * 分页查询
     * @return
     * @throws IOException 
     */

    public String pageQuery() throws IOException {
        // 由于提交的表单中新增了其他条件,所以我们在分页查询之前,需要封装条件
        DetachedCriteria detachedCriteria2 = pageBean.getDetachedCriteria();

        // QBC查询语句:当关联查询时,也即多表查询,需要创建别名,通过别名才可以访问关联对象的属性

        // 先根据分区的地址关键字进行模糊查询
        String addresskey = model.getAddresskey();
        if (StringUtils.isNotBlank(addresskey)) {
            detachedCriteria2.add(Restrictions.like("addresskey""%" + addresskey + "%"));
        }

        // 再根据 省/市/区 进行模糊查询
        Region region = model.getRegion();
        if (region != null) {
            // 创建别名,用于多表查询
            detachedCriteria2.createAlias("region""r");

            String province = region.getProvince();
            String city = region.getCity();
            String district = region.getDistrict();

            if (StringUtils.isNotBlank(province)) {
                // 根据 省 进行模糊查询(注意:关联查询)
                detachedCriteria2.add(Restrictions.like("r.province""%" + province + "%"));
            }
            if (StringUtils.isNotBlank(city)) {
                // 根据 市 进行模糊查询(注意:关联查询)
                detachedCriteria2.add(Restrictions.like("r.city""%" + city + "%"));
            }
            if (StringUtils.isNotBlank(district)) {
                // 根据 区 进行模糊查询(注意:关联查询)
                detachedCriteria2.add(Restrictions.like("r.district""%" + district + "%"));
            }
        }

        subareaService.pageQuery(pageBean);
        this.writePageBean2Json(pageBean, new String[] {"currentPage""pageSize""detachedCriteria""decidedzone""subareas"});
        return "none";
    }

9、分区数据导出功能

  • 导出Excel文件提供客户下载

第一步:为“导出”按钮绑定事件

    // 导出Excel文件,注意:文件下载必须是同步提交方式
    function doExport() {
        // get方式提交
        window.location.href = "${pageContext.request.contextPath}/subareaAction_exportXls.action";
    }

第二步:在SubareaAction中提供导出方法

    /**
     * 使用POI写入Excel文件,提供下载
     * @return
     * @throws IOException 
     */

    public String exportXls() throws IOException {
        List list = subareaService.findAll();

        // 在内存中创建一个Excel文件,通过输出流写到客户端提供下载
        HSSFWorkbook workbook = new HSSFWorkbook();
        // 创建一个sheet页
        HSSFSheet sheet = workbook.createSheet("分区数据");
        // 创建标题行
        HSSFRow headRow = sheet.createRow(0);
        // 设置标题行单元格的内容
        headRow.createCell(0).setCellValue("分区编号");
        headRow.createCell(1).setCellValue("区域编号");
        headRow.createCell(2).setCellValue("地址关键字");
        headRow.createCell(3).setCellValue("省市区");
        // 遍历list集合
        for (Subarea subarea : list) {
            HSSFRow dataRow = sheet.createRow(sheet.getLastRowNum() + 1);
            dataRow.createCell(0).setCellValue(subarea.getId());
            dataRow.createCell(1).setCellValue(subarea.getRegion().getId());
            dataRow.createCell(2).setCellValue(subarea.getAddresskey());

            Region region = subarea.getRegion();
            dataRow.createCell(3).setCellValue(region.getProvince() + region.getCity() + region.getDistrict());
        }

        String filename = "分区数据.xls";
        String agent = ServletActionContext.getRequest().getHeader("User-Agent"); // 浏览器类型
        filename = FileUtils.encodeDownloadFilename(filename, agent);

        // 一个流两个头
        ServletOutputStream out = ServletActionContext.getResponse().getOutputStream();
        String contentType = ServletActionContext.getServletContext().getMimeType(filename);
        ServletActionContext.getResponse().setContentType(contentType);
        ServletActionContext.getResponse().setHeader("content-disposition""attchment;filename=" + filename);
        workbook.write(out);

        return "none";
    }

浏览器界面效果图如下:


推荐阅读
  • 网络运维工程师负责确保企业IT基础设施的稳定运行,保障业务连续性和数据安全。他们需要具备多种技能,包括搭建和维护网络环境、监控系统性能、处理突发事件等。本文将探讨网络运维工程师的职业前景及其平均薪酬水平。 ... [详细]
  • 本文深入探讨了JavaScript中实现继承的四种常见方法,包括原型链继承、构造函数继承、组合继承和寄生组合继承。对于正在学习或从事Web前端开发的技术人员来说,理解这些继承模式对于提高代码质量和维护性至关重要。 ... [详细]
  • 本文将详细介绍如何在ThinkPHP6框架中实现多数据库的部署,包括读写分离的策略,以及如何通过负载均衡和MySQL同步技术优化数据库性能。 ... [详细]
  • 掌握远程执行Linux脚本和命令的技巧
    本文将详细介绍如何利用Python的Paramiko库实现远程执行Linux脚本和命令,帮助读者快速掌握这一实用技能。通过具体的示例和详尽的解释,让初学者也能轻松上手。 ... [详细]
  • 本文介绍了如何使用PHP代码实现微信平台的媒体素材上传功能,详细解释了API接口的使用方法和注意事项,确保文件路径正确以避免常见的错误。 ... [详细]
  • 帝国CMS多图上传插件详解及使用指南
    本文介绍了一款用于帝国CMS的多图上传插件,该插件通过Flash技术实现批量图片上传功能,显著提升了多图上传效率。文章详细说明了插件的安装、配置和使用方法。 ... [详细]
  • 本文详细介绍了 Flink 和 YARN 的交互机制。YARN 是 Hadoop 生态系统中的资源管理组件,类似于 Spark on YARN 的配置方式。我们将基于官方文档,深入探讨如何在 YARN 上部署和运行 Flink 任务。 ... [详细]
  • 本文详细介绍如何利用已搭建的LAMP(Linux、Apache、MySQL、PHP)环境,快速创建一个基于WordPress的内容管理系统(CMS)。WordPress是一款流行的开源博客平台,适用于个人或小型团队使用。 ... [详细]
  • Struts与Spring框架的集成指南
    本文详细介绍了如何将Struts和Spring两个流行的Java Web开发框架进行整合,涵盖从环境配置到代码实现的具体步骤。 ... [详细]
  • 解决FCKeditor应用主题后上传问题及优化配置
    本文介绍了在Freetextbox收费后选择FCKeditor作为替代方案时遇到的上传问题及其解决方案。通过调整配置文件和调试工具,最终解决了上传失败的问题,并对相关配置进行了优化。 ... [详细]
  • 本文详细介绍了在使用 SmartUpload 组件进行文件上传时,如何正确配置和查找文件保存路径。通过具体的代码示例和步骤说明,帮助开发者快速解决上传路径配置的问题。 ... [详细]
  • 在PHP后端开发中遇到一个难题:通过第三方类文件发送短信功能返回的JSON字符串无法解析。本文将探讨可能的原因并提供解决方案。 ... [详细]
  • 为了解决不同服务器间共享图片的需求,我们最初考虑建立一个FTP图片服务器。然而,考虑到项目是一个简单的CMS系统,为了简化流程,团队决定探索七牛云存储的解决方案。本文将详细介绍使用七牛云存储的过程和心得。 ... [详细]
  • 在Java应用程序开发过程中,FTP协议被广泛用于文件的上传和下载操作。本文通过Jakarta Commons Net库中的FTPClient类,详细介绍如何实现文件的上传和下载功能。 ... [详细]
  • 本文详细分析了Hive在启动过程中遇到的权限拒绝错误,并提供了多种解决方案,包括调整文件权限、用户组设置以及环境变量配置等。 ... [详细]
author-avatar
快乐碧云轩在江湖
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有