今天产品提了一个需求,要求用户上传图片进行剪切,上网搜了一下 cropperjs 挺不错,官网api不怎么看得懂,记录一下使用方法

我使用的是vue,移动端的项目。

官网地址:cropperjs

GitHub地址:https://github.com/fengyuanchen/cropperjs/blob/master/README.md

先看效果图,不然没有吸引力啊!!哈哈

这里只记录使用方法,至于怎么实现和要修改一些参数,大家前往官网api了解。

首先我们先安装 npm install cropperjs exif-js

新建一个文件 存放cropperjs 处理图片的方法(我放到了 static 文件下的 clipper.js 文件下了)

import Cropper from ‘cropperjs‘
import Exif from ‘exif-js‘
export default {
  install( Vue ){
    //初始化方法
    Vue.prototype.initilize = function( opt ){
      let self = this;
      this.options = opt;
      //创建dom
      this.createElement();
      this.resultObj = opt.resultObj;
      //初始化裁剪对象
      this.cropper = new Cropper( this.preview , {
        aspectRatio : opt.aspectRatio || 1 ,
        autoCropArea : opt.autoCropArea || 0.8 ,
        viewMode : 1,
        guides : opt.aspectRatio == ‘Free‘ ? false : true ,
        cropBoxResizable : opt.aspectRatio == ‘Free‘ ? false : true ,
        cropBoxMovable : opt.aspectRatio == ‘Free‘ ? false : true ,
        dragCrop : opt.aspectRatio == ‘Free‘ ? false : true ,
        background : false,
        checkOrientation : true ,
        checkCrossOrigin : true ,
        zoomable : false,
        zoomOnWheel : false ,
        center : false ,
        toggleDragModeOnDblclick : false ,
        ready : function () {
          // console.log(self.cropper.rotate(90))
          if( opt.aspectRatio == ‘Free‘ ){
            let cropBox = self.cropper.cropBox;
            cropBox.querySelector(‘span.cropper-view-box‘).style.outline = ‘none‘;
            self.cropper.disable();
          }
        }
      });
    }
    //创建一些必要的DOM,用于图片裁剪
    Vue.prototype.createElement = function () {
      //初始化图片为空对象
      this.preview = null;

      let str = ‘<div><img id="clip_image" src="originUrl"></div><button type="button" id="cancel_clip">取消</button><button type="button" id="clip_button">确定</button>‘;
      str+= ‘<div class="crop_loading"><div class="crop_content"><img src="../static/loading.gif"><div class="crop_text">图片上传中</div></div></div>‘;
      str+= ‘<div class="crop_success"><div class="crop_success_text">上传成功</div></div></div>‘;

      let body = document.getElementsByTagName(‘body‘)[0];
      this.reagion = document.createElement(‘div‘);
      this.reagion.id = ‘clip_container‘;
      this.reagion.className = ‘container‘;
      this.reagion.innerHTML = str;
      //添加创建好的DOM元素
      body.appendChild(this.reagion);
      this.preview = document.getElementById(‘clip_image‘);

      //绑定一些方法
      this.initFunction();
    }
    //初始化一些函数绑定
    Vue.prototype.initFunction = function  () {
      let self =this;
      this.clickBtn = document.getElementById(‘clip_button‘);
      this.cancelBtn = document.getElementById(‘cancel_clip‘);
      //确定事件
      this.addEvent( this.clickBtn , ‘click‘ , function () {
        self.crop();
      })
      //取消事件
      this.addEvent( this.cancelBtn , ‘click‘ , function () {
        self.destoried();
      })
      //清空input的值
      this.addEvent( this.fileObj , ‘click‘ , function () {
        this.value = ‘‘;
      })
    }

    //外部接口,用于input[‘file‘]对象change时的调用
    Vue.prototype.clip = function ( e , opt ) {
      let self = this;

      this.fileObj = e.srcElement;

      let files = e.target.files || e.dataTransfer.files;

      if (!files.length) return false;  //不是图片直接返回

      //调用初始化方法
      this.initilize( opt );

      //获取图片文件资源
      this.picValue = files[0];

      //去获取拍照时的信息,解决拍出来的照片旋转问题
      // Exif.getData( files[0] , function(){
      //   self.Orientation = Exif.getTag( files[0], ‘Orientation‘);
      //   console.log(self.Orientation)
      // });

      //调用方法转成url格式
      this.originUrl = this.getObjectURL( this.picValue );

      //每次替换图片要重新得到新的url
      if(this.cropper){
        this.cropper.replace(this.originUrl);
      }

    }
    //图片转码方法
    Vue.prototype.getObjectURL = function(file) {
      let url = null ;
      if (window.createObjectURL!=undefined) { // basic
        url = window.createObjectURL(file) ;
      } else if (window.URL!=undefined) { // mozilla(firefox)
        url = window.URL.createObjectURL(file) ;
      } else if (window.webkitURL!=undefined) { // webkit or chrome
        url = window.webkitURL.createObjectURL(file) ;
      }
      return url ;
    }
    //点击确定进行裁剪
    Vue.prototype.crop  = function () {
      let self = this;
      let image = new Image();
      let croppedCanvas;
      let roundedCanvas;

      // Crop
      document.querySelector(‘.crop_loading‘).style.display = ‘block‘;

      setTimeout(function () {
        croppedCanvas = self.cropper.getCroppedCanvas();
        // Round
        roundedCanvas = self.getRoundedCanvas(croppedCanvas);

        let imgData = roundedCanvas.toDataURL();
        image.src = imgData;

        //判断图片是否大于100k,不大于直接上传,反之压缩
        if( imgData.length < (100 * 1024) ){
          self.resultObj.src = imgData;
          //图片上传
          self.postImg( imgData );

        }else{
          image.onload = function () {
            //压缩处理
            let data = self.compress( image , self.Orientation );
            self.resultObj.src = data;
            //图片上传
            self.postImg( data );
          }
        }
      },20)
    }
    //获取裁剪图片资源
    Vue.prototype.getRoundedCanvas = function(sourceCanvas) {

      let canvas = document.createElement(‘canvas‘);
      let context = canvas.getContext(‘2d‘);
      let width = sourceCanvas.width;
      let height = sourceCanvas.height;

      canvas.width = width;
      canvas.height = height;

      context.imageSmoothingEnabled = true;
      context.drawImage(sourceCanvas, 0, 0, width, height);
      context.globalCompositeOperation = ‘destination-in‘;
      context.beginPath();
      context.rect(0 , 0 , width , height );
      context.fill();

      return canvas;
    }
    //销毁原来的对象
    Vue.prototype.destoried = function () {
      let self = this;
      //移除事件
      this.removeEvent( this.clickBtn , ‘click‘ , null );
      this.removeEvent( this.cancelBtn , ‘click‘ , null );
      this.removeEvent( this.fileObj , ‘click‘ , null );
      //移除裁剪框
      this.reagion.parentNode.removeChild(this.reagion);

      //销毁裁剪对象
      this.cropper.destroy();
      this.cropper = null;
    }
    //图片上传
    Vue.prototype.postImg = function( imageData ) {
      //这边写图片的上传
      let self = this;
      self.destoried();

      window.setTimeout( function () {
        document.querySelector(‘.crop_loading‘).style.display = ‘none‘;
        document.querySelector(‘.crop_success‘).style.display = ‘block‘;
        //裁剪完后摧毁对象
          self.destoried();
      },3000)
    }

    //图片旋转
    Vue.prototype.rotateImg = function( img ,  direction , canvas ) {
      //最小与最大旋转方向,图片旋转4次后回到原方向
      const min_step = 0;
      const max_step = 3;
      if (img == null )return;
      //img的高度和宽度不能在img元素隐藏后获取,否则会出错
      let height = img.height;
      let width = img.width;
      let step = 2;
      if (step == null) {
        step = min_step;
      }
      if ( direction == ‘right‘) {
        step++;
        //旋转到原位置,即超过最大值
        step > max_step && (step = min_step);
      } else {
        step--;
        step < min_step && (step = max_step);
      }
      //旋转角度以弧度值为参数
      let degree = step * 90 * Math.PI / 180;
      let ctx = canvas.getContext(‘2d‘);
      switch (step) {
        case 0:
          canvas.width = width;
          canvas.height = height;
          ctx.drawImage(img, 0, 0);
          break;
        case 1:
          canvas.width = height;
          canvas.height = width;
          ctx.rotate(degree);
          ctx.drawImage(img, 0, -height);
          break;
        case 2:
          canvas.width = width;
          canvas.height = height;
          ctx.rotate(degree);
          ctx.drawImage(img, -width, -height);
          break;
        case 3:
          canvas.width = height;
          canvas.height = width;
          ctx.rotate(degree);
          ctx.drawImage(img, -width, 0);
          break;
      }
    }

    //图片压缩
    Vue.prototype.compress = function ( img , Orientation ) {
      let canvas = document.createElement("canvas");
      let ctx = canvas.getContext(‘2d‘);
      //瓦片canvas
      let tCanvas = document.createElement("canvas");
      let tctx = tCanvas.getContext("2d");
      let initSize = img.src.length;
      let width = img.width;
      let height = img.height;

      //如果图片大于四百万像素,计算压缩比并将大小压至400万以下
      let ratio;
      if ((ratio = width * height / 4000000) > 1) {
        console.log("大于400万像素")
        ratio = Math.sqrt(ratio);
        width /= ratio;
        height /= ratio;
      } else {
        ratio = 1;
      }
      canvas.width = width;
      canvas.height = height;
      //        铺底色
      ctx.fillStyle = "#fff";
      ctx.fillRect(0, 0, canvas.width, canvas.height);
      //如果图片像素大于100万则使用瓦片绘制
      let count;
      if ((count = width * height / 1000000) > 1) {
        count = ~~(Math.sqrt(count) + 1); //计算要分成多少块瓦片
        //            计算每块瓦片的宽和高
        let nw = ~~(width / count);
        let nh = ~~(height / count);
        tCanvas.width = nw;
        tCanvas.height = nh;
        for (let i = 0; i < count; i++) {
          for (let j = 0; j < count; j++) {
            tctx.drawImage(img, i * nw * ratio, j * nh * ratio, nw * ratio, nh * ratio, 0, 0, nw, nh);
            ctx.drawImage(tCanvas, i * nw, j * nh, nw, nh);
          }
        }
      } else {
        ctx.drawImage(img, 0, 0, width, height);
      }
      //修复ios上传图片的时候 被旋转的问题
      if( Orientation != "" && Orientation != 1){
        switch(Orientation){
          case 6://需要顺时针(向左)90度旋转
            this.rotateImg(img,‘left‘,canvas);
            break;
          case 8://需要逆时针(向右)90度旋转
            this.rotateImg(img,‘right‘,canvas);
            break;
          case 3://需要180度旋转
            this.rotateImg(img,‘right‘,canvas);//转两次
            this.rotateImg(img,‘right‘,canvas);
            break;
        }
      }
      //进行最小压缩
      let ndata = canvas.toDataURL( ‘image/jpeg‘ , 0.1);
      console.log(‘压缩前:‘ + initSize);
      console.log(‘压缩后:‘ + ndata.length);
      console.log(‘压缩率:‘ + ~~(100 * (initSize - ndata.length) / initSize) + "%");
      tCanvas.width = tCanvas.height = canvas.width = canvas.height = 0;

      return ndata;
    }

    //添加事件
    Vue.prototype.addEvent = function ( obj , type , fn ) {
      if( obj.addEventListener ){
        obj.addEventListener( type , fn , false );
      }else{
        obj.attachEvent( ‘on‘ + type , fn );
      }
    }
    //移除事件
    Vue.prototype.removeEvent = function ( obj , type , fn ) {
      if( obj.removeEventListener ){
        obj.removeEventListener( type , fn , false );
      }else{
        obj.detachEvent( ‘on‘ + type , fn );
      }
    }
  }
}

