hellowoody / iql

issues db
0 stars 0 forks source link

blog #2

Open hellowoody opened 4 years ago

hellowoody commented 4 years ago

blog list

hellowoody commented 4 years ago

nodejs不同浏览器跳转问题

前提:配置好简单的nodejs和bootstrap的服务,让用户可以通过localhost:8001和localhost:8001/index.html都可以访问

fs.readFile("."+request.url,'utf-8',function(err,data)
            {
                if(err)
                {
                    throw err;
                }
                response.writeHead(200,{"Content-Type":{
                    ".html":"text/type",
                    ".css":"text/css",
                    ".js" :"application/javascript"
                }
                });
                response.write(data);
                response.end();
            })

火狐:两个入口都可以访问,样式也正常加载了。

ie:localhost:8001/index.html可以正常访问,localhost:8001是下载index.html页面

chrome:两个入口都可以访问,但样式加载不了

解决办法:

switch (ext)
   {
       case ".css":
       case ".js":
           fs.readFile("."+request.url,'utf-8',function(err,data){
               if(err)
               {
                   throw err;
               }
               response.writeHead(200,{"Content-Type":{
                   ".css":"text/css",
                   ".js" :"application/javascript"
               }[ext]
               });
               response.write(data);
               response.end();
           })
           break;
       default :
         fs.readFile("./index.html","utf-8",function(err,data)<br>                {<br>                    if(err) throw err;<br>  
hellowoody commented 4 years ago

windows下用wubi安装ubuntu14.04

第一步,下载分区助手,分出一个空白区来(注:以wubi这种方式装ubuntu最大为30G,所以分区时分10-30G之间即可)

第二步,从ubuntu官网上下载ubuntu14.04-desktop版本的iso。

第三步,用解压缩工具打开iso文件,注意不需要全部解压,只需将根目录下的wubi.exe解压出来即可。将ubuntu14.04-desktop.iso和wubi.exe放到第一步新分出来的区。

第四步,开始安装,注意安装时要将网络断开,因为在联网状态下安装,会重新下载ubuntu14.04-desktop.iso,浪费时间。断开网络,双击wubi.exe,弹出窗口选择安装的盘符,用户名和密码,点击确认。系统自动重启,等待安装完成

第五步,重启完之后,14.04会出现系统挂载的问题,进入不了图形界面的系统。我们需要开机,进入紫色的选择ubuntu启动的画面。按键盘E,进入编辑。找到“ro rootflags=sync”,改为“rw rootflags=sync”。再按F10启动。没错,改一个字母,系统就可以在wubi中启动了。

第六步,没错和你想的一样,这种方式只有一次有效。每次开机都要手动修改。但是我们可以通过修改配置文件的方式,使其永久有效。 (a)进入系统后,打开终端,执行 $sudo gedit /etc/grub.d/10_lupin 改动文件的第 150 行,把ro改成rw,保存。 linux ${rel_dirname}/${basename} root=${LINUX_HOST_DEVICE} loop=${loop_file_relative} ro ${args} //修改前 linux ${rel_dirname}/${basename} root=${LINUX_HOST_DEVICE} loop=${loop_file_relative} rw ${args} //修改后 (b)然后更新启动器的配置文件 $sudo update-grub

最后,安装结束

hellowoody commented 4 years ago

ubuntu14.04 配置jdk环境变量

第一步,下载tar.gz的jdk,

执行sudo tar zxvf ./jdk的名字.tar.gz -C 你要指定的路径

第二步,修改jdk的权限

sudo chmod 766 -R 你JDK的路

第三步,按“ctrl+alt+t”打开终端

第四步,输入sudo gedit /etc/profile,系统弹出profile文本文件,在最后加入下面的代码

set java environment

export JAVA_HOME=你JDK的路径 export JRE_HOME=${JAVA_HOME}/jre export CLASSPATH=.:${JAVA_HOME}/lib:4{JRE_HOME}/lib export PATH=${JAVA_HOME}/bin:$PATH

第五步,source /etc/profile 用于重新执行刚修改的初始或文件,使之立即生效

第六步,Ubuntu系统默认安装并使用OpenJDK(usr/lib/jvm/),因此需要手动修改系统默认的JDK sudo update-alternatives --install /usr/bin/java java 你JDK的路径/bin/java 300 sudo update-alternatives --install /usr/bin/javac javac 你JDK的路径/bin/javac 300 sudo update-alternatives --install /usr/bin/jar jar 你JDK的路径/bin/jar 300 sudo update-alternatives --install /usr/bin/javah javah 你JDK的路径/bin/javah 300 sudo update-alternatives --install /usr/bin/javap javap 你JDK的路径/bin/javap 300

第七步,sudo update-alternatives --config java

第八步,至此配置完成,输入java -version、javac或java检查是否配置成功。

hellowoody commented 4 years ago

ubuntu apt-get install xxx命令,下载的路径及清除方式

ubuntu或是其他linux系统,安装软件时会执行 sudo apt-get install 软件名称 。

观察命令行输出的内容,发现会从网上下载一些安装包,那有些人的问题就来了,下载的文件到哪去了,会不会随着安装东西越多,这些下载的安装包消耗硬盘空间。

首先,下载的安装包存到系统的/var/cache/apt/archives路径下

然后,如何清理他们,执行sudo apt-get clean

hellowoody commented 4 years ago

oracle 快速建表空间,用户,赋权

本文章并不是给初学者详细详解oracle下如何建立表空间,用户,赋权等命令参数及含义,本文章可能只是对有一定的oracle基础的朋友有所帮助。

最近项目中又开始需要oracle数据库,在搭建环境时,发现自己的oracle命令已经忘了差不多,所以我从网上找来一些语句:可以快速的建表空间,用户并且赋权,由于我现在的项目并不是很严谨,所以像临时表空间,oracle的权限分配等细节,在这里会被忽略。

由于10g开始,oracle的控制台改成网页形式,不但界面改变而且相对的速度也慢了许多,所以我喜欢用命令行的方式操作后台。

1.登陆oracle的sqlplus(windows和Linus都适用)

sqlplus /nolog

connect sysdba as sysdba

show user; //如果成功,输入该命令应该返回“SYS”

2.建表空间语句

CREATE TABLESPACE 表空间的名字 DATAFILE 'E:\oracle\product\10.1.0\oradata\表空间的名字.ORA' SIZE 表空间大小 AUTOEXTEND ON;

3.建立用户

create user 用户名 identified by 用户密码 default tablespace 表空间名字 (temporary TABLESPACE 临时表空间名字);

4.赋权

grant resource,connect,dba to 用户名; //将dba的权限赋给用户

5.配置oracle net manage后,就可以用PL/SQL Developer连接工具访问刚刚建好的表空间了。

hellowoody commented 4 years ago

oracle 导入导出(imp,exp)dump数据文件

需要注意的是,不同版本的oracle的导入导出应符合下面的规则:

不管是从低版本导到高版本,还是高版本导入低版本。导出时需要在原本的版本上导出dump文件,再在需要导入的版本上进行导入(有一些绕)。

1.进入windows的doc窗口(cmd)

2.导出dump文件

exp username/password@服务名 file=文件路径及文件名.dmp //这里的服务名指的是net manage里的连接名

导出某几张表

exp username/password@服务名 tables=table1,table2 file=文件路径及文件名.dmp

3.导入dump文件

imp username/password@服务名 file=文件路径及文件名.dmp full=y

导入某几张表

imp username/password@服务名 tables=table1,table2 file=文件路径及文件名.dmp full=y

hellowoody commented 4 years ago

linux的tar简单使用

tar -zxvf xxx.tar.gz -C 目的目录路径

hellowoody commented 4 years ago

oracle 写declare例子

注意:

1.赋值要用 :=

2.或的关系用 or 表示

declare
prefix VARCHAR2(2); --主表前缀
res_value VARCHAR2(20);
sysdate_char VARCHAR2(20);
begin
  prefix := 'QZ';
  select to_char(sysdate,'yyyyMMdd') into sysdate_char from dual;
  for i in (select col1,col2 from table_name ) loop
    if i.col1= 'col1' or i.col2= 'col2'  then
    res_value := '123456';
    end if;
  end loop;
  res_value := SUBSTR(res_value,0,2);  --截取字符串 从第一位(0和1都代表第一位)开始截取2为数
  dbms_output.put_line( res_value ); -- 输出语句
end;

最后输出 “12”

hellowoody commented 4 years ago

firefox浏览器设置新页面后激活

1.在firefox地址栏输入about:config

2.选择browser.tabs.loadDivertedInBackground,将值改为true

hellowoody commented 4 years ago

oracle下常用查询更新命令(身份证号判断男女,更新语句多表查询)

1.update数据

UPDATE VOL_BASEINFO b 
SET (b.vlbi_zzmmm, b.vlbi_mzm) = (select t.zz,t.mz from MY_ZHB_2014 t where t.xh= b.vlbi_xh)
where b.vlbi_xh like '14%';

2.根据身份证号判断男女 decode(mod(to_number(substr('身份证号', 17, 1)), 2), 0, '2', '1') a.先截取身份证的倒数第二位数字 b.被2整除 c.如果余数是0,则为2-女 d.如果余数是不为0,则为1-男

select decode(mod(to_number(substr('身份证号', 17, 1)), 2), 0, '2', '1')  
from dual

3.在sql中 insert into 中能插入select 语句,这里要求A和B的表结构是一样的

insert into A select * From B;

如果不一样,则需要使用:

insert into A(C1,C2,...) select C1,C2,... From B;
//这里C1、C2分别指A表与B表字段大小和类型都相同的列。
hellowoody commented 4 years ago

debian配置简单的vsftp服务器

安装vsftp

apt-get install update

apt-get install vsftpd

查看ftp是否启动

netstat -a|grep ftp*

ftp -localhost

hellowoody commented 4 years ago

win7 32位下装oracle 10g报未知错误

Oracle 10g版本是10201_database_win32,安装提示“程序异常终止,发生未知错误”。

1.修改Oracle 10G\database\stage\prereq\db\refhost.xml 在

后面添加 2.到install目录中找到oraparam.ini文件,把 \#Windows=4.0,5.0,5.1,5.2 修改成 \#Windows=4.0,5.0,5.1,5.2,6.1 并在后面添加 [Windows-6.1-required] \#Minimum display colours for OUI to run MIN_DISPLAY_COLORS=256 \#Minimum CPU speed required for OUI \#CPU=300 [Windows-6.1-optional] 3.右键setup.exe,属性->兼容性->以兼容模式运行这个程序 windows xp( service pack 3),以管理员身份运行安装就可以了。
hellowoody commented 4 years ago

sqlserver2005 存储过程模板及调用

本模板主要提供快速创建一个存储过程

本例子中包含:循环游标,事务

USE [数据库名称]
GO
/******    脚本日期: 11/25/2014 01:05:48 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE PROCEDURE [用户].[存储过程名称] 
    @epId varchar(20),
    @bizname varchar(150),
    @resValue varchar(2) OUTPUT
AS
BEGIN
    SET NOCOUNT ON;
    declare @epName varchar(500);
    declare @belongSepa varchar(6);
    declare @processinstid numeric(18, 0);
    declare @orgid numeric(10, 0);
    SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
        set @resValue = 0;
    declare order_cursor CURSOR LOCAL FORWARD_ONLY KEYSET SCROLL_LOCKS FOR select ep_name,belong_sepa,processinstid,orgid from ENTERPRISE where ep_id = @epId;    
    OPEN order_cursor
    --开始循环游标变量
    FETCH NEXT FROM order_cursor INTO @epName,@belongSepa,@processinstid,@orgid;
    WHILE (@@FETCH_STATUS = 0)
    BEGIN
    insert into BAK_OLD_EPINFO values (@epId,@epName,@processinstid,@orgid,@belongSepa,getdate());
    if (@@ERROR=0)
    BEGIN
        SET @resValue=0; --成功
        delete from ENTERPRISE where ep_id = @epId;
    END
    ELSE
    BEGIN
        SET @resValue=1; --失败
    END
    FETCH NEXT FROM order_cursor INTO @epName,@belongSepa,@processinstid,@orgid;    
    END
    CLOSE order_cursor
    DEALLOCATE order_cursor
    SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
END
set ANSI_NULLS ON
set QUOTED_IDENTIFIER ON
go

ALTER PROCEDURE [dbo].[deleteAMANDTPByAMID] 
    -- Add the parameters for the stored procedure here
    @amId varchar(20),
    @resValue varchar(2) OUTPUT
    --0 成功 1 失败 2 计划在审批
AS
BEGIN
    declare @tpid varchar(20);
    declare @epidcs varchar(20);
    declare @epidcz varchar(20);
    declare @processid numeric(18, 0);
    SET NOCOUNT ON;
    SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
    BEGIN
        select @tpid=tp_id,@epidcs=en_id_cs,@epidcz=en_id_cz,@processid=PROCESSINSTID from TRANSFER_PLAN where am_id=@amId;
        if (@processid is not null)
        BEGIN
            set @resValue = '2';
        END
        ELSE
        BEGIN
            insert BAK_OLD_AMTP (am_id,tp_id,ep_id_cs,ep_id_cz,sysdate) values (@amid,@tpid,@epidcs,@epidcz,getdate());
            if (@@ERROR=0)
            BEGIN
                set @resValue = '0';
                update AGREEMENT set status='1' where am_id=@amId;
                update TRANSFER_PLAN set status='1' where am_id=@amId;
            END
            ELSE
            BEGIN
                SET @resValue= '1'; --失败
            END
        END
    END
    SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
END

如何调用的语句例子

declare @epid varchar(50);
declare @epname varchar(500);
declare @resvalue varchar(500);
set @epid = '123456';
set @epname = 'xxxxxx';
exec [用户].[存储过程名称] @epid,@epname,@resvalue output
print @resvalue;
hellowoody commented 4 years ago

SqlServer和mysql几个不同之处(主要是存储过程的语法)

1.SqlServer中的类型bit(1)对应MySQL的tinyint(1)

2.SqlServer中用getdate()获取数据库系统当前时间;MySQL中NOW()获取数据库系统当前时间

3.SqlServer中转型需要用cast,mysql不需要用cast;

4.SqlServer存储过程中循环中退出需要用break,mysql用leave

hellowoody commented 4 years ago

debian下搭建邮件服务器

第一步

#apt-get update

#apt-get upgrade

#apt-get install postfix libsasl2-2 sasl2-bin libsasl2-modules dovecot-imapd dovecot-pop3d dovecot-common

第二步

配置邮件服务器

首先,停掉这三项服务:

  #postfix stop

  #service dovecot stop

  

  接下来,修改postfix的配置文件/etc/postfix/main.cf

  由于采用了sasl做认证,同时用dovecot作为pop3、imap和smtp的服务,所以需要修改postfix的配置文件以适应需求。

  在/etc/postfix/main.cf文件中增加以下信息以支持sasl认证和dovecot服务。

  smtpd_sasl_type = dovecot

  smtpd_sasl_path = private/auth

  smtpd_sasl_auth_enable = yes

  smtpd_sasl_local_domain = yourdomain.com

smtpd_recipient_restrictions = permit_mynetworks,permit_sasl_authenticated,reject_unauth_destination

  smtpd_sasl_security_options = noanonymous

  message_size_limit = 10240000

  另外main.cf中的以下两个参数

  myhostname = yourhostname

  mydestination = yourdomain.com, localhost.localdomain, localhost

  分别代表了你的服务器信息,myhostname是你当前主机名,mydestination其中的yourdomain.com则是邮件服务器名。

  修改完毕后保存该文件。

  

  接下来,修改/etc/dovecot/dovecot.conf文件

  修改参数protocols为

  protocols = pop3 imap

  修改mail_location为

  mail_location = mbox:~/mail:INBOX=/var/mail/%u (这里需要注意dovecot设置的邮件类型为mbox,而postfix默认邮件格式也能是Maildir。如果这里设置的为mail_location=mail:~/Maildir,会出现收件箱里没有东西)

  设置参数disable_plaintext_auth为

  disable_plaintext_auth = no

  找到auth default,将auth default改名为auth default2。

  然后在这行前面增加如下信息

auth default {

mechanisms = plain login

passdb pam {

}

userdb passwd {

}

socket listen {

client {

path = /var/spool/postfix/private/auth

mode = 0660

user = postfix

group = postfix

}

}

}

  注意:这里的每个‘{’前都有个空格。

  然后保存该文件。

  接下来,修改sasl配置文件。

  debian默认的安装配置中sasl并不会自动启动,需要修改/etc/default/saslauthd文件。

  将其中的START=no修改为START=yes。

  修改

  OPTIONS="-c -m /var/run/saslauthd"

  为

  OPTIONS="-c -m /var/spool/postfix/var/run/saslauthd"

  然后保存该文件。

  在启用sasl的情况下postfix运行需要sasldb2文件在postfix的chroot环境中。

  同时为了保证saslauthd能和postfix通讯,需要作如下的修改。

  删除位于/va/run目录下的saslauthd目录,然后创建一个指向/var/spool/postfix/var/run/saslauthd的符号连接。

  sudo rm -r /var/run/saslauthd/

  sudo mkdir -p /var/spool/postfix/var/run/saslauthd

  sudo ln -s /var/spool/postfix/var/run/saslauthd /var/run

  sudo chgrp sasl /var/spool/postfix/var/run/saslauthd

  sudo adduser postfix sasl

  另外postfix在运行的时候需要将/etc/sasldb2文件拷贝到chroot环境中。

  修改/etc/init.d/postfix文件,修改其中的FILES变量,在其中增加etc/sasldb2

  完成以上工作后启动三个服务。

   #postfix start

service dovecot start

  然后新增用户

  adduser username

  至此,邮件服务器的配置成功。

 查询系统邮件日志文件:/var/log/mail.log

启动Postfix 和 Dovecot

首先检查系统中是否安装了sendmail,一般情况都有sendmail,这里要先将sendmail服务关闭,或者是卸载sendmail

#service sendmail stop #关闭sendmail服务

#chkconfig sendmail off #关闭开机自动运行sendmail服务

#postfix start #打开postfix服务

#chkconfig postfix on #开机自动运行postfix服务(我设置后开机任旧不会自动运行postfix)

#service dovecot start #打开dovecot服务

#chkconfig dovecot on #开机自动运行dovecot服务

将postfix加入到root的组:

#usermod -G root postfix

检查服务是否开启,如果服务打开会显示如下结果

#nmap localhost

PORT STATE SERVICE

22/tcp open ssh

25/tcp open smtp

110/tcp open pop3

如果没有安装nmap命令,可以使用netstat命令

#netstat -nlt

添加邮件服务器用户并分配邮箱 首先添加邮件服务器用户

#adduser username //添加用户

#passwd username //设置密码

由于前面设置mail_location=mbox:~/mail:INBOX=/var/mail/%u,所以,所创建的用户目录下有具有mail目录,如果不存在该目录,如下操作

#telnet ip地址 110

#user username

+OK

#pass password

+login

#chmod 700 /home/username/Maildir //很重要

hellowoody commented 4 years ago

nginx root 和 alias 配置区别

nginx指定文件路径有两种方式root和alias,这两者的用法区别,使用方法总结了下,方便大家在应用过程中,快速响应。root与alias主要区别在于nginx如何解释location后面的uri,这会使两者分别以不同的方式将请求映射到服务器文件上。

[root] 语法:root path 默认值:root html 配置段:http、server、location、if

[alias] 语法:alias path 配置段:location

实例:

location ~ ^/weblogs/ {
 root /data/weblogs/www.ttlsa.com;
 autoindex on;
 auth_basic            "Restricted";
 auth_basic_user_file  passwd/weblogs;
}

如果一个请求的URI是/weblogs/httplogs/www.ttlsa.com-access.log时,web服务器将会返回服务器上的/data/weblogs/www.ttlsa.com/weblogs/httplogs/www.ttlsa.com-access.log的文件。 [info]root会根据完整的URI请求来映射,也就是/path/uri。[/info] 因此,前面的请求映射为path/weblogs/httplogs/www.ttlsa.com-access.log。

location ^~ /binapp/ {  
 limit_conn limit 4;
 limit_rate 200k;
 internal;  
 alias /data/statics/bin/apps/;
}

alias会把location后面配置的路径丢弃掉,把当前匹配到的目录指向到指定的目录。如果一个请求的URI是/binapp/a.ttlsa.com/favicon时,web服务器将会返回服务器上的/data/statics/bin/apps/a.ttlsa.com/favicon.jgp的文件。

  1. 使用alias时,目录名后面一定要加"/"。
  2. alias可以指定任何名称。
  3. alias在使用正则匹配时,必须捕捉要匹配的内容并在指定的内容处使用。
  4. alias只能位于location块中。
hellowoody commented 4 years ago

windows中用cmd 删除文件夹以及文件夹里面的所有内容

如果你要删除的整个文件夹以及文件夹里面的所有内容的话 rd/s/q 盘符:某个文件夹 (这样整个文件夹所有的文件和文件夹都删除了) 比如我想删除D盘的123文件夹以及123文件夹里面所有的内容 rd/s/q d:123 这样就删除了

如果只是单独删除某个文件的话用 del/f/s/q 盘符:文件名 比如我想删除D盘的456文件夹里面的789这个记事本文件 del/f/s/q d:456789.txt 删除文件的话记住要加上它的后缀名

hellowoody commented 4 years ago

axios跨域访问js端配置

由于vue2.0官方选择axios来完成 ajax 请求,所以我最近开始用axios写ajax的请求操作。我之前用的架构都是前后端分离,所以必然存在跨域问题。我根据github上axios的官方文档,写了post请求方法,可惜浏览器console控制台中输出跨域问题的错误。

服务端我已经做了http头报文header中的跨域处理

Access-Control-Allow-Origin: * 

但是js客户端我按照官方文档操作,发现依然有跨域问题,随后我在网上搜索相关的解决方法,发现需要在js客户端中,也就是http请求报文的头部设置编码格式Content-Type为application/x-www-form-urlencoded

axios({
    method:'post',
    url:'请求地址',
    data:{
        param:'参数'
    },
    headers: {
            'Content-Type': 'application/x-www-form-urlencoded',
    }
}).then(function(res){
    return res.data;
});

如果不加这句,post请求是可以访问到服务端,但是服务端不能正常的返回给客户端,并且在浏览器中console控制台中输出跨域错误

hellowoody commented 4 years ago

多页面网站禁用浏览器后退键

如标题所说,本文提供的方法适用于多页面网站,如SPA单页面应用等不适用。

利用window.onpopstate和window.history.pushState 控制浏览器后退键失效,下面说一下如何实现。

1.在你需要禁止浏览器后退键的页面上加上下面的代码:

<script>
$(document).ready(function(){
  //判断当前浏览器是否支持history和pushState,据我测试当前大部分浏览器都支持
  if(window.history && window.history.pushState){
    window.history.pushState({},null,location.href);
  }
});
</script>

当你加上上面的代码时,你发现当你点击一次浏览器的后退键,页面并没有后退,你可能感觉好像是解决问题了,但是当你再次点击后退键时页面还是返回到上一页面。

原因是window.history.pushState()方法是向浏览器历史添加了一个状态,它有三个参数分别是,一个状态对象(其实就是页面的参数),一个标题(现在被忽略了),以及一个可选的URL地址。

当你在页面上写了window.history.pushState({},null,location.href);浏览器会在history中添加location.href,同时页面并不跳转,但你只加了一次,所以只对一次后退事件起作用。如何彻底解决这个问题呢?还需要在全局增加onpopstate事件,详情请看第2步。

2.在全局增加一个onpopstate事件,这里我用的是匿名函数(当然也可以直接写):

;(function(window,undefined){
  'use strict'
  //判断当前浏览器是否支持history和pushState,据我测试当前大部分浏览器都支持
  if(window.history && window.history.pushState)
  {
    window.onpopstate = function(){
      window.history.pushState({},null,"");
      //window.history.forward(1);  这句我没理解什么意思,不加也可以实现,所以注释掉了
    }
  }
})(window);

这时你发现,在你的目标页面上点击浏览器回退键页面不会跳转了,问题解决。但是上面的代码是什么意思呢?

window.onpopstate事件会监听浏览器的后退、前进按钮(或者在JavaScript代码中调用类似history.back()、history.forward()、history.go()方法),但是调用像 history.pushState() 或者history.replaceState() 不会触发popstate事件。

所以我们在全局中增加popstate监听事件,一但用户点击后退按钮时就会触发这个方法,该方法会再次向histroy中添加一个链接,防止用户再一次点击后退按钮。

hellowoody commented 4 years ago

单页面网站禁用浏览器后退键

该文章适用于angularjs创建的单页面应用SPA,如vuejs或react.js不在本文讨论范围内。

我用angularjs和sb-admin2实现了前端框架,在不考虑浏览器后退键的情况下,是用$state和$stateParams实现后退功能的,当然也可以自己创建全局单例或是localstroge、sessionstorge处理历史跳转数据。

项目上线后,客户反应当页面跳转到3级或4级页面,点击浏览器后退键时,页面返回上级页面后提示缺少参数,页面的数据也是空白的,为了解决这一问题,我的解决方案是禁用浏览器后退键,下面是解决方法:

在angular.module("appId").run()方法中,用$locationChangeStart监听url地址变化。代码如下:

$rootScope.$on('$locationChangeStart',function(ev,to,from){
    if($rootScope.previousState && $rootScope.nowState && $rootScope.previousState == to)
    {
        console.log('not back');
        ev.preventDefault();
    }
    else{
        $rootScope.previousState = from;
        $rootScope.nowState = to;
    }
});

这样之前的问题就可以避免了,但是我也发现了一个小bug。虽然禁用了浏览器的后退键,用户点击业务页面上的返回按钮时,页面虽然成功返回到上一页面,但是浏览器地址栏的url没有变,当再次点击返回按钮时url又变回正常。

因为对功能没啥影响,就没有再深入了解该问题,如果有高人解决或是了解该问题,望请告知。

hellowoody commented 4 years ago

gitbook 使用命令

我用的是mac pro本,因为硬盘存储空间有限,同时gitbook的客户端用户交互感一般,所以我不推荐下载。

我一般是在线编写文章后,用命令下载同步到本地,在生成html。我喜欢用离线的html文档形式,无论是编写api还是技术文档查看传阅都很方便。

我写这篇文章是2018年2月,引用gitbook升级版频繁并且前后的命令和操作方式不一致,所以请慎重参考。

1. 下载gitbook

sudo npm install -g gitbook-cli

2. 创建目录并初始化

mkdir test
cd test
git init                      #初始化git
git pull http://url.git       #将网站上的book下载同步到本地
  1. 生成html文档
gitbook build --gitbook=2.6.7

好像从 3.0.0 版起, gitbook build 生成的 website 就不支持 local 打开了(其实也能打开,会有问题如点击左侧目录并不跳转的问题), 必需要 webserver 开启;

实在要完全静态的, 需要以 2.6.7 版build编译( 在有些浏览器下估计不太完美 )

hellowoody commented 4 years ago

利用html5 canvas动态画饼状图

先来看效果图

image

这里并没引用jquery等第三方库,只是用dom操作和canvas的特性编写的。 canvas画圆大体分为实心圆和空心圆。 根据需求分析知道该圆为实心圆。

1.先用canvas画实心圆

image

//伪代码
var canvas = document.createElement("canvas");
var ctx = canvas.getContext('2d');
ctx.beginPath();
ctx.arc(圆心x轴坐标,圆心y轴坐标,半径,开始角,结束角);
ctx.fillStyle = 'green';
ctx.closePath();
ctx.fill();

2.根据不同颜色绘制饼状图

//伪代码
var canvas = document.createElement("canvas");
var ctx = canvas.getContext('2d');
ctx.beginPath();
ctx.arc(圆心x轴坐标,圆心y轴坐标,半径,绿色开始角,绿色结束角);
ctx.fillStyle = 'green';
ctx.closePath();
ctx.fill();
ctx.beginPath();
ctx.arc(圆心x轴坐标,圆心y轴坐标,半径,红色开始角,红色结束角);
ctx.fillStyle = 'red';
ctx.closePath();
ctx.fill();
ctx.beginPath();
ctx.arc(圆心x轴坐标,圆心y轴坐标,半径,黄色开始角,黄色结束角);
ctx.fillStyle = 'yellow';
ctx.closePath();
ctx.fill();
ctx.beginPath();
ctx.arc(圆心x轴坐标,圆心y轴坐标,半径,紫色开始角,紫色结束角);
ctx.fillStyle = 'purple';
ctx.closePath();
ctx.fill();

3.动态绘制饼状图

动态绘制圆网上普遍推荐三种方法:requestAnimationFrame、setInterval(定时)、还有动态算角度的。 这里我用的是第一种requestAnimationFrame方式。 在编写的过程中发现一个问题,就是动态画圆时并不是以圆心的坐标画的。为了解决这一问题,需要在每次画圆时重新将canvas的画笔的坐标定义为最初圆心的坐标。

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title></title>
    <style>
        #graph {
/*            border: 1px solid black;
            height: 100%;
            width: 100%;
            box-sizing: border-box;*/
        }
    </style>
</head>
<body>
<div id="circle" style="width: 500px;float: left;"></div>
</body>
</html>
<script type="text/javascript">
(function(window,undefined){
    var data = [
        {"product":"产品1","sales":[192.44 ,210.54 ,220.84 ,230.11 ,220.85 ,210.59 ,205.49 ,200.55 ,195.71 ,187.46 ,180.66 ,170.90]},
        {"product":"产品2","sales":[122.41 ,133.16 ,145.65 ,158.00 ,164.84 ,178.62 ,185.70 ,190.13 ,195.53 ,198.88 ,204.32 ,210.91]},
        {"product":"产品3","sales":[170.30 ,175.00 ,170.79 ,165.10 ,165.62 ,160.92 ,155.92 ,145.77 ,145.17 ,140.27 ,135.99 ,130.33]},
        {"product":"产品4","sales":[165.64 ,170.15 ,175.10 ,185.32 ,190.90 ,190.01 ,187.05 ,183.74 ,177.24 ,181.90 ,179.54 ,175.98]}
    ]

    var dom_circle = document.getElementById('circle');
    if(dom_circle != undefined && dom_circle != null)
    {
        var canvas = document.createElement("canvas");
        dom_circle.appendChild(canvas);
        var ctx = canvas.getContext('2d');
        var defaultStyle = function(Dom,canvas){
            if(Dom.clientWidth <= 300)
            {
                canvas.width = 300;
                Dom.style.overflowX = "auto";
            }
            else{
                canvas.width = Dom.clientWidth;
            }
            if(Dom.clientHeight <= 300)
            {
                canvas.height = 300;
                Dom.style.overflowY = "auto";
            }
            else
            {
                canvas.height = Dom.clientHeight;
            }
            //坐标轴区域
            //注意,实际画折线图区域还要比这个略小一点
            return {
                p1:'green',
                p2:'red',
                p3:'yellow',
                p4:'purple',
                x: 0 ,    //坐标轴在canvas上的left坐标
                y: 0 ,    //坐标轴在canvas上的top坐标
                maxX: canvas.width ,   //坐标轴在canvas上的right坐标
                maxY: canvas.height ,   //坐标轴在canvas上的bottom坐标
                r:(canvas.width)/2,  //起点
                ry:(canvas.height)/2,  //起点
                cr: (canvas.width)/4, //半径
                startAngle:-(1/2*Math.PI),               //开始角度
                endAngle:(-(1/2*Math.PI)+2*Math.PI),     //结束角度
                xAngle:1*(Math.PI/180)                   //偏移量
            };
        }
        //画圆
        var tmpAngle = -(1/2*Math.PI);
        var ds = null;
        var sum = data[0]['sales'][0]+data[0]['sales'][1]+data[0]['sales'][2]+data[0]['sales'][3]
        var percent1 = data[0]['sales'][0]/sum * Math.PI * 2 ;
        var percent2 = data[0]['sales'][1]/sum * Math.PI * 2 + percent1;
        var percent3 = data[0]['sales'][2]/sum * Math.PI * 2 + percent2;
        var percent4 = data[0]['sales'][3]/sum * Math.PI * 2 + percent3;
        console.log(percent1);
        console.log(percent2);
        console.log(percent3);
        console.log(percent4);
        var tmpSum = 0;
        var drawCircle = function(){

            if(tmpAngle >= ds.endAngle)
            {
                return false;
            }
            else if(tmpAngle+ ds.xAngle > ds.endAngle)
            {
                tmpAngle = ds.endAngle;
            }
            else{
                tmpAngle += ds.xAngle;
                tmpSum += ds.xAngle
            }
            // console.log(ds.startAngle+'***'+tmpAngle);
            // console.log(tmpSum);
            // ctx.clearRect(ds.x,ds.y,canvas.width,canvas.height);

            if(tmpSum > percent1 && tmpSum <percent2)
            {
                ctx.beginPath();
                ctx.moveTo(ds.r,ds.ry);
                ctx.arc(ds.r,ds.ry,ds.cr,ds.startAngle+percent1,tmpAngle);

                ctx.fillStyle = ds.p2;
            }
            else if(tmpSum > percent2 && tmpSum <percent3)
            {
                ctx.beginPath();
                ctx.moveTo(ds.r,ds.ry);
                ctx.arc(ds.r,ds.ry,ds.cr,ds.startAngle+percent2,tmpAngle);
                ctx.fillStyle = ds.p3;
            }
            else if(tmpSum > percent3 )
            {
                ctx.beginPath();
                ctx.moveTo(ds.r,ds.ry);
                ctx.arc(ds.r,ds.ry,ds.cr,ds.startAngle+percent3,tmpAngle);
                ctx.fillStyle = ds.p4;
            }
            else{
                ctx.beginPath();
                ctx.moveTo(ds.r,ds.ry);
                ctx.arc(ds.r,ds.ry,ds.cr,ds.startAngle,tmpAngle);
                ctx.fillStyle = ds.p1;
            }
            ctx.closePath();
            ctx.fill();
            requestAnimationFrame(drawCircle);
        }
        this.toDraw = function(){
            ds= defaultStyle(dom_circle,canvas);
            // console.log(tmpAngle);
            // console.log(ds.xAngle)
            ctx.clearRect(ds.x,ds.y,canvas.width,canvas.height);
            drawCircle();
        }
        this.toDraw();
        var self = this;
        window.onresize = function(){
            self.toDraw()
        }
    }

})(window); 
</script>
hellowoody commented 4 years ago

Golang实现拓扑排序-DFS算法版

问题描述:有一串数字1到5,按照下面的关于顺序的要求,重新排列并打印出来。要求如下:2在5前出现,3在2前出现,4在1前出现,1在3前出现。

该问题是一个非常典型的拓扑排序的问题,一般解决拓扑排序的方案是采用DFS-深度优先算法,对于DFS算法我的浅薄理解就是递归,因拓扑排序问题本身会有一些前置条件(本文不过多介绍拓扑算法的定义),所以解决该问题就有了以下思路。

先将排序要求声明成map(把map的key,value看作对顺序的要求,key应在value前出现),然后遍历1-5这几个数,将每次遍历取出的数在map中key查找是否存在,如果存在就按map中key,value的关系,放入结果数组中。再用刚map[key]获取的value去map中的key查找是否存在,如果存在就将新的key和value放入结果数组的一头一尾,以此类推,最终打印结果数组,应满足本题的要求。下面就用Golang实现上述的问题。

package main

import (
    "fmt"
    "strconv"
)

//edge 要求的顺序
var edge map[string]string = map[string]string{
    "2": "5",
    "3": "2",
    "4": "1",
    "1": "3",
}

func main() {
    //结果数组
    var q []string = make([]string, 0)
    //已访问数组
    var visited []string = make([]string, 0)
    for i := 0; i < 5; i++ {
        tupusort(&q, &visited, strconv.Itoa(i))
    }
    // fmt.Printf("visited: %v \n", visited)
    reverse(q)
    fmt.Printf("topusort: %v \n", q)
}

//拓扑排序-DFS
func tupusort(q *[]string, visited *[]string, element string) {
    if !isVisited(visited, element) {
        *visited = append(*visited, element)
        if edge[element] != "" {
            tupusort(q, visited, edge[element])
        }
        *q = append(*q, element)
    }
}

//检查是否存在已访问的数组中
func isVisited(visited *[]string, element string) bool {
    var isVisited bool = false
    for _, item := range *visited {
        if item == element {
            isVisited = true
            break
        }
    }
    return isVisited
}

//反转数组顺序
func reverse(arr []string) {
    for i, j := 0, len(arr)-1; i < j; i, j = i+1, j-1 {
        arr[i], arr[j] = arr[j], arr[i]
    }
}

最后输出结果为

topusort: [4 1 3 2 5 0]