欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

springboot+thymeleaf+layui的實(shí)現(xiàn)示例

 更新時(shí)間:2023年12月12日 09:16:04   作者:左邊的天堂  
本文主要介紹了springboot+thymeleaf+layui的實(shí)現(xiàn)示例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧

一、背景

公司運(yùn)營(yíng)的同事有個(gè)任務(wù),提供一個(gè)數(shù)據(jù)文件給我,然后從數(shù)據(jù)庫(kù)中找出對(duì)應(yīng)的加密串再導(dǎo)出來(lái)給他。這個(gè)活不算是很難,但時(shí)不時(shí)就會(huì)有需求。

同事給我的文件有時(shí)是給excel表格,每一行有4列,逗號(hào)隔開(kāi),合并成一列數(shù)據(jù),這類文件需要把所有數(shù)據(jù)復(fù)制到文本編輯器進(jìn)行處理,把逗號(hào)替換成空格,再使用列塊編輯模式復(fù)制2、3、4列替換原來(lái)的excel數(shù)據(jù)。有時(shí)是給.DAT的文件,這類文件需要手動(dòng)修改后綴為csv,修改后就跟普通的excel表格一樣打開(kāi),去掉第一列。最后添加一行表頭,再對(duì)第一列進(jìn)行篩選去重。

去重后準(zhǔn)備導(dǎo)入到數(shù)據(jù)庫(kù)的臨時(shí)表,在此之前需要手動(dòng)清空臨時(shí)表的歷史數(shù)據(jù)。導(dǎo)入后再執(zhí)行一段sql語(yǔ)句,然后把查詢結(jié)果導(dǎo)出為excel文件給到同事。

這樣的工作重復(fù)重復(fù)再重復(fù),確實(shí)挺無(wú)趣的,何不鼓搗一個(gè)工具給同事自己去處理?

二、步驟

2.1 項(xiàng)目搭建

項(xiàng)目結(jié)構(gòu)如下圖:

創(chuàng)建項(xiàng)目,使用springboot 2.5.14、poi 4.1.2、mybatis,前端使用 thymeleaf + layui-v2.6.8 。

具體看maven配置

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.5.14</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.xxx</groupId>
    <artifactId>test</artifactId>
    <version>1.0</version>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>

    <dependencies>
        <!-- Spring框架基本的核心工具 -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context-support</artifactId>
        </dependency>
        <!-- SpringBoot Web容器 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <exclusions>
                <exclusion>
                    <groupId>org.apache.tomcat.embed</groupId>
                    <artifactId>tomcat-embed-el</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>org.apache.tomcat.embed</groupId>
                    <artifactId>tomcat-embed-websocket</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        
        <!-- thymeleaf -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>

        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.1.4</version>
            <exclusions>
                <exclusion>
                    <groupId>com.zaxxer</groupId>
                    <artifactId>HikariCP</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

        <!-- 阿里數(shù)據(jù)庫(kù)連接池 -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.2.16</version>
        </dependency>

        <!-- Mysql驅(qū)動(dòng)包 -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>

        <!--常用工具類 -->
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
        </dependency>

        <!-- io常用工具類 -->
        <dependency>
            <groupId>commons-io</groupId>
            <artifactId>commons-io</artifactId>
            <version>2.11.0</version>
        </dependency>

        <!-- excel工具 -->
        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi-ooxml</artifactId>
            <version>4.1.2</version>
            <exclusions>
                <exclusion>
                    <groupId>org.apache.commons</groupId>
                    <artifactId>commons-math3</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>org.zaxxer</groupId>
                    <artifactId>SparseBitSet</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

        <!-- servlet包 -->
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
        </dependency>

        <!-- lombok -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <scope>provided</scope>
        </dependency>

    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.1</version>
                <configuration>
                    <source>${java.version}</source>
                    <target>${java.version}</target>
                    <encoding>${project.build.sourceEncoding}</encoding>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <version>2.7.3</version>
                <executions>
                    <execution>
                        <goals>
                            <goal>repackage</goal>
                        </goals>
                    </execution>
                </executions>
                <configuration>
                    <mainClass>com.xxx.AdminApplication</mainClass>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>