在main中引入clipper文件,调用vue,use(clipper)方法使用

// The Vue build version to load with the `import` command
// (runtime-only or standalone) has been set in webpack.base.conf with an alias.
import Vue from ‘vue‘
import App from ‘./App‘
import router from ‘./router‘
import clipper from ‘../static/clipper‘

Vue.config.productionTip = false

Vue.use(clipper)

/* eslint-disable no-new */
new Vue({
  el: ‘#app‘,
  router,
  template: ‘<App/>‘,
  components: { App }
})

组件中调用clipper.js中的方法

<template>
    <div class="hello">
        <img src="./assets/upload.png"  id="img">
        <input type="file" accept="image/jpeg,image/png,image/jpeg" @change="change($event)">
    </div>
</template>

<script>
export default{
  data(){
    return{

    }
  },
  methods:{
    change(event){
      let image=document.getElementById(‘img‘);
      this.clip(event,{
        resultObj:image,
        aspectRatio:1,
      })
    }
  }
}
</script>

 

然后加上剪切页面的样式,可以放在app.vue中

#clip_button {
  position: absolute;
  right: 10%;
  bottom: 20px;
  width: 80px;
  height: 40px;
  border:none;
  border-radius: 2px;
  background: #1AAD19;
  color: #fff;
}
#cancel_clip{
  position: absolute;
  left: 10%;
  bottom: 20px;
  width: 80px;
  height: 40px;
  border:none;
  border-radius: 2px;
  color: #fff;
  background:#E64340;
}
#clip_container.container {
  z-index: 99;
  position: fixed;
  padding-top: 60px;
  left: 0;
  top: 0;
  right: 0;
  bottom: 0;
  background:rgba(0,0,0,1);
}
#clip_container.container > div{
  position: absolute;
  width: 100%;
  height: 100%;
  top: 50%;
  left: 50%;
  -webkit-transform: translate(-50%,-50%);
  transform: translate(-50%,-50%);
}
#clip_image {
  max-width: 100%;
}

