博客

  • docker 常用命令

    docker 常用命令

     

    docker 使用时 常用的命令

    了解指定的 Docker 命令使用方法
    docker command –help

    搜索镜像
    docker search ubuntu

    拉取镜像
    docker pull ubuntu:22.10

    推送镜像到Docker仓库
    docker push chunblog/ubuntu:20220809

    将容器保存为镜像
    docker commit -a “chunblog” -m “ubuntu:22.10 update” f8ccd6d9c225 ubuntu:20220809

    -a:指定镜像作者
    -m:描述信息
    f8ccd6d9c225:容器ID
    ubuntu:20220809:要创建的目标镜像名

    删除镜像
    docker rmi ubuntu:22.10

    运行容器
    docker run -it ubuntu:22.10 /bin/bash

    可用参数
    -d, –detach=false, 指定容器运行于前台还是后台,默认为false
    -i, –interactive=false, 打开STDIN,用于控制台交互
    -t, –tty=false, 分配tty设备,该可以支持终端登录,默认为false
    -u, –user=””, 指定容器的用户
    -a, –attach=[], 登录容器(必须是以docker run -d启动的容器)
    -w, –workdir=”” 指定容器的工作目录
    -c, –cpu-shares=0 设置容器CPU权重,在CPU共享场景使用
    -e, –env=[] 指定环境变量,容器中可以使用该环境变量
    -m, –memory=”” 指定容器的内存上限
    -P, –publish-all=false 指定容器暴露的端口
    -p, –publish=[] 指定容器暴露的端口
    -h, –hostname=”” 指定容器的主机名
    -v, –volume=[] 给容器挂载存储卷,挂载到容器的某个目录
    –volumes-from=[] 给容器挂载其他容器上的卷,挂载到容器的某个目录
    –cap-add=[] 添加权限,权限清单详见:http://linux.die.net/man/7/capabilities
    –cap-drop=[] 删除权限,权限清单详见:http://linux.die.net/man/7/capabilities
    –cidfile=”” 运行容器后,在指定文件中写入容器PID值,一种典型的监控系统用法
    –cpuset=”” 设置容器可以使用哪些CPU,此参数可以用来容器独占CPU
    –device=[] 添加主机设备给容器,相当于设备直通
    –dns=[] 指定容器的dns服务器
    –dns-search=[] 指定容器的dns搜索域名,写入到容器的/etc/resolv.conf文件
    –entrypoint=”” 覆盖image的入口点
    –env-file=[] 指定环境变量文件,文件格式为每行一个环境变量
    –expose=[] 指定容器暴露的端口,即修改镜像的暴露端口
    –link=[] 指定容器间的关联,使用其他容器的IP、env等信息
    –lxc-conf=[] 指定容器的配置文件,只有在指定–exec-driver=lxc时使用
    –name=”” 指定容器名字,后续可以通过名字进行容器管理,links特性需要使用名字
    –net=”bridge” 容器网络设置:
    bridge 使用docker daemon指定的网桥
    host 容器使用宿主机的网络
    container:NAME或ID 使用其他容器的网路,共享IP和PORT等网络资源
    none 容器使用自己的网络(类似–net=bridge) 但是不进行配置
    –privileged=false, 指定容器是否为特权容器,特权容器拥有所有的capabilities
    –restart=”no” 指定容器停止后的重启策略:
    no:容器退出时不重启
    on-failure:容器故障退出(返回值非零)时重启
    always:容器退出时总是重启
    –rm=false 指定容器停止后自动删除容器(不支持以docker run -d启动的容器)
    –sig-proxy=true 设置由代理接受并处理信号,但是SIGCHLD、SIGSTOP和SIGKILL不能被代理

    运行一个与宿主机公用网络的容器 就不需要映射端口了

    docker run –name chunblog –net=host –restart=always -dit -v /www/server/mysqlData:/www/server/mysqlData -v /www/backup:/www/backup -v /www/wwwlogs:/www/wwwlogs -v /www/wwwroot:/www/wwwroot chunblog/ubuntu:20220809

    不加–net=host时需要映射端口

    docker run –name chunblog –restart=always -dit -p 80:80 -p 443:443 -p 9706:9706 -p 888:888 -v /www/server/mysqlData:/www/server/mysqlData -v /www/backup:/www/backup -v /www/wwwlogs:/www/wwwlogs -v /www/wwwroot:/www/wwwroot chunblog/ubuntu:20220809

    查看运行中的容器
    docker ps

    查看所有容器
    docker ps -a

    启动容器
    docker start <容器ID>

    重启容器
    docker restart <容器ID>

    暂停容器
    docker pause <容器ID>

    取消暂停的容器
    docker unpause <容器ID>

    停止容器
    docker stop <容器ID>

    重启容器
    docker restart <容器ID>

    删除容器
    docker rm <容器ID>

    进入容器
    docker exec -it <容器ID> /bin/bash

    导出容器
    docker export <容器ID> > ubuntu.tar

    导入容器
    docker import ubuntu.tar chunblog/ubuntu:22.10

    查看容器运行日志
    docker logs <容器ID>

  • 使用 dockerfile 来构建自己的镜像

    使用 dockerfile 来构建自己的镜像

    Dockerfile 是一个文本文件,其内包含了一条条的 指令(Instruction),每一条指令构建一层,因此每一条指令的内容,就是描述该层应当如何构建。

    Dockerfile 示例

    #基础镜像
    FROM ubuntu
    #添加文件到镜像
    COPY ./GatewayWorkerNew /www/server/GatewayWorker
    COPY start.sh /home/start.sh
    COPY php.ini /www/server/php/74/etc/php.ini
    #构架镜像时运行命理
    RUN chmod +x /home/start.sh && \
        rm -f /install.sh
    
    #为镜像添加匿名卷
    VOLUME ["/www/server/mysqlData","/www/backup","/www/wwwlogs","/www/wwwroot"]
    #暴露端口
    EXPOSE 80 443 9706 888
    #容器启动命令
    CMD ["sh","/home/start.sh"]

    FROM 指定基础镜像

    所谓定制镜像,那一定是以一个镜像为基础,在其上进行定制。

    这里我使用 ubuntu 作为基础镜像,你可以指定版本号比如 FROM ubuntu:20.04 不指定时默认使用 latest 版本,基础镜像是必须指定的。而 FROM 就是指定 基础镜像,因此一个 Dockerfile 中 FROM 是必备的指令,并且必须是第一条指令。

    COPY 的文件/目录复制到镜像内

    格式 COPY [–chown=<user>:<group>] <源路径>… <目标路径>

    拷贝文件到指定目录
    COPY start.sh /home/start.sh

    拷贝目录
    COPY ./GatewayWorkerNew /www/server/GatewayWorker

    在使用该指令的时候还可以加上 –chown=<user>:<group> 选项来改变文件的所属用户及所属组。
    COPY –chown=55:mygroup files* /mydir/
    COPY –chown=bin files* /mydir/
    COPY –chown=1 files* /mydir/
    COPY –chown=10:11 files* /mydir/

    RUN 在定制镜像时是执行的命令行

    在使用RUN命理时应该把命令整合为一条,Dockerfile 中每一个指令都会建立一层镜像,所以应该把命令使用 && 链接为一条命令。
    RUN chmod +x /home/start.sh && \
    rm -f /install.sh

    VOLUME 定义匿名卷

    容器运行时应该尽量保持容器存储层不发生写操作,对于数据库类需要保存动态数据的应用,其数据库文件应该保存于卷(volume)中了,防止运行时用户忘记将动态文件所保存目录挂载为卷,在 Dockerfile 中,我们可以事先指定某些目录挂载为匿名卷,这样在运行时如果用户不指定挂载,其应用也可以正常运行,不会向容器存储层写入大量数据。

    VOLUME [“/www/server/mysqlData”,”/www/backup”,”/www/wwwlogs”,”/www/wwwroot”]

    VOLUME /www/server/mysqlData

    以上两种写法都是可以的

    EXPOSE 暴露端口

    格式为:EXPOSE <端口1> [<端口2>…]
    EXPOSE 指令是声明容器运行时提供服务的端口,这只是一个声明,在容器运行时并不会因为这个声明应用就会开启这个端口的服务。

    在 Dockerfile 中写入这样的声明有两个好处,一个是帮助镜像使用者理解这个镜像服务的守护端口,以方便配置映射;另一个用处则是在运行时使用随机端口映射时,也就是 docker run -P 时,会自动随机映射 EXPOSE 的端口。
    要将 EXPOSE 和在运行时使用 -p <宿主端口>:<容器端口> 区分开来。-p,是映射宿主端口和容器端口,换句话说,就是将容器的对应端口服务公开给外界访问,而 EXPOSE 仅仅是声明容器打算使用什么端口而已,并不会自动在宿主进行端口映射。

    CMD 容器启动命令

    Docker 不是虚拟机,容器就是进程。既然是进程,那么在启动容器的时候,需要指定所运行的程序及参数。CMD 指令就是用于指定默认的容器主进程的启动命令的。
    提到 CMD 就不得不提容器中应用在前台执行和后台执行的问题。这是初学者常出现的一个混淆。
    Docker 不是虚拟机,容器中的应用都应该以前台执行,而不是像虚拟机、物理机里面那样,用 systemd 去启动后台服务,容器内没有后台服务的概念。

    一些初学者将 CMD 写为:
    CMD service nginx start

    然后发现容器执行后就立即退出了。甚至在容器内去使用 systemctl 命令结果却发现根本执行不了。
    这就是因为没有搞明白前台、后台的概念,没有区分容器和虚拟机的差异,依旧在以传统虚拟机的角度去理解容器。

    对于容器而言,其启动程序就是容器应用进程,容器就是为了主进程而存在的,主进程退出,容器就失去了存在的意义,从而退出,其它辅助进程不是它需要关心的东西。

    而使用 service nginx start 命令,则是希望 upstart 来以后台守护进程形式启动 nginx 服务。而刚才说了 CMD service nginx start 会被理解为 CMD [ “sh”, “-c”, “service nginx start”],因此主进程实际上是 sh。那么当 service nginx start 命令结束后,sh 也就结束了,sh 作为主进程退出了,自然就会令容器退出。

    正确的做法是直接执行 nginx 可执行文件,并且要求以前台形式运行。比如:
    CMD [“nginx”, “-g”, “daemon off;”]

    使用dockerfile文件构造镜像

    docker build –pull –rm -f dockerfile -t ubuntu:20220809

    build参数说明

    –build-arg=[] :设置镜像创建时的变量;

    –cpu-shares :设置 cpu 使用权重;

    –cpu-period :限制 CPU CFS周期;

    –cpu-quota :限制 CPU CFS配额;

    –cpuset-cpus :指定使用的CPU id;

    –cpuset-mems :指定使用的内存 id;

    –disable-content-trust :忽略校验,默认开启;

    -f :指定要使用的Dockerfile路径;

    –force-rm :设置镜像过程中删除中间容器;

    –isolation :使用容器隔离技术;

    –label=[] :设置镜像使用的元数据;

    -m :设置内存最大值;

    –memory-swap :设置Swap的最大值为内存+swap,”-1″表示不限swap;

    –no-cache :创建镜像的过程不使用缓存;

    –pull :尝试去更新镜像的新版本;

    –quiet, -q :安静模式,成功后只输出镜像 ID;

    –rm :设置镜像成功后删除中间容器;

    –shm-size :设置/dev/shm的大小,默认值是64M;

    –ulimit :Ulimit配置。

    –squash :将 Dockerfile 中所有的操作压缩为一层。

    –tag, -t: 镜像的名字及标签,通常 name:tag 或者 name 格式;可以在一次构建中为一个镜像设置多个标签。

    –network: 默认 default。在构建期间设置RUN指令的网络模式

    Dockerfile 指令表

    指令 使用方法 描述
    FROM FROM ubuntu 指定基础镜像
    RUN RUN apt update -y 构架镜像时执行的命令
    COPY COPY /dir /mydir/                     COPY –chown=www:nginx /dir /mydir/ 文件/目录复制到镜像内 可指定文件的所属用户及所属组
    ADD ADD mydata.tar.gz /data                COPY /dir /mydir/                     COPY –chown=www:nginx /dir /mydir/ ADD 指令和 COPY 的格式和性质基本一致。但是在 COPY 基础上增加了一些功能。如果 <源路径> 为一个 tar 压缩文件的话,压缩格式为 gzip, bzip2 以及 xz 的情况下,ADD 指令将会自动解压缩这个压缩文件到 <目标路径> 去。另外需要注意的是,ADD 指令会令镜像构建缓存失效,从而可能会令镜像构建变得比较缓慢。因此在 COPY 和 ADD 指令中选择的时候,可以遵循这样的原则,所有的文件复制均使用 COPY 指令,仅在需要自动解压缩的场合使用 ADD。
    ENV ENV VERSION=1.0 DEBUG=on \
    NAME=”Happy Feet”
    ENV 设置环境变量
    ARG ARG DOCKER_USERNAME=ubuntu

    FROM ${DOCKER_USERNAME}/22.04            上面使用过以后就失效了,如果后面还需要使用需要再次指定

    ARG 构建参数 构建参数和 ENV 的效果一样,都是设置环境变量。所不同的是,ARG 所设置的构建环境的环境变量,在将来容器运行时是不会存在这些环境变量的。但是不要因此就使用 ARG 保存密码之类的信息,因为 docker history 还是可以看到所有值的
    VOLUME VOLUME [“/data1″,”/data2”] 容器运行时应该尽量保持容器存储层不发生写操作,对于数据库类需要保存动态数据的应用,其数据库文件应该保存于卷(volume)中,为了防止运行时用户忘记将动态文件所保存目录挂载为卷,在 Dockerfile 中,我们可以事先指定某些目录挂载为匿名卷,这样在运行时如果用户不指定挂载,其应用也可以正常运行,不会向容器存储层写入大量数据
    EXPOSE EXPOSE 80 443 EXPOSE 指令是声明容器运行时提供服务的端口,这只是一个声明,在容器运行时并不会因为这个声明应用就会开启这个端口的服务。在 Dockerfile 中写入这样的声明有两个好处,一个是帮助镜像使用者理解这个镜像服务的守护端口,以方便配置映射;另一个用处则是在运行时使用随机端口映射时,也就是 docker run -P 时,会自动随机映射 EXPOSE 的端口。
    要将 EXPOSE 和在运行时使用 -p <宿主端口>:<容器端口> 区分开来。-p,是映射宿主端口和容器端口,换句话说,就是将容器的对应端口服务公开给外界访问,而 EXPOSE 仅仅是声明容器打算使用什么端口而已,并不会自动在宿主进行端口映射。
    WORKDIR WORKDIR /home 指定工作目录,以后各层的当前目录就被改为指定的目录,如该目录不存在,WORKDIR 会帮你建立目录。
    USER USER redis 指定当前用户,改变之后层的执行 RUN, CMD 以及 ENTRYPOINT 这类命令的身份。
    LABEL LABEL org.opencontainers.image.authors=”chunblog” LABEL 指令用来给镜像以键值对的形式添加一些元数据(metadata)
    SHELL SHELL [“/bin/sh”, “-c”] SHELL 指令可以指定 RUN ENTRYPOINT CMD 指令的 shell
    ONBUILD ONBUILD RUN [ “npm”, “install” ] ONBUILD 是一个特殊的指令,它后面跟的是其它指令,比如 RUN, COPY 等,而这些指令,在当前镜像构建时并不会被执行。只有当以当前镜像为基础镜像,去构建下一级镜像的时候才会被执行。
    HEALTHCHECK HEALTHCHECK [选项] CMD <命令> 健康检查
    ENTRYPOINT ENTRYPOINT [ “ping”, “114.114.114.114” ] ENTRYPOINT 入口点,ENTRYPOINT 的目的和 CMD 一样,都是在指定容器启动程序及参数。ENTRYPOINT 在运行时也可以替代,不过比 CMD 要略显繁琐,需要通过 docker run 的参数 –entrypoint 来指定。
    CMD CMD [“bash”] 容器启动命令

     

  • 使用 acme.sh 部署 Let’s Encrypt 泛域名通用证书

    Let’s Encrypt两种域名验证方式

    1. DNS验证:配置一个随机的 TXT 记录来验证
    2. 文件验证:配置一个随机的 URL 地址来验证

    泛域名通用证书只能使用DNS TXT记录验证,acme.sh支持多种dnsapi我使用的是阿里云DNS,可以根据你的域名服务商进行选择支持列表。

    安装 acme.sh

    curl  https://get.acme.sh | sh -s email=my@example.com

    my@example.com 改为你自己的邮箱地址用于接收通知。

    acme.sh自动为你创建定时任务, 每天 0:00 点自动检测所有的证书, 如果快过期了, 需要更新, 则会自动更新证书。

    配置阿里云AK

    设置临时系统变量

    #你的阿里云AccessKeyId
    export Ali_Key="AccessKeyId"
    #你的AccessKeySecret
    export Ali_Secret="AccessKeySecret"

    颁发证书

    可以进行单域名、多域名、泛域名进行颁发。

    acme.sh --issue --dns dns_ali -d chunblog.com -d *.chunblog.com

    这里会使用dnsapi自动添加TXT记录,进行域名的 DNS 验证,中间会等待 2分钟左右来验证正确性,验证完成后记录会自动删除。

    出现Cert success表示成功 后面会有证书存放路径。

    复制/安装证书

    Nginx

    acme.sh --dns dns_ali -d chunblog.com -d *.chunblog.com\
     --install-cert\
     --key-file /www/cert/chunblog.com/key.pem\
     --fullchain-file /www/cert/chunblog.com/cert.pem\
     --reloadcmd "/etc/init.d/nginx reload"

    参数说明

    –install-cert    将颁发的证书安装到 apache/nginx 或任何其他服务器。

    –key-file    发行/续订后将密钥文件复制到的路径。

    –fullchain-file    发布/续订后将全链证书文件复制到的路径。

    –reloadcmd    发布/更新后重新加载服务器后要执行的命令

    然后修改Nginx配置文件使用 /www/cert/chunblog.com/key.pem、/www/cert/chunblog.com/cert.pem即可

    server
    {
        listen 80;
        listen 443 ssl http2;
        server_name chunblog.com www.chunblog.com;
        index index.php index.html index.htm;
        root /www/chunblog.com;
    
        ssl_certificate    /www/server/panel/vhost/cert/chunblog.com/fullchain.pem;
        ssl_certificate_key    /www/server/panel/vhost/cert/chunblog.com/privkey.pem;
        ssl_protocols TLSv1.1 TLSv1.2 TLSv1.3;
        ssl_ciphers EECDH+CHACHA20:EECDH+CHACHA20-draft:EECDH+AES128:RSA+AES128:EECDH+AES256:RSA+AES256:EECDH+3DES:RSA+3DES:!MD5;
        ssl_prefer_server_ciphers on;
        ssl_session_cache shared:SSL:10m;
        ssl_session_timeout 10m;
    
        #禁止访问的文件或目录
        location ~ ^/(\.user.ini|\.htaccess|\.git|\.svn|\.project|LICENSE|README.md)
        {
            return 404;
        }
        
        location ~ .*\.(gif|jpg|jpeg|png|bmp|swf)$
        {
            expires      30d;
            error_log /dev/null;
            access_log /dev/null;
        }
        
        location ~ .*\.(js|css)?$
        {
            expires      12h;
            error_log /dev/null;
            access_log /dev/null; 
        }
        access_log  /www/wwwlogs/chunblog.com.log;
        error_log  /www/wwwlogs/chunblog.com.error.log;
    }

    安装acme.sh时已自动创建定时任务,证书到期前会自动更新证书并重载Nginx配置使证书生效。

  • Linux mysql数据库每天0点自动备份

    通过定时任务实现每天0点整备份数据,并保存最近30天的备份,过期自动删除。

    第一步新建 my_dump.cnf 配置文件

    my_dump.cnf配置文件用于储存数据库账号密码,在此以root为例,内容如下

    [mysqldump]
    #备份用户
    user=root
    #备份用户密码
    password=root

    第二部新建脚本文件

    内容如下:

    #!/bin/bash
    DB_NAME='demo1' #要备份的数据库
    BACKUP_DIR='/data/backup/mysqldata/' # 备份保存目录
    CURRENT_TIME=`date +%Y%m%d%H%M`
    FILEPATH=$BACKUP_DIR$DB_NAME'_'$CURRENT_TIME'.sql.gz'
    echo '开始导出数据库...'
    #defaults-extra-file=/root/my_dump.cnf 第一步的配置文件路径
    mysqldump --defaults-extra-file=/root/my_dump.cnf $DB_NAME | gzip > $FILEPATH
    #删除30天前的配置文件
    find ${BACKUP_DIR} -mtime +30 -name "${DB_NAME}_*.sql.gz" -exec rm -f {} \;
    echo '导出成功,文件名为: '$FILEPATH

    第三步添加定时任务

    执行命令:crontab -e

    写入以下内容:

    0 0 * * *       /data/crontab/database_backup.sh #每天0点0分备份数据库

    至此数据库自动备份已配置完成!

    注意事项

    确认cron任务是启动状态,以Centos7 为例 输入 systemctl status crond 显示绿色状态

    输入crontab -l  确认你输入的内容已保存

  • Linux crontab实现秒级定时任务

    Linux crontab 定时任务最少到每分钟执行一次,一些需要频繁运行启动的应用场景无法满足。

    Linux crontab:

    *    *    *    *    *
    -    -    -    -    -
    |    |    |    |    |
    |    |    |    |    +----- day of week (0 - 7) (Sunday=0 or 7) OR sun,mon,tue,wed,thu,fri,sat
    |    |    |    +---------- month (1 - 12) OR jan,feb,mar,apr ...
    |    |    +--------------- day of month (1 - 31)
    |    +-------------------- hour (0 - 23)
    +------------------------- minute (0 - 59)

    crontab能到分钟级,我们可以在sh文件里写一个循环,然后每循环一次间隔多少秒执行下次循环。

    先不说了看代码

    #!/bin/sh
    step=2 #循环间隔的秒数
    #(i+step) 是每次循环i值+step 也就是每次循环+2 i到60停止循环
    for (( i = 0; i < 60; i=(i+step) )); do   
            /www/server/php/56/bin/php /www/wwwroot/crontab.php
    	/www/server/php/56/bin/php /www/wwwroot/crontab_1.php
    	/www/server/php/56/bin/php /www/wwwroot/crontab_2.php
    	/www/server/php/56/bin/php /www/wwwroot/crontab_3.php
    	/www/server/php/56/bin/php /www/wwwroot/crontab_4.php
    	/www/server/php/56/bin/php /www/wwwroot/crontab_5.php
    	/www/server/php/56/bin/php /www/wwwroot/crontab_6.php
    	/www/server/php/56/bin/php /www/wwwroot/crontab_7.php
        sleep $step
    done
    exit 0

    我在项目里使用这种方式定时任务稳定运行了有半年了,会有一点点的时间误差 因为中间PHP脚本运行需要耗时,但是这些误差可以忽略不计。

    crontab添加定时任务的时候需要根据脚本的几个数值做相应的时间调整

    比如我上面的间隔时间是2秒,每次循环i都会加2,当i到60的时候就会停止,crontab定时任务如下

    执行crontab -e 添加如下内容 每分钟运行一次

    */1 * * * * /data/apps/crontab/php_api_clear.sh

    比如你的任务需要5秒运行一次 间隔时间(step =5)循环 i 到180 停止 那么定时任务应设为每3分钟执行一次,

    脚本内容如下:

    #!/bin/sh
    step=5 #间隔的秒数
    for (( i = 0; i < 180; i=(i+step) )); do
            /www/server/php/56/bin/php /www/wwwroot/crontab.php
    	/www/server/php/56/bin/php /www/wwwroot/crontab_1.php
    	/www/server/php/56/bin/php /www/wwwroot/crontab_2.php
    	/www/server/php/56/bin/php /www/wwwroot/crontab_3.php
    	/www/server/php/56/bin/php /www/wwwroot/crontab_4.php
    	/www/server/php/56/bin/php /www/wwwroot/crontab_5.php
    	/www/server/php/56/bin/php /www/wwwroot/crontab_6.php
    	/www/server/php/56/bin/php /www/wwwroot/crontab_7.php
        sleep $step
    done
    exit 0

     

  • Nuxt 刷新使用富文本 报错 Document / Window not defined 究极解决方案

    安装方法:npm install -d vue-quill-editor quill vue-cropper

    官网:

    vue-quill-editor:https://quilljs.com/

    git:https://github.com/surmon-china/vue-quill-editor

    vue-cropper:https://github.com/xyxiao001/vue-cropper

    nuxt框架基于vue的服务端渲染,有报错 Document / Window not defined 的问题,使用富文本编辑器进行组件单独引入的时候会报错,在组件中使用

    if (process.client) {
      require('external_library')
    }

    之后element会显示不出来,所以在使用这种引入插件的时候建议直接在

    plugins文件夹内直接全局注册,使用服务器渲染
    plugins: [
        '@/plugins/element-ui',
        '@/plugins/bootstrap-vue',
        { src: '@/plugins/map', ssr: false },
        { src: '@/plugins/vue-quill-editor', ssr: false },
        { src: '@/plugins/vue-cropper', ssr: false }
      ],

    组件使用代码:(网上摘取)

    在使用上传的时候action是传输一个链接地址,如果想根据自己定义的axios上传需要添加一个auto-upload=”false”意思不取消自动上传更据:on-change=”onChange”(文件状态改变时的钩子,添加文件、上传成功和上传失败时都会被调用)动作进行上传

    <template>
      <div>
        <!-- 图片上传组件-->
        <el-upload
          :before-upload="beforeUpload"
          :show-file-list="false"
          accept="image/*"
          action=""
          class="avatar-uploader"
          hidden
          name="file"
          type='file'>
          <el-button size="small" type="primary">点击上传图片 到 文本编辑器</el-button>
        </el-upload>
        <!-- 编辑器组件-->
        <quill-editor
          :options="editorOption"
          @change="onEditorChange($event)"
          class="editor"
          ref="myQuillEditor"
          v-model="content">
        </quill-editor>
        <!-- 图片裁剪组件-->
        <el-dialog :visible.sync="isShowCropper" top="5vh">
          <VueCropper
            :autoCrop="option.autoCrop"
            :autoCropHeight="option.autoCropHeight"
            :autoCropWidth="option.autoCropWidth"
            :canScale="option.canScale"
            :fixed="option.fixed"
            :fixedNumber="option.fixedNumber"
            :img="option.img"
            :info="option.info"
            :outputSize="option.outputSize"
            :outputType="option.outputType"
            ref="cropper"
            style="height:600px;margin:20px 0"
          >
          </VueCropper>
          <br/>
    
          <el-button @click="onCubeImg()" type="primary">生成图片</el-button>
          <el-button @click="isShowCropper = false">取消</el-button>
        </el-dialog>
      </div>
    </template>
    <script>
      // 富文本工具栏配置
      const toolbarOptions = [
        ['bold', 'italic', 'underline', 'strike'], // 加粗 斜体 下划线 删除线
        ['blockquote', 'code-block'], // 引用  代码块
        [{ header: 1 }, { header: 2 }], // 1、2 级标题
        [{ list: 'ordered' }, { list: 'bullet' }], // 有序、无序列表
        [{ script: 'sub' }, { script: 'super' }], // 上标/下标
        [{ indent: '-1' }, { indent: '+1' }], // 缩进
        // [{'direction': 'rtl'}],                         // 文本方向
        [{ size: ['small', false, 'large', 'huge'] }], // 字体大小
        [{ header: [1, 2, 3, 4, 5, 6, false] }], // 标题
        [{ color: [] }, { background: [] }], // 字体颜色、字体背景颜色
        [{ font: [] }], // 字体种类
        [{ align: [] }], // 对齐方式
        ['clean'], // 清除文本格式
        ['link', 'image', 'video'] // 链接、图片、视频
      ]
    
    
      import 'quill/dist/quill.core.css'
      import 'quill/dist/quill.snow.css'
      import 'quill/dist/quill.bubble.css'
      import { uImage } from '@/api/uploadImage.js'
    
      export default {
        name: 'textEditor',
        components: {},
        props: {
          /*编辑器的内容*/
          value: {
            type: String
          },
          /*图片大小*/
          maxSize: {
            type: Number,
            default: 4000 //kb
          }
        },
        data() {
          return {
            // 富文本数据
            content: '',
            quillUpdateImg: false, // 根据图片上传状态来确定是否显示loading动画,刚开始是false,不显示
            editorOption: {
              theme: 'snow', // or 'bubble'
              placeholder: '请输入您想输入的内容',
              modules: {
                toolbar: {
                  container: toolbarOptions,
                  // 和上传按钮进行绑定
                  handlers: {
                    image: function(value) {
                      console.log(value)
                      if (value) {
                        // 触发input框选择图片文件
                        document.querySelector('.avatar-uploader input').click()
                      } else {
                        this.quill.format('image', false)
                      }
                    }
                  }
                }
              }
            },
            // 切图器数据
            option: {
              img: '',                         // 裁剪图片的地址
              info: true,                      // 裁剪框的大小信息
              outputSize: 1,                   // 裁剪生成图片的质量
              outputType: 'png',               // 裁剪生成图片的格式
              canScale: true,                 // 图片是否允许滚轮缩放
              autoCrop: true,                  // 是否默认生成截图框
              autoCropWidth: 150,              // 默认生成截图框宽度
              autoCropHeight: 150,             // 默认生成截图框高度
              fixed: true,                    // 是否开启截图框宽高固定比例
              fixedNumber: [4, 4]             // 截图框的宽高比例
            },
            isShowCropper: false,
            isClient: true,
            width: '150px',
            height: '150px'
          }
        },
        methods: {
          // 上传切图前调用
          beforeUpload(file) {
            /**
             * URL.createObjectURL(file)生成本地的url
             * 返回false终止了自动上传
             * */
            this.option.img = URL.createObjectURL(file)
            this.option.autoCropWidth = this.width
            this.option.autoCropHeight = this.height
            this.isShowCropper = true
            return false
          },
          // 确定裁剪图片
          onCubeImg() {
            // 获取cropper的截图的base64 数据data
            this.$refs.cropper.getCropData(data => {
              //截取bas64 截取base64的格式 和 图片的后缀名
              let baseSplit= data.split(',')
              let format = baseSplit[0].split('/')[1].split(';')[0]
              //获取base64格式的信息
              let base = ''
              if (process.client) {
                base = window.atob(baseSplit[1])
              }
              //转格式:base64转图片
              /**
               *格式为:File
               * lastModified: 1593134876792
               * lastModifiedDate: Fri Jun 26 2020 09:27:56 GMT+0800 (中国标准时间) {}
               * name: "img.png"
               * size: 15407
               * type: "image/png"
               * webkitRelativePath: ""
               * */
              let index = base.length
              let u8arr = new Uint8Array(index)
              while (index--) {
                u8arr[index] = base.charCodeAt(index)
              }
              let blods = new File([u8arr],'img.' + format,{type: 'image/' + format })
    
              /**
               * 建立一个formData表单,之后把上面的base64转换后的格式使用append传递给FormData
               * */
              let fromData = new FormData()
              fromData.append('file',blods)
    
              this.$notify({
                title: '成功',
                message: '正在上传中',
                type: 'success'
              });
              //发出网络请求
              uImage(fromData).then(res => {
                this.$notify({
                  title: '成功',
                  message: '图片上传成功',
                  type: 'success'
                });
                // 获取富文本组件实例
                let quill = this.$refs.myQuillEditor.quill
                if(res.code === 200){
                  // 获取光标所在位置
                  /**
                   *  quill.insertEmbed(length, 'image', res.data.url)
                   *  图片显示的位置,根据length后的位置显示
                   * */
                  if (quill.getSelection() && quill.getSelection().index) {
                    let length = quill.getSelection().index
                    quill.insertEmbed(length, 'image', res.data.url)
                  } else {
                    let length = 0
                    quill.insertEmbed(length, 'image', res.data.url)
                  }
                  // 调整光标到最后
                  quill.setSelection(length + 1)
                }else {
                  console.log(res.data.code)
                  this.$notify({
                    title: '警告',
                    message: '图片上传失败',
                    type: 'warning'
                  });
                }
                this.isShowCropper = false
                // 先将显示图片地址清空,防止重复显示
                this.option.img = ''
              })
            })
          },
          onEditorChange() {
    
            console.log(this.content)
            //富文本内容改变事件 发送给父级元素
            this.$emit('change', this.content)
          }
        },
        mounted() {
          setTimeout(() => {
            this.content = this.value
          }, 500)
        }
      }
    </script>
    <style lang="scss">
    
    </style>
    
    

     

  • Nuxt.js使用百度地图vue-baidu-map

    vue-baidu-map文档:https://dafrok.github.io/vue-baidu-map/#/zh/start/usage

    安装vue-baidu-map:npm i vue-baidu-map -D

    在plugins新建map.js:(名字是,map.js):

    import BaiduMap from 'vue-baidu-map'
    import Vue from 'vue'
    Vue.use(BaiduMap, {
      ak: '申请的百度地图密匙'
    })

    在页面中使用:

    <template>
      <div>
        <baidu-map class="bdwindow" :dragging="dragging" :center="center" :zoom="zoom" style="height:500px" :scroll-wheel-zoom='scroll'>
              <bm-info-window :position="center" :title="title" :show="show">
                  <p class="bdwtext" v-html="contents"></p>
              </bm-info-window>
        </baidu-map>
      </div>
    </template>
    <script>
    export default {
      data () {
            return {
                jump_path:"",
                center: {lng: 120.4373010751, lat: 23.1095638170},
                zoom: 15,  //缩放级别
                title:"标题",
                contents: '地址:具体地址信息',  //标签内容
                show: true,  //显示标签
                scroll:true,  //地图缩放
                dragging:true,  //地图拖拽
            }
        }
    }
    </script>