為了節(jié)省jar包體積,盡可能把不需要的依賴給排除。

2.2 后端處理邏輯

Controller內(nèi)容

import com.xxx.domain.Result;
import com.xxx.domain.CellItem;
import com.xxx.domain.HelmetConfig;
import com.xxx.service.HelmetService;
import com.xxx.utils.file.DatUtil;
import com.xxx.utils.poi.ExcelUtil;
import org.apache.commons.io.FilenameUtils;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.util.Arrays;
import java.util.List;

/**
 * 通用請(qǐng)求處理
 *
 * @author admin
 */
@Controller
public class CommonController {
    public static final String[] EXCEL_EXTENSION = {"xls", "xlsx", "XLS", "XLSX"};
    public static final String DAT_EXTENSION = "DAT";

    @Resource
    private HelmetService helmetService;

    @GetMapping(value = {"/", "/index"})
    public String index(Model model) {
        return "index";
    }

    /**
     * 通用下載請(qǐng)求
     */
    @GetMapping("/download")
    public void fileDownload(HttpServletResponse response) {
        List<HelmetConfig> list = helmetService.queryAll();
        ExcelUtil<HelmetConfig> util = new ExcelUtil<>(HelmetConfig.class);
        util.exportExcel(response, list, "Sheet1");
    }

    /**
     * 通用上傳請(qǐng)求(單個(gè))
     */
    @PostMapping("/upload")
    @ResponseBody
    public Result uploadFile(MultipartFile file) {
        if (file == null || file.isEmpty()) {
            return Result.error("文件不能為空");
        }
        String extension = FilenameUtils.getExtension(file.getOriginalFilename());
        List<CellItem> list;
        if (Arrays.asList(EXCEL_EXTENSION).contains(extension)) {
            list = ExcelUtil.getData(file);
        } else if (DAT_EXTENSION.equalsIgnoreCase(extension)) {
            list = DatUtil.readDat(file);
        } else {
            return Result.error("文件格式不正確");
        }
        if (list.isEmpty()) {
            return Result.error("操作失敗,請(qǐng)重試");
        }
        helmetService.batchAdd(list);
        return Result.success("操作成功,請(qǐng)點(diǎn)擊【下載文件】");
    }
}

數(shù)據(jù)庫(kù)根據(jù)最后的查詢sql創(chuàng)建一個(gè)視圖(View),通過(guò)mybatis對(duì)這個(gè)試圖進(jìn)行查詢,然后把結(jié)構(gòu)進(jìn)行導(dǎo)出即可。

ExcelUtil.getData()內(nèi)容

public static List<CellItem> getData(MultipartFile file) {
    InputStream inputStream = null;
    List<CellItem> rowList = new ArrayList<>();
    try {
        inputStream = file.getInputStream();
        XSSFWorkbook wb = new XSSFWorkbook(inputStream);

        int ignoreRows = 0;
        int sheetNum = wb.getNumberOfSheets();
        //for循環(huán):取前N個(gè)表,下標(biāo)從0開(kāi)始
        for (int i = 0; i < sheetNum; i++) {
            XSSFSheet sheetI = wb.getSheetAt(i);
            //列數(shù)
            int cellSize = sheetI.getRow(0).getLastCellNum();
            //第N+1行開(kāi)始,可以通過(guò)傳參,從第N+1行開(kāi)始取
            for (int rowIndex = ignoreRows; rowIndex <= sheetI.getLastRowNum(); rowIndex++) {
                XSSFRow row = sheetI.getRow(rowIndex);
                if (row == null) {
                    continue;
                }
                if (cellSize == 1) {
                    XSSFCell cell = row.getCell(0);
                    String cellValue = cell.getStringCellValue();
                    if (cellValue.contains(",")) {
                        CellItem item = new CellItem();
                        String[] cells = cellValue.split(",");
                        String deviceId = cells[1];
                        Boolean exists = checkExists(rowList, deviceId);
                        if (exists) {
                            continue;
                        }
                        item.setDeviceId(deviceId.trim());
                        item.setProductId(cells[2]);
                        item.setMac(cells[3]);
                        rowList.add(item);
                    }
                } else if (cellSize == 4){
                    //在每行中的每一列,從下標(biāo)1開(kāi)始,忽略第一列,一直取到所有
                    CellItem item = new CellItem();
                    String deviceId = row.getCell(1).getStringCellValue();
                    Boolean exists = checkExists(rowList, deviceId);
                    if (exists) {
                        continue;
                    }
                    item.setDeviceId(deviceId.trim());
                    item.setProductId(row.getCell(2).getStringCellValue());
                    item.setMac(row.getCell(3).getStringCellValue());
                    rowList.add(item);
                }
            }
        }
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        if (inputStream != null) {
            try {
                inputStream.close();
            } catch (Exception e) {
                log.error("文件流關(guān)閉失敗:{}", e.getMessage());
            }
        }
    }
    return rowList;
}