.cropper-container {
  font-size: 0;
  line-height: 0;
  position: relative;
  -webkit-user-select: none;
  -moz-user-select: none;
  -ms-user-select: none;
  user-select: none;
  direction: ltr;
  -ms-touch-action: none;
  touch-action: none
}
.crop_loading , .crop_success {
  display: none;
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  z-index: 9;
}
.crop_loading .crop_content{
  position: absolute;
  top: 50% ;
  left: 50%;
  text-align: center;
  background: #000;
  opacity: 0.9;
  height: 100px;
  width: 100px;
  vertical-align: middle;
  color: #fff;
  padding-top: 20px;
  font-size: 16px;
  -webkit-border-radius:3px;
  border-radius:3px;
  -webkit-transform: translate(-50%,-50%);
  transform: translate(-50%,-50%);
}
.crop_loading .crop_content img{
  margin-top: 15px;
  margin-bottom: 10px;
}
.crop_success .crop_success_text{
  position: absolute;
  top: 50% ;
  left: 50%;
  text-align: center;
  background: #000;
  opacity: 0.9;
  width: 120px;
  height: 30px;
  color: #fff;
  line-height: 30px;
  font-size: 16px;
  -webkit-border-radius:3px;
  border-radius:3px;
  -webkit-transform: translate(-50%,-50%);
  transform: translate(-50%,-50%);
}
.cropper-container img {
  /* Avoid margin top issue (Occur only when margin-top <= -height) */
  display: block;
  min-width: 0 !important;
  max-width: none !important;
  min-height: 0 !important;
  max-height: none !important;
  width: 100%;
  height: 100%;
  image-orientation: 0deg
}