private static Boolean checkExists(List<CellItem> rowList, String key) {
    for (int i = 0; i < rowList.size(); i++) {
        CellItem item = rowList.get(i);
        if (item.getDeviceId().equals(key.trim())) {
            return Boolean.TRUE;
        }
    }
    return Boolean.FALSE;
}

DatUtil.readDat()

public static List<CellItem> readDat(MultipartFile file) {
    List<CellItem> list = new ArrayList<>();
    try (BufferedReader reader = new BufferedReader(new InputStreamReader(file.getInputStream()))) {
        String line;
        while ((line = reader.readLine()) != null) {
            String[] split = line.split(",");
            String deviceId = split[1];
            Boolean exists = checkExists(list, deviceId);
            if (exists) {
                continue;
            }
            CellItem item = new CellItem();
            item.setDeviceId(deviceId.trim());
            item.setMac(split[2]);
            item.setProductId(split[3]);
            list.add(item);
        }
    } catch (IOException e) {
        e.printStackTrace();
    }
    return list;
}

private static Boolean checkExists(List<CellItem> rowList, String key) {
    for (int i = 0; i < rowList.size(); i++) {
        CellItem item = rowList.get(i);
        if (item.getDeviceId().equals(key.trim())) {
            return Boolean.TRUE;
        }
    }
    return Boolean.FALSE;
}

導(dǎo)出的代碼這里省略了。

2.3 配置

application.yml

# 開(kāi)發(fā)環(huán)境配置
server:
  # 服務(wù)器的HTTP端口
  port: 8080
  servlet:
    # 應(yīng)用的訪問(wèn)路徑
    context-path: /

# Spring配置
spring:
  profiles:
    active: druid
  #thymeleaf 頁(yè)面的緩存開(kāi)關(guān)
  thymeleaf:
    enabled: true
    cache: true
    mode: HTML5
    encoding: utf-8
    suffix: .html
  # 文件上傳
  servlet:
    multipart:
      # 單個(gè)文件大小
      max-file-size: 10MB
      # 設(shè)置總上傳的文件大小
      max-request-size: 50MB

# MyBatis配置
mybatis:
  # 搜索指定包別名
  typeAliasesPackage: com.xxx.domain
  # 配置mapper的掃描,找到所有的mapper.xml映射文件
  mapperLocations: classpath:mapper/*.xml
  # 加載全局的配置文件
  configLocation: classpath:mybatis/mybatis-config.xml

# 日志配置
logging:
  level:
    com.xxx: info
    org.springframework: warn

數(shù)據(jù)庫(kù)配置application-druid.yml

# 數(shù)據(jù)源配置
spring:
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://172.16.1.2:3306/test?useUnicode=true&useSSL=false&allowLoadLocalInfile=false&autoReconnect=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&serverTimezone=GMT%2B8
    username: root
    password: root
    #Spring Boot 默認(rèn)是不注入這些屬性值的,需要自己綁定
    #druid 數(shù)據(jù)源專有配置
    initialSize: 5
    minIdle: 5
    maxActive: 20
    maxWait: 60000
    timeBetweenEvictionRunsMillis: 60000
    minEvictableIdleTimeMillis: 300000
    validationQuery: SELECT 1 FROM DUAL
    testWhileIdle: true
    testOnBorrow: false
    testOnReturn: false
    poolPreparedStatements: true

2.3 前端處理邏輯

layui的相關(guān)文件放到resources/static目錄,再新建一個(gè)index.html文件放入resources/templates目錄,這兩個(gè)目錄是thymeleaf默認(rèn)的,如果要修改可以在application.yml進(jìn)行配置。靜態(tài)文件如下:

在這里插入圖片描述

為了壓縮jar包的體積,把所有不必要的文件都精簡(jiǎn)掉了。

以下是index.html內(nèi)容

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
  <meta charset="UTF-8">
  <title>測(cè)試</title>
  <script th:src="@{/layui.js}"></script>
  <link rel="stylesheet" th:href="@{/css/layui.css}" rel="external nofollow"  media="all">
</head>
<body>
  <div class="layui-card">
    <div class="layui-card-header">操作面板</div>
    <div class="layui-card-body">
      <div class="layui-tab" lay-filter="window">
        <ul class="layui-tab-title">
          <li class="layui-this" lay-id="uploadTab">文件上傳</li>
          <li lay-id="downloadTab">文件下載</li>
        </ul>
        <div class="layui-tab-content">
          <div class="layui-tab-item layui-show">
            <form id="upload_form" class="layui-form" enctype="multipart/form-data">
              <div class="layui-form-item">
                <label class="layui-form-label">文件</label>
                <div class="layui-input-block">
                  <button type="button" class="layui-btn" id="upload">
                    <i class="layui-icon">&#xe61f;</i>選擇文件
                  </button>
                </div>
              </div>
              <div class="layui-form-item">
                <div class="layui-input-block">
                  <button id="btnSubmit" class="layui-btn" onclick="return false;">立即提交</button>
                </div>
              </div>
            </form>
          </div>
          <div class="layui-tab-item">
            <div class="layui-form-item">
              <label class="layui-form-label">文件</label>
              <div class="layui-input-block">
                <button type="button" class="layui-btn" id="downloadBtn">
                  <i class="layui-icon">&#xe601;</i>下載文件
                </button>
              </div>
            </div>
          </div>
        </div>
      </div>
    </div>
  </div>
</body>
</html>
<script>
    layui.use(['upload', 'layer', 'element'], function () {
        let $ = layui.jquery
            , layer = layui.layer
            , element = layui.element
            , upload = layui.upload;

        //執(zhí)行實(shí)例
        upload.render({
            elem: '#upload' //綁定元素
            , url: '/upload' //上傳接口
            , accept: 'file' //允許上傳的文件類型,不寫(xiě)默認(rèn)是圖片
            , acceptMime: ".xlsx,.xls,.DAT,.dat" //不寫(xiě)默認(rèn)驗(yàn)證圖片格式,一定要省略【exts】參數(shù)
            , auto: false //選擇文件后不自動(dòng)上傳
            , bindAction: '#btnSubmit' //指向一個(gè)按鈕觸發(fā)上傳
            , before: function (obj) {
                layer.load(); //上傳loading
            }
            ,done: function (res) {
                console.log(res)
                layer.closeAll('loading'); //關(guān)閉loading
                layer.alert(res.msg);
                if (res.code === 200) {
                    element.tabChange('window', 'downloadTab');
                }
            }
            , error: function (res) {
                console.error(res)
                layer.msg(res.msg);
                layer.closeAll('loading'); //關(guān)閉loading
            }
        });
        

        $("#downloadBtn").on('click', function () {
            location.href = "/download";
        })
    });
</script>

編輯好測(cè)試沒(méi)問(wèn)題直接打包放到服務(wù)器上執(zhí)行就可以啦。

三、實(shí)現(xiàn)效果

3.1 文件導(dǎo)入

在這里插入圖片描述

導(dǎo)入成功后會(huì)自動(dòng)切換到【文件下載】的tab頁(yè)

3.2 文件導(dǎo)出

在這里插入圖片描述

到此這篇關(guān)于springboot+thymeleaf+layui的實(shí)現(xiàn)示例的文章就介紹到這了,更多相關(guān)springboot thymeleaf layui內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • java連接zookeeper的實(shí)現(xiàn)示例

    java連接zookeeper的實(shí)現(xiàn)示例

    ZooKeeper官方提供了Java API,可以通過(guò)Java代碼來(lái)連接zookeeper服務(wù)進(jìn)行操作,本文就來(lái)介紹一下java連接zookeeper的實(shí)現(xiàn)示例,具有一定的參考價(jià)值,感興趣的可以了解一下
    2023-11-11
  • java 根據(jù)漢字生成拼音全拼或拼音首字母的示例

    java 根據(jù)漢字生成拼音全拼或拼音首字母的示例

    這篇文章主要介紹了java 根據(jù)漢字生成拼音全拼或拼音首字母的示例,幫助大家更好的利用Java處理數(shù)據(jù),感興趣的朋友可以了解下
    2020-11-11
  • Java面向?qū)ο笾畠?nèi)部類案例講解

    Java面向?qū)ο笾畠?nèi)部類案例講解

    這篇文章主要介紹了Java面向?qū)ο笾畠?nèi)部類案例講解,本篇文章通過(guò)簡(jiǎn)要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下
    2021-08-08
  • MyBatis-Plus中最簡(jiǎn)單的查詢操作教程(Lambda)

    MyBatis-Plus中最簡(jiǎn)單的查詢操作教程(Lambda)

    這篇文章主要給大家介紹了關(guān)于MyBatis-Plus中最簡(jiǎn)單的查詢操作的相關(guān)資料,文中通過(guò)實(shí)例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2022-03-03
  • SpringBoot后端解決跨域問(wèn)題的3種方案分享

    SpringBoot后端解決跨域問(wèn)題的3種方案分享

    這篇文章主要給大家分享介紹了關(guān)于SpringBoot后端解決跨域問(wèn)題的3種方案,跨域指的是瀏覽器不能執(zhí)行其他網(wǎng)站的腳本,它是由瀏覽器的同源策略造成的,是瀏覽器施加的安全限制,需要的朋友可以參考下
    2023-07-07
  • Springboot的spring-boot-maven-plugin導(dǎo)入失敗的解決方案

    Springboot的spring-boot-maven-plugin導(dǎo)入失敗的解決方案

    這篇文章主要介紹了Springboot的spring-boot-maven-plugin導(dǎo)入失敗的解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-07-07
  • Spring?Boot中@Autowired注入為空的原因以及解決方法

    Spring?Boot中@Autowired注入為空的原因以及解決方法

    最近在開(kāi)發(fā)中遇到了使用@Autowired注解自動(dòng)裝配時(shí)會(huì)報(bào)空指針,發(fā)現(xiàn)對(duì)象并沒(méi)有裝配進(jìn)來(lái),下面這篇文章主要給大家介紹了關(guān)于Spring?Boot中@Autowired注入為空的原因以及解決方法,需要的朋友可以參考下
    2024-01-01
  • Java 封裝的使用詳解

    Java 封裝的使用詳解

    在面向?qū)ο蟪淌皆O(shè)計(jì)方法中,封裝(英語(yǔ):Encapsulation)是指一種將抽象性函式接口的實(shí)現(xiàn)細(xì)節(jié)部分包裝、隱藏起來(lái)的方法。封裝可以被認(rèn)為是一個(gè)保護(hù)屏障,防止該類的代碼和數(shù)據(jù)被外部類定義的代碼隨機(jī)訪問(wèn)。要訪問(wèn)該類的代碼和數(shù)據(jù),必須通過(guò)嚴(yán)格的接口控制
    2021-11-11
  • JavaWeb開(kāi)發(fā)中alias攔截器的使用方法

    JavaWeb開(kāi)發(fā)中alias攔截器的使用方法

    本文給大家介紹在JavaWeb開(kāi)發(fā)中alias攔截器的使用方法相關(guān)知識(shí),本文介紹的非常詳細(xì),具有參考借鑒價(jià)值,感興趣的朋友一起看下吧
    2016-08-08
  • SpringBoot?整合?Spring-Session?實(shí)現(xiàn)分布式會(huì)話項(xiàng)目實(shí)戰(zhàn)

    SpringBoot?整合?Spring-Session?實(shí)現(xiàn)分布式會(huì)話項(xiàng)目實(shí)戰(zhàn)

    本文主要介紹了SpringBoot?整合?Spring-Session?實(shí)現(xiàn)分布式會(huì)話項(xiàng)目實(shí)戰(zhàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2022-07-07

最新評(píng)論