.cropper-wrap-box,
.cropper-canvas,
.cropper-drag-box,
.cropper-crop-box,
.cropper-modal {
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
}

.cropper-wrap-box {
  overflow: hidden;
}

.cropper-drag-box {
  opacity: 0;
  background-color: #fff;
}

.cropper-modal {
  opacity: .5;
  background-color: #000;
}

.cropper-view-box {
  display: block;
  overflow: hidden;

  width: 100%;
  height: 100%;

  outline: 1px solid #39f;
  outline-color: rgba(51, 153, 255, 0.75);
}

.cropper-dashed {
  position: absolute;

  display: block;

  opacity: .5;
  border: 0 dashed #eee
}

.cropper-dashed.dashed-h {
  top: 33.33333%;
  left: 0;
  width: 100%;
  height: 33.33333%;
  border-top-width: 1px;
  border-bottom-width: 1px
}

.cropper-dashed.dashed-v {
  top: 0;
  left: 33.33333%;
  width: 33.33333%;
  height: 100%;
  border-right-width: 1px;
  border-left-width: 1px
}

.cropper-center {
  position: absolute;
  top: 50%;
  left: 50%;

  display: block;

  width: 0;
  height: 0;

  opacity: .75
}

.cropper-center:before,
.cropper-center:after {
  position: absolute;
  display: block;
  content: ‘ ‘;
  background-color: #eee
}

.cropper-center:before {
  top: 0;
  left: -3px;
  width: 7px;
  height: 1px
}

.cropper-center:after {
  top: -3px;
  left: 0;
  width: 1px;
  height: 7px
}

.cropper-face,
.cropper-line,
.cropper-point {
  position: absolute;

  display: block;

  width: 100%;
  height: 100%;

  opacity: .1;
}

.cropper-face {
  top: 0;
  left: 0;

  background-color: #fff;
}

.cropper-line {
  background-color: #39f
}

.cropper-line.line-e {
  top: 0;
  right: -3px;
  width: 5px;
  cursor: e-resize
}

.cropper-line.line-n {
  top: -3px;
  left: 0;
  height: 5px;
  cursor: n-resize
}

.cropper-line.line-w {
  top: 0;
  left: -3px;
  width: 5px;
  cursor: w-resize
}

.cropper-line.line-s {
  bottom: -3px;
  left: 0;
  height: 5px;
  cursor: s-resize
}

.cropper-point {
  width: 5px;
  height: 5px;

  opacity: .75;
  background-color: #39f
}

.cropper-point.point-e {
  top: 50%;
  right: -3px;
  margin-top: -3px;
  cursor: e-resize
}

.cropper-point.point-n {
  top: -3px;
  left: 50%;
  margin-left: -3px;
  cursor: n-resize
}

.cropper-point.point-w {
  top: 50%;
  left: -3px;
  margin-top: -3px;
  cursor: w-resize
}

.cropper-point.point-s {
  bottom: -3px;
  left: 50%;
  margin-left: -3px;
  cursor: s-resize
}

.cropper-point.point-ne {
  top: -3px;
  right: -3px;
  cursor: ne-resize
}

.cropper-point.point-nw {
  top: -3px;
  left: -3px;
  cursor: nw-resize
}

.cropper-point.point-sw {
  bottom: -3px;
  left: -3px;
  cursor: sw-resize
}

.cropper-point.point-se {
  right: -3px;
  bottom: -3px;
  width: 20px;
  height: 20px;
  cursor: se-resize;
  opacity: 1
}

@media (min-width: 768px) {

  .cropper-point.point-se {
    width: 15px;
    height: 15px
  }
}

@media (min-width: 992px) {

  .cropper-point.point-se {
    width: 10px;
    height: 10px
  }
}

@media (min-width: 1200px) {

  .cropper-point.point-se {
    width: 5px;
    height: 5px;
    opacity: .75
  }
}

.cropper-point.point-se:before {
  position: absolute;
  right: -50%;
  bottom: -50%;
  display: block;
  width: 200%;
  height: 200%;
  content: ‘ ‘;
  opacity: 0;
  background-color: #39f
}

.cropper-invisible {
  opacity: 0;
}

.cropper-bg {
  background-image: url(‘data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAAA3NCSVQICAjb4U/gAAAABlBMVEXMzMz////TjRV2AAAACXBIWXMAAArrAAAK6wGCiw1aAAAAHHRFWHRTb2Z0d2FyZQBBZG9iZSBGaXJld29ya3MgQ1M26LyyjAAAABFJREFUCJlj+M/AgBVhF/0PAH6/D/HkDxOGAAAAAElFTkSuQmCC‘);
}

.cropper-hide {
  position: absolute;

  display: block;

  width: 0;
  height: 0;
}

.cropper-hidden {
  display: none !important;
}

.cropper-move {
  cursor: move;
}

.cropper-crop {
  cursor: crosshair;
}

.cropper-disabled .cropper-drag-box,
.cropper-disabled .cropper-face,
.cropper-disabled .cropper-line,
.cropper-disabled .cropper-point {
  cursor: not-allowed;
}

恭喜你又成功,搬运了一次代码!!!

clipper

原文地址:https://www.cnblogs.com/liangziaha/p/11962061.html

时间: 2024-08-25 08:12:49

今天产品提了一个需求,要求用户上传图片进行剪切,上网搜了一下 cropperjs 挺不错,官网api不怎么看得懂,记录一下使用方法的相关文章

一个需求变更的故事

昨天我们的物流部门提了一个需求,希望我能为他们做一张出库明细报表,以便他们统计和核对数据.嗯嗯,这个很简单的说,复制一个类似的模板,替换下数据源,按日期分组,20分钟搞定! 这里简单插一下,介绍下我们系统中的报表的实现.报表是采用的第三方控件FastReport,通过设计报表模板->定义报表(选择模板.分期规则.会计主体.报送对象)->生成报表(即时.按分期规则自动). 物流部的同事用即时报表功能看过后提了个新的需求或者是BUG,没有按仓库分组呀!亲! 汗呀,疏忽了,赶紧加上,5分钟搞定!然后

以终为始,一个需求的养成史

需求是需要养的,一个需求从诞生到面向用户,需要经历重重锤炼. 下面就从一个图来看一下一个需求究竟要经历哪些曲折才能够直接或者间接的作用于用户. 当时我刚从测试转到产品的时候,我的上一任产品经理因为项目上的调整需要把工作重心调整到其他项目,所以他原本的项目就直接给了我来负责(我们组长全程监督,以免出纰漏).新手一上来,就要独立承接项目,虽然组长全程监督,但也只是会在关键节点上来进行把关,很多细节上的东西怎么做都需要自己去摸索.由我负责发的第一个版本,真的是手忙脚乱,不过也幸好高压之下完整的走了一遍

码农的产品思维培养第2节----一个需求的奋斗史(人人都是产品经理)

今天我们继续坚持每日一节的产品思维培养,我喜欢在纸上画,喜欢做笔记.不是为了自己后面回去看,而是为了当时更好理解.不知道大家是否认同这点. 今天看到苏杰的一句话,其实和我之前讲过的是一致的,看来英雄所见略同,还是给大家分享一下"和学习任何领域的知识一样,建议大家在了解了知识框架之后,坚持"需求驱动学习"". 第二章,讲述的是一个需求的奋斗史.其实就是描述如何从用户那里得到需求,得到需求后如何处理的一个过程.今天,我们这一节讲如何从用户那里拿到需求. 用户研究,或者说

从产品和用户角度,思考需求和用户体验

需求来源于生活,用户体验是为了相对核心的需求而产生的新需求,需求的极致就是把一种需求变成习惯. 在产品这个圈子里,经常会遇到两个词,一个叫需求,另一个叫用户体验.在我看的各类产品相关的书籍和文章中,这两个词的出现率也是相当高的.经常在跟别人聊天的时候,也把这几个词挂在嘴边,忽悠忽悠.随着自己的成长和自己眼界的拓展,渐渐地理解了这两个词的意义. 一.需求的思考 从产品人的角度,需求就是产品人能给用户解决的问题.从用户的角度来说,需求就是用户需要产品人解决的问题. 举个例子说,假如某公司PM设计了一

产品经理如何做好需求挖掘

一.什么是需求 产品是用来解决用户的问题的,那么这些问题就是需求 例子:饿了么,解决用户不想出门吃饭,节省吃饭时间的需求 从互联网产品角度出发,需求就是:用户真正需要的,而不是用户想要的. 例子:我想要一匹更快的马,真正需要的其实是一个更快的交通工具. 二.为什么要做需求挖掘 挖掘到有价值的需求产品才更有前景 需求挖掘不够,容易产生拍脑袋需求,对公司发展不利 三.如何做好需求挖掘 来自用户:挖掘用户内心的想法和意图 来自竞品:竞品的功能给我们产生的启发 来自文献:文献提供的数据和趋势指明产品改进

产品经理思维模型拆解——需求篇

文章地址:http://www.woshipm.com/pmd/357813.html.产品的设计就是产品思维的一种体现,通过场景化的需求分析方式.可以让你从面,到线,到点,去逐个做好每一块分组的功能.每一个分组里面,考虑会有哪些东西,为什么会有这些东西,然后再考虑,每一个功能事件,会引发什么样的后果,应该给用户怎样的一个反馈. 一.产品经理是个啥? 一句话:在平衡商业目标与用户体验的情况下,解决某些用户,某些场景下的某些需求的人. 再简单点说,产品经理就是解决问题的人. 二.一款产品的立足点是

读书笔记-一个需求的奋斗史

一个需求的奋斗史,到二级目录 需求来源于用户,走向用户,首先讲的便是用户. 如何采集需求 需求分析的过程 获得高层支持的过程 需求管理

基于Servlet、JSP、JDBC、MySQL的一个简单的用户注冊模块(附完整源代码)

近期看老罗视频,做了一个简单的用户注冊系统.用户通过网页(JSP)输入用户名.真名和password,Servlet接收后通过JDBC将信息保存到MySQL中.尽管是个简单的不能再简单的东西,但麻雀虽小,五脏俱全,在此做一归纳和整理.以下先上源代码: 一.index.jsp <%@ page language="java" import="java.util.*" pageEncoding="utf-8"%> <% String

编写一个程序,用户输入两个数,求出其加减乘除,并用消息框显示计算结果

编写一个程序,用户输入两个数,求出其加减乘除,并用消息框显示计算结果 import javax.swing.JOptionPane; public class Test{ public static void main(String[] args) { int n1=Integer.parseInt(JOptionPane.showInputDialog("Input number 1: ")); int n2=Integer.parseInt(JOptionPane.showInpu