diff --git a/README.md b/README.md index 1ee3b8dd..b6c1d32a 100644 --- a/README.md +++ b/README.md @@ -88,10 +88,11 @@ System.out.println(responseData); - 微服务端自动验证(JSR-303) - 支持Spring Cloud Gateway - Admin管理平台,统一管理微服务配置,管理路由管理,微服务上下线 +- 门户网站,提供用户注册账号 - 接入方管理+秘钥管理 - 接口权限分配 - 文件上传/下载 -- 提供基础SDK(含:Java,C#,Python,Go) +- 提供基础SDK(含:Java,C++,C#,Python,Go,Rust,Nodejs) - 接口限流 - 文档整合 - 应用授权 @@ -111,9 +112,11 @@ System.out.println(responseData); ![秘钥信息](https://images.gitee.com/uploads/images/2019/0711/174921_bd817533_332975.png "秘钥信息") -![API文档](https://images.gitee.com/uploads/images/2019/0711/174939_97886883_332975.png "API文档") +- 门户网站 -![沙箱环境](https://images.gitee.com/uploads/images/2019/0711/175226_3f69346a_332975.png "沙箱环境") +![首页](https://images.gitee.com/uploads/images/2020/1107/104607_b86161f0_332975.png "portal0.png") + +![文档页](https://images.gitee.com/uploads/images/2020/1107/104342_d44849a9_332975.png "portal1.png") ## 工程说明 diff --git a/changelog.md b/changelog.md index 3a2b354d..264e3c1c 100644 --- a/changelog.md +++ b/changelog.md @@ -1,5 +1,13 @@ # changelog +## 4.2.0 + +需要执行`sop-upgrade-4.2.0.sql` + +- 新增ISV用户平台 +- 新增门户网站(portal) +- 新增`C++`,`Rust`语言SDK + ## 4.1.0 需要执行`sop-upgrade-4.1.0.sql` diff --git a/doc/docs/files/10010_快速体验.md b/doc/docs/files/10010_快速体验.md index a28b020e..7eb0e08b 100644 --- a/doc/docs/files/10010_快速体验.md +++ b/doc/docs/files/10010_快速体验.md @@ -38,6 +38,10 @@ 登录账号:admin/123456 +## 启动门户网站 + +见:`sop-website/sop-portal/README.md` + ## 启动文档中心 文档中心代码在sop-website工程中 diff --git a/sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/api/isv/param/IsvKeysFormUpdate.java b/sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/api/isv/param/IsvKeysFormUpdate.java index 652db61e..a9d2903b 100644 --- a/sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/api/isv/param/IsvKeysFormUpdate.java +++ b/sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/api/isv/param/IsvKeysFormUpdate.java @@ -29,14 +29,6 @@ public class IsvKeysFormUpdate { @Max(value = 2, message = "秘钥格式错误") private Byte keyFormat; - /** 开发者生成的公钥, 数据库字段:public_key_isv */ - @ApiDocField(description = "开发者生成的公钥") - private String publicKeyIsv; - - /** 开发者生成的私钥(交给开发者), 数据库字段:private_key_isv */ - @ApiDocField(description = "开发者生成的私钥") - private String privateKeyIsv; - /** 平台生成的公钥(交给开发者), 数据库字段:public_key_platform */ @ApiDocField(description = "平台生成的公钥") private String publicKeyPlatform; diff --git a/sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/api/isv/result/IsvInfoVO.java b/sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/api/isv/result/IsvInfoVO.java index 00873b31..fdc0b0c2 100644 --- a/sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/api/isv/result/IsvInfoVO.java +++ b/sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/api/isv/result/IsvInfoVO.java @@ -29,6 +29,8 @@ public class IsvInfoVO { @ApiDocField(description = "备注") private String remark; + private Long userId; + /** 数据库字段:gmt_create */ @ApiDocField(description = "添加时间") private Date gmtCreate; diff --git a/sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/api/service/ResourceApi.java b/sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/api/service/ResourceApi.java new file mode 100644 index 00000000..ca605491 --- /dev/null +++ b/sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/api/service/ResourceApi.java @@ -0,0 +1,54 @@ +package com.gitee.sop.adminserver.api.service; + +import com.gitee.easyopen.annotation.Api; +import com.gitee.easyopen.annotation.ApiService; +import com.gitee.sop.adminserver.api.system.param.IspPageParam; +import com.gitee.sop.adminserver.api.system.param.IspResourceParam; +import com.gitee.sop.adminserver.common.CopyUtil; +import com.gitee.sop.adminserver.entity.IspResource; +import com.gitee.sop.adminserver.mapper.IspResourceMapper; +import org.springframework.beans.factory.annotation.Autowired; + +import javax.validation.constraints.NotNull; +import java.util.List; + +/** + * @author tanghc + */ +@ApiService +public class ResourceApi { + + public static final byte RESOURCE_TYPE_SDK = (byte) 0; + + @Autowired + private IspResourceMapper ispResourceMapper; + + + @Api(name = "isp.sdk.list") + List list(IspPageParam param) { + return ispResourceMapper.list(param.toQuery()); + } + + @Api(name = "isp.sdk.add") + void addSdk(IspResourceParam param) { + IspResource ispResource = CopyUtil.copyBean(param, IspResource::new); + ispResource.setType(RESOURCE_TYPE_SDK); + ispResourceMapper.saveIgnoreNull(ispResource); + } + + @Api(name = "isp.sdk.update") + void updateSdk(IspResourceParam param) { + IspResource resource = ispResourceMapper.getById(param.getId()); + CopyUtil.copyProperties(param, resource); + ispResourceMapper.update(resource); + } + + @Api(name = "isp.sdk.delete") + void deleteSdk(@NotNull Long id) { + IspResource resource = ispResourceMapper.getById(id); + if (resource != null) { + ispResourceMapper.deleteById(id); + } + } + +} diff --git a/sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/api/system/param/IspPageParam.java b/sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/api/system/param/IspPageParam.java new file mode 100644 index 00000000..ecba947c --- /dev/null +++ b/sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/api/system/param/IspPageParam.java @@ -0,0 +1,9 @@ +package com.gitee.sop.adminserver.api.system.param; + +import com.gitee.fastmybatis.core.query.param.PageParam; + +/** + * @author tanghc + */ +public class IspPageParam extends PageParam { +} diff --git a/sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/api/system/param/IspResourceParam.java b/sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/api/system/param/IspResourceParam.java new file mode 100644 index 00000000..36391e1a --- /dev/null +++ b/sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/api/system/param/IspResourceParam.java @@ -0,0 +1,27 @@ +package com.gitee.sop.adminserver.api.system.param; + +import lombok.Data; + + +/** + * 表名:isp_resource + * 备注:ISP资源表 + * + * @author tanghc + */ +@Data +public class IspResourceParam { + + private Long id; + + /** 资源名称, 数据库字段:name */ + private String name; + + private String version; + + /** 资源内容(URL), 数据库字段:content */ + private String content; + + private String extContent; + +} diff --git a/sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/api/system/result/IspResourceResult.java b/sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/api/system/result/IspResourceResult.java new file mode 100644 index 00000000..88c38f76 --- /dev/null +++ b/sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/api/system/result/IspResourceResult.java @@ -0,0 +1,27 @@ +package com.gitee.sop.adminserver.api.system.result; + +import lombok.Data; + + +/** + * 表名:isp_resource + * 备注:ISP资源表 + * + * @author tanghc + */ +@Data +public class IspResourceResult { + + private Long id; + + /** 资源名称, 数据库字段:name */ + private String name; + + private String version; + + /** 资源内容(URL), 数据库字段:content */ + private String content; + + private String extContent; + +} diff --git a/sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/entity/IspResource.java b/sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/entity/IspResource.java new file mode 100644 index 00000000..021f3e5d --- /dev/null +++ b/sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/entity/IspResource.java @@ -0,0 +1,50 @@ +package com.gitee.sop.adminserver.entity; + +import lombok.Data; + +import javax.persistence.Column; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.Table; +import java.util.Date; + + +/** + * 表名:isp_resource + * 备注:ISP资源表 + * + * @author tanghc + */ +@Table(name = "isp_resource") +@Data +public class IspResource { + /** 数据库字段:id */ + @Id + @Column(name = "id") + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + /** 资源名称, 数据库字段:name */ + private String name; + + private String version; + + /** 资源内容(URL), 数据库字段:content */ + private String content; + + private String extContent; + + /** 资源类型:0:SDK链接, 数据库字段:type */ + private Byte type; + + /** 数据库字段:is_deleted */ + @com.gitee.fastmybatis.core.annotation.LogicDelete + private Byte isDeleted; + + /** 数据库字段:gmt_create */ + private Date gmtCreate; + + /** 数据库字段:gmt_modified */ + private Date gmtModified; +} diff --git a/sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/entity/IsvInfo.java b/sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/entity/IsvInfo.java index 9e1b85dd..d44330a4 100644 --- a/sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/entity/IsvInfo.java +++ b/sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/entity/IsvInfo.java @@ -31,6 +31,9 @@ public class IsvInfo { /** 1启用,2禁用, 数据库字段:status */ private Byte status; + /** user_account.id */ + private Long userId; + /** 备注,数据库字段:remark */ private String remark; diff --git a/sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/mapper/IspResourceMapper.java b/sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/mapper/IspResourceMapper.java new file mode 100644 index 00000000..22524566 --- /dev/null +++ b/sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/mapper/IspResourceMapper.java @@ -0,0 +1,11 @@ +package com.gitee.sop.adminserver.mapper; + +import com.gitee.fastmybatis.core.mapper.CrudMapper; +import com.gitee.sop.adminserver.entity.IspResource; + + +/** + * @author tanghc + */ +public interface IspResourceMapper extends CrudMapper { +} diff --git a/sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/service/ConfigPushService.java b/sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/service/ConfigPushService.java index c2b0e943..892a98f8 100644 --- a/sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/service/ConfigPushService.java +++ b/sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/service/ConfigPushService.java @@ -37,9 +37,6 @@ public class ConfigPushService { @Autowired private ServerService serverService; - @Value("${gateway.host:}") - private String gatewayHost; - @Value("${sop.secret}") private String secret; diff --git a/sop-admin/sop-admin-server/src/main/resources/public/index.html b/sop-admin/sop-admin-server/src/main/resources/public/index.html index 661b755c..24b2cfda 100644 --- a/sop-admin/sop-admin-server/src/main/resources/public/index.html +++ b/sop-admin/sop-admin-server/src/main/resources/public/index.html @@ -1 +1 @@ -SOP Admin
\ No newline at end of file +SOP Admin
\ No newline at end of file diff --git a/sop-admin/sop-admin-server/src/main/resources/public/static/js/app.3f83e9e2.js b/sop-admin/sop-admin-server/src/main/resources/public/static/js/app.3f83e9e2.js new file mode 100644 index 00000000..8502889d --- /dev/null +++ b/sop-admin/sop-admin-server/src/main/resources/public/static/js/app.3f83e9e2.js @@ -0,0 +1 @@ +(window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["app"],{0:function(e,t,n){e.exports=n("56d7")},"0a7b":function(e,t,n){},"0cb8":function(e,t,n){},"186a":function(e,t,n){"use strict";var a=n("dc52"),i=n.n(a);i.a},"18f0":function(e,t,n){"use strict";n.r(t);var a=n("e017"),i=n.n(a),o=n("21a1"),r=n.n(o),c=new i.a({id:"icon-link",use:"icon-link-usage",viewBox:"0 0 128 128",content:''});r.a.add(c);t["default"]=c},2536:function(e,t,n){},"2a3d":function(e,t,n){"use strict";n.r(t);var a=n("e017"),i=n.n(a),o=n("21a1"),r=n.n(o),c=new i.a({id:"icon-password",use:"icon-password-usage",viewBox:"0 0 128 128",content:''});r.a.add(c);t["default"]=c},"30c3":function(e,t,n){"use strict";n.r(t);var a=n("e017"),i=n.n(a),o=n("21a1"),r=n.n(o),c=new i.a({id:"icon-example",use:"icon-example-usage",viewBox:"0 0 128 128",content:''});r.a.add(c);t["default"]=c},"34c8":function(e,t,n){"use strict";var a=n("88a1"),i=n.n(a);i.a},"405a":function(e,t,n){"use strict";var a=n("4a83"),i=n.n(a);i.a},"47f1":function(e,t,n){"use strict";n.r(t);var a=n("e017"),i=n.n(a),o=n("21a1"),r=n.n(o),c=new i.a({id:"icon-table",use:"icon-table-usage",viewBox:"0 0 128 128",content:''});r.a.add(c);t["default"]=c},"4a83":function(e,t,n){},"4df5":function(e,t,n){"use strict";n.r(t);var a=n("e017"),i=n.n(a),o=n("21a1"),r=n.n(o),c=new i.a({id:"icon-eye",use:"icon-eye-usage",viewBox:"0 0 128 64",content:''});r.a.add(c);t["default"]=c},"50be":function(e,t,n){"use strict";var a=n("2536"),i=n.n(a);i.a},"51ff":function(e,t,n){var a={"./dashboard.svg":"f782","./example.svg":"30c3","./eye-open.svg":"d7ec","./eye.svg":"4df5","./form.svg":"eb1b","./link.svg":"18f0","./nested.svg":"dcf8","./password.svg":"2a3d","./table.svg":"47f1","./tree.svg":"93cd","./user.svg":"b3b5"};function i(e){var t=o(e);return n(t)}function o(e){var t=a[e];if(!(t+1)){var n=new Error("Cannot find module '"+e+"'");throw n.code="MODULE_NOT_FOUND",n}return t}i.keys=function(){return Object.keys(a)},i.resolve=o,e.exports=i,i.id="51ff"},"56d7":function(e,t,n){"use strict";n.r(t);n("cadf"),n("551c"),n("f751"),n("097d");var a=n("2b0e"),i=(n("f5df"),n("5c96")),o=n.n(i),r=(n("0fae"),n("f0d9")),c=n.n(r),s=(n("b20f"),function(){var e=this,t=e.$createElement,n=e._self._c||t;return n("div",{attrs:{id:"app"}},[n("router-view")],1)}),u=[],l={name:"App"},d=l,m=n("2877"),h=Object(m["a"])(d,s,u,!1,null,null,null),f=h.exports,p=n("2f62"),v=(n("7f7f"),{sidebar:function(e){return e.app.sidebar},device:function(e){return e.app.device},token:function(e){return e.user.token},avatar:function(e){return e.user.avatar},name:function(e){return e.user.name}}),b=v,g=n("a78e"),w=n.n(g),x={sidebar:{opened:!w.a.get("sidebarStatus")||!!+w.a.get("sidebarStatus"),withoutAnimation:!1},device:"desktop"},y={TOGGLE_SIDEBAR:function(e){e.sidebar.opened=!e.sidebar.opened,e.sidebar.withoutAnimation=!1,e.sidebar.opened?w.a.set("sidebarStatus",1):w.a.set("sidebarStatus",0)},CLOSE_SIDEBAR:function(e,t){w.a.set("sidebarStatus",0),e.sidebar.opened=!1,e.sidebar.withoutAnimation=t},TOGGLE_DEVICE:function(e,t){e.device=t}},k={toggleSideBar:function(e){var t=e.commit;t("TOGGLE_SIDEBAR")},closeSideBar:function(e,t){var n=e.commit,a=t.withoutAnimation;n("CLOSE_SIDEBAR",a)},toggleDevice:function(e,t){var n=e.commit;n("TOGGLE_DEVICE",t)}},C={namespaced:!0,state:x,mutations:y,actions:k},_=n("83d6"),O=n.n(_),S=O.a.showSettings,z=O.a.fixedHeader,B=O.a.sidebarLogo,M={showSettings:S,fixedHeader:z,sidebarLogo:B},H={CHANGE_SETTING:function(e,t){var n=t.key,a=t.value;e.hasOwnProperty(n)&&(e[n]=a)}},T={changeSetting:function(e,t){var n=e.commit;n("CHANGE_SETTING",t)}},E={namespaced:!0,state:M,mutations:H,actions:T},L=n("bc3a"),$=n.n(L),A=n("5f87"),V=$.a.create({baseURL:"/api",withCredentials:!0,timeout:5e3});V.interceptors.request.use(function(e){return bt.getters.token&&(e.headers["X-Token"]=Object(A["a"])()),e},function(e){return console.log(e),Promise.reject(e)}),V.interceptors.response.use(function(e){var t=e.data;return 2e4!==t.code?(Object(i["Message"])({message:t.message||"error",type:"error",duration:5e3}),50008!==t.code&&50012!==t.code&&50014!==t.code||i["MessageBox"].confirm("You have been logged out, you can cancel to stay on this page, or log in again","Confirm logout",{confirmButtonText:"Re-Login",cancelButtonText:"Cancel",type:"warning"}).then(function(){bt.dispatch("user/resetToken").then(function(){location.reload()})}),Promise.reject(t.message||"error")):t},function(e){return console.log("err"+e),Object(i["Message"])({message:e.message,type:"error",duration:5e3}),Promise.reject(e)});var j=V;function I(e){return j({url:"/user/login",method:"post",data:e})}function P(e){return j({url:"/user/info",method:"get",params:{token:e}})}function N(){return j({url:"/user/logout",method:"post"})}var D=n("8c4f"),R=function(){var e=this,t=e.$createElement,n=e._self._c||t;return n("div",{staticClass:"app-wrapper",class:e.classObj},["mobile"===e.device&&e.sidebar.opened?n("div",{staticClass:"drawer-bg",on:{click:e.handleClickOutside}}):e._e(),e._v(" "),n("sidebar",{staticClass:"sidebar-container"}),e._v(" "),n("div",{staticClass:"main-container"},[n("div",{class:{"fixed-header":e.fixedHeader}},[n("navbar")],1),e._v(" "),n("app-main")],1)],1)},q=[],G=function(){var e=this,t=e.$createElement,n=e._self._c||t;return n("div",{staticClass:"navbar"},[n("hamburger",{staticClass:"hamburger-container",attrs:{"is-active":e.sidebar.opened},on:{toggleClick:e.toggleSideBar}}),e._v(" "),n("breadcrumb",{staticClass:"breadcrumb-container"}),e._v(" "),n("div",{staticClass:"right-menu"},[n("el-button",{staticStyle:{"margin-right":"10px"},attrs:{type:"text"},on:{click:e.doLogout}},[e._v("退出")])],1)],1)},F=[],U=n("db72"),K=function(){var e=this,t=e.$createElement,n=e._self._c||t;return n("el-breadcrumb",{staticClass:"app-breadcrumb",attrs:{separator:"/"}},[n("transition-group",{attrs:{name:"breadcrumb"}},e._l(e.levelList,function(t,a){return n("el-breadcrumb-item",{key:t.path},["noRedirect"===t.redirect||a==e.levelList.length-1?n("span",{staticClass:"no-redirect"},[e._v(e._s(t.meta.title))]):n("a",{on:{click:function(n){return n.preventDefault(),e.handleLink(t)}}},[e._v(e._s(t.meta.title))])])}),1)],1)},W=[],J=n("bd11"),X=n.n(J),Y={data:function(){return{levelList:null}},watch:{$route:function(){this.getBreadcrumb()}},created:function(){this.getBreadcrumb()},methods:{getBreadcrumb:function(){var e=this.$route.matched.filter(function(e){return e.meta&&e.meta.title}),t=e[0];this.isDashboard(t)||(e=[{path:"/dashboard",meta:{title:"Dashboard"}}].concat(e)),this.levelList=e.filter(function(e){return e.meta&&e.meta.title&&!1!==e.meta.breadcrumb})},isDashboard:function(e){var t=e&&e.name;return!!t&&t.trim().toLocaleLowerCase()==="Dashboard".toLocaleLowerCase()},pathCompile:function(e){var t=this.$route.params,n=X.a.compile(e);return n(t)},handleLink:function(e){var t=e.redirect,n=e.path;t?this.$router.push(t):this.$router.push(this.pathCompile(n))}}},Q=Y,Z=(n("34c8"),Object(m["a"])(Q,K,W,!1,null,"62cc9144",null)),ee=Z.exports,te=function(){var e=this,t=e.$createElement,n=e._self._c||t;return n("div",{staticStyle:{padding:"0 15px"},on:{click:e.toggleClick}},[n("svg",{staticClass:"hamburger",class:{"is-active":e.isActive},attrs:{viewBox:"0 0 1024 1024",xmlns:"http://www.w3.org/2000/svg",width:"64",height:"64"}},[n("path",{attrs:{d:"M408 442h480c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8H408c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8zm-8 204c0 4.4 3.6 8 8 8h480c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8H408c-4.4 0-8 3.6-8 8v56zm504-486H120c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0 632H120c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zM142.4 642.1L298.7 519a8.84 8.84 0 0 0 0-13.9L142.4 381.9c-5.8-4.6-14.4-.5-14.4 6.9v246.3a8.9 8.9 0 0 0 14.4 7z"}})])])},ne=[],ae={name:"Hamburger",props:{isActive:{type:Boolean,default:!1}},methods:{toggleClick:function(){this.$emit("toggleClick")}}},ie=ae,oe=(n("186a"),Object(m["a"])(ie,te,ne,!1,null,"49e15297",null)),re=oe.exports,ce={components:{Breadcrumb:ee,Hamburger:re},computed:Object(U["a"])({},Object(p["b"])(["sidebar","avatar"])),methods:{toggleSideBar:function(){this.$store.dispatch("app/toggleSideBar")},doLogout:function(){this.logout()}}},se=ce,ue=(n("405a"),Object(m["a"])(se,G,F,!1,null,"1eff5c58",null)),le=ue.exports,de=function(){var e=this,t=e.$createElement,n=e._self._c||t;return n("div",{class:{"has-logo":e.showLogo}},[e.showLogo?n("logo",{attrs:{collapse:e.isCollapse}}):e._e(),e._v(" "),n("el-scrollbar",{attrs:{"wrap-class":"scrollbar-wrapper"}},[n("el-menu",{attrs:{"default-active":e.activeMenu,collapse:e.isCollapse,"background-color":e.variables.menuBg,"text-color":e.variables.menuText,"unique-opened":!1,"active-text-color":e.variables.menuActiveText,"collapse-transition":!1,"default-openeds":e.opened,mode:"vertical"}},e._l(e.routes,function(e){return n("sidebar-item",{key:e.path,attrs:{item:e,"base-path":e.path}})}),1)],1)],1)},me=[],he=function(){var e=this,t=e.$createElement,n=e._self._c||t;return n("div",{staticClass:"sidebar-logo-container",class:{collapse:e.collapse}},[n("transition",{attrs:{name:"sidebarLogoFade"}},[e.collapse?n("router-link",{key:"collapse",staticClass:"sidebar-logo-link",attrs:{to:"/"}},[e.logo?n("img",{staticClass:"sidebar-logo",attrs:{src:e.logo}}):n("h1",{staticClass:"sidebar-title"},[e._v(e._s(e.title)+" ")])]):n("router-link",{key:"expand",staticClass:"sidebar-logo-link",attrs:{to:"/"}},[e.logo?n("img",{staticClass:"sidebar-logo",attrs:{src:e.logo}}):e._e(),e._v(" "),n("h1",{staticClass:"sidebar-title"},[e._v(e._s(e.title)+" ")])])],1)],1)},fe=[],pe={name:"SidebarLogo",props:{collapse:{type:Boolean,required:!0}},data:function(){return{title:"SOP Admin",logo:"https://wpimg.wallstcn.com/69a1c46c-eb1c-4b46-8bd4-e9e686ef5251.png"}}},ve=pe,be=(n("7c27"),Object(m["a"])(ve,he,fe,!1,null,"b905289c",null)),ge=be.exports,we=function(){var e=this,t=e.$createElement,n=e._self._c||t;return e.item.hidden?e._e():n("div",{staticClass:"menu-wrapper"},[!e.hasOneShowingChild(e.item.children,e.item)||e.onlyOneChild.children&&!e.onlyOneChild.noShowingChildren||e.item.alwaysShow?n("el-submenu",{ref:"subMenu",attrs:{index:e.resolvePath(e.item.path),"popper-append-to-body":""}},[n("template",{slot:"title"},[e.item.meta?n("item",{attrs:{icon:e.item.meta&&e.item.meta.icon,title:e.item.meta.title}}):e._e()],1),e._v(" "),e._l(e.item.children,function(t){return n("sidebar-item",{key:t.path,staticClass:"nest-menu",attrs:{"is-nest":!0,item:t,"base-path":e.resolvePath(t.path)}})})],2):[e.onlyOneChild.meta?n("app-link",{attrs:{to:e.resolvePath(e.onlyOneChild.path)}},[n("el-menu-item",{class:{"submenu-title-noDropdown":!e.isNest},attrs:{index:e.resolvePath(e.onlyOneChild.path)}},[n("item",{attrs:{icon:e.onlyOneChild.meta.icon||e.item.meta&&e.item.meta.icon,title:e.onlyOneChild.meta.title}})],1)],1):e._e()]],2)},xe=[],ye=n("df7c"),ke=n.n(ye);function Ce(e){return/^(https?:|mailto:|tel:)/.test(e)}var _e,Oe,Se={name:"MenuItem",functional:!0,props:{icon:{type:String,default:""},title:{type:String,default:""}},render:function(e,t){var n=t.props,a=n.icon,i=n.title,o=[];return a&&o.push(e("svg-icon",{attrs:{"icon-class":a}})),i&&o.push(e("span",{slot:"title"},[i])),o}},ze=Se,Be=Object(m["a"])(ze,_e,Oe,!1,null,null,null),Me=Be.exports,He=function(){var e=this,t=e.$createElement,n=e._self._c||t;return n("component",e._b({},"component",e.linkProps(e.to),!1),[e._t("default")],2)},Te=[],Ee={props:{to:{type:String,required:!0}},methods:{linkProps:function(e){return Ce(e)?{is:"a",href:e,target:"_blank",rel:"noopener"}:{is:"router-link",to:e}}}},Le=Ee,$e=Object(m["a"])(Le,He,Te,!1,null,null,null),Ae=$e.exports,Ve={computed:{device:function(){return this.$store.state.app.device}},mounted:function(){this.fixBugIniOS()},methods:{fixBugIniOS:function(){var e=this,t=this.$refs.subMenu;if(t){var n=t.handleMouseleave;t.handleMouseleave=function(t){"mobile"!==e.device&&n(t)}}}}},je={name:"SidebarItem",components:{Item:Me,AppLink:Ae},mixins:[Ve],props:{item:{type:Object,required:!0},isNest:{type:Boolean,default:!1},basePath:{type:String,default:""}},data:function(){return this.onlyOneChild=null,{}},methods:{hasOneShowingChild:function(){var e=this,t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:[],n=arguments.length>1?arguments[1]:void 0,a=t.filter(function(t){return!t.hidden&&(e.onlyOneChild=t,!0)});return 1===a.length||0===a.length&&(this.onlyOneChild=Object(U["a"])({},n,{path:"",noShowingChildren:!0}),!0)},resolvePath:function(e){return Ce(e)?e:Ce(this.basePath)?this.basePath:ke.a.resolve(this.basePath,e)}}},Ie=je,Pe=Object(m["a"])(Ie,we,xe,!1,null,null,null),Ne=Pe.exports,De=n("cf1e"),Re=n.n(De),qe={components:{SidebarItem:Ne,Logo:ge},computed:Object(U["a"])({},Object(p["b"])(["sidebar"]),{routes:function(){return this.$router.options.routes},opened:function(){return this.routes.filter(function(e){return e.meta&&e.meta.open}).map(function(e){return e.path})},activeMenu:function(){var e=this.$route,t=e.meta,n=e.path;return t.activeMenu?t.activeMenu:n},showLogo:function(){return this.$store.state.settings.sidebarLogo},variables:function(){return Re.a},isCollapse:function(){return!this.sidebar.opened}})},Ge=qe,Fe=Object(m["a"])(Ge,de,me,!1,null,null,null),Ue=Fe.exports,Ke=function(){var e=this,t=e.$createElement,n=e._self._c||t;return n("section",{staticClass:"app-main"},[n("transition",{attrs:{name:"fade-transform",mode:"out-in"}},[n("router-view",{key:e.key})],1)],1)},We=[],Je={name:"AppMain",computed:{key:function(){return this.$route.fullPath}}},Xe=Je,Ye=(n("50be"),Object(m["a"])(Xe,Ke,We,!1,null,"43c24f68",null)),Qe=Ye.exports,Ze=document,et=Ze.body,tt=992,nt={watch:{$route:function(e){"mobile"===this.device&&this.sidebar.opened&&bt.dispatch("app/closeSideBar",{withoutAnimation:!1})}},beforeMount:function(){window.addEventListener("resize",this.$_resizeHandler)},beforeDestroy:function(){window.removeEventListener("resize",this.$_resizeHandler)},mounted:function(){var e=this.$_isMobile();e&&(bt.dispatch("app/toggleDevice","mobile"),bt.dispatch("app/closeSideBar",{withoutAnimation:!0}))},methods:{$_isMobile:function(){var e=et.getBoundingClientRect();return e.width-1'});r.a.add(c);t["default"]=c},"9f2b":function(e,t,n){"use strict";var a=n("bf90"),i=n.n(a);i.a},b20f:function(e,t,n){e.exports={menuText:"#bfcbd9",menuActiveText:"#409EFF",subMenuActiveText:"#f4f4f5",menuBg:"#304156",menuHover:"#263445",subMenuBg:"#1f2d3d",subMenuHover:"#001528",sideBarWidth:"210px"}},b3b5:function(e,t,n){"use strict";n.r(t);var a=n("e017"),i=n.n(a),o=n("21a1"),r=n.n(o),c=new i.a({id:"icon-user",use:"icon-user-usage",viewBox:"0 0 130 130",content:''});r.a.add(c);t["default"]=c},bf90:function(e,t,n){},cf1e:function(e,t,n){e.exports={menuText:"#bfcbd9",menuActiveText:"#409EFF",subMenuActiveText:"#f4f4f5",menuBg:"#304156",menuHover:"#263445",subMenuBg:"#1f2d3d",subMenuHover:"#001528",sideBarWidth:"210px"}},d7ec:function(e,t,n){"use strict";n.r(t);var a=n("e017"),i=n.n(a),o=n("21a1"),r=n.n(o),c=new i.a({id:"icon-eye-open",use:"icon-eye-open-usage",viewBox:"0 0 1024 1024",content:''});r.a.add(c);t["default"]=c},dc52:function(e,t,n){},dcf8:function(e,t,n){"use strict";n.r(t);var a=n("e017"),i=n.n(a),o=n("21a1"),r=n.n(o),c=new i.a({id:"icon-nested",use:"icon-nested-usage",viewBox:"0 0 128 128",content:''});r.a.add(c);t["default"]=c},eb1b:function(e,t,n){"use strict";n.r(t);var a=n("e017"),i=n.n(a),o=n("21a1"),r=n.n(o),c=new i.a({id:"icon-form",use:"icon-form-usage",viewBox:"0 0 128 128",content:''});r.a.add(c);t["default"]=c},f782:function(e,t,n){"use strict";n.r(t);var a=n("e017"),i=n.n(a),o=n("21a1"),r=n.n(o),c=new i.a({id:"icon-dashboard",use:"icon-dashboard-usage",viewBox:"0 0 128 100",content:''});r.a.add(c);t["default"]=c}},[[0,"runtime","chunk-elementUI","chunk-libs"]]]); \ No newline at end of file diff --git a/sop-admin/sop-admin-server/src/main/resources/public/static/js/app.f323bdd7.js b/sop-admin/sop-admin-server/src/main/resources/public/static/js/app.f323bdd7.js deleted file mode 100644 index 0ef6965e..00000000 --- a/sop-admin/sop-admin-server/src/main/resources/public/static/js/app.f323bdd7.js +++ /dev/null @@ -1 +0,0 @@ -(window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["app"],{0:function(e,t,n){e.exports=n("56d7")},"0a7b":function(e,t,n){},"0cb8":function(e,t,n){},"186a":function(e,t,n){"use strict";var a=n("dc52"),i=n.n(a);i.a},"18f0":function(e,t,n){"use strict";n.r(t);var a=n("e017"),i=n.n(a),o=n("21a1"),r=n.n(o),c=new i.a({id:"icon-link",use:"icon-link-usage",viewBox:"0 0 128 128",content:''});r.a.add(c);t["default"]=c},2536:function(e,t,n){},"2a3d":function(e,t,n){"use strict";n.r(t);var a=n("e017"),i=n.n(a),o=n("21a1"),r=n.n(o),c=new i.a({id:"icon-password",use:"icon-password-usage",viewBox:"0 0 128 128",content:''});r.a.add(c);t["default"]=c},"30c3":function(e,t,n){"use strict";n.r(t);var a=n("e017"),i=n.n(a),o=n("21a1"),r=n.n(o),c=new i.a({id:"icon-example",use:"icon-example-usage",viewBox:"0 0 128 128",content:''});r.a.add(c);t["default"]=c},"34c8":function(e,t,n){"use strict";var a=n("88a1"),i=n.n(a);i.a},"405a":function(e,t,n){"use strict";var a=n("4a83"),i=n.n(a);i.a},"47f1":function(e,t,n){"use strict";n.r(t);var a=n("e017"),i=n.n(a),o=n("21a1"),r=n.n(o),c=new i.a({id:"icon-table",use:"icon-table-usage",viewBox:"0 0 128 128",content:''});r.a.add(c);t["default"]=c},"4a83":function(e,t,n){},"4df5":function(e,t,n){"use strict";n.r(t);var a=n("e017"),i=n.n(a),o=n("21a1"),r=n.n(o),c=new i.a({id:"icon-eye",use:"icon-eye-usage",viewBox:"0 0 128 64",content:''});r.a.add(c);t["default"]=c},"50be":function(e,t,n){"use strict";var a=n("2536"),i=n.n(a);i.a},"51ff":function(e,t,n){var a={"./dashboard.svg":"f782","./example.svg":"30c3","./eye-open.svg":"d7ec","./eye.svg":"4df5","./form.svg":"eb1b","./link.svg":"18f0","./nested.svg":"dcf8","./password.svg":"2a3d","./table.svg":"47f1","./tree.svg":"93cd","./user.svg":"b3b5"};function i(e){var t=o(e);return n(t)}function o(e){var t=a[e];if(!(t+1)){var n=new Error("Cannot find module '"+e+"'");throw n.code="MODULE_NOT_FOUND",n}return t}i.keys=function(){return Object.keys(a)},i.resolve=o,e.exports=i,i.id="51ff"},"56d7":function(e,t,n){"use strict";n.r(t);n("cadf"),n("551c"),n("f751"),n("097d");var a=n("2b0e"),i=(n("f5df"),n("5c96")),o=n.n(i),r=(n("0fae"),n("f0d9")),c=n.n(r),s=(n("b20f"),function(){var e=this,t=e.$createElement,n=e._self._c||t;return n("div",{attrs:{id:"app"}},[n("router-view")],1)}),u=[],l={name:"App"},d=l,m=n("2877"),h=Object(m["a"])(d,s,u,!1,null,null,null),f=h.exports,p=n("2f62"),v=(n("7f7f"),{sidebar:function(e){return e.app.sidebar},device:function(e){return e.app.device},token:function(e){return e.user.token},avatar:function(e){return e.user.avatar},name:function(e){return e.user.name}}),b=v,g=n("a78e"),w=n.n(g),x={sidebar:{opened:!w.a.get("sidebarStatus")||!!+w.a.get("sidebarStatus"),withoutAnimation:!1},device:"desktop"},y={TOGGLE_SIDEBAR:function(e){e.sidebar.opened=!e.sidebar.opened,e.sidebar.withoutAnimation=!1,e.sidebar.opened?w.a.set("sidebarStatus",1):w.a.set("sidebarStatus",0)},CLOSE_SIDEBAR:function(e,t){w.a.set("sidebarStatus",0),e.sidebar.opened=!1,e.sidebar.withoutAnimation=t},TOGGLE_DEVICE:function(e,t){e.device=t}},k={toggleSideBar:function(e){var t=e.commit;t("TOGGLE_SIDEBAR")},closeSideBar:function(e,t){var n=e.commit,a=t.withoutAnimation;n("CLOSE_SIDEBAR",a)},toggleDevice:function(e,t){var n=e.commit;n("TOGGLE_DEVICE",t)}},C={namespaced:!0,state:x,mutations:y,actions:k},_=n("83d6"),O=n.n(_),S=O.a.showSettings,z=O.a.fixedHeader,B=O.a.sidebarLogo,M={showSettings:S,fixedHeader:z,sidebarLogo:B},H={CHANGE_SETTING:function(e,t){var n=t.key,a=t.value;e.hasOwnProperty(n)&&(e[n]=a)}},T={changeSetting:function(e,t){var n=e.commit;n("CHANGE_SETTING",t)}},E={namespaced:!0,state:M,mutations:H,actions:T},L=n("bc3a"),$=n.n(L),A=n("5f87"),V=$.a.create({baseURL:"/api",withCredentials:!0,timeout:5e3});V.interceptors.request.use(function(e){return bt.getters.token&&(e.headers["X-Token"]=Object(A["a"])()),e},function(e){return console.log(e),Promise.reject(e)}),V.interceptors.response.use(function(e){var t=e.data;return 2e4!==t.code?(Object(i["Message"])({message:t.message||"error",type:"error",duration:5e3}),50008!==t.code&&50012!==t.code&&50014!==t.code||i["MessageBox"].confirm("You have been logged out, you can cancel to stay on this page, or log in again","Confirm logout",{confirmButtonText:"Re-Login",cancelButtonText:"Cancel",type:"warning"}).then(function(){bt.dispatch("user/resetToken").then(function(){location.reload()})}),Promise.reject(t.message||"error")):t},function(e){return console.log("err"+e),Object(i["Message"])({message:e.message,type:"error",duration:5e3}),Promise.reject(e)});var j=V;function I(e){return j({url:"/user/login",method:"post",data:e})}function P(e){return j({url:"/user/info",method:"get",params:{token:e}})}function N(){return j({url:"/user/logout",method:"post"})}var D=n("8c4f"),R=function(){var e=this,t=e.$createElement,n=e._self._c||t;return n("div",{staticClass:"app-wrapper",class:e.classObj},["mobile"===e.device&&e.sidebar.opened?n("div",{staticClass:"drawer-bg",on:{click:e.handleClickOutside}}):e._e(),e._v(" "),n("sidebar",{staticClass:"sidebar-container"}),e._v(" "),n("div",{staticClass:"main-container"},[n("div",{class:{"fixed-header":e.fixedHeader}},[n("navbar")],1),e._v(" "),n("app-main")],1)],1)},q=[],G=function(){var e=this,t=e.$createElement,n=e._self._c||t;return n("div",{staticClass:"navbar"},[n("hamburger",{staticClass:"hamburger-container",attrs:{"is-active":e.sidebar.opened},on:{toggleClick:e.toggleSideBar}}),e._v(" "),n("breadcrumb",{staticClass:"breadcrumb-container"}),e._v(" "),n("div",{staticClass:"right-menu"},[n("el-button",{staticStyle:{"margin-right":"10px"},attrs:{type:"text"},on:{click:e.doLogout}},[e._v("退出")])],1)],1)},F=[],U=n("db72"),K=function(){var e=this,t=e.$createElement,n=e._self._c||t;return n("el-breadcrumb",{staticClass:"app-breadcrumb",attrs:{separator:"/"}},[n("transition-group",{attrs:{name:"breadcrumb"}},e._l(e.levelList,function(t,a){return n("el-breadcrumb-item",{key:t.path},["noRedirect"===t.redirect||a==e.levelList.length-1?n("span",{staticClass:"no-redirect"},[e._v(e._s(t.meta.title))]):n("a",{on:{click:function(n){return n.preventDefault(),e.handleLink(t)}}},[e._v(e._s(t.meta.title))])])}),1)],1)},W=[],J=n("bd11"),X=n.n(J),Y={data:function(){return{levelList:null}},watch:{$route:function(){this.getBreadcrumb()}},created:function(){this.getBreadcrumb()},methods:{getBreadcrumb:function(){var e=this.$route.matched.filter(function(e){return e.meta&&e.meta.title}),t=e[0];this.isDashboard(t)||(e=[{path:"/dashboard",meta:{title:"Dashboard"}}].concat(e)),this.levelList=e.filter(function(e){return e.meta&&e.meta.title&&!1!==e.meta.breadcrumb})},isDashboard:function(e){var t=e&&e.name;return!!t&&t.trim().toLocaleLowerCase()==="Dashboard".toLocaleLowerCase()},pathCompile:function(e){var t=this.$route.params,n=X.a.compile(e);return n(t)},handleLink:function(e){var t=e.redirect,n=e.path;t?this.$router.push(t):this.$router.push(this.pathCompile(n))}}},Q=Y,Z=(n("34c8"),Object(m["a"])(Q,K,W,!1,null,"62cc9144",null)),ee=Z.exports,te=function(){var e=this,t=e.$createElement,n=e._self._c||t;return n("div",{staticStyle:{padding:"0 15px"},on:{click:e.toggleClick}},[n("svg",{staticClass:"hamburger",class:{"is-active":e.isActive},attrs:{viewBox:"0 0 1024 1024",xmlns:"http://www.w3.org/2000/svg",width:"64",height:"64"}},[n("path",{attrs:{d:"M408 442h480c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8H408c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8zm-8 204c0 4.4 3.6 8 8 8h480c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8H408c-4.4 0-8 3.6-8 8v56zm504-486H120c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0 632H120c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zM142.4 642.1L298.7 519a8.84 8.84 0 0 0 0-13.9L142.4 381.9c-5.8-4.6-14.4-.5-14.4 6.9v246.3a8.9 8.9 0 0 0 14.4 7z"}})])])},ne=[],ae={name:"Hamburger",props:{isActive:{type:Boolean,default:!1}},methods:{toggleClick:function(){this.$emit("toggleClick")}}},ie=ae,oe=(n("186a"),Object(m["a"])(ie,te,ne,!1,null,"49e15297",null)),re=oe.exports,ce={components:{Breadcrumb:ee,Hamburger:re},computed:Object(U["a"])({},Object(p["b"])(["sidebar","avatar"])),methods:{toggleSideBar:function(){this.$store.dispatch("app/toggleSideBar")},doLogout:function(){this.logout()}}},se=ce,ue=(n("405a"),Object(m["a"])(se,G,F,!1,null,"1eff5c58",null)),le=ue.exports,de=function(){var e=this,t=e.$createElement,n=e._self._c||t;return n("div",{class:{"has-logo":e.showLogo}},[e.showLogo?n("logo",{attrs:{collapse:e.isCollapse}}):e._e(),e._v(" "),n("el-scrollbar",{attrs:{"wrap-class":"scrollbar-wrapper"}},[n("el-menu",{attrs:{"default-active":e.activeMenu,collapse:e.isCollapse,"background-color":e.variables.menuBg,"text-color":e.variables.menuText,"unique-opened":!1,"active-text-color":e.variables.menuActiveText,"collapse-transition":!1,mode:"vertical"}},e._l(e.routes,function(e){return n("sidebar-item",{key:e.path,attrs:{item:e,"base-path":e.path}})}),1)],1)],1)},me=[],he=function(){var e=this,t=e.$createElement,n=e._self._c||t;return n("div",{staticClass:"sidebar-logo-container",class:{collapse:e.collapse}},[n("transition",{attrs:{name:"sidebarLogoFade"}},[e.collapse?n("router-link",{key:"collapse",staticClass:"sidebar-logo-link",attrs:{to:"/"}},[e.logo?n("img",{staticClass:"sidebar-logo",attrs:{src:e.logo}}):n("h1",{staticClass:"sidebar-title"},[e._v(e._s(e.title)+" ")])]):n("router-link",{key:"expand",staticClass:"sidebar-logo-link",attrs:{to:"/"}},[e.logo?n("img",{staticClass:"sidebar-logo",attrs:{src:e.logo}}):e._e(),e._v(" "),n("h1",{staticClass:"sidebar-title"},[e._v(e._s(e.title)+" ")])])],1)],1)},fe=[],pe={name:"SidebarLogo",props:{collapse:{type:Boolean,required:!0}},data:function(){return{title:"SOP Admin",logo:"https://wpimg.wallstcn.com/69a1c46c-eb1c-4b46-8bd4-e9e686ef5251.png"}}},ve=pe,be=(n("7c27"),Object(m["a"])(ve,he,fe,!1,null,"b905289c",null)),ge=be.exports,we=function(){var e=this,t=e.$createElement,n=e._self._c||t;return e.item.hidden?e._e():n("div",{staticClass:"menu-wrapper"},[!e.hasOneShowingChild(e.item.children,e.item)||e.onlyOneChild.children&&!e.onlyOneChild.noShowingChildren||e.item.alwaysShow?n("el-submenu",{ref:"subMenu",attrs:{index:e.resolvePath(e.item.path),"popper-append-to-body":""}},[n("template",{slot:"title"},[e.item.meta?n("item",{attrs:{icon:e.item.meta&&e.item.meta.icon,title:e.item.meta.title}}):e._e()],1),e._v(" "),e._l(e.item.children,function(t){return n("sidebar-item",{key:t.path,staticClass:"nest-menu",attrs:{"is-nest":!0,item:t,"base-path":e.resolvePath(t.path)}})})],2):[e.onlyOneChild.meta?n("app-link",{attrs:{to:e.resolvePath(e.onlyOneChild.path)}},[n("el-menu-item",{class:{"submenu-title-noDropdown":!e.isNest},attrs:{index:e.resolvePath(e.onlyOneChild.path)}},[n("item",{attrs:{icon:e.onlyOneChild.meta.icon||e.item.meta&&e.item.meta.icon,title:e.onlyOneChild.meta.title}})],1)],1):e._e()]],2)},xe=[],ye=n("df7c"),ke=n.n(ye);function Ce(e){return/^(https?:|mailto:|tel:)/.test(e)}var _e,Oe,Se={name:"MenuItem",functional:!0,props:{icon:{type:String,default:""},title:{type:String,default:""}},render:function(e,t){var n=t.props,a=n.icon,i=n.title,o=[];return a&&o.push(e("svg-icon",{attrs:{"icon-class":a}})),i&&o.push(e("span",{slot:"title"},[i])),o}},ze=Se,Be=Object(m["a"])(ze,_e,Oe,!1,null,null,null),Me=Be.exports,He=function(){var e=this,t=e.$createElement,n=e._self._c||t;return n("component",e._b({},"component",e.linkProps(e.to),!1),[e._t("default")],2)},Te=[],Ee={props:{to:{type:String,required:!0}},methods:{linkProps:function(e){return Ce(e)?{is:"a",href:e,target:"_blank",rel:"noopener"}:{is:"router-link",to:e}}}},Le=Ee,$e=Object(m["a"])(Le,He,Te,!1,null,null,null),Ae=$e.exports,Ve={computed:{device:function(){return this.$store.state.app.device}},mounted:function(){this.fixBugIniOS()},methods:{fixBugIniOS:function(){var e=this,t=this.$refs.subMenu;if(t){var n=t.handleMouseleave;t.handleMouseleave=function(t){"mobile"!==e.device&&n(t)}}}}},je={name:"SidebarItem",components:{Item:Me,AppLink:Ae},mixins:[Ve],props:{item:{type:Object,required:!0},isNest:{type:Boolean,default:!1},basePath:{type:String,default:""}},data:function(){return this.onlyOneChild=null,{}},methods:{hasOneShowingChild:function(){var e=this,t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:[],n=arguments.length>1?arguments[1]:void 0,a=t.filter(function(t){return!t.hidden&&(e.onlyOneChild=t,!0)});return 1===a.length||0===a.length&&(this.onlyOneChild=Object(U["a"])({},n,{path:"",noShowingChildren:!0}),!0)},resolvePath:function(e){return Ce(e)?e:Ce(this.basePath)?this.basePath:ke.a.resolve(this.basePath,e)}}},Ie=je,Pe=Object(m["a"])(Ie,we,xe,!1,null,null,null),Ne=Pe.exports,De=n("cf1e"),Re=n.n(De),qe={components:{SidebarItem:Ne,Logo:ge},computed:Object(U["a"])({},Object(p["b"])(["sidebar"]),{routes:function(){return this.$router.options.routes},activeMenu:function(){var e=this.$route,t=e.meta,n=e.path;return t.activeMenu?t.activeMenu:n},showLogo:function(){return this.$store.state.settings.sidebarLogo},variables:function(){return Re.a},isCollapse:function(){return!this.sidebar.opened}})},Ge=qe,Fe=Object(m["a"])(Ge,de,me,!1,null,null,null),Ue=Fe.exports,Ke=function(){var e=this,t=e.$createElement,n=e._self._c||t;return n("section",{staticClass:"app-main"},[n("transition",{attrs:{name:"fade-transform",mode:"out-in"}},[n("router-view",{key:e.key})],1)],1)},We=[],Je={name:"AppMain",computed:{key:function(){return this.$route.fullPath}}},Xe=Je,Ye=(n("50be"),Object(m["a"])(Xe,Ke,We,!1,null,"43c24f68",null)),Qe=Ye.exports,Ze=document,et=Ze.body,tt=992,nt={watch:{$route:function(e){"mobile"===this.device&&this.sidebar.opened&&bt.dispatch("app/closeSideBar",{withoutAnimation:!1})}},beforeMount:function(){window.addEventListener("resize",this.$_resizeHandler)},beforeDestroy:function(){window.removeEventListener("resize",this.$_resizeHandler)},mounted:function(){var e=this.$_isMobile();e&&(bt.dispatch("app/toggleDevice","mobile"),bt.dispatch("app/closeSideBar",{withoutAnimation:!0}))},methods:{$_isMobile:function(){var e=et.getBoundingClientRect();return e.width-1'});r.a.add(c);t["default"]=c},"9f2b":function(e,t,n){"use strict";var a=n("bf90"),i=n.n(a);i.a},b20f:function(e,t,n){e.exports={menuText:"#bfcbd9",menuActiveText:"#409EFF",subMenuActiveText:"#f4f4f5",menuBg:"#304156",menuHover:"#263445",subMenuBg:"#1f2d3d",subMenuHover:"#001528",sideBarWidth:"210px"}},b3b5:function(e,t,n){"use strict";n.r(t);var a=n("e017"),i=n.n(a),o=n("21a1"),r=n.n(o),c=new i.a({id:"icon-user",use:"icon-user-usage",viewBox:"0 0 130 130",content:''});r.a.add(c);t["default"]=c},bf90:function(e,t,n){},cf1e:function(e,t,n){e.exports={menuText:"#bfcbd9",menuActiveText:"#409EFF",subMenuActiveText:"#f4f4f5",menuBg:"#304156",menuHover:"#263445",subMenuBg:"#1f2d3d",subMenuHover:"#001528",sideBarWidth:"210px"}},d7ec:function(e,t,n){"use strict";n.r(t);var a=n("e017"),i=n.n(a),o=n("21a1"),r=n.n(o),c=new i.a({id:"icon-eye-open",use:"icon-eye-open-usage",viewBox:"0 0 1024 1024",content:''});r.a.add(c);t["default"]=c},dc52:function(e,t,n){},dcf8:function(e,t,n){"use strict";n.r(t);var a=n("e017"),i=n.n(a),o=n("21a1"),r=n.n(o),c=new i.a({id:"icon-nested",use:"icon-nested-usage",viewBox:"0 0 128 128",content:''});r.a.add(c);t["default"]=c},eb1b:function(e,t,n){"use strict";n.r(t);var a=n("e017"),i=n.n(a),o=n("21a1"),r=n.n(o),c=new i.a({id:"icon-form",use:"icon-form-usage",viewBox:"0 0 128 128",content:''});r.a.add(c);t["default"]=c},f782:function(e,t,n){"use strict";n.r(t);var a=n("e017"),i=n.n(a),o=n("21a1"),r=n.n(o),c=new i.a({id:"icon-dashboard",use:"icon-dashboard-usage",viewBox:"0 0 128 100",content:''});r.a.add(c);t["default"]=c}},[[0,"runtime","chunk-elementUI","chunk-libs"]]]); \ No newline at end of file diff --git a/sop-admin/sop-admin-server/src/main/resources/public/static/js/chunk-25908fca.eac43cde.js b/sop-admin/sop-admin-server/src/main/resources/public/static/js/chunk-25908fca.eac43cde.js deleted file mode 100644 index ae542f28..00000000 --- a/sop-admin/sop-admin-server/src/main/resources/public/static/js/chunk-25908fca.eac43cde.js +++ /dev/null @@ -1 +0,0 @@ -(window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["chunk-25908fca"],{"021d":function(e,t,a){},"0bac":function(e,t,a){"use strict";var s=a("021d"),i=a.n(s);i.a},cb56:function(e,t,a){"use strict";a.r(t);var s=function(){var e=this,t=e.$createElement,a=e._self._c||t;return a("div",{staticClass:"app-container"},[a("el-form",{staticClass:"demo-form-inline",attrs:{inline:!0,model:e.searchFormData,size:"mini"}},[a("el-form-item",{attrs:{label:"AppId"}},[a("el-input",{staticStyle:{width:"250px"},attrs:{clearable:!0,placeholder:"AppId"},model:{value:e.searchFormData.appKey,callback:function(t){e.$set(e.searchFormData,"appKey",t)},expression:"searchFormData.appKey"}})],1),e._v(" "),a("el-form-item",[a("el-button",{attrs:{type:"primary",icon:"el-icon-search"},on:{click:e.onSearchTable}},[e._v("查询")])],1)],1),e._v(" "),a("el-button",{staticStyle:{"margin-bottom":"10px"},attrs:{type:"primary",size:"mini",icon:"el-icon-plus"},on:{click:e.onAdd}},[e._v("新增ISV")]),e._v(" "),a("el-table",{attrs:{data:e.pageInfo.list,border:"",fit:"","highlight-current-row":""}},[a("el-table-column",{attrs:{prop:"appKey",label:"AppId",width:"250"}}),e._v(" "),a("el-table-column",{attrs:{prop:"",label:"秘钥",width:"80"},scopedSlots:e._u([{key:"default",fn:function(t){return[a("el-button",{attrs:{type:"text",size:"mini"},on:{click:function(a){return e.onShowKeys(t.row)}}},[e._v("查看")])]}}])}),e._v(" "),a("el-table-column",{attrs:{prop:"roleList",label:"角色",width:"150","show-overflow-tooltip":!0},scopedSlots:e._u([{key:"default",fn:function(t){return[a("span",{domProps:{innerHTML:e._s(e.roleRender(t.row))}})]}}])}),e._v(" "),a("el-table-column",{attrs:{prop:"status",label:"状态",width:"80"},scopedSlots:e._u([{key:"default",fn:function(t){return[1===t.row.status?a("span",{staticStyle:{color:"#67C23A"}},[e._v("启用")]):e._e(),e._v(" "),2===t.row.status?a("span",{staticStyle:{color:"#F56C6C"}},[e._v("禁用")]):e._e()]}}])}),e._v(" "),a("el-table-column",{attrs:{prop:"gmtCreate",label:"添加时间",width:"160"}}),e._v(" "),a("el-table-column",{attrs:{prop:"remark",label:"备注",width:"200","show-overflow-tooltip":!0}}),e._v(" "),a("el-table-column",{attrs:{label:"操作",width:"200"},scopedSlots:e._u([{key:"default",fn:function(t){return[a("el-button",{attrs:{type:"text",size:"mini"},on:{click:function(a){return e.onTableUpdate(t.row)}}},[e._v("修改")]),e._v(" "),a("el-button",{attrs:{type:"text",size:"mini"},on:{click:function(a){return e.onKeysUpdate(t.row)}}},[e._v("秘钥管理")]),e._v(" "),a("el-button",{attrs:{type:"text",size:"mini"},on:{click:function(a){return e.onExportKeys(t.row)}}},[e._v("导出秘钥")])]}}])})],1),e._v(" "),a("el-pagination",{staticStyle:{"margin-top":"5px"},attrs:{background:"","current-page":e.searchFormData.pageIndex,"page-size":e.searchFormData.pageSize,"page-sizes":[5,10,20,40],total:e.pageInfo.total,layout:"total, sizes, prev, pager, next"},on:{"size-change":e.onSizeChange,"current-change":e.onPageIndexChange}}),e._v(" "),a("el-dialog",{attrs:{title:e.isvDialogTitle,visible:e.isvDialogVisible,"close-on-click-modal":!1},on:{"update:visible":function(t){e.isvDialogVisible=t},close:e.onIsvDialogClose}},[a("el-form",{ref:"isvForm",attrs:{rules:e.rulesIsvForm,model:e.isvDialogFormData,"label-width":"120px",size:"mini"}},[a("el-form-item",{attrs:{label:"appId"}},[0===e.isvDialogFormData.id?a("span",{staticStyle:{color:"gray"}},[e._v("(系统自动生成)")]):a("span",[e._v(e._s(e.isvDialogFormData.appKey))])]),e._v(" "),a("el-form-item",{attrs:{label:"角色"}},[a("el-checkbox-group",{model:{value:e.isvDialogFormData.roleCode,callback:function(t){e.$set(e.isvDialogFormData,"roleCode",t)},expression:"isvDialogFormData.roleCode"}},e._l(e.roles,function(t){return a("el-checkbox",{key:t.roleCode,attrs:{label:t.roleCode}},[e._v(e._s(t.description))])}),1)],1),e._v(" "),a("el-form-item",{attrs:{label:"备注",prop:"remark"}},[a("el-input",{attrs:{type:"textarea"},model:{value:e.isvDialogFormData.remark,callback:function(t){e.$set(e.isvDialogFormData,"remark",t)},expression:"isvDialogFormData.remark"}})],1),e._v(" "),a("el-form-item",{attrs:{label:"状态"}},[a("el-radio-group",{model:{value:e.isvDialogFormData.status,callback:function(t){e.$set(e.isvDialogFormData,"status",t)},expression:"isvDialogFormData.status"}},[a("el-radio",{attrs:{label:1,name:"status"}},[e._v("启用")]),e._v(" "),a("el-radio",{attrs:{label:2,name:"status"}},[e._v("禁用")])],1)],1)],1),e._v(" "),a("div",{staticClass:"dialog-footer",attrs:{slot:"footer"},slot:"footer"},[a("el-button",{on:{click:function(t){e.isvDialogVisible=!1}}},[e._v("取 消")]),e._v(" "),a("el-button",{attrs:{type:"primary",disabled:e.isSaveButtonDisabled},on:{click:e.onIsvDialogSave}},[e._v("保 存")])],1)],1),e._v(" "),a("el-dialog",{attrs:{title:"秘钥信息",visible:e.isvKeysDialogVisible},on:{"update:visible":function(t){e.isvKeysDialogVisible=t},close:function(t){return e.resetForm("isvKeysFrom")}}},[a("el-form",{ref:"isvKeysFrom",staticClass:"key-view",attrs:{model:e.isvKeysFormData,"label-width":"160px",size:"mini"}},[a("el-form-item",{attrs:{label:""}},[a("el-alert",{attrs:{title:"带 ★ 的分配给开发者",type:"warning",closable:!1}})],1),e._v(" "),a("el-form-item",{attrs:{label:e.selfLabel("appId")}},[a("span",[e._v(e._s(e.isvKeysFormData.appKey))])]),e._v(" "),a("el-form-item",{directives:[{name:"show",rawName:"v-show",value:e.showKeys(),expression:"showKeys()"}],attrs:{label:"秘钥格式"}},[1===e.isvKeysFormData.keyFormat?a("span",[e._v("PKCS8(JAVA适用)")]):e._e(),e._v(" "),2===e.isvKeysFormData.keyFormat?a("span",[e._v("PKCS1(非JAVA适用)")]):e._e()]),e._v(" "),a("el-form-item",{directives:[{name:"show",rawName:"v-show",value:2===e.isvKeysFormData.signType,expression:"isvKeysFormData.signType === 2"}],attrs:{label:e.selfLabel("secret")}},[a("span",[e._v(e._s(e.isvKeysFormData.secret))])]),e._v(" "),a("el-tabs",{directives:[{name:"show",rawName:"v-show",value:e.showKeys(),expression:"showKeys()"}],staticClass:"keyTabs",attrs:{type:"card"},model:{value:e.activeName,callback:function(t){e.activeName=t},expression:"activeName"}},[a("el-tab-pane",{attrs:{label:"ISV公私钥",name:"first"}},[a("el-form-item",{attrs:{label:"ISV公钥"}},[a("el-input",{attrs:{type:"textarea",readonly:""},model:{value:e.isvKeysFormData.publicKeyIsv,callback:function(t){e.$set(e.isvKeysFormData,"publicKeyIsv",t)},expression:"isvKeysFormData.publicKeyIsv"}})],1),e._v(" "),a("el-form-item",{attrs:{label:e.selfLabel("ISV私钥")}},[a("el-input",{attrs:{type:"textarea",readonly:""},model:{value:e.isvKeysFormData.privateKeyIsv,callback:function(t){e.$set(e.isvKeysFormData,"privateKeyIsv",t)},expression:"isvKeysFormData.privateKeyIsv"}})],1)],1),e._v(" "),a("el-tab-pane",{attrs:{label:"平台公私钥[可选]",name:"second"}},[a("el-form-item",{attrs:{label:"平台公钥"}},[a("el-input",{attrs:{type:"textarea",readonly:""},model:{value:e.isvKeysFormData.publicKeyPlatform,callback:function(t){e.$set(e.isvKeysFormData,"publicKeyPlatform",t)},expression:"isvKeysFormData.publicKeyPlatform"}})],1),e._v(" "),a("el-form-item",{attrs:{prop:"privateKeyPlatform",label:"平台私钥"}},[a("el-input",{attrs:{type:"textarea",readonly:""},model:{value:e.isvKeysFormData.privateKeyPlatform,callback:function(t){e.$set(e.isvKeysFormData,"privateKeyPlatform",t)},expression:"isvKeysFormData.privateKeyPlatform"}})],1)],1)],1)],1),e._v(" "),a("span",{staticClass:"dialog-footer",attrs:{slot:"footer"},slot:"footer"},[a("el-button",{on:{click:function(t){e.isvKeysDialogVisible=!1}}},[e._v("关 闭")])],1)],1)],1)},i=[],o={data:function(){return{searchFormData:{appKey:"",pageIndex:1,pageSize:10},pageInfo:{list:[],total:0},roles:[],isvDialogVisible:!1,isvDialogTitle:"新增ISV",isvDialogFormData:{id:0,status:1,remark:"",roleCode:[]},rulesIsvForm:{remark:[{min:0,max:100,message:"长度在 1 到 100 个字符",trigger:"blur"}]},activeName:"first",isSaveButtonDisabled:!1,isvKeysDialogVisible:!1,isvKeysFormData:{appKey:"",secret:"",publicKeyIsv:"",privateKeyIsv:"",publicKeyPlatform:"",privateKeyPlatform:"",signType:""}}},created:function(){this.loadTable(),this.loadRouteRole()},methods:{loadTable:function(){this.post("isv.info.page",this.searchFormData,function(e){this.pageInfo=e.data})},loadRouteRole:function(){0===this.roles.length&&this.post("role.listall",{},function(e){this.roles=e.data})},onShowKeys:function(e){this.post("isv.keys.get",{appKey:e.appKey},function(e){var t=this;this.isvKeysDialogVisible=!0,this.$nextTick(function(){Object.assign(t.isvKeysFormData,e.data)})})},onSearchTable:function(){this.loadTable()},onTableUpdate:function(e){var t=this;this.isvDialogTitle="修改ISV",this.isvDialogVisible=!0,this.$nextTick(function(){t.post("isv.info.get",{id:e.id},function(e){for(var t=e.data,a=t.roleList,s=[],i=0;i0?a("el-link",{staticStyle:{"text-decoration":"underline"},attrs:{underline:!1,type:"danger"},on:{click:function(a){return t.onShowErrorDetail(e.row)}}},[t._v("\n "+t._s(e.row.errorCount)+"\n ")]):t._e(),t._v(" "),0===e.row.errorCount?a("span",[t._v("0")]):t._e()]}}])},[a("template",{slot:"header"},[t._v("\n 失败次数\n "),a("el-tooltip",{attrs:{effect:"dark",content:"只统计微服务返回的未知错误,JSR-303验证错误算作成功",placement:"top-end"}},[a("i",{staticClass:"el-icon-question",staticStyle:{cursor:"pointer"}})])],1)],2)],1),t._v(" "),a("el-dialog",{attrs:{title:"错误详情",visible:t.logDetailVisible,width:"60%"},on:{"update:visible":function(e){t.logDetailVisible=e}}},[a("div",{staticStyle:{"overflow-x":"auto"},domProps:{innerHTML:t._s(t.errorMsgDetail)}}),t._v(" "),a("div",{staticClass:"dialog-footer",attrs:{slot:"footer"},slot:"footer"},[a("el-button",{attrs:{type:"primary"},on:{click:function(e){t.logDetailVisible=!1}}},[t._v("关 闭")])],1)])],1)},o=[],r={data:function(){return{searchFormData:{routeId:""},tableData:[],logDetailVisible:!1,errorMsgDetail:""}},created:function(){this.loadTable()},methods:{loadTable:function(){this.post("monitor.data.list",this.searchFormData,function(t){var e=t.data;this.tableData=e.monitorInfoData})},onShowErrorDetail:function(t){var e=t.errorMsgList;this.errorMsgDetail=e.length>0?e.join("
"):"无内容",this.logDetailVisible=!0}}},i=r,n=a("2877"),s=Object(n["a"])(i,l,o,!1,null,null,null);e["default"]=s.exports}}]); \ No newline at end of file diff --git a/sop-admin/sop-admin-server/src/main/resources/public/static/js/chunk-2d0d6219.113d6c0f.js b/sop-admin/sop-admin-server/src/main/resources/public/static/js/chunk-2d0d6219.113d6c0f.js new file mode 100644 index 00000000..fb8fc907 --- /dev/null +++ b/sop-admin/sop-admin-server/src/main/resources/public/static/js/chunk-2d0d6219.113d6c0f.js @@ -0,0 +1 @@ +(window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["chunk-2d0d6219"],{"70f0":function(t,e,o){"use strict";o.r(e);var a=function(){var t=this,e=t.$createElement,o=t._self._c||e;return o("div",{staticClass:"app-container"},[o("el-form",{attrs:{size:"mini"}},[o("el-form-item",[o("el-button",{attrs:{type:"primary",icon:"el-icon-upload"},on:{click:t.onAddSdk}},[t._v("发布SDK")])],1)],1),t._v(" "),o("el-table",{attrs:{data:t.list,border:""}},[o("el-table-column",{attrs:{prop:"name",label:"SDK",width:"120"}}),t._v(" "),o("el-table-column",{attrs:{prop:"version",label:"版本",width:"120"}}),t._v(" "),o("el-table-column",{attrs:{prop:"content",label:"下载地址"},scopedSlots:t._u([{key:"default",fn:function(e){return[o("el-link",{attrs:{type:"primary",href:e.row.content,target:"_blank"}},[t._v(t._s(e.row.content))])]}}])}),t._v(" "),o("el-table-column",{attrs:{label:"操作",width:"150",align:"center"},scopedSlots:t._u([{key:"default",fn:function(e){return[o("el-button",{attrs:{type:"text",size:"mini"},on:{click:function(o){return t.onSdkUpdate(e.row)}}},[t._v("编辑")]),t._v(" "),o("el-button",{attrs:{type:"text",size:"mini"},on:{click:function(o){return t.onSdkDelete(e.row)}}},[t._v("删除")])]}}])})],1),t._v(" "),o("el-dialog",{attrs:{title:t.sdkDlgTitle,visible:t.sdkDlgAddShow,"close-on-click-modal":!1},on:{"update:visible":function(e){t.sdkDlgAddShow=e},close:function(e){return t.resetForm("sdkAddForm")}}},[o("el-form",{ref:"sdkAddForm",attrs:{model:t.sdkFormAddData,rules:t.sdkFormRule,"label-width":"100px"}},[o("el-form-item",{attrs:{prop:"name",label:"选择语言"}},[o("el-select",{attrs:{placeholder:"请选择"},model:{value:t.sdkFormAddData.name,callback:function(e){t.$set(t.sdkFormAddData,"name",e)},expression:"sdkFormAddData.name"}},t._l(t.sdkConfigs,function(t){return o("el-option",{key:t.name,attrs:{label:t.name,value:t.name}})}),1)],1),t._v(" "),o("el-form-item",{attrs:{prop:"version",label:"版本"}},[o("el-input",{attrs:{maxlength:"30","show-word-limit":"",placeholder:"如:1.0"},model:{value:t.sdkFormAddData.version,callback:function(e){t.$set(t.sdkFormAddData,"version",e)},expression:"sdkFormAddData.version"}})],1),t._v(" "),o("el-form-item",{attrs:{prop:"content",label:"下载地址"}},[o("el-input",{attrs:{maxlength:"100","show-word-limit":""},model:{value:t.sdkFormAddData.content,callback:function(e){t.$set(t.sdkFormAddData,"content",e)},expression:"sdkFormAddData.content"}})],1),t._v(" "),o("el-form-item",{attrs:{prop:"extContent",label:"调用示例"}},[o("el-input",{attrs:{type:"textarea",rows:12,placeholder:"填写SDK调用示例代码,支持markdown语法"},model:{value:t.sdkFormAddData.extContent,callback:function(e){t.$set(t.sdkFormAddData,"extContent",e)},expression:"sdkFormAddData.extContent"}})],1)],1),t._v(" "),o("div",{staticClass:"dialog-footer",attrs:{slot:"footer"},slot:"footer"},[o("el-button",{on:{click:function(e){t.sdkDlgAddShow=!1}}},[t._v("取 消")]),t._v(" "),o("el-button",{attrs:{type:"primary"},on:{click:t.onSubmitForm}},[t._v("保 存")])],1)],1)],1)},n=[],d=(o("7f7f"),function(){return{id:0,name:"",version:"",content:"",extContent:""}}),s={data:function(){return{searchFormData:{},sdkDownloadConfig:[],sdkConfigs:[],sdkDlgTitle:"",sdkDlgAddShow:!1,sdkFormUpdateData:d(),sdkFormAddData:d(),sdkFormLoading:!1,sdkFormRule:{name:[{required:!0,message:"请填名称",trigger:"blur"}],version:[{required:!0,message:"请填版本",trigger:"blur"}],content:[{required:!0,message:"请填写URL",trigger:"blur"}],extContent:[{required:!0,message:"请填写调用示例",trigger:"blur"}]},downloadUrl:"",list:[]}},created:function(){this.loadLangSelector(),this.loadTable()},methods:{loadLangSelector:function(){var t=this;this.getFile("static/sdkConfig.json?q=".concat((new Date).getTime()),function(e){t.sdkConfigs=e.langList})},loadTable:function(){var t=this;this.post("isp.sdk.list",this.searchFormData,function(e){t.list=e.data})},onSizeChange:function(t){this.searchFormData.pageSize=t,this.loadTable()},onPageIndexChange:function(t){this.searchFormData.pageIndex=t,this.loadTable()},onAddSdk:function(){this.sdkDlgTitle="添加SDK",this.sdkFormAddData=d(),this.sdkDlgAddShow=!0},onSdkUpdate:function(t){this.sdkDlgTitle="修改SDK",this.sdkFormAddData=d(),Object.assign(this.sdkFormAddData,t),this.sdkDlgAddShow=!0},onSdkDelete:function(t){var e=this;this.confirm("确认要删除【".concat(t.name,"】吗?"),function(o){e.post("isp.sdk.delete",{id:t.id},function(t){o(),e.tip("删除成功"),e.loadTable()})})},onSubmitForm:function(){var t=this;this.$refs.sdkAddForm.validate(function(e){if(e){var o=t.sdkFormAddData.id?"isp.sdk.update":"isp.sdk.add";t.post(o,t.sdkFormAddData,function(){this.sdkDlgAddShow=!1,this.loadTable()})}})}}},l=s,r=o("2877"),i=Object(r["a"])(l,a,n,!1,null,null,null);e["default"]=i.exports}}]); \ No newline at end of file diff --git a/sop-admin/sop-admin-vue/public/static/sdkConfig.json b/sop-admin/sop-admin-vue/public/static/sdkConfig.json new file mode 100644 index 00000000..bdd7e592 --- /dev/null +++ b/sop-admin/sop-admin-vue/public/static/sdkConfig.json @@ -0,0 +1,24 @@ +{ + "langList": [ + { + "name": "Java" + }, + { + "name": "C#" + }, + { + "name": "C++" + }, + { + "name": "Go" + },{ + "name": "NodeJS" + }, + { + "name": "Python" + }, + { + "name": "Rust" + } + ] +} diff --git a/sop-admin/sop-admin-vue/src/layout/components/Sidebar/index.vue b/sop-admin/sop-admin-vue/src/layout/components/Sidebar/index.vue index da39034f..c40c203c 100644 --- a/sop-admin/sop-admin-vue/src/layout/components/Sidebar/index.vue +++ b/sop-admin/sop-admin-vue/src/layout/components/Sidebar/index.vue @@ -10,6 +10,7 @@ :unique-opened="false" :active-text-color="variables.menuActiveText" :collapse-transition="false" + :default-openeds="opened" mode="vertical" > @@ -33,6 +34,13 @@ export default { routes() { return this.$router.options.routes }, + opened() { + return this.routes.filter(route => { + return route.meta && route.meta.open + }).map(route => { + return route.path + }) + }, activeMenu() { const route = this.$route const { meta, path } = route diff --git a/sop-admin/sop-admin-vue/src/router/index.js b/sop-admin/sop-admin-vue/src/router/index.js index 990abc28..86591ba6 100644 --- a/sop-admin/sop-admin-vue/src/router/index.js +++ b/sop-admin/sop-admin-vue/src/router/index.js @@ -59,7 +59,7 @@ export const constantRoutes = [ path: '/service', component: Layout, name: 'Service', - meta: { title: '服务管理', icon: 'example' }, + meta: { title: '服务管理', icon: 'example', open: true }, children: [ { path: 'list', @@ -90,6 +90,12 @@ export const constantRoutes = [ name: 'Blacklist', component: () => import('@/views/service/ipBlacklist'), meta: { title: 'IP黑名单' } + }, + { + path: 'sdk', + name: 'Sdk', + component: () => import('@/views/service/sdk'), + meta: { title: 'SDK管理' } } ] }, @@ -98,7 +104,7 @@ export const constantRoutes = [ path: '/isv', component: Layout, name: 'Isv', - meta: { title: 'ISV管理', icon: 'user' }, + meta: { title: 'ISV管理', icon: 'user', open: true }, children: [ { path: 'list', diff --git a/sop-admin/sop-admin-vue/src/utils/global.js b/sop-admin/sop-admin-vue/src/utils/global.js index 46a95251..d83c0814 100644 --- a/sop-admin/sop-admin-vue/src/utils/global.js +++ b/sop-admin/sop-admin-vue/src/utils/global.js @@ -94,6 +94,17 @@ Object.assign(Vue.prototype, { } }).catch(function() {}) }, + /** + * 文件必须放在public下面 + * @param path 相对于public文件夹路径,如文件在public/static/sign.md,填:static/sign.md + * @param callback 回调函数,函数参数是文件内容 + */ + getFile: function(path, callback) { + axios.get(path) + .then(function(response) { + callback.call(this, response.data) + }) + }, downloadText(filename, text) { const element = document.createElement('a') element.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(text)) diff --git a/sop-admin/sop-admin-vue/src/views/isv/index.vue b/sop-admin/sop-admin-vue/src/views/isv/index.vue index 1c4e519c..e28bcf0b 100644 --- a/sop-admin/sop-admin-vue/src/views/isv/index.vue +++ b/sop-admin/sop-admin-vue/src/views/isv/index.vue @@ -32,13 +32,28 @@ + + + + diff --git a/sop-admin/sop-admin-vue/src/views/service/sdk.vue b/sop-admin/sop-admin-vue/src/views/service/sdk.vue new file mode 100644 index 00000000..36ec0a3b --- /dev/null +++ b/sop-admin/sop-admin-vue/src/views/service/sdk.vue @@ -0,0 +1,179 @@ + + diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/filter/IndexFilter.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/filter/IndexFilter.java index b21621b1..236752cc 100644 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/filter/IndexFilter.java +++ b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/filter/IndexFilter.java @@ -83,6 +83,7 @@ public class IndexFilter implements WebFilter { ServerRequest serverRequest = ServerWebExchangeUtil.createReadBodyRequest(exchange); // 读取请求体中的内容 Mono modifiedBody = serverRequest.bodyToMono(byte[].class) + .switchIfEmpty(Mono.just("".getBytes())) .flatMap(data -> { // 构建ApiParam ApiParam apiParam = ServerWebExchangeUtil.getApiParam(exchange, data); diff --git a/sop-example/sop-story/pom.xml b/sop-example/sop-story/pom.xml index 524d9f39..70606d68 100644 --- a/sop-example/sop-story/pom.xml +++ b/sop-example/sop-story/pom.xml @@ -53,7 +53,7 @@ com.github.xiaoymin swagger-bootstrap-ui - 1.9.5 + 1.9.6 io.springfox diff --git a/sop-mysql5.6以下版本.sql b/sop-mysql5.6以下版本.sql index 4d7e0fc9..d836c742 100644 --- a/sop-mysql5.6以下版本.sql +++ b/sop-mysql5.6以下版本.sql @@ -19,7 +19,8 @@ DROP TABLE IF EXISTS `admin_user_info`; DROP TABLE IF EXISTS `config_service_route`; DROP TABLE IF EXISTS `monitor_info`; DROP TABLE IF EXISTS `monitor_info_error`; - +DROP TABLE IF EXISTS `user_account`; +DROP TABLE IF EXISTS `isp_resource`; CREATE TABLE `admin_user_info` ( `id` BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT, @@ -246,6 +247,34 @@ CREATE TABLE `monitor_info_error` ( KEY `idx_routeid` (`route_id`) USING BTREE ) ENGINE=InnoDB DEFAULT CHARSET=utf8; +ALTER TABLE `isv_info` ADD COLUMN `user_id` BIGINT NULL DEFAULT 0 COMMENT 'user_account.id' AFTER `remark`; + +CREATE INDEX `idx_userid` USING BTREE ON `isv_info` (`user_id`); + +CREATE TABLE `user_account` ( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, + `username` varchar(128) NOT NULL DEFAULT '' COMMENT '用户名(邮箱)', + `password` varchar(128) NOT NULL DEFAULT '' COMMENT '密码', + `status` tinyint(4) NOT NULL DEFAULT '1' COMMENT '2:邮箱未验证,1:启用,0:禁用', + `gmt_create` DATETIME DEFAULT NULL, + `gmt_modified` DATETIME DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `uk_username` (`username`) USING BTREE +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='用户信息'; + +CREATE TABLE `isp_resource` ( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, + `name` varchar(64) NOT NULL DEFAULT '' COMMENT '资源名称', + `content` varchar(128) NOT NULL DEFAULT '' COMMENT '资源内容(URL)', + `ext_content` text, + `version` varchar(32) NOT NULL DEFAULT '' COMMENT '版本', + `type` tinyint(4) NOT NULL DEFAULT '0' COMMENT '资源类型:0:SDK链接', + `is_deleted` tinyint(4) NOT NULL DEFAULT '0', + `gmt_create` DATETIME DEFAULT NULL, + `gmt_modified` DATETIME DEFAULT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='ISP资源表'; + INSERT INTO `admin_user_info` (`id`, `username`, `password`, `status`, `gmt_create`, `gmt_modified`) VALUES (1,'admin','a62cd510fb9a8a557a27ef279569091f',1,'2019-04-02 19:55:26','2019-04-02 19:55:26'); diff --git a/sop-sdk/sdk-c++/.gitignore b/sop-sdk/sdk-c++/.gitignore new file mode 100644 index 00000000..c23f2db9 --- /dev/null +++ b/sop-sdk/sdk-c++/.gitignore @@ -0,0 +1,27 @@ +/target/ +/cmake-build-debug/ +!.mvn/wrapper/maven-wrapper.jar + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +/build/ + diff --git a/sop-sdk/sdk-c++/CMakeLists.txt b/sop-sdk/sdk-c++/CMakeLists.txt new file mode 100644 index 00000000..afb33d11 --- /dev/null +++ b/sop-sdk/sdk-c++/CMakeLists.txt @@ -0,0 +1,42 @@ +# cmake和CMakeLists.txt教程 +# https://www.jianshu.com/p/cb4f8136a265 +cmake_minimum_required(VERSION 3.16) +project(sdk_cxx) + +set(CMAKE_CXX_STANDARD 14) +set(LIB_SRC + common/OpenClient.cpp + common/OpenClient.h + common/RequestType.h + common/tool.h + common/sign.h + common/RSASign.h + common/RSASign.cpp + common/sha256.hpp + request/BaseRequest.h + request/MemberInfoGetRequest.hpp + thirdparty/base64/base64.h + thirdparty/base64/base64.cpp + thirdparty/CJsonObject/cJSON.c + thirdparty/CJsonObject/cJSON.h + thirdparty/CJsonObject/CJsonObject.hpp + thirdparty/CJsonObject/CJsonObject.cpp + thirdparty/x2struct/x2struct.hpp + common/sign.cpp common/tool.cpp common/sha256.cpp + request/BaseRequest.cpp response/BaseResponse.h response/MemberInfoGetResponse.h) + +# openssl安装路径 +set(OPENSSL_INC_DIR /usr/local/opt/openssl/include) +set(OPENSSL_LINK_DIR /usr/local/opt/openssl/lib) + +include_directories(${OPENSSL_INC_DIR}) +link_directories(${OPENSSL_LINK_DIR}) +link_libraries(ssl crypto) + +# 添加类库 +add_library(lib ${LIB_SRC}) +# 添加可执行文件 +add_executable(sdk_cxx main.cpp) + +# 可执行文件依赖lib库 +target_link_libraries(sdk_cxx lib ssl) \ No newline at end of file diff --git a/sop-sdk/sdk-c++/aa.txt b/sop-sdk/sdk-c++/aa.txt new file mode 100644 index 00000000..524527cb --- /dev/null +++ b/sop-sdk/sdk-c++/aa.txt @@ -0,0 +1 @@ +hello你好123 \ No newline at end of file diff --git a/sop-sdk/sdk-c++/bb.txt b/sop-sdk/sdk-c++/bb.txt new file mode 100644 index 00000000..85f8e329 --- /dev/null +++ b/sop-sdk/sdk-c++/bb.txt @@ -0,0 +1 @@ +文件bb的内容 \ No newline at end of file diff --git a/sop-sdk/sdk-c++/common/OpenClient.cpp b/sop-sdk/sdk-c++/common/OpenClient.cpp new file mode 100644 index 00000000..fbb48e61 --- /dev/null +++ b/sop-sdk/sdk-c++/common/OpenClient.cpp @@ -0,0 +1,133 @@ +#include "OpenClient.h" +#include "httplib.h" +#include "tool.h" +#include "sign.h" + +#include "../thirdparty/CJsonObject/CJsonObject.hpp" + +httplib::Headers headers = { + {"Accept-Encoding", "identity"} +}; + +const string ERROR_NODE = "error_response"; + +OpenClient::OpenClient(const string &appId, const string &privateKeyFilePath, const string &url) { + this->appId = appId; + this->privateKeyFilePath = privateKeyFilePath; + + char *_url = const_cast(url.c_str()); + char *host; + int port; + char *path; + tool::parse_url(_url, &host, &port, &path); + this->hostInfo = HostInfo{ + host = host, + port = port, + path = path + }; +} + +neb::CJsonObject OpenClient::execute(BaseRequest *request) { + return this->execute(request, ""); +} + +neb::CJsonObject OpenClient::execute(BaseRequest *request, const string &token) { + string method = request->getMethod(); + string version = request->getVersion(); + RequestType requestType = request->getRequestType(); + map bizModel = request->bizModel; + // 创建HTTP请求客户端 + httplib::Client cli(this->hostInfo.host, this->hostInfo.port); + // 构建请求参数 + map allParams = this->buildParams(request, token); + char *path = this->hostInfo.path; + string responseBody; + // 如果有文件上传 + if (!request->getFiles().empty()) { + httplib::MultipartFormDataItems items = OpenClient::getMultipartFormDataItems( + allParams, request->getFiles()); + responseBody = cli.Post(path, headers, items)->body; + } else { + switch (requestType) { + case GET: { + responseBody = cli.Get(path, allParams, headers)->body; + break; + } + case POST_FORM: { + responseBody = cli.Post(path, headers, OpenClient::getParams(allParams))->body; + break; + } + case POST_JSON: { + string json = tool::mapToJson(allParams); + responseBody = cli.Post(path, json, "application/json")->body; + break; + } + case POST_FILE: { + httplib::MultipartFormDataItems items = OpenClient::getMultipartFormDataItems( + allParams, request->getFiles()); + responseBody = cli.Post(path, headers, items)->body; + } + } + } + return OpenClient::parseResponse(responseBody, request); +} + + +httplib::Params OpenClient::getParams(map allParams) { + httplib::Params params; + map::iterator it; + for (it = allParams.begin(); it != allParams.end(); ++it) { + params.emplace(it->first, it->second); + } + return params; +} + +map OpenClient::buildParams(BaseRequest *request, const string &token) { + map allParams; + allParams["app_id"] = this->appId; + allParams["method"] = request->getMethod(); + allParams["charset"] = "UTF-8"; + allParams["sign_type"] = "RSA2"; + allParams["timestamp"] = tool::getTime(); + allParams["version"] = request->getVersion(); + + if (!token.empty()) { + allParams["app_auth_token"] = token; + } + + map bizModel = request->bizModel; + + allParams.insert(bizModel.begin(), bizModel.end()); + + // 生成签名 + string sign = signutil::createSign(allParams, this->privateKeyFilePath, "RSA2"); + allParams["sign"] = sign; + return allParams; +} + +httplib::MultipartFormDataItems +OpenClient::getMultipartFormDataItems(map allParams, vector fileInfoList) { + httplib::MultipartFormDataItems items = {}; + map::iterator it; + for (it = allParams.begin(); it != allParams.end(); ++it) { + items.push_back({it->first, it->second, "", ""}); + } + // 添加上传文件 + vector::iterator vit; + for (vit = fileInfoList.begin(); vit != fileInfoList.end(); vit++) { + string content = tool::getFileContent(vit->filepath); + items.push_back({vit->name, content, tool::getFilename(vit->filepath), "application/octet-stream"}); + } + return items; +} + +neb::CJsonObject OpenClient::parseResponse(const string& responseBody, BaseRequest *request) { + neb::CJsonObject oJson(responseBody); + neb::CJsonObject data = oJson[ERROR_NODE]; + if (data.IsEmpty()) { + string method = request->getMethod(); + string nodeName = tool::replace(method.c_str(),".","_") + "_response"; + data = oJson[nodeName]; + } + return data; +} diff --git a/sop-sdk/sdk-c++/common/OpenClient.h b/sop-sdk/sdk-c++/common/OpenClient.h new file mode 100644 index 00000000..df28b1c3 --- /dev/null +++ b/sop-sdk/sdk-c++/common/OpenClient.h @@ -0,0 +1,66 @@ +#ifndef SDK_CXX_OPENCLIENT_H +#define SDK_CXX_OPENCLIENT_H + +#include +#include "httplib.h" +#include "../request/BaseRequest.h" +#include "../thirdparty/CJsonObject/CJsonObject.hpp" + +using namespace std; + +struct HostInfo { + string host; + int port; + char *path; +}; + +/** + * 请求客户端 + */ +class OpenClient { +private: + /** 应用id */ + string appId; + /** 私钥文件路径 */ + string privateKeyFilePath; + +public: + /** + * 创建客户端对象 + * @param appId 应用ID + * @param privateKeyFilePath 应用私钥路径 + * @param url 请求URL + */ + OpenClient(const string &appId, const string &privateKeyFilePath, const string &url); + + /** + * 发送请求 + * @param request 请求对象,BaseRequest的子类 + * @param token token + * @return 返回响应结果 + */ + neb::CJsonObject execute(BaseRequest *request, const string& token); + + /** + * 发送请求 + * @param request 请求对象,BaseRequest的子类 + * @return 返回响应结果 + */ + neb::CJsonObject execute(BaseRequest *request); + +private: + + HostInfo hostInfo; + + map buildParams(BaseRequest *request, const string& token); + + static httplib::MultipartFormDataItems + getMultipartFormDataItems(map allParams, vector fileInfoList); + + static httplib::Params getParams(map params); + + static neb::CJsonObject parseResponse(const string& responseBody,BaseRequest *request); +}; + + +#endif //SDK_CXX_OPENCLIENT_H diff --git a/sop-sdk/sdk-c++/common/RSASign.cpp b/sop-sdk/sdk-c++/common/RSASign.cpp new file mode 100644 index 00000000..232270ab --- /dev/null +++ b/sop-sdk/sdk-c++/common/RSASign.cpp @@ -0,0 +1,94 @@ +#include "stdafx.h" +#include "RSASign.h" +#include "../thirdparty/base64/base64.h" + +RSA* GetPublicKeyEx(char* szPath) +{ + RSA *pubkey = RSA_new(); + + BIO *pubio; + + pubio = BIO_new_file(szPath, "rb"); + pubkey = PEM_read_bio_RSAPublicKey(pubio, &pubkey, NULL, NULL); + + BIO_free(pubio); + + return pubkey; +} + +RSA* GetPrivateKeyEx(char* szPath) +{ + RSA *prikey = RSA_new(); + + BIO *priio; + + priio = BIO_new_file(szPath, "rb"); + prikey = PEM_read_bio_RSAPrivateKey(priio, &prikey, NULL, NULL); + + BIO_free(priio); + + return prikey; +} + +bool RSASignAction(const string& strEnData, char *privateKeyFilePath, string &strSigned) +{ + int nlen = strEnData.length(); + RSA *prsa = NULL; + if (NULL == (prsa = GetPrivateKeyEx(privateKeyFilePath))) + { + RSA_free(prsa); + printf("获取私钥失败\n"); + return false; + } + + char szTmp[1024] = { 0 }; + //对待签名数据做SHA1摘要 + SHA256((const unsigned char*)strEnData.c_str(), nlen, (unsigned char*)szTmp); + int nLength; + unsigned int nLengthRet; + char szTmp1[1024] = { 0 }; //位数一定不能小于等于RSA的128位,没有‘\0’,数据会增加后面位数 +// unsigned char *szTmp1 = {0}; + nLength = RSA_sign(NID_sha256, (unsigned char *)szTmp, 32, (unsigned char *)szTmp1, &nLengthRet, prsa); + if (nLength != 1) + { + RSA_free(prsa); + return false; + } + strSigned = base64_encode((unsigned char *)szTmp1, strlen((const char *)szTmp1)); + RSA_free(prsa); + + return true; +} + +bool RSAVerifyAction(string strEnData, string &strSigned) +{ + int nlen = strEnData.length(); + printf("验签的原始数据:[%s]\n", strEnData.c_str()); + + RSA *prsa = NULL; + if (NULL == (prsa = GetPublicKeyEx(PUBLIC_KEY_FILE_EX))) + { + RSA_free(prsa); + printf("获取公钥失败\n"); + return -1; + } + + //对待签名数据做SHA1摘要 + char szTmp[1024] = { 0 }; + int nLen = strEnData.length(); + SHA256((const unsigned char*)strEnData.c_str(), nLen, (unsigned char*)szTmp); + + int nLength; + unsigned int nLengthRet = strSigned.length(); + printf("nLengthRet2 = %d\n", nLengthRet); + nLength = RSA_verify(NID_sha256, (unsigned char *)szTmp, 32, (unsigned char*)strSigned.c_str(), nLengthRet, prsa); + if (nLength != 1) + { + RSA_free(prsa); + return false; + } + + RSA_free(prsa); + + return true; +} \ No newline at end of file diff --git a/sop-sdk/sdk-c++/common/RSASign.h b/sop-sdk/sdk-c++/common/RSASign.h new file mode 100644 index 00000000..00bd7e6b --- /dev/null +++ b/sop-sdk/sdk-c++/common/RSASign.h @@ -0,0 +1,19 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +using namespace std; + +#define PUBLIC_KEY_FILE_EX "publicEx.pem" //公钥文件 +#define PRIVATE_KEY_FILE_EX "privateEx.pem" //私钥文件 + +RSA* GetPublicKeyEx(char* szPath); +RSA* GetPrivateKeyEx(char* szPath); + +bool RSASignAction(const string& strEnData,char *privateKeyFilePath, string &strSigned); +bool RSAVerifyAction(string strEnData, string &strSigned); diff --git a/sop-sdk/sdk-c++/common/RequestType.h b/sop-sdk/sdk-c++/common/RequestType.h new file mode 100644 index 00000000..0d4439c1 --- /dev/null +++ b/sop-sdk/sdk-c++/common/RequestType.h @@ -0,0 +1,11 @@ +#ifndef SDK_CXX_REQUESTTYPE_H +#define SDK_CXX_REQUESTTYPE_H + +enum RequestType { + GET, + POST_FORM, + POST_JSON, + POST_FILE +}; + +#endif //SDK_CXX_REQUESTTYPE_H diff --git a/sop-sdk/sdk-c++/common/httplib.h b/sop-sdk/sdk-c++/common/httplib.h new file mode 100644 index 00000000..20cfd729 --- /dev/null +++ b/sop-sdk/sdk-c++/common/httplib.h @@ -0,0 +1,5741 @@ +// +// httplib.h +// +// Copyright (c) 2020 Yuji Hirose. All rights reserved. +// MIT License +// + +#ifndef CPPHTTPLIB_HTTPLIB_H +#define CPPHTTPLIB_HTTPLIB_H + +/* + * Configuration + */ + +#ifndef CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND +#define CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND 5 +#endif + +#ifndef CPPHTTPLIB_KEEPALIVE_TIMEOUT_USECOND +#define CPPHTTPLIB_KEEPALIVE_TIMEOUT_USECOND 0 +#endif + +#ifndef CPPHTTPLIB_KEEPALIVE_MAX_COUNT +#define CPPHTTPLIB_KEEPALIVE_MAX_COUNT 5 +#endif + +#ifndef CPPHTTPLIB_CONNECTION_TIMEOUT_SECOND +#define CPPHTTPLIB_CONNECTION_TIMEOUT_SECOND 300 +#endif + +#ifndef CPPHTTPLIB_CONNECTION_TIMEOUT_USECOND +#define CPPHTTPLIB_CONNECTION_TIMEOUT_USECOND 0 +#endif + +#ifndef CPPHTTPLIB_READ_TIMEOUT_SECOND +#define CPPHTTPLIB_READ_TIMEOUT_SECOND 5 +#endif + +#ifndef CPPHTTPLIB_READ_TIMEOUT_USECOND +#define CPPHTTPLIB_READ_TIMEOUT_USECOND 0 +#endif + +#ifndef CPPHTTPLIB_WRITE_TIMEOUT_SECOND +#define CPPHTTPLIB_WRITE_TIMEOUT_SECOND 5 +#endif + +#ifndef CPPHTTPLIB_WRITE_TIMEOUT_USECOND +#define CPPHTTPLIB_WRITE_TIMEOUT_USECOND 0 +#endif + +#ifndef CPPHTTPLIB_IDLE_INTERVAL_SECOND +#define CPPHTTPLIB_IDLE_INTERVAL_SECOND 0 +#endif + +#ifndef CPPHTTPLIB_IDLE_INTERVAL_USECOND +#ifdef _WIN32 +#define CPPHTTPLIB_IDLE_INTERVAL_USECOND 10000 +#else +#define CPPHTTPLIB_IDLE_INTERVAL_USECOND 0 +#endif +#endif + +#ifndef CPPHTTPLIB_REQUEST_URI_MAX_LENGTH +#define CPPHTTPLIB_REQUEST_URI_MAX_LENGTH 8192 +#endif + +#ifndef CPPHTTPLIB_REDIRECT_MAX_COUNT +#define CPPHTTPLIB_REDIRECT_MAX_COUNT 20 +#endif + +#ifndef CPPHTTPLIB_PAYLOAD_MAX_LENGTH +#define CPPHTTPLIB_PAYLOAD_MAX_LENGTH ((std::numeric_limits::max)()) +#endif + +#ifndef CPPHTTPLIB_RECV_BUFSIZ +#define CPPHTTPLIB_RECV_BUFSIZ size_t(4096u) +#endif + +#ifndef CPPHTTPLIB_THREAD_POOL_COUNT +#define CPPHTTPLIB_THREAD_POOL_COUNT \ + ((std::max)(8u, std::thread::hardware_concurrency() > 0 \ + ? std::thread::hardware_concurrency() - 1 \ + : 0)) +#endif + +// Prefer gnu::deprecated, otherwise gcc complains if we use +// [[deprecated]] together with pedantic. +#ifndef CPPHTTPLIB_DEPRECATED +#if defined(__has_cpp_attribute) +#if __has_cpp_attribute(gnu::deprecated) +#define CPPHTTPLIB_DEPRECATED [[gnu::deprecated]] +#else +#if __has_cpp_attribute(deprecated) +#define CPPHTTPLIB_DEPRECATED [[deprecated]] +#else +#define CPPHTTPLIB_DEPRECATED +#endif +#endif +#else +#define CPPHTTPLIB_DEPRECATED +#endif +#endif + +/* + * Headers + */ + +#ifdef _WIN32 +#ifndef _CRT_SECURE_NO_WARNINGS +#define _CRT_SECURE_NO_WARNINGS +#endif //_CRT_SECURE_NO_WARNINGS + +#ifndef _CRT_NONSTDC_NO_DEPRECATE +#define _CRT_NONSTDC_NO_DEPRECATE +#endif //_CRT_NONSTDC_NO_DEPRECATE + +#if defined(_MSC_VER) +#ifdef _WIN64 +using ssize_t = __int64; +#else +using ssize_t = int; +#endif + +#if _MSC_VER < 1900 +#define snprintf _snprintf_s +#endif +#endif // _MSC_VER + +#ifndef S_ISREG +#define S_ISREG(m) (((m)&S_IFREG) == S_IFREG) +#endif // S_ISREG + +#ifndef S_ISDIR +#define S_ISDIR(m) (((m)&S_IFDIR) == S_IFDIR) +#endif // S_ISDIR + +#ifndef NOMINMAX +#define NOMINMAX +#endif // NOMINMAX + +#include +#include +#include + +#ifndef WSA_FLAG_NO_HANDLE_INHERIT +#define WSA_FLAG_NO_HANDLE_INHERIT 0x80 +#endif + +#ifdef _MSC_VER +#pragma comment(lib, "ws2_32.lib") +#endif + +#ifndef strcasecmp +#define strcasecmp _stricmp +#endif // strcasecmp + +using socket_t = SOCKET; +#ifdef CPPHTTPLIB_USE_POLL +#define poll(fds, nfds, timeout) WSAPoll(fds, nfds, timeout) +#endif + +#else // not _WIN32 + +#include +#include +#include +#include +#include +#ifdef CPPHTTPLIB_USE_POLL +#include +#endif +#include +#include +#include +#include +#include + +using socket_t = int; +#define INVALID_SOCKET (-1) +#endif //_WIN32 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT +#include +#include +#include +#include + +#include +#include +#include + +// #if OPENSSL_VERSION_NUMBER < 0x1010100fL +// #error Sorry, OpenSSL versions prior to 1.1.1 are not supported +// #endif + +#if OPENSSL_VERSION_NUMBER < 0x10100000L +#include +inline const unsigned char *ASN1_STRING_get0_data(const ASN1_STRING *asn1) { + return M_ASN1_STRING_data(asn1); +} +#endif +#endif + +#ifdef CPPHTTPLIB_ZLIB_SUPPORT +#include +#endif +/* + * Declaration + */ +namespace httplib { + +namespace detail { + +struct ci { + bool operator()(const std::string &s1, const std::string &s2) const { + return std::lexicographical_compare( + s1.begin(), s1.end(), s2.begin(), s2.end(), + [](char c1, char c2) { return ::tolower(c1) < ::tolower(c2); }); + } +}; + +} // namespace detail + +using Headers = std::multimap; + +using Params = std::multimap; +using Match = std::smatch; + +using Progress = std::function; + +struct Response; +using ResponseHandler = std::function; + +struct MultipartFormData { + std::string name; + std::string content; + std::string filename; + std::string content_type; +}; +using MultipartFormDataItems = std::vector; +using MultipartFormDataMap = std::multimap; + +class DataSink { +public: + DataSink() : os(&sb_), sb_(*this) {} + + DataSink(const DataSink &) = delete; + DataSink &operator=(const DataSink &) = delete; + DataSink(DataSink &&) = delete; + DataSink &operator=(DataSink &&) = delete; + + std::function write; + std::function done; + std::function is_writable; + std::ostream os; + +private: + class data_sink_streambuf : public std::streambuf { + public: + data_sink_streambuf(DataSink &sink) : sink_(sink) {} + + protected: + std::streamsize xsputn(const char *s, std::streamsize n) { + sink_.write(s, static_cast(n)); + return n; + } + + private: + DataSink &sink_; + }; + + data_sink_streambuf sb_; +}; + +using ContentProvider = + std::function; + +using ChunkedContentProvider = + std::function; + +using ContentReceiver = + std::function; + +using MultipartContentHeader = + std::function; + +class ContentReader { +public: + using Reader = std::function; + using MultipartReader = std::function; + + ContentReader(Reader reader, MultipartReader multipart_reader) + : reader_(reader), multipart_reader_(multipart_reader) {} + + bool operator()(MultipartContentHeader header, + ContentReceiver receiver) const { + return multipart_reader_(header, receiver); + } + + bool operator()(ContentReceiver receiver) const { return reader_(receiver); } + + Reader reader_; + MultipartReader multipart_reader_; +}; + +using Range = std::pair; +using Ranges = std::vector; + +struct Request { + std::string method; + std::string path; + std::string query; + Headers headers; + std::string body; + + std::string remote_addr; + int remote_port = -1; + + // for server + std::string version; + std::string target; + Params params; + MultipartFormDataMap files; + Ranges ranges; + Match matches; + + // for client + size_t redirect_count = CPPHTTPLIB_REDIRECT_MAX_COUNT; + ResponseHandler response_handler; + ContentReceiver content_receiver; + size_t content_length = 0; + ContentProvider content_provider; + Progress progress; + +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + const SSL *ssl; +#endif + + bool has_header(const char *key) const; + std::string get_header_value(const char *key, size_t id = 0) const; + size_t get_header_value_count(const char *key) const; + void set_header(const char *key, const char *val); + void set_header(const char *key, const std::string &val); + + bool has_param(const char *key) const; + std::string get_param_value(const char *key, size_t id = 0) const; + size_t get_param_value_count(const char *key) const; + + bool is_multipart_form_data() const; + + bool has_file(const char *key) const; + MultipartFormData get_file_value(const char *key) const; + + // private members... + size_t authorization_count_ = 0; +}; + +struct Response { + std::string version; + int status = -1; + Headers headers; + std::string body; + + bool has_header(const char *key) const; + std::string get_header_value(const char *key, size_t id = 0) const; + size_t get_header_value_count(const char *key) const; + void set_header(const char *key, const char *val); + void set_header(const char *key, const std::string &val); + + void set_redirect(const char *url, int status = 302); + void set_content(const char *s, size_t n, const char *content_type); + void set_content(std::string s, const char *content_type); + + void set_content_provider( + size_t length, ContentProvider provider, + std::function resource_releaser = [] {}); + + void set_chunked_content_provider( + ChunkedContentProvider provider, + std::function resource_releaser = [] {}); + + Response() = default; + Response(const Response &) = default; + Response &operator=(const Response &) = default; + Response(Response &&) = default; + Response &operator=(Response &&) = default; + ~Response() { + if (content_provider_resource_releaser_) { + content_provider_resource_releaser_(); + } + } + + // private members... + size_t content_length_ = 0; + ContentProvider content_provider_; + std::function content_provider_resource_releaser_; +}; + +class Stream { +public: + virtual ~Stream() = default; + + virtual bool is_readable() const = 0; + virtual bool is_writable() const = 0; + + virtual ssize_t read(char *ptr, size_t size) = 0; + virtual ssize_t write(const char *ptr, size_t size) = 0; + virtual void get_remote_ip_and_port(std::string &ip, int &port) const = 0; + + template + ssize_t write_format(const char *fmt, const Args &... args); + ssize_t write(const char *ptr); + ssize_t write(const std::string &s); +}; + +class TaskQueue { +public: + TaskQueue() = default; + virtual ~TaskQueue() = default; + + virtual void enqueue(std::function fn) = 0; + virtual void shutdown() = 0; + + virtual void on_idle(){}; +}; + +class ThreadPool : public TaskQueue { +public: + explicit ThreadPool(size_t n) : shutdown_(false) { + while (n) { + threads_.emplace_back(worker(*this)); + n--; + } + } + + ThreadPool(const ThreadPool &) = delete; + ~ThreadPool() override = default; + + void enqueue(std::function fn) override { + std::unique_lock lock(mutex_); + jobs_.push_back(fn); + cond_.notify_one(); + } + + void shutdown() override { + // Stop all worker threads... + { + std::unique_lock lock(mutex_); + shutdown_ = true; + } + + cond_.notify_all(); + + // Join... + for (auto &t : threads_) { + t.join(); + } + } + +private: + struct worker { + explicit worker(ThreadPool &pool) : pool_(pool) {} + + void operator()() { + for (;;) { + std::function fn; + { + std::unique_lock lock(pool_.mutex_); + + pool_.cond_.wait( + lock, [&] { return !pool_.jobs_.empty() || pool_.shutdown_; }); + + if (pool_.shutdown_ && pool_.jobs_.empty()) { break; } + + fn = pool_.jobs_.front(); + pool_.jobs_.pop_front(); + } + + assert(true == static_cast(fn)); + fn(); + } + } + + ThreadPool &pool_; + }; + friend struct worker; + + std::vector threads_; + std::list> jobs_; + + bool shutdown_; + + std::condition_variable cond_; + std::mutex mutex_; +}; + +using Logger = std::function; + +using SocketOptions = std::function; + +inline void default_socket_options(socket_t sock) { + int yes = 1; +#ifdef _WIN32 + setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, reinterpret_cast(&yes), + sizeof(yes)); + setsockopt(sock, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, + reinterpret_cast(&yes), sizeof(yes)); +#else +#ifdef SO_REUSEPORT + setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, reinterpret_cast(&yes), + sizeof(yes)); +#else + setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, reinterpret_cast(&yes), + sizeof(yes)); +#endif +#endif +} + +class Server { +public: + using Handler = std::function; + using HandlerWithContentReader = std::function; + using Expect100ContinueHandler = + std::function; + + Server(); + + virtual ~Server(); + + virtual bool is_valid() const; + + Server &Get(const char *pattern, Handler handler); + Server &Post(const char *pattern, Handler handler); + Server &Post(const char *pattern, HandlerWithContentReader handler); + Server &Put(const char *pattern, Handler handler); + Server &Put(const char *pattern, HandlerWithContentReader handler); + Server &Patch(const char *pattern, Handler handler); + Server &Patch(const char *pattern, HandlerWithContentReader handler); + Server &Delete(const char *pattern, Handler handler); + Server &Delete(const char *pattern, HandlerWithContentReader handler); + Server &Options(const char *pattern, Handler handler); + + CPPHTTPLIB_DEPRECATED bool set_base_dir(const char *dir, + const char *mount_point = nullptr); + bool set_mount_point(const char *mount_point, const char *dir); + bool remove_mount_point(const char *mount_point); + void set_file_extension_and_mimetype_mapping(const char *ext, + const char *mime); + void set_file_request_handler(Handler handler); + + void set_error_handler(Handler handler); + void set_expect_100_continue_handler(Expect100ContinueHandler handler); + void set_logger(Logger logger); + + void set_socket_options(SocketOptions socket_options); + + void set_keep_alive_max_count(size_t count); + void set_read_timeout(time_t sec, time_t usec = 0); + void set_write_timeout(time_t sec, time_t usec = 0); + void set_idle_interval(time_t sec, time_t usec = 0); + + void set_payload_max_length(size_t length); + + bool bind_to_port(const char *host, int port, int socket_flags = 0); + int bind_to_any_port(const char *host, int socket_flags = 0); + bool listen_after_bind(); + + bool listen(const char *host, int port, int socket_flags = 0); + + bool is_running() const; + void stop(); + + std::function new_task_queue; + +protected: + bool process_request(Stream &strm, bool close_connection, + bool &connection_closed, + const std::function &setup_request); + + std::atomic svr_sock_; + size_t keep_alive_max_count_ = CPPHTTPLIB_KEEPALIVE_MAX_COUNT; + time_t read_timeout_sec_ = CPPHTTPLIB_READ_TIMEOUT_SECOND; + time_t read_timeout_usec_ = CPPHTTPLIB_READ_TIMEOUT_USECOND; + time_t write_timeout_sec_ = CPPHTTPLIB_WRITE_TIMEOUT_SECOND; + time_t write_timeout_usec_ = CPPHTTPLIB_WRITE_TIMEOUT_USECOND; + time_t idle_interval_sec_ = CPPHTTPLIB_IDLE_INTERVAL_SECOND; + time_t idle_interval_usec_ = CPPHTTPLIB_IDLE_INTERVAL_USECOND; + size_t payload_max_length_ = CPPHTTPLIB_PAYLOAD_MAX_LENGTH; + +private: + using Handlers = std::vector>; + using HandlersForContentReader = + std::vector>; + + socket_t create_server_socket(const char *host, int port, int socket_flags, + SocketOptions socket_options) const; + int bind_internal(const char *host, int port, int socket_flags); + bool listen_internal(); + + bool routing(Request &req, Response &res, Stream &strm); + bool handle_file_request(Request &req, Response &res, bool head = false); + bool dispatch_request(Request &req, Response &res, Handlers &handlers); + bool dispatch_request_for_content_reader(Request &req, Response &res, + ContentReader content_reader, + HandlersForContentReader &handlers); + + bool parse_request_line(const char *s, Request &req); + bool write_response(Stream &strm, bool close_connection, const Request &req, + Response &res); + bool write_content_with_provider(Stream &strm, const Request &req, + Response &res, const std::string &boundary, + const std::string &content_type); + bool read_content(Stream &strm, Request &req, Response &res); + bool + read_content_with_content_receiver(Stream &strm, Request &req, Response &res, + ContentReceiver receiver, + MultipartContentHeader multipart_header, + ContentReceiver multipart_receiver); + bool read_content_core(Stream &strm, Request &req, Response &res, + ContentReceiver receiver, + MultipartContentHeader mulitpart_header, + ContentReceiver multipart_receiver); + + virtual bool process_and_close_socket(socket_t sock); + + std::atomic is_running_; + std::vector> base_dirs_; + std::map file_extension_and_mimetype_map_; + Handler file_request_handler_; + Handlers get_handlers_; + Handlers post_handlers_; + HandlersForContentReader post_handlers_for_content_reader_; + Handlers put_handlers_; + HandlersForContentReader put_handlers_for_content_reader_; + Handlers patch_handlers_; + HandlersForContentReader patch_handlers_for_content_reader_; + Handlers delete_handlers_; + HandlersForContentReader delete_handlers_for_content_reader_; + Handlers options_handlers_; + Handler error_handler_; + Logger logger_; + Expect100ContinueHandler expect_100_continue_handler_; + SocketOptions socket_options_ = default_socket_options; +}; + +class Client { +public: + explicit Client(const std::string &host); + + explicit Client(const std::string &host, int port); + + explicit Client(const std::string &host, int port, + const std::string &client_cert_path, + const std::string &client_key_path); + + virtual ~Client(); + + virtual bool is_valid() const; + + std::shared_ptr Get(const char *path); + + std::shared_ptr Get(const char *path, const Headers &headers); + + std::shared_ptr Get(const char *path, Progress progress); + + std::shared_ptr Get(const char *path, const Headers &headers, + Progress progress); + + std::shared_ptr Get(const char *path, std::map params, const Headers &headers); + + std::shared_ptr Get(const char *path, + ContentReceiver content_receiver); + + std::shared_ptr Get(const char *path, const Headers &headers, + ContentReceiver content_receiver); + + std::shared_ptr + Get(const char *path, ContentReceiver content_receiver, Progress progress); + + std::shared_ptr Get(const char *path, const Headers &headers, + ContentReceiver content_receiver, + Progress progress); + + std::shared_ptr Get(const char *path, const Headers &headers, + ResponseHandler response_handler, + ContentReceiver content_receiver); + + std::shared_ptr Get(const char *path, const Headers &headers, + ResponseHandler response_handler, + ContentReceiver content_receiver, + Progress progress); + + std::shared_ptr Head(const char *path); + + std::shared_ptr Head(const char *path, const Headers &headers); + + std::shared_ptr Post(const char *path); + + std::shared_ptr Post(const char *path, const std::string &body, + const char *content_type); + + std::shared_ptr Post(const char *path, const Headers &headers, + const std::string &body, + const char *content_type); + + std::shared_ptr Post(const char *path, size_t content_length, + ContentProvider content_provider, + const char *content_type); + + std::shared_ptr Post(const char *path, const Headers &headers, + size_t content_length, + ContentProvider content_provider, + const char *content_type); + + std::shared_ptr Post(const char *path, const Params ¶ms); + + std::shared_ptr Post(const char *path, const Headers &headers, + const Params ¶ms); + + std::shared_ptr Post(const char *path, + const MultipartFormDataItems &items); + + std::shared_ptr Post(const char *path, const Headers &headers, + const MultipartFormDataItems &items); + + std::shared_ptr Put(const char *path); + + std::shared_ptr Put(const char *path, const std::string &body, + const char *content_type); + + std::shared_ptr Put(const char *path, const Headers &headers, + const std::string &body, + const char *content_type); + + std::shared_ptr Put(const char *path, size_t content_length, + ContentProvider content_provider, + const char *content_type); + + std::shared_ptr Put(const char *path, const Headers &headers, + size_t content_length, + ContentProvider content_provider, + const char *content_type); + + std::shared_ptr Put(const char *path, const Params ¶ms); + + std::shared_ptr Put(const char *path, const Headers &headers, + const Params ¶ms); + + std::shared_ptr Patch(const char *path, const std::string &body, + const char *content_type); + + std::shared_ptr Patch(const char *path, const Headers &headers, + const std::string &body, + const char *content_type); + + std::shared_ptr Patch(const char *path, size_t content_length, + ContentProvider content_provider, + const char *content_type); + + std::shared_ptr Patch(const char *path, const Headers &headers, + size_t content_length, + ContentProvider content_provider, + const char *content_type); + + std::shared_ptr Delete(const char *path); + + std::shared_ptr Delete(const char *path, const std::string &body, + const char *content_type); + + std::shared_ptr Delete(const char *path, const Headers &headers); + + std::shared_ptr Delete(const char *path, const Headers &headers, + const std::string &body, + const char *content_type); + + std::shared_ptr Options(const char *path); + + std::shared_ptr Options(const char *path, const Headers &headers); + + bool send(const Request &req, Response &res); + + size_t is_socket_open() const; + + void stop(); + + CPPHTTPLIB_DEPRECATED void set_timeout_sec(time_t timeout_sec); + void set_connection_timeout(time_t sec, time_t usec = 0); + void set_read_timeout(time_t sec, time_t usec = 0); + void set_write_timeout(time_t sec, time_t usec = 0); + + void set_basic_auth(const char *username, const char *password); +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + void set_digest_auth(const char *username, const char *password); +#endif + + void set_keep_alive(bool on); + void set_follow_location(bool on); + + void set_compress(bool on); + + void set_decompress(bool on); + + void set_interface(const char *intf); + + void set_proxy(const char *host, int port); + void set_proxy_basic_auth(const char *username, const char *password); +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + void set_proxy_digest_auth(const char *username, const char *password); +#endif + + void set_logger(Logger logger); + +protected: + struct Socket { + socket_t sock = INVALID_SOCKET; +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + SSL *ssl = nullptr; +#endif + + bool is_open() const { return sock != INVALID_SOCKET; } + }; + + virtual bool create_and_connect_socket(Socket &socket); + virtual void close_socket(Socket &socket, bool process_socket_ret); + + bool process_request(Stream &strm, const Request &req, Response &res, + bool close_connection); + + // Socket endoint information + const std::string host_; + const int port_; + const std::string host_and_port_; + + // Current open socket + Socket socket_; + mutable std::mutex socket_mutex_; + std::recursive_mutex request_mutex_; + + // Settings + std::string client_cert_path_; + std::string client_key_path_; + + time_t connection_timeout_sec_ = CPPHTTPLIB_CONNECTION_TIMEOUT_SECOND; + time_t connection_timeout_usec_ = CPPHTTPLIB_CONNECTION_TIMEOUT_USECOND; + time_t read_timeout_sec_ = CPPHTTPLIB_READ_TIMEOUT_SECOND; + time_t read_timeout_usec_ = CPPHTTPLIB_READ_TIMEOUT_USECOND; + time_t write_timeout_sec_ = CPPHTTPLIB_WRITE_TIMEOUT_SECOND; + time_t write_timeout_usec_ = CPPHTTPLIB_WRITE_TIMEOUT_USECOND; + + std::string basic_auth_username_; + std::string basic_auth_password_; +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + std::string digest_auth_username_; + std::string digest_auth_password_; +#endif + + bool keep_alive_ = false; + bool follow_location_ = false; + + bool compress_ = false; + bool decompress_ = true; + + std::string interface_; + + std::string proxy_host_; + int proxy_port_; + + std::string proxy_basic_auth_username_; + std::string proxy_basic_auth_password_; +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + std::string proxy_digest_auth_username_; + std::string proxy_digest_auth_password_; +#endif + + Logger logger_; + + void copy_settings(const Client &rhs) { + client_cert_path_ = rhs.client_cert_path_; + client_key_path_ = rhs.client_key_path_; + connection_timeout_sec_ = rhs.connection_timeout_sec_; + read_timeout_sec_ = rhs.read_timeout_sec_; + read_timeout_usec_ = rhs.read_timeout_usec_; + write_timeout_sec_ = rhs.write_timeout_sec_; + write_timeout_usec_ = rhs.write_timeout_usec_; + basic_auth_username_ = rhs.basic_auth_username_; + basic_auth_password_ = rhs.basic_auth_password_; +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + digest_auth_username_ = rhs.digest_auth_username_; + digest_auth_password_ = rhs.digest_auth_password_; +#endif + keep_alive_ = rhs.keep_alive_; + follow_location_ = rhs.follow_location_; + compress_ = rhs.compress_; + decompress_ = rhs.decompress_; + interface_ = rhs.interface_; + proxy_host_ = rhs.proxy_host_; + proxy_port_ = rhs.proxy_port_; + proxy_basic_auth_username_ = rhs.proxy_basic_auth_username_; + proxy_basic_auth_password_ = rhs.proxy_basic_auth_password_; +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + proxy_digest_auth_username_ = rhs.proxy_digest_auth_username_; + proxy_digest_auth_password_ = rhs.proxy_digest_auth_password_; +#endif + logger_ = rhs.logger_; + } + +private: + socket_t create_client_socket() const; + bool read_response_line(Stream &strm, Response &res); + bool write_request(Stream &strm, const Request &req, bool close_connection); + bool redirect(const Request &req, Response &res); + bool handle_request(Stream &strm, const Request &req, Response &res, + bool close_connection); + + std::shared_ptr send_with_content_provider( + const char *method, const char *path, const Headers &headers, + const std::string &body, size_t content_length, + ContentProvider content_provider, const char *content_type); + + virtual bool process_socket(Socket &socket, + std::function callback); + virtual bool is_ssl() const; +}; + +inline void Get(std::vector &requests, const char *path, + const Headers &headers) { + Request req; + req.method = "GET"; + req.path = path; + req.headers = headers; + requests.emplace_back(std::move(req)); +} + +inline void Get(std::vector &requests, const char *path) { + Get(requests, path, Headers()); +} + +inline void Post(std::vector &requests, const char *path, + const Headers &headers, const std::string &body, + const char *content_type) { + Request req; + req.method = "POST"; + req.path = path; + req.headers = headers; + if (content_type) { req.headers.emplace("Content-Type", content_type); } + req.body = body; + requests.emplace_back(std::move(req)); +} + +inline void Post(std::vector &requests, const char *path, + const std::string &body, const char *content_type) { + Post(requests, path, Headers(), body, content_type); +} + +inline void Post(std::vector &requests, const char *path, + size_t content_length, ContentProvider content_provider, + const char *content_type) { + Request req; + req.method = "POST"; + req.headers = Headers(); + req.path = path; + req.content_length = content_length; + req.content_provider = content_provider; + + if (content_type) { req.headers.emplace("Content-Type", content_type); } + + requests.emplace_back(std::move(req)); +} + +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT +class SSLServer : public Server { +public: + SSLServer(const char *cert_path, const char *private_key_path, + const char *client_ca_cert_file_path = nullptr, + const char *client_ca_cert_dir_path = nullptr); + + SSLServer(X509 *cert, EVP_PKEY *private_key, + X509_STORE *client_ca_cert_store = nullptr); + + ~SSLServer() override; + + bool is_valid() const override; + +private: + bool process_and_close_socket(socket_t sock) override; + + SSL_CTX *ctx_; + std::mutex ctx_mutex_; +}; + +class SSLClient : public Client { +public: + explicit SSLClient(const std::string &host); + + explicit SSLClient(const std::string &host, int port); + + explicit SSLClient(const std::string &host, int port, + const std::string &client_cert_path, + const std::string &client_key_path); + + explicit SSLClient(const std::string &host, int port, X509 *client_cert, + EVP_PKEY *client_key); + + ~SSLClient() override; + + bool is_valid() const override; + + void set_ca_cert_path(const char *ca_cert_file_path, + const char *ca_cert_dir_path = nullptr); + + void set_ca_cert_store(X509_STORE *ca_cert_store); + + void enable_server_certificate_verification(bool enabled); + + long get_openssl_verify_result() const; + + SSL_CTX *ssl_context() const; + +private: + bool create_and_connect_socket(Socket &socket) override; + void close_socket(Socket &socket, bool process_socket_ret) override; + + bool process_socket(Socket &socket, + std::function callback) override; + bool is_ssl() const override; + + bool connect_with_proxy(Socket &sock, Response &res, bool &success); + bool initialize_ssl(Socket &socket); + + bool verify_host(X509 *server_cert) const; + bool verify_host_with_subject_alt_name(X509 *server_cert) const; + bool verify_host_with_common_name(X509 *server_cert) const; + bool check_host_name(const char *pattern, size_t pattern_len) const; + + SSL_CTX *ctx_; + std::mutex ctx_mutex_; + std::vector host_components_; + + std::string ca_cert_file_path_; + std::string ca_cert_dir_path_; + X509_STORE *ca_cert_store_ = nullptr; + bool server_certificate_verification_ = false; + long verify_result_ = 0; + + friend class Client; +}; +#endif + +class Client2 { +public: + explicit Client2(const char *scheme_host_port) + : Client2(scheme_host_port, std::string(), std::string()) {} + + explicit Client2(const char *scheme_host_port, + const std::string &client_cert_path, + const std::string &client_key_path) { + const static std::regex re(R"(^(https?)://([^:/?#]+)(?::(\d+))?)"); + + std::cmatch m; + if (std::regex_match(scheme_host_port, m, re)) { + auto scheme = m[1].str(); + auto host = m[2].str(); + auto port_str = m[3].str(); + + auto port = !port_str.empty() ? std::stoi(port_str) + : (scheme == "https" ? 443 : 80); + + if (scheme == "https") { +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + is_ssl_ = true; + cli_ = std::make_shared(host.c_str(), port, client_cert_path, + client_key_path); +#endif + } else { + cli_ = std::make_shared(host.c_str(), port, client_cert_path, + client_key_path); + } + } + } + + ~Client2() {} + + bool is_valid() const { return cli_ != nullptr; } + + std::shared_ptr Get(const char *path) { return cli_->Get(path); } + + std::shared_ptr Get(const char *path, const Headers &headers) { + return cli_->Get(path, headers); + } + + std::shared_ptr Get(const char *path, Progress progress) { + return cli_->Get(path, progress); + } + + std::shared_ptr Get(const char *path, const Headers &headers, + Progress progress) { + return cli_->Get(path, headers, progress); + } + + std::shared_ptr Get(const char *path, + ContentReceiver content_receiver) { + return cli_->Get(path, content_receiver); + } + + std::shared_ptr Get(const char *path, const Headers &headers, + ContentReceiver content_receiver) { + return cli_->Get(path, headers, content_receiver); + } + + std::shared_ptr + Get(const char *path, ContentReceiver content_receiver, Progress progress) { + return cli_->Get(path, content_receiver, progress); + } + + std::shared_ptr Get(const char *path, const Headers &headers, + ContentReceiver content_receiver, + Progress progress) { + return cli_->Get(path, headers, content_receiver, progress); + } + + std::shared_ptr Get(const char *path, const Headers &headers, + ResponseHandler response_handler, + ContentReceiver content_receiver) { + return cli_->Get(path, headers, response_handler, content_receiver); + } + + std::shared_ptr Get(const char *path, const Headers &headers, + ResponseHandler response_handler, + ContentReceiver content_receiver, + Progress progress) { + return cli_->Get(path, headers, response_handler, content_receiver, + progress); + } + + std::shared_ptr Head(const char *path) { return cli_->Head(path); } + + std::shared_ptr Head(const char *path, const Headers &headers) { + return cli_->Head(path, headers); + } + + std::shared_ptr Post(const char *path) { return cli_->Post(path); } + + std::shared_ptr Post(const char *path, const std::string &body, + const char *content_type) { + return cli_->Post(path, body, content_type); + } + + std::shared_ptr Post(const char *path, const Headers &headers, + const std::string &body, + const char *content_type) { + return cli_->Post(path, headers, body, content_type); + } + + std::shared_ptr Post(const char *path, size_t content_length, + ContentProvider content_provider, + const char *content_type) { + return cli_->Post(path, content_length, content_provider, content_type); + } + + std::shared_ptr Post(const char *path, const Headers &headers, + size_t content_length, + ContentProvider content_provider, + const char *content_type) { + return cli_->Post(path, headers, content_length, content_provider, + content_type); + } + + std::shared_ptr Post(const char *path, const Params ¶ms) { + return cli_->Post(path, params); + } + + std::shared_ptr Post(const char *path, const Headers &headers, + const Params ¶ms) { + return cli_->Post(path, headers, params); + } + + std::shared_ptr Post(const char *path, + const MultipartFormDataItems &items) { + return cli_->Post(path, items); + } + + std::shared_ptr Post(const char *path, const Headers &headers, + const MultipartFormDataItems &items) { + return cli_->Post(path, headers, items); + } + + std::shared_ptr Put(const char *path) { return cli_->Put(path); } + + std::shared_ptr Put(const char *path, const std::string &body, + const char *content_type) { + return cli_->Put(path, body, content_type); + } + + std::shared_ptr Put(const char *path, const Headers &headers, + const std::string &body, + const char *content_type) { + return cli_->Put(path, headers, body, content_type); + } + + std::shared_ptr Put(const char *path, size_t content_length, + ContentProvider content_provider, + const char *content_type) { + return cli_->Put(path, content_length, content_provider, content_type); + } + + std::shared_ptr Put(const char *path, const Headers &headers, + size_t content_length, + ContentProvider content_provider, + const char *content_type) { + return cli_->Put(path, headers, content_length, content_provider, + content_type); + } + + std::shared_ptr Put(const char *path, const Params ¶ms) { + return cli_->Put(path, params); + } + + std::shared_ptr Put(const char *path, const Headers &headers, + const Params ¶ms) { + return cli_->Put(path, headers, params); + } + + std::shared_ptr Patch(const char *path, const std::string &body, + const char *content_type) { + return cli_->Patch(path, body, content_type); + } + + std::shared_ptr Patch(const char *path, const Headers &headers, + const std::string &body, + const char *content_type) { + return cli_->Patch(path, headers, body, content_type); + } + + std::shared_ptr Patch(const char *path, size_t content_length, + ContentProvider content_provider, + const char *content_type) { + return cli_->Patch(path, content_length, content_provider, content_type); + } + + std::shared_ptr Patch(const char *path, const Headers &headers, + size_t content_length, + ContentProvider content_provider, + const char *content_type) { + return cli_->Patch(path, headers, content_length, content_provider, + content_type); + } + + std::shared_ptr Delete(const char *path) { + return cli_->Delete(path); + } + + std::shared_ptr Delete(const char *path, const std::string &body, + const char *content_type) { + return cli_->Delete(path, body, content_type); + } + + std::shared_ptr Delete(const char *path, const Headers &headers) { + return cli_->Delete(path, headers); + } + + std::shared_ptr Delete(const char *path, const Headers &headers, + const std::string &body, + const char *content_type) { + return cli_->Delete(path, headers, body, content_type); + } + + std::shared_ptr Options(const char *path) { + return cli_->Options(path); + } + + std::shared_ptr Options(const char *path, const Headers &headers) { + return cli_->Options(path, headers); + } + + bool send(const Request &req, Response &res) { return cli_->send(req, res); } + + bool is_socket_open() { return cli_->is_socket_open(); } + + void stop() { cli_->stop(); } + + Client2 &set_connection_timeout(time_t sec, time_t usec) { + cli_->set_connection_timeout(sec, usec); + return *this; + } + + Client2 &set_read_timeout(time_t sec, time_t usec) { + cli_->set_read_timeout(sec, usec); + return *this; + } + + Client2 &set_basic_auth(const char *username, const char *password) { + cli_->set_basic_auth(username, password); + return *this; + } + +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + Client2 &set_digest_auth(const char *username, const char *password) { + cli_->set_digest_auth(username, password); + return *this; + } +#endif + + Client2 &set_keep_alive(bool on) { + cli_->set_keep_alive(on); + return *this; + } + + Client2 &set_follow_location(bool on) { + cli_->set_follow_location(on); + return *this; + } + + Client2 &set_compress(bool on) { + cli_->set_compress(on); + return *this; + } + + Client2 &set_decompress(bool on) { + cli_->set_decompress(on); + return *this; + } + + Client2 &set_interface(const char *intf) { + cli_->set_interface(intf); + return *this; + } + + Client2 &set_proxy(const char *host, int port) { + cli_->set_proxy(host, port); + return *this; + } + + Client2 &set_proxy_basic_auth(const char *username, const char *password) { + cli_->set_proxy_basic_auth(username, password); + return *this; + } + +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + Client2 &set_proxy_digest_auth(const char *username, const char *password) { + cli_->set_proxy_digest_auth(username, password); + return *this; + } +#endif + + Client2 &set_logger(Logger logger) { + cli_->set_logger(logger); + return *this; + } + + // SSL +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + Client2 &set_ca_cert_path(const char *ca_cert_file_path, + const char *ca_cert_dir_path = nullptr) { + if (is_ssl_) { + static_cast(*cli_).set_ca_cert_path(ca_cert_file_path, + ca_cert_dir_path); + } + return *this; + } + + Client2 &set_ca_cert_store(X509_STORE *ca_cert_store) { + if (is_ssl_) { + static_cast(*cli_).set_ca_cert_store(ca_cert_store); + } + return *this; + } + + Client2 &enable_server_certificate_verification(bool enabled) { + if (is_ssl_) { + static_cast(*cli_).enable_server_certificate_verification( + enabled); + } + return *this; + } + + long get_openssl_verify_result() const { + if (is_ssl_) { + return static_cast(*cli_).get_openssl_verify_result(); + } + return -1; // NOTE: -1 doesn't match any of X509_V_ERR_??? + } + + SSL_CTX *ssl_context() const { + if (is_ssl_) { return static_cast(*cli_).ssl_context(); } + return nullptr; + } +#endif + +private: +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + bool is_ssl_ = false; +#endif + std::shared_ptr cli_; +}; + +// ---------------------------------------------------------------------------- + +/* + * Implementation + */ + +namespace detail { + +inline bool is_hex(char c, int &v) { + if (0x20 <= c && isdigit(c)) { + v = c - '0'; + return true; + } else if ('A' <= c && c <= 'F') { + v = c - 'A' + 10; + return true; + } else if ('a' <= c && c <= 'f') { + v = c - 'a' + 10; + return true; + } + return false; +} + +inline bool from_hex_to_i(const std::string &s, size_t i, size_t cnt, + int &val) { + if (i >= s.size()) { return false; } + + val = 0; + for (; cnt; i++, cnt--) { + if (!s[i]) { return false; } + int v = 0; + if (is_hex(s[i], v)) { + val = val * 16 + v; + } else { + return false; + } + } + return true; +} + +inline std::string from_i_to_hex(size_t n) { + const char *charset = "0123456789abcdef"; + std::string ret; + do { + ret = charset[n & 15] + ret; + n >>= 4; + } while (n > 0); + return ret; +} + +inline size_t to_utf8(int code, char *buff) { + if (code < 0x0080) { + buff[0] = (code & 0x7F); + return 1; + } else if (code < 0x0800) { + buff[0] = static_cast(0xC0 | ((code >> 6) & 0x1F)); + buff[1] = static_cast(0x80 | (code & 0x3F)); + return 2; + } else if (code < 0xD800) { + buff[0] = static_cast(0xE0 | ((code >> 12) & 0xF)); + buff[1] = static_cast(0x80 | ((code >> 6) & 0x3F)); + buff[2] = static_cast(0x80 | (code & 0x3F)); + return 3; + } else if (code < 0xE000) { // D800 - DFFF is invalid... + return 0; + } else if (code < 0x10000) { + buff[0] = static_cast(0xE0 | ((code >> 12) & 0xF)); + buff[1] = static_cast(0x80 | ((code >> 6) & 0x3F)); + buff[2] = static_cast(0x80 | (code & 0x3F)); + return 3; + } else if (code < 0x110000) { + buff[0] = static_cast(0xF0 | ((code >> 18) & 0x7)); + buff[1] = static_cast(0x80 | ((code >> 12) & 0x3F)); + buff[2] = static_cast(0x80 | ((code >> 6) & 0x3F)); + buff[3] = static_cast(0x80 | (code & 0x3F)); + return 4; + } + + // NOTREACHED + return 0; +} + +// NOTE: This code came up with the following stackoverflow post: +// https://stackoverflow.com/questions/180947/base64-decode-snippet-in-c +inline std::string base64_encode(const std::string &in) { + static const auto lookup = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + + std::string out; + out.reserve(in.size()); + + int val = 0; + int valb = -6; + + for (auto c : in) { + val = (val << 8) + static_cast(c); + valb += 8; + while (valb >= 0) { + out.push_back(lookup[(val >> valb) & 0x3F]); + valb -= 6; + } + } + + if (valb > -6) { out.push_back(lookup[((val << 8) >> (valb + 8)) & 0x3F]); } + + while (out.size() % 4) { + out.push_back('='); + } + + return out; +} + +inline bool is_file(const std::string &path) { + struct stat st; + return stat(path.c_str(), &st) >= 0 && S_ISREG(st.st_mode); +} + +inline bool is_dir(const std::string &path) { + struct stat st; + return stat(path.c_str(), &st) >= 0 && S_ISDIR(st.st_mode); +} + +inline bool is_valid_path(const std::string &path) { + size_t level = 0; + size_t i = 0; + + // Skip slash + while (i < path.size() && path[i] == '/') { + i++; + } + + while (i < path.size()) { + // Read component + auto beg = i; + while (i < path.size() && path[i] != '/') { + i++; + } + + auto len = i - beg; + assert(len > 0); + + if (!path.compare(beg, len, ".")) { + ; + } else if (!path.compare(beg, len, "..")) { + if (level == 0) { return false; } + level--; + } else { + level++; + } + + // Skip slash + while (i < path.size() && path[i] == '/') { + i++; + } + } + + return true; +} + +inline void read_file(const std::string &path, std::string &out) { + std::ifstream fs(path, std::ios_base::binary); + fs.seekg(0, std::ios_base::end); + auto size = fs.tellg(); + fs.seekg(0); + out.resize(static_cast(size)); + fs.read(&out[0], static_cast(size)); +} + +inline std::string file_extension(const std::string &path) { + std::smatch m; + static auto re = std::regex("\\.([a-zA-Z0-9]+)$"); + if (std::regex_search(path, m, re)) { return m[1].str(); } + return std::string(); +} + +template void split(const char *b, const char *e, char d, Fn fn) { + int i = 0; + int beg = 0; + + while (e ? (b + i != e) : (b[i] != '\0')) { + if (b[i] == d) { + fn(&b[beg], &b[i]); + beg = i + 1; + } + i++; + } + + if (i) { fn(&b[beg], &b[i]); } +} + +// NOTE: until the read size reaches `fixed_buffer_size`, use `fixed_buffer` +// to store data. The call can set memory on stack for performance. +class stream_line_reader { +public: + stream_line_reader(Stream &strm, char *fixed_buffer, size_t fixed_buffer_size) + : strm_(strm), fixed_buffer_(fixed_buffer), + fixed_buffer_size_(fixed_buffer_size) {} + + const char *ptr() const { + if (glowable_buffer_.empty()) { + return fixed_buffer_; + } else { + return glowable_buffer_.data(); + } + } + + size_t size() const { + if (glowable_buffer_.empty()) { + return fixed_buffer_used_size_; + } else { + return glowable_buffer_.size(); + } + } + + bool end_with_crlf() const { + auto end = ptr() + size(); + return size() >= 2 && end[-2] == '\r' && end[-1] == '\n'; + } + + bool getline() { + fixed_buffer_used_size_ = 0; + glowable_buffer_.clear(); + + for (size_t i = 0;; i++) { + char byte; + auto n = strm_.read(&byte, 1); + + if (n < 0) { + return false; + } else if (n == 0) { + if (i == 0) { + return false; + } else { + break; + } + } + + append(byte); + + if (byte == '\n') { break; } + } + + return true; + } + +private: + void append(char c) { + if (fixed_buffer_used_size_ < fixed_buffer_size_ - 1) { + fixed_buffer_[fixed_buffer_used_size_++] = c; + fixed_buffer_[fixed_buffer_used_size_] = '\0'; + } else { + if (glowable_buffer_.empty()) { + assert(fixed_buffer_[fixed_buffer_used_size_] == '\0'); + glowable_buffer_.assign(fixed_buffer_, fixed_buffer_used_size_); + } + glowable_buffer_ += c; + } + } + + Stream &strm_; + char *fixed_buffer_; + const size_t fixed_buffer_size_; + size_t fixed_buffer_used_size_ = 0; + std::string glowable_buffer_; +}; + +inline int close_socket(socket_t sock) { +#ifdef _WIN32 + return closesocket(sock); +#else + return close(sock); +#endif +} + +template inline ssize_t handle_EINTR(T fn) { + ssize_t res = false; + while (true) { + res = fn(); + if (res < 0 && errno == EINTR) { continue; } + break; + } + return res; +} + +inline ssize_t select_read(socket_t sock, time_t sec, time_t usec) { +#ifdef CPPHTTPLIB_USE_POLL + struct pollfd pfd_read; + pfd_read.fd = sock; + pfd_read.events = POLLIN; + + auto timeout = static_cast(sec * 1000 + usec); + + return handle_EINTR([&]() { return poll(&pfd_read, 1, timeout); }); +#else + fd_set fds; + FD_ZERO(&fds); + FD_SET(sock, &fds); + + timeval tv; + tv.tv_sec = static_cast(sec); + tv.tv_usec = static_cast(usec); + + return handle_EINTR([&]() { + return select(static_cast(sock + 1), &fds, nullptr, nullptr, &tv); + }); +#endif +} + +inline ssize_t select_write(socket_t sock, time_t sec, time_t usec) { +#ifdef CPPHTTPLIB_USE_POLL + struct pollfd pfd_read; + pfd_read.fd = sock; + pfd_read.events = POLLOUT; + + auto timeout = static_cast(sec * 1000 + usec / 1000); + + return handle_EINTR([&]() { return poll(&pfd_read, 1, timeout); }); +#else + fd_set fds; + FD_ZERO(&fds); + FD_SET(sock, &fds); + + timeval tv; + tv.tv_sec = static_cast(sec); + tv.tv_usec = static_cast(usec); + + return handle_EINTR([&]() { + return select(static_cast(sock + 1), nullptr, &fds, nullptr, &tv); + }); +#endif +} + +inline bool wait_until_socket_is_ready(socket_t sock, time_t sec, time_t usec) { +#ifdef CPPHTTPLIB_USE_POLL + struct pollfd pfd_read; + pfd_read.fd = sock; + pfd_read.events = POLLIN | POLLOUT; + + auto timeout = static_cast(sec * 1000 + usec); + + auto poll_res = handle_EINTR([&]() { return poll(&pfd_read, 1, timeout); }); + + if (poll_res > 0 && pfd_read.revents & (POLLIN | POLLOUT)) { + int error = 0; + socklen_t len = sizeof(error); + auto res = getsockopt(sock, SOL_SOCKET, SO_ERROR, + reinterpret_cast(&error), &len); + return res >= 0 && !error; + } + return false; +#else + fd_set fdsr; + FD_ZERO(&fdsr); + FD_SET(sock, &fdsr); + + auto fdsw = fdsr; + auto fdse = fdsr; + + timeval tv; + tv.tv_sec = static_cast(sec); + tv.tv_usec = static_cast(usec); + + auto ret = handle_EINTR([&]() { + return select(static_cast(sock + 1), &fdsr, &fdsw, &fdse, &tv); + }); + + if (ret > 0 && (FD_ISSET(sock, &fdsr) || FD_ISSET(sock, &fdsw))) { + int error = 0; + socklen_t len = sizeof(error); + return getsockopt(sock, SOL_SOCKET, SO_ERROR, + reinterpret_cast(&error), &len) >= 0 && + !error; + } + return false; +#endif +} + +class SocketStream : public Stream { +public: + SocketStream(socket_t sock, time_t read_timeout_sec, time_t read_timeout_usec, + time_t write_timeout_sec, time_t write_timeout_usec); + ~SocketStream() override; + + bool is_readable() const override; + bool is_writable() const override; + ssize_t read(char *ptr, size_t size) override; + ssize_t write(const char *ptr, size_t size) override; + void get_remote_ip_and_port(std::string &ip, int &port) const override; + +private: + socket_t sock_; + time_t read_timeout_sec_; + time_t read_timeout_usec_; + time_t write_timeout_sec_; + time_t write_timeout_usec_; +}; + +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT +class SSLSocketStream : public Stream { +public: + SSLSocketStream(socket_t sock, SSL *ssl, time_t read_timeout_sec, + time_t read_timeout_usec, time_t write_timeout_sec, + time_t write_timeout_usec); + ~SSLSocketStream() override; + + bool is_readable() const override; + bool is_writable() const override; + ssize_t read(char *ptr, size_t size) override; + ssize_t write(const char *ptr, size_t size) override; + void get_remote_ip_and_port(std::string &ip, int &port) const override; + +private: + socket_t sock_; + SSL *ssl_; + time_t read_timeout_sec_; + time_t read_timeout_usec_; + time_t write_timeout_sec_; + time_t write_timeout_usec_; +}; +#endif + +class BufferStream : public Stream { +public: + BufferStream() = default; + ~BufferStream() override = default; + + bool is_readable() const override; + bool is_writable() const override; + ssize_t read(char *ptr, size_t size) override; + ssize_t write(const char *ptr, size_t size) override; + void get_remote_ip_and_port(std::string &ip, int &port) const override; + + const std::string &get_buffer() const; + +private: + std::string buffer; + size_t position = 0; +}; + +inline bool keep_alive(socket_t sock) { + using namespace std::chrono; + auto start = steady_clock::now(); + while (true) { + auto val = select_read(sock, 0, 10000); + if (val < 0) { + return false; + } else if (val == 0) { + auto current = steady_clock::now(); + auto duration = duration_cast(current - start); + auto timeout = CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND * 100 + + CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND; + if (duration.count() > timeout) { return false; } + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + } else { + return true; + } + } +} + +template +inline bool process_server_socket_core(socket_t sock, + size_t keep_alive_max_count, + T callback) { + assert(keep_alive_max_count > 0); + auto ret = false; + auto count = keep_alive_max_count; + while (count > 0 && keep_alive(sock)) { + auto close_connection = count == 1; + auto connection_closed = false; + ret = callback(close_connection, connection_closed); + if (!ret || connection_closed) { break; } + count--; + } + return ret; +} + +template +inline bool process_server_socket(socket_t sock, size_t keep_alive_max_count, + time_t read_timeout_sec, + time_t read_timeout_usec, + time_t write_timeout_sec, + time_t write_timeout_usec, T callback) { + return process_server_socket_core( + sock, keep_alive_max_count, + [&](bool close_connection, bool connection_closed) { + SocketStream strm(sock, read_timeout_sec, read_timeout_usec, + write_timeout_sec, write_timeout_usec); + return callback(strm, close_connection, connection_closed); + }); +} + +template +inline bool process_client_socket(socket_t sock, time_t read_timeout_sec, + time_t read_timeout_usec, + time_t write_timeout_sec, + time_t write_timeout_usec, T callback) { + SocketStream strm(sock, read_timeout_sec, read_timeout_usec, + write_timeout_sec, write_timeout_usec); + return callback(strm); +} + +inline int shutdown_socket(socket_t sock) { +#ifdef _WIN32 + return shutdown(sock, SD_BOTH); +#else + return shutdown(sock, SHUT_RDWR); +#endif +} + +template +socket_t create_socket(const char *host, int port, int socket_flags, + SocketOptions socket_options, + BindOrConnect bind_or_connect) { + // Get address info + struct addrinfo hints; + struct addrinfo *result; + + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = socket_flags; + hints.ai_protocol = 0; + + auto service = std::to_string(port); + + if (getaddrinfo(host, service.c_str(), &hints, &result)) { + return INVALID_SOCKET; + } + + for (auto rp = result; rp; rp = rp->ai_next) { + // Create a socket +#ifdef _WIN32 + auto sock = WSASocketW(rp->ai_family, rp->ai_socktype, rp->ai_protocol, + nullptr, 0, WSA_FLAG_NO_HANDLE_INHERIT); + /** + * Since the WSA_FLAG_NO_HANDLE_INHERIT is only supported on Windows 7 SP1 + * and above the socket creation fails on older Windows Systems. + * + * Let's try to create a socket the old way in this case. + * + * Reference: + * https://docs.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-wsasocketa + * + * WSA_FLAG_NO_HANDLE_INHERIT: + * This flag is supported on Windows 7 with SP1, Windows Server 2008 R2 with + * SP1, and later + * + */ + if (sock == INVALID_SOCKET) { + sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); + } +#else + auto sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); +#endif + if (sock == INVALID_SOCKET) { continue; } + +#ifndef _WIN32 + if (fcntl(sock, F_SETFD, FD_CLOEXEC) == -1) { continue; } +#endif + + if (socket_options) { socket_options(sock); } + + if (rp->ai_family == AF_INET6) { + int no = 0; + setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, reinterpret_cast(&no), + sizeof(no)); + } + + // bind or connect + if (bind_or_connect(sock, *rp)) { + freeaddrinfo(result); + return sock; + } + + close_socket(sock); + } + + freeaddrinfo(result); + return INVALID_SOCKET; +} + +inline void set_nonblocking(socket_t sock, bool nonblocking) { +#ifdef _WIN32 + auto flags = nonblocking ? 1UL : 0UL; + ioctlsocket(sock, FIONBIO, &flags); +#else + auto flags = fcntl(sock, F_GETFL, 0); + fcntl(sock, F_SETFL, + nonblocking ? (flags | O_NONBLOCK) : (flags & (~O_NONBLOCK))); +#endif +} + +inline bool is_connection_error() { +#ifdef _WIN32 + return WSAGetLastError() != WSAEWOULDBLOCK; +#else + return errno != EINPROGRESS; +#endif +} + +inline bool bind_ip_address(socket_t sock, const char *host) { + struct addrinfo hints; + struct addrinfo *result; + + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = 0; + + if (getaddrinfo(host, "0", &hints, &result)) { return false; } + + auto ret = false; + for (auto rp = result; rp; rp = rp->ai_next) { + const auto &ai = *rp; + if (!::bind(sock, ai.ai_addr, static_cast(ai.ai_addrlen))) { + ret = true; + break; + } + } + + freeaddrinfo(result); + return ret; +} + +#ifndef _WIN32 +inline std::string if2ip(const std::string &ifn) { + struct ifaddrs *ifap; + getifaddrs(&ifap); + for (auto ifa = ifap; ifa; ifa = ifa->ifa_next) { + if (ifa->ifa_addr && ifn == ifa->ifa_name) { + if (ifa->ifa_addr->sa_family == AF_INET) { + auto sa = reinterpret_cast(ifa->ifa_addr); + char buf[INET_ADDRSTRLEN]; + if (inet_ntop(AF_INET, &sa->sin_addr, buf, INET_ADDRSTRLEN)) { + freeifaddrs(ifap); + return std::string(buf, INET_ADDRSTRLEN); + } + } + } + } + freeifaddrs(ifap); + return std::string(); +} +#endif + +inline socket_t create_client_socket(const char *host, int port, + SocketOptions socket_options, + time_t timeout_sec, time_t timeout_usec, + const std::string &intf) { + return create_socket( + host, port, 0, socket_options, + [&](socket_t sock, struct addrinfo &ai) -> bool { + if (!intf.empty()) { +#ifndef _WIN32 + auto ip = if2ip(intf); + if (ip.empty()) { ip = intf; } + if (!bind_ip_address(sock, ip.c_str())) { return false; } +#endif + } + + set_nonblocking(sock, true); + + auto ret = + ::connect(sock, ai.ai_addr, static_cast(ai.ai_addrlen)); + if (ret < 0) { + if (is_connection_error() || + !wait_until_socket_is_ready(sock, timeout_sec, timeout_usec)) { + close_socket(sock); + return false; + } + } + + set_nonblocking(sock, false); + return true; + }); +} + +inline void get_remote_ip_and_port(const struct sockaddr_storage &addr, + socklen_t addr_len, std::string &ip, + int &port) { + if (addr.ss_family == AF_INET) { + port = ntohs(reinterpret_cast(&addr)->sin_port); + } else if (addr.ss_family == AF_INET6) { + port = + ntohs(reinterpret_cast(&addr)->sin6_port); + } + + std::array ipstr{}; + if (!getnameinfo(reinterpret_cast(&addr), addr_len, + ipstr.data(), static_cast(ipstr.size()), nullptr, + 0, NI_NUMERICHOST)) { + ip = ipstr.data(); + } +} + +inline void get_remote_ip_and_port(socket_t sock, std::string &ip, int &port) { + struct sockaddr_storage addr; + socklen_t addr_len = sizeof(addr); + + if (!getpeername(sock, reinterpret_cast(&addr), + &addr_len)) { + get_remote_ip_and_port(addr, addr_len, ip, port); + } +} + +inline const char * +find_content_type(const std::string &path, + const std::map &user_data) { + auto ext = file_extension(path); + + auto it = user_data.find(ext); + if (it != user_data.end()) { return it->second.c_str(); } + + if (ext == "txt") { + return "text/plain"; + } else if (ext == "html" || ext == "htm") { + return "text/html"; + } else if (ext == "css") { + return "text/css"; + } else if (ext == "jpeg" || ext == "jpg") { + return "image/jpg"; + } else if (ext == "png") { + return "image/png"; + } else if (ext == "gif") { + return "image/gif"; + } else if (ext == "svg") { + return "image/svg+xml"; + } else if (ext == "ico") { + return "image/x-icon"; + } else if (ext == "json") { + return "application/json"; + } else if (ext == "pdf") { + return "application/pdf"; + } else if (ext == "js") { + return "application/javascript"; + } else if (ext == "wasm") { + return "application/wasm"; + } else if (ext == "xml") { + return "application/xml"; + } else if (ext == "xhtml") { + return "application/xhtml+xml"; + } + return nullptr; +} + +inline const char *status_message(int status) { + switch (status) { + case 100: return "Continue"; + case 101: return "Switching Protocol"; + case 102: return "Processing"; + case 103: return "Early Hints"; + case 200: return "OK"; + case 201: return "Created"; + case 202: return "Accepted"; + case 203: return "Non-Authoritative Information"; + case 204: return "No Content"; + case 205: return "Reset Content"; + case 206: return "Partial Content"; + case 207: return "Multi-Status"; + case 208: return "Already Reported"; + case 226: return "IM Used"; + case 300: return "Multiple Choice"; + case 301: return "Moved Permanently"; + case 302: return "Found"; + case 303: return "See Other"; + case 304: return "Not Modified"; + case 305: return "Use Proxy"; + case 306: return "unused"; + case 307: return "Temporary Redirect"; + case 308: return "Permanent Redirect"; + case 400: return "Bad Request"; + case 401: return "Unauthorized"; + case 402: return "Payment Required"; + case 403: return "Forbidden"; + case 404: return "Not Found"; + case 405: return "Method Not Allowed"; + case 406: return "Not Acceptable"; + case 407: return "Proxy Authentication Required"; + case 408: return "Request Timeout"; + case 409: return "Conflict"; + case 410: return "Gone"; + case 411: return "Length Required"; + case 412: return "Precondition Failed"; + case 413: return "Payload Too Large"; + case 414: return "URI Too Long"; + case 415: return "Unsupported Media Type"; + case 416: return "Range Not Satisfiable"; + case 417: return "Expectation Failed"; + case 418: return "I'm a teapot"; + case 421: return "Misdirected Request"; + case 422: return "Unprocessable Entity"; + case 423: return "Locked"; + case 424: return "Failed Dependency"; + case 425: return "Too Early"; + case 426: return "Upgrade Required"; + case 428: return "Precondition Required"; + case 429: return "Too Many Requests"; + case 431: return "Request Header Fields Too Large"; + case 451: return "Unavailable For Legal Reasons"; + case 501: return "Not Implemented"; + case 502: return "Bad Gateway"; + case 503: return "Service Unavailable"; + case 504: return "Gateway Timeout"; + case 505: return "HTTP Version Not Supported"; + case 506: return "Variant Also Negotiates"; + case 507: return "Insufficient Storage"; + case 508: return "Loop Detected"; + case 510: return "Not Extended"; + case 511: return "Network Authentication Required"; + + default: + case 500: return "Internal Server Error"; + } +} + +#ifdef CPPHTTPLIB_ZLIB_SUPPORT +inline bool can_compress(const std::string &content_type) { + return !content_type.find("text/") || content_type == "image/svg+xml" || + content_type == "application/javascript" || + content_type == "application/json" || + content_type == "application/xml" || + content_type == "application/xhtml+xml"; +} + +inline bool compress(std::string &content) { + z_stream strm; + strm.zalloc = Z_NULL; + strm.zfree = Z_NULL; + strm.opaque = Z_NULL; + + auto ret = deflateInit2(&strm, Z_DEFAULT_COMPRESSION, Z_DEFLATED, 31, 8, + Z_DEFAULT_STRATEGY); + if (ret != Z_OK) { return false; } + + strm.avail_in = static_cast(content.size()); + strm.next_in = + const_cast(reinterpret_cast(content.data())); + + std::string compressed; + + std::array buff{}; + do { + strm.avail_out = buff.size(); + strm.next_out = reinterpret_cast(buff.data()); + ret = deflate(&strm, Z_FINISH); + assert(ret != Z_STREAM_ERROR); + compressed.append(buff.data(), buff.size() - strm.avail_out); + } while (strm.avail_out == 0); + + assert(ret == Z_STREAM_END); + assert(strm.avail_in == 0); + + content.swap(compressed); + + deflateEnd(&strm); + return true; +} + +class decompressor { +public: + decompressor() { + std::memset(&strm, 0, sizeof(strm)); + strm.zalloc = Z_NULL; + strm.zfree = Z_NULL; + strm.opaque = Z_NULL; + + // 15 is the value of wbits, which should be at the maximum possible value + // to ensure that any gzip stream can be decoded. The offset of 32 specifies + // that the stream type should be automatically detected either gzip or + // deflate. + is_valid_ = inflateInit2(&strm, 32 + 15) == Z_OK; + } + + ~decompressor() { inflateEnd(&strm); } + + bool is_valid() const { return is_valid_; } + + template + bool decompress(const char *data, size_t data_length, T callback) { + int ret = Z_OK; + + strm.avail_in = static_cast(data_length); + strm.next_in = const_cast(reinterpret_cast(data)); + + std::array buff{}; + do { + strm.avail_out = buff.size(); + strm.next_out = reinterpret_cast(buff.data()); + + ret = inflate(&strm, Z_NO_FLUSH); + assert(ret != Z_STREAM_ERROR); + switch (ret) { + case Z_NEED_DICT: + case Z_DATA_ERROR: + case Z_MEM_ERROR: inflateEnd(&strm); return false; + } + + if (!callback(buff.data(), buff.size() - strm.avail_out)) { + return false; + } + } while (strm.avail_out == 0); + + return ret == Z_OK || ret == Z_STREAM_END; + } + +private: + bool is_valid_; + z_stream strm; +}; +#endif + +inline bool has_header(const Headers &headers, const char *key) { + return headers.find(key) != headers.end(); +} + +inline const char *get_header_value(const Headers &headers, const char *key, + size_t id = 0, const char *def = nullptr) { + auto rng = headers.equal_range(key); + auto it = rng.first; + std::advance(it, static_cast(id)); + if (it != rng.second) { return it->second.c_str(); } + return def; +} + +inline uint64_t get_header_value_uint64(const Headers &headers, const char *key, + uint64_t def = 0) { + auto it = headers.find(key); + if (it != headers.end()) { + return std::strtoull(it->second.data(), nullptr, 10); + } + return def; +} + +inline void parse_header(const char *beg, const char *end, Headers &headers) { + auto p = beg; + while (p < end && *p != ':') { + p++; + } + if (p < end) { + auto key_end = p; + p++; // skip ':' + while (p < end && (*p == ' ' || *p == '\t')) { + p++; + } + if (p < end) { + auto val_begin = p; + while (p < end) { + p++; + } + headers.emplace(std::string(beg, key_end), std::string(val_begin, end)); + } + } +} + +inline bool read_headers(Stream &strm, Headers &headers) { + const auto bufsiz = 2048; + char buf[bufsiz]; + stream_line_reader line_reader(strm, buf, bufsiz); + + for (;;) { + if (!line_reader.getline()) { return false; } + + // Check if the line ends with CRLF. + if (line_reader.end_with_crlf()) { + // Blank line indicates end of headers. + if (line_reader.size() == 2) { break; } + } else { + continue; // Skip invalid line. + } + + // Skip trailing spaces and tabs. + auto end = line_reader.ptr() + line_reader.size() - 2; + while (line_reader.ptr() < end && (end[-1] == ' ' || end[-1] == '\t')) { + end--; + } + + parse_header(line_reader.ptr(), end, headers); + } + + return true; +} + +inline bool read_content_with_length(Stream &strm, uint64_t len, + Progress progress, ContentReceiver out) { + char buf[CPPHTTPLIB_RECV_BUFSIZ]; + + uint64_t r = 0; + while (r < len) { + auto read_len = static_cast(len - r); + auto n = strm.read(buf, (std::min)(read_len, CPPHTTPLIB_RECV_BUFSIZ)); + if (n <= 0) { return false; } + + if (!out(buf, static_cast(n))) { return false; } + + r += static_cast(n); + + if (progress) { + if (!progress(r, len)) { return false; } + } + } + + return true; +} + +inline void skip_content_with_length(Stream &strm, uint64_t len) { + char buf[CPPHTTPLIB_RECV_BUFSIZ]; + uint64_t r = 0; + while (r < len) { + auto read_len = static_cast(len - r); + auto n = strm.read(buf, (std::min)(read_len, CPPHTTPLIB_RECV_BUFSIZ)); + if (n <= 0) { return; } + r += static_cast(n); + } +} + +inline bool read_content_without_length(Stream &strm, ContentReceiver out) { + char buf[CPPHTTPLIB_RECV_BUFSIZ]; + for (;;) { + auto n = strm.read(buf, CPPHTTPLIB_RECV_BUFSIZ); + if (n < 0) { + return false; + } else if (n == 0) { + return true; + } + if (!out(buf, static_cast(n))) { return false; } + } + + return true; +} + +inline bool read_content_chunked(Stream &strm, ContentReceiver out) { + const auto bufsiz = 16; + char buf[bufsiz]; + + stream_line_reader line_reader(strm, buf, bufsiz); + + if (!line_reader.getline()) { return false; } + + unsigned long chunk_len; + while (true) { + char *end_ptr; + + chunk_len = std::strtoul(line_reader.ptr(), &end_ptr, 16); + + if (end_ptr == line_reader.ptr()) { return false; } + if (chunk_len == ULONG_MAX) { return false; } + + if (chunk_len == 0) { break; } + + if (!read_content_with_length(strm, chunk_len, nullptr, out)) { + return false; + } + + if (!line_reader.getline()) { return false; } + + if (strcmp(line_reader.ptr(), "\r\n")) { break; } + + if (!line_reader.getline()) { return false; } + } + + if (chunk_len == 0) { + // Reader terminator after chunks + if (!line_reader.getline() || strcmp(line_reader.ptr(), "\r\n")) + return false; + } + + return true; +} + +inline bool is_chunked_transfer_encoding(const Headers &headers) { + return !strcasecmp(get_header_value(headers, "Transfer-Encoding", 0, ""), + "chunked"); +} + +template +bool read_content(Stream &strm, T &x, size_t payload_max_length, int &status, + Progress progress, ContentReceiver receiver, + bool decompress) { + + ContentReceiver out = [&](const char *buf, size_t n) { + return receiver(buf, n); + }; + +#ifdef CPPHTTPLIB_ZLIB_SUPPORT + decompressor decompressor; +#endif + + if (decompress) { +#ifdef CPPHTTPLIB_ZLIB_SUPPORT + std::string content_encoding = x.get_header_value("Content-Encoding"); + if (content_encoding.find("gzip") != std::string::npos || + content_encoding.find("deflate") != std::string::npos) { + if (!decompressor.is_valid()) { + status = 500; + return false; + } + + out = [&](const char *buf, size_t n) { + return decompressor.decompress(buf, n, [&](const char *buf, size_t n) { + return receiver(buf, n); + }); + }; + } +#else + if (x.get_header_value("Content-Encoding") == "gzip") { + status = 415; + return false; + } +#endif + } + + auto ret = true; + auto exceed_payload_max_length = false; + + if (is_chunked_transfer_encoding(x.headers)) { + ret = read_content_chunked(strm, out); + } else if (!has_header(x.headers, "Content-Length")) { + ret = read_content_without_length(strm, out); + } else { + auto len = get_header_value_uint64(x.headers, "Content-Length", 0); + if (len > payload_max_length) { + exceed_payload_max_length = true; + skip_content_with_length(strm, len); + ret = false; + } else if (len > 0) { + ret = read_content_with_length(strm, len, progress, out); + } + } + + if (!ret) { status = exceed_payload_max_length ? 413 : 400; } + return ret; +} + +template +inline ssize_t write_headers(Stream &strm, const T &info, + const Headers &headers) { + ssize_t write_len = 0; + for (const auto &x : info.headers) { + if (x.first == "EXCEPTION_WHAT") { continue; } + auto len = + strm.write_format("%s: %s\r\n", x.first.c_str(), x.second.c_str()); + if (len < 0) { return len; } + write_len += len; + } + for (const auto &x : headers) { + auto len = + strm.write_format("%s: %s\r\n", x.first.c_str(), x.second.c_str()); + if (len < 0) { return len; } + write_len += len; + } + auto len = strm.write("\r\n"); + if (len < 0) { return len; } + write_len += len; + return write_len; +} + +inline bool write_data(Stream &strm, const char *d, size_t l) { + size_t offset = 0; + while (offset < l) { + auto length = strm.write(d + offset, l - offset); + if (length < 0) { return false; } + offset += static_cast(length); + } + return true; +} + +template +inline ssize_t write_content(Stream &strm, ContentProvider content_provider, + size_t offset, size_t length, T is_shutting_down) { + size_t begin_offset = offset; + size_t end_offset = offset + length; + + auto ok = true; + + DataSink data_sink; + data_sink.write = [&](const char *d, size_t l) { + if (ok) { + offset += l; + if (!write_data(strm, d, l)) { ok = false; } + } + }; + data_sink.is_writable = [&](void) { return ok && strm.is_writable(); }; + + while (ok && offset < end_offset && !is_shutting_down()) { + if (!content_provider(offset, end_offset - offset, data_sink)) { + return -1; + } + if (!ok) { return -1; } + } + + return static_cast(offset - begin_offset); +} + +template +inline ssize_t write_content_chunked(Stream &strm, + ContentProvider content_provider, + T is_shutting_down) { + size_t offset = 0; + auto data_available = true; + ssize_t total_written_length = 0; + + auto ok = true; + + DataSink data_sink; + data_sink.write = [&](const char *d, size_t l) { + if (ok) { + data_available = l > 0; + offset += l; + + // Emit chunked response header and footer for each chunk + auto chunk = from_i_to_hex(l) + "\r\n" + std::string(d, l) + "\r\n"; + if (write_data(strm, chunk.data(), chunk.size())) { + total_written_length += chunk.size(); + } else { + ok = false; + } + } + }; + data_sink.done = [&](void) { + data_available = false; + if (ok) { + static const std::string done_marker("0\r\n\r\n"); + if (write_data(strm, done_marker.data(), done_marker.size())) { + total_written_length += done_marker.size(); + } else { + ok = false; + } + } + }; + data_sink.is_writable = [&](void) { return ok && strm.is_writable(); }; + + while (data_available && !is_shutting_down()) { + if (!content_provider(offset, 0, data_sink)) { return -1; } + if (!ok) { return -1; } + } + + return total_written_length; +} + +template +inline bool redirect(T &cli, const Request &req, Response &res, + const std::string &path) { + Request new_req = req; + new_req.path = path; + new_req.redirect_count -= 1; + + if (res.status == 303 && (req.method != "GET" && req.method != "HEAD")) { + new_req.method = "GET"; + new_req.body.clear(); + new_req.headers.clear(); + } + + Response new_res; + + auto ret = cli.send(new_req, new_res); + if (ret) { res = new_res; } + return ret; +} + +inline std::string encode_url(const std::string &s) { + std::string result; + + for (size_t i = 0; s[i]; i++) { + switch (s[i]) { + case ' ': result += "+"; break; + case '=': result += "%3d"; break; + case '+': result += "%2B"; break; + case '\r': result += "%0D"; break; + case '\n': result += "%0A"; break; + case '\'': result += "%27"; break; + case ',': result += "%2C"; break; + // case ':': result += "%3A"; break; // ok? probably... + case ';': result += "%3B"; break; + default: + auto c = static_cast(s[i]); + if (c >= 0x80) { + result += '%'; + char hex[4]; + auto len = snprintf(hex, sizeof(hex) - 1, "%02X", c); + assert(len == 2); + result.append(hex, static_cast(len)); + } else { + result += s[i]; + } + break; + } + } + + return result; +} + +inline std::string decode_url(const std::string &s, + bool convert_plus_to_space) { + std::string result; + + for (size_t i = 0; i < s.size(); i++) { + if (s[i] == '%' && i + 1 < s.size()) { + if (s[i + 1] == 'u') { + int val = 0; + if (from_hex_to_i(s, i + 2, 4, val)) { + // 4 digits Unicode codes + char buff[4]; + size_t len = to_utf8(val, buff); + if (len > 0) { result.append(buff, len); } + i += 5; // 'u0000' + } else { + result += s[i]; + } + } else { + int val = 0; + if (from_hex_to_i(s, i + 1, 2, val)) { + // 2 digits hex codes + result += static_cast(val); + i += 2; // '00' + } else { + result += s[i]; + } + } + } else if (convert_plus_to_space && s[i] == '+') { + result += ' '; + } else { + result += s[i]; + } + } + + return result; +} + +inline std::string params_to_query_str(const Params ¶ms) { + std::string query; + + for (auto it = params.begin(); it != params.end(); ++it) { + if (it != params.begin()) { query += "&"; } + query += it->first; + query += "="; + query += detail::encode_url(it->second); + } + + return query; +} + +inline void parse_query_text(const std::string &s, Params ¶ms) { + split(&s[0], &s[s.size()], '&', [&](const char *b, const char *e) { + std::string key; + std::string val; + split(b, e, '=', [&](const char *b2, const char *e2) { + if (key.empty()) { + key.assign(b2, e2); + } else { + val.assign(b2, e2); + } + }); + params.emplace(decode_url(key, true), decode_url(val, true)); + }); +} + +inline bool parse_multipart_boundary(const std::string &content_type, + std::string &boundary) { + auto pos = content_type.find("boundary="); + if (pos == std::string::npos) { return false; } + boundary = content_type.substr(pos + 9); + if (boundary.length() >= 2 && boundary.front() == '"' && + boundary.back() == '"') { + boundary = boundary.substr(1, boundary.size() - 2); + } + return !boundary.empty(); +} + +inline bool parse_range_header(const std::string &s, Ranges &ranges) { + static auto re_first_range = std::regex(R"(bytes=(\d*-\d*(?:,\s*\d*-\d*)*))"); + std::smatch m; + if (std::regex_match(s, m, re_first_range)) { + auto pos = static_cast(m.position(1)); + auto len = static_cast(m.length(1)); + bool all_valid_ranges = true; + split(&s[pos], &s[pos + len], ',', [&](const char *b, const char *e) { + if (!all_valid_ranges) return; + static auto re_another_range = std::regex(R"(\s*(\d*)-(\d*))"); + std::cmatch cm; + if (std::regex_match(b, e, cm, re_another_range)) { + ssize_t first = -1; + if (!cm.str(1).empty()) { + first = static_cast(std::stoll(cm.str(1))); + } + + ssize_t last = -1; + if (!cm.str(2).empty()) { + last = static_cast(std::stoll(cm.str(2))); + } + + if (first != -1 && last != -1 && first > last) { + all_valid_ranges = false; + return; + } + ranges.emplace_back(std::make_pair(first, last)); + } + }); + return all_valid_ranges; + } + return false; +} + +class MultipartFormDataParser { +public: + MultipartFormDataParser() = default; + + void set_boundary(std::string &&boundary) { boundary_ = boundary; } + + bool is_valid() const { return is_valid_; } + + template + bool parse(const char *buf, size_t n, T content_callback, U header_callback) { + static const std::regex re_content_type(R"(^Content-Type:\s*(.*?)\s*$)", + std::regex_constants::icase); + + static const std::regex re_content_disposition( + "^Content-Disposition:\\s*form-data;\\s*name=\"(.*?)\"(?:;\\s*filename=" + "\"(.*?)\")?\\s*$", + std::regex_constants::icase); + static const std::string dash_ = "--"; + static const std::string crlf_ = "\r\n"; + + buf_.append(buf, n); // TODO: performance improvement + + while (!buf_.empty()) { + switch (state_) { + case 0: { // Initial boundary + auto pattern = dash_ + boundary_ + crlf_; + if (pattern.size() > buf_.size()) { return true; } + auto pos = buf_.find(pattern); + if (pos != 0) { return false; } + buf_.erase(0, pattern.size()); + off_ += pattern.size(); + state_ = 1; + break; + } + case 1: { // New entry + clear_file_info(); + state_ = 2; + break; + } + case 2: { // Headers + auto pos = buf_.find(crlf_); + while (pos != std::string::npos) { + // Empty line + if (pos == 0) { + if (!header_callback(file_)) { + is_valid_ = false; + return false; + } + buf_.erase(0, crlf_.size()); + off_ += crlf_.size(); + state_ = 3; + break; + } + + auto header = buf_.substr(0, pos); + { + std::smatch m; + if (std::regex_match(header, m, re_content_type)) { + file_.content_type = m[1]; + } else if (std::regex_match(header, m, re_content_disposition)) { + file_.name = m[1]; + file_.filename = m[2]; + } + } + + buf_.erase(0, pos + crlf_.size()); + off_ += pos + crlf_.size(); + pos = buf_.find(crlf_); + } + if (state_ != 3) { return true; } + break; + } + case 3: { // Body + { + auto pattern = crlf_ + dash_; + if (pattern.size() > buf_.size()) { return true; } + + auto pos = buf_.find(pattern); + if (pos == std::string::npos) { + pos = buf_.size(); + while (pos > 0) { + auto c = buf_[pos - 1]; + if (c != '\r' && c != '\n' && c != '-') { break; } + pos--; + } + } + + if (!content_callback(buf_.data(), pos)) { + is_valid_ = false; + return false; + } + + off_ += pos; + buf_.erase(0, pos); + } + + { + auto pattern = crlf_ + dash_ + boundary_; + if (pattern.size() > buf_.size()) { return true; } + + auto pos = buf_.find(pattern); + if (pos != std::string::npos) { + if (!content_callback(buf_.data(), pos)) { + is_valid_ = false; + return false; + } + + off_ += pos + pattern.size(); + buf_.erase(0, pos + pattern.size()); + state_ = 4; + } else { + if (!content_callback(buf_.data(), pattern.size())) { + is_valid_ = false; + return false; + } + + off_ += pattern.size(); + buf_.erase(0, pattern.size()); + } + } + break; + } + case 4: { // Boundary + if (crlf_.size() > buf_.size()) { return true; } + if (buf_.find(crlf_) == 0) { + buf_.erase(0, crlf_.size()); + off_ += crlf_.size(); + state_ = 1; + } else { + auto pattern = dash_ + crlf_; + if (pattern.size() > buf_.size()) { return true; } + if (buf_.find(pattern) == 0) { + buf_.erase(0, pattern.size()); + off_ += pattern.size(); + is_valid_ = true; + state_ = 5; + } else { + return true; + } + } + break; + } + case 5: { // Done + is_valid_ = false; + return false; + } + } + } + + return true; + } + +private: + void clear_file_info() { + file_.name.clear(); + file_.filename.clear(); + file_.content_type.clear(); + } + + std::string boundary_; + + std::string buf_; + size_t state_ = 0; + bool is_valid_ = false; + size_t off_ = 0; + MultipartFormData file_; +}; + +inline std::string to_lower(const char *beg, const char *end) { + std::string out; + auto it = beg; + while (it != end) { + out += static_cast(::tolower(*it)); + it++; + } + return out; +} + +inline std::string make_multipart_data_boundary() { + static const char data[] = + "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; + + std::random_device seed_gen; + std::mt19937 engine(seed_gen()); + + std::string result = "--cpp-httplib-multipart-data-"; + + for (auto i = 0; i < 16; i++) { + result += data[engine() % (sizeof(data) - 1)]; + } + + return result; +} + +inline std::pair +get_range_offset_and_length(const Request &req, size_t content_length, + size_t index) { + auto r = req.ranges[index]; + + if (r.first == -1 && r.second == -1) { + return std::make_pair(0, content_length); + } + + auto slen = static_cast(content_length); + + if (r.first == -1) { + r.first = slen - r.second; + r.second = slen - 1; + } + + if (r.second == -1) { r.second = slen - 1; } + + return std::make_pair(r.first, r.second - r.first + 1); +} + +inline std::string make_content_range_header_field(size_t offset, size_t length, + size_t content_length) { + std::string field = "bytes "; + field += std::to_string(offset); + field += "-"; + field += std::to_string(offset + length - 1); + field += "/"; + field += std::to_string(content_length); + return field; +} + +template +bool process_multipart_ranges_data(const Request &req, Response &res, + const std::string &boundary, + const std::string &content_type, + SToken stoken, CToken ctoken, + Content content) { + for (size_t i = 0; i < req.ranges.size(); i++) { + ctoken("--"); + stoken(boundary); + ctoken("\r\n"); + if (!content_type.empty()) { + ctoken("Content-Type: "); + stoken(content_type); + ctoken("\r\n"); + } + + auto offsets = get_range_offset_and_length(req, res.body.size(), i); + auto offset = offsets.first; + auto length = offsets.second; + + ctoken("Content-Range: "); + stoken(make_content_range_header_field(offset, length, res.body.size())); + ctoken("\r\n"); + ctoken("\r\n"); + if (!content(offset, length)) { return false; } + ctoken("\r\n"); + } + + ctoken("--"); + stoken(boundary); + ctoken("--\r\n"); + + return true; +} + +inline std::string make_multipart_ranges_data(const Request &req, Response &res, + const std::string &boundary, + const std::string &content_type) { + std::string data; + + process_multipart_ranges_data( + req, res, boundary, content_type, + [&](const std::string &token) { data += token; }, + [&](const char *token) { data += token; }, + [&](size_t offset, size_t length) { + data += res.body.substr(offset, length); + return true; + }); + + return data; +} + +inline size_t +get_multipart_ranges_data_length(const Request &req, Response &res, + const std::string &boundary, + const std::string &content_type) { + size_t data_length = 0; + + process_multipart_ranges_data( + req, res, boundary, content_type, + [&](const std::string &token) { data_length += token.size(); }, + [&](const char *token) { data_length += strlen(token); }, + [&](size_t /*offset*/, size_t length) { + data_length += length; + return true; + }); + + return data_length; +} + +template +inline bool write_multipart_ranges_data(Stream &strm, const Request &req, + Response &res, + const std::string &boundary, + const std::string &content_type, + T is_shutting_down) { + return process_multipart_ranges_data( + req, res, boundary, content_type, + [&](const std::string &token) { strm.write(token); }, + [&](const char *token) { strm.write(token); }, + [&](size_t offset, size_t length) { + return write_content(strm, res.content_provider_, offset, length, + is_shutting_down) >= 0; + }); +} + +inline std::pair +get_range_offset_and_length(const Request &req, const Response &res, + size_t index) { + auto r = req.ranges[index]; + + if (r.second == -1) { + r.second = static_cast(res.content_length_) - 1; + } + + return std::make_pair(r.first, r.second - r.first + 1); +} + +inline bool expect_content(const Request &req) { + if (req.method == "POST" || req.method == "PUT" || req.method == "PATCH" || + req.method == "PRI" || + (req.method == "DELETE" && req.has_header("Content-Length"))) { + return true; + } + // TODO: check if Content-Length is set + return false; +} + +inline bool has_crlf(const char *s) { + auto p = s; + while (*p) { + if (*p == '\r' || *p == '\n') { return true; } + p++; + } + return false; +} + +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT +template +inline std::string message_digest(const std::string &s, Init init, + Update update, Final final, + size_t digest_length) { + using namespace std; + + std::vector md(digest_length, 0); + CTX ctx; + init(&ctx); + update(&ctx, s.data(), s.size()); + final(md.data(), &ctx); + + stringstream ss; + for (auto c : md) { + ss << setfill('0') << setw(2) << hex << (unsigned int)c; + } + return ss.str(); +} + +inline std::string MD5(const std::string &s) { + return message_digest(s, MD5_Init, MD5_Update, MD5_Final, + MD5_DIGEST_LENGTH); +} + +inline std::string SHA_256(const std::string &s) { + return message_digest(s, SHA256_Init, SHA256_Update, SHA256_Final, + SHA256_DIGEST_LENGTH); +} + +inline std::string SHA_512(const std::string &s) { + return message_digest(s, SHA512_Init, SHA512_Update, SHA512_Final, + SHA512_DIGEST_LENGTH); +} +#endif + +#ifdef _WIN32 +class WSInit { +public: + WSInit() { + WSADATA wsaData; + WSAStartup(0x0002, &wsaData); + } + + ~WSInit() { WSACleanup(); } +}; + +static WSInit wsinit_; +#endif + +} // namespace detail + +// Header utilities +inline std::pair make_range_header(Ranges ranges) { + std::string field = "bytes="; + auto i = 0; + for (auto r : ranges) { + if (i != 0) { field += ", "; } + if (r.first != -1) { field += std::to_string(r.first); } + field += '-'; + if (r.second != -1) { field += std::to_string(r.second); } + i++; + } + return std::make_pair("Range", field); +} + +inline std::pair +make_basic_authentication_header(const std::string &username, + const std::string &password, + bool is_proxy = false) { + auto field = "Basic " + detail::base64_encode(username + ":" + password); + auto key = is_proxy ? "Proxy-Authorization" : "Authorization"; + return std::make_pair(key, field); +} + +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT +inline std::pair make_digest_authentication_header( + const Request &req, const std::map &auth, + size_t cnonce_count, const std::string &cnonce, const std::string &username, + const std::string &password, bool is_proxy = false) { + using namespace std; + + string nc; + { + stringstream ss; + ss << setfill('0') << setw(8) << hex << cnonce_count; + nc = ss.str(); + } + + auto qop = auth.at("qop"); + if (qop.find("auth-int") != std::string::npos) { + qop = "auth-int"; + } else { + qop = "auth"; + } + + std::string algo = "MD5"; + if (auth.find("algorithm") != auth.end()) { algo = auth.at("algorithm"); } + + string response; + { + auto H = algo == "SHA-256" + ? detail::SHA_256 + : algo == "SHA-512" ? detail::SHA_512 : detail::MD5; + + auto A1 = username + ":" + auth.at("realm") + ":" + password; + + auto A2 = req.method + ":" + req.path; + if (qop == "auth-int") { A2 += ":" + H(req.body); } + + response = H(H(A1) + ":" + auth.at("nonce") + ":" + nc + ":" + cnonce + + ":" + qop + ":" + H(A2)); + } + + auto field = "Digest username=\"" + username + "\", realm=\"" + + auth.at("realm") + "\", nonce=\"" + auth.at("nonce") + + "\", uri=\"" + req.path + "\", algorithm=" + algo + + ", qop=" + qop + ", nc=\"" + nc + "\", cnonce=\"" + cnonce + + "\", response=\"" + response + "\""; + + auto key = is_proxy ? "Proxy-Authorization" : "Authorization"; + return std::make_pair(key, field); +} +#endif + +inline bool parse_www_authenticate(const Response &res, + std::map &auth, + bool is_proxy) { + auto auth_key = is_proxy ? "Proxy-Authenticate" : "WWW-Authenticate"; + if (res.has_header(auth_key)) { + static auto re = std::regex(R"~((?:(?:,\s*)?(.+?)=(?:"(.*?)"|([^,]*))))~"); + auto s = res.get_header_value(auth_key); + auto pos = s.find(' '); + if (pos != std::string::npos) { + auto type = s.substr(0, pos); + if (type == "Basic") { + return false; + } else if (type == "Digest") { + s = s.substr(pos + 1); + auto beg = std::sregex_iterator(s.begin(), s.end(), re); + for (auto i = beg; i != std::sregex_iterator(); ++i) { + auto m = *i; + auto key = s.substr(static_cast(m.position(1)), + static_cast(m.length(1))); + auto val = m.length(2) > 0 + ? s.substr(static_cast(m.position(2)), + static_cast(m.length(2))) + : s.substr(static_cast(m.position(3)), + static_cast(m.length(3))); + auth[key] = val; + } + return true; + } + } + } + return false; +} + +// https://stackoverflow.com/questions/440133/how-do-i-create-a-random-alpha-numeric-string-in-c/440240#answer-440240 +inline std::string random_string(size_t length) { + auto randchar = []() -> char { + const char charset[] = "0123456789" + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz"; + const size_t max_index = (sizeof(charset) - 1); + return charset[static_cast(rand()) % max_index]; + }; + std::string str(length, 0); + std::generate_n(str.begin(), length, randchar); + return str; +} + +// Request implementation +inline bool Request::has_header(const char *key) const { + return detail::has_header(headers, key); +} + +inline std::string Request::get_header_value(const char *key, size_t id) const { + return detail::get_header_value(headers, key, id, ""); +} + +inline size_t Request::get_header_value_count(const char *key) const { + auto r = headers.equal_range(key); + return static_cast(std::distance(r.first, r.second)); +} + +inline void Request::set_header(const char *key, const char *val) { + if (!detail::has_crlf(key) && !detail::has_crlf(val)) { + headers.emplace(key, val); + } +} + +inline void Request::set_header(const char *key, const std::string &val) { + if (!detail::has_crlf(key) && !detail::has_crlf(val.c_str())) { + headers.emplace(key, val); + } +} + +inline bool Request::has_param(const char *key) const { + return params.find(key) != params.end(); +} + +inline std::string Request::get_param_value(const char *key, size_t id) const { + auto rng = params.equal_range(key); + auto it = rng.first; + std::advance(it, static_cast(id)); + if (it != rng.second) { return it->second; } + return std::string(); +} + +inline size_t Request::get_param_value_count(const char *key) const { + auto r = params.equal_range(key); + return static_cast(std::distance(r.first, r.second)); +} + +inline bool Request::is_multipart_form_data() const { + const auto &content_type = get_header_value("Content-Type"); + return !content_type.find("multipart/form-data"); +} + +inline bool Request::has_file(const char *key) const { + return files.find(key) != files.end(); +} + +inline MultipartFormData Request::get_file_value(const char *key) const { + auto it = files.find(key); + if (it != files.end()) { return it->second; } + return MultipartFormData(); +} + +// Response implementation +inline bool Response::has_header(const char *key) const { + return headers.find(key) != headers.end(); +} + +inline std::string Response::get_header_value(const char *key, + size_t id) const { + return detail::get_header_value(headers, key, id, ""); +} + +inline size_t Response::get_header_value_count(const char *key) const { + auto r = headers.equal_range(key); + return static_cast(std::distance(r.first, r.second)); +} + +inline void Response::set_header(const char *key, const char *val) { + if (!detail::has_crlf(key) && !detail::has_crlf(val)) { + headers.emplace(key, val); + } +} + +inline void Response::set_header(const char *key, const std::string &val) { + if (!detail::has_crlf(key) && !detail::has_crlf(val.c_str())) { + headers.emplace(key, val); + } +} + +inline void Response::set_redirect(const char *url, int stat) { + if (!detail::has_crlf(url)) { + set_header("Location", url); + if (300 <= stat && stat < 400) { + this->status = stat; + } else { + this->status = 302; + } + } +} + +inline void Response::set_content(const char *s, size_t n, + const char *content_type) { + body.assign(s, n); + set_header("Content-Type", content_type); +} + +inline void Response::set_content(std::string s, const char *content_type) { + body = std::move(s); + set_header("Content-Type", content_type); +} + +inline void +Response::set_content_provider(size_t in_length, ContentProvider provider, + std::function resource_releaser) { + assert(in_length > 0); + content_length_ = in_length; + content_provider_ = [provider](size_t offset, size_t length, DataSink &sink) { + return provider(offset, length, sink); + }; + content_provider_resource_releaser_ = resource_releaser; +} + +inline void Response::set_chunked_content_provider( + ChunkedContentProvider provider, std::function resource_releaser) { + content_length_ = 0; + content_provider_ = [provider](size_t offset, size_t, DataSink &sink) { + return provider(offset, sink); + }; + content_provider_resource_releaser_ = resource_releaser; +} + +// Rstream implementation +inline ssize_t Stream::write(const char *ptr) { + return write(ptr, strlen(ptr)); +} + +inline ssize_t Stream::write(const std::string &s) { + return write(s.data(), s.size()); +} + +template +inline ssize_t Stream::write_format(const char *fmt, const Args &... args) { + std::array buf; + +#if defined(_MSC_VER) && _MSC_VER < 1900 + auto sn = _snprintf_s(buf, bufsiz, buf.size() - 1, fmt, args...); +#else + auto sn = snprintf(buf.data(), buf.size() - 1, fmt, args...); +#endif + if (sn <= 0) { return sn; } + + auto n = static_cast(sn); + + if (n >= buf.size() - 1) { + std::vector glowable_buf(buf.size()); + + while (n >= glowable_buf.size() - 1) { + glowable_buf.resize(glowable_buf.size() * 2); +#if defined(_MSC_VER) && _MSC_VER < 1900 + n = static_cast(_snprintf_s(&glowable_buf[0], glowable_buf.size(), + glowable_buf.size() - 1, fmt, + args...)); +#else + n = static_cast( + snprintf(&glowable_buf[0], glowable_buf.size() - 1, fmt, args...)); +#endif + } + return write(&glowable_buf[0], n); + } else { + return write(buf.data(), n); + } +} + +namespace detail { + +// Socket stream implementation +inline SocketStream::SocketStream(socket_t sock, time_t read_timeout_sec, + time_t read_timeout_usec, + time_t write_timeout_sec, + time_t write_timeout_usec) + : sock_(sock), read_timeout_sec_(read_timeout_sec), + read_timeout_usec_(read_timeout_usec), + write_timeout_sec_(write_timeout_sec), + write_timeout_usec_(write_timeout_usec) {} + +inline SocketStream::~SocketStream() {} + +inline bool SocketStream::is_readable() const { + return select_read(sock_, read_timeout_sec_, read_timeout_usec_) > 0; +} + +inline bool SocketStream::is_writable() const { + return select_write(sock_, write_timeout_sec_, write_timeout_usec_) > 0; +} + +inline ssize_t SocketStream::read(char *ptr, size_t size) { + if (!is_readable()) { return -1; } + +#ifdef _WIN32 + if (size > static_cast((std::numeric_limits::max)())) { + return -1; + } + return recv(sock_, ptr, static_cast(size), 0); +#else + return handle_EINTR([&]() { return recv(sock_, ptr, size, 0); }); +#endif +} + +inline ssize_t SocketStream::write(const char *ptr, size_t size) { + if (!is_writable()) { return -1; } + +#ifdef _WIN32 + if (size > static_cast((std::numeric_limits::max)())) { + return -1; + } + return send(sock_, ptr, static_cast(size), 0); +#else + return handle_EINTR([&]() { return send(sock_, ptr, size, 0); }); +#endif +} + +inline void SocketStream::get_remote_ip_and_port(std::string &ip, + int &port) const { + return detail::get_remote_ip_and_port(sock_, ip, port); +} + +// Buffer stream implementation +inline bool BufferStream::is_readable() const { return true; } + +inline bool BufferStream::is_writable() const { return true; } + +inline ssize_t BufferStream::read(char *ptr, size_t size) { +#if defined(_MSC_VER) && _MSC_VER < 1900 + auto len_read = buffer._Copy_s(ptr, size, size, position); +#else + auto len_read = buffer.copy(ptr, size, position); +#endif + position += static_cast(len_read); + return static_cast(len_read); +} + +inline ssize_t BufferStream::write(const char *ptr, size_t size) { + buffer.append(ptr, size); + return static_cast(size); +} + +inline void BufferStream::get_remote_ip_and_port(std::string & /*ip*/, + int & /*port*/) const {} + +inline const std::string &BufferStream::get_buffer() const { return buffer; } + +} // namespace detail + +// HTTP server implementation +inline Server::Server() : svr_sock_(INVALID_SOCKET), is_running_(false) { +#ifndef _WIN32 + signal(SIGPIPE, SIG_IGN); +#endif + new_task_queue = [] { return new ThreadPool(CPPHTTPLIB_THREAD_POOL_COUNT); }; +} + +inline Server::~Server() {} + +inline Server &Server::Get(const char *pattern, Handler handler) { + get_handlers_.push_back(std::make_pair(std::regex(pattern), handler)); + return *this; +} + +inline Server &Server::Post(const char *pattern, Handler handler) { + post_handlers_.push_back(std::make_pair(std::regex(pattern), handler)); + return *this; +} + +inline Server &Server::Post(const char *pattern, + HandlerWithContentReader handler) { + post_handlers_for_content_reader_.push_back( + std::make_pair(std::regex(pattern), handler)); + return *this; +} + +inline Server &Server::Put(const char *pattern, Handler handler) { + put_handlers_.push_back(std::make_pair(std::regex(pattern), handler)); + return *this; +} + +inline Server &Server::Put(const char *pattern, + HandlerWithContentReader handler) { + put_handlers_for_content_reader_.push_back( + std::make_pair(std::regex(pattern), handler)); + return *this; +} + +inline Server &Server::Patch(const char *pattern, Handler handler) { + patch_handlers_.push_back(std::make_pair(std::regex(pattern), handler)); + return *this; +} + +inline Server &Server::Patch(const char *pattern, + HandlerWithContentReader handler) { + patch_handlers_for_content_reader_.push_back( + std::make_pair(std::regex(pattern), handler)); + return *this; +} + +inline Server &Server::Delete(const char *pattern, Handler handler) { + delete_handlers_.push_back(std::make_pair(std::regex(pattern), handler)); + return *this; +} + +inline Server &Server::Delete(const char *pattern, + HandlerWithContentReader handler) { + delete_handlers_for_content_reader_.push_back( + std::make_pair(std::regex(pattern), handler)); + return *this; +} + +inline Server &Server::Options(const char *pattern, Handler handler) { + options_handlers_.push_back(std::make_pair(std::regex(pattern), handler)); + return *this; +} + +inline bool Server::set_base_dir(const char *dir, const char *mount_point) { + return set_mount_point(mount_point, dir); +} + +inline bool Server::set_mount_point(const char *mount_point, const char *dir) { + if (detail::is_dir(dir)) { + std::string mnt = mount_point ? mount_point : "/"; + if (!mnt.empty() && mnt[0] == '/') { + base_dirs_.emplace_back(mnt, dir); + return true; + } + } + return false; +} + +inline bool Server::remove_mount_point(const char *mount_point) { + for (auto it = base_dirs_.begin(); it != base_dirs_.end(); ++it) { + if (it->first == mount_point) { + base_dirs_.erase(it); + return true; + } + } + return false; +} + +inline void Server::set_file_extension_and_mimetype_mapping(const char *ext, + const char *mime) { + file_extension_and_mimetype_map_[ext] = mime; +} + +inline void Server::set_file_request_handler(Handler handler) { + file_request_handler_ = std::move(handler); +} + +inline void Server::set_error_handler(Handler handler) { + error_handler_ = std::move(handler); +} + +inline void Server::set_socket_options(SocketOptions socket_options) { + socket_options_ = socket_options; +} + +inline void Server::set_logger(Logger logger) { logger_ = std::move(logger); } + +inline void +Server::set_expect_100_continue_handler(Expect100ContinueHandler handler) { + expect_100_continue_handler_ = std::move(handler); +} + +inline void Server::set_keep_alive_max_count(size_t count) { + keep_alive_max_count_ = count; +} + +inline void Server::set_read_timeout(time_t sec, time_t usec) { + read_timeout_sec_ = sec; + read_timeout_usec_ = usec; +} + +inline void Server::set_write_timeout(time_t sec, time_t usec) { + write_timeout_sec_ = sec; + write_timeout_usec_ = usec; +} + +inline void Server::set_idle_interval(time_t sec, time_t usec) { + idle_interval_sec_ = sec; + idle_interval_usec_ = usec; +} + +inline void Server::set_payload_max_length(size_t length) { + payload_max_length_ = length; +} + +inline bool Server::bind_to_port(const char *host, int port, int socket_flags) { + if (bind_internal(host, port, socket_flags) < 0) return false; + return true; +} +inline int Server::bind_to_any_port(const char *host, int socket_flags) { + return bind_internal(host, 0, socket_flags); +} + +inline bool Server::listen_after_bind() { return listen_internal(); } + +inline bool Server::listen(const char *host, int port, int socket_flags) { + return bind_to_port(host, port, socket_flags) && listen_internal(); +} + +inline bool Server::is_running() const { return is_running_; } + +inline void Server::stop() { + if (is_running_) { + assert(svr_sock_ != INVALID_SOCKET); + std::atomic sock(svr_sock_.exchange(INVALID_SOCKET)); + detail::shutdown_socket(sock); + detail::close_socket(sock); + } +} + +inline bool Server::parse_request_line(const char *s, Request &req) { + const static std::regex re( + "(GET|HEAD|POST|PUT|DELETE|CONNECT|OPTIONS|TRACE|PATCH|PRI) " + "(([^?]+)(?:\\?(.*?))?) (HTTP/1\\.[01])\r\n"); + + std::cmatch m; + if (std::regex_match(s, m, re)) { + req.version = std::string(m[5]); + req.method = std::string(m[1]); + req.target = std::string(m[2]); + req.path = detail::decode_url(m[3], false); + + // Parse query text + auto len = std::distance(m[4].first, m[4].second); + if (len > 0) { detail::parse_query_text(m[4], req.params); } + + return true; + } + + return false; +} + +inline bool Server::write_response(Stream &strm, bool close_connection, + const Request &req, Response &res) { + assert(res.status != -1); + + if (400 <= res.status && error_handler_) { error_handler_(req, res); } + + detail::BufferStream bstrm; + + // Response line + if (!bstrm.write_format("HTTP/1.1 %d %s\r\n", res.status, + detail::status_message(res.status))) { + return false; + } + + // Headers + if (close_connection || req.get_header_value("Connection") == "close") { + res.set_header("Connection", "close"); + } + + if (!close_connection && req.get_header_value("Connection") == "Keep-Alive") { + res.set_header("Connection", "Keep-Alive"); + } + + if (!res.has_header("Content-Type") && + (!res.body.empty() || res.content_length_ > 0)) { + res.set_header("Content-Type", "text/plain"); + } + + if (!res.has_header("Accept-Ranges") && req.method == "HEAD") { + res.set_header("Accept-Ranges", "bytes"); + } + + std::string content_type; + std::string boundary; + + if (req.ranges.size() > 1) { + boundary = detail::make_multipart_data_boundary(); + + auto it = res.headers.find("Content-Type"); + if (it != res.headers.end()) { + content_type = it->second; + res.headers.erase(it); + } + + res.headers.emplace("Content-Type", + "multipart/byteranges; boundary=" + boundary); + } + + if (res.body.empty()) { + if (res.content_length_ > 0) { + size_t length = 0; + if (req.ranges.empty()) { + length = res.content_length_; + } else if (req.ranges.size() == 1) { + auto offsets = + detail::get_range_offset_and_length(req, res.content_length_, 0); + auto offset = offsets.first; + length = offsets.second; + auto content_range = detail::make_content_range_header_field( + offset, length, res.content_length_); + res.set_header("Content-Range", content_range); + } else { + length = detail::get_multipart_ranges_data_length(req, res, boundary, + content_type); + } + res.set_header("Content-Length", std::to_string(length)); + } else { + if (res.content_provider_) { + res.set_header("Transfer-Encoding", "chunked"); + } else { + res.set_header("Content-Length", "0"); + } + } + } else { + if (req.ranges.empty()) { + ; + } else if (req.ranges.size() == 1) { + auto offsets = + detail::get_range_offset_and_length(req, res.body.size(), 0); + auto offset = offsets.first; + auto length = offsets.second; + auto content_range = detail::make_content_range_header_field( + offset, length, res.body.size()); + res.set_header("Content-Range", content_range); + res.body = res.body.substr(offset, length); + } else { + res.body = + detail::make_multipart_ranges_data(req, res, boundary, content_type); + } + +#ifdef CPPHTTPLIB_ZLIB_SUPPORT + // TODO: 'Accept-Encoding' has gzip, not gzip;q=0 + const auto &encodings = req.get_header_value("Accept-Encoding"); + if (encodings.find("gzip") != std::string::npos && + detail::can_compress(res.get_header_value("Content-Type"))) { + if (detail::compress(res.body)) { + res.set_header("Content-Encoding", "gzip"); + } + } +#endif + + auto length = std::to_string(res.body.size()); + res.set_header("Content-Length", length); + } + + if (!detail::write_headers(bstrm, res, Headers())) { return false; } + + // Flush buffer + auto &data = bstrm.get_buffer(); + strm.write(data.data(), data.size()); + + // Body + if (req.method != "HEAD") { + if (!res.body.empty()) { + if (!strm.write(res.body)) { return false; } + } else if (res.content_provider_) { + if (!write_content_with_provider(strm, req, res, boundary, + content_type)) { + return false; + } + } + } + + // Log + if (logger_) { logger_(req, res); } + + return true; +} + +inline bool +Server::write_content_with_provider(Stream &strm, const Request &req, + Response &res, const std::string &boundary, + const std::string &content_type) { + auto is_shutting_down = [this]() { + return this->svr_sock_ == INVALID_SOCKET; + }; + + if (res.content_length_) { + if (req.ranges.empty()) { + if (detail::write_content(strm, res.content_provider_, 0, + res.content_length_, is_shutting_down) < 0) { + return false; + } + } else if (req.ranges.size() == 1) { + auto offsets = + detail::get_range_offset_and_length(req, res.content_length_, 0); + auto offset = offsets.first; + auto length = offsets.second; + if (detail::write_content(strm, res.content_provider_, offset, length, + is_shutting_down) < 0) { + return false; + } + } else { + if (!detail::write_multipart_ranges_data( + strm, req, res, boundary, content_type, is_shutting_down)) { + return false; + } + } + } else { + if (detail::write_content_chunked(strm, res.content_provider_, + is_shutting_down) < 0) { + return false; + } + } + return true; +} + +inline bool Server::read_content(Stream &strm, Request &req, Response &res) { + MultipartFormDataMap::iterator cur; + if (read_content_core( + strm, req, res, + // Regular + [&](const char *buf, size_t n) { + if (req.body.size() + n > req.body.max_size()) { return false; } + req.body.append(buf, n); + return true; + }, + // Multipart + [&](const MultipartFormData &file) { + cur = req.files.emplace(file.name, file); + return true; + }, + [&](const char *buf, size_t n) { + auto &content = cur->second.content; + if (content.size() + n > content.max_size()) { return false; } + content.append(buf, n); + return true; + })) { + const auto &content_type = req.get_header_value("Content-Type"); + if (!content_type.find("application/x-www-form-urlencoded")) { + detail::parse_query_text(req.body, req.params); + } + return true; + } + return false; +} + +inline bool Server::read_content_with_content_receiver( + Stream &strm, Request &req, Response &res, ContentReceiver receiver, + MultipartContentHeader multipart_header, + ContentReceiver multipart_receiver) { + return read_content_core(strm, req, res, receiver, multipart_header, + multipart_receiver); +} + +inline bool Server::read_content_core(Stream &strm, Request &req, Response &res, + ContentReceiver receiver, + MultipartContentHeader mulitpart_header, + ContentReceiver multipart_receiver) { + detail::MultipartFormDataParser multipart_form_data_parser; + ContentReceiver out; + + if (req.is_multipart_form_data()) { + const auto &content_type = req.get_header_value("Content-Type"); + std::string boundary; + if (!detail::parse_multipart_boundary(content_type, boundary)) { + res.status = 400; + return false; + } + + multipart_form_data_parser.set_boundary(std::move(boundary)); + out = [&](const char *buf, size_t n) { + /* For debug + size_t pos = 0; + while (pos < n) { + auto read_size = std::min(1, n - pos); + auto ret = multipart_form_data_parser.parse( + buf + pos, read_size, multipart_receiver, mulitpart_header); + if (!ret) { return false; } + pos += read_size; + } + return true; + */ + return multipart_form_data_parser.parse(buf, n, multipart_receiver, + mulitpart_header); + }; + } else { + out = receiver; + } + + if (!detail::read_content(strm, req, payload_max_length_, res.status, + Progress(), out, true)) { + return false; + } + + if (req.is_multipart_form_data()) { + if (!multipart_form_data_parser.is_valid()) { + res.status = 400; + return false; + } + } + + return true; +} + +inline bool Server::handle_file_request(Request &req, Response &res, + bool head) { + for (const auto &kv : base_dirs_) { + const auto &mount_point = kv.first; + const auto &base_dir = kv.second; + + // Prefix match + if (!req.path.find(mount_point)) { + std::string sub_path = "/" + req.path.substr(mount_point.size()); + if (detail::is_valid_path(sub_path)) { + auto path = base_dir + sub_path; + if (path.back() == '/') { path += "index.html"; } + + if (detail::is_file(path)) { + detail::read_file(path, res.body); + auto type = + detail::find_content_type(path, file_extension_and_mimetype_map_); + if (type) { res.set_header("Content-Type", type); } + res.status = 200; + if (!head && file_request_handler_) { + file_request_handler_(req, res); + } + return true; + } + } + } + } + return false; +} + +inline socket_t +Server::create_server_socket(const char *host, int port, int socket_flags, + SocketOptions socket_options) const { + return detail::create_socket( + host, port, socket_flags, socket_options, + [](socket_t sock, struct addrinfo &ai) -> bool { + if (::bind(sock, ai.ai_addr, static_cast(ai.ai_addrlen))) { + return false; + } + if (::listen(sock, 5)) { // Listen through 5 channels + return false; + } + return true; + }); +} + +inline int Server::bind_internal(const char *host, int port, int socket_flags) { + if (!is_valid()) { return -1; } + + svr_sock_ = create_server_socket(host, port, socket_flags, socket_options_); + if (svr_sock_ == INVALID_SOCKET) { return -1; } + + if (port == 0) { + struct sockaddr_storage addr; + socklen_t addr_len = sizeof(addr); + if (getsockname(svr_sock_, reinterpret_cast(&addr), + &addr_len) == -1) { + return -1; + } + if (addr.ss_family == AF_INET) { + return ntohs(reinterpret_cast(&addr)->sin_port); + } else if (addr.ss_family == AF_INET6) { + return ntohs(reinterpret_cast(&addr)->sin6_port); + } else { + return -1; + } + } else { + return port; + } +} + +inline bool Server::listen_internal() { + auto ret = true; + is_running_ = true; + + { + std::unique_ptr task_queue(new_task_queue()); + + while (svr_sock_ != INVALID_SOCKET) { +#ifndef _WIN32 + if (idle_interval_sec_ > 0 || idle_interval_usec_ > 0) { +#endif + auto val = detail::select_read(svr_sock_, idle_interval_sec_, + idle_interval_usec_); + if (val == 0) { // Timeout + task_queue->on_idle(); + continue; + } +#ifndef _WIN32 + } +#endif + socket_t sock = accept(svr_sock_, nullptr, nullptr); + + if (sock == INVALID_SOCKET) { + if (errno == EMFILE) { + // The per-process limit of open file descriptors has been reached. + // Try to accept new connections after a short sleep. + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + continue; + } + if (svr_sock_ != INVALID_SOCKET) { + detail::close_socket(svr_sock_); + ret = false; + } else { + ; // The server socket was closed by user. + } + break; + } + +#if __cplusplus > 201703L + task_queue->enqueue([=, this]() { process_and_close_socket(sock); }); +#else + task_queue->enqueue([=]() { process_and_close_socket(sock); }); +#endif + } + + task_queue->shutdown(); + } + + is_running_ = false; + return ret; +} + +inline bool Server::routing(Request &req, Response &res, Stream &strm) { + // File handler + bool is_head_request = req.method == "HEAD"; + if ((req.method == "GET" || is_head_request) && + handle_file_request(req, res, is_head_request)) { + return true; + } + + if (detail::expect_content(req)) { + // Content reader handler + { + ContentReader reader( + [&](ContentReceiver receiver) { + return read_content_with_content_receiver(strm, req, res, receiver, + nullptr, nullptr); + }, + [&](MultipartContentHeader header, ContentReceiver receiver) { + return read_content_with_content_receiver(strm, req, res, nullptr, + header, receiver); + }); + + if (req.method == "POST") { + if (dispatch_request_for_content_reader( + req, res, reader, post_handlers_for_content_reader_)) { + return true; + } + } else if (req.method == "PUT") { + if (dispatch_request_for_content_reader( + req, res, reader, put_handlers_for_content_reader_)) { + return true; + } + } else if (req.method == "PATCH") { + if (dispatch_request_for_content_reader( + req, res, reader, patch_handlers_for_content_reader_)) { + return true; + } + } else if (req.method == "DELETE") { + if (dispatch_request_for_content_reader( + req, res, reader, delete_handlers_for_content_reader_)) { + return true; + } + } + } + + // Read content into `req.body` + if (!read_content(strm, req, res)) { return false; } + } + + // Regular handler + if (req.method == "GET" || req.method == "HEAD") { + return dispatch_request(req, res, get_handlers_); + } else if (req.method == "POST") { + return dispatch_request(req, res, post_handlers_); + } else if (req.method == "PUT") { + return dispatch_request(req, res, put_handlers_); + } else if (req.method == "DELETE") { + return dispatch_request(req, res, delete_handlers_); + } else if (req.method == "OPTIONS") { + return dispatch_request(req, res, options_handlers_); + } else if (req.method == "PATCH") { + return dispatch_request(req, res, patch_handlers_); + } + + res.status = 400; + return false; +} + +inline bool Server::dispatch_request(Request &req, Response &res, + Handlers &handlers) { + + try { + for (const auto &x : handlers) { + const auto &pattern = x.first; + const auto &handler = x.second; + + if (std::regex_match(req.path, req.matches, pattern)) { + handler(req, res); + return true; + } + } + } catch (const std::exception &ex) { + res.status = 500; + res.set_header("EXCEPTION_WHAT", ex.what()); + } catch (...) { + res.status = 500; + res.set_header("EXCEPTION_WHAT", "UNKNOWN"); + } + return false; +} + +inline bool Server::dispatch_request_for_content_reader( + Request &req, Response &res, ContentReader content_reader, + HandlersForContentReader &handlers) { + for (const auto &x : handlers) { + const auto &pattern = x.first; + const auto &handler = x.second; + + if (std::regex_match(req.path, req.matches, pattern)) { + handler(req, res, content_reader); + return true; + } + } + return false; +} + +inline bool +Server::process_request(Stream &strm, bool close_connection, + bool &connection_closed, + const std::function &setup_request) { + std::array buf{}; + + detail::stream_line_reader line_reader(strm, buf.data(), buf.size()); + + // Connection has been closed on client + if (!line_reader.getline()) { return false; } + + Request req; + Response res; + + res.version = "HTTP/1.1"; + + // Check if the request URI doesn't exceed the limit + if (line_reader.size() > CPPHTTPLIB_REQUEST_URI_MAX_LENGTH) { + Headers dummy; + detail::read_headers(strm, dummy); + res.status = 414; + return write_response(strm, close_connection, req, res); + } + + // Request line and headers + if (!parse_request_line(line_reader.ptr(), req) || + !detail::read_headers(strm, req.headers)) { + res.status = 400; + return write_response(strm, close_connection, req, res); + } + + if (req.get_header_value("Connection") == "close") { + connection_closed = true; + } + + if (req.version == "HTTP/1.0" && + req.get_header_value("Connection") != "Keep-Alive") { + connection_closed = true; + } + + strm.get_remote_ip_and_port(req.remote_addr, req.remote_port); + req.set_header("REMOTE_ADDR", req.remote_addr); + req.set_header("REMOTE_PORT", std::to_string(req.remote_port)); + + if (req.has_header("Range")) { + const auto &range_header_value = req.get_header_value("Range"); + if (!detail::parse_range_header(range_header_value, req.ranges)) { + // TODO: error + } + } + + if (setup_request) { setup_request(req); } + + if (req.get_header_value("Expect") == "100-continue") { + auto status = 100; + if (expect_100_continue_handler_) { + status = expect_100_continue_handler_(req, res); + } + switch (status) { + case 100: + case 417: + strm.write_format("HTTP/1.1 %d %s\r\n\r\n", status, + detail::status_message(status)); + break; + default: return write_response(strm, close_connection, req, res); + } + } + + // Rounting + if (routing(req, res, strm)) { + if (res.status == -1) { res.status = req.ranges.empty() ? 200 : 206; } + } else { + if (res.status == -1) { res.status = 404; } + } + + return write_response(strm, close_connection, req, res); +} + +inline bool Server::is_valid() const { return true; } + +inline bool Server::process_and_close_socket(socket_t sock) { + auto ret = detail::process_server_socket( + sock, keep_alive_max_count_, read_timeout_sec_, read_timeout_usec_, + write_timeout_sec_, write_timeout_usec_, + [this](Stream &strm, bool close_connection, bool &connection_closed) { + return process_request(strm, close_connection, connection_closed, + nullptr); + }); + + // std::this_thread::sleep_for(std::chrono::milliseconds(1)); + detail::shutdown_socket(sock); + detail::close_socket(sock); + return ret; +} + +// HTTP client implementation +inline Client::Client(const std::string &host) + : Client(host, 80, std::string(), std::string()) {} + +inline Client::Client(const std::string &host, int port) + : Client(host, port, std::string(), std::string()) {} + +inline Client::Client(const std::string &host, int port, + const std::string &client_cert_path, + const std::string &client_key_path) + : host_(host), port_(port), + host_and_port_(host_ + ":" + std::to_string(port_)), + client_cert_path_(client_cert_path), client_key_path_(client_key_path) {} + +inline Client::~Client() { stop(); } + +inline bool Client::is_valid() const { return true; } + +inline socket_t Client::create_client_socket() const { + if (!proxy_host_.empty()) { + return detail::create_client_socket(proxy_host_.c_str(), proxy_port_, + nullptr, connection_timeout_sec_, + connection_timeout_usec_, interface_); + } + return detail::create_client_socket(host_.c_str(), port_, nullptr, + connection_timeout_sec_, + connection_timeout_usec_, interface_); +} + +inline bool Client::create_and_connect_socket(Socket &socket) { + auto sock = create_client_socket(); + if (sock == INVALID_SOCKET) { return false; } + socket.sock = sock; + return true; +} + +inline void Client::close_socket(Socket &socket, bool /*process_socket_ret*/) { + detail::close_socket(socket.sock); + socket_.sock = INVALID_SOCKET; +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + socket_.ssl = nullptr; +#endif +} + +inline bool Client::read_response_line(Stream &strm, Response &res) { + std::array buf; + + detail::stream_line_reader line_reader(strm, buf.data(), buf.size()); + + if (!line_reader.getline()) { return false; } + + const static std::regex re("(HTTP/1\\.[01]) (\\d+?) .*\r\n"); + + std::cmatch m; + if (std::regex_match(line_reader.ptr(), m, re)) { + res.version = std::string(m[1]); + res.status = std::stoi(std::string(m[2])); + } + + return true; +} + +inline bool Client::send(const Request &req, Response &res) { + std::lock_guard request_mutex_guard(request_mutex_); + + { + std::lock_guard guard(socket_mutex_); + + auto is_alive = false; + if (socket_.is_open()) { + is_alive = detail::select_write(socket_.sock, 0, 0) > 0; + if (!is_alive) { close_socket(socket_, false); } + } + + if (!is_alive) { + if (!create_and_connect_socket(socket_)) { return false; } + +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + // TODO: refactoring + if (is_ssl()) { + auto &scli = static_cast(*this); + if (!proxy_host_.empty()) { + bool success = false; + if (!scli.connect_with_proxy(socket_, res, success)) { + return success; + } + } + + if (!scli.initialize_ssl(socket_)) { return false; } + } +#endif + } + } + + auto close_connection = !keep_alive_; + + auto ret = process_socket(socket_, [&](Stream &strm) { + return handle_request(strm, req, res, close_connection); + }); + + if (close_connection) { stop(); } + + return ret; +} + +inline bool Client::handle_request(Stream &strm, const Request &req, + Response &res, bool close_connection) { + if (req.path.empty()) { return false; } + + bool ret; + + if (!is_ssl() && !proxy_host_.empty()) { + auto req2 = req; + req2.path = "http://" + host_and_port_ + req.path; + ret = process_request(strm, req2, res, close_connection); + } else { + ret = process_request(strm, req, res, close_connection); + } + + if (!ret) { return false; } + + if (300 < res.status && res.status < 400 && follow_location_) { + ret = redirect(req, res); + } + +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + if ((res.status == 401 || res.status == 407) && + req.authorization_count_ < 5) { + auto is_proxy = res.status == 407; + const auto &username = + is_proxy ? proxy_digest_auth_username_ : digest_auth_username_; + const auto &password = + is_proxy ? proxy_digest_auth_password_ : digest_auth_password_; + + if (!username.empty() && !password.empty()) { + std::map auth; + if (parse_www_authenticate(res, auth, is_proxy)) { + Request new_req = req; + new_req.authorization_count_ += 1; + auto key = is_proxy ? "Proxy-Authorization" : "Authorization"; + new_req.headers.erase(key); + new_req.headers.insert(make_digest_authentication_header( + req, auth, new_req.authorization_count_, random_string(10), + username, password, is_proxy)); + + Response new_res; + + ret = send(new_req, new_res); + if (ret) { res = new_res; } + } + } + } +#endif + + return ret; +} + +inline bool Client::redirect(const Request &req, Response &res) { + if (req.redirect_count == 0) { return false; } + + auto location = res.get_header_value("location"); + if (location.empty()) { return false; } + + const static std::regex re( + R"(^(?:(https?):)?(?://([^:/?#]*)(?::(\d+))?)?([^?#]*(?:\?[^#]*)?)(?:#.*)?)"); + + std::smatch m; + if (!std::regex_match(location, m, re)) { return false; } + + auto scheme = is_ssl() ? "https" : "http"; + + auto next_scheme = m[1].str(); + auto next_host = m[2].str(); + auto port_str = m[3].str(); + auto next_path = m[4].str(); + + auto next_port = port_; + if (!port_str.empty()) { + next_port = std::stoi(port_str); + } else if (!next_scheme.empty()) { + next_port = next_scheme == "https" ? 443 : 80; + } + + if (next_scheme.empty()) { next_scheme = scheme; } + if (next_host.empty()) { next_host = host_; } + if (next_path.empty()) { next_path = "/"; } + + if (next_scheme == scheme && next_host == host_ && next_port == port_) { + return detail::redirect(*this, req, res, next_path); + } else { + if (next_scheme == "https") { +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + SSLClient cli(next_host.c_str(), next_port); + cli.copy_settings(*this); + return detail::redirect(cli, req, res, next_path); +#else + return false; +#endif + } else { + Client cli(next_host.c_str(), next_port); + cli.copy_settings(*this); + return detail::redirect(cli, req, res, next_path); + } + } +} + +inline bool Client::write_request(Stream &strm, const Request &req, + bool close_connection) { + detail::BufferStream bstrm; + + // Request line + std::string path = detail::encode_url(req.path); + if (!req.query.empty()) { + path = path + "?" + req.query; + } + + bstrm.write_format("%s %s HTTP/1.1\r\n", req.method.c_str(), path.c_str()); + + // Additonal headers + Headers headers; + if (close_connection) { headers.emplace("Connection", "close"); } + + if (!req.has_header("Host")) { + if (is_ssl()) { + if (port_ == 443) { + headers.emplace("Host", host_); + } else { + headers.emplace("Host", host_and_port_); + } + } else { + if (port_ == 80) { + headers.emplace("Host", host_); + } else { + headers.emplace("Host", host_and_port_); + } + } + } + + if (!req.has_header("Accept")) { headers.emplace("Accept", "*/*"); } + + if (!req.has_header("User-Agent")) { + headers.emplace("User-Agent", "cpp-httplib/0.7"); + } + + if (req.body.empty()) { + if (req.content_provider) { + auto length = std::to_string(req.content_length); + headers.emplace("Content-Length", length); + } else { + headers.emplace("Content-Length", "0"); + } + } else { + if (!req.has_header("Content-Type")) { + headers.emplace("Content-Type", "text/plain"); + } + + if (!req.has_header("Content-Length")) { + auto length = std::to_string(req.body.size()); + headers.emplace("Content-Length", length); + } + } + + if (!basic_auth_username_.empty() && !basic_auth_password_.empty()) { + headers.insert(make_basic_authentication_header( + basic_auth_username_, basic_auth_password_, false)); + } + + if (!proxy_basic_auth_username_.empty() && + !proxy_basic_auth_password_.empty()) { + headers.insert(make_basic_authentication_header( + proxy_basic_auth_username_, proxy_basic_auth_password_, true)); + } + + detail::write_headers(bstrm, req, headers); + + // Flush buffer + auto &data = bstrm.get_buffer(); + if (!detail::write_data(strm, data.data(), data.size())) { return false; } + + // Body + if (req.body.empty()) { + if (req.content_provider) { + size_t offset = 0; + size_t end_offset = req.content_length; + + bool ok = true; + + DataSink data_sink; + data_sink.write = [&](const char *d, size_t l) { + if (ok) { + if (detail::write_data(strm, d, l)) { + offset += l; + } else { + ok = false; + } + } + }; + data_sink.is_writable = [&](void) { return ok && strm.is_writable(); }; + + while (offset < end_offset) { + if (!req.content_provider(offset, end_offset - offset, data_sink)) { + return false; + } + if (!ok) { return false; } + } + } + } else { + return detail::write_data(strm, req.body.data(), req.body.size()); + } + + return true; +} + +inline std::shared_ptr Client::send_with_content_provider( + const char *method, const char *path, const Headers &headers, + const std::string &body, size_t content_length, + ContentProvider content_provider, const char *content_type) { + Request req; + req.method = method; + req.headers = headers; + req.path = path; + + if (content_type) { req.headers.emplace("Content-Type", content_type); } + +#ifdef CPPHTTPLIB_ZLIB_SUPPORT + if (compress_) { + if (content_provider) { + size_t offset = 0; + + DataSink data_sink; + data_sink.write = [&](const char *data, size_t data_len) { + req.body.append(data, data_len); + offset += data_len; + }; + data_sink.is_writable = [&](void) { return true; }; + + while (offset < content_length) { + if (!content_provider(offset, content_length - offset, data_sink)) { + return nullptr; + } + } + } else { + req.body = body; + } + + if (!detail::compress(req.body)) { return nullptr; } + req.headers.emplace("Content-Encoding", "gzip"); + } else +#endif + { + if (content_provider) { + req.content_length = content_length; + req.content_provider = content_provider; + } else { + req.body = body; + } + } + + auto res = std::make_shared(); + + return send(req, *res) ? res : nullptr; +} + +inline bool Client::process_request(Stream &strm, const Request &req, + Response &res, bool close_connection) { + // Send request + if (!write_request(strm, req, close_connection)) { return false; } + + // Receive response and headers + if (!read_response_line(strm, res) || + !detail::read_headers(strm, res.headers)) { + return false; + } + + if (req.response_handler) { + if (!req.response_handler(res)) { return false; } + } + + // Body + if (req.method != "HEAD" && req.method != "CONNECT") { + auto out = + req.content_receiver + ? static_cast([&](const char *buf, size_t n) { + return req.content_receiver(buf, n); + }) + : static_cast([&](const char *buf, size_t n) { + if (res.body.size() + n > res.body.max_size()) { return false; } + res.body.append(buf, n); + return true; + }); + + int dummy_status; + if (!detail::read_content(strm, res, (std::numeric_limits::max)(), + dummy_status, req.progress, out, decompress_)) { + return false; + } + } + + if (res.get_header_value("Connection") == "close" || + res.version == "HTTP/1.0") { + stop(); + } + + // Log + if (logger_) { logger_(req, res); } + + return true; +} + +inline bool Client::process_socket(Socket &socket, + std::function callback) { + return detail::process_client_socket(socket.sock, read_timeout_sec_, + read_timeout_usec_, write_timeout_sec_, + write_timeout_usec_, callback); +} + +inline bool Client::is_ssl() const { return false; } + + +inline std::shared_ptr Client::Get(const char *path) { + return Get(path, Headers(), Progress()); +} + +inline std::shared_ptr Client::Get(const char *path, + Progress progress) { + return Get(path, Headers(), std::move(progress)); +} + +inline std::shared_ptr Client::Get(const char *path, + const Headers &headers) { + return Get(path, headers, Progress()); +} + +inline std::shared_ptr +Client::Get(const char *path, const Headers &headers, Progress progress) { + Request req; + req.method = "GET"; + req.path = path; + req.headers = headers; + req.progress = std::move(progress); + + auto res = std::make_shared(); + return send(req, *res) ? res : nullptr; +} + +inline +std::shared_ptr +Client::Get(const char *path, std::map params, const Headers &headers) { + Request req; + req.method = "GET"; + req.path = path; + req.headers = headers; + req.progress = std::move(Progress()); + + std::string query; + std::map::iterator iter; + for(iter = params.begin(); iter != params.end(); iter++) { + query.append("&").append(iter->first + "=" + detail::encode_url(iter->second)); + } + req.query = query; + + auto res = std::make_shared(); + return send(req, *res) ? res : nullptr; +} + +inline std::shared_ptr Client::Get(const char *path, + ContentReceiver content_receiver) { + return Get(path, Headers(), nullptr, std::move(content_receiver), Progress()); +} + +inline std::shared_ptr Client::Get(const char *path, + ContentReceiver content_receiver, + Progress progress) { + return Get(path, Headers(), nullptr, std::move(content_receiver), + std::move(progress)); +} + +inline std::shared_ptr Client::Get(const char *path, + const Headers &headers, + ContentReceiver content_receiver) { + return Get(path, headers, nullptr, std::move(content_receiver), Progress()); +} + +inline std::shared_ptr Client::Get(const char *path, + const Headers &headers, + ContentReceiver content_receiver, + Progress progress) { + return Get(path, headers, nullptr, std::move(content_receiver), + std::move(progress)); +} + +inline std::shared_ptr Client::Get(const char *path, + const Headers &headers, + ResponseHandler response_handler, + ContentReceiver content_receiver) { + return Get(path, headers, std::move(response_handler), content_receiver, + Progress()); +} + +inline std::shared_ptr Client::Get(const char *path, + const Headers &headers, + ResponseHandler response_handler, + ContentReceiver content_receiver, + Progress progress) { + Request req; + req.method = "GET"; + req.path = path; + req.headers = headers; + req.response_handler = std::move(response_handler); + req.content_receiver = std::move(content_receiver); + req.progress = std::move(progress); + + auto res = std::make_shared(); + return send(req, *res) ? res : nullptr; +} + +inline std::shared_ptr Client::Head(const char *path) { + return Head(path, Headers()); +} + +inline std::shared_ptr Client::Head(const char *path, + const Headers &headers) { + Request req; + req.method = "HEAD"; + req.headers = headers; + req.path = path; + + auto res = std::make_shared(); + + return send(req, *res) ? res : nullptr; +} + +inline std::shared_ptr Client::Post(const char *path) { + return Post(path, std::string(), nullptr); +} + +inline std::shared_ptr Client::Post(const char *path, + const std::string &body, + const char *content_type) { + return Post(path, Headers(), body, content_type); +} + +inline std::shared_ptr Client::Post(const char *path, + const Headers &headers, + const std::string &body, + const char *content_type) { + return send_with_content_provider("POST", path, headers, body, 0, nullptr, + content_type); +} + +inline std::shared_ptr Client::Post(const char *path, + const Params ¶ms) { + return Post(path, Headers(), params); +} + +inline std::shared_ptr Client::Post(const char *path, + size_t content_length, + ContentProvider content_provider, + const char *content_type) { + return Post(path, Headers(), content_length, content_provider, content_type); +} + +inline std::shared_ptr +Client::Post(const char *path, const Headers &headers, size_t content_length, + ContentProvider content_provider, const char *content_type) { + return send_with_content_provider("POST", path, headers, std::string(), + content_length, content_provider, + content_type); +} + +inline std::shared_ptr +Client::Post(const char *path, const Headers &headers, const Params ¶ms) { + auto query = detail::params_to_query_str(params); + return Post(path, headers, query, "application/x-www-form-urlencoded"); +} + +inline std::shared_ptr +Client::Post(const char *path, const MultipartFormDataItems &items) { + return Post(path, Headers(), items); +} + +inline std::shared_ptr +Client::Post(const char *path, const Headers &headers, + const MultipartFormDataItems &items) { + auto boundary = detail::make_multipart_data_boundary(); + + std::string body; + + for (const auto &item : items) { + body += "--" + boundary + "\r\n"; + body += "Content-Disposition: form-data; name=\"" + item.name + "\""; + if (!item.filename.empty()) { + body += "; filename=\"" + item.filename + "\""; + } + body += "\r\n"; + if (!item.content_type.empty()) { + body += "Content-Type: " + item.content_type + "\r\n"; + } + body += "\r\n"; + body += item.content + "\r\n"; + } + + body += "--" + boundary + "--\r\n"; + + std::string content_type = "multipart/form-data; boundary=" + boundary; + return Post(path, headers, body, content_type.c_str()); +} + +inline std::shared_ptr Client::Put(const char *path) { + return Put(path, std::string(), nullptr); +} + +inline std::shared_ptr Client::Put(const char *path, + const std::string &body, + const char *content_type) { + return Put(path, Headers(), body, content_type); +} + +inline std::shared_ptr Client::Put(const char *path, + const Headers &headers, + const std::string &body, + const char *content_type) { + return send_with_content_provider("PUT", path, headers, body, 0, nullptr, + content_type); +} + +inline std::shared_ptr Client::Put(const char *path, + size_t content_length, + ContentProvider content_provider, + const char *content_type) { + return Put(path, Headers(), content_length, content_provider, content_type); +} + +inline std::shared_ptr +Client::Put(const char *path, const Headers &headers, size_t content_length, + ContentProvider content_provider, const char *content_type) { + return send_with_content_provider("PUT", path, headers, std::string(), + content_length, content_provider, + content_type); +} + +inline std::shared_ptr Client::Put(const char *path, + const Params ¶ms) { + return Put(path, Headers(), params); +} + +inline std::shared_ptr +Client::Put(const char *path, const Headers &headers, const Params ¶ms) { + auto query = detail::params_to_query_str(params); + return Put(path, headers, query, "application/x-www-form-urlencoded"); +} + +inline std::shared_ptr Client::Patch(const char *path, + const std::string &body, + const char *content_type) { + return Patch(path, Headers(), body, content_type); +} + +inline std::shared_ptr Client::Patch(const char *path, + const Headers &headers, + const std::string &body, + const char *content_type) { + return send_with_content_provider("PATCH", path, headers, body, 0, nullptr, + content_type); +} + +inline std::shared_ptr Client::Patch(const char *path, + size_t content_length, + ContentProvider content_provider, + const char *content_type) { + return Patch(path, Headers(), content_length, content_provider, content_type); +} + +inline std::shared_ptr +Client::Patch(const char *path, const Headers &headers, size_t content_length, + ContentProvider content_provider, const char *content_type) { + return send_with_content_provider("PATCH", path, headers, std::string(), + content_length, content_provider, + content_type); +} + +inline std::shared_ptr Client::Delete(const char *path) { + return Delete(path, Headers(), std::string(), nullptr); +} + +inline std::shared_ptr Client::Delete(const char *path, + const std::string &body, + const char *content_type) { + return Delete(path, Headers(), body, content_type); +} + +inline std::shared_ptr Client::Delete(const char *path, + const Headers &headers) { + return Delete(path, headers, std::string(), nullptr); +} + +inline std::shared_ptr Client::Delete(const char *path, + const Headers &headers, + const std::string &body, + const char *content_type) { + Request req; + req.method = "DELETE"; + req.headers = headers; + req.path = path; + + if (content_type) { req.headers.emplace("Content-Type", content_type); } + req.body = body; + + auto res = std::make_shared(); + + return send(req, *res) ? res : nullptr; +} + +inline std::shared_ptr Client::Options(const char *path) { + return Options(path, Headers()); +} + +inline std::shared_ptr Client::Options(const char *path, + const Headers &headers) { + Request req; + req.method = "OPTIONS"; + req.path = path; + req.headers = headers; + + auto res = std::make_shared(); + + return send(req, *res) ? res : nullptr; +} + +inline size_t Client::is_socket_open() const { + std::lock_guard guard(socket_mutex_); + return socket_.is_open(); +} + +inline void Client::stop() { + std::lock_guard guard(socket_mutex_); + if (socket_.is_open()) { + detail::shutdown_socket(socket_.sock); + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + close_socket(socket_, true); + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + } +} + +inline void Client::set_timeout_sec(time_t timeout_sec) { + set_connection_timeout(timeout_sec, 0); +} + +inline void Client::set_connection_timeout(time_t sec, time_t usec) { + connection_timeout_sec_ = sec; + connection_timeout_usec_ = usec; +} + +inline void Client::set_read_timeout(time_t sec, time_t usec) { + read_timeout_sec_ = sec; + read_timeout_usec_ = usec; +} + +inline void Client::set_write_timeout(time_t sec, time_t usec) { + write_timeout_sec_ = sec; + write_timeout_usec_ = usec; +} + +inline void Client::set_basic_auth(const char *username, const char *password) { + basic_auth_username_ = username; + basic_auth_password_ = password; +} + +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT +inline void Client::set_digest_auth(const char *username, + const char *password) { + digest_auth_username_ = username; + digest_auth_password_ = password; +} +#endif + +inline void Client::set_keep_alive(bool on) { keep_alive_ = on; } + +inline void Client::set_follow_location(bool on) { follow_location_ = on; } + +inline void Client::set_compress(bool on) { compress_ = on; } + +inline void Client::set_decompress(bool on) { decompress_ = on; } + +inline void Client::set_interface(const char *intf) { interface_ = intf; } + +inline void Client::set_proxy(const char *host, int port) { + proxy_host_ = host; + proxy_port_ = port; +} + +inline void Client::set_proxy_basic_auth(const char *username, + const char *password) { + proxy_basic_auth_username_ = username; + proxy_basic_auth_password_ = password; +} + +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT +inline void Client::set_proxy_digest_auth(const char *username, + const char *password) { + proxy_digest_auth_username_ = username; + proxy_digest_auth_password_ = password; +} +#endif + +inline void Client::set_logger(Logger logger) { logger_ = std::move(logger); } + +/* + * SSL Implementation + */ +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT +namespace detail { + +template +inline SSL *ssl_new(socket_t sock, SSL_CTX *ctx, std::mutex &ctx_mutex, + U SSL_connect_or_accept, V setup) { + SSL *ssl = nullptr; + { + std::lock_guard guard(ctx_mutex); + ssl = SSL_new(ctx); + } + + if (ssl) { + auto bio = BIO_new_socket(static_cast(sock), BIO_NOCLOSE); + SSL_set_bio(ssl, bio, bio); + + if (!setup(ssl) || SSL_connect_or_accept(ssl) != 1) { + SSL_shutdown(ssl); + { + std::lock_guard guard(ctx_mutex); + SSL_free(ssl); + } + return nullptr; + } + } + + return ssl; +} + +inline void ssl_delete(std::mutex &ctx_mutex, SSL *ssl, + bool process_socket_ret) { + if (process_socket_ret) { + SSL_shutdown(ssl); // shutdown only if not already closed by remote + } + + std::lock_guard guard(ctx_mutex); + SSL_free(ssl); +} + +template +inline bool +process_server_socket_ssl(SSL *ssl, socket_t sock, size_t keep_alive_max_count, + time_t read_timeout_sec, time_t read_timeout_usec, + time_t write_timeout_sec, time_t write_timeout_usec, + T callback) { + return process_server_socket_core( + sock, keep_alive_max_count, + [&](bool close_connection, bool connection_closed) { + SSLSocketStream strm(sock, ssl, read_timeout_sec, read_timeout_usec, + write_timeout_sec, write_timeout_usec); + return callback(strm, close_connection, connection_closed); + }); +} + +template +inline bool +process_client_socket_ssl(SSL *ssl, socket_t sock, time_t read_timeout_sec, + time_t read_timeout_usec, time_t write_timeout_sec, + time_t write_timeout_usec, T callback) { + SSLSocketStream strm(sock, ssl, read_timeout_sec, read_timeout_usec, + write_timeout_sec, write_timeout_usec); + return callback(strm); +} + +#if OPENSSL_VERSION_NUMBER < 0x10100000L +static std::shared_ptr> openSSL_locks_; + +class SSLThreadLocks { +public: + SSLThreadLocks() { + openSSL_locks_ = + std::make_shared>(CRYPTO_num_locks()); + CRYPTO_set_locking_callback(locking_callback); + } + + ~SSLThreadLocks() { CRYPTO_set_locking_callback(nullptr); } + +private: + static void locking_callback(int mode, int type, const char * /*file*/, + int /*line*/) { + auto &lk = (*openSSL_locks_)[static_cast(type)]; + if (mode & CRYPTO_LOCK) { + lk.lock(); + } else { + lk.unlock(); + } + } +}; + +#endif + +class SSLInit { +public: + SSLInit() { +#if OPENSSL_VERSION_NUMBER < 0x1010001fL + SSL_load_error_strings(); + SSL_library_init(); +#else + OPENSSL_init_ssl( + OPENSSL_INIT_LOAD_SSL_STRINGS | OPENSSL_INIT_LOAD_CRYPTO_STRINGS, NULL); +#endif + } + + ~SSLInit() { +#if OPENSSL_VERSION_NUMBER < 0x1010001fL + ERR_free_strings(); +#endif + } + +private: +#if OPENSSL_VERSION_NUMBER < 0x10100000L + SSLThreadLocks thread_init_; +#endif +}; + +// SSL socket stream implementation +inline SSLSocketStream::SSLSocketStream(socket_t sock, SSL *ssl, + time_t read_timeout_sec, + time_t read_timeout_usec, + time_t write_timeout_sec, + time_t write_timeout_usec) + : sock_(sock), ssl_(ssl), read_timeout_sec_(read_timeout_sec), + read_timeout_usec_(read_timeout_usec), + write_timeout_sec_(write_timeout_sec), + write_timeout_usec_(write_timeout_usec) {} + +inline SSLSocketStream::~SSLSocketStream() {} + +inline bool SSLSocketStream::is_readable() const { + return detail::select_read(sock_, read_timeout_sec_, read_timeout_usec_) > 0; +} + +inline bool SSLSocketStream::is_writable() const { + return detail::select_write(sock_, write_timeout_sec_, write_timeout_usec_) > + 0; +} + +inline ssize_t SSLSocketStream::read(char *ptr, size_t size) { + if (SSL_pending(ssl_) > 0 || is_readable()) { + return SSL_read(ssl_, ptr, static_cast(size)); + } + return -1; +} + +inline ssize_t SSLSocketStream::write(const char *ptr, size_t size) { + if (is_writable()) { return SSL_write(ssl_, ptr, static_cast(size)); } + return -1; +} + +inline void SSLSocketStream::get_remote_ip_and_port(std::string &ip, + int &port) const { + detail::get_remote_ip_and_port(sock_, ip, port); +} + +static SSLInit sslinit_; + +} // namespace detail + +// SSL HTTP server implementation +inline SSLServer::SSLServer(const char *cert_path, const char *private_key_path, + const char *client_ca_cert_file_path, + const char *client_ca_cert_dir_path) { + ctx_ = SSL_CTX_new(SSLv23_server_method()); + + if (ctx_) { + SSL_CTX_set_options(ctx_, + SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | + SSL_OP_NO_COMPRESSION | + SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION); + + // auto ecdh = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1); + // SSL_CTX_set_tmp_ecdh(ctx_, ecdh); + // EC_KEY_free(ecdh); + + if (SSL_CTX_use_certificate_chain_file(ctx_, cert_path) != 1 || + SSL_CTX_use_PrivateKey_file(ctx_, private_key_path, SSL_FILETYPE_PEM) != + 1) { + SSL_CTX_free(ctx_); + ctx_ = nullptr; + } else if (client_ca_cert_file_path || client_ca_cert_dir_path) { + // if (client_ca_cert_file_path) { + // auto list = SSL_load_client_CA_file(client_ca_cert_file_path); + // SSL_CTX_set_client_CA_list(ctx_, list); + // } + + SSL_CTX_load_verify_locations(ctx_, client_ca_cert_file_path, + client_ca_cert_dir_path); + + SSL_CTX_set_verify( + ctx_, + SSL_VERIFY_PEER | + SSL_VERIFY_FAIL_IF_NO_PEER_CERT, // SSL_VERIFY_CLIENT_ONCE, + nullptr); + } + } +} + +inline SSLServer::SSLServer(X509 *cert, EVP_PKEY *private_key, + X509_STORE *client_ca_cert_store) { + ctx_ = SSL_CTX_new(SSLv23_server_method()); + + if (ctx_) { + SSL_CTX_set_options(ctx_, + SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | + SSL_OP_NO_COMPRESSION | + SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION); + + if (SSL_CTX_use_certificate(ctx_, cert) != 1 || + SSL_CTX_use_PrivateKey(ctx_, private_key) != 1) { + SSL_CTX_free(ctx_); + ctx_ = nullptr; + } else if (client_ca_cert_store) { + + SSL_CTX_set_cert_store(ctx_, client_ca_cert_store); + + SSL_CTX_set_verify( + ctx_, + SSL_VERIFY_PEER | + SSL_VERIFY_FAIL_IF_NO_PEER_CERT, // SSL_VERIFY_CLIENT_ONCE, + nullptr); + } + } +} + +inline SSLServer::~SSLServer() { + if (ctx_) { SSL_CTX_free(ctx_); } +} + +inline bool SSLServer::is_valid() const { return ctx_; } + +inline bool SSLServer::process_and_close_socket(socket_t sock) { + auto ssl = detail::ssl_new(sock, ctx_, ctx_mutex_, SSL_accept, + [](SSL * /*ssl*/) { return true; }); + + if (ssl) { + auto ret = detail::process_server_socket_ssl( + ssl, sock, keep_alive_max_count_, read_timeout_sec_, read_timeout_usec_, + write_timeout_sec_, write_timeout_usec_, + [this, ssl](Stream &strm, bool close_connection, + bool &connection_closed) { + return process_request(strm, close_connection, connection_closed, + [&](Request &req) { req.ssl = ssl; }); + }); + + detail::ssl_delete(ctx_mutex_, ssl, ret); + return ret; + } + + detail::close_socket(sock); + return false; +} + +// SSL HTTP client implementation +inline SSLClient::SSLClient(const std::string &host) + : SSLClient(host, 443, std::string(), std::string()) {} + +inline SSLClient::SSLClient(const std::string &host, int port) + : SSLClient(host, port, std::string(), std::string()) {} + +inline SSLClient::SSLClient(const std::string &host, int port, + const std::string &client_cert_path, + const std::string &client_key_path) + : Client(host, port, client_cert_path, client_key_path) { + ctx_ = SSL_CTX_new(SSLv23_client_method()); + + detail::split(&host_[0], &host_[host_.size()], '.', + [&](const char *b, const char *e) { + host_components_.emplace_back(std::string(b, e)); + }); + if (!client_cert_path.empty() && !client_key_path.empty()) { + if (SSL_CTX_use_certificate_file(ctx_, client_cert_path.c_str(), + SSL_FILETYPE_PEM) != 1 || + SSL_CTX_use_PrivateKey_file(ctx_, client_key_path.c_str(), + SSL_FILETYPE_PEM) != 1) { + SSL_CTX_free(ctx_); + ctx_ = nullptr; + } + } +} + +inline SSLClient::SSLClient(const std::string &host, int port, + X509 *client_cert, EVP_PKEY *client_key) + : Client(host, port) { + ctx_ = SSL_CTX_new(SSLv23_client_method()); + + detail::split(&host_[0], &host_[host_.size()], '.', + [&](const char *b, const char *e) { + host_components_.emplace_back(std::string(b, e)); + }); + if (client_cert != nullptr && client_key != nullptr) { + if (SSL_CTX_use_certificate(ctx_, client_cert) != 1 || + SSL_CTX_use_PrivateKey(ctx_, client_key) != 1) { + SSL_CTX_free(ctx_); + ctx_ = nullptr; + } + } +} + +inline SSLClient::~SSLClient() { + if (ctx_) { SSL_CTX_free(ctx_); } +} + +inline bool SSLClient::is_valid() const { return ctx_; } + +inline void SSLClient::set_ca_cert_path(const char *ca_cert_file_path, + const char *ca_cert_dir_path) { + if (ca_cert_file_path) { ca_cert_file_path_ = ca_cert_file_path; } + if (ca_cert_dir_path) { ca_cert_dir_path_ = ca_cert_dir_path; } +} + +inline void SSLClient::set_ca_cert_store(X509_STORE *ca_cert_store) { + if (ca_cert_store) { ca_cert_store_ = ca_cert_store; } +} + +inline void SSLClient::enable_server_certificate_verification(bool enabled) { + server_certificate_verification_ = enabled; +} + +inline long SSLClient::get_openssl_verify_result() const { + return verify_result_; +} + +inline SSL_CTX *SSLClient::ssl_context() const { return ctx_; } + +inline bool SSLClient::create_and_connect_socket(Socket &socket) { + return is_valid() && Client::create_and_connect_socket(socket); +} + +inline bool SSLClient::connect_with_proxy(Socket &socket, Response &res, + bool &success) { + success = true; + Response res2; + + if (!detail::process_client_socket( + socket.sock, read_timeout_sec_, read_timeout_usec_, + write_timeout_sec_, write_timeout_usec_, [&](Stream &strm) { + Request req2; + req2.method = "CONNECT"; + req2.path = host_and_port_; + return process_request(strm, req2, res2, false); + })) { + close_socket(socket, true); + success = false; + return false; + } + + if (res2.status == 407) { + if (!proxy_digest_auth_username_.empty() && + !proxy_digest_auth_password_.empty()) { + std::map auth; + if (parse_www_authenticate(res2, auth, true)) { + Response res3; + if (!detail::process_client_socket( + socket.sock, read_timeout_sec_, read_timeout_usec_, + write_timeout_sec_, write_timeout_usec_, [&](Stream &strm) { + Request req3; + req3.method = "CONNECT"; + req3.path = host_and_port_; + req3.headers.insert(make_digest_authentication_header( + req3, auth, 1, random_string(10), + proxy_digest_auth_username_, proxy_digest_auth_password_, + true)); + return process_request(strm, req3, res3, false); + })) { + close_socket(socket, true); + success = false; + return false; + } + } + } else { + res = res2; + return false; + } + } + + return true; +} + +inline bool SSLClient::initialize_ssl(Socket &socket) { + auto ssl = detail::ssl_new( + socket.sock, ctx_, ctx_mutex_, + [&](SSL *ssl) { + if (ca_cert_file_path_.empty() && ca_cert_store_ == nullptr) { + SSL_CTX_set_verify(ctx_, SSL_VERIFY_NONE, nullptr); + } else if (!ca_cert_file_path_.empty()) { + if (!SSL_CTX_load_verify_locations(ctx_, ca_cert_file_path_.c_str(), + nullptr)) { + return false; + } + SSL_CTX_set_verify(ctx_, SSL_VERIFY_PEER, nullptr); + } else if (ca_cert_store_ != nullptr) { + if (SSL_CTX_get_cert_store(ctx_) != ca_cert_store_) { + SSL_CTX_set_cert_store(ctx_, ca_cert_store_); + } + SSL_CTX_set_verify(ctx_, SSL_VERIFY_PEER, nullptr); + } + + if (SSL_connect(ssl) != 1) { return false; } + + if (server_certificate_verification_) { + verify_result_ = SSL_get_verify_result(ssl); + + if (verify_result_ != X509_V_OK) { return false; } + + auto server_cert = SSL_get_peer_certificate(ssl); + + if (server_cert == nullptr) { return false; } + + if (!verify_host(server_cert)) { + X509_free(server_cert); + return false; + } + X509_free(server_cert); + } + + return true; + }, + [&](SSL *ssl) { + SSL_set_tlsext_host_name(ssl, host_.c_str()); + return true; + }); + + if (ssl) { + socket.ssl = ssl; + return true; + } + + close_socket(socket, false); + return false; +} + +inline void SSLClient::close_socket(Socket &socket, bool process_socket_ret) { + detail::close_socket(socket.sock); + socket_.sock = INVALID_SOCKET; + if (socket.ssl) { + detail::ssl_delete(ctx_mutex_, socket.ssl, process_socket_ret); + socket_.ssl = nullptr; + } +} + +inline bool +SSLClient::process_socket(Socket &socket, + std::function callback) { + assert(socket.ssl); + return detail::process_client_socket_ssl( + socket.ssl, socket.sock, read_timeout_sec_, read_timeout_usec_, + write_timeout_sec_, write_timeout_usec_, callback); +} + +inline bool SSLClient::is_ssl() const { return true; } + +inline bool SSLClient::verify_host(X509 *server_cert) const { + /* Quote from RFC2818 section 3.1 "Server Identity" + + If a subjectAltName extension of type dNSName is present, that MUST + be used as the identity. Otherwise, the (most specific) Common Name + field in the Subject field of the certificate MUST be used. Although + the use of the Common Name is existing practice, it is deprecated and + Certification Authorities are encouraged to use the dNSName instead. + + Matching is performed using the matching rules specified by + [RFC2459]. If more than one identity of a given type is present in + the certificate (e.g., more than one dNSName name, a match in any one + of the set is considered acceptable.) Names may contain the wildcard + character * which is considered to match any single domain name + component or component fragment. E.g., *.a.com matches foo.a.com but + not bar.foo.a.com. f*.com matches foo.com but not bar.com. + + In some cases, the URI is specified as an IP address rather than a + hostname. In this case, the iPAddress subjectAltName must be present + in the certificate and must exactly match the IP in the URI. + + */ + return verify_host_with_subject_alt_name(server_cert) || + verify_host_with_common_name(server_cert); +} + +inline bool +SSLClient::verify_host_with_subject_alt_name(X509 *server_cert) const { + auto ret = false; + + auto type = GEN_DNS; + + struct in6_addr addr6; + struct in_addr addr; + size_t addr_len = 0; + +#ifndef __MINGW32__ + if (inet_pton(AF_INET6, host_.c_str(), &addr6)) { + type = GEN_IPADD; + addr_len = sizeof(struct in6_addr); + } else if (inet_pton(AF_INET, host_.c_str(), &addr)) { + type = GEN_IPADD; + addr_len = sizeof(struct in_addr); + } +#endif + + auto alt_names = static_cast( + X509_get_ext_d2i(server_cert, NID_subject_alt_name, nullptr, nullptr)); + + if (alt_names) { + auto dsn_matched = false; + auto ip_mached = false; + + auto count = sk_GENERAL_NAME_num(alt_names); + + for (auto i = 0; i < count && !dsn_matched; i++) { + auto val = sk_GENERAL_NAME_value(alt_names, i); + if (val->type == type) { + auto name = (const char *)ASN1_STRING_get0_data(val->d.ia5); + auto name_len = (size_t)ASN1_STRING_length(val->d.ia5); + + if (strlen(name) == name_len) { + switch (type) { + case GEN_DNS: dsn_matched = check_host_name(name, name_len); break; + + case GEN_IPADD: + if (!memcmp(&addr6, name, addr_len) || + !memcmp(&addr, name, addr_len)) { + ip_mached = true; + } + break; + } + } + } + } + + if (dsn_matched || ip_mached) { ret = true; } + } + + GENERAL_NAMES_free((STACK_OF(GENERAL_NAME) *)alt_names); + return ret; +} + +inline bool SSLClient::verify_host_with_common_name(X509 *server_cert) const { + const auto subject_name = X509_get_subject_name(server_cert); + + if (subject_name != nullptr) { + char name[BUFSIZ]; + auto name_len = X509_NAME_get_text_by_NID(subject_name, NID_commonName, + name, sizeof(name)); + + if (name_len != -1) { + return check_host_name(name, static_cast(name_len)); + } + } + + return false; +} + +inline bool SSLClient::check_host_name(const char *pattern, + size_t pattern_len) const { + if (host_.size() == pattern_len && host_ == pattern) { return true; } + + // Wildcard match + // https://bugs.launchpad.net/ubuntu/+source/firefox-3.0/+bug/376484 + std::vector pattern_components; + detail::split(&pattern[0], &pattern[pattern_len], '.', + [&](const char *b, const char *e) { + pattern_components.emplace_back(std::string(b, e)); + }); + + if (host_components_.size() != pattern_components.size()) { return false; } + + auto itr = pattern_components.begin(); + for (const auto &h : host_components_) { + auto &p = *itr; + if (p != h && p != "*") { + auto partial_match = (p.size() > 0 && p[p.size() - 1] == '*' && + !p.compare(0, p.size() - 1, h)); + if (!partial_match) { return false; } + } + ++itr; + } + + return true; +} +#endif + +// ---------------------------------------------------------------------------- + +} // namespace httplib + +#endif // CPPHTTPLIB_HTTPLIB_H diff --git a/sop-sdk/sdk-c++/common/sha256.cpp b/sop-sdk/sdk-c++/common/sha256.cpp new file mode 100644 index 00000000..41678ff4 --- /dev/null +++ b/sop-sdk/sdk-c++/common/sha256.cpp @@ -0,0 +1,270 @@ +/* +* Filename: sha256.cpp +* Author: L.Y. +* Brief: SHA256算法实现 +* Version: V1.0.0 +* Update log: +* 1)20191108-20191113 V1.0.0 +* 1、初次版本。 +* TODO: +* Attention: +* 1)输入信息中有中文时,得到的数字指纹与使用SHA256在线加密工具得到数字指纹可能不相同。 +* 原因是中文的编码方式不同。 +*/ + +#include "sha256.hpp" + +#include +#include +#include +#include + +namespace digest +{ + +//////////////////////////////// 静态数据成员初始化 ////////////////////////////////////////// + + std::vector Sha256::initial_message_digest_ = + { + 0x6a09e667, 0xbb67ae85, 0x3c6ef372, + 0xa54ff53a, 0x510e527f, 0x9b05688c, + 0x1f83d9ab, 0x5be0cd19 + }; + + std::vector Sha256::add_constant_ = + { + 0x428a2f98,0x71374491,0xb5c0fbcf,0xe9b5dba5,0x3956c25b,0x59f111f1,0x923f82a4,0xab1c5ed5, + 0xd807aa98,0x12835b01,0x243185be,0x550c7dc3,0x72be5d74,0x80deb1fe,0x9bdc06a7,0xc19bf174, + 0xe49b69c1,0xefbe4786,0x0fc19dc6,0x240ca1cc,0x2de92c6f,0x4a7484aa,0x5cb0a9dc,0x76f988da, + 0x983e5152,0xa831c66d,0xb00327c8,0xbf597fc7,0xc6e00bf3,0xd5a79147,0x06ca6351,0x14292967, + 0x27b70a85,0x2e1b2138,0x4d2c6dfc,0x53380d13,0x650a7354,0x766a0abb,0x81c2c92e,0x92722c85, + 0xa2bfe8a1,0xa81a664b,0xc24b8b70,0xc76c51a3,0xd192e819,0xd6990624,0xf40e3585,0x106aa070, + 0x19a4c116,0x1e376c08,0x2748774c,0x34b0bcb5,0x391c0cb3,0x4ed8aa4a,0x5b9cca4f,0x682e6ff3, + 0x748f82ee,0x78a5636f,0x84c87814,0x8cc70208,0x90befffa,0xa4506ceb,0xbef9a3f7,0xc67178f2 + }; + + +//////////////////////////////// 成员函数的定义 ////////////////////////////////////////// + + bool Sha256::encrypt(const std::vector& input_message, + std::vector* _digest) + { + if (!input_message.empty() && _digest) + { + //! 文本预处理 + std::vector message = input_message; + preprocessing(&message); + + //! 将文本分解成连续的64Byte大小的数据块 + std::vector> chunks; + breakTextInto64ByteChunks(message, &chunks); + + //! 由64Byte大小的数据块,构造出64个4Byte大小的字。然后进行循环迭代。 + std::vector message_digest(initial_message_digest_); // 初始化信息摘要 + + std::vector words; + for (const auto& chunk : chunks) + { + structureWords(chunk, &words); + transform(words, &message_digest); + } + + //! 获取最终结果 + produceFinalHashValue(message_digest, _digest); + + return true; + } + else + { + return false; + } + } + + std::string Sha256::getHexMessageDigest(const std::string& message) + { + if (!message.empty()) + { + std::vector __message; + for (auto it = message.begin(); it != message.end(); ++it) + { + __message.push_back(static_cast(*it)); + } + + std::vector digest; + encrypt(__message, &digest); + + std::ostringstream o_s; + o_s << std::hex << std::setiosflags(std::ios::uppercase); + for (auto it = digest.begin(); it != digest.end(); ++it) + { + o_s << std::setw(2) << std::setfill('0') + << static_cast(*it); + } + + return o_s.str(); + } + else + { + return ""; + } + } + + bool Sha256::preprocessing(std::vector* _message) const + { + if (_message) + { + const uint64_t original_bit_size = _message->size() * 8; + + //! 附加填充比特 + size_t remainder = _message->size() % 64; + if (remainder < 56) + { + _message->push_back(0x80); // ox80 == 10000000 + for (size_t i = 1; i < 56 - remainder; ++i) + { + _message->push_back(0x00); + } + } + else if (remainder == 56) + { + _message->push_back(0x80); + for (size_t i = 1; i < 64; ++i) + { + _message->push_back(0x00); + } + } + else + { + _message->push_back(0x80); + for (size_t i = 1; i < 64 - remainder + 56; ++i) + { + _message->push_back(0x00); + } + } + + //! 附加原始文本的长度值 + for (int i = 1; i <= 8; ++i) + { + uint8_t c = static_cast(original_bit_size >> (64 - 8 * i)); + _message->push_back(c); + } + + return true; + } + else + { + return false; + } + } + + bool Sha256::breakTextInto64ByteChunks(const std::vector& message, + std::vector>* _chunks) const + { + if (_chunks && 0 == message.size() % 64) + { + _chunks->clear(); // 清空输出buffer + + size_t quotient = message.size() / 64; + for (size_t i = 0; i < quotient; ++i) + { + std::vector temp(message.begin() + i * 64, message.begin() + (i + 1) * 64); + _chunks->push_back(temp); + } + return true; + } + else + { + return false; + } + } + + bool Sha256::structureWords(const std::vector& chunk, + std::vector* _words) const + { + if (_words && 64 == chunk.size()) + { + _words->resize(64); + + auto& words = *_words; + for (int i = 0; i < 16; ++i) + { + words[i] = (static_cast(chunk[i * 4]) << 24) + | (static_cast(chunk[i * 4 + 1]) << 16) + | (static_cast(chunk[i * 4 + 2]) << 8) + | static_cast(chunk[i * 4 + 3]); + } + for (int i = 16; i < 64; ++i) + { + words[i] = small_sigma1(words[i - 2]) + + words[i - 7] + + small_sigma0(words[i - 15]) + + words[i - 16]; + } + return true; + } + else + { + return false; + } + } + + bool Sha256::transform(const std::vector& words, + std::vector* _message_digest) const + { + if (_message_digest && 8 == _message_digest->size() && 64 == words.size()) + { + std::vector d = *_message_digest; + + for (int i = 0; i < 64; ++i) + { + uint32_t temp1 = d[7] + big_sigma1(d[4]) + ch(d[4], d[5], d[6]) + add_constant_[i] + words[i]; + uint32_t temp2 = big_sigma0(d[0]) + maj(d[0], d[1], d[2]); + + d[7] = d[6]; + d[6] = d[5]; + d[5] = d[4]; + d[4] = d[3] + temp1; + d[3] = d[2]; + d[2] = d[1]; + d[1] = d[0]; + d[0] = temp1 + temp2; + } + + for (int i = 0; i < 8; ++i) + { + (*_message_digest)[i] += d[i]; + } + + return true; + } + else + { + return false; + } + } + + bool Sha256::produceFinalHashValue(const std::vector& input, + std::vector* _output) const + { + if (_output) + { + _output->clear(); + + for (auto it = input.begin(); it != input.end(); ++it) + { + for (int i = 0; i < 4; i++) + { + _output->push_back(static_cast((*it) >> (24 - 8 * i))); + } + } + return true; + } + else + { + return false; + } + } + +} // namespace ly + + diff --git a/sop-sdk/sdk-c++/common/sha256.hpp b/sop-sdk/sdk-c++/common/sha256.hpp new file mode 100644 index 00000000..f2a825dd --- /dev/null +++ b/sop-sdk/sdk-c++/common/sha256.hpp @@ -0,0 +1,144 @@ +/* +* Filename: sha256.hpp +* Author: L.Y. +* Brief: SHA256算法实现 +* Version: V1.0.0 +* Update log: +* 1)20191108-20191113 V1.0.0 +* 1、初次版本。 +* TODO: +* Attention: +* 1)输入信息中有中文时,得到的数字指纹与使用SHA256在线加密工具得到数字指纹可能不相同。 +* 原因是中文的编码方式不同。 +*/ + +#ifndef SHA256_HPP +#define SHA256_HPP + +#include + +#include +#include + +namespace digest +{ + +// +// \brief: SHA256算法实现 +// + class Sha256 + { + public: + //! 默认构造函数 + Sha256() {} + + //! 析构函数 + virtual ~Sha256() {} + + /** @brief: 使用SHA256算法,获取输入信息的摘要(数字指纹) + @param[in] message: 输入信息 + @param[out] _digest: 摘要(数字指纹) + @return: 是否成功 + */ + bool encrypt(const std::vector& message, + std::vector* _digest); + + /** @brief: 获取十六进制表示的信息摘要(数字指纹) + @param[in] message: 输入信息 + @return: 十六进制表示的信息摘要(数字指纹) + */ + std::string getHexMessageDigest(const std::string& message); + + protected: + /////// SHA256算法中定义的6种逻辑运算 /////// + inline uint32_t ch(uint32_t x, uint32_t y, uint32_t z) const; + inline uint32_t maj(uint32_t x, uint32_t y, uint32_t z) const; + inline uint32_t big_sigma0(uint32_t x) const; + inline uint32_t big_sigma1(uint32_t x) const; + inline uint32_t small_sigma0(uint32_t x) const; + inline uint32_t small_sigma1(uint32_t x) const; + + /** @brief: SHA256算法对输入信息的预处理,包括“附加填充比特”和“附加长度值” + 附加填充比特: 在报文末尾进行填充,先补第一个比特为1,然后都补0,直到长度满足对512取模后余数是448。需要注意的是,信息必须进行填充。 + 附加长度值: 用一个64位的数据来表示原始消息(填充前的消息)的长度,并将其补到已经进行了填充操作的消息后面。 + @param[in][out] _message: 待处理的信息 + @return: 是否成功 + */ + bool preprocessing(std::vector* _message) const; + + /** @brief: 将信息分解成连续的64Byte大小的数据块 + @param[in] message: 输入信息,长度为64Byte的倍数 + @param[out] _chunks: 输出数据块 + @return: 是否成功 + */ + bool breakTextInto64ByteChunks(const std::vector& message, + std::vector>* _chunks) const; + + /** @brief: 由64Byte大小的数据块,构造出64个4Byte大小的字。 + 构造算法:前16个字直接由数据块分解得到,其余的字由如下迭代公式得到: + W[t] = small_sigma1(W[t-2]) + W[t-7] + small_sigma0(W[t-15]) + W[t-16] + @param[in] chunk: 输入数据块,大小为64Byte + @param[out] _words: 输出字 + @return: 是否成功 + */ + bool structureWords(const std::vector& chunk, + std::vector* _words) const; + + /** @breif: 基于64个4Byte大小的字,进行64次循环加密 + @param[in] words: 64个4Byte大小的字 + @param[in][out] _message_digest: 信息摘要 + @return: 是否成功 + */ + bool transform(const std::vector& words, + std::vector* _message_digest) const; + + /** @brief: 输出最终的哈希值(数字指纹) + @param[in] input: 步长为32bit的哈希值 + @param[out] _output: 步长为8bit的哈希值 + @return: 是否成功 + */ + bool produceFinalHashValue(const std::vector& input, + std::vector* _output) const; + + + private: + static std::vector initial_message_digest_; // 在SHA256算法中的初始信息摘要,这些常量是对自然数中前8个质数的平方根的小数部分取前32bit而来。 + static std::vector add_constant_; // 在SHA256算法中,用到64个常量,这些常量是对自然数中前64个质数的立方根的小数部分取前32bit而来。 + }; + + +///////////////////////////////// 内联函数&模版函数的定义 ///////////////////////////////////////// + + inline uint32_t Sha256::ch(uint32_t x, uint32_t y, uint32_t z) const + { + return (x & y) ^ ((~x) & z); + } + + inline uint32_t Sha256::maj(uint32_t x, uint32_t y, uint32_t z) const + { + return (x & y) ^ (x & z) ^ (y & z); + } + + inline uint32_t Sha256::big_sigma0(uint32_t x) const + { + return (x >> 2 | x << 30) ^ (x >> 13 | x << 19) ^ (x >> 22 | x << 10); + } + + inline uint32_t Sha256::big_sigma1(uint32_t x) const + { + return (x >> 6 | x << 26) ^ (x >> 11 | x << 21) ^ (x >> 25 | x << 7); + } + + inline uint32_t Sha256::small_sigma0(uint32_t x) const + { + return (x >> 7 | x << 25) ^ (x >> 18 | x << 14) ^ (x >> 3); + } + + inline uint32_t Sha256::small_sigma1(uint32_t x) const + { + return (x >> 17 | x << 15) ^ (x >> 19 | x << 13) ^ (x >> 10); + } + +} // namespace ly + +#endif // SHA256_HPP diff --git a/sop-sdk/sdk-c++/common/sign.cpp b/sop-sdk/sdk-c++/common/sign.cpp new file mode 100644 index 00000000..43df6c83 --- /dev/null +++ b/sop-sdk/sdk-c++/common/sign.cpp @@ -0,0 +1,118 @@ +#include + +#include "../thirdparty/base64/base64.h" +#include "sign.h" +#include "RSASign.h" +#include "tool.h" + +namespace signutil { + string createSign(map params, const string& privateKeyFilepath, const string& signType) { + string content = getSignContent(std::move(params)); + return sign(content, privateKeyFilepath, ""); + } + + string sign(const string& content, const string& privateKeyFilepath, const string& hash) { + BIO *bufio = NULL; + RSA *rsa = NULL; + EVP_PKEY *evpKey = NULL; + bool verify = false; + EVP_MD_CTX ctx; + int result = 0; + unsigned int size = 0; + char *sign = NULL; + std::string signStr = ""; + + //bufio = BIO_new_mem_buf((void*)private_key, -1); + //if (bufio == NULL) { + // ERR("BIO_new_mem_buf failed"); + // goto safe_exit; + //} + bufio = BIO_new(BIO_s_file()); + // 读取私钥文件 + BIO_read_filename(bufio, privateKeyFilepath.c_str()); + + // 私钥字符串转换成私钥对象 + rsa = PEM_read_bio_RSAPrivateKey(bufio, NULL, NULL, NULL); + if (rsa == NULL) { + ERR("PEM_read_bio_RSAPrivateKey failed"); + goto safe_exit; + } + + evpKey = EVP_PKEY_new(); + if (evpKey == NULL) { + ERR("EVP_PKEY_new failed"); + goto safe_exit; + } + + if ((result = EVP_PKEY_set1_RSA(evpKey, rsa)) != 1) { + ERR("EVP_PKEY_set1_RSA failed"); + goto safe_exit; + } + + EVP_MD_CTX_init(&ctx); + + // SHA256签名 + if (result == 1 && (result = EVP_SignInit_ex(&ctx, + EVP_sha256(), NULL)) != 1) { + ERR("EVP_SignInit_ex failed"); + } + + if (result == 1 && (result = EVP_SignUpdate(&ctx, + content.c_str(), content.size())) != 1) { + ERR("EVP_SignUpdate failed"); + } + + size = EVP_PKEY_size(evpKey); + sign = (char*)malloc(size+1); + memset(sign, 0, size+1); + + if (result == 1 && (result = EVP_SignFinal(&ctx, + (unsigned char*)sign, + &size, evpKey)) != 1) { + ERR("EVP_SignFinal failed"); + } + + if (result == 1) { + verify = true; + } else { + ERR("verify failed"); + } + + signStr = base64_encode((const unsigned char*)sign, size); + EVP_MD_CTX_cleanup(&ctx); + free(sign); + + safe_exit: + if (rsa != NULL) { + RSA_free(rsa); + rsa = NULL; + } + + if (evpKey != NULL) { + EVP_PKEY_free(evpKey); + evpKey = NULL; + } + + if (bufio != NULL) { + BIO_free_all(bufio); + bufio = NULL; + } + + return signStr; + + } + + string getSignContent(map params){ + map::iterator iter; + string content; + for(iter = params.begin(); iter != params.end(); iter++) { + content.append("&").append(iter->first + "=" + iter->second); + } + return content.substr(1); + } + + void ERR(const string &msg) { + throw msg; + } +} + diff --git a/sop-sdk/sdk-c++/common/sign.h b/sop-sdk/sdk-c++/common/sign.h new file mode 100644 index 00000000..9c21368e --- /dev/null +++ b/sop-sdk/sdk-c++/common/sign.h @@ -0,0 +1,30 @@ + + +#ifndef SDK_CXX_SIGN_H +#define SDK_CXX_SIGN_H + +#include +#include + +#include +#include +#include +#include +#include +#include + +using namespace std; + +namespace signutil { + + string createSign(map params, const string& privateKeyFilepath, const string& signType); + + string sign(const string& content, const string& privateKeyFilepath, const string& hash); + + string getSignContent(map params); + + void ERR(const string &msg); +} + + +#endif //SDK_CXX_SIGN_H diff --git a/sop-sdk/sdk-c++/common/stdafx.cpp b/sop-sdk/sdk-c++/common/stdafx.cpp new file mode 100644 index 00000000..46cd1889 --- /dev/null +++ b/sop-sdk/sdk-c++/common/stdafx.cpp @@ -0,0 +1,2 @@ + +#include "stdafx.h" diff --git a/sop-sdk/sdk-c++/common/stdafx.h b/sop-sdk/sdk-c++/common/stdafx.h new file mode 100644 index 00000000..14d514e8 --- /dev/null +++ b/sop-sdk/sdk-c++/common/stdafx.h @@ -0,0 +1,6 @@ +#pragma once + +#include "targetver.h" + + + diff --git a/sop-sdk/sdk-c++/common/targetver.h b/sop-sdk/sdk-c++/common/targetver.h new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/sop-sdk/sdk-c++/common/targetver.h @@ -0,0 +1 @@ + diff --git a/sop-sdk/sdk-c++/common/tool.cpp b/sop-sdk/sdk-c++/common/tool.cpp new file mode 100644 index 00000000..96f5dc22 --- /dev/null +++ b/sop-sdk/sdk-c++/common/tool.cpp @@ -0,0 +1,174 @@ +#include +#include "tool.h" +#include +#include +#include + +namespace tool { + bool endWith(const string &str, const string &tail) { + return str.compare(str.size() - tail.size(), tail.size(), tail) == 0; + } + + bool startWith(const string &str, const string &head) { + return str.compare(0, head.size(), head) == 0; + } + + string getTime() { + time_t timep; + time(&timep); + char tmp[64]; + strftime(tmp, sizeof(tmp), "%Y-%m-%d %H:%M:%S", localtime(&timep)); + return tmp; + } + + int parse_url(char *url, char **serverstrp, int *portp, char **pathstrp) { + char buf[256]; + int serverlen, numread = 0; + /* go through the url */ + + /* reset url to point PAST the http:// */ + + /* assume it's always 7 chars! */ + string _url = url; + bool isHttps = startWith(_url, "https"); + int len = 7; + if (isHttps) { + len = 8; + } + url = url + len; + /* no http:// now... server is simply up to the next / or : */ + + sscanf(url, "%255[^/:]", buf); + serverlen = strlen(buf); + *serverstrp = (char *) malloc(serverlen + 1); + strcpy(*serverstrp, buf); + if (url[serverlen] == ':') { + /* get the port */ + + sscanf(&url[serverlen + 1], "%d%n", portp, &numread); + /* add one to go PAST it */ + + numread++; + + } else { + if (isHttps) { + *portp = 443; + } else { + *portp = 80; + } + + } + /* the path is a pointer into the rest of url */ + + *pathstrp = &url[serverlen + numread]; + return 0; + } + + std::string url_encode(const std::string &str) { + std::string strTemp = ""; + size_t length = str.length(); + for (size_t i = 0; i < length; i++) { + if (isalnum((unsigned char) str[i]) || + (str[i] == '-') || + (str[i] == '_') || + (str[i] == '.') || + (str[i] == '~')) + strTemp += str[i]; + else if (str[i] == ' ') + strTemp += "+"; + else { + strTemp += '%'; + strTemp += ToHex((unsigned char) str[i] >> 4); + strTemp += ToHex((unsigned char) str[i] % 16); + } + } + return strTemp; + } + + + std::string url_decode(const std::string &str) { + std::string strTemp = ""; + size_t length = str.length(); + for (size_t i = 0; i < length; i++) { + if (str[i] == '+') strTemp += ' '; + else if (str[i] == '%') { + assert(i + 2 < length); + unsigned char high = FromHex((unsigned char) str[++i]); + unsigned char low = FromHex((unsigned char) str[++i]); + strTemp += high * 16 + low; + } else strTemp += str[i]; + } + return strTemp; + } + + + unsigned char ToHex(unsigned char x) { + return x > 9 ? x + 55 : x + 48; + } + + unsigned char FromHex(unsigned char x) { + unsigned char y; + if (x >= 'A' && x <= 'Z') y = x - 'A' + 10; + else if (x >= 'a' && x <= 'z') y = x - 'a' + 10; + else if (x >= '0' && x <= '9') y = x - '0'; + else + assert(0); + return y; + } + + string mapToJson(std::map map_info) { +// Json::Value jObject; +// for (map::const_iterator iter = map_info.begin( ); iter != map_info.end( ); ++iter) +// { +// jObject[iter->first] = iter->second; +// } +// return jObject.toStyledString(); + return "{}"; + } + + string getFilename(string filepath) { + { + if (filepath.empty()) { + return ""; + } + std::string::size_type iPos; +#ifdef Q_OS_WIN + iPos = strFullName.find_last_of('\\') + 1; +#else + iPos = filepath.find_last_of('/') + 1; +#endif + + return filepath.substr(iPos, filepath.length() - iPos); + } + } + + string getFileContent(string filepath) { + ifstream fin(filepath); + stringstream buffer; + buffer << fin.rdbuf(); + string fileContent(buffer.str()); + fin.close(); + return fileContent; + } + + std::string replace(const char *pszSrc, const char *pszOld, const char *pszNew) + { + std::string strContent, strTemp; + strContent.assign( pszSrc ); + std::string::size_type nPos = 0; + while( true ) + { + nPos = strContent.find(pszOld, nPos); + strTemp = strContent.substr(nPos+strlen(pszOld), strContent.length()); + if ( nPos == std::string::npos ) + { + break; + } + strContent.replace(nPos,strContent.length(), pszNew ); + strContent.append(strTemp); + nPos +=strlen(pszNew) - strlen(pszOld)+1; //防止重复替换 避免死循环 + } + return strContent; + } + +} \ No newline at end of file diff --git a/sop-sdk/sdk-c++/common/tool.h b/sop-sdk/sdk-c++/common/tool.h new file mode 100644 index 00000000..1560d496 --- /dev/null +++ b/sop-sdk/sdk-c++/common/tool.h @@ -0,0 +1,52 @@ + + +#ifndef SDK_CXX_TOOL_H +#define SDK_CXX_TOOL_H + +#include +#include + +using namespace std; + +namespace tool { + + bool endWith(const string &str, const string &tail); + + bool startWith(const string &str, const string &head); + + string getTime(); + + int parse_url(char *url, char **serverstrp, int *portp, char **pathstrp); + + std::string url_encode(const std::string &szToEncode); + + std::string url_decode(const std::string &SRC); + + unsigned char ToHex(unsigned char x); + + unsigned char FromHex(unsigned char x); + + string mapToJson(std::map m); + + string getFilename(string filepath); + + string getFileContent(string filepath); + + + /** + * 函数: + * replace(替换字符串) + * 参数: + * pszSrc:源字符串 + * pszOld:需要替换的字符串 + * pszNew:新字符串 + * 返回值: + * 返回替换后的字符串 + * 备注: + * 需要添加#include 头文件 + * ssdwujianhua 2017/08/30 + */ + std::string replace(const char *pszSrc, const char *pszOld, const char *pszNew); +} + +#endif //SDK_CXX_TOOL_H diff --git a/sop-sdk/sdk-c++/main.cpp b/sop-sdk/sdk-c++/main.cpp new file mode 100644 index 00000000..7ff6e706 --- /dev/null +++ b/sop-sdk/sdk-c++/main.cpp @@ -0,0 +1,41 @@ +#include + +#include "common/OpenClient.h" +#include "request/BaseRequest.h" +#include "request/MemberInfoGetRequest.hpp" + +// 应用ID +string appId = "2020051325943082302177280"; +// 存放私钥的文件路径 +string privateKeyFile = "/Users/thc/IdeaProjects/opc/opc-sdk/sdk-c++/privateEx.pem"; +// 请求接口 +string url = "http://localhost:7071/prod/gw68uy85"; + +OpenClient openClient(appId, privateKeyFile, url); + +int main() { + // 创建请求 + MemberInfoGetRequest request; + + // 业务参数 + map bizModel; + bizModel["name"] = "jim"; + bizModel["age"] = "22"; + bizModel["address"] = "xx"; + + request.bizModel = bizModel; + + // 添加上传文件 +// request->setFiles({ +// FileInfo{"aa", "/Users/thc/IdeaProjects/opc/opc-sdk/sdk-c++/aa.txt"}, +// FileInfo{"bb", "/Users/thc/IdeaProjects/opc/opc-sdk/sdk-c++/bb.txt"} +// }); + + // 发送请求 + neb::CJsonObject jsonObj = openClient.execute(&request); + std::cout << jsonObj.ToString() << std::endl; + std::cout << "id:" << jsonObj["id"].ToString() << std::endl; + std::cout << "is_vip:" << jsonObj["member_info"]["is_vip"].ToString() << std::endl; + return 0; +} + diff --git a/sop-sdk/sdk-c++/privateEx.pem b/sop-sdk/sdk-c++/privateEx.pem new file mode 100644 index 00000000..84769da7 --- /dev/null +++ b/sop-sdk/sdk-c++/privateEx.pem @@ -0,0 +1,15 @@ +-----BEGIN RSA PRIVATE KEY----- +MIICXAIBAAKBgQCHJlAPN+1dCbgc3HiahQkT2W/skecGWOCkSX4CPvEc8oIk6544 +xihEwShHnfrapiQdF2fndv5agrhg4FyOHheST42L5MnCk+4Km+mWm5GDvmFS7Sa2 +aZ5o3regY0MUoJ7D74dYjE3UYFuTujAXiXjGpAwa9qOcKotov5LCkSfUeQIDAQAB +AoGAB1cyw8LYRQSHQCUO9Wiaq730jPNHSrJW4EGAIz/XMYjv/fCgx0lnDEX4CbzI +UGoz/bME4R721YRyXoutJ0h14/cGrt/TEn/TMI0xnISzJHr8VSlyBkQEdfO/W3LO +qjs/UYq2Bz4+kJROJHreM+7d5hiIWLzLBlyI8cSU92ySmHECQQDwju2SoRu88kQP +1qr4seZyKQa8DHTVyCoa6LtPLXyJsdgWgY4KyqJHwMUumEC2Zhhu833CR0ZXbfta +uQDmwAVJAkEAj9M225jrPasaD5vPO7thxtEqgV4F/ZyNKH0Z8bDH27KaKkQ+8GMt +kxwKVckZXs2bMvg/6tCiDZkWAxawNrvFsQJBANmTrPWOmpQPW9gnhYRjA9gFm33C +lno2DT9BeQloTtgL7zKMA3lnRdg4VyCJvR48waS4vupVpR228D1iT5pl22ECQF1M +JUzkcM0rPheb+h2EW1QOgWU0Keyvbj4ykO7gv3T78dezN6TWoUzJpsapUiTWeXPh +6AyZ1FW/1bChOiP3QLECQGAbObmsYlN0bjzPYChwWYeYjErXuv51a44GZCNWinFw +GGiHU9ZAqF8RzmBVW4htwj0j/Yry/V1Sp0uoP0zu3uA= +-----END RSA PRIVATE KEY----- diff --git a/sop-sdk/sdk-c++/readme.md b/sop-sdk/sdk-c++/readme.md new file mode 100644 index 00000000..70bf3430 --- /dev/null +++ b/sop-sdk/sdk-c++/readme.md @@ -0,0 +1,3 @@ +# SDK for C++ + +使用方式见:main.cpp \ No newline at end of file diff --git a/sop-sdk/sdk-c++/request/BaseRequest.cpp b/sop-sdk/sdk-c++/request/BaseRequest.cpp new file mode 100644 index 00000000..80de6fdd --- /dev/null +++ b/sop-sdk/sdk-c++/request/BaseRequest.cpp @@ -0,0 +1,17 @@ + +#include "BaseRequest.h" + +#include + + +vector BaseRequest::getFiles() { + return this->files; +} + +void BaseRequest::addFile(const FileInfo& filepath) { + this->files.push_back(filepath); +} + +void BaseRequest::setFiles(vector files) { + this->files = std::move(files); +} diff --git a/sop-sdk/sdk-c++/request/BaseRequest.h b/sop-sdk/sdk-c++/request/BaseRequest.h new file mode 100644 index 00000000..519f9f15 --- /dev/null +++ b/sop-sdk/sdk-c++/request/BaseRequest.h @@ -0,0 +1,60 @@ +#ifndef SDK_CXX_BASEREQUEST_H +#define SDK_CXX_BASEREQUEST_H + +#include +#include +#include +#include "../common/RequestType.h" + +using namespace std; + +struct FileInfo { + /** 表单名称 */ + string name; + /** 文件完整路径 */ + string filepath; +}; + + +/** + * 请求类基类,其它请求类需要继承这个类 + */ +class BaseRequest { +public: + + /** + * 业务参数 + */ + map bizModel; + + /** + * 定义接口名称 + * @return 返回接口名称 + */ + virtual string getMethod() = 0; + + /** + * 定义接口版本号 + * @return 返回版本号 + */ + virtual string getVersion() = 0; + + /** + * 定义请求方式 + * @return 返回请求方式 + */ + virtual RequestType getRequestType() = 0; + + vector getFiles(); + + void setFiles(vector files); + +private: + vector files = {}; + + void addFile(const FileInfo& filepath); +}; + + + +#endif //SDK_CXX_BASEREQUEST_H diff --git a/sop-sdk/sdk-c++/request/MemberInfoGetRequest.hpp b/sop-sdk/sdk-c++/request/MemberInfoGetRequest.hpp new file mode 100644 index 00000000..09062626 --- /dev/null +++ b/sop-sdk/sdk-c++/request/MemberInfoGetRequest.hpp @@ -0,0 +1,29 @@ +#ifndef SDK_CXX_MEMBERINFOGETREQUEST_HPP +#define SDK_CXX_MEMBERINFOGETREQUEST_HPP + +#include +#include "BaseRequest.h" + +using namespace std; + +class MemberInfoGetRequest : public BaseRequest { + +public: + string getMethod() override; + string getVersion() override; + RequestType getRequestType() override; +}; + +string MemberInfoGetRequest::getMethod() { + return "member.info.get"; +} + +string MemberInfoGetRequest::getVersion() { + return "1.0"; +} + +RequestType MemberInfoGetRequest::getRequestType() { + return GET; +} + +#endif //SDK_CXX_MEMBERINFOGETREQUEST_HPP diff --git a/sop-sdk/sdk-c++/thirdparty/CJsonObject/CJsonObject.cpp b/sop-sdk/sdk-c++/thirdparty/CJsonObject/CJsonObject.cpp new file mode 100644 index 00000000..c43cce9a --- /dev/null +++ b/sop-sdk/sdk-c++/thirdparty/CJsonObject/CJsonObject.cpp @@ -0,0 +1,3523 @@ +/******************************************************************************* + * Project: neb + * @file CJsonObject.cpp + * @brief + * @author bwarliao + * @date: 2014-7-16 + * @note + * Modify history: + ******************************************************************************/ + +#include "CJsonObject.hpp" + +#ifdef _WIN32 +#define snprintf _snprintf_s +#endif + +namespace neb +{ + +CJsonObject::CJsonObject() + : m_pJsonData(NULL), m_pExternJsonDataRef(NULL), m_pKeyTravers(NULL) +{ + // m_pJsonData = cJSON_CreateObject(); +} + +CJsonObject::CJsonObject(const std::string& strJson) + : m_pJsonData(NULL), m_pExternJsonDataRef(NULL), m_pKeyTravers(NULL) +{ + Parse(strJson); +} + +CJsonObject::CJsonObject(const CJsonObject* pJsonObject) + : m_pJsonData(NULL), m_pExternJsonDataRef(NULL), m_pKeyTravers(NULL) +{ + if (pJsonObject) + { + Parse(pJsonObject->ToString()); + } +} + +CJsonObject::CJsonObject(const CJsonObject& oJsonObject) + : m_pJsonData(NULL), m_pExternJsonDataRef(NULL), m_pKeyTravers(NULL) +{ + Parse(oJsonObject.ToString()); +} + +CJsonObject::~CJsonObject() +{ + Clear(); +} + +CJsonObject& CJsonObject::operator=(const CJsonObject& oJsonObject) +{ + Parse(oJsonObject.ToString().c_str()); + return(*this); +} + +bool CJsonObject::operator==(const CJsonObject& oJsonObject) const +{ + return(this->ToString() == oJsonObject.ToString()); +} + +bool CJsonObject::AddEmptySubObject(const std::string& strKey) +{ + cJSON* pFocusData = NULL; + if (m_pJsonData != NULL) + { + pFocusData = m_pJsonData; + } + else if (m_pExternJsonDataRef != NULL) + { + pFocusData = m_pExternJsonDataRef; + } + else + { + m_pJsonData = cJSON_CreateObject(); + m_pKeyTravers = m_pJsonData; + pFocusData = m_pJsonData; + } + + if (pFocusData == NULL) + { + m_strErrMsg = "json data is null!"; + return(false); + } + if (pFocusData->type != cJSON_Object) + { + m_strErrMsg = "not a json object! json array?"; + return(false); + } + if (cJSON_GetObjectItem(pFocusData, strKey.c_str()) != NULL) + { + m_strErrMsg = "key exists!"; + return(false); + } + cJSON* pJsonStruct = cJSON_CreateObject(); + if (pJsonStruct == NULL) + { + m_strErrMsg = std::string("create sub empty object error!"); + return(false); + } + cJSON_AddItemToObject(pFocusData, strKey.c_str(), pJsonStruct); + m_pKeyTravers = pFocusData; + return(true); +} + +bool CJsonObject::AddEmptySubArray(const std::string& strKey) +{ + cJSON* pFocusData = NULL; + if (m_pJsonData != NULL) + { + pFocusData = m_pJsonData; + } + else if (m_pExternJsonDataRef != NULL) + { + pFocusData = m_pExternJsonDataRef; + } + else + { + m_pJsonData = cJSON_CreateObject(); + m_pKeyTravers = m_pJsonData; + pFocusData = m_pJsonData; + } + + if (pFocusData == NULL) + { + m_strErrMsg = "json data is null!"; + return(false); + } + if (pFocusData->type != cJSON_Object) + { + m_strErrMsg = "not a json object! json array?"; + return(false); + } + if (cJSON_GetObjectItem(pFocusData, strKey.c_str()) != NULL) + { + m_strErrMsg = "key exists!"; + return(false); + } + cJSON* pJsonStruct = cJSON_CreateArray(); + if (pJsonStruct == NULL) + { + m_strErrMsg = std::string("create sub empty array error!"); + return(false); + } + cJSON_AddItemToObject(pFocusData, strKey.c_str(), pJsonStruct); + m_pKeyTravers = pFocusData; + return(true); +} + +bool CJsonObject::GetKey(std::string& strKey) +{ + if (IsArray()) + { + return(false); + } + if (m_pKeyTravers == NULL) + { + if (m_pJsonData != NULL) + { + m_pKeyTravers = m_pJsonData; + } + else if (m_pExternJsonDataRef != NULL) + { + m_pKeyTravers = m_pExternJsonDataRef; + } + return(false); + } + else if (m_pKeyTravers == m_pJsonData || m_pKeyTravers == m_pExternJsonDataRef) + { + cJSON *c = m_pKeyTravers->child; + if (c) + { + strKey = c->string; + m_pKeyTravers = c->next; + return(true); + } + else + { + return(false); + } + } + else + { + strKey = m_pKeyTravers->string; + m_pKeyTravers = m_pKeyTravers->next; + return(true); + } +} + +void CJsonObject::ResetTraversing() +{ + if (m_pJsonData != NULL) + { + m_pKeyTravers = m_pJsonData; + } + else + { + m_pKeyTravers = m_pExternJsonDataRef; + } +} + +CJsonObject& CJsonObject::operator[](const std::string& strKey) +{ + std::map::iterator iter; + iter = m_mapJsonObjectRef.find(strKey); + if (iter == m_mapJsonObjectRef.end()) + { + cJSON* pJsonStruct = NULL; + if (m_pJsonData != NULL) + { + if (m_pJsonData->type == cJSON_Object) + { + pJsonStruct = cJSON_GetObjectItem(m_pJsonData, strKey.c_str()); + } + } + else if (m_pExternJsonDataRef != NULL) + { + if (m_pExternJsonDataRef->type == cJSON_Object) + { + pJsonStruct = cJSON_GetObjectItem(m_pExternJsonDataRef, strKey.c_str()); + } + } + if (pJsonStruct == NULL) + { + CJsonObject* pJsonObject = new CJsonObject(); + m_mapJsonObjectRef.insert(std::pair(strKey, pJsonObject)); + return(*pJsonObject); + } + else + { + CJsonObject* pJsonObject = new CJsonObject(pJsonStruct); + m_mapJsonObjectRef.insert(std::pair(strKey, pJsonObject)); + return(*pJsonObject); + } + } + else + { + return(*(iter->second)); + } +} + +CJsonObject& CJsonObject::operator[](unsigned int uiWhich) +{ + std::map::iterator iter; + iter = m_mapJsonArrayRef.find(uiWhich); + if (iter == m_mapJsonArrayRef.end()) + { + cJSON* pJsonStruct = NULL; + if (m_pJsonData != NULL) + { + if (m_pJsonData->type == cJSON_Array) + { + pJsonStruct = cJSON_GetArrayItem(m_pJsonData, uiWhich); + } + } + else if (m_pExternJsonDataRef != NULL) + { + if (m_pExternJsonDataRef->type == cJSON_Array) + { + pJsonStruct = cJSON_GetArrayItem(m_pExternJsonDataRef, uiWhich); + } + } + if (pJsonStruct == NULL) + { + CJsonObject* pJsonObject = new CJsonObject(); + m_mapJsonArrayRef.insert(std::pair(uiWhich, pJsonObject)); + return(*pJsonObject); + } + else + { + CJsonObject* pJsonObject = new CJsonObject(pJsonStruct); + m_mapJsonArrayRef.insert(std::pair(uiWhich, pJsonObject)); + return(*pJsonObject); + } + } + else + { + return(*(iter->second)); + } +} + +std::string CJsonObject::operator()(const std::string& strKey) const +{ + cJSON* pJsonStruct = NULL; + if (m_pJsonData != NULL) + { + if (m_pJsonData->type == cJSON_Object) + { + pJsonStruct = cJSON_GetObjectItem(m_pJsonData, strKey.c_str()); + } + } + else if (m_pExternJsonDataRef != NULL) + { + if(m_pExternJsonDataRef->type == cJSON_Object) + { + pJsonStruct = cJSON_GetObjectItem(m_pExternJsonDataRef, strKey.c_str()); + } + } + if (pJsonStruct == NULL) + { + return(std::string("")); + } + if (pJsonStruct->type == cJSON_String) + { + return(pJsonStruct->valuestring); + } + else if (pJsonStruct->type == cJSON_Int) + { + char szNumber[128] = {0}; + if (pJsonStruct->sign == -1) + { + if (pJsonStruct->valueint <= (int64)INT_MAX && (int64)pJsonStruct->valueint >= (int64)INT_MIN) + { + snprintf(szNumber, sizeof(szNumber), "%d", (int32)pJsonStruct->valueint); + } + else + { + snprintf(szNumber, sizeof(szNumber), "%lld", (int64)pJsonStruct->valueint); + } + } + else + { + if ((uint64)pJsonStruct->valueint <= (uint64)UINT_MAX) + { + snprintf(szNumber, sizeof(szNumber), "%u", (uint32)pJsonStruct->valueint); + } + else + { + snprintf(szNumber, sizeof(szNumber), "%llu", pJsonStruct->valueint); + } + } + return(std::string(szNumber)); + } + else if (pJsonStruct->type == cJSON_Double) + { + char szNumber[128] = {0}; + if (fabs(pJsonStruct->valuedouble) < 1.0e-6 || fabs(pJsonStruct->valuedouble) > 1.0e9) + { + snprintf(szNumber, sizeof(szNumber), "%e", pJsonStruct->valuedouble); + } + else + { + snprintf(szNumber, sizeof(szNumber), "%f", pJsonStruct->valuedouble); + } + } + else if (pJsonStruct->type == cJSON_False) + { + return(std::string("false")); + } + else if (pJsonStruct->type == cJSON_True) + { + return(std::string("true")); + } + return(std::string("")); +} + +std::string CJsonObject::operator()(unsigned int uiWhich) const +{ + cJSON* pJsonStruct = NULL; + if (m_pJsonData != NULL) + { + if (m_pJsonData->type == cJSON_Array) + { + pJsonStruct = cJSON_GetArrayItem(m_pJsonData, uiWhich); + } + } + else if (m_pExternJsonDataRef != NULL) + { + if(m_pExternJsonDataRef->type == cJSON_Array) + { + pJsonStruct = cJSON_GetArrayItem(m_pExternJsonDataRef, uiWhich); + } + } + if (pJsonStruct == NULL) + { + return(std::string("")); + } + if (pJsonStruct->type == cJSON_String) + { + return(pJsonStruct->valuestring); + } + else if (pJsonStruct->type == cJSON_Int) + { + char szNumber[128] = {0}; + if (pJsonStruct->sign == -1) + { + if (pJsonStruct->valueint <= (int64)INT_MAX && (int64)pJsonStruct->valueint >= (int64)INT_MIN) + { + snprintf(szNumber, sizeof(szNumber), "%d", (int32)pJsonStruct->valueint); + } + else + { + snprintf(szNumber, sizeof(szNumber), "%lld", (int64)pJsonStruct->valueint); + } + } + else + { + if ((uint64)pJsonStruct->valueint <= (uint64)UINT_MAX) + { + snprintf(szNumber, sizeof(szNumber), "%u", (uint32)pJsonStruct->valueint); + } + else + { + snprintf(szNumber, sizeof(szNumber), "%llu", pJsonStruct->valueint); + } + } + return(std::string(szNumber)); + } + else if (pJsonStruct->type == cJSON_Double) + { + char szNumber[128] = {0}; + if (fabs(pJsonStruct->valuedouble) < 1.0e-6 || fabs(pJsonStruct->valuedouble) > 1.0e9) + { + snprintf(szNumber, sizeof(szNumber), "%e", pJsonStruct->valuedouble); + } + else + { + snprintf(szNumber, sizeof(szNumber), "%f", pJsonStruct->valuedouble); + } + } + else if (pJsonStruct->type == cJSON_False) + { + return(std::string("false")); + } + else if (pJsonStruct->type == cJSON_True) + { + return(std::string("true")); + } + return(std::string("")); +} + +bool CJsonObject::Parse(const std::string& strJson) +{ + Clear(); + m_pJsonData = cJSON_Parse(strJson.c_str()); + m_pKeyTravers = m_pJsonData; + if (m_pJsonData == NULL) + { + m_strErrMsg = std::string("prase json string error at ") + cJSON_GetErrorPtr(); + return(false); + } + return(true); +} + +void CJsonObject::Clear() +{ + m_pExternJsonDataRef = NULL; + m_pKeyTravers = NULL; + if (m_pJsonData != NULL) + { + cJSON_Delete(m_pJsonData); + m_pJsonData = NULL; + } + for (std::map::iterator iter = m_mapJsonArrayRef.begin(); + iter != m_mapJsonArrayRef.end(); ++iter) + { + if (iter->second != NULL) + { + delete (iter->second); + iter->second = NULL; + } + } + m_mapJsonArrayRef.clear(); + for (std::map::iterator iter = m_mapJsonObjectRef.begin(); + iter != m_mapJsonObjectRef.end(); ++iter) + { + if (iter->second != NULL) + { + delete (iter->second); + iter->second = NULL; + } + } + m_mapJsonObjectRef.clear(); +} + +bool CJsonObject::IsEmpty() const +{ + if (m_pJsonData != NULL) + { + return(false); + } + else if (m_pExternJsonDataRef != NULL) + { + return(false); + } + return(true); +} + +bool CJsonObject::IsArray() const +{ + cJSON* pFocusData = NULL; + if (m_pJsonData != NULL) + { + pFocusData = m_pJsonData; + } + else if (m_pExternJsonDataRef != NULL) + { + pFocusData = m_pExternJsonDataRef; + } + + if (pFocusData == NULL) + { + return(false); + } + + if (pFocusData->type == cJSON_Array) + { + return(true); + } + else + { + return(false); + } +} + +std::string CJsonObject::ToString() const +{ + char* pJsonString = NULL; + std::string strJsonData = ""; + if (m_pJsonData != NULL) + { + pJsonString = cJSON_PrintUnformatted(m_pJsonData); + } + else if (m_pExternJsonDataRef != NULL) + { + pJsonString = cJSON_PrintUnformatted(m_pExternJsonDataRef); + } + if (pJsonString != NULL) + { + strJsonData = pJsonString; + free(pJsonString); + } + return(strJsonData); +} + +std::string CJsonObject::ToFormattedString() const +{ + char* pJsonString = NULL; + std::string strJsonData = ""; + if (m_pJsonData != NULL) + { + pJsonString = cJSON_Print(m_pJsonData); + } + else if (m_pExternJsonDataRef != NULL) + { + pJsonString = cJSON_Print(m_pExternJsonDataRef); + } + if (pJsonString != NULL) + { + strJsonData = pJsonString; + free(pJsonString); + } + return(strJsonData); +} + + +bool CJsonObject::Get(const std::string& strKey, CJsonObject& oJsonObject) const +{ + cJSON* pJsonStruct = NULL; + if (m_pJsonData != NULL) + { + if (m_pJsonData->type == cJSON_Object) + { + pJsonStruct = cJSON_GetObjectItem(m_pJsonData, strKey.c_str()); + } + } + else if (m_pExternJsonDataRef != NULL) + { + if(m_pExternJsonDataRef->type == cJSON_Object) + { + pJsonStruct = cJSON_GetObjectItem(m_pExternJsonDataRef, strKey.c_str()); + } + } + if (pJsonStruct == NULL) + { + return(false); + } + char* pJsonString = cJSON_Print(pJsonStruct); + std::string strJsonData = pJsonString; + free(pJsonString); + if (oJsonObject.Parse(strJsonData)) + { + return(true); + } + else + { + return(false); + } +} + +bool CJsonObject::Get(const std::string& strKey, std::string& strValue) const +{ + cJSON* pJsonStruct = NULL; + if (m_pJsonData != NULL) + { + if (m_pJsonData->type == cJSON_Object) + { + pJsonStruct = cJSON_GetObjectItem(m_pJsonData, strKey.c_str()); + } + } + else if (m_pExternJsonDataRef != NULL) + { + if(m_pExternJsonDataRef->type == cJSON_Object) + { + pJsonStruct = cJSON_GetObjectItem(m_pExternJsonDataRef, strKey.c_str()); + } + } + if (pJsonStruct == NULL) + { + return(false); + } + if (pJsonStruct->type != cJSON_String) + { + return(false); + } + strValue = pJsonStruct->valuestring; + return(true); +} + +bool CJsonObject::Get(const std::string& strKey, int32& iValue) const +{ + cJSON* pJsonStruct = NULL; + if (m_pJsonData != NULL) + { + if (m_pJsonData->type == cJSON_Object) + { + pJsonStruct = cJSON_GetObjectItem(m_pJsonData, strKey.c_str()); + } + } + else if (m_pExternJsonDataRef != NULL) + { + if(m_pExternJsonDataRef->type == cJSON_Object) + { + pJsonStruct = cJSON_GetObjectItem(m_pExternJsonDataRef, strKey.c_str()); + } + } + if (pJsonStruct == NULL) + { + return(false); + } + if (pJsonStruct->type == cJSON_Int) + { + iValue = (int32)(pJsonStruct->valueint); + return(true); + } + else if (pJsonStruct->type == cJSON_Double) + { + iValue = (int32)(pJsonStruct->valuedouble); + return(true); + } + return(false); +} + +bool CJsonObject::Get(const std::string& strKey, uint32& uiValue) const +{ + cJSON* pJsonStruct = NULL; + if (m_pJsonData != NULL) + { + if (m_pJsonData->type == cJSON_Object) + { + pJsonStruct = cJSON_GetObjectItem(m_pJsonData, strKey.c_str()); + } + } + else if (m_pExternJsonDataRef != NULL) + { + if(m_pExternJsonDataRef->type == cJSON_Object) + { + pJsonStruct = cJSON_GetObjectItem(m_pExternJsonDataRef, strKey.c_str()); + } + } + if (pJsonStruct == NULL) + { + return(false); + } + if (pJsonStruct->type == cJSON_Int) + { + uiValue = (uint32)(pJsonStruct->valueint); + return(true); + } + else if (pJsonStruct->type == cJSON_Double) + { + uiValue = (uint32)(pJsonStruct->valuedouble); + return(true); + } + return(false); +} + +bool CJsonObject::Get(const std::string& strKey, int64& llValue) const +{ + cJSON* pJsonStruct = NULL; + if (m_pJsonData != NULL) + { + if (m_pJsonData->type == cJSON_Object) + { + pJsonStruct = cJSON_GetObjectItem(m_pJsonData, strKey.c_str()); + } + } + else if (m_pExternJsonDataRef != NULL) + { + if(m_pExternJsonDataRef->type == cJSON_Object) + { + pJsonStruct = cJSON_GetObjectItem(m_pExternJsonDataRef, strKey.c_str()); + } + } + if (pJsonStruct == NULL) + { + return(false); + } + if (pJsonStruct->type == cJSON_Int) + { + llValue = (int64)(pJsonStruct->valueint); + return(true); + } + else if (pJsonStruct->type == cJSON_Double) + { + llValue = (int64)(pJsonStruct->valuedouble); + return(true); + } + return(false); +} + +bool CJsonObject::Get(const std::string& strKey, uint64& ullValue) const +{ + cJSON* pJsonStruct = NULL; + if (m_pJsonData != NULL) + { + if (m_pJsonData->type == cJSON_Object) + { + pJsonStruct = cJSON_GetObjectItem(m_pJsonData, strKey.c_str()); + } + } + else if (m_pExternJsonDataRef != NULL) + { + if(m_pExternJsonDataRef->type == cJSON_Object) + { + pJsonStruct = cJSON_GetObjectItem(m_pExternJsonDataRef, strKey.c_str()); + } + } + if (pJsonStruct == NULL) + { + return(false); + } + if (pJsonStruct->type == cJSON_Int) + { + ullValue = (uint64)(pJsonStruct->valueint); + return(true); + } + else if (pJsonStruct->type == cJSON_Double) + { + ullValue = (uint64)(pJsonStruct->valuedouble); + return(true); + } + return(false); +} + +bool CJsonObject::Get(const std::string& strKey, bool& bValue) const +{ + cJSON* pJsonStruct = NULL; + if (m_pJsonData != NULL) + { + if (m_pJsonData->type == cJSON_Object) + { + pJsonStruct = cJSON_GetObjectItem(m_pJsonData, strKey.c_str()); + } + } + else if (m_pExternJsonDataRef != NULL) + { + if(m_pExternJsonDataRef->type == cJSON_Object) + { + pJsonStruct = cJSON_GetObjectItem(m_pExternJsonDataRef, strKey.c_str()); + } + } + if (pJsonStruct == NULL) + { + return(false); + } + if (pJsonStruct->type > cJSON_True) + { + return(false); + } + bValue = pJsonStruct->type; + return(true); +} + +bool CJsonObject::Get(const std::string& strKey, float& fValue) const +{ + cJSON* pJsonStruct = NULL; + if (m_pJsonData != NULL) + { + if (m_pJsonData->type == cJSON_Object) + { + pJsonStruct = cJSON_GetObjectItem(m_pJsonData, strKey.c_str()); + } + } + else if (m_pExternJsonDataRef != NULL) + { + if(m_pExternJsonDataRef->type == cJSON_Object) + { + pJsonStruct = cJSON_GetObjectItem(m_pExternJsonDataRef, strKey.c_str()); + } + } + if (pJsonStruct == NULL) + { + return(false); + } + if (pJsonStruct->type == cJSON_Double || pJsonStruct->type == cJSON_Int) + { + fValue = (float)(pJsonStruct->valuedouble); + return(true); + } + return(false); +} + +bool CJsonObject::Get(const std::string& strKey, double& dValue) const +{ + cJSON* pJsonStruct = NULL; + if (m_pJsonData != NULL) + { + if (m_pJsonData->type == cJSON_Object) + { + pJsonStruct = cJSON_GetObjectItem(m_pJsonData, strKey.c_str()); + } + } + else if (m_pExternJsonDataRef != NULL) + { + if(m_pExternJsonDataRef->type == cJSON_Object) + { + pJsonStruct = cJSON_GetObjectItem(m_pExternJsonDataRef, strKey.c_str()); + } + } + if (pJsonStruct == NULL) + { + return(false); + } + if (pJsonStruct->type == cJSON_Double || pJsonStruct->type == cJSON_Int) + { + dValue = pJsonStruct->valuedouble; + return(true); + } + return(false); +} + +bool CJsonObject::IsNull(const std::string& strKey) const +{ + cJSON* pJsonStruct = NULL; + if (m_pJsonData != NULL) + { + if (m_pJsonData->type == cJSON_Object) + { + pJsonStruct = cJSON_GetObjectItem(m_pJsonData, strKey.c_str()); + } + } + else if (m_pExternJsonDataRef != NULL) + { + if(m_pExternJsonDataRef->type == cJSON_Object) + { + pJsonStruct = cJSON_GetObjectItem(m_pExternJsonDataRef, strKey.c_str()); + } + } + if (pJsonStruct == NULL) + { + return(false); + } + if (pJsonStruct->type != cJSON_NULL) + { + return(false); + } + return(true); +} + +bool CJsonObject::Add(const std::string& strKey, const CJsonObject& oJsonObject) +{ + cJSON* pFocusData = NULL; + if (m_pJsonData != NULL) + { + pFocusData = m_pJsonData; + } + else if (m_pExternJsonDataRef != NULL) + { + pFocusData = m_pExternJsonDataRef; + } + else + { + m_pJsonData = cJSON_CreateObject(); + m_pKeyTravers = m_pJsonData; + pFocusData = m_pJsonData; + } + + if (pFocusData == NULL) + { + m_strErrMsg = "json data is null!"; + return(false); + } + if (pFocusData->type != cJSON_Object) + { + m_strErrMsg = "not a json object! json array?"; + return(false); + } + if (cJSON_GetObjectItem(pFocusData, strKey.c_str()) != NULL) + { + m_strErrMsg = "key exists!"; + return(false); + } + cJSON* pJsonStruct = cJSON_Parse(oJsonObject.ToString().c_str()); + if (pJsonStruct == NULL) + { + m_strErrMsg = std::string("prase json string error at ") + cJSON_GetErrorPtr(); + return(false); + } + cJSON_AddItemToObject(pFocusData, strKey.c_str(), pJsonStruct); + if (cJSON_GetObjectItem(pFocusData, strKey.c_str()) == NULL) + { + return(false); + } + std::map::iterator iter = m_mapJsonObjectRef.find(strKey); + if (iter != m_mapJsonObjectRef.end()) + { + if (iter->second != NULL) + { + delete (iter->second); + iter->second = NULL; + } + m_mapJsonObjectRef.erase(iter); + } + m_pKeyTravers = pFocusData; + return(true); +} + +bool CJsonObject::Add(const std::string& strKey, const std::string& strValue) +{ + cJSON* pFocusData = NULL; + if (m_pJsonData != NULL) + { + pFocusData = m_pJsonData; + } + else if (m_pExternJsonDataRef != NULL) + { + pFocusData = m_pExternJsonDataRef; + } + else + { + m_pJsonData = cJSON_CreateObject(); + m_pKeyTravers = m_pJsonData; + pFocusData = m_pJsonData; + } + + if (pFocusData == NULL) + { + m_strErrMsg = "json data is null!"; + return(false); + } + if (pFocusData->type != cJSON_Object) + { + m_strErrMsg = "not a json object! json array?"; + return(false); + } + if (cJSON_GetObjectItem(pFocusData, strKey.c_str()) != NULL) + { + m_strErrMsg = "key exists!"; + return(false); + } + cJSON* pJsonStruct = cJSON_CreateString(strValue.c_str()); + if (pJsonStruct == NULL) + { + return(false); + } + cJSON_AddItemToObject(pFocusData, strKey.c_str(), pJsonStruct); + if (cJSON_GetObjectItem(pFocusData, strKey.c_str()) == NULL) + { + return(false); + } + m_pKeyTravers = pFocusData; + return(true); +} + +bool CJsonObject::Add(const std::string& strKey, int32 iValue) +{ + cJSON* pFocusData = NULL; + if (m_pJsonData != NULL) + { + pFocusData = m_pJsonData; + } + else if (m_pExternJsonDataRef != NULL) + { + pFocusData = m_pExternJsonDataRef; + } + else + { + m_pJsonData = cJSON_CreateObject(); + m_pKeyTravers = m_pJsonData; + pFocusData = m_pJsonData; + } + + if (pFocusData == NULL) + { + m_strErrMsg = "json data is null!"; + return(false); + } + if (pFocusData->type != cJSON_Object) + { + m_strErrMsg = "not a json object! json array?"; + return(false); + } + if (cJSON_GetObjectItem(pFocusData, strKey.c_str()) != NULL) + { + m_strErrMsg = "key exists!"; + return(false); + } + cJSON* pJsonStruct = cJSON_CreateInt((uint64)iValue, -1); + if (pJsonStruct == NULL) + { + return(false); + } + cJSON_AddItemToObject(pFocusData, strKey.c_str(), pJsonStruct); + if (cJSON_GetObjectItem(pFocusData, strKey.c_str()) == NULL) + { + return(false); + } + m_pKeyTravers = pFocusData; + return(true); +} + +bool CJsonObject::Add(const std::string& strKey, uint32 uiValue) +{ + cJSON* pFocusData = NULL; + if (m_pJsonData != NULL) + { + pFocusData = m_pJsonData; + } + else if (m_pExternJsonDataRef != NULL) + { + pFocusData = m_pExternJsonDataRef; + } + else + { + m_pJsonData = cJSON_CreateObject(); + m_pKeyTravers = m_pJsonData; + pFocusData = m_pJsonData; + } + + if (pFocusData == NULL) + { + m_strErrMsg = "json data is null!"; + return(false); + } + if (pFocusData->type != cJSON_Object) + { + m_strErrMsg = "not a json object! json array?"; + return(false); + } + if (cJSON_GetObjectItem(pFocusData, strKey.c_str()) != NULL) + { + m_strErrMsg = "key exists!"; + return(false); + } + cJSON* pJsonStruct = cJSON_CreateInt((uint64)uiValue, 1); + if (pJsonStruct == NULL) + { + return(false); + } + cJSON_AddItemToObject(pFocusData, strKey.c_str(), pJsonStruct); + if (cJSON_GetObjectItem(pFocusData, strKey.c_str()) == NULL) + { + return(false); + } + m_pKeyTravers = pFocusData; + return(true); +} + +bool CJsonObject::Add(const std::string& strKey, int64 llValue) +{ + cJSON* pFocusData = NULL; + if (m_pJsonData != NULL) + { + pFocusData = m_pJsonData; + } + else if (m_pExternJsonDataRef != NULL) + { + pFocusData = m_pExternJsonDataRef; + } + else + { + m_pJsonData = cJSON_CreateObject(); + m_pKeyTravers = m_pJsonData; + pFocusData = m_pJsonData; + } + + if (pFocusData == NULL) + { + m_strErrMsg = "json data is null!"; + return(false); + } + if (pFocusData->type != cJSON_Object) + { + m_strErrMsg = "not a json object! json array?"; + return(false); + } + if (cJSON_GetObjectItem(pFocusData, strKey.c_str()) != NULL) + { + m_strErrMsg = "key exists!"; + return(false); + } + cJSON* pJsonStruct = cJSON_CreateInt((uint64)llValue, -1); + if (pJsonStruct == NULL) + { + return(false); + } + cJSON_AddItemToObject(pFocusData, strKey.c_str(), pJsonStruct); + if (cJSON_GetObjectItem(pFocusData, strKey.c_str()) == NULL) + { + return(false); + } + m_pKeyTravers = pFocusData; + return(true); +} + +bool CJsonObject::Add(const std::string& strKey, uint64 ullValue) +{ + cJSON* pFocusData = NULL; + if (m_pJsonData != NULL) + { + pFocusData = m_pJsonData; + } + else if (m_pExternJsonDataRef != NULL) + { + pFocusData = m_pExternJsonDataRef; + } + else + { + m_pJsonData = cJSON_CreateObject(); + m_pKeyTravers = m_pJsonData; + pFocusData = m_pJsonData; + } + + if (pFocusData == NULL) + { + m_strErrMsg = "json data is null!"; + return(false); + } + if (pFocusData->type != cJSON_Object) + { + m_strErrMsg = "not a json object! json array?"; + return(false); + } + if (cJSON_GetObjectItem(pFocusData, strKey.c_str()) != NULL) + { + m_strErrMsg = "key exists!"; + return(false); + } + cJSON* pJsonStruct = cJSON_CreateInt(ullValue, 1); + if (pJsonStruct == NULL) + { + return(false); + } + cJSON_AddItemToObject(pFocusData, strKey.c_str(), pJsonStruct); + if (cJSON_GetObjectItem(pFocusData, strKey.c_str()) == NULL) + { + return(false); + } + m_pKeyTravers = pFocusData; + return(true); +} + +bool CJsonObject::Add(const std::string& strKey, bool bValue, bool bValueAgain) +{ + cJSON* pFocusData = NULL; + if (m_pJsonData != NULL) + { + pFocusData = m_pJsonData; + } + else if (m_pExternJsonDataRef != NULL) + { + pFocusData = m_pExternJsonDataRef; + } + else + { + m_pJsonData = cJSON_CreateObject(); + m_pKeyTravers = m_pJsonData; + pFocusData = m_pJsonData; + } + + if (pFocusData == NULL) + { + m_strErrMsg = "json data is null!"; + return(false); + } + if (pFocusData->type != cJSON_Object) + { + m_strErrMsg = "not a json object! json array?"; + return(false); + } + if (cJSON_GetObjectItem(pFocusData, strKey.c_str()) != NULL) + { + m_strErrMsg = "key exists!"; + return(false); + } + cJSON* pJsonStruct = cJSON_CreateBool(bValue); + if (pJsonStruct == NULL) + { + return(false); + } + cJSON_AddItemToObject(pFocusData, strKey.c_str(), pJsonStruct); + if (cJSON_GetObjectItem(pFocusData, strKey.c_str()) == NULL) + { + return(false); + } + m_pKeyTravers = pFocusData; + return(true); +} + +bool CJsonObject::Add(const std::string& strKey, float fValue) +{ + cJSON* pFocusData = NULL; + if (m_pJsonData != NULL) + { + pFocusData = m_pJsonData; + } + else if (m_pExternJsonDataRef != NULL) + { + pFocusData = m_pExternJsonDataRef; + } + else + { + m_pJsonData = cJSON_CreateObject(); + m_pKeyTravers = m_pJsonData; + pFocusData = m_pJsonData; + } + + if (pFocusData == NULL) + { + m_strErrMsg = "json data is null!"; + return(false); + } + if (pFocusData->type != cJSON_Object) + { + m_strErrMsg = "not a json object! json array?"; + return(false); + } + if (cJSON_GetObjectItem(pFocusData, strKey.c_str()) != NULL) + { + m_strErrMsg = "key exists!"; + return(false); + } + cJSON* pJsonStruct = cJSON_CreateDouble((double)fValue, -1); + if (pJsonStruct == NULL) + { + return(false); + } + cJSON_AddItemToObject(pFocusData, strKey.c_str(), pJsonStruct); + if (cJSON_GetObjectItem(pFocusData, strKey.c_str()) == NULL) + { + return(false); + } + m_pKeyTravers = pFocusData; + return(true); +} + +bool CJsonObject::Add(const std::string& strKey, double dValue) +{ + cJSON* pFocusData = NULL; + if (m_pJsonData != NULL) + { + pFocusData = m_pJsonData; + } + else if (m_pExternJsonDataRef != NULL) + { + pFocusData = m_pExternJsonDataRef; + } + else + { + m_pJsonData = cJSON_CreateObject(); + m_pKeyTravers = m_pJsonData; + pFocusData = m_pJsonData; + } + + if (pFocusData == NULL) + { + m_strErrMsg = "json data is null!"; + return(false); + } + if (pFocusData->type != cJSON_Object) + { + m_strErrMsg = "not a json object! json array?"; + return(false); + } + if (cJSON_GetObjectItem(pFocusData, strKey.c_str()) != NULL) + { + m_strErrMsg = "key exists!"; + return(false); + } + cJSON* pJsonStruct = cJSON_CreateDouble((double)dValue, -1); + if (pJsonStruct == NULL) + { + return(false); + } + cJSON_AddItemToObject(pFocusData, strKey.c_str(), pJsonStruct); + if (cJSON_GetObjectItem(pFocusData, strKey.c_str()) == NULL) + { + return(false); + } + m_pKeyTravers = pFocusData; + return(true); +} + +bool CJsonObject::AddNull(const std::string& strKey) +{ + cJSON* pFocusData = NULL; + if (m_pJsonData != NULL) + { + pFocusData = m_pJsonData; + } + else if (m_pExternJsonDataRef != NULL) + { + pFocusData = m_pExternJsonDataRef; + } + else + { + m_pJsonData = cJSON_CreateObject(); + m_pKeyTravers = m_pJsonData; + pFocusData = m_pJsonData; + } + + if (pFocusData == NULL) + { + m_strErrMsg = "json data is null!"; + return(false); + } + if (pFocusData->type != cJSON_Object) + { + m_strErrMsg = "not a json object! json array?"; + return(false); + } + if (cJSON_GetObjectItem(pFocusData, strKey.c_str()) != NULL) + { + m_strErrMsg = "key exists!"; + return(false); + } + cJSON* pJsonStruct = cJSON_CreateNull(); + if (pJsonStruct == NULL) + { + return(false); + } + cJSON_AddItemToObject(pFocusData, strKey.c_str(), pJsonStruct); + if (cJSON_GetObjectItem(pFocusData, strKey.c_str()) == NULL) + { + return(false); + } + m_pKeyTravers = pFocusData; + return(true); +} + +bool CJsonObject::Delete(const std::string& strKey) +{ + cJSON* pFocusData = NULL; + if (m_pJsonData == NULL) + { + pFocusData = m_pExternJsonDataRef; + } + else + { + pFocusData = m_pJsonData; + } + if (pFocusData == NULL) + { + m_strErrMsg = "json data is null!"; + return(false); + } + if (pFocusData->type != cJSON_Object) + { + m_strErrMsg = "not a json object! json array?"; + return(false); + } + cJSON_DeleteItemFromObject(pFocusData, strKey.c_str()); + std::map::iterator iter = m_mapJsonObjectRef.find(strKey); + if (iter != m_mapJsonObjectRef.end()) + { + if (iter->second != NULL) + { + delete (iter->second); + iter->second = NULL; + } + m_mapJsonObjectRef.erase(iter); + } + m_pKeyTravers = pFocusData; + return(true); +} + +bool CJsonObject::Replace(const std::string& strKey, const CJsonObject& oJsonObject) +{ + cJSON* pFocusData = NULL; + if (m_pJsonData == NULL) + { + pFocusData = m_pExternJsonDataRef; + } + else + { + pFocusData = m_pJsonData; + } + if (pFocusData == NULL) + { + m_strErrMsg = "json data is null!"; + return(false); + } + if (pFocusData->type != cJSON_Object) + { + m_strErrMsg = "not a json object! json array?"; + return(false); + } + cJSON* pJsonStruct = cJSON_Parse(oJsonObject.ToString().c_str()); + if (pJsonStruct == NULL) + { + m_strErrMsg = std::string("prase json string error at ") + cJSON_GetErrorPtr(); + return(false); + } + cJSON_ReplaceItemInObject(pFocusData, strKey.c_str(), pJsonStruct); + if (cJSON_GetObjectItem(pFocusData, strKey.c_str()) == NULL) + { + return(false); + } + std::map::iterator iter = m_mapJsonObjectRef.find(strKey); + if (iter != m_mapJsonObjectRef.end()) + { + if (iter->second != NULL) + { + delete (iter->second); + iter->second = NULL; + } + m_mapJsonObjectRef.erase(iter); + } + return(true); +} + +bool CJsonObject::Replace(const std::string& strKey, const std::string& strValue) +{ + cJSON* pFocusData = NULL; + if (m_pJsonData == NULL) + { + pFocusData = m_pExternJsonDataRef; + } + else + { + pFocusData = m_pJsonData; + } + if (pFocusData == NULL) + { + m_strErrMsg = "json data is null!"; + return(false); + } + if (pFocusData->type != cJSON_Object) + { + m_strErrMsg = "not a json object! json array?"; + return(false); + } + cJSON* pJsonStruct = cJSON_CreateString(strValue.c_str()); + if (pJsonStruct == NULL) + { + return(false); + } + std::map::iterator iter = m_mapJsonObjectRef.find(strKey); + if (iter != m_mapJsonObjectRef.end()) + { + if (iter->second != NULL) + { + delete (iter->second); + iter->second = NULL; + } + m_mapJsonObjectRef.erase(iter); + } + cJSON_ReplaceItemInObject(pFocusData, strKey.c_str(), pJsonStruct); + if (cJSON_GetObjectItem(pFocusData, strKey.c_str()) == NULL) + { + return(false); + } + return(true); +} + +bool CJsonObject::Replace(const std::string& strKey, int32 iValue) +{ + cJSON* pFocusData = NULL; + if (m_pJsonData == NULL) + { + pFocusData = m_pExternJsonDataRef; + } + else + { + pFocusData = m_pJsonData; + } + if (pFocusData == NULL) + { + m_strErrMsg = "json data is null!"; + return(false); + } + if (pFocusData->type != cJSON_Object) + { + m_strErrMsg = "not a json object! json array?"; + return(false); + } + cJSON* pJsonStruct = cJSON_CreateInt((uint64)iValue, -1); + if (pJsonStruct == NULL) + { + return(false); + } + std::map::iterator iter = m_mapJsonObjectRef.find(strKey); + if (iter != m_mapJsonObjectRef.end()) + { + if (iter->second != NULL) + { + delete (iter->second); + iter->second = NULL; + } + m_mapJsonObjectRef.erase(iter); + } + cJSON_ReplaceItemInObject(pFocusData, strKey.c_str(), pJsonStruct); + if (cJSON_GetObjectItem(pFocusData, strKey.c_str()) == NULL) + { + return(false); + } + return(true); +} + +bool CJsonObject::Replace(const std::string& strKey, uint32 uiValue) +{ + cJSON* pFocusData = NULL; + if (m_pJsonData == NULL) + { + pFocusData = m_pExternJsonDataRef; + } + else + { + pFocusData = m_pJsonData; + } + if (pFocusData == NULL) + { + m_strErrMsg = "json data is null!"; + return(false); + } + if (pFocusData->type != cJSON_Object) + { + m_strErrMsg = "not a json object! json array?"; + return(false); + } + cJSON* pJsonStruct = cJSON_CreateInt((uint64)uiValue, 1); + if (pJsonStruct == NULL) + { + return(false); + } + std::map::iterator iter = m_mapJsonObjectRef.find(strKey); + if (iter != m_mapJsonObjectRef.end()) + { + if (iter->second != NULL) + { + delete (iter->second); + iter->second = NULL; + } + m_mapJsonObjectRef.erase(iter); + } + cJSON_ReplaceItemInObject(pFocusData, strKey.c_str(), pJsonStruct); + if (cJSON_GetObjectItem(pFocusData, strKey.c_str()) == NULL) + { + return(false); + } + return(true); +} + +bool CJsonObject::Replace(const std::string& strKey, int64 llValue) +{ + cJSON* pFocusData = NULL; + if (m_pJsonData == NULL) + { + pFocusData = m_pExternJsonDataRef; + } + else + { + pFocusData = m_pJsonData; + } + if (pFocusData == NULL) + { + m_strErrMsg = "json data is null!"; + return(false); + } + if (pFocusData->type != cJSON_Object) + { + m_strErrMsg = "not a json object! json array?"; + return(false); + } + cJSON* pJsonStruct = cJSON_CreateInt((uint64)llValue, -1); + if (pJsonStruct == NULL) + { + return(false); + } + std::map::iterator iter = m_mapJsonObjectRef.find(strKey); + if (iter != m_mapJsonObjectRef.end()) + { + if (iter->second != NULL) + { + delete (iter->second); + iter->second = NULL; + } + m_mapJsonObjectRef.erase(iter); + } + cJSON_ReplaceItemInObject(pFocusData, strKey.c_str(), pJsonStruct); + if (cJSON_GetObjectItem(pFocusData, strKey.c_str()) == NULL) + { + return(false); + } + return(true); +} + +bool CJsonObject::Replace(const std::string& strKey, uint64 ullValue) +{ + cJSON* pFocusData = NULL; + if (m_pJsonData == NULL) + { + pFocusData = m_pExternJsonDataRef; + } + else + { + pFocusData = m_pJsonData; + } + if (pFocusData == NULL) + { + m_strErrMsg = "json data is null!"; + return(false); + } + if (pFocusData->type != cJSON_Object) + { + m_strErrMsg = "not a json object! json array?"; + return(false); + } + cJSON* pJsonStruct = cJSON_CreateInt((uint64)ullValue, 1); + if (pJsonStruct == NULL) + { + return(false); + } + std::map::iterator iter = m_mapJsonObjectRef.find(strKey); + if (iter != m_mapJsonObjectRef.end()) + { + if (iter->second != NULL) + { + delete (iter->second); + iter->second = NULL; + } + m_mapJsonObjectRef.erase(iter); + } + cJSON_ReplaceItemInObject(pFocusData, strKey.c_str(), pJsonStruct); + if (cJSON_GetObjectItem(pFocusData, strKey.c_str()) == NULL) + { + return(false); + } + return(true); +} + +bool CJsonObject::Replace(const std::string& strKey, bool bValue, bool bValueAgain) +{ + cJSON* pFocusData = NULL; + if (m_pJsonData == NULL) + { + pFocusData = m_pExternJsonDataRef; + } + else + { + pFocusData = m_pJsonData; + } + if (pFocusData == NULL) + { + m_strErrMsg = "json data is null!"; + return(false); + } + if (pFocusData->type != cJSON_Object) + { + m_strErrMsg = "not a json object! json array?"; + return(false); + } + cJSON* pJsonStruct = cJSON_CreateBool(bValue); + if (pJsonStruct == NULL) + { + return(false); + } + std::map::iterator iter = m_mapJsonObjectRef.find(strKey); + if (iter != m_mapJsonObjectRef.end()) + { + if (iter->second != NULL) + { + delete (iter->second); + iter->second = NULL; + } + m_mapJsonObjectRef.erase(iter); + } + cJSON_ReplaceItemInObject(pFocusData, strKey.c_str(), pJsonStruct); + if (cJSON_GetObjectItem(pFocusData, strKey.c_str()) == NULL) + { + return(false); + } + return(true); +} + +bool CJsonObject::Replace(const std::string& strKey, float fValue) +{ + cJSON* pFocusData = NULL; + if (m_pJsonData == NULL) + { + pFocusData = m_pExternJsonDataRef; + } + else + { + pFocusData = m_pJsonData; + } + if (pFocusData == NULL) + { + m_strErrMsg = "json data is null!"; + return(false); + } + if (pFocusData->type != cJSON_Object) + { + m_strErrMsg = "not a json object! json array?"; + return(false); + } + cJSON* pJsonStruct = cJSON_CreateDouble((double)fValue, -1); + if (pJsonStruct == NULL) + { + return(false); + } + std::map::iterator iter = m_mapJsonObjectRef.find(strKey); + if (iter != m_mapJsonObjectRef.end()) + { + if (iter->second != NULL) + { + delete (iter->second); + iter->second = NULL; + } + m_mapJsonObjectRef.erase(iter); + } + cJSON_ReplaceItemInObject(pFocusData, strKey.c_str(), pJsonStruct); + if (cJSON_GetObjectItem(pFocusData, strKey.c_str()) == NULL) + { + return(false); + } + return(true); +} + +bool CJsonObject::Replace(const std::string& strKey, double dValue) +{ + cJSON* pFocusData = NULL; + if (m_pJsonData == NULL) + { + pFocusData = m_pExternJsonDataRef; + } + else + { + pFocusData = m_pJsonData; + } + if (pFocusData == NULL) + { + m_strErrMsg = "json data is null!"; + return(false); + } + if (pFocusData->type != cJSON_Object) + { + m_strErrMsg = "not a json object! json array?"; + return(false); + } + cJSON* pJsonStruct = cJSON_CreateDouble((double)dValue, -1); + if (pJsonStruct == NULL) + { + return(false); + } + std::map::iterator iter = m_mapJsonObjectRef.find(strKey); + if (iter != m_mapJsonObjectRef.end()) + { + if (iter->second != NULL) + { + delete (iter->second); + iter->second = NULL; + } + m_mapJsonObjectRef.erase(iter); + } + cJSON_ReplaceItemInObject(pFocusData, strKey.c_str(), pJsonStruct); + if (cJSON_GetObjectItem(pFocusData, strKey.c_str()) == NULL) + { + return(false); + } + return(true); +} + +bool CJsonObject::ReplaceWithNull(const std::string& strKey) +{ + cJSON* pFocusData = NULL; + if (m_pJsonData == NULL) + { + pFocusData = m_pExternJsonDataRef; + } + else + { + pFocusData = m_pJsonData; + } + if (pFocusData == NULL) + { + m_strErrMsg = "json data is null!"; + return(false); + } + if (pFocusData->type != cJSON_Object) + { + m_strErrMsg = "not a json object! json array?"; + return(false); + } + cJSON* pJsonStruct = cJSON_CreateNull(); + if (pJsonStruct == NULL) + { + return(false); + } + std::map::iterator iter = m_mapJsonObjectRef.find(strKey); + if (iter != m_mapJsonObjectRef.end()) + { + if (iter->second != NULL) + { + delete (iter->second); + iter->second = NULL; + } + m_mapJsonObjectRef.erase(iter); + } + cJSON_ReplaceItemInObject(pFocusData, strKey.c_str(), pJsonStruct); + if (cJSON_GetObjectItem(pFocusData, strKey.c_str()) == NULL) + { + return(false); + } + return(true); +} + +int CJsonObject::GetArraySize() +{ + if (m_pJsonData != NULL) + { + if (m_pJsonData->type == cJSON_Array) + { + return(cJSON_GetArraySize(m_pJsonData)); + } + } + else if (m_pExternJsonDataRef != NULL) + { + if(m_pExternJsonDataRef->type == cJSON_Array) + { + return(cJSON_GetArraySize(m_pExternJsonDataRef)); + } + } + return(0); +} + +bool CJsonObject::Get(int iWhich, CJsonObject& oJsonObject) const +{ + cJSON* pJsonStruct = NULL; + if (m_pJsonData != NULL) + { + if (m_pJsonData->type == cJSON_Array) + { + pJsonStruct = cJSON_GetArrayItem(m_pJsonData, iWhich); + } + } + else if (m_pExternJsonDataRef != NULL) + { + if(m_pExternJsonDataRef->type == cJSON_Array) + { + pJsonStruct = cJSON_GetArrayItem(m_pExternJsonDataRef, iWhich); + } + } + if (pJsonStruct == NULL) + { + return(false); + } + char* pJsonString = cJSON_Print(pJsonStruct); + std::string strJsonData = pJsonString; + free(pJsonString); + if (oJsonObject.Parse(strJsonData)) + { + return(true); + } + else + { + return(false); + } +} + +bool CJsonObject::Get(int iWhich, std::string& strValue) const +{ + cJSON* pJsonStruct = NULL; + if (m_pJsonData != NULL) + { + if (m_pJsonData->type == cJSON_Array) + { + pJsonStruct = cJSON_GetArrayItem(m_pJsonData, iWhich); + } + } + else if (m_pExternJsonDataRef != NULL) + { + if(m_pExternJsonDataRef->type == cJSON_Array) + { + pJsonStruct = cJSON_GetArrayItem(m_pExternJsonDataRef, iWhich); + } + } + if (pJsonStruct == NULL) + { + return(false); + } + if (pJsonStruct->type != cJSON_String) + { + return(false); + } + strValue = pJsonStruct->valuestring; + return(true); +} + +bool CJsonObject::Get(int iWhich, int32& iValue) const +{ + cJSON* pJsonStruct = NULL; + if (m_pJsonData != NULL) + { + if (m_pJsonData->type == cJSON_Array) + { + pJsonStruct = cJSON_GetArrayItem(m_pJsonData, iWhich); + } + } + else if (m_pExternJsonDataRef != NULL) + { + if(m_pExternJsonDataRef->type == cJSON_Array) + { + pJsonStruct = cJSON_GetArrayItem(m_pExternJsonDataRef, iWhich); + } + } + if (pJsonStruct == NULL) + { + return(false); + } + if (pJsonStruct->type == cJSON_Int) + { + iValue = (int32)(pJsonStruct->valueint); + return(true); + } + else if (pJsonStruct->type == cJSON_Double) + { + iValue = (int32)(pJsonStruct->valuedouble); + return(true); + } + return(false); +} + +bool CJsonObject::Get(int iWhich, uint32& uiValue) const +{ + cJSON* pJsonStruct = NULL; + if (m_pJsonData != NULL) + { + if (m_pJsonData->type == cJSON_Array) + { + pJsonStruct = cJSON_GetArrayItem(m_pJsonData, iWhich); + } + } + else if (m_pExternJsonDataRef != NULL) + { + if(m_pExternJsonDataRef->type == cJSON_Array) + { + pJsonStruct = cJSON_GetArrayItem(m_pExternJsonDataRef, iWhich); + } + } + if (pJsonStruct == NULL) + { + return(false); + } + if (pJsonStruct->type == cJSON_Int) + { + uiValue = (uint32)(pJsonStruct->valueint); + return(true); + } + else if (pJsonStruct->type == cJSON_Double) + { + uiValue = (uint32)(pJsonStruct->valuedouble); + return(true); + } + return(false); +} + +bool CJsonObject::Get(int iWhich, int64& llValue) const +{ + cJSON* pJsonStruct = NULL; + if (m_pJsonData != NULL) + { + if (m_pJsonData->type == cJSON_Array) + { + pJsonStruct = cJSON_GetArrayItem(m_pJsonData, iWhich); + } + } + else if (m_pExternJsonDataRef != NULL) + { + if(m_pExternJsonDataRef->type == cJSON_Array) + { + pJsonStruct = cJSON_GetArrayItem(m_pExternJsonDataRef, iWhich); + } + } + if (pJsonStruct == NULL) + { + return(false); + } + if (pJsonStruct->type == cJSON_Int) + { + llValue = (int64)(pJsonStruct->valueint); + return(true); + } + else if (pJsonStruct->type == cJSON_Double) + { + llValue = (int64)(pJsonStruct->valuedouble); + return(true); + } + return(false); +} + +bool CJsonObject::Get(int iWhich, uint64& ullValue) const +{ + cJSON* pJsonStruct = NULL; + if (m_pJsonData != NULL) + { + if (m_pJsonData->type == cJSON_Array) + { + pJsonStruct = cJSON_GetArrayItem(m_pJsonData, iWhich); + } + } + else if (m_pExternJsonDataRef != NULL) + { + if(m_pExternJsonDataRef->type == cJSON_Array) + { + pJsonStruct = cJSON_GetArrayItem(m_pExternJsonDataRef, iWhich); + } + } + if (pJsonStruct == NULL) + { + return(false); + } + if (pJsonStruct->type == cJSON_Int) + { + ullValue = (uint64)(pJsonStruct->valueint); + return(true); + } + else if (pJsonStruct->type == cJSON_Double) + { + ullValue = (uint64)(pJsonStruct->valuedouble); + return(true); + } + return(false); +} + +bool CJsonObject::Get(int iWhich, bool& bValue) const +{ + cJSON* pJsonStruct = NULL; + if (m_pJsonData != NULL) + { + if (m_pJsonData->type == cJSON_Array) + { + pJsonStruct = cJSON_GetArrayItem(m_pJsonData, iWhich); + } + } + else if (m_pExternJsonDataRef != NULL) + { + if(m_pExternJsonDataRef->type == cJSON_Array) + { + pJsonStruct = cJSON_GetArrayItem(m_pExternJsonDataRef, iWhich); + } + } + if (pJsonStruct == NULL) + { + return(false); + } + if (pJsonStruct->type > cJSON_True) + { + return(false); + } + bValue = pJsonStruct->type; + return(true); +} + +bool CJsonObject::Get(int iWhich, float& fValue) const +{ + cJSON* pJsonStruct = NULL; + if (m_pJsonData != NULL) + { + if (m_pJsonData->type == cJSON_Array) + { + pJsonStruct = cJSON_GetArrayItem(m_pJsonData, iWhich); + } + } + else if (m_pExternJsonDataRef != NULL) + { + if(m_pExternJsonDataRef->type == cJSON_Array) + { + pJsonStruct = cJSON_GetArrayItem(m_pExternJsonDataRef, iWhich); + } + } + if (pJsonStruct == NULL) + { + return(false); + } + if (pJsonStruct->type == cJSON_Double || pJsonStruct->type == cJSON_Int) + { + fValue = (float)(pJsonStruct->valuedouble); + return(true); + } + return(false); +} + +bool CJsonObject::Get(int iWhich, double& dValue) const +{ + cJSON* pJsonStruct = NULL; + if (m_pJsonData != NULL) + { + if (m_pJsonData->type == cJSON_Array) + { + pJsonStruct = cJSON_GetArrayItem(m_pJsonData, iWhich); + } + } + else if (m_pExternJsonDataRef != NULL) + { + if(m_pExternJsonDataRef->type == cJSON_Array) + { + pJsonStruct = cJSON_GetArrayItem(m_pExternJsonDataRef, iWhich); + } + } + if (pJsonStruct == NULL) + { + return(false); + } + if (pJsonStruct->type == cJSON_Double || pJsonStruct->type == cJSON_Int) + { + dValue = pJsonStruct->valuedouble; + return(true); + } + return(false); +} + +bool CJsonObject::IsNull(int iWhich) const +{ + cJSON* pJsonStruct = NULL; + if (m_pJsonData != NULL) + { + if (m_pJsonData->type == cJSON_Array) + { + pJsonStruct = cJSON_GetArrayItem(m_pJsonData, iWhich); + } + } + else if (m_pExternJsonDataRef != NULL) + { + if(m_pExternJsonDataRef->type == cJSON_Array) + { + pJsonStruct = cJSON_GetArrayItem(m_pExternJsonDataRef, iWhich); + } + } + if (pJsonStruct == NULL) + { + return(false); + } + if (pJsonStruct->type != cJSON_NULL) + { + return(false); + } + return(true); +} + +bool CJsonObject::Add(const CJsonObject& oJsonObject) +{ + cJSON* pFocusData = NULL; + if (m_pJsonData != NULL) + { + pFocusData = m_pJsonData; + } + else if (m_pExternJsonDataRef != NULL) + { + pFocusData = m_pExternJsonDataRef; + } + else + { + m_pJsonData = cJSON_CreateArray(); + pFocusData = m_pJsonData; + } + + if (pFocusData == NULL) + { + m_strErrMsg = "json data is null!"; + return(false); + } + if (pFocusData->type != cJSON_Array) + { + m_strErrMsg = "not a json array! json object?"; + return(false); + } + cJSON* pJsonStruct = cJSON_Parse(oJsonObject.ToString().c_str()); + if (pJsonStruct == NULL) + { + m_strErrMsg = std::string("prase json string error at ") + cJSON_GetErrorPtr(); + return(false); + } + int iArraySizeBeforeAdd = cJSON_GetArraySize(pFocusData); + cJSON_AddItemToArray(pFocusData, pJsonStruct); + int iArraySizeAfterAdd = cJSON_GetArraySize(pFocusData); + if (iArraySizeAfterAdd == iArraySizeBeforeAdd) + { + return(false); + } + unsigned int uiLastIndex = (unsigned int)cJSON_GetArraySize(pFocusData) - 1; + for (std::map::iterator iter = m_mapJsonArrayRef.begin(); + iter != m_mapJsonArrayRef.end(); ) + { + if (iter->first >= uiLastIndex) + { + if (iter->second != NULL) + { + delete (iter->second); + iter->second = NULL; + } + m_mapJsonArrayRef.erase(iter++); + } + else + { + iter++; + } + } + return(true); +} + +bool CJsonObject::Add(const std::string& strValue) +{ + cJSON* pFocusData = NULL; + if (m_pJsonData != NULL) + { + pFocusData = m_pJsonData; + } + else if (m_pExternJsonDataRef != NULL) + { + pFocusData = m_pExternJsonDataRef; + } + else + { + m_pJsonData = cJSON_CreateArray(); + pFocusData = m_pJsonData; + } + + if (pFocusData == NULL) + { + m_strErrMsg = "json data is null!"; + return(false); + } + if (pFocusData->type != cJSON_Array) + { + m_strErrMsg = "not a json array! json object?"; + return(false); + } + cJSON* pJsonStruct = cJSON_CreateString(strValue.c_str()); + if (pJsonStruct == NULL) + { + return(false); + } + int iArraySizeBeforeAdd = cJSON_GetArraySize(pFocusData); + cJSON_AddItemToArray(pFocusData, pJsonStruct); + int iArraySizeAfterAdd = cJSON_GetArraySize(pFocusData); + if (iArraySizeAfterAdd == iArraySizeBeforeAdd) + { + return(false); + } + return(true); +} + +bool CJsonObject::Add(int32 iValue) +{ + cJSON* pFocusData = NULL; + if (m_pJsonData != NULL) + { + pFocusData = m_pJsonData; + } + else if (m_pExternJsonDataRef != NULL) + { + pFocusData = m_pExternJsonDataRef; + } + else + { + m_pJsonData = cJSON_CreateArray(); + pFocusData = m_pJsonData; + } + + if (pFocusData == NULL) + { + m_strErrMsg = "json data is null!"; + return(false); + } + if (pFocusData->type != cJSON_Array) + { + m_strErrMsg = "not a json array! json object?"; + return(false); + } + cJSON* pJsonStruct = cJSON_CreateInt((uint64)iValue, -1); + if (pJsonStruct == NULL) + { + return(false); + } + int iArraySizeBeforeAdd = cJSON_GetArraySize(pFocusData); + cJSON_AddItemToArray(pFocusData, pJsonStruct); + int iArraySizeAfterAdd = cJSON_GetArraySize(pFocusData); + if (iArraySizeAfterAdd == iArraySizeBeforeAdd) + { + return(false); + } + return(true); +} + +bool CJsonObject::Add(uint32 uiValue) +{ + cJSON* pFocusData = NULL; + if (m_pJsonData != NULL) + { + pFocusData = m_pJsonData; + } + else if (m_pExternJsonDataRef != NULL) + { + pFocusData = m_pExternJsonDataRef; + } + else + { + m_pJsonData = cJSON_CreateArray(); + pFocusData = m_pJsonData; + } + + if (pFocusData == NULL) + { + m_strErrMsg = "json data is null!"; + return(false); + } + if (pFocusData->type != cJSON_Array) + { + m_strErrMsg = "not a json array! json object?"; + return(false); + } + cJSON* pJsonStruct = cJSON_CreateInt((uint64)uiValue, 1); + if (pJsonStruct == NULL) + { + return(false); + } + int iArraySizeBeforeAdd = cJSON_GetArraySize(pFocusData); + cJSON_AddItemToArray(pFocusData, pJsonStruct); + int iArraySizeAfterAdd = cJSON_GetArraySize(pFocusData); + if (iArraySizeAfterAdd == iArraySizeBeforeAdd) + { + return(false); + } + return(true); +} + +bool CJsonObject::Add(int64 llValue) +{ + cJSON* pFocusData = NULL; + if (m_pJsonData != NULL) + { + pFocusData = m_pJsonData; + } + else if (m_pExternJsonDataRef != NULL) + { + pFocusData = m_pExternJsonDataRef; + } + else + { + m_pJsonData = cJSON_CreateArray(); + pFocusData = m_pJsonData; + } + + if (pFocusData == NULL) + { + m_strErrMsg = "json data is null!"; + return(false); + } + if (pFocusData->type != cJSON_Array) + { + m_strErrMsg = "not a json array! json object?"; + return(false); + } + cJSON* pJsonStruct = cJSON_CreateInt((uint64)llValue, -1); + if (pJsonStruct == NULL) + { + return(false); + } + int iArraySizeBeforeAdd = cJSON_GetArraySize(pFocusData); + cJSON_AddItemToArray(pFocusData, pJsonStruct); + int iArraySizeAfterAdd = cJSON_GetArraySize(pFocusData); + if (iArraySizeAfterAdd == iArraySizeBeforeAdd) + { + return(false); + } + return(true); +} + +bool CJsonObject::Add(uint64 ullValue) +{ + cJSON* pFocusData = NULL; + if (m_pJsonData != NULL) + { + pFocusData = m_pJsonData; + } + else if (m_pExternJsonDataRef != NULL) + { + pFocusData = m_pExternJsonDataRef; + } + else + { + m_pJsonData = cJSON_CreateArray(); + pFocusData = m_pJsonData; + } + + if (pFocusData == NULL) + { + m_strErrMsg = "json data is null!"; + return(false); + } + if (pFocusData->type != cJSON_Array) + { + m_strErrMsg = "not a json array! json object?"; + return(false); + } + cJSON* pJsonStruct = cJSON_CreateInt((uint64)ullValue, 1); + if (pJsonStruct == NULL) + { + return(false); + } + int iArraySizeBeforeAdd = cJSON_GetArraySize(pFocusData); + cJSON_AddItemToArray(pFocusData, pJsonStruct); + int iArraySizeAfterAdd = cJSON_GetArraySize(pFocusData); + if (iArraySizeAfterAdd == iArraySizeBeforeAdd) + { + return(false); + } + return(true); +} + +bool CJsonObject::Add(int iAnywhere, bool bValue) +{ + cJSON* pFocusData = NULL; + if (m_pJsonData != NULL) + { + pFocusData = m_pJsonData; + } + else if (m_pExternJsonDataRef != NULL) + { + pFocusData = m_pExternJsonDataRef; + } + else + { + m_pJsonData = cJSON_CreateArray(); + pFocusData = m_pJsonData; + } + + if (pFocusData == NULL) + { + m_strErrMsg = "json data is null!"; + return(false); + } + if (pFocusData->type != cJSON_Array) + { + m_strErrMsg = "not a json array! json object?"; + return(false); + } + cJSON* pJsonStruct = cJSON_CreateBool(bValue); + if (pJsonStruct == NULL) + { + return(false); + } + int iArraySizeBeforeAdd = cJSON_GetArraySize(pFocusData); + cJSON_AddItemToArray(pFocusData, pJsonStruct); + int iArraySizeAfterAdd = cJSON_GetArraySize(pFocusData); + if (iArraySizeAfterAdd == iArraySizeBeforeAdd) + { + return(false); + } + return(true); +} + +bool CJsonObject::Add(float fValue) +{ + cJSON* pFocusData = NULL; + if (m_pJsonData != NULL) + { + pFocusData = m_pJsonData; + } + else if (m_pExternJsonDataRef != NULL) + { + pFocusData = m_pExternJsonDataRef; + } + else + { + m_pJsonData = cJSON_CreateArray(); + pFocusData = m_pJsonData; + } + + if (pFocusData == NULL) + { + m_strErrMsg = "json data is null!"; + return(false); + } + if (pFocusData->type != cJSON_Array) + { + m_strErrMsg = "not a json array! json object?"; + return(false); + } + cJSON* pJsonStruct = cJSON_CreateDouble((double)fValue, -1); + if (pJsonStruct == NULL) + { + return(false); + } + int iArraySizeBeforeAdd = cJSON_GetArraySize(pFocusData); + cJSON_AddItemToArray(pFocusData, pJsonStruct); + int iArraySizeAfterAdd = cJSON_GetArraySize(pFocusData); + if (iArraySizeAfterAdd == iArraySizeBeforeAdd) + { + return(false); + } + return(true); +} + +bool CJsonObject::Add(double dValue) +{ + cJSON* pFocusData = NULL; + if (m_pJsonData != NULL) + { + pFocusData = m_pJsonData; + } + else if (m_pExternJsonDataRef != NULL) + { + pFocusData = m_pExternJsonDataRef; + } + else + { + m_pJsonData = cJSON_CreateArray(); + pFocusData = m_pJsonData; + } + + if (pFocusData == NULL) + { + m_strErrMsg = "json data is null!"; + return(false); + } + if (pFocusData->type != cJSON_Array) + { + m_strErrMsg = "not a json array! json object?"; + return(false); + } + cJSON* pJsonStruct = cJSON_CreateDouble((double)dValue, -1); + if (pJsonStruct == NULL) + { + return(false); + } + int iArraySizeBeforeAdd = cJSON_GetArraySize(pFocusData); + cJSON_AddItemToArray(pFocusData, pJsonStruct); + int iArraySizeAfterAdd = cJSON_GetArraySize(pFocusData); + if (iArraySizeAfterAdd == iArraySizeBeforeAdd) + { + return(false); + } + return(true); +} + +bool CJsonObject::AddNull() +{ + cJSON* pFocusData = NULL; + if (m_pJsonData != NULL) + { + pFocusData = m_pJsonData; + } + else if (m_pExternJsonDataRef != NULL) + { + pFocusData = m_pExternJsonDataRef; + } + else + { + m_pJsonData = cJSON_CreateArray(); + pFocusData = m_pJsonData; + } + + if (pFocusData == NULL) + { + m_strErrMsg = "json data is null!"; + return(false); + } + if (pFocusData->type != cJSON_Array) + { + m_strErrMsg = "not a json array! json object?"; + return(false); + } + cJSON* pJsonStruct = cJSON_CreateNull(); + if (pJsonStruct == NULL) + { + return(false); + } + int iArraySizeBeforeAdd = cJSON_GetArraySize(pFocusData); + cJSON_AddItemToArray(pFocusData, pJsonStruct); + int iArraySizeAfterAdd = cJSON_GetArraySize(pFocusData); + if (iArraySizeAfterAdd == iArraySizeBeforeAdd) + { + return(false); + } + return(true); +} + +bool CJsonObject::AddAsFirst(const CJsonObject& oJsonObject) +{ + cJSON* pFocusData = NULL; + if (m_pJsonData != NULL) + { + pFocusData = m_pJsonData; + } + else if (m_pExternJsonDataRef != NULL) + { + pFocusData = m_pExternJsonDataRef; + } + else + { + m_pJsonData = cJSON_CreateArray(); + pFocusData = m_pJsonData; + } + + if (pFocusData == NULL) + { + m_strErrMsg = "json data is null!"; + return(false); + } + if (pFocusData->type != cJSON_Array) + { + m_strErrMsg = "not a json array! json object?"; + return(false); + } + cJSON* pJsonStruct = cJSON_Parse(oJsonObject.ToString().c_str()); + if (pJsonStruct == NULL) + { + m_strErrMsg = std::string("prase json string error at ") + cJSON_GetErrorPtr(); + return(false); + } + int iArraySizeBeforeAdd = cJSON_GetArraySize(pFocusData); + cJSON_AddItemToArrayHead(pFocusData, pJsonStruct); + int iArraySizeAfterAdd = cJSON_GetArraySize(pFocusData); + if (iArraySizeAfterAdd == iArraySizeBeforeAdd) + { + return(false); + } + for (std::map::iterator iter = m_mapJsonArrayRef.begin(); + iter != m_mapJsonArrayRef.end(); ) + { + if (iter->second != NULL) + { + delete (iter->second); + iter->second = NULL; + } + m_mapJsonArrayRef.erase(iter++); + } + return(true); +} + +bool CJsonObject::AddAsFirst(const std::string& strValue) +{ + cJSON* pFocusData = NULL; + if (m_pJsonData != NULL) + { + pFocusData = m_pJsonData; + } + else if (m_pExternJsonDataRef != NULL) + { + pFocusData = m_pExternJsonDataRef; + } + else + { + m_pJsonData = cJSON_CreateArray(); + pFocusData = m_pJsonData; + } + + if (pFocusData == NULL) + { + m_strErrMsg = "json data is null!"; + return(false); + } + if (pFocusData->type != cJSON_Array) + { + m_strErrMsg = "not a json array! json object?"; + return(false); + } + cJSON* pJsonStruct = cJSON_CreateString(strValue.c_str()); + if (pJsonStruct == NULL) + { + return(false); + } + int iArraySizeBeforeAdd = cJSON_GetArraySize(pFocusData); + cJSON_AddItemToArrayHead(pFocusData, pJsonStruct); + int iArraySizeAfterAdd = cJSON_GetArraySize(pFocusData); + if (iArraySizeAfterAdd == iArraySizeBeforeAdd) + { + return(false); + } + return(true); +} + +bool CJsonObject::AddAsFirst(int32 iValue) +{ + cJSON* pFocusData = NULL; + if (m_pJsonData != NULL) + { + pFocusData = m_pJsonData; + } + else if (m_pExternJsonDataRef != NULL) + { + pFocusData = m_pExternJsonDataRef; + } + else + { + m_pJsonData = cJSON_CreateArray(); + pFocusData = m_pJsonData; + } + + if (pFocusData == NULL) + { + m_strErrMsg = "json data is null!"; + return(false); + } + if (pFocusData->type != cJSON_Array) + { + m_strErrMsg = "not a json array! json object?"; + return(false); + } + cJSON* pJsonStruct = cJSON_CreateInt((uint64)iValue, -1); + if (pJsonStruct == NULL) + { + return(false); + } + int iArraySizeBeforeAdd = cJSON_GetArraySize(pFocusData); + cJSON_AddItemToArrayHead(pFocusData, pJsonStruct); + int iArraySizeAfterAdd = cJSON_GetArraySize(pFocusData); + if (iArraySizeAfterAdd == iArraySizeBeforeAdd) + { + return(false); + } + return(true); +} + +bool CJsonObject::AddAsFirst(uint32 uiValue) +{ + cJSON* pFocusData = NULL; + if (m_pJsonData != NULL) + { + pFocusData = m_pJsonData; + } + else if (m_pExternJsonDataRef != NULL) + { + pFocusData = m_pExternJsonDataRef; + } + else + { + m_pJsonData = cJSON_CreateArray(); + pFocusData = m_pJsonData; + } + + if (pFocusData == NULL) + { + m_strErrMsg = "json data is null!"; + return(false); + } + if (pFocusData->type != cJSON_Array) + { + m_strErrMsg = "not a json array! json object?"; + return(false); + } + cJSON* pJsonStruct = cJSON_CreateInt((uint64)uiValue, -1); + if (pJsonStruct == NULL) + { + return(false); + } + int iArraySizeBeforeAdd = cJSON_GetArraySize(pFocusData); + cJSON_AddItemToArrayHead(pFocusData, pJsonStruct); + int iArraySizeAfterAdd = cJSON_GetArraySize(pFocusData); + if (iArraySizeAfterAdd == iArraySizeBeforeAdd) + { + return(false); + } + return(true); +} + +bool CJsonObject::AddAsFirst(int64 llValue) +{ + cJSON* pFocusData = NULL; + if (m_pJsonData != NULL) + { + pFocusData = m_pJsonData; + } + else if (m_pExternJsonDataRef != NULL) + { + pFocusData = m_pExternJsonDataRef; + } + else + { + m_pJsonData = cJSON_CreateArray(); + pFocusData = m_pJsonData; + } + + if (pFocusData == NULL) + { + m_strErrMsg = "json data is null!"; + return(false); + } + if (pFocusData->type != cJSON_Array) + { + m_strErrMsg = "not a json array! json object?"; + return(false); + } + cJSON* pJsonStruct = cJSON_CreateInt((uint64)llValue, -1); + if (pJsonStruct == NULL) + { + return(false); + } + int iArraySizeBeforeAdd = cJSON_GetArraySize(pFocusData); + cJSON_AddItemToArrayHead(pFocusData, pJsonStruct); + int iArraySizeAfterAdd = cJSON_GetArraySize(pFocusData); + if (iArraySizeAfterAdd == iArraySizeBeforeAdd) + { + return(false); + } + return(true); +} + +bool CJsonObject::AddAsFirst(uint64 ullValue) +{ + cJSON* pFocusData = NULL; + if (m_pJsonData != NULL) + { + pFocusData = m_pJsonData; + } + else if (m_pExternJsonDataRef != NULL) + { + pFocusData = m_pExternJsonDataRef; + } + else + { + m_pJsonData = cJSON_CreateArray(); + pFocusData = m_pJsonData; + } + + if (pFocusData == NULL) + { + m_strErrMsg = "json data is null!"; + return(false); + } + if (pFocusData->type != cJSON_Array) + { + m_strErrMsg = "not a json array! json object?"; + return(false); + } + cJSON* pJsonStruct = cJSON_CreateInt((uint64)ullValue, -1); + if (pJsonStruct == NULL) + { + return(false); + } + int iArraySizeBeforeAdd = cJSON_GetArraySize(pFocusData); + cJSON_AddItemToArrayHead(pFocusData, pJsonStruct); + int iArraySizeAfterAdd = cJSON_GetArraySize(pFocusData); + if (iArraySizeAfterAdd == iArraySizeBeforeAdd) + { + return(false); + } + return(true); +} + +bool CJsonObject::AddAsFirst(int iAnywhere, bool bValue) +{ + cJSON* pFocusData = NULL; + if (m_pJsonData != NULL) + { + pFocusData = m_pJsonData; + } + else if (m_pExternJsonDataRef != NULL) + { + pFocusData = m_pExternJsonDataRef; + } + else + { + m_pJsonData = cJSON_CreateArray(); + pFocusData = m_pJsonData; + } + + if (pFocusData == NULL) + { + m_strErrMsg = "json data is null!"; + return(false); + } + if (pFocusData->type != cJSON_Array) + { + m_strErrMsg = "not a json array! json object?"; + return(false); + } + cJSON* pJsonStruct = cJSON_CreateBool(bValue); + if (pJsonStruct == NULL) + { + return(false); + } + int iArraySizeBeforeAdd = cJSON_GetArraySize(pFocusData); + cJSON_AddItemToArrayHead(pFocusData, pJsonStruct); + int iArraySizeAfterAdd = cJSON_GetArraySize(pFocusData); + if (iArraySizeAfterAdd == iArraySizeBeforeAdd) + { + return(false); + } + return(true); +} + +bool CJsonObject::AddAsFirst(float fValue) +{ + cJSON* pFocusData = NULL; + if (m_pJsonData != NULL) + { + pFocusData = m_pJsonData; + } + else if (m_pExternJsonDataRef != NULL) + { + pFocusData = m_pExternJsonDataRef; + } + else + { + m_pJsonData = cJSON_CreateArray(); + pFocusData = m_pJsonData; + } + + if (pFocusData == NULL) + { + m_strErrMsg = "json data is null!"; + return(false); + } + if (pFocusData->type != cJSON_Array) + { + m_strErrMsg = "not a json array! json object?"; + return(false); + } + cJSON* pJsonStruct = cJSON_CreateDouble((double)fValue, -1); + if (pJsonStruct == NULL) + { + return(false); + } + int iArraySizeBeforeAdd = cJSON_GetArraySize(pFocusData); + cJSON_AddItemToArrayHead(pFocusData, pJsonStruct); + int iArraySizeAfterAdd = cJSON_GetArraySize(pFocusData); + if (iArraySizeAfterAdd == iArraySizeBeforeAdd) + { + return(false); + } + return(true); +} + +bool CJsonObject::AddAsFirst(double dValue) +{ + cJSON* pFocusData = NULL; + if (m_pJsonData != NULL) + { + pFocusData = m_pJsonData; + } + else if (m_pExternJsonDataRef != NULL) + { + pFocusData = m_pExternJsonDataRef; + } + else + { + m_pJsonData = cJSON_CreateArray(); + pFocusData = m_pJsonData; + } + + if (pFocusData == NULL) + { + m_strErrMsg = "json data is null!"; + return(false); + } + if (pFocusData->type != cJSON_Array) + { + m_strErrMsg = "not a json array! json object?"; + return(false); + } + cJSON* pJsonStruct = cJSON_CreateDouble((double)dValue, -1); + if (pJsonStruct == NULL) + { + return(false); + } + int iArraySizeBeforeAdd = cJSON_GetArraySize(pFocusData); + cJSON_AddItemToArrayHead(pFocusData, pJsonStruct); + int iArraySizeAfterAdd = cJSON_GetArraySize(pFocusData); + if (iArraySizeAfterAdd == iArraySizeBeforeAdd) + { + return(false); + } + return(true); +} + +bool CJsonObject::AddNullAsFirst() +{ + cJSON* pFocusData = NULL; + if (m_pJsonData != NULL) + { + pFocusData = m_pJsonData; + } + else if (m_pExternJsonDataRef != NULL) + { + pFocusData = m_pExternJsonDataRef; + } + else + { + m_pJsonData = cJSON_CreateArray(); + pFocusData = m_pJsonData; + } + + if (pFocusData == NULL) + { + m_strErrMsg = "json data is null!"; + return(false); + } + if (pFocusData->type != cJSON_Array) + { + m_strErrMsg = "not a json array! json object?"; + return(false); + } + cJSON* pJsonStruct = cJSON_CreateNull(); + if (pJsonStruct == NULL) + { + return(false); + } + int iArraySizeBeforeAdd = cJSON_GetArraySize(pFocusData); + cJSON_AddItemToArrayHead(pFocusData, pJsonStruct); + int iArraySizeAfterAdd = cJSON_GetArraySize(pFocusData); + if (iArraySizeAfterAdd == iArraySizeBeforeAdd) + { + return(false); + } + return(true); +} + +bool CJsonObject::Delete(int iWhich) +{ + cJSON* pFocusData = NULL; + if (m_pJsonData == NULL) + { + pFocusData = m_pExternJsonDataRef; + } + else + { + pFocusData = m_pJsonData; + } + if (pFocusData == NULL) + { + m_strErrMsg = "json data is null!"; + return(false); + } + if (pFocusData->type != cJSON_Array) + { + m_strErrMsg = "not a json array! json object?"; + return(false); + } + cJSON_DeleteItemFromArray(pFocusData, iWhich); + for (std::map::iterator iter = m_mapJsonArrayRef.begin(); + iter != m_mapJsonArrayRef.end(); ) + { + if (iter->first >= (unsigned int)iWhich) + { + if (iter->second != NULL) + { + delete (iter->second); + iter->second = NULL; + } + m_mapJsonArrayRef.erase(iter++); + } + else + { + iter++; + } + } + return(true); +} + +bool CJsonObject::Replace(int iWhich, const CJsonObject& oJsonObject) +{ + cJSON* pFocusData = NULL; + if (m_pJsonData == NULL) + { + pFocusData = m_pExternJsonDataRef; + } + else + { + pFocusData = m_pJsonData; + } + if (pFocusData == NULL) + { + m_strErrMsg = "json data is null!"; + return(false); + } + if (pFocusData->type != cJSON_Array) + { + m_strErrMsg = "not a json array! json object?"; + return(false); + } + cJSON* pJsonStruct = cJSON_Parse(oJsonObject.ToString().c_str()); + if (pJsonStruct == NULL) + { + m_strErrMsg = std::string("prase json string error at ") + cJSON_GetErrorPtr(); + return(false); + } + cJSON_ReplaceItemInArray(pFocusData, iWhich, pJsonStruct); + if (cJSON_GetArrayItem(pFocusData, iWhich) == NULL) + { + return(false); + } + std::map::iterator iter = m_mapJsonArrayRef.find(iWhich); + if (iter != m_mapJsonArrayRef.end()) + { + if (iter->second != NULL) + { + delete (iter->second); + iter->second = NULL; + } + m_mapJsonArrayRef.erase(iter); + } + return(true); +} + +bool CJsonObject::Replace(int iWhich, const std::string& strValue) +{ + cJSON* pFocusData = NULL; + if (m_pJsonData == NULL) + { + pFocusData = m_pExternJsonDataRef; + } + else + { + pFocusData = m_pJsonData; + } + if (pFocusData == NULL) + { + m_strErrMsg = "json data is null!"; + return(false); + } + if (pFocusData->type != cJSON_Array) + { + m_strErrMsg = "not a json array! json object?"; + return(false); + } + cJSON* pJsonStruct = cJSON_CreateString(strValue.c_str()); + if (pJsonStruct == NULL) + { + return(false); + } + std::map::iterator iter = m_mapJsonArrayRef.find(iWhich); + if (iter != m_mapJsonArrayRef.end()) + { + if (iter->second != NULL) + { + delete (iter->second); + iter->second = NULL; + } + m_mapJsonArrayRef.erase(iter); + } + cJSON_ReplaceItemInArray(pFocusData, iWhich, pJsonStruct); + if (cJSON_GetArrayItem(pFocusData, iWhich) == NULL) + { + return(false); + } + return(true); +} + +bool CJsonObject::Replace(int iWhich, int32 iValue) +{ + cJSON* pFocusData = NULL; + if (m_pJsonData == NULL) + { + pFocusData = m_pExternJsonDataRef; + } + else + { + pFocusData = m_pJsonData; + } + if (pFocusData == NULL) + { + m_strErrMsg = "json data is null!"; + return(false); + } + if (pFocusData->type != cJSON_Array) + { + m_strErrMsg = "not a json array! json object?"; + return(false); + } + cJSON* pJsonStruct = cJSON_CreateInt((uint64)iValue, -1); + if (pJsonStruct == NULL) + { + return(false); + } + std::map::iterator iter = m_mapJsonArrayRef.find(iWhich); + if (iter != m_mapJsonArrayRef.end()) + { + if (iter->second != NULL) + { + delete (iter->second); + iter->second = NULL; + } + m_mapJsonArrayRef.erase(iter); + } + cJSON_ReplaceItemInArray(pFocusData, iWhich, pJsonStruct); + if (cJSON_GetArrayItem(pFocusData, iWhich) == NULL) + { + return(false); + } + return(true); +} + +bool CJsonObject::Replace(int iWhich, uint32 uiValue) +{ + cJSON* pFocusData = NULL; + if (m_pJsonData == NULL) + { + pFocusData = m_pExternJsonDataRef; + } + else + { + pFocusData = m_pJsonData; + } + if (pFocusData == NULL) + { + m_strErrMsg = "json data is null!"; + return(false); + } + if (pFocusData->type != cJSON_Array) + { + m_strErrMsg = "not a json array! json object?"; + return(false); + } + cJSON* pJsonStruct = cJSON_CreateInt((uint64)uiValue, 1); + if (pJsonStruct == NULL) + { + return(false); + } + std::map::iterator iter = m_mapJsonArrayRef.find(iWhich); + if (iter != m_mapJsonArrayRef.end()) + { + if (iter->second != NULL) + { + delete (iter->second); + iter->second = NULL; + } + m_mapJsonArrayRef.erase(iter); + } + cJSON_ReplaceItemInArray(pFocusData, iWhich, pJsonStruct); + if (cJSON_GetArrayItem(pFocusData, iWhich) == NULL) + { + return(false); + } + return(true); +} + +bool CJsonObject::Replace(int iWhich, int64 llValue) +{ + cJSON* pFocusData = NULL; + if (m_pJsonData == NULL) + { + pFocusData = m_pExternJsonDataRef; + } + else + { + pFocusData = m_pJsonData; + } + if (pFocusData == NULL) + { + m_strErrMsg = "json data is null!"; + return(false); + } + if (pFocusData->type != cJSON_Array) + { + m_strErrMsg = "not a json array! json object?"; + return(false); + } + cJSON* pJsonStruct = cJSON_CreateInt((uint64)((uint64)llValue), -1); + if (pJsonStruct == NULL) + { + return(false); + } + std::map::iterator iter = m_mapJsonArrayRef.find(iWhich); + if (iter != m_mapJsonArrayRef.end()) + { + if (iter->second != NULL) + { + delete (iter->second); + iter->second = NULL; + } + m_mapJsonArrayRef.erase(iter); + } + cJSON_ReplaceItemInArray(pFocusData, iWhich, pJsonStruct); + if (cJSON_GetArrayItem(pFocusData, iWhich) == NULL) + { + return(false); + } + return(true); +} + +bool CJsonObject::Replace(int iWhich, uint64 ullValue) +{ + cJSON* pFocusData = NULL; + if (m_pJsonData == NULL) + { + pFocusData = m_pExternJsonDataRef; + } + else + { + pFocusData = m_pJsonData; + } + if (pFocusData == NULL) + { + m_strErrMsg = "json data is null!"; + return(false); + } + if (pFocusData->type != cJSON_Array) + { + m_strErrMsg = "not a json array! json object?"; + return(false); + } + cJSON* pJsonStruct = cJSON_CreateInt((uint64)ullValue, 1); + if (pJsonStruct == NULL) + { + return(false); + } + std::map::iterator iter = m_mapJsonArrayRef.find(iWhich); + if (iter != m_mapJsonArrayRef.end()) + { + if (iter->second != NULL) + { + delete (iter->second); + iter->second = NULL; + } + m_mapJsonArrayRef.erase(iter); + } + cJSON_ReplaceItemInArray(pFocusData, iWhich, pJsonStruct); + if (cJSON_GetArrayItem(pFocusData, iWhich) == NULL) + { + return(false); + } + return(true); +} + +bool CJsonObject::Replace(int iWhich, bool bValue, bool bValueAgain) +{ + cJSON* pFocusData = NULL; + if (m_pJsonData == NULL) + { + pFocusData = m_pExternJsonDataRef; + } + else + { + pFocusData = m_pJsonData; + } + if (pFocusData == NULL) + { + m_strErrMsg = "json data is null!"; + return(false); + } + if (pFocusData->type != cJSON_Array) + { + m_strErrMsg = "not a json array! json object?"; + return(false); + } + cJSON* pJsonStruct = cJSON_CreateBool(bValue); + if (pJsonStruct == NULL) + { + return(false); + } + std::map::iterator iter = m_mapJsonArrayRef.find(iWhich); + if (iter != m_mapJsonArrayRef.end()) + { + if (iter->second != NULL) + { + delete (iter->second); + iter->second = NULL; + } + m_mapJsonArrayRef.erase(iter); + } + cJSON_ReplaceItemInArray(pFocusData, iWhich, pJsonStruct); + if (cJSON_GetArrayItem(pFocusData, iWhich) == NULL) + { + return(false); + } + return(true); +} + +bool CJsonObject::Replace(int iWhich, float fValue) +{ + cJSON* pFocusData = NULL; + if (m_pJsonData == NULL) + { + pFocusData = m_pExternJsonDataRef; + } + else + { + pFocusData = m_pJsonData; + } + if (pFocusData == NULL) + { + m_strErrMsg = "json data is null!"; + return(false); + } + if (pFocusData->type != cJSON_Array) + { + m_strErrMsg = "not a json array! json object?"; + return(false); + } + cJSON* pJsonStruct = cJSON_CreateDouble((double)fValue, -1); + if (pJsonStruct == NULL) + { + return(false); + } + std::map::iterator iter = m_mapJsonArrayRef.find(iWhich); + if (iter != m_mapJsonArrayRef.end()) + { + if (iter->second != NULL) + { + delete (iter->second); + iter->second = NULL; + } + m_mapJsonArrayRef.erase(iter); + } + cJSON_ReplaceItemInArray(pFocusData, iWhich, pJsonStruct); + if (cJSON_GetArrayItem(pFocusData, iWhich) == NULL) + { + return(false); + } + return(true); +} + +bool CJsonObject::Replace(int iWhich, double dValue) +{ + cJSON* pFocusData = NULL; + if (m_pJsonData == NULL) + { + pFocusData = m_pExternJsonDataRef; + } + else + { + pFocusData = m_pJsonData; + } + if (pFocusData == NULL) + { + m_strErrMsg = "json data is null!"; + return(false); + } + if (pFocusData->type != cJSON_Array) + { + m_strErrMsg = "not a json array! json object?"; + return(false); + } + cJSON* pJsonStruct = cJSON_CreateDouble((double)dValue, -1); + if (pJsonStruct == NULL) + { + return(false); + } + std::map::iterator iter = m_mapJsonArrayRef.find(iWhich); + if (iter != m_mapJsonArrayRef.end()) + { + if (iter->second != NULL) + { + delete (iter->second); + iter->second = NULL; + } + m_mapJsonArrayRef.erase(iter); + } + cJSON_ReplaceItemInArray(pFocusData, iWhich, pJsonStruct); + if (cJSON_GetArrayItem(pFocusData, iWhich) == NULL) + { + return(false); + } + return(true); +} + +bool CJsonObject::ReplaceWithNull(int iWhich) +{ + cJSON* pFocusData = NULL; + if (m_pJsonData == NULL) + { + pFocusData = m_pExternJsonDataRef; + } + else + { + pFocusData = m_pJsonData; + } + if (pFocusData == NULL) + { + m_strErrMsg = "json data is null!"; + return(false); + } + if (pFocusData->type != cJSON_Array) + { + m_strErrMsg = "not a json array! json object?"; + return(false); + } + cJSON* pJsonStruct = cJSON_CreateNull(); + if (pJsonStruct == NULL) + { + return(false); + } + std::map::iterator iter = m_mapJsonArrayRef.find(iWhich); + if (iter != m_mapJsonArrayRef.end()) + { + if (iter->second != NULL) + { + delete (iter->second); + iter->second = NULL; + } + m_mapJsonArrayRef.erase(iter); + } + cJSON_ReplaceItemInArray(pFocusData, iWhich, pJsonStruct); + if (cJSON_GetArrayItem(pFocusData, iWhich) == NULL) + { + return(false); + } + return(true); +} + +CJsonObject::CJsonObject(cJSON* pJsonData) + : m_pJsonData(NULL), m_pExternJsonDataRef(pJsonData), m_pKeyTravers(pJsonData) +{ +} + +} + + diff --git a/sop-sdk/sdk-c++/thirdparty/CJsonObject/CJsonObject.hpp b/sop-sdk/sdk-c++/thirdparty/CJsonObject/CJsonObject.hpp new file mode 100644 index 00000000..05637e00 --- /dev/null +++ b/sop-sdk/sdk-c++/thirdparty/CJsonObject/CJsonObject.hpp @@ -0,0 +1,156 @@ +/******************************************************************************* + * Project: neb + * @file CJsonObject.hpp + * @brief Json + * @author bwarliao + * @date: 2014-7-16 + * @note + * Modify history: + ******************************************************************************/ + +#ifndef CJSONOBJECT_HPP_ +#define CJSONOBJECT_HPP_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef __cplusplus +extern "C" { +#endif +#include "cJSON.h" +#ifdef __cplusplus +} +#endif + + +namespace neb +{ + +class CJsonObject +{ +public: // method of ordinary json object or json array + CJsonObject(); + CJsonObject(const std::string& strJson); + CJsonObject(const CJsonObject* pJsonObject); + CJsonObject(const CJsonObject& oJsonObject); + virtual ~CJsonObject(); + + CJsonObject& operator=(const CJsonObject& oJsonObject); + bool operator==(const CJsonObject& oJsonObject) const; + bool Parse(const std::string& strJson); + void Clear(); + bool IsEmpty() const; + bool IsArray() const; + std::string ToString() const; + std::string ToFormattedString() const; + const std::string& GetErrMsg() const + { + return(m_strErrMsg); + } + +public: // method of ordinary json object + bool AddEmptySubObject(const std::string& strKey); + bool AddEmptySubArray(const std::string& strKey); + bool GetKey(std::string& strKey); + void ResetTraversing(); + CJsonObject& operator[](const std::string& strKey); + std::string operator()(const std::string& strKey) const; + bool Get(const std::string& strKey, CJsonObject& oJsonObject) const; + bool Get(const std::string& strKey, std::string& strValue) const; + bool Get(const std::string& strKey, int32& iValue) const; + bool Get(const std::string& strKey, uint32& uiValue) const; + bool Get(const std::string& strKey, int64& llValue) const; + bool Get(const std::string& strKey, uint64& ullValue) const; + bool Get(const std::string& strKey, bool& bValue) const; + bool Get(const std::string& strKey, float& fValue) const; + bool Get(const std::string& strKey, double& dValue) const; + bool IsNull(const std::string& strKey) const; + bool Add(const std::string& strKey, const CJsonObject& oJsonObject); + bool Add(const std::string& strKey, const std::string& strValue); + bool Add(const std::string& strKey, int32 iValue); + bool Add(const std::string& strKey, uint32 uiValue); + bool Add(const std::string& strKey, int64 llValue); + bool Add(const std::string& strKey, uint64 ullValue); + bool Add(const std::string& strKey, bool bValue, bool bValueAgain); + bool Add(const std::string& strKey, float fValue); + bool Add(const std::string& strKey, double dValue); + bool AddNull(const std::string& strKey); // add null like this: "key":null + bool Delete(const std::string& strKey); + bool Replace(const std::string& strKey, const CJsonObject& oJsonObject); + bool Replace(const std::string& strKey, const std::string& strValue); + bool Replace(const std::string& strKey, int32 iValue); + bool Replace(const std::string& strKey, uint32 uiValue); + bool Replace(const std::string& strKey, int64 llValue); + bool Replace(const std::string& strKey, uint64 ullValue); + bool Replace(const std::string& strKey, bool bValue, bool bValueAgain); + bool Replace(const std::string& strKey, float fValue); + bool Replace(const std::string& strKey, double dValue); + bool ReplaceWithNull(const std::string& strKey); // replace value with null + +public: // method of json array + int GetArraySize(); + CJsonObject& operator[](unsigned int uiWhich); + std::string operator()(unsigned int uiWhich) const; + bool Get(int iWhich, CJsonObject& oJsonObject) const; + bool Get(int iWhich, std::string& strValue) const; + bool Get(int iWhich, int32& iValue) const; + bool Get(int iWhich, uint32& uiValue) const; + bool Get(int iWhich, int64& llValue) const; + bool Get(int iWhich, uint64& ullValue) const; + bool Get(int iWhich, bool& bValue) const; + bool Get(int iWhich, float& fValue) const; + bool Get(int iWhich, double& dValue) const; + bool IsNull(int iWhich) const; + bool Add(const CJsonObject& oJsonObject); + bool Add(const std::string& strValue); + bool Add(int32 iValue); + bool Add(uint32 uiValue); + bool Add(int64 llValue); + bool Add(uint64 ullValue); + bool Add(int iAnywhere, bool bValue); + bool Add(float fValue); + bool Add(double dValue); + bool AddNull(); // add a null value + bool AddAsFirst(const CJsonObject& oJsonObject); + bool AddAsFirst(const std::string& strValue); + bool AddAsFirst(int32 iValue); + bool AddAsFirst(uint32 uiValue); + bool AddAsFirst(int64 llValue); + bool AddAsFirst(uint64 ullValue); + bool AddAsFirst(int iAnywhere, bool bValue); + bool AddAsFirst(float fValue); + bool AddAsFirst(double dValue); + bool AddNullAsFirst(); // add a null value + bool Delete(int iWhich); + bool Replace(int iWhich, const CJsonObject& oJsonObject); + bool Replace(int iWhich, const std::string& strValue); + bool Replace(int iWhich, int32 iValue); + bool Replace(int iWhich, uint32 uiValue); + bool Replace(int iWhich, int64 llValue); + bool Replace(int iWhich, uint64 ullValue); + bool Replace(int iWhich, bool bValue, bool bValueAgain); + bool Replace(int iWhich, float fValue); + bool Replace(int iWhich, double dValue); + bool ReplaceWithNull(int iWhich); // replace with a null value + +private: + CJsonObject(cJSON* pJsonData); + +private: + cJSON* m_pJsonData; + cJSON* m_pExternJsonDataRef; + cJSON* m_pKeyTravers; + std::string m_strErrMsg; + std::map m_mapJsonArrayRef; + std::map m_mapJsonObjectRef; +}; + +} + +#endif /* CJSONHELPER_HPP_ */ diff --git a/sop-sdk/sdk-c++/thirdparty/CJsonObject/cJSON.c b/sop-sdk/sdk-c++/thirdparty/CJsonObject/cJSON.c new file mode 100644 index 00000000..daaac3fd --- /dev/null +++ b/sop-sdk/sdk-c++/thirdparty/CJsonObject/cJSON.c @@ -0,0 +1,1091 @@ +/* + Copyright (c) 2009 Dave Gamble + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. + */ + +/* cJSON */ +/* JSON parser in C. */ + +#include +#include +#include +#include +#include +#include +#include +#include "cJSON.h" + +#ifndef INT_MAX +#define INT_MAX 2147483647 +#define INT_MIN (-INT_MAX - 1) +#define UINT_MAX 4294967295U +#endif + +static const char *ep; + +const char *cJSON_GetErrorPtr() +{ + return ep; +} + +static int cJSON_strcasecmp(const char *s1, const char *s2) +{ + if (!s1) + return (s1 == s2) ? 0 : 1; + if (!s2) + return 1; + for (; tolower(*s1) == tolower(*s2); ++s1, ++s2) + if (*s1 == 0) + return 0; + return tolower(*(const unsigned char *)s1) + - tolower(*(const unsigned char *)s2); +} + +static void *(*cJSON_malloc)(size_t sz) = malloc; +static void (*cJSON_free)(void *ptr) = free; + +static char* cJSON_strdup(const char* str) +{ + size_t len; + char* copy; + + len = strlen(str) + 1; + if (!(copy = (char*) cJSON_malloc(len))) + return 0; + memcpy(copy, str, len); + return copy; +} + +void cJSON_InitHooks(cJSON_Hooks* hooks) +{ + if (!hooks) + { /* Reset hooks */ + cJSON_malloc = malloc; + cJSON_free = free; + return; + } + + cJSON_malloc = (hooks->malloc_fn) ? hooks->malloc_fn : malloc; + cJSON_free = (hooks->free_fn) ? hooks->free_fn : free; +} + +/* Internal constructor. */ +static cJSON *cJSON_New_Item() +{ + cJSON* node = (cJSON*) cJSON_malloc(sizeof(cJSON)); + if (node) + memset(node, 0, sizeof(cJSON)); + return node; +} + +/* Delete a cJSON structure. */ +void cJSON_Delete(cJSON *c) +{ + cJSON *next; + while (c) + { + next = c->next; + if (!(c->type & cJSON_IsReference) && c->child) + cJSON_Delete(c->child); + if (!(c->type & cJSON_IsReference) && c->valuestring) + cJSON_free(c->valuestring); + if (c->string) + cJSON_free(c->string); + cJSON_free(c); + c = next; + } +} + +/* Parse the input text to generate a number, and populate the result into item. */ +static const char *parse_number(cJSON *item, const char *num) +{ + long double n = 0, scale = 0; + int subscale = 0, signsubscale = 1; + item->sign = 1; + + /* Could use sscanf for this? */ + if (*num == '-') + item->sign = -1, num++; /* Has sign? */ + if (*num == '0') + num++; /* is zero */ + if (*num >= '1' && *num <= '9') + do + n = (n * 10.0) + (*num++ - '0'); + while (*num >= '0' && *num <= '9'); /* Number? */ + if (*num == '.' && num[1] >= '0' && num[1] <= '9') + { + num++; + do + n = (n * 10.0) + (*num++ - '0'), scale--; + while (*num >= '0' && *num <= '9'); + } /* Fractional part? */ + if (*num == 'e' || *num == 'E') /* Exponent? */ + { + num++; + if (*num == '+') + num++; + else if (*num == '-') + signsubscale = -1, num++; /* With sign? */ + while (*num >= '0' && *num <= '9') + subscale = (subscale * 10) + (*num++ - '0'); /* Number? */ + } + + if (scale == 0 && subscale == 0) + { + item->valuedouble = (double)(item->sign * n); + item->valueint = item->sign * (int64)n; + item->type = cJSON_Int; + } + else + { + n = item->sign * n * pow(10.0, (scale + subscale * signsubscale)); /* number = +/- number.fraction * 10^+/- exponent */ + item->valuedouble = (double)n; + item->valueint = (int64)n; + item->type = cJSON_Double; + } + return num; +} + +/* Render the number nicely from the given item into a string. */ +static char *print_double(cJSON *item) +{ + char *str; + double d = item->valuedouble; + str = (char*) cJSON_malloc(64); /* This is a nice tradeoff. */ + if (str) + { + if (fabs(d) < 1.0e-6 || fabs(d) > 1.0e9) + sprintf(str, "%lf", d); + else + sprintf(str, "%f", d); + } + return str; +} + +static char *print_int(cJSON *item) +{ + char *str; + str = (char*) cJSON_malloc(22); /* 2^64+1 can be represented in 21 chars. */ + if (str) + { + if (item->sign == -1) + { + if ((int64)item->valueint <= (int64)INT_MAX && (int64)item->valueint >= (int64)INT_MIN) + { + sprintf(str, "%d", (int32)item->valueint); + } + else + { + sprintf(str, "%lld", (int64)item->valueint); + } + } + else + { + if (item->valueint <= (uint64)UINT_MAX) + { + sprintf(str, "%u", (uint32)item->valueint); + } + else + { + sprintf(str, "%llu", item->valueint); + } + } + } + return str; +} + +/* Parse the input text into an unescaped cstring, and populate item. */ +static const unsigned char firstByteMark[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, + 0xF8, 0xFC }; +static const char *parse_string(cJSON *item, const char *str) +{ + const char *ptr = str + 1; + char *ptr2; + char *out; + int len = 0; + unsigned uc, uc2; + if (*str != '\"') + { + ep = str; + return 0; + } /* not a string! */ + + while (*ptr != '\"' && *ptr && ++len) + if (*ptr++ == '\\') + ptr++; /* Skip escaped quotes. */ + + out = (char*) cJSON_malloc(len + 1); /* This is how long we need for the string, roughly. */ + if (!out) + return 0; + + ptr = str + 1; + ptr2 = out; + while (*ptr != '\"' && *ptr) + { + if (*ptr != '\\') + *ptr2++ = *ptr++; + else + { + ptr++; + switch (*ptr) + { + case 'b': + *ptr2++ = '\b'; + break; + case 'f': + *ptr2++ = '\f'; + break; + case 'n': + *ptr2++ = '\n'; + break; + case 'r': + *ptr2++ = '\r'; + break; + case 't': + *ptr2++ = '\t'; + break; + case 'u': /* transcode utf16 to utf8. */ + sscanf(ptr + 1, "%4x", &uc); + ptr += 4; /* get the unicode char. */ + + if ((uc >= 0xDC00 && uc <= 0xDFFF) || uc == 0) + break; // check for invalid. + + if (uc >= 0xD800 && uc <= 0xDBFF) // UTF16 surrogate pairs. + { + if (ptr[1] != '\\' || ptr[2] != 'u') + break; // missing second-half of surrogate. + sscanf(ptr + 3, "%4x", &uc2); + ptr += 6; + if (uc2 < 0xDC00 || uc2 > 0xDFFF) + break; // invalid second-half of surrogate. + uc = 0x10000 | ((uc & 0x3FF) << 10) | (uc2 & 0x3FF); + } + + len = 4; + if (uc < 0x80) + len = 1; + else if (uc < 0x800) + len = 2; + else if (uc < 0x10000) + len = 3; + ptr2 += len; + + switch (len) + { + case 4: + *--ptr2 = ((uc | 0x80) & 0xBF); + uc >>= 6; + case 3: + *--ptr2 = ((uc | 0x80) & 0xBF); + uc >>= 6; + case 2: + *--ptr2 = ((uc | 0x80) & 0xBF); + uc >>= 6; + case 1: + *--ptr2 = (uc | firstByteMark[len]); + } + ptr2 += len; + break; + default: + *ptr2++ = *ptr; + break; + } + ptr++; + } + } + *ptr2 = 0; + if (*ptr == '\"') + ptr++; + item->valuestring = out; + item->type = cJSON_String; + return ptr; +} + +/* Render the cstring provided to an escaped version that can be printed. */ +static char *print_string_ptr(const char *str) +{ + const char *ptr; + char *ptr2, *out; + int len = 0; + unsigned char token; + + if (!str) + return cJSON_strdup(""); + ptr = str; + while ((token = *ptr) && ++len) + { + if (strchr("\"\\\b\f\n\r\t", token)) + len++; + else if (token < 32) + len += 5; + ptr++; + } + + out = (char*) cJSON_malloc(len + 3); + if (!out) + return 0; + + ptr2 = out; + ptr = str; + *ptr2++ = '\"'; + while (*ptr) + { + if ((unsigned char) *ptr > 31 && *ptr != '\"' && *ptr != '\\') + *ptr2++ = *ptr++; + else + { + *ptr2++ = '\\'; + switch (token = *ptr++) + { + case '\\': + *ptr2++ = '\\'; + break; + case '\"': + *ptr2++ = '\"'; + break; + case '\b': + *ptr2++ = 'b'; + break; + case '\f': + *ptr2++ = 'f'; + break; + case '\n': + *ptr2++ = 'n'; + break; + case '\r': + *ptr2++ = 'r'; + break; + case '\t': + *ptr2++ = 't'; + break; + default: + sprintf(ptr2, "u%04x", token); + ptr2 += 5; + break; /* escape and print */ + } + } + } + *ptr2++ = '\"'; + *ptr2++ = 0; + return out; +} +/* Invote print_string_ptr (which is useful) on an item. */ +static char *print_string(cJSON *item) +{ + return print_string_ptr(item->valuestring); +} + +/* Predeclare these prototypes. */ +static const char *parse_value(cJSON *item, const char *value); +static char *print_value(cJSON *item, int depth, int fmt); +static const char *parse_array(cJSON *item, const char *value); +static char *print_array(cJSON *item, int depth, int fmt); +static const char *parse_object(cJSON *item, const char *value); +static char *print_object(cJSON *item, int depth, int fmt); + +/* Utility to jump whitespace and cr/lf */ +static const char *skip(const char *in) +{ + while (in && *in && (unsigned char) *in <= 32) + in++; + return in; +} + +/* Parse an object - create a new root, and populate. */ +cJSON *cJSON_Parse(const char *value) +{ + cJSON *c = cJSON_New_Item(); + ep = 0; + if (!c) + return 0; /* memory fail */ + + if (!parse_value(c, skip(value))) + { + cJSON_Delete(c); + return 0; + } + return c; +} + +/* Render a cJSON item/entity/structure to text. */ +char *cJSON_Print(cJSON *item) +{ + return print_value(item, 0, 1); +} +char *cJSON_PrintUnformatted(cJSON *item) +{ + return print_value(item, 0, 0); +} + +/* Parser core - when encountering text, process appropriately. */ +static const char *parse_value(cJSON *item, const char *value) +{ + if (!value) + return 0; /* Fail on null. */ + if (!strncmp(value, "null", 4)) + { + item->type = cJSON_NULL; + return value + 4; + } + if (!strncmp(value, "false", 5)) + { + item->type = cJSON_False; + return value + 5; + } + if (!strncmp(value, "true", 4)) + { + item->type = cJSON_True; + item->valueint = 1; + return value + 4; + } + if (*value == '\"') + { + return parse_string(item, value); + } + if (*value == '-' || (*value >= '0' && *value <= '9')) + { + return parse_number(item, value); + } + if (*value == '[') + { + return parse_array(item, value); + } + if (*value == '{') + { + return parse_object(item, value); + } + + ep = value; + return 0; /* failure. */ +} + +/* Render a value to text. */ +static char *print_value(cJSON *item, int depth, int fmt) +{ + char *out = 0; + if (!item) + return 0; + switch ((item->type) & 255) + { + case cJSON_NULL: + out = cJSON_strdup("null"); + break; + case cJSON_False: + out = cJSON_strdup("false"); + break; + case cJSON_True: + out = cJSON_strdup("true"); + break; + case cJSON_Int: + out = print_int(item); + break; + case cJSON_Double: + out = print_double(item); + break; + case cJSON_String: + out = print_string(item); + break; + case cJSON_Array: + out = print_array(item, depth, fmt); + break; + case cJSON_Object: + out = print_object(item, depth, fmt); + break; + } + return out; +} + +/* Build an array from input text. */ +static const char *parse_array(cJSON *item, const char *value) +{ + cJSON *child; + if (*value != '[') + { + ep = value; + return 0; + } /* not an array! */ + + item->type = cJSON_Array; + value = skip(value + 1); + if (*value == ']') + return value + 1; /* empty array. */ + + item->child = child = cJSON_New_Item(); + if (!item->child) + return 0; /* memory fail */ + value = skip(parse_value(child, skip(value))); /* skip any spacing, get the value. */ + if (!value) + return 0; + + while (*value == ',') + { + cJSON *new_item; + if (!(new_item = cJSON_New_Item())) + return 0; /* memory fail */ + child->next = new_item; + new_item->prev = child; + child = new_item; + value = skip(parse_value(child, skip(value + 1))); + if (!value) + return 0; /* memory fail */ + } + + if (*value == ']') + return value + 1; /* end of array */ + ep = value; + return 0; /* malformed. */ +} + +/* Render an array to text */ +static char *print_array(cJSON *item, int depth, int fmt) +{ + char **entries; + char *out = 0, *ptr, *ret; + int len = 5; + cJSON *child = item->child; + int numentries = 0, i = 0, fail = 0; + + /* How many entries in the array? */ + while (child) + numentries++, child = child->next; + /* Allocate an array to hold the values for each */ + entries = (char**) cJSON_malloc(numentries * sizeof(char*)); + if (!entries) + return 0; + memset(entries, 0, numentries * sizeof(char*)); + /* Retrieve all the results: */ + child = item->child; + while (child && !fail) + { + ret = print_value(child, depth + 1, fmt); + entries[i++] = ret; + if (ret) + len += strlen(ret) + 2 + (fmt ? 1 : 0); + else + fail = 1; + child = child->next; + } + + /* If we didn't fail, try to malloc the output string */ + if (!fail) + out = (char*) cJSON_malloc(len); + /* If that fails, we fail. */ + if (!out) + fail = 1; + + /* Handle failure. */ + if (fail) + { + for (i = 0; i < numentries; i++) + if (entries[i]) + cJSON_free(entries[i]); + cJSON_free(entries); + return 0; + } + + /* Compose the output array. */ + *out = '['; + ptr = out + 1; + *ptr = 0; + for (i = 0; i < numentries; i++) + { + strcpy(ptr, entries[i]); + ptr += strlen(entries[i]); + if (i != numentries - 1) + { + *ptr++ = ','; + if (fmt) + *ptr++ = ' '; + *ptr = 0; + } + cJSON_free(entries[i]); + } + cJSON_free(entries); + *ptr++ = ']'; + *ptr++ = 0; + return out; +} + +/* Build an object from the text. */ +static const char *parse_object(cJSON *item, const char *value) +{ + cJSON *child; + if (*value != '{') + { + ep = value; + return 0; + } /* not an object! */ + + item->type = cJSON_Object; + value = skip(value + 1); + if (*value == '}') + return value + 1; /* empty array. */ + + item->child = child = cJSON_New_Item(); + if (!item->child) + return 0; + value = skip(parse_string(child, skip(value))); + if (!value) + return 0; + child->string = child->valuestring; + child->valuestring = 0; + if (*value != ':') + { + ep = value; + return 0; + } /* fail! */ + value = skip(parse_value(child, skip(value + 1))); /* skip any spacing, get the value. */ + if (!value) + return 0; + + while (*value == ',') + { + cJSON *new_item; + if (!(new_item = cJSON_New_Item())) + return 0; /* memory fail */ + child->next = new_item; + new_item->prev = child; + child = new_item; + value = skip(parse_string(child, skip(value + 1))); + if (!value) + return 0; + child->string = child->valuestring; + child->valuestring = 0; + if (*value != ':') + { + ep = value; + return 0; + } /* fail! */ + value = skip(parse_value(child, skip(value + 1))); /* skip any spacing, get the value. */ + if (!value) + return 0; + } + + if (*value == '}') + return value + 1; /* end of array */ + ep = value; + return 0; /* malformed. */ +} + +/* Render an object to text. */ +static char *print_object(cJSON *item, int depth, int fmt) +{ + char **entries = 0, **names = 0; + char *out = 0, *ptr, *ret, *str; + int len = 7, i = 0, j; + cJSON *child = item->child; + int numentries = 0, fail = 0; + /* Count the number of entries. */ + while (child) + numentries++, child = child->next; + /* Allocate space for the names and the objects */ + entries = (char**) cJSON_malloc(numentries * sizeof(char*)); + if (!entries) + return 0; + names = (char**) cJSON_malloc(numentries * sizeof(char*)); + if (!names) + { + cJSON_free(entries); + return 0; + } + memset(entries, 0, sizeof(char*) * numentries); + memset(names, 0, sizeof(char*) * numentries); + + /* Collect all the results into our arrays: */ + child = item->child; + depth++; + if (fmt) + len += depth; + while (child) + { + names[i] = str = print_string_ptr(child->string); + entries[i++] = ret = print_value(child, depth, fmt); + if (str && ret) + len += strlen(ret) + strlen(str) + 2 + (fmt ? 2 + depth : 0); + else + fail = 1; + child = child->next; + } + + /* Try to allocate the output string */ + if (!fail) + out = (char*) cJSON_malloc(len); + if (!out) + fail = 1; + + /* Handle failure */ + if (fail) + { + for (i = 0; i < numentries; i++) + { + if (names[i]) + cJSON_free(names[i]); + if (entries[i]) + cJSON_free(entries[i]); + } + cJSON_free(names); + cJSON_free(entries); + return 0; + } + + /* Compose the output: */ + *out = '{'; + ptr = out + 1; + if (fmt) + *ptr++ = '\n'; + *ptr = 0; + for (i = 0; i < numentries; i++) + { + if (fmt) + for (j = 0; j < depth; j++) + *ptr++ = '\t'; + strcpy(ptr, names[i]); + ptr += strlen(names[i]); + *ptr++ = ':'; + if (fmt) + *ptr++ = '\t'; + strcpy(ptr, entries[i]); + ptr += strlen(entries[i]); + if (i != numentries - 1) + *ptr++ = ','; + if (fmt) + *ptr++ = '\n'; + *ptr = 0; + cJSON_free(names[i]); + cJSON_free(entries[i]); + } + + cJSON_free(names); + cJSON_free(entries); + if (fmt) + for (i = 0; i < depth - 1; i++) + *ptr++ = '\t'; + *ptr++ = '}'; + *ptr++ = 0; + return out; +} + +/* Get Array size/item / object item. */ +int cJSON_GetArraySize(cJSON *array) +{ + cJSON *c = array->child; + int i = 0; + while (c) + i++, c = c->next; + return i; +} +cJSON *cJSON_GetArrayItem(cJSON *array, int item) +{ + cJSON *c = array->child; + while (c && item > 0) + item--, c = c->next; + return c; +} +cJSON *cJSON_GetObjectItem(cJSON *object, const char *string) +{ + cJSON *c = object->child; + while (c && cJSON_strcasecmp(c->string, string)) + c = c->next; + return c; +} + +/* Utility for array list handling. */ +static void suffix_object(cJSON *prev, cJSON *item) +{ + prev->next = item; + item->prev = prev; +} +/* Utility for handling references. */ +static cJSON *create_reference(cJSON *item) +{ + cJSON *ref = cJSON_New_Item(); + if (!ref) + return 0; + memcpy(ref, item, sizeof(cJSON)); + ref->string = 0; + ref->type |= cJSON_IsReference; + ref->next = ref->prev = 0; + return ref; +} + +/* Add item to array/object. */ +void cJSON_AddItemToArray(cJSON *array, cJSON *item) +{ + cJSON *c = array->child; + if (!item) + return; + if (!c) + { + array->child = item; + } + else + { + while (c && c->next) + c = c->next; + suffix_object(c, item); + } +} + +void cJSON_AddItemToArrayHead(cJSON *array, cJSON *item) +{ + cJSON *c = array->child; + if (!item) + return; + if (!c) + { + array->child = item; + } + else + { + item->prev = c->prev; + item->next = c; + c->prev = item; + array->child = item; + } +} + +void cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item) +{ + if (!item) + return; + if (item->string) + cJSON_free(item->string); + item->string = cJSON_strdup(string); + cJSON_AddItemToArray(object, item); +} +void cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item) +{ + cJSON_AddItemToArray(array, create_reference(item)); +} +void cJSON_AddItemReferenceToObject(cJSON *object, const char *string, + cJSON *item) +{ + cJSON_AddItemToObject(object, string, create_reference(item)); +} + +cJSON *cJSON_DetachItemFromArray(cJSON *array, int which) +{ + cJSON *c = array->child; + while (c && which > 0) + c = c->next, which--; + if (!c) + return 0; + if (c->prev) + c->prev->next = c->next; + if (c->next) + c->next->prev = c->prev; + if (c == array->child) + array->child = c->next; + c->prev = c->next = 0; + return c; +} +void cJSON_DeleteItemFromArray(cJSON *array, int which) +{ + cJSON_Delete(cJSON_DetachItemFromArray(array, which)); +} +cJSON *cJSON_DetachItemFromObject(cJSON *object, const char *string) +{ + int i = 0; + cJSON *c = object->child; + while (c && cJSON_strcasecmp(c->string, string)) + i++, c = c->next; + if (c) + return cJSON_DetachItemFromArray(object, i); + return 0; +} +void cJSON_DeleteItemFromObject(cJSON *object, const char *string) +{ + cJSON_Delete(cJSON_DetachItemFromObject(object, string)); +} + +/* Replace array/object items with new ones. */ +void cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem) +{ + cJSON *c = array->child; + while (c && which > 0) + c = c->next, which--; + if (!c) + return; + newitem->next = c->next; + newitem->prev = c->prev; + if (newitem->next) + newitem->next->prev = newitem; + if (c == array->child) + array->child = newitem; + else + newitem->prev->next = newitem; + c->next = c->prev = 0; + cJSON_Delete(c); +} +void cJSON_ReplaceItemInObject(cJSON *object, const char *string, + cJSON *newitem) +{ + int i = 0; + cJSON *c = object->child; + while (c && cJSON_strcasecmp(c->string, string)) + i++, c = c->next; + if (c) + { + newitem->string = cJSON_strdup(string); + cJSON_ReplaceItemInArray(object, i, newitem); + } +} + +/* Create basic types: */ +cJSON *cJSON_CreateNull() +{ + cJSON *item = cJSON_New_Item(); + if (item) + item->type = cJSON_NULL; + return item; +} +cJSON *cJSON_CreateTrue() +{ + cJSON *item = cJSON_New_Item(); + if (item) + item->type = cJSON_True; + return item; +} +cJSON *cJSON_CreateFalse() +{ + cJSON *item = cJSON_New_Item(); + if (item) + item->type = cJSON_False; + return item; +} +cJSON *cJSON_CreateBool(int b) +{ + cJSON *item = cJSON_New_Item(); + if (item) + item->type = b ? cJSON_True : cJSON_False; + return item; +} +cJSON *cJSON_CreateDouble(double num, int sign) +{ + cJSON *item = cJSON_New_Item(); + if (item) + { + item->type = cJSON_Double; + item->valuedouble = num; + item->valueint = (int64)num; + item->sign = sign; + } + return item; +} +cJSON *cJSON_CreateInt(uint64 num, int sign) +{ + cJSON *item = cJSON_New_Item(); + if (item) + { + item->type = cJSON_Int; + item->valuedouble = (double)num; + item->valueint = (int64)num; + item->sign = sign; + } + return item; +} +cJSON *cJSON_CreateString(const char *string) +{ + cJSON *item = cJSON_New_Item(); + if (item) + { + item->type = cJSON_String; + item->valuestring = cJSON_strdup(string); + } + return item; +} +cJSON *cJSON_CreateArray() +{ + cJSON *item = cJSON_New_Item(); + if (item) + item->type = cJSON_Array; + return item; +} +cJSON *cJSON_CreateObject() +{ + cJSON *item = cJSON_New_Item(); + if (item) + item->type = cJSON_Object; + return item; +} + +/* Create Arrays: */ +cJSON *cJSON_CreateIntArray(int *numbers, int sign, int count) +{ + int i; + cJSON *n = 0, *p = 0, *a = cJSON_CreateArray(); + for (i = 0; a && i < count; i++) + { + n = cJSON_CreateDouble((long double)((unsigned int)numbers[i]), sign); + if (!i) + a->child = n; + else + suffix_object(p, n); + p = n; + } + return a; +} +cJSON *cJSON_CreateFloatArray(float *numbers, int count) +{ + int i; + cJSON *n = 0, *p = 0, *a = cJSON_CreateArray(); + for (i = 0; a && i < count; i++) + { + n = cJSON_CreateDouble((long double)numbers[i], -1); + if (!i) + a->child = n; + else + suffix_object(p, n); + p = n; + } + return a; +} +cJSON *cJSON_CreateDoubleArray(double *numbers, int count) +{ + int i; + cJSON *n = 0, *p = 0, *a = cJSON_CreateArray(); + for (i = 0; a && i < count; i++) + { + n = cJSON_CreateDouble((long double)numbers[i], -1); + if (!i) + a->child = n; + else + suffix_object(p, n); + p = n; + } + return a; +} +cJSON *cJSON_CreateStringArray(const char **strings, int count) +{ + int i; + cJSON *n = 0, *p = 0, *a = cJSON_CreateArray(); + for (i = 0; a && i < count; i++) + { + n = cJSON_CreateString(strings[i]); + if (!i) + a->child = n; + else + suffix_object(p, n); + p = n; + } + return a; +} + diff --git a/sop-sdk/sdk-c++/thirdparty/CJsonObject/cJSON.h b/sop-sdk/sdk-c++/thirdparty/CJsonObject/cJSON.h new file mode 100644 index 00000000..a94ced54 --- /dev/null +++ b/sop-sdk/sdk-c++/thirdparty/CJsonObject/cJSON.h @@ -0,0 +1,151 @@ +/* + Copyright (c) 2009 Dave Gamble + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. + */ + +#ifndef cJSON__h +#define cJSON__h + +#include + +typedef int int32; +typedef unsigned int uint32; +#ifndef _WIN32 + #if __WORDSIZE == 64 + typedef long int64; + typedef unsigned long uint64; + #endif +#else + typedef long long int64; + typedef unsigned long long uint64; +#endif + + +#ifdef __cplusplus +extern "C" +{ +#endif + +/* cJSON Types: */ +#define cJSON_False 0 +#define cJSON_True 1 +#define cJSON_NULL 2 +#define cJSON_Int 3 +#define cJSON_Double 4 +#define cJSON_String 5 +#define cJSON_Array 6 +#define cJSON_Object 7 + +#define cJSON_IsReference 256 + +/* The cJSON structure: */ +typedef struct cJSON +{ + struct cJSON *next, *prev; /* next/prev allow you to walk array/object chains. Alternatively, use GetArraySize/GetArrayItem/GetObjectItem */ + struct cJSON *child; /* An array or object item will have a child pointer pointing to a chain of the items in the array/object. */ + + int type; /* The type of the item, as above. */ + + char *valuestring; /* The item's string, if type==cJSON_String */ + int64 valueint; /* The item's number, if type==cJSON_Number */ + double valuedouble; /* The item's number, if type==cJSON_Number */ + int sign; /* sign of valueint, 1(unsigned), -1(signed) */ + + char *string; /* The item's name string, if this item is the child of, or is in the list of subitems of an object. */ +} cJSON; + +typedef struct cJSON_Hooks +{ + void *(*malloc_fn)(size_t sz); + void (*free_fn)(void *ptr); +} cJSON_Hooks; + +/* Supply malloc, realloc and free functions to cJSON */ +extern void cJSON_InitHooks(cJSON_Hooks* hooks); + +/* Supply a block of JSON, and this returns a cJSON object you can interrogate. Call cJSON_Delete when finished. */ +extern cJSON *cJSON_Parse(const char *value); +/* Render a cJSON entity to text for transfer/storage. Free the char* when finished. */ +extern char *cJSON_Print(cJSON *item); +/* Render a cJSON entity to text for transfer/storage without any formatting. Free the char* when finished. */ +extern char *cJSON_PrintUnformatted(cJSON *item); +/* Delete a cJSON entity and all subentities. */ +extern void cJSON_Delete(cJSON *c); + +/* Returns the number of items in an array (or object). */ +extern int cJSON_GetArraySize(cJSON *array); +/* Retrieve item number "item" from array "array". Returns NULL if unsuccessful. */ +extern cJSON *cJSON_GetArrayItem(cJSON *array, int item); +/* Get item "string" from object. Case insensitive. */ +extern cJSON *cJSON_GetObjectItem(cJSON *object, const char *string); + +/* For analysing failed parses. This returns a pointer to the parse error. You'll probably need to look a few chars back to make sense of it. Defined when cJSON_Parse() returns 0. 0 when cJSON_Parse() succeeds. */ +extern const char *cJSON_GetErrorPtr(); + +/* These calls create a cJSON item of the appropriate type. */ +extern cJSON *cJSON_CreateNull(); +extern cJSON *cJSON_CreateTrue(); +extern cJSON *cJSON_CreateFalse(); +extern cJSON *cJSON_CreateBool(int b); +extern cJSON *cJSON_CreateDouble(double num, int sign); +extern cJSON *cJSON_CreateInt(uint64 num, int sign); +extern cJSON *cJSON_CreateString(const char *string); +extern cJSON *cJSON_CreateArray(); +extern cJSON *cJSON_CreateObject(); + +/* These utilities create an Array of count items. */ +extern cJSON *cJSON_CreateIntArray(int *numbers, int sign, int count); +extern cJSON *cJSON_CreateFloatArray(float *numbers, int count); +extern cJSON *cJSON_CreateDoubleArray(double *numbers, int count); +extern cJSON *cJSON_CreateStringArray(const char **strings, int count); + +/* Append item to the specified array/object. */ +extern void cJSON_AddItemToArray(cJSON *array, cJSON *item); +extern void cJSON_AddItemToArrayHead(cJSON *array, cJSON *item); /* add by Bwar on 2015-01-28 */ +extern void cJSON_AddItemToObject(cJSON *object, const char *string, + cJSON *item); +/* Append reference to item to the specified array/object. Use this when you want to add an existing cJSON to a new cJSON, but don't want to corrupt your existing cJSON. */ +extern void cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item); +extern void cJSON_AddItemReferenceToObject(cJSON *object, const char *string, + cJSON *item); + +/* Remove/Detatch items from Arrays/Objects. */ +extern cJSON *cJSON_DetachItemFromArray(cJSON *array, int which); +extern void cJSON_DeleteItemFromArray(cJSON *array, int which); +extern cJSON *cJSON_DetachItemFromObject(cJSON *object, const char *string); +extern void cJSON_DeleteItemFromObject(cJSON *object, const char *string); + +/* Update array items. */ +extern void cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem); +extern void cJSON_ReplaceItemInObject(cJSON *object, const char *string, + cJSON *newitem); + +#define cJSON_AddNullToObject(object,name) cJSON_AddItemToObject(object, name, cJSON_CreateNull()) +#define cJSON_AddTrueToObject(object,name) cJSON_AddItemToObject(object, name, cJSON_CreateTrue()) +#define cJSON_AddFalseToObject(object,name) cJSON_AddItemToObject(object, name, cJSON_CreateFalse()) +#define cJSON_AddNumberToObject(object,name,n) cJSON_AddItemToObject(object, name, cJSON_CreateNumber(n)) +#define cJSON_AddStringToObject(object,name,s) cJSON_AddItemToObject(object, name, cJSON_CreateString(s)) + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/sop-sdk/sdk-c++/thirdparty/base64/base64.cpp b/sop-sdk/sdk-c++/thirdparty/base64/base64.cpp new file mode 100644 index 00000000..a0c583d6 --- /dev/null +++ b/sop-sdk/sdk-c++/thirdparty/base64/base64.cpp @@ -0,0 +1,103 @@ +// +// base64.cpp +// CPPWork +// +// Created by cocoa on 16/8/5. +// Copyright © 2016年 cc. All rights reserved. +// + +#include "base64.h" + +static const std::string base64_chars = + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz" + "0123456789+/"; + + +static inline bool is_base64(unsigned char c) { + return (isalnum(c) || (c == '+') || (c == '/')); +} + +std::string base64_encode(unsigned char const* bytes_to_encode, unsigned int in_len) { + std::string ret; + int i = 0; + int j = 0; + unsigned char char_array_3[3]; + unsigned char char_array_4[4]; + + while (in_len--) { + char_array_3[i++] = *(bytes_to_encode++); + if (i == 3) { + char_array_4[0] = (char_array_3[0] & 0xfc) >> 2; + char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4); + char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6); + char_array_4[3] = char_array_3[2] & 0x3f; + + for(i = 0; (i <4) ; i++) + ret += base64_chars[char_array_4[i]]; + i = 0; + } + } + + if (i) + { + for(j = i; j < 3; j++) + char_array_3[j] = '\0'; + + char_array_4[0] = (char_array_3[0] & 0xfc) >> 2; + char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4); + char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6); + char_array_4[3] = char_array_3[2] & 0x3f; + + for (j = 0; (j < i + 1); j++) + ret += base64_chars[char_array_4[j]]; + + while((i++ < 3)) + ret += '='; + + } + + return ret; + +} +std::string base64_decode(std::string const& encoded_string) { + int in_len = encoded_string.size(); + int i = 0; + int j = 0; + int in_ = 0; + unsigned char char_array_4[4], char_array_3[3]; + std::string ret; + + while (in_len-- && ( encoded_string[in_] != '=') && is_base64(encoded_string[in_])) { + char_array_4[i++] = encoded_string[in_]; in_++; + if (i ==4) { + for (i = 0; i <4; i++) + char_array_4[i] = base64_chars.find(char_array_4[i]); + + char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4); + char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2); + char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3]; + + for (i = 0; (i < 3); i++) + ret += char_array_3[i]; + i = 0; + } + } + + if (i) { + for (j = i; j <4; j++) + char_array_4[j] = 0; + + for (j = 0; j <4; j++) + char_array_4[j] = base64_chars.find(char_array_4[j]); + + char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4); + char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2); + char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3]; + + for (j = 0; (j < i - 1); j++) ret += char_array_3[j]; + } + + return ret; +} + diff --git a/sop-sdk/sdk-c++/thirdparty/base64/base64.h b/sop-sdk/sdk-c++/thirdparty/base64/base64.h new file mode 100755 index 00000000..19e29cfd --- /dev/null +++ b/sop-sdk/sdk-c++/thirdparty/base64/base64.h @@ -0,0 +1,10 @@ +#ifndef base64_h +#define base64_h + +#include + +std::string base64_encode(unsigned char const* bytes_to_encode, unsigned int in_len); +std::string base64_decode(std::string const& encoded_string); + + +#endif /* base64_h */ \ No newline at end of file diff --git a/sop-sdk/sdk-rust/readme.md b/sop-sdk/sdk-rust/readme.md new file mode 100644 index 00000000..a0d0632d --- /dev/null +++ b/sop-sdk/sdk-rust/readme.md @@ -0,0 +1,5 @@ +# sdk for rust + +使用方式见:`sdk-test/src/main.rs` + +封装步骤参考 `sdk/src/request/memberinfoget.rs` 和 `sdk/src/response/memberinfoget.rs` \ No newline at end of file diff --git a/sop-sdk/sdk-rust/sdk-test/.gitignore b/sop-sdk/sdk-rust/sdk-test/.gitignore new file mode 100644 index 00000000..ea8c4bf7 --- /dev/null +++ b/sop-sdk/sdk-rust/sdk-test/.gitignore @@ -0,0 +1 @@ +/target diff --git a/sop-sdk/sdk-rust/sdk-test/Cargo.lock b/sop-sdk/sdk-rust/sdk-test/Cargo.lock new file mode 100644 index 00000000..36a847ba --- /dev/null +++ b/sop-sdk/sdk-rust/sdk-test/Cargo.lock @@ -0,0 +1,1558 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "aho-corasick" +version = "0.7.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "043164d8ba5c4c3035fec9bbee8647c0261d788f3474306f93bb65901cae0e86" +dependencies = [ + "memchr", +] + +[[package]] +name = "arc-swap" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d25d88fd6b8041580a654f9d0c581a047baee2b3efee13275f2fc392fc75034" + +[[package]] +name = "autocfg" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d49d90015b3c36167a20fe2810c5cd875ad504b39cff3d4eae7977e6b7c1cb2" + +[[package]] +name = "autocfg" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d" + +[[package]] +name = "base64" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3441f0f7b02788e948e47f457ca01f1d7e6d92c693bc132c22b087d3141c03ff" + +[[package]] +name = "bitflags" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" + +[[package]] +name = "block-buffer" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" +dependencies = [ + "generic-array", +] + +[[package]] +name = "bumpalo" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e8c087f005730276d1096a652e92a8bacee2e2472bcc9715a74d2bec38b5820" + +[[package]] +name = "byteorder" +version = "1.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de" + +[[package]] +name = "bytes" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "118cf036fbb97d0816e3c34b2d7a1e8cfc60f68fcf63d550ddbe9bd5f59c213b" +dependencies = [ + "loom", +] + +[[package]] +name = "cc" +version = "1.0.56" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77c1f1d60091c1b73e2b1f4560ab419204b178e625fa945ded7b660becd2bd46" + +[[package]] +name = "cfg-if" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" + +[[package]] +name = "chrono" +version = "0.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80094f509cf8b5ae86a4966a39b3ff66cd7e2a3e594accec3743ff3fabeab5b2" +dependencies = [ + "num-integer", + "num-traits", + "time", +] + +[[package]] +name = "core-foundation" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57d24c7a13c43e870e37c1556b74555437870a04514f7685f5b354e090567171" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3a71ab494c0b5b860bdc8407ae08978052417070c2ced38573a9157ad75b8ac" + +[[package]] +name = "cpuid-bool" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d375c433320f6c5057ae04a04376eef4d04ce2801448cf8863a78da99107be4" + +[[package]] +name = "digest" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" +dependencies = [ + "generic-array", +] + +[[package]] +name = "dtoa" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "134951f4028bdadb9b84baf4232681efbf277da25144b9b0ad65df75946c422b" + +[[package]] +name = "encoding_rs" +version = "0.8.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8ac63f94732332f44fe654443c46f6375d1939684c17b0afb6cb56b0456e171" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + +[[package]] +name = "fuchsia-cprng" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" + +[[package]] +name = "fuchsia-zircon" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82" +dependencies = [ + "bitflags", + "fuchsia-zircon-sys", +] + +[[package]] +name = "fuchsia-zircon-sys" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" + +[[package]] +name = "futures-channel" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f366ad74c28cca6ba456d95e6422883cfb4b252a83bed929c83abfdbbf2967d5" +dependencies = [ + "futures-core", +] + +[[package]] +name = "futures-core" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59f5fff90fd5d971f936ad674802482ba441b6f09ba5e15fd8b39145582ca399" + +[[package]] +name = "futures-io" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de27142b013a8e869c14957e6d2edeef89e97c289e69d042ee3a49acd8b51789" + +[[package]] +name = "futures-sink" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f2032893cb734c7a05d85ce0cc8b8c4075278e93b24b66f9de99d6eb0fa8acc" + +[[package]] +name = "futures-task" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdb66b5f09e22019b1ab0830f7785bcea8e7a42148683f99214f73f8ec21a626" +dependencies = [ + "once_cell", +] + +[[package]] +name = "futures-util" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8764574ff08b701a084482c3c7031349104b07ac897393010494beaa18ce32c6" +dependencies = [ + "futures-core", + "futures-io", + "futures-task", + "memchr", + "pin-project", + "pin-utils", + "slab", +] + +[[package]] +name = "gcc" +version = "0.3.55" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f5f3913fa0bfe7ee1fd8248b6b9f42a5af4b9d65ec2dd2c3c26132b950ecfc2" + +[[package]] +name = "generator" +version = "0.6.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "add72f17bb81521258fcc8a7a3245b1e184e916bfbe34f0ea89558f440df5c68" +dependencies = [ + "cc", + "libc", + "log", + "rustc_version", + "winapi 0.3.9", +] + +[[package]] +name = "generic-array" +version = "0.14.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac746a5f3bbfdadd6106868134545e684693d54d9d44f6e9588a7d54af0bf980" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7abc8dd8451921606d809ba32e95b6111925cd2906060d2dcc29c070220503eb" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "h2" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79b7246d7e4b979c03fa093da39cfb3617a96bbeee6310af63991668d7e843ff" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http", + "indexmap", + "log", + "slab", + "tokio", + "tokio-util", +] + +[[package]] +name = "hermit-abi" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9586eedd4ce6b3c498bc3b4dd92fc9f11166aa908a914071953768066c67909" +dependencies = [ + "libc", +] + +[[package]] +name = "http" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28d569972648b2c512421b5f2a405ad6ac9666547189d0c5477a3f200f3e02f9" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http-body" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13d5ff830006f7646652e057693569bfe0d51760c0085a071769d142a205111b" +dependencies = [ + "bytes", + "http", +] + +[[package]] +name = "httparse" +version = "1.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd179ae861f0c2e53da70d892f5f3029f9594be0c41dc5269cd371691b1dc2f9" + +[[package]] +name = "hyper" +version = "0.13.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6e7655b9594024ad0ee439f3b5a7299369dc2a3f459b47c696f9ff676f9aa1f" +dependencies = [ + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "httparse", + "itoa", + "log", + "pin-project", + "socket2", + "time", + "tokio", + "tower-service", + "want", +] + +[[package]] +name = "hyper-tls" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3adcd308402b9553630734e9c36b77a7e48b3821251ca2493e8cd596763aafaa" +dependencies = [ + "bytes", + "hyper", + "native-tls", + "tokio", + "tokio-tls", +] + +[[package]] +name = "idna" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02e2673c30ee86b5b96a9cb52ad15718aa1f966f5ab9ad54a8b95d5ca33120a9" +dependencies = [ + "matches", + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "indexmap" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c398b2b113b55809ceb9ee3e753fcbac793f1956663f3c36549c1346015c2afe" +dependencies = [ + "autocfg 1.0.0", +] + +[[package]] +name = "iovec" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2b3ea6ff95e175473f8ffe6a7eb7c00d054240321b84c57051175fe3c1e075e" +dependencies = [ + "libc", +] + +[[package]] +name = "itoa" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc6f3ad7b9d11a0c00842ff8de1b60ee58661048eb8049ed33c73594f359d7e6" + +[[package]] +name = "js-sys" +version = "0.3.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4b9172132a62451e56142bff9afc91c8e4a4500aa5b847da36815b63bfda916" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "kernel32-sys" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" +dependencies = [ + "winapi 0.2.8", + "winapi-build", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +dependencies = [ + "spin", +] + +[[package]] +name = "libc" +version = "0.2.71" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9457b06509d27052635f90d6466700c65095fdf75409b3fbdd903e988b886f49" + +[[package]] +name = "libm" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7d73b3f436185384286bd8098d17ec07c9a7d2388a6599f824d8502b529702a" + +[[package]] +name = "log" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "loom" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ecc775857611e1df29abba5c41355cdf540e7e9d4acfdf0f355eefee82330b7" +dependencies = [ + "cfg-if", + "generator", + "scoped-tls", +] + +[[package]] +name = "matches" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08" + +[[package]] +name = "memchr" +version = "2.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3728d817d99e5ac407411fa471ff9800a778d88a24685968b36824eaf4bee400" + +[[package]] +name = "mime" +version = "0.3.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" + +[[package]] +name = "mime_guess" +version = "2.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2684d4c2e97d99848d30b324b00c8fcc7e5c897b7cbb5819b09e7c90e8baf212" +dependencies = [ + "mime", + "unicase", +] + +[[package]] +name = "mio" +version = "0.6.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fce347092656428bc8eaf6201042cb551b8d67855af7374542a92a0fbfcac430" +dependencies = [ + "cfg-if", + "fuchsia-zircon", + "fuchsia-zircon-sys", + "iovec", + "kernel32-sys", + "libc", + "log", + "miow 0.2.1", + "net2", + "slab", + "winapi 0.2.8", +] + +[[package]] +name = "mio-named-pipes" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5e374eff525ce1c5b7687c4cef63943e7686524a387933ad27ca7ec43779cb3" +dependencies = [ + "log", + "mio", + "miow 0.3.5", + "winapi 0.3.9", +] + +[[package]] +name = "mio-uds" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afcb699eb26d4332647cc848492bbc15eafb26f08d0304550d5aa1f612e066f0" +dependencies = [ + "iovec", + "libc", + "mio", +] + +[[package]] +name = "miow" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c1f2f3b1cf331de6896aabf6e9d55dca90356cc9960cca7eaaf408a355ae919" +dependencies = [ + "kernel32-sys", + "net2", + "winapi 0.2.8", + "ws2_32-sys", +] + +[[package]] +name = "miow" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07b88fb9795d4d36d62a012dfbf49a8f5cf12751f36d31a9dbe66d528e58979e" +dependencies = [ + "socket2", + "winapi 0.3.9", +] + +[[package]] +name = "native-tls" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b0d88c06fe90d5ee94048ba40409ef1d9315d86f6f38c2efdaad4fb50c58b2d" +dependencies = [ + "lazy_static", + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", +] + +[[package]] +name = "net2" +version = "0.2.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ba7c918ac76704fb42afcbbb43891e72731f3dcca3bef2a19786297baf14af7" +dependencies = [ + "cfg-if", + "libc", + "winapi 0.3.9", +] + +[[package]] +name = "num-bigint" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "090c7f9998ee0ff65aa5b723e4009f7b217707f1fb5ea551329cc4d6231fb304" +dependencies = [ + "autocfg 1.0.0", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-bigint-dig" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3d03c330f9f7a2c19e3c0b42698e48141d0809c78cd9b6219f85bd7d7e892aa" +dependencies = [ + "autocfg 0.1.7", + "byteorder", + "lazy_static", + "libm", + "num-integer", + "num-iter", + "num-traits", + "rand 0.7.3", + "serde", + "smallvec", + "zeroize", +] + +[[package]] +name = "num-integer" +version = "0.1.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d59457e662d541ba17869cf51cf177c0b5f0cbf476c66bdc90bf1edac4f875b" +dependencies = [ + "autocfg 1.0.0", + "num-traits", +] + +[[package]] +name = "num-iter" +version = "0.1.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a6e6b7c748f995c4c29c5f5ae0248536e04a5739927c74ec0fa564805094b9f" +dependencies = [ + "autocfg 1.0.0", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac267bcc07f48ee5f8935ab0d24f316fb722d7a1292e2913f0cc196b29ffd611" +dependencies = [ + "autocfg 1.0.0", +] + +[[package]] +name = "num_cpus" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "once_cell" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b631f7e854af39a1739f401cf34a8a013dfe09eac4fa4dba91e9768bd28168d" + +[[package]] +name = "opaque-debug" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" + +[[package]] +name = "openssl" +version = "0.10.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d575eff3665419f9b83678ff2815858ad9d11567e082f5ac1814baba4e2bcb4" +dependencies = [ + "bitflags", + "cfg-if", + "foreign-types", + "lazy_static", + "libc", + "openssl-sys", +] + +[[package]] +name = "openssl-probe" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77af24da69f9d9341038eba93a073b1fdaaa1b788221b00a69bce9e762cb32de" + +[[package]] +name = "openssl-sys" +version = "0.9.58" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a842db4709b604f0fe5d1170ae3565899be2ad3d9cbc72dedc789ac0511f78de" +dependencies = [ + "autocfg 1.0.0", + "cc", + "libc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "pem" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59698ea79df9bf77104aefd39cc3ec990cb9693fb59c3b0a70ddf2646fdffb4b" +dependencies = [ + "base64", + "once_cell", + "regex", +] + +[[package]] +name = "percent-encoding" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" + +[[package]] +name = "pin-project" +version = "0.4.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12e3a6cdbfe94a5e4572812a0201f8c0ed98c1c452c7b8563ce2276988ef9c17" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "0.4.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a0ffd45cf79d88737d7cc85bfd5d2894bee1139b356e616fe85dc389c61aaf7" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "pin-project-lite" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282adbf10f2698a7a77f8e983a74b2d18176c19a7fd32a45446139ae7b02b715" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "pkg-config" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05da548ad6865900e60eaba7f589cc0783590a92e940c26953ff81ddbab2d677" + +[[package]] +name = "ppv-lite86" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "237a5ed80e274dbc66f86bd59c1e25edc039660be53194b5fe0a482e0f2612ea" + +[[package]] +name = "proc-macro2" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "beae6331a816b1f65d04c45b078fd8e6c93e8071771f41b8163255bbd8d7c8fa" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "quote" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa563d17ecb180e500da1cfd2b028310ac758de548efdd203e18f283af693f37" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.3.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64ac302d8f83c0c1974bf758f6b041c6c8ada916fbb44a609158ca8b064cc76c" +dependencies = [ + "libc", + "rand 0.4.6", +] + +[[package]] +name = "rand" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "552840b97013b1a26992c11eac34bdd778e464601a4c2054b5f0bff7c6761293" +dependencies = [ + "fuchsia-cprng", + "libc", + "rand_core 0.3.1", + "rdrand", + "winapi 0.3.9", +] + +[[package]] +name = "rand" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" +dependencies = [ + "getrandom", + "libc", + "rand_chacha", + "rand_core 0.5.1", + "rand_hc", +] + +[[package]] +name = "rand_chacha" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" +dependencies = [ + "ppv-lite86", + "rand_core 0.5.1", +] + +[[package]] +name = "rand_core" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" +dependencies = [ + "rand_core 0.4.2", +] + +[[package]] +name = "rand_core" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc" + +[[package]] +name = "rand_core" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rand_hc" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" +dependencies = [ + "rand_core 0.5.1", +] + +[[package]] +name = "rdrand" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2" +dependencies = [ + "rand_core 0.3.1", +] + +[[package]] +name = "redox_syscall" +version = "0.1.56" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84" + +[[package]] +name = "regex" +version = "1.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c3780fcf44b193bc4d09f36d2a3c87b251da4a046c87795a0d35f4f927ad8e6" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", + "thread_local", +] + +[[package]] +name = "regex-syntax" +version = "0.6.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26412eb97c6b088a6997e05f69403a802a92d520de2f8e63c2b65f9e0f47c4e8" + +[[package]] +name = "remove_dir_all" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" +dependencies = [ + "winapi 0.3.9", +] + +[[package]] +name = "reqwest" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b82c9238b305f26f53443e3a4bc8528d64b8d0bee408ec949eb7bf5635ec680" +dependencies = [ + "base64", + "bytes", + "encoding_rs", + "futures-core", + "futures-util", + "http", + "http-body", + "hyper", + "hyper-tls", + "js-sys", + "lazy_static", + "log", + "mime", + "mime_guess", + "native-tls", + "percent-encoding", + "pin-project-lite", + "serde", + "serde_json", + "serde_urlencoded", + "tokio", + "tokio-tls", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "winreg", +] + +[[package]] +name = "rsa" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3648b669b10afeab18972c105e284a7b953a669b0be3514c27f9b17acab2f9cd" +dependencies = [ + "byteorder", + "digest", + "lazy_static", + "num-bigint-dig", + "num-integer", + "num-iter", + "num-traits", + "pem", + "rand 0.7.3", + "sha2", + "simple_asn1", + "subtle", + "thiserror", + "zeroize", +] + +[[package]] +name = "rust-crypto" +version = "0.2.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f76d05d3993fd5f4af9434e8e436db163a12a9d40e1a58a726f27a01dfd12a2a" +dependencies = [ + "gcc", + "libc", + "rand 0.3.23", + "rustc-serialize", + "time", +] + +[[package]] +name = "rustc-serialize" +version = "0.3.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcf128d1287d2ea9d80910b5f1120d0b8eede3fbf1abe91c40d39ea7d51e6fda" + +[[package]] +name = "rustc_version" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" +dependencies = [ + "semver", +] + +[[package]] +name = "ryu" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" + +[[package]] +name = "schannel" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f05ba609c234e60bee0d547fe94a4c7e9da733d1c962cf6e59efa4cd9c8bc75" +dependencies = [ + "lazy_static", + "winapi 0.3.9", +] + +[[package]] +name = "scoped-tls" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "332ffa32bf586782a3efaeb58f127980944bbc8c4d6913a86107ac2a5ab24b28" + +[[package]] +name = "sdk" +version = "0.1.0" +dependencies = [ + "base64", + "chrono", + "reqwest", + "rsa", + "rust-crypto", + "serde", + "serde_json", + "tokio", +] + +[[package]] +name = "sdk-test" +version = "0.1.0" +dependencies = [ + "sdk", +] + +[[package]] +name = "security-framework" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64808902d7d99f78eaddd2b4e2509713babc3dc3c85ad6f4c447680f3c01e535" +dependencies = [ + "bitflags", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17bf11d99252f512695eb468de5516e5cf75455521e69dfe343f3b74e4748405" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "semver" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" +dependencies = [ + "semver-parser", +] + +[[package]] +name = "semver-parser" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" + +[[package]] +name = "serde" +version = "1.0.114" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5317f7588f0a5078ee60ef675ef96735a1442132dc645eb1d12c018620ed8cd3" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.114" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a0be94b04690fbaed37cddffc5c134bf537c8e3329d53e982fe04c374978f8e" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.56" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3433e879a558dde8b5e8feb2a04899cf34fdde1fafb894687e52105fc1162ac3" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "serde_urlencoded" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ec5d77e2d4c73717816afac02670d5c4f534ea95ed430442cad02e7a6e32c97" +dependencies = [ + "dtoa", + "itoa", + "serde", + "url", +] + +[[package]] +name = "sha2" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2933378ddfeda7ea26f48c555bdad8bb446bf8a3d17832dc83e380d444cfb8c1" +dependencies = [ + "block-buffer", + "cfg-if", + "cpuid-bool", + "digest", + "opaque-debug", +] + +[[package]] +name = "signal-hook-registry" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94f478ede9f64724c5d173d7bb56099ec3e2d9fc2774aac65d34b8b890405f41" +dependencies = [ + "arc-swap", + "libc", +] + +[[package]] +name = "simple_asn1" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b25ecba7165254f0c97d6c22a64b1122a03634b18d20a34daf21e18f892e618" +dependencies = [ + "chrono", + "num-bigint", + "num-traits", +] + +[[package]] +name = "slab" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8" + +[[package]] +name = "smallvec" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7cb5678e1615754284ec264d9bb5b4c27d2018577fd90ac0ceb578591ed5ee4" + +[[package]] +name = "socket2" +version = "0.3.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03088793f677dce356f3ccc2edb1b314ad191ab702a5de3faf49304f7e104918" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "winapi 0.3.9", +] + +[[package]] +name = "spin" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" + +[[package]] +name = "subtle" +version = "2.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "502d53007c02d7605a05df1c1a73ee436952781653da5d0bf57ad608f66932c1" + +[[package]] +name = "syn" +version = "1.0.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8d5d96e8cbb005d6959f119f773bfaebb5684296108fb32600c00cde305b2cd" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "synstructure" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b834f2d66f734cb897113e34aaff2f1ab4719ca946f9a7358dba8f8064148701" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "unicode-xid", +] + +[[package]] +name = "tempfile" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a6e24d9338a0a5be79593e2fa15a648add6138caa803e2d5bc782c371732ca9" +dependencies = [ + "cfg-if", + "libc", + "rand 0.7.3", + "redox_syscall", + "remove_dir_all", + "winapi 0.3.9", +] + +[[package]] +name = "thiserror" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7dfdd070ccd8ccb78f4ad66bf1982dc37f620ef696c6b5028fe2ed83dd3d0d08" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd80fc12f73063ac132ac92aceea36734f04a1d93c1240c6944e23a3b8841793" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "thread_local" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d40c6d1b69745a6ec6fb1ca717914848da4b44ae29d9b3080cbee91d72a69b14" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "time" +version = "0.1.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca8a50ef2360fbd1eeb0ecd46795a87a19024eb4b53c5dc916ca1fd95fe62438" +dependencies = [ + "libc", + "winapi 0.3.9", +] + +[[package]] +name = "tinyvec" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53953d2d3a5ad81d9f844a32f14ebb121f50b650cd59d0ee2a07cf13c617efed" + +[[package]] +name = "tokio" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d099fa27b9702bed751524694adbe393e18b36b204da91eb1cbbbbb4a5ee2d58" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "iovec", + "lazy_static", + "libc", + "memchr", + "mio", + "mio-named-pipes", + "mio-uds", + "num_cpus", + "pin-project-lite", + "signal-hook-registry", + "slab", + "tokio-macros", + "winapi 0.3.9", +] + +[[package]] +name = "tokio-macros" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0c3acc6aa564495a0f2e1d59fab677cd7f81a19994cfc7f3ad0e64301560389" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tokio-tls" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a70f4fcd7b3b24fb194f837560168208f669ca8cb70d0c4b862944452396343" +dependencies = [ + "native-tls", + "tokio", +] + +[[package]] +name = "tokio-util" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be8242891f2b6cbef26a2d7e8605133c2c554cd35b3e4948ea892d6d68436499" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "log", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tower-service" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e987b6bf443f4b5b3b6f38704195592cca41c5bb7aedd3c3693c7081f8289860" + +[[package]] +name = "try-lock" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e604eb7b43c06650e854be16a2a03155743d3752dd1c943f6829e26b7a36e382" + +[[package]] +name = "typenum" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "373c8a200f9e67a0c95e62a4f52fbf80c23b4381c05a17845531982fa99e6b33" + +[[package]] +name = "unicase" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6" +dependencies = [ + "version_check", +] + +[[package]] +name = "unicode-bidi" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5" +dependencies = [ + "matches", +] + +[[package]] +name = "unicode-normalization" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fb19cf769fa8c6a80a162df694621ebeb4dafb606470b2b2fce0be40a98a977" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "unicode-xid" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" + +[[package]] +name = "url" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "829d4a8476c35c9bf0bbce5a3b23f4106f79728039b726d292bb93bc106787cb" +dependencies = [ + "idna", + "matches", + "percent-encoding", +] + +[[package]] +name = "vcpkg" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6454029bf181f092ad1b853286f23e2c507d8e8194d01d92da4a55c274a5508c" + +[[package]] +name = "version_check" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5a972e5669d67ba988ce3dc826706fb0a8b01471c088cb0b6110b805cc36aed" + +[[package]] +name = "want" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" +dependencies = [ + "log", + "try-lock", +] + +[[package]] +name = "wasi" +version = "0.9.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" + +[[package]] +name = "wasm-bindgen" +version = "0.2.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a634620115e4a229108b71bde263bb4220c483b3f07f5ba514ee8d15064c4c2" +dependencies = [ + "cfg-if", + "serde", + "serde_json", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e53963b583d18a5aa3aaae4b4c1cb535218246131ba22a71f05b518098571df" +dependencies = [ + "bumpalo", + "lazy_static", + "log", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dba48d66049d2a6cc8488702e7259ab7afc9043ad0dc5448444f46f2a453b362" +dependencies = [ + "cfg-if", + "js-sys", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fcfd5ef6eec85623b4c6e844293d4516470d8f19cd72d0d12246017eb9060b8" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9adff9ee0e94b926ca81b57f57f86d5545cdcb1d259e21ec9bdd95b901754c75" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f7b90ea6c632dd06fd765d44542e234d5e63d9bb917ecd64d79778a13bd79ae" + +[[package]] +name = "web-sys" +version = "0.3.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "863539788676619aac1a23e2df3655e96b32b0e05eb72ca34ba045ad573c625d" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "winapi" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-build" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "winreg" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0120db82e8a1e0b9fb3345a539c478767c0048d842860994d96113d5b667bd69" +dependencies = [ + "winapi 0.3.9", +] + +[[package]] +name = "ws2_32-sys" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e" +dependencies = [ + "winapi 0.2.8", + "winapi-build", +] + +[[package]] +name = "zeroize" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3cbac2ed2ba24cc90f5e06485ac8c7c1e5449fe8911aef4d8877218af021a5b8" +dependencies = [ + "zeroize_derive", +] + +[[package]] +name = "zeroize_derive" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de251eec69fc7c1bc3923403d18ececb929380e016afe103da75f396704f8ca2" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] diff --git a/sop-sdk/sdk-rust/sdk-test/Cargo.toml b/sop-sdk/sdk-rust/sdk-test/Cargo.toml new file mode 100644 index 00000000..cfbadb30 --- /dev/null +++ b/sop-sdk/sdk-rust/sdk-test/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "sdk-test" +version = "0.1.0" +authors = ["thc"] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +# 依赖sdk模块 +sdk = { path = "../sdk"} \ No newline at end of file diff --git a/sop-sdk/sdk-rust/sdk-test/aa.txt b/sop-sdk/sdk-rust/sdk-test/aa.txt new file mode 100644 index 00000000..524527cb --- /dev/null +++ b/sop-sdk/sdk-rust/sdk-test/aa.txt @@ -0,0 +1 @@ +hello你好123 \ No newline at end of file diff --git a/sop-sdk/sdk-rust/sdk-test/bb.txt b/sop-sdk/sdk-rust/sdk-test/bb.txt new file mode 100644 index 00000000..85f8e329 --- /dev/null +++ b/sop-sdk/sdk-rust/sdk-test/bb.txt @@ -0,0 +1 @@ +文件bb的内容 \ No newline at end of file diff --git a/sop-sdk/sdk-rust/sdk-test/src/main.rs b/sop-sdk/sdk-rust/sdk-test/src/main.rs new file mode 100644 index 00000000..8c244ed4 --- /dev/null +++ b/sop-sdk/sdk-rust/sdk-test/src/main.rs @@ -0,0 +1,76 @@ +extern crate sdk; + +use std::collections::HashMap; + +use sdk::client::OpenClient; +use sdk::http::UploadFile; +use sdk::request::BaseRequest; +use sdk::request::memberinfoget::MemberInfoGetRequest; +use sdk::response::memberinfoget::MemberInfoGetResponse; +use std::env; + +fn main() { + // 创建请求客户端 + let client = OpenClient { + // 应用ID + app_id: "2020051325943082302177280", + // 应用私钥 + private_key: "MIICXAIBAAKBgQCHJlAPN+1dCbgc3HiahQkT2W/skecGWOCkSX4CPvEc8oIk6544\nxihEwShHnfrapiQdF2fndv5agrhg4FyOHheST42L5MnCk+4Km+mWm5GDvmFS7Sa2\naZ5o3regY0MUoJ7D74dYjE3UYFuTujAXiXjGpAwa9qOcKotov5LCkSfUeQIDAQAB\nAoGAB1cyw8LYRQSHQCUO9Wiaq730jPNHSrJW4EGAIz/XMYjv/fCgx0lnDEX4CbzI\nUGoz/bME4R721YRyXoutJ0h14/cGrt/TEn/TMI0xnISzJHr8VSlyBkQEdfO/W3LO\nqjs/UYq2Bz4+kJROJHreM+7d5hiIWLzLBlyI8cSU92ySmHECQQDwju2SoRu88kQP\n1qr4seZyKQa8DHTVyCoa6LtPLXyJsdgWgY4KyqJHwMUumEC2Zhhu833CR0ZXbfta\nuQDmwAVJAkEAj9M225jrPasaD5vPO7thxtEqgV4F/ZyNKH0Z8bDH27KaKkQ+8GMt\nkxwKVckZXs2bMvg/6tCiDZkWAxawNrvFsQJBANmTrPWOmpQPW9gnhYRjA9gFm33C\nlno2DT9BeQloTtgL7zKMA3lnRdg4VyCJvR48waS4vupVpR228D1iT5pl22ECQF1M\nJUzkcM0rPheb+h2EW1QOgWU0Keyvbj4ykO7gv3T78dezN6TWoUzJpsapUiTWeXPh\n6AyZ1FW/1bChOiP3QLECQGAbObmsYlN0bjzPYChwWYeYjErXuv51a44GZCNWinFw\nGGiHU9ZAqF8RzmBVW4htwj0j/Yry/V1Sp0uoP0zu3uA=", + // 请求地址 + url: "http://localhost:7071/prod/gw68uy85", + }; + // 业务参数 + let mut biz_model = HashMap::new(); + biz_model.insert("name", "jim".to_string()); + biz_model.insert("address", "xx".to_string()); + biz_model.insert("age", "22".to_string()); + + let mut files = vec![]; + + // 添加上传文件 + /*let mod_dir = env::current_dir().unwrap().display().to_string(); + files = vec![ + UploadFile { name:"file1", path: format!("{}/{}", &mod_dir, "aa.txt")}, + UploadFile { name:"file2", path: format!("{}/{}", &mod_dir, "bb.txt") }, + ];*/ + + + // 创建请求,设置业务参数 + let request = MemberInfoGetRequest { + base: BaseRequest { biz_model: biz_model, files: files } + }; + + // 发送请求 + let response:MemberInfoGetResponse = client.execute(request); + + // 成功 + if response.sub_code.len() == 0 { + println!("resp:{:#?}", response) + } else { + println!("error:{:#?}", response) + } +} + +#[cfg(test)] +mod tests { + use sdk::sign::{HashType, SignUtil}; + use sdk::http::HttpTool; + use std::collections::HashMap; + + #[test] + fn it_works() { + let content = "123"; + let private_key = "MIICXAIBAAKBgQCHJlAPN+1dCbgc3HiahQkT2W/skecGWOCkSX4CPvEc8oIk6544\nxihEwShHnfrapiQdF2fndv5agrhg4FyOHheST42L5MnCk+4Km+mWm5GDvmFS7Sa2\naZ5o3regY0MUoJ7D74dYjE3UYFuTujAXiXjGpAwa9qOcKotov5LCkSfUeQIDAQAB\nAoGAB1cyw8LYRQSHQCUO9Wiaq730jPNHSrJW4EGAIz/XMYjv/fCgx0lnDEX4CbzI\nUGoz/bME4R721YRyXoutJ0h14/cGrt/TEn/TMI0xnISzJHr8VSlyBkQEdfO/W3LO\nqjs/UYq2Bz4+kJROJHreM+7d5hiIWLzLBlyI8cSU92ySmHECQQDwju2SoRu88kQP\n1qr4seZyKQa8DHTVyCoa6LtPLXyJsdgWgY4KyqJHwMUumEC2Zhhu833CR0ZXbfta\nuQDmwAVJAkEAj9M225jrPasaD5vPO7thxtEqgV4F/ZyNKH0Z8bDH27KaKkQ+8GMt\nkxwKVckZXs2bMvg/6tCiDZkWAxawNrvFsQJBANmTrPWOmpQPW9gnhYRjA9gFm33C\nlno2DT9BeQloTtgL7zKMA3lnRdg4VyCJvR48waS4vupVpR228D1iT5pl22ECQF1M\nJUzkcM0rPheb+h2EW1QOgWU0Keyvbj4ykO7gv3T78dezN6TWoUzJpsapUiTWeXPh\n6AyZ1FW/1bChOiP3QLECQGAbObmsYlN0bjzPYChwWYeYjErXuv51a44GZCNWinFw\nGGiHU9ZAqF8RzmBVW4htwj0j/Yry/V1Sp0uoP0zu3uA="; + let sign = SignUtil::rsa_sign(content, private_key, HashType::Sha256); + println!("sign:{}", sign); + } + + #[test] + fn test_http() { + let mut map = HashMap::new(); + map.insert("aaa", "bbb"); + let response = HttpTool::get("http://baidu.com", &map, &HashMap::new()); + println!("response:{:#?}", response); + } + +} \ No newline at end of file diff --git a/sop-sdk/sdk-rust/sdk/.gitignore b/sop-sdk/sdk-rust/sdk/.gitignore new file mode 100644 index 00000000..ea8c4bf7 --- /dev/null +++ b/sop-sdk/sdk-rust/sdk/.gitignore @@ -0,0 +1 @@ +/target diff --git a/sop-sdk/sdk-rust/sdk/Cargo.lock b/sop-sdk/sdk-rust/sdk/Cargo.lock new file mode 100644 index 00000000..1f2c6d7f --- /dev/null +++ b/sop-sdk/sdk-rust/sdk/Cargo.lock @@ -0,0 +1,1551 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "aho-corasick" +version = "0.7.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "043164d8ba5c4c3035fec9bbee8647c0261d788f3474306f93bb65901cae0e86" +dependencies = [ + "memchr", +] + +[[package]] +name = "arc-swap" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d25d88fd6b8041580a654f9d0c581a047baee2b3efee13275f2fc392fc75034" + +[[package]] +name = "autocfg" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d49d90015b3c36167a20fe2810c5cd875ad504b39cff3d4eae7977e6b7c1cb2" + +[[package]] +name = "autocfg" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d" + +[[package]] +name = "base64" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3441f0f7b02788e948e47f457ca01f1d7e6d92c693bc132c22b087d3141c03ff" + +[[package]] +name = "bitflags" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" + +[[package]] +name = "block-buffer" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" +dependencies = [ + "generic-array", +] + +[[package]] +name = "bumpalo" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e8c087f005730276d1096a652e92a8bacee2e2472bcc9715a74d2bec38b5820" + +[[package]] +name = "byteorder" +version = "1.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de" + +[[package]] +name = "bytes" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "118cf036fbb97d0816e3c34b2d7a1e8cfc60f68fcf63d550ddbe9bd5f59c213b" +dependencies = [ + "loom", +] + +[[package]] +name = "cc" +version = "1.0.56" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77c1f1d60091c1b73e2b1f4560ab419204b178e625fa945ded7b660becd2bd46" + +[[package]] +name = "cfg-if" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" + +[[package]] +name = "chrono" +version = "0.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80094f509cf8b5ae86a4966a39b3ff66cd7e2a3e594accec3743ff3fabeab5b2" +dependencies = [ + "num-integer", + "num-traits", + "time", +] + +[[package]] +name = "core-foundation" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57d24c7a13c43e870e37c1556b74555437870a04514f7685f5b354e090567171" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3a71ab494c0b5b860bdc8407ae08978052417070c2ced38573a9157ad75b8ac" + +[[package]] +name = "cpuid-bool" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d375c433320f6c5057ae04a04376eef4d04ce2801448cf8863a78da99107be4" + +[[package]] +name = "digest" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" +dependencies = [ + "generic-array", +] + +[[package]] +name = "dtoa" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "134951f4028bdadb9b84baf4232681efbf277da25144b9b0ad65df75946c422b" + +[[package]] +name = "encoding_rs" +version = "0.8.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8ac63f94732332f44fe654443c46f6375d1939684c17b0afb6cb56b0456e171" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + +[[package]] +name = "fuchsia-cprng" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" + +[[package]] +name = "fuchsia-zircon" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82" +dependencies = [ + "bitflags", + "fuchsia-zircon-sys", +] + +[[package]] +name = "fuchsia-zircon-sys" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" + +[[package]] +name = "futures-channel" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f366ad74c28cca6ba456d95e6422883cfb4b252a83bed929c83abfdbbf2967d5" +dependencies = [ + "futures-core", +] + +[[package]] +name = "futures-core" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59f5fff90fd5d971f936ad674802482ba441b6f09ba5e15fd8b39145582ca399" + +[[package]] +name = "futures-io" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de27142b013a8e869c14957e6d2edeef89e97c289e69d042ee3a49acd8b51789" + +[[package]] +name = "futures-sink" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f2032893cb734c7a05d85ce0cc8b8c4075278e93b24b66f9de99d6eb0fa8acc" + +[[package]] +name = "futures-task" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdb66b5f09e22019b1ab0830f7785bcea8e7a42148683f99214f73f8ec21a626" +dependencies = [ + "once_cell", +] + +[[package]] +name = "futures-util" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8764574ff08b701a084482c3c7031349104b07ac897393010494beaa18ce32c6" +dependencies = [ + "futures-core", + "futures-io", + "futures-task", + "memchr", + "pin-project", + "pin-utils", + "slab", +] + +[[package]] +name = "gcc" +version = "0.3.55" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f5f3913fa0bfe7ee1fd8248b6b9f42a5af4b9d65ec2dd2c3c26132b950ecfc2" + +[[package]] +name = "generator" +version = "0.6.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "add72f17bb81521258fcc8a7a3245b1e184e916bfbe34f0ea89558f440df5c68" +dependencies = [ + "cc", + "libc", + "log", + "rustc_version", + "winapi 0.3.9", +] + +[[package]] +name = "generic-array" +version = "0.14.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac746a5f3bbfdadd6106868134545e684693d54d9d44f6e9588a7d54af0bf980" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7abc8dd8451921606d809ba32e95b6111925cd2906060d2dcc29c070220503eb" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "h2" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79b7246d7e4b979c03fa093da39cfb3617a96bbeee6310af63991668d7e843ff" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http", + "indexmap", + "log", + "slab", + "tokio", + "tokio-util", +] + +[[package]] +name = "hermit-abi" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9586eedd4ce6b3c498bc3b4dd92fc9f11166aa908a914071953768066c67909" +dependencies = [ + "libc", +] + +[[package]] +name = "http" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28d569972648b2c512421b5f2a405ad6ac9666547189d0c5477a3f200f3e02f9" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http-body" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13d5ff830006f7646652e057693569bfe0d51760c0085a071769d142a205111b" +dependencies = [ + "bytes", + "http", +] + +[[package]] +name = "httparse" +version = "1.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd179ae861f0c2e53da70d892f5f3029f9594be0c41dc5269cd371691b1dc2f9" + +[[package]] +name = "hyper" +version = "0.13.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6e7655b9594024ad0ee439f3b5a7299369dc2a3f459b47c696f9ff676f9aa1f" +dependencies = [ + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "httparse", + "itoa", + "log", + "pin-project", + "socket2", + "time", + "tokio", + "tower-service", + "want", +] + +[[package]] +name = "hyper-tls" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3adcd308402b9553630734e9c36b77a7e48b3821251ca2493e8cd596763aafaa" +dependencies = [ + "bytes", + "hyper", + "native-tls", + "tokio", + "tokio-tls", +] + +[[package]] +name = "idna" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02e2673c30ee86b5b96a9cb52ad15718aa1f966f5ab9ad54a8b95d5ca33120a9" +dependencies = [ + "matches", + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "indexmap" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c398b2b113b55809ceb9ee3e753fcbac793f1956663f3c36549c1346015c2afe" +dependencies = [ + "autocfg 1.0.0", +] + +[[package]] +name = "iovec" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2b3ea6ff95e175473f8ffe6a7eb7c00d054240321b84c57051175fe3c1e075e" +dependencies = [ + "libc", +] + +[[package]] +name = "itoa" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc6f3ad7b9d11a0c00842ff8de1b60ee58661048eb8049ed33c73594f359d7e6" + +[[package]] +name = "js-sys" +version = "0.3.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4b9172132a62451e56142bff9afc91c8e4a4500aa5b847da36815b63bfda916" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "kernel32-sys" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" +dependencies = [ + "winapi 0.2.8", + "winapi-build", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +dependencies = [ + "spin", +] + +[[package]] +name = "libc" +version = "0.2.71" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9457b06509d27052635f90d6466700c65095fdf75409b3fbdd903e988b886f49" + +[[package]] +name = "libm" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7d73b3f436185384286bd8098d17ec07c9a7d2388a6599f824d8502b529702a" + +[[package]] +name = "log" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "loom" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ecc775857611e1df29abba5c41355cdf540e7e9d4acfdf0f355eefee82330b7" +dependencies = [ + "cfg-if", + "generator", + "scoped-tls", +] + +[[package]] +name = "matches" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08" + +[[package]] +name = "memchr" +version = "2.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3728d817d99e5ac407411fa471ff9800a778d88a24685968b36824eaf4bee400" + +[[package]] +name = "mime" +version = "0.3.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" + +[[package]] +name = "mime_guess" +version = "2.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2684d4c2e97d99848d30b324b00c8fcc7e5c897b7cbb5819b09e7c90e8baf212" +dependencies = [ + "mime", + "unicase", +] + +[[package]] +name = "mio" +version = "0.6.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fce347092656428bc8eaf6201042cb551b8d67855af7374542a92a0fbfcac430" +dependencies = [ + "cfg-if", + "fuchsia-zircon", + "fuchsia-zircon-sys", + "iovec", + "kernel32-sys", + "libc", + "log", + "miow 0.2.1", + "net2", + "slab", + "winapi 0.2.8", +] + +[[package]] +name = "mio-named-pipes" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5e374eff525ce1c5b7687c4cef63943e7686524a387933ad27ca7ec43779cb3" +dependencies = [ + "log", + "mio", + "miow 0.3.5", + "winapi 0.3.9", +] + +[[package]] +name = "mio-uds" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afcb699eb26d4332647cc848492bbc15eafb26f08d0304550d5aa1f612e066f0" +dependencies = [ + "iovec", + "libc", + "mio", +] + +[[package]] +name = "miow" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c1f2f3b1cf331de6896aabf6e9d55dca90356cc9960cca7eaaf408a355ae919" +dependencies = [ + "kernel32-sys", + "net2", + "winapi 0.2.8", + "ws2_32-sys", +] + +[[package]] +name = "miow" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07b88fb9795d4d36d62a012dfbf49a8f5cf12751f36d31a9dbe66d528e58979e" +dependencies = [ + "socket2", + "winapi 0.3.9", +] + +[[package]] +name = "native-tls" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b0d88c06fe90d5ee94048ba40409ef1d9315d86f6f38c2efdaad4fb50c58b2d" +dependencies = [ + "lazy_static", + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", +] + +[[package]] +name = "net2" +version = "0.2.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ba7c918ac76704fb42afcbbb43891e72731f3dcca3bef2a19786297baf14af7" +dependencies = [ + "cfg-if", + "libc", + "winapi 0.3.9", +] + +[[package]] +name = "num-bigint" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "090c7f9998ee0ff65aa5b723e4009f7b217707f1fb5ea551329cc4d6231fb304" +dependencies = [ + "autocfg 1.0.0", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-bigint-dig" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3d03c330f9f7a2c19e3c0b42698e48141d0809c78cd9b6219f85bd7d7e892aa" +dependencies = [ + "autocfg 0.1.7", + "byteorder", + "lazy_static", + "libm", + "num-integer", + "num-iter", + "num-traits", + "rand 0.7.3", + "serde", + "smallvec", + "zeroize", +] + +[[package]] +name = "num-integer" +version = "0.1.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d59457e662d541ba17869cf51cf177c0b5f0cbf476c66bdc90bf1edac4f875b" +dependencies = [ + "autocfg 1.0.0", + "num-traits", +] + +[[package]] +name = "num-iter" +version = "0.1.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a6e6b7c748f995c4c29c5f5ae0248536e04a5739927c74ec0fa564805094b9f" +dependencies = [ + "autocfg 1.0.0", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac267bcc07f48ee5f8935ab0d24f316fb722d7a1292e2913f0cc196b29ffd611" +dependencies = [ + "autocfg 1.0.0", +] + +[[package]] +name = "num_cpus" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "once_cell" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b631f7e854af39a1739f401cf34a8a013dfe09eac4fa4dba91e9768bd28168d" + +[[package]] +name = "opaque-debug" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" + +[[package]] +name = "openssl" +version = "0.10.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d575eff3665419f9b83678ff2815858ad9d11567e082f5ac1814baba4e2bcb4" +dependencies = [ + "bitflags", + "cfg-if", + "foreign-types", + "lazy_static", + "libc", + "openssl-sys", +] + +[[package]] +name = "openssl-probe" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77af24da69f9d9341038eba93a073b1fdaaa1b788221b00a69bce9e762cb32de" + +[[package]] +name = "openssl-sys" +version = "0.9.58" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a842db4709b604f0fe5d1170ae3565899be2ad3d9cbc72dedc789ac0511f78de" +dependencies = [ + "autocfg 1.0.0", + "cc", + "libc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "pem" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59698ea79df9bf77104aefd39cc3ec990cb9693fb59c3b0a70ddf2646fdffb4b" +dependencies = [ + "base64", + "once_cell", + "regex", +] + +[[package]] +name = "percent-encoding" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" + +[[package]] +name = "pin-project" +version = "0.4.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12e3a6cdbfe94a5e4572812a0201f8c0ed98c1c452c7b8563ce2276988ef9c17" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "0.4.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a0ffd45cf79d88737d7cc85bfd5d2894bee1139b356e616fe85dc389c61aaf7" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "pin-project-lite" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282adbf10f2698a7a77f8e983a74b2d18176c19a7fd32a45446139ae7b02b715" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "pkg-config" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05da548ad6865900e60eaba7f589cc0783590a92e940c26953ff81ddbab2d677" + +[[package]] +name = "ppv-lite86" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "237a5ed80e274dbc66f86bd59c1e25edc039660be53194b5fe0a482e0f2612ea" + +[[package]] +name = "proc-macro2" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "beae6331a816b1f65d04c45b078fd8e6c93e8071771f41b8163255bbd8d7c8fa" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "quote" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa563d17ecb180e500da1cfd2b028310ac758de548efdd203e18f283af693f37" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.3.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64ac302d8f83c0c1974bf758f6b041c6c8ada916fbb44a609158ca8b064cc76c" +dependencies = [ + "libc", + "rand 0.4.6", +] + +[[package]] +name = "rand" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "552840b97013b1a26992c11eac34bdd778e464601a4c2054b5f0bff7c6761293" +dependencies = [ + "fuchsia-cprng", + "libc", + "rand_core 0.3.1", + "rdrand", + "winapi 0.3.9", +] + +[[package]] +name = "rand" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" +dependencies = [ + "getrandom", + "libc", + "rand_chacha", + "rand_core 0.5.1", + "rand_hc", +] + +[[package]] +name = "rand_chacha" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" +dependencies = [ + "ppv-lite86", + "rand_core 0.5.1", +] + +[[package]] +name = "rand_core" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" +dependencies = [ + "rand_core 0.4.2", +] + +[[package]] +name = "rand_core" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc" + +[[package]] +name = "rand_core" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rand_hc" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" +dependencies = [ + "rand_core 0.5.1", +] + +[[package]] +name = "rdrand" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2" +dependencies = [ + "rand_core 0.3.1", +] + +[[package]] +name = "redox_syscall" +version = "0.1.56" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84" + +[[package]] +name = "regex" +version = "1.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c3780fcf44b193bc4d09f36d2a3c87b251da4a046c87795a0d35f4f927ad8e6" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", + "thread_local", +] + +[[package]] +name = "regex-syntax" +version = "0.6.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26412eb97c6b088a6997e05f69403a802a92d520de2f8e63c2b65f9e0f47c4e8" + +[[package]] +name = "remove_dir_all" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" +dependencies = [ + "winapi 0.3.9", +] + +[[package]] +name = "reqwest" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b82c9238b305f26f53443e3a4bc8528d64b8d0bee408ec949eb7bf5635ec680" +dependencies = [ + "base64", + "bytes", + "encoding_rs", + "futures-core", + "futures-util", + "http", + "http-body", + "hyper", + "hyper-tls", + "js-sys", + "lazy_static", + "log", + "mime", + "mime_guess", + "native-tls", + "percent-encoding", + "pin-project-lite", + "serde", + "serde_json", + "serde_urlencoded", + "tokio", + "tokio-tls", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "winreg", +] + +[[package]] +name = "rsa" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3648b669b10afeab18972c105e284a7b953a669b0be3514c27f9b17acab2f9cd" +dependencies = [ + "byteorder", + "digest", + "lazy_static", + "num-bigint-dig", + "num-integer", + "num-iter", + "num-traits", + "pem", + "rand 0.7.3", + "sha2", + "simple_asn1", + "subtle", + "thiserror", + "zeroize", +] + +[[package]] +name = "rust-crypto" +version = "0.2.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f76d05d3993fd5f4af9434e8e436db163a12a9d40e1a58a726f27a01dfd12a2a" +dependencies = [ + "gcc", + "libc", + "rand 0.3.23", + "rustc-serialize", + "time", +] + +[[package]] +name = "rustc-serialize" +version = "0.3.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcf128d1287d2ea9d80910b5f1120d0b8eede3fbf1abe91c40d39ea7d51e6fda" + +[[package]] +name = "rustc_version" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" +dependencies = [ + "semver", +] + +[[package]] +name = "ryu" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" + +[[package]] +name = "schannel" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f05ba609c234e60bee0d547fe94a4c7e9da733d1c962cf6e59efa4cd9c8bc75" +dependencies = [ + "lazy_static", + "winapi 0.3.9", +] + +[[package]] +name = "scoped-tls" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "332ffa32bf586782a3efaeb58f127980944bbc8c4d6913a86107ac2a5ab24b28" + +[[package]] +name = "sdk" +version = "0.1.0" +dependencies = [ + "base64", + "chrono", + "reqwest", + "rsa", + "rust-crypto", + "serde", + "serde_json", + "tokio", +] + +[[package]] +name = "security-framework" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64808902d7d99f78eaddd2b4e2509713babc3dc3c85ad6f4c447680f3c01e535" +dependencies = [ + "bitflags", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17bf11d99252f512695eb468de5516e5cf75455521e69dfe343f3b74e4748405" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "semver" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" +dependencies = [ + "semver-parser", +] + +[[package]] +name = "semver-parser" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" + +[[package]] +name = "serde" +version = "1.0.114" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5317f7588f0a5078ee60ef675ef96735a1442132dc645eb1d12c018620ed8cd3" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.114" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a0be94b04690fbaed37cddffc5c134bf537c8e3329d53e982fe04c374978f8e" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.56" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3433e879a558dde8b5e8feb2a04899cf34fdde1fafb894687e52105fc1162ac3" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "serde_urlencoded" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ec5d77e2d4c73717816afac02670d5c4f534ea95ed430442cad02e7a6e32c97" +dependencies = [ + "dtoa", + "itoa", + "serde", + "url", +] + +[[package]] +name = "sha2" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2933378ddfeda7ea26f48c555bdad8bb446bf8a3d17832dc83e380d444cfb8c1" +dependencies = [ + "block-buffer", + "cfg-if", + "cpuid-bool", + "digest", + "opaque-debug", +] + +[[package]] +name = "signal-hook-registry" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94f478ede9f64724c5d173d7bb56099ec3e2d9fc2774aac65d34b8b890405f41" +dependencies = [ + "arc-swap", + "libc", +] + +[[package]] +name = "simple_asn1" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b25ecba7165254f0c97d6c22a64b1122a03634b18d20a34daf21e18f892e618" +dependencies = [ + "chrono", + "num-bigint", + "num-traits", +] + +[[package]] +name = "slab" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8" + +[[package]] +name = "smallvec" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7cb5678e1615754284ec264d9bb5b4c27d2018577fd90ac0ceb578591ed5ee4" + +[[package]] +name = "socket2" +version = "0.3.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03088793f677dce356f3ccc2edb1b314ad191ab702a5de3faf49304f7e104918" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "winapi 0.3.9", +] + +[[package]] +name = "spin" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" + +[[package]] +name = "subtle" +version = "2.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "502d53007c02d7605a05df1c1a73ee436952781653da5d0bf57ad608f66932c1" + +[[package]] +name = "syn" +version = "1.0.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8d5d96e8cbb005d6959f119f773bfaebb5684296108fb32600c00cde305b2cd" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "synstructure" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b834f2d66f734cb897113e34aaff2f1ab4719ca946f9a7358dba8f8064148701" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "unicode-xid", +] + +[[package]] +name = "tempfile" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a6e24d9338a0a5be79593e2fa15a648add6138caa803e2d5bc782c371732ca9" +dependencies = [ + "cfg-if", + "libc", + "rand 0.7.3", + "redox_syscall", + "remove_dir_all", + "winapi 0.3.9", +] + +[[package]] +name = "thiserror" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7dfdd070ccd8ccb78f4ad66bf1982dc37f620ef696c6b5028fe2ed83dd3d0d08" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd80fc12f73063ac132ac92aceea36734f04a1d93c1240c6944e23a3b8841793" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "thread_local" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d40c6d1b69745a6ec6fb1ca717914848da4b44ae29d9b3080cbee91d72a69b14" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "time" +version = "0.1.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca8a50ef2360fbd1eeb0ecd46795a87a19024eb4b53c5dc916ca1fd95fe62438" +dependencies = [ + "libc", + "winapi 0.3.9", +] + +[[package]] +name = "tinyvec" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53953d2d3a5ad81d9f844a32f14ebb121f50b650cd59d0ee2a07cf13c617efed" + +[[package]] +name = "tokio" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d099fa27b9702bed751524694adbe393e18b36b204da91eb1cbbbbb4a5ee2d58" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "iovec", + "lazy_static", + "libc", + "memchr", + "mio", + "mio-named-pipes", + "mio-uds", + "num_cpus", + "pin-project-lite", + "signal-hook-registry", + "slab", + "tokio-macros", + "winapi 0.3.9", +] + +[[package]] +name = "tokio-macros" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0c3acc6aa564495a0f2e1d59fab677cd7f81a19994cfc7f3ad0e64301560389" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tokio-tls" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a70f4fcd7b3b24fb194f837560168208f669ca8cb70d0c4b862944452396343" +dependencies = [ + "native-tls", + "tokio", +] + +[[package]] +name = "tokio-util" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be8242891f2b6cbef26a2d7e8605133c2c554cd35b3e4948ea892d6d68436499" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "log", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tower-service" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e987b6bf443f4b5b3b6f38704195592cca41c5bb7aedd3c3693c7081f8289860" + +[[package]] +name = "try-lock" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e604eb7b43c06650e854be16a2a03155743d3752dd1c943f6829e26b7a36e382" + +[[package]] +name = "typenum" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "373c8a200f9e67a0c95e62a4f52fbf80c23b4381c05a17845531982fa99e6b33" + +[[package]] +name = "unicase" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6" +dependencies = [ + "version_check", +] + +[[package]] +name = "unicode-bidi" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5" +dependencies = [ + "matches", +] + +[[package]] +name = "unicode-normalization" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fb19cf769fa8c6a80a162df694621ebeb4dafb606470b2b2fce0be40a98a977" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "unicode-xid" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" + +[[package]] +name = "url" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "829d4a8476c35c9bf0bbce5a3b23f4106f79728039b726d292bb93bc106787cb" +dependencies = [ + "idna", + "matches", + "percent-encoding", +] + +[[package]] +name = "vcpkg" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6454029bf181f092ad1b853286f23e2c507d8e8194d01d92da4a55c274a5508c" + +[[package]] +name = "version_check" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5a972e5669d67ba988ce3dc826706fb0a8b01471c088cb0b6110b805cc36aed" + +[[package]] +name = "want" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" +dependencies = [ + "log", + "try-lock", +] + +[[package]] +name = "wasi" +version = "0.9.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" + +[[package]] +name = "wasm-bindgen" +version = "0.2.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a634620115e4a229108b71bde263bb4220c483b3f07f5ba514ee8d15064c4c2" +dependencies = [ + "cfg-if", + "serde", + "serde_json", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e53963b583d18a5aa3aaae4b4c1cb535218246131ba22a71f05b518098571df" +dependencies = [ + "bumpalo", + "lazy_static", + "log", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dba48d66049d2a6cc8488702e7259ab7afc9043ad0dc5448444f46f2a453b362" +dependencies = [ + "cfg-if", + "js-sys", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fcfd5ef6eec85623b4c6e844293d4516470d8f19cd72d0d12246017eb9060b8" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9adff9ee0e94b926ca81b57f57f86d5545cdcb1d259e21ec9bdd95b901754c75" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f7b90ea6c632dd06fd765d44542e234d5e63d9bb917ecd64d79778a13bd79ae" + +[[package]] +name = "web-sys" +version = "0.3.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "863539788676619aac1a23e2df3655e96b32b0e05eb72ca34ba045ad573c625d" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "winapi" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-build" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "winreg" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0120db82e8a1e0b9fb3345a539c478767c0048d842860994d96113d5b667bd69" +dependencies = [ + "winapi 0.3.9", +] + +[[package]] +name = "ws2_32-sys" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e" +dependencies = [ + "winapi 0.2.8", + "winapi-build", +] + +[[package]] +name = "zeroize" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3cbac2ed2ba24cc90f5e06485ac8c7c1e5449fe8911aef4d8877218af021a5b8" +dependencies = [ + "zeroize_derive", +] + +[[package]] +name = "zeroize_derive" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de251eec69fc7c1bc3923403d18ececb929380e016afe103da75f396704f8ca2" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] diff --git a/sop-sdk/sdk-rust/sdk/Cargo.toml b/sop-sdk/sdk-rust/sdk/Cargo.toml new file mode 100644 index 00000000..fe37e126 --- /dev/null +++ b/sop-sdk/sdk-rust/sdk/Cargo.toml @@ -0,0 +1,23 @@ +[package] +name = "sdk" +version = "0.1.0" +authors = ["thc"] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +# 时间api +chrono = "0.4" +# rsa库 +rsa = "0.3.0" +# 加密库 +rust-crypto = "^0.2" +# base64库 +base64 = "0.12.3" +# HTTP客户端 https://github.com/seanmonstar/reqwest +reqwest = { version = "0.10", features = ["blocking", "json"] } +tokio = { version = "0.2", features = ["full"] } +# 处理json https://serde.rs/ +serde = { version = "1.0.114", features = ["derive"] } +serde_json = "1.0.56" \ No newline at end of file diff --git a/sop-sdk/sdk-rust/sdk/src/client.rs b/sop-sdk/sdk-rust/sdk/src/client.rs new file mode 100644 index 00000000..5122de15 --- /dev/null +++ b/sop-sdk/sdk-rust/sdk/src/client.rs @@ -0,0 +1,112 @@ +use std::collections::HashMap; + +use chrono::Local; +use serde::de::DeserializeOwned; + +use crate::http::HttpTool; +use crate::request::{Request, RequestType}; +use crate::sign::{SignType, SignUtil}; +use serde_json::Value; + +pub struct OpenClient { + /// 应用ID + pub app_id: &'static str, + /// 应用私钥,PKCS#1 + pub private_key: &'static str, + /// 请求url + pub url: &'static str, +} + +impl OpenClient { + + /// 发送请求 + /// + /// - request: 请求对象 + /// + /// 返回结果 + pub fn execute(&self, request: impl Request) -> T { + self.execute_token(request, "") + } + + ///发送请求 + /// + /// - request: 请求对象 + /// - token:token + /// + /// 返回结果 + pub fn execute_token(&self, request: impl Request, token: &'static str) -> T { + let struct_obj: T; + + let request_type = request.get_request_type(); + let headers = &OpenClient::get_default_headers(); + let all_params = &self.build_params(&request, token); + + if request.get_base().files.len() > 0 { + let base = request.get_base(); + let files = &base.files; + let resp = HttpTool::post_file(self.url, all_params, files, headers); + struct_obj = self.parse_response(resp, &request); + } else { + match request_type { + RequestType::Get => { + let resp = HttpTool::get(self.url, all_params, headers); + struct_obj = self.parse_response(resp, &request); + } + RequestType::PostForm => { + let resp = HttpTool::post_form(self.url, all_params, headers); + struct_obj = self.parse_response(resp, &request); + } + RequestType::PostJson => { + let resp = HttpTool::post_json(self.url, all_params, headers); + struct_obj = self.parse_response(resp, &request); + } + RequestType::PostFile => { + let base = request.get_base(); + let files = &base.files; + let resp = HttpTool::post_file(self.url, all_params, files, headers); + struct_obj = self.parse_response(resp, &request); + } + } + } + struct_obj + } + + fn get_default_headers() -> HashMap<&'static str, &'static str> { + let mut headers = HashMap::new(); + headers.insert("Accept-Encoding", "identity"); + headers + } + + fn parse_response(&self, resp: reqwest::blocking::Response, request: &impl Request) -> T { + let root: HashMap = resp.json().expect("error"); + request.parse_response(root) + } + + /// 构建请求参数 + fn build_params(&self, request: &impl Request, token: &str) -> HashMap<&'static str, String> { + let method = request.get_method(); + let version = request.get_version(); + + let mut all_params = HashMap::new(); + all_params.insert("app_id", self.app_id.to_string()); + all_params.insert("method", method.to_string()); + all_params.insert("charset", "UTF-8".to_string()); + all_params.insert("timestamp", Local::now().format("%Y-%m-%d %H:%M:%S").to_string()); + all_params.insert("version", version.to_string()); + + // 添加业务参数 + for entry in &request.get_base().biz_model { + all_params.insert(entry.0, entry.1.to_string()); + } + + if !token.is_empty() { + all_params.insert("app_auth_token", token.to_string()); + } + + // 创建签名 + let sign = SignUtil::create_sign(&all_params, self.private_key, SignType::RSA2); + all_params.insert("sign", sign); + + all_params + } +} \ No newline at end of file diff --git a/sop-sdk/sdk-rust/sdk/src/http.rs b/sop-sdk/sdk-rust/sdk/src/http.rs new file mode 100644 index 00000000..8e0a2fe0 --- /dev/null +++ b/sop-sdk/sdk-rust/sdk/src/http.rs @@ -0,0 +1,158 @@ +/* +Cargo.toml: + +[dependencies] +reqwest = { version = "0.10", features = ["blocking", "json"] } +tokio = { version = "0.2", features = ["full"] } +serde = { version = "1.0.114", features = ["derive"] } +serde_json = "1.0.56" + */ +use std::collections::HashMap; + +use reqwest::blocking::Response; +use reqwest::header::{HeaderMap, HeaderValue, HeaderName}; +use std::str::FromStr; +use serde::Serialize; + +/// HTTP请求工具 +pub struct HttpTool {} + +/// 上传文件对象 +pub struct UploadFile { + /// 上传文件表单名称 + pub name: &'static str, + /// 文件全路径 + pub path: String, +} + +impl HttpTool { + + /// get请求 + /// + /// - url:请求url + /// - params:请求参数 + /// - headers:请求header + /// + /// # Example + /// + /// ```rust + /// let mut params = HashMap::new(); + /// params.insert("name", "Jim"); + /// params.insert("age", "12"); + /// let resp = HttpTool::get(url, ¶ms, &HashMap::new()); + /// ``` + /// return: Response对象 + pub fn get(url: &str, params: &T, headers: &HashMap<&str, &str>) -> Response { + let client = reqwest::blocking::Client::new(); + let res = client + .get(url) + .query(params) + .headers(super::http::HttpTool::get_headers_map(headers)) + .send(); + let resp = res.expect("request error"); + resp + } + + /// post模拟表单请求 + /// + /// - url:请求url + /// - params:请求参数 + /// - headers:请求header + /// + /// # Example + /// + /// ```rust + /// let mut params = HashMap::new(); + /// params.insert("name", "Jim"); + /// params.insert("age", "12"); + /// let resp = HttpTool::post_form(url, ¶ms, &HashMap::new()); + /// ``` + /// return: Response对象 + pub fn post_form(url: &str, params: &T, headers: &HashMap<&str, &str>) -> Response { + let client = reqwest::blocking::Client::new(); + let res = client + .post(url) + .form(params) + .headers(super::http::HttpTool::get_headers_map(headers)) + .send(); + let resp = res.expect("request error"); + resp + } + + /// post发送json + /// + /// - url:请求url + /// - params:请求参数 + /// - headers:请求header + /// + /// # Example + /// + /// ```rust + /// let mut params = HashMap::new(); + /// params.insert("name", "Jim"); + /// params.insert("age", "12"); + /// let resp = HttpTool::post_json(url, ¶ms, &HashMap::new()); + /// ``` + /// return: Response对象 + pub fn post_json(url: &str, params: &T, headers: &HashMap<&str, &str>) -> Response { + let client = reqwest::blocking::Client::new(); + let res = client + .post(url) + .json(params) + .headers(super::http::HttpTool::get_headers_map(headers)) + .send(); + let resp = res.expect("request error"); + resp + } + + /// post上传文件 + /// + /// - url:请求url + /// - params:请求参数 + /// - files:上传文件 + /// - headers:请求header + /// + /// # Example + /// + /// ```rust + /// let mut params = HashMap::new(); + /// params.insert("name", String::from("Jim")); + /// params.insert("age",String::from("12")); + /// + /// // 设置上传文件 + /// let mut files = vec![ + /// UploadFile { name:"file1", path: String::from("/User/xx/aa.txt") }, + /// UploadFile { name:"file2", path: String::from("/User/xx/bb.txt") }, + /// ]; + /// + /// let resp = HttpTool::post_file(url, ¶ms, &files, &HashMap::new()); + /// ``` + /// return: Response对象 + pub fn post_file(url: &str, params: &HashMap<&str, String>, files: &Vec, headers: &HashMap<&str, &str>) -> Response { + let client = reqwest::blocking::Client::new(); + let mut form = reqwest::blocking::multipart::Form::new(); + // 添加普通参数 + for entry in params { + form = form.text(entry.0.to_string(), entry.1.to_string()); + } + // 添加文件 + for file in files { + form = form.file(file.name.to_string(), file.path.to_string()).unwrap(); + } + let res = client + .post(url) + .multipart(form) + .headers(super::http::HttpTool::get_headers_map(headers)) + .send(); + let resp = res.expect("request error"); + resp + } + + fn get_headers_map(headers: &HashMap<&str, &str>) -> HeaderMap { + let mut header_map = HeaderMap::new(); + for entry in headers { + header_map.insert(HeaderName::from_str(entry.0).unwrap(), HeaderValue::from_str(entry.1).unwrap()); + } + header_map + } +} \ No newline at end of file diff --git a/sop-sdk/sdk-rust/sdk/src/lib.rs b/sop-sdk/sdk-rust/sdk/src/lib.rs new file mode 100644 index 00000000..7bebdbf5 --- /dev/null +++ b/sop-sdk/sdk-rust/sdk/src/lib.rs @@ -0,0 +1,14 @@ +pub mod sign; +pub mod client; +pub mod request; +pub mod response; +pub mod http; +extern crate chrono; + +/*#[cfg(test)] +mod tests { + #[test] + fn it_works() { + assert_eq!(2 + 2, 4); + } +}*/ \ No newline at end of file diff --git a/sop-sdk/sdk-rust/sdk/src/request/memberinfoget.rs b/sop-sdk/sdk-rust/sdk/src/request/memberinfoget.rs new file mode 100644 index 00000000..5b92e984 --- /dev/null +++ b/sop-sdk/sdk-rust/sdk/src/request/memberinfoget.rs @@ -0,0 +1,26 @@ +use crate::request::{BaseRequest, Request, RequestType}; + +pub struct MemberInfoGetRequest { + // 固定这么写 + pub base: BaseRequest +} + +impl Request for MemberInfoGetRequest { + + fn get_method(&self) -> &str { + "member.info.get" + } + + fn get_version(&self) -> &str { + "1.0" + } + + fn get_request_type(&self) -> RequestType { + RequestType::Get + } + + // 固定这么写 + fn get_base(&self) -> &BaseRequest { + &self.base + } +} diff --git a/sop-sdk/sdk-rust/sdk/src/request/mod.rs b/sop-sdk/sdk-rust/sdk/src/request/mod.rs new file mode 100644 index 00000000..de9cf5e5 --- /dev/null +++ b/sop-sdk/sdk-rust/sdk/src/request/mod.rs @@ -0,0 +1,45 @@ + +use std::collections::HashMap; + +use serde_json::Value; + +use crate::http::UploadFile; +use serde::de::DeserializeOwned; + +pub mod memberinfoget; + +pub enum RequestType { + Get, + PostJson, + PostForm, + PostFile, +} + +pub trait Request { + /// 返回接口名称 + fn get_method(&self) -> &str; + + /// 返回版本号 + fn get_version(&self) -> &str; + + /// 返回请求方式 + fn get_request_type(&self) -> RequestType; + + /// 返回base + fn get_base(&self) -> &BaseRequest; + + fn parse_response(&self, root: HashMap) -> T { + let mut data = root.get("error_response"); + if data.is_none() { + let data_name = self.get_method().replace(".", "_") + "_response"; + data = root.get(data_name.as_str()); + } + let value = serde_json::to_value(data.unwrap()).unwrap(); + serde_json::from_value(value).unwrap() + } +} + +pub struct BaseRequest { + pub biz_model: HashMap<&'static str, String>, + pub files: Vec +} diff --git a/sop-sdk/sdk-rust/sdk/src/response/memberinfoget.rs b/sop-sdk/sdk-rust/sdk/src/response/memberinfoget.rs new file mode 100644 index 00000000..72e5caaf --- /dev/null +++ b/sop-sdk/sdk-rust/sdk/src/response/memberinfoget.rs @@ -0,0 +1,36 @@ +extern crate serde; + +use serde::{Deserialize}; + +// 响应参数 +#[derive(Deserialize, Debug)] +pub struct MemberInfoGetResponse { + // ~~~ 固定部分 ~~~ + pub code: String, + pub msg: String, + // json中可能没有sub_code属性,因此需要加上 #[serde(default)] + // 详见:https://serde.rs/field-attrs.html + #[serde(default)] + pub sub_code: String, + #[serde(default)] + pub sub_msg: String, + // ~~~ 固定部分 ~~~ + + // 下面是业务字段 + #[serde(default)] + pub id: u32, + + #[serde(default)] + pub name: String, + + pub member_info: MemberInfo +} + +#[derive(Deserialize, Debug)] +pub struct MemberInfo { + #[serde(default)] + pub is_vip: i8, + + #[serde(default)] + pub vip_endtime: String +} \ No newline at end of file diff --git a/sop-sdk/sdk-rust/sdk/src/response/mod.rs b/sop-sdk/sdk-rust/sdk/src/response/mod.rs new file mode 100644 index 00000000..1abd561c --- /dev/null +++ b/sop-sdk/sdk-rust/sdk/src/response/mod.rs @@ -0,0 +1 @@ +pub mod memberinfoget; diff --git a/sop-sdk/sdk-rust/sdk/src/sign.rs b/sop-sdk/sdk-rust/sdk/src/sign.rs new file mode 100644 index 00000000..041705c6 --- /dev/null +++ b/sop-sdk/sdk-rust/sdk/src/sign.rs @@ -0,0 +1,108 @@ +extern crate rsa; +extern crate crypto; + +use std::collections::HashMap; + +use rsa::{RSAPrivateKey, PaddingScheme}; +use rsa::Hash; + +use crypto::sha2::Sha256; +use crypto::digest::Digest; +use std::iter::repeat; + +pub struct SignUtil {} + +pub enum SignType { + RSA, + RSA2, +} + +pub enum HashType { + Sha1, + Sha256 +} + +impl SignUtil { + + pub fn create_sign(all_params: &HashMap<&str, String>, private_key: &str, sign_type: SignType) -> String { + let content = SignUtil::get_sign_content(all_params); + // println!("content:{}", content); + let hash_type; + match sign_type { + SignType::RSA => hash_type = HashType::Sha1, + SignType::RSA2 => hash_type = HashType::Sha256, + } + + SignUtil::rsa_sign(content.as_str(), private_key, hash_type) + } + + /// RSA签名 + /// + /// - content: 签名内容 + /// - private_key: 私钥,PKCS#1 + /// - hash_type: hash类型 + /// + /// # Examples + /// + /// ``` + /// use sdk::sign::{SignUtil, HashType}; + /// + /// let content = "123"; + /// let private_key = "your private key"; + /// let sign = SignUtil::rsa_sign(content, private_key, HashType::Sha256); + /// + /// println!("sign:{}", sign); + /// ``` + /// return: 返回base64字符串 + pub fn rsa_sign(content: &str, private_key: &str, hash_type: HashType) -> String { + // 格式化私钥 + let der_encoded = private_key + .lines() + .filter(|line| !line.starts_with("-")) + .fold(String::new(), |mut data, line| { + data.push_str(&line); + data + }); + let der_bytes = base64::decode(der_encoded).expect("failed to decode base64 content"); + // 获取私钥对象 + let private_key = RSAPrivateKey::from_pkcs1(&der_bytes).expect("failed to parse key"); + + // 创建一个Sha256对象 + let mut hasher = Sha256::new(); + // 对内容进行摘要 + hasher.input_str(content); + // 将摘要结果保存到buf中 + let mut buf: Vec = repeat(0).take((hasher.output_bits()+7)/8).collect(); + hasher.result(&mut buf); + + // 对摘要进行签名 + let hash; + match hash_type { + HashType::Sha1 => hash = Hash::SHA1, + HashType::Sha256 => hash = Hash::SHA2_256 + } + let sign_result = private_key.sign(PaddingScheme::PKCS1v15Sign {hash: Option::from(hash) }, &buf); + // 签名结果转化为base64 + let vec = sign_result.expect("create sign error for base64"); + + base64::encode(vec) + } + + fn get_sign_content(all_params: &HashMap<&str, String>) -> String { + let mut content = Vec::new(); + let keys = all_params.keys(); + let mut sorted_keys = Vec::new(); + for key in keys { + sorted_keys.push(key); + } + // 排序 + sorted_keys.sort(); + for key in sorted_keys { + let val = all_params.get(key); + if val.is_some() { + content.push(key.to_string() + "=" + val.unwrap()); + } + } + content.join("&") + } +} \ No newline at end of file diff --git a/sop-upgrade-4.2.0.sql b/sop-upgrade-4.2.0.sql new file mode 100644 index 00000000..7746e190 --- /dev/null +++ b/sop-upgrade-4.2.0.sql @@ -0,0 +1,30 @@ +use sop; + +ALTER TABLE `sop`.`isv_info` ADD COLUMN `user_id` BIGINT NULL DEFAULT 0 COMMENT 'user_account.id' AFTER `remark`; + +CREATE INDEX `idx_userid` USING BTREE ON `sop`.`isv_info` (`user_id`); + + +CREATE TABLE `user_account` ( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, + `username` varchar(128) NOT NULL DEFAULT '' COMMENT '用户名(邮箱)', + `password` varchar(128) NOT NULL DEFAULT '' COMMENT '密码', + `status` tinyint(4) NOT NULL DEFAULT '1' COMMENT '2:邮箱未验证,1:启用,0:禁用', + `gmt_create` datetime DEFAULT CURRENT_TIMESTAMP, + `gmt_modified` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (`id`), + UNIQUE KEY `uk_username` (`username`) USING BTREE +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='用户信息'; + +CREATE TABLE `isp_resource` ( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, + `name` varchar(64) NOT NULL DEFAULT '' COMMENT '资源名称', + `content` varchar(128) NOT NULL DEFAULT '' COMMENT '资源内容(URL)', + `ext_content` text, + `version` varchar(32) NOT NULL DEFAULT '' COMMENT '版本', + `type` tinyint(4) NOT NULL DEFAULT '0' COMMENT '资源类型:0:SDK链接', + `is_deleted` tinyint(4) NOT NULL DEFAULT '0', + `gmt_create` datetime DEFAULT CURRENT_TIMESTAMP, + `gmt_modified` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='ISP资源表'; \ No newline at end of file diff --git a/sop-website/pom.xml b/sop-website/pom.xml index bc6a6f00..ff44d1cc 100644 --- a/sop-website/pom.xml +++ b/sop-website/pom.xml @@ -1,77 +1,21 @@ - - - - com.gitee.sop - sop-parent - 4.1.0-SNAPSHOT - ../pom.xml - - - 4.0.0 - sop-website - - - 1.8 - - - - - - com.gitee.sop - sop-bridge-nacos - - 4.1.0-SNAPSHOT - - - - org.springframework.boot - spring-boot-starter-web - - - - org.apache.commons - commons-lang3 - - - com.squareup.okhttp3 - okhttp - - - - com.alibaba - fastjson - - - org.apache.httpcomponents - httpclient - - - - org.projectlombok - lombok - 1.18.4 - provided - - - - org.springframework.boot - spring-boot-starter-test - test - - - - - - - org.springframework.boot - spring-boot-maven-plugin - - - - - + + + + com.gitee.sop + sop-parent + 4.1.0-SNAPSHOT + ../pom.xml + + + 4.0.0 + sop-website + 4.1.0-SNAPSHOT + pom + + + sop-website-server + + \ No newline at end of file diff --git a/sop-website/sop-portal/.babelrc b/sop-website/sop-portal/.babelrc new file mode 100644 index 00000000..42d28faf --- /dev/null +++ b/sop-website/sop-portal/.babelrc @@ -0,0 +1,22 @@ +{ + "plugins": [ + "transform-decorators-legacy", + "transform-class-properties", + "transform-object-rest-spread", + "transform-object-assign", + [ + "transform-runtime", + { + "helpers": false, + "polyfill": false, + "regenerator": true, + "moduleName": "babel-runtime" + } + ] + ], + "presets": [ + "react", + "stage-0", + "es2015" + ] +} diff --git a/sop-website/sop-portal/.docsite b/sop-website/sop-portal/.docsite new file mode 100644 index 00000000..e69de29b diff --git a/sop-website/sop-portal/.eslintrc b/sop-website/sop-portal/.eslintrc new file mode 100644 index 00000000..0e70ce4c --- /dev/null +++ b/sop-website/sop-portal/.eslintrc @@ -0,0 +1,28 @@ +{ + "extends": "eslint-config-ali/react", + "parser": "babel-eslint", + "env": { + "browser": true, + "node": true, + "mocha": true + }, + "globals": { + "AK_GLOBAL": true, + "dd": {}, + "_czc": {}, + "dplus": {}, + "salt": {}, + "_": true, + "homePageData": {}, + "Lang": {} + }, + "rules": { + "max-len": 0, + "new-cap": [2, { "newIsCap": true, "properties": false }], + "react/jsx-indent": 0, + "react/jsx-indent-props": 0, + "indent": 0, + "radix": ["error", "as-needed"], + "linebreak-style": [0 ,"error", "windows"] + } +} \ No newline at end of file diff --git a/sop-website/sop-portal/.gitignore b/sop-website/sop-portal/.gitignore new file mode 100644 index 00000000..4996fc3b --- /dev/null +++ b/sop-website/sop-portal/.gitignore @@ -0,0 +1,17 @@ +.DS_Store +node_modules/ +dist/ +build/ +npm-debug.log* +yarn-debug.log* +yarn-error.log* +package-lock.json +tests/**/coverage/ + +# Editor directories and files +.idea +.vscode +*.suo +*.ntvs* +*.njsproj +*.sln diff --git a/sop-website/sop-portal/.nojekyll b/sop-website/sop-portal/.nojekyll new file mode 100644 index 00000000..e69de29b diff --git a/sop-website/sop-portal/404.html b/sop-website/sop-portal/404.html new file mode 100644 index 00000000..2518eab7 --- /dev/null +++ b/sop-website/sop-portal/404.html @@ -0,0 +1,18 @@ + + + + + + + + + + diff --git a/sop-website/sop-portal/README.md b/sop-website/sop-portal/README.md new file mode 100644 index 00000000..41e59a5b --- /dev/null +++ b/sop-website/sop-portal/README.md @@ -0,0 +1,24 @@ +# 门户网站 + +纯静态网站,作为开放平台基本介绍使用,可独立部署,logo制作:http://www.uugai.com/ + +# 初始化 + +npm install + +# 启动 + +docsite start + +# 打包 + +首先,需要配置下站点的根路径,修改site_config/site.js中的rootPath字段。规则如下: + +当部署根路径为/,则设置为''空字符串即可。 + +当部署根路径不为/,则设置为具体的根路径,注意需以/开头,但不能有尾/。 + +运行`build.sh` + +将dist内容上传到服务器 + diff --git a/sop-website/sop-portal/blog/en-us/blog1.md b/sop-website/sop-portal/blog/en-us/blog1.md new file mode 100644 index 00000000..506a3e14 --- /dev/null +++ b/sop-website/sop-portal/blog/en-us/blog1.md @@ -0,0 +1,10 @@ +--- +key1: hello +key2: world +--- + +# blog1 + +it supports the resolution of meta data,the text between `---`(at least three`-`)written in the format of `key:value`,will be resolved to `md_json/blog.json`,`filename` and `__html` are preserved. + +filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text \ No newline at end of file diff --git a/sop-website/sop-portal/blog/zh-cn/blog1.md b/sop-website/sop-portal/blog/zh-cn/blog1.md new file mode 100644 index 00000000..9537e4b0 --- /dev/null +++ b/sop-website/sop-portal/blog/zh-cn/blog1.md @@ -0,0 +1,10 @@ +--- +key1: hello +key2: world +--- + +# 博客1 + +支持元数据的解析,`---`(至少三个`-`)开头之间的数据按照`key:value`的形式,最终会被解析到`md_json/blog.json`中,其中`filename`和`__html`为保留字段,请勿使用。 + +博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充 \ No newline at end of file diff --git a/sop-website/sop-portal/build.sh b/sop-website/sop-portal/build.sh new file mode 100644 index 00000000..8517b27e --- /dev/null +++ b/sop-website/sop-portal/build.sh @@ -0,0 +1,20 @@ +#!/bin/sh + +echo "开始构建..." + +dist_dir="dist" + +if [ ! -d "$dist_dir" ]; then + mkdir $dist_dir +fi + +docsite build + +echo "复制文件到${dist_dir}目录" + +rm -rf $dist_dir/* +cp -r zh-cn/* $dist_dir +cp -r build $dist_dir/build +cp -r img $dist_dir/img + +echo "构建完毕" diff --git a/sop-website/sop-portal/docs/en-us/demo1.md b/sop-website/sop-portal/docs/en-us/demo1.md new file mode 100644 index 00000000..d3baa456 --- /dev/null +++ b/sop-website/sop-portal/docs/en-us/demo1.md @@ -0,0 +1,249 @@ +# example of footnote + +this is a demo of the usage of footnote. +this is footnote1 [^1]。click it and see what will happen。 + +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. + + + +[^1]: this is the explanation of footnote \ No newline at end of file diff --git a/sop-website/sop-portal/docs/en-us/demo2.md b/sop-website/sop-portal/docs/en-us/demo2.md new file mode 100644 index 00000000..5c626342 --- /dev/null +++ b/sop-website/sop-portal/docs/en-us/demo2.md @@ -0,0 +1,16 @@ +# demo for reference of md or image for relative path + +if you want to reference to another md, the path can be written in the format of relative path under the circumstance of file system. +click here: [demo1.md](./demo1.md)。 + +the same is to image. +![](./img/brhtqqzh.jpeg) + +# demo for math typesetting(katex) + +$E = mc^2$ + +$$x = a_{1}^n + a_{2}^n + a_{3}^n$$ + +$$\sqrt[3]{X}$$ +$$\sqrt{5 - x}$$ \ No newline at end of file diff --git a/sop-website/sop-portal/docs/en-us/dir/demo3.md b/sop-website/sop-portal/docs/en-us/dir/demo3.md new file mode 100644 index 00000000..a54d1405 --- /dev/null +++ b/sop-website/sop-portal/docs/en-us/dir/demo3.md @@ -0,0 +1,7 @@ +# demo for reference of md or image for relative path, across directory + +if you want to reference to another md, the path can be written in the format of relative path under the circumstance of file system. +click here: [demo1.md](../demo1.md)。 + +the same is to image. +![](../img/brhtqqzh.jpeg) \ No newline at end of file diff --git a/sop-website/sop-portal/docs/en-us/img/brhtqqzh.jpeg b/sop-website/sop-portal/docs/en-us/img/brhtqqzh.jpeg new file mode 100644 index 00000000..854369b8 Binary files /dev/null and b/sop-website/sop-portal/docs/en-us/img/brhtqqzh.jpeg differ diff --git a/sop-website/sop-portal/docs/zh-cn/demo1.md b/sop-website/sop-portal/docs/zh-cn/demo1.md new file mode 100644 index 00000000..ec856921 --- /dev/null +++ b/sop-website/sop-portal/docs/zh-cn/demo1.md @@ -0,0 +1,401 @@ +# 脚注的使用 + +这是一段示例文字,展示脚注的使用。 +这是脚注1 [^1]。点击脚注滚动至对应脚注处。 + +下面是一大段文字,填充开始。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 + + +[^1]: 这是脚注的说明 \ No newline at end of file diff --git a/sop-website/sop-portal/docs/zh-cn/demo2.md b/sop-website/sop-portal/docs/zh-cn/demo2.md new file mode 100644 index 00000000..cae6a14b --- /dev/null +++ b/sop-website/sop-portal/docs/zh-cn/demo2.md @@ -0,0 +1,17 @@ +# 文档之间跳转、图片引用的示例 + +文档之间的跳转链接按照文件目录之间的相对关系配置即可,推荐在同一语言目录下的不同文件夹间跳转,以缩短配置路径。 +点击跳转: [demo1.md](./demo1.md)。 + +图片的引用,支持绝对引用和相对引用,如果是相对引用,同文档之间的相互引用一样,是按照文件之间的相对路径进行处理的。比如 +![](./img/brhtqqzh.jpeg) + +# 数学公式的使用(katex) + +$E = mc^2$ + +$$x = a_{1}^n + a_{2}^n + a_{3}^n$$ + +$$\sqrt[3]{X}$$ +$$\sqrt{5 - x}$$ + diff --git a/sop-website/sop-portal/docs/zh-cn/dir/demo3.md b/sop-website/sop-portal/docs/zh-cn/dir/demo3.md new file mode 100644 index 00000000..9d0bddb9 --- /dev/null +++ b/sop-website/sop-portal/docs/zh-cn/dir/demo3.md @@ -0,0 +1,8 @@ +# 跨目录文档之间跳转、图片引用的示例 + +这是一段文字,将要跳转到demo1。 +文档之间的跳转链接按照文件目录之间的相对关系配置即可,推荐在同一语言目录下的不同文件夹间跳转,以缩短配置路径。 +点击跳转: [demo1.md](../demo1.md)。 + +图片的引用,支持绝对引用和相对引用,如果是相对引用,同文档之间的相互引用一样,是按照文件之间的相对路径进行处理的。比如 +![](../img/brhtqqzh.jpeg) \ No newline at end of file diff --git a/sop-website/sop-portal/docs/zh-cn/img/brhtqqzh.jpeg b/sop-website/sop-portal/docs/zh-cn/img/brhtqqzh.jpeg new file mode 100644 index 00000000..854369b8 Binary files /dev/null and b/sop-website/sop-portal/docs/zh-cn/img/brhtqqzh.jpeg differ diff --git a/sop-website/sop-portal/docsite.config.yml b/sop-website/sop-portal/docsite.config.yml new file mode 100644 index 00000000..69921ec2 --- /dev/null +++ b/sop-website/sop-portal/docsite.config.yml @@ -0,0 +1,35 @@ +pages: + # key is the dirname of pages in src/pages + home: + # 首页配置 + zh-cn: + title: 'XX开放平台' + keywords: '开放平台,Open,Api' + description: '一站式开放接口服务平台,可快速搭建起自己的开放平台' + # home config + en-us: + title: 'XX开放平台' + keywords: '开放平台,Open,Api' + description: '一站式开放接口服务平台,可快速搭建起自己的开放平台' + community: + # 社区页配置 + zh-cn: + title: '网页标签title' + keywords: '关键词1,关键词2' + description: '页面内容简介' + # community page config + en-us: + title: 'page title' + keywords: 'keyword1,keyword2' + description: 'page description' + blog: + # 博客列表页配置 + zh-cn: + title: '网页标签title' + keywords: '关键词1,关键词2' + description: '页面内容简介' + en-us: + # blog list page config + title: 'page title' + keywords: 'keyword1,keyword2' + description: 'page description' diff --git a/sop-website/sop-portal/en-us/blog/blog1.html b/sop-website/sop-portal/en-us/blog/blog1.html new file mode 100644 index 00000000..de746ffb --- /dev/null +++ b/sop-website/sop-portal/en-us/blog/blog1.html @@ -0,0 +1,26 @@ + + + + + + + + + + blog1 + + + + +

blog1

+

it supports the resolution of meta data,the text between ---(at least three-)written in the format of key:value,will be resolved to md_json/blog.jsonfilename and __html are preserved.

+

filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text

+
+ + + + + + diff --git a/sop-website/sop-portal/en-us/blog/blog1.json b/sop-website/sop-portal/en-us/blog/blog1.json new file mode 100644 index 00000000..367933f5 --- /dev/null +++ b/sop-website/sop-portal/en-us/blog/blog1.json @@ -0,0 +1,9 @@ +{ + "filename": "blog1.md", + "__html": "

blog1

\n

it supports the resolution of meta data,the text between ---(at least three-)written in the format of key:value,will be resolved to md_json/blog.jsonfilename and __html are preserved.

\n

filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text

\n", + "link": "/en-us/blog/blog1.html", + "meta": { + "key1": "hello", + "key2": "world" + } +} \ No newline at end of file diff --git a/sop-website/sop-portal/en-us/blog/blog2.html b/sop-website/sop-portal/en-us/blog/blog2.html new file mode 100644 index 00000000..cd295cbe --- /dev/null +++ b/sop-website/sop-portal/en-us/blog/blog2.html @@ -0,0 +1,26 @@ + + + + + + + + + + blog2 + + + + +

blog2

+

it supports the resolution of meta data,the text between ---(at least three-)written in the format of key:value,will be resolved to md_json/blog.jsonfilename and __html are preserved.

+

filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text

+
+ + + + + + diff --git a/sop-website/sop-portal/en-us/blog/blog2.json b/sop-website/sop-portal/en-us/blog/blog2.json new file mode 100644 index 00000000..56fb8512 --- /dev/null +++ b/sop-website/sop-portal/en-us/blog/blog2.json @@ -0,0 +1,9 @@ +{ + "filename": "blog2.md", + "__html": "

blog2

\n

it supports the resolution of meta data,the text between ---(at least three-)written in the format of key:value,will be resolved to md_json/blog.jsonfilename and __html are preserved.

\n

filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text

\n", + "link": "/en-us/blog/blog2.html", + "meta": { + "key1": "hello", + "key2": "world" + } +} \ No newline at end of file diff --git a/sop-website/sop-portal/en-us/blog/blog3.html b/sop-website/sop-portal/en-us/blog/blog3.html new file mode 100644 index 00000000..abb7e217 --- /dev/null +++ b/sop-website/sop-portal/en-us/blog/blog3.html @@ -0,0 +1,26 @@ + + + + + + + + + + blog3 + + + + +

blog3

+

it supports the resolution of meta data,the text between ---(at least three-)written in the format of key:value,will be resolved to md_json/blog.jsonfilename and __html are preserved.

+

filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text

+
+ + + + + + diff --git a/sop-website/sop-portal/en-us/blog/blog3.json b/sop-website/sop-portal/en-us/blog/blog3.json new file mode 100644 index 00000000..081163f9 --- /dev/null +++ b/sop-website/sop-portal/en-us/blog/blog3.json @@ -0,0 +1,9 @@ +{ + "filename": "blog3.md", + "__html": "

blog3

\n

it supports the resolution of meta data,the text between ---(at least three-)written in the format of key:value,will be resolved to md_json/blog.jsonfilename and __html are preserved.

\n

filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text

\n", + "link": "/en-us/blog/blog3.html", + "meta": { + "key1": "hello", + "key2": "world" + } +} \ No newline at end of file diff --git a/sop-website/sop-portal/en-us/blog/blog4.html b/sop-website/sop-portal/en-us/blog/blog4.html new file mode 100644 index 00000000..6463eb29 --- /dev/null +++ b/sop-website/sop-portal/en-us/blog/blog4.html @@ -0,0 +1,26 @@ + + + + + + + + + + blog4 + + + + +

blog4

+

it supports the resolution of meta data,the text between ---(at least three-)written in the format of key:value,will be resolved to md_json/blog.jsonfilename and __html are preserved.

+

filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text

+
+ + + + + + diff --git a/sop-website/sop-portal/en-us/blog/blog4.json b/sop-website/sop-portal/en-us/blog/blog4.json new file mode 100644 index 00000000..c81ca9b9 --- /dev/null +++ b/sop-website/sop-portal/en-us/blog/blog4.json @@ -0,0 +1,9 @@ +{ + "filename": "blog4.md", + "__html": "

blog4

\n

it supports the resolution of meta data,the text between ---(at least three-)written in the format of key:value,will be resolved to md_json/blog.jsonfilename and __html are preserved.

\n

filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text

\n", + "link": "/en-us/blog/blog4.html", + "meta": { + "key1": "hello", + "key2": "world" + } +} \ No newline at end of file diff --git a/sop-website/sop-portal/en-us/blog/blog5.html b/sop-website/sop-portal/en-us/blog/blog5.html new file mode 100644 index 00000000..532f4817 --- /dev/null +++ b/sop-website/sop-portal/en-us/blog/blog5.html @@ -0,0 +1,26 @@ + + + + + + + + + + blog5 + + + + +

blog5

+

it supports the resolution of meta data,the text between ---(at least three-)written in the format of key:value,will be resolved to md_json/blog.jsonfilename and __html are preserved.

+

filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text

+
+ + + + + + diff --git a/sop-website/sop-portal/en-us/blog/blog5.json b/sop-website/sop-portal/en-us/blog/blog5.json new file mode 100644 index 00000000..9c18fd2d --- /dev/null +++ b/sop-website/sop-portal/en-us/blog/blog5.json @@ -0,0 +1,9 @@ +{ + "filename": "blog5.md", + "__html": "

blog5

\n

it supports the resolution of meta data,the text between ---(at least three-)written in the format of key:value,will be resolved to md_json/blog.jsonfilename and __html are preserved.

\n

filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text

\n", + "link": "/en-us/blog/blog5.html", + "meta": { + "key1": "hello", + "key2": "world" + } +} \ No newline at end of file diff --git a/sop-website/sop-portal/en-us/blog/blog6.html b/sop-website/sop-portal/en-us/blog/blog6.html new file mode 100644 index 00000000..c56d2053 --- /dev/null +++ b/sop-website/sop-portal/en-us/blog/blog6.html @@ -0,0 +1,26 @@ + + + + + + + + + + blog6 + + + + +

blog6

+

it supports the resolution of meta data,the text between ---(at least three-)written in the format of key:value,will be resolved to md_json/blog.jsonfilename and __html are preserved.

+

filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text

+
+ + + + + + diff --git a/sop-website/sop-portal/en-us/blog/blog6.json b/sop-website/sop-portal/en-us/blog/blog6.json new file mode 100644 index 00000000..b6fae5ed --- /dev/null +++ b/sop-website/sop-portal/en-us/blog/blog6.json @@ -0,0 +1,9 @@ +{ + "filename": "blog6.md", + "__html": "

blog6

\n

it supports the resolution of meta data,the text between ---(at least three-)written in the format of key:value,will be resolved to md_json/blog.jsonfilename and __html are preserved.

\n

filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text

\n", + "link": "/en-us/blog/blog6.html", + "meta": { + "key1": "hello", + "key2": "world" + } +} \ No newline at end of file diff --git a/sop-website/sop-portal/en-us/blog/blog7.html b/sop-website/sop-portal/en-us/blog/blog7.html new file mode 100644 index 00000000..97652bf0 --- /dev/null +++ b/sop-website/sop-portal/en-us/blog/blog7.html @@ -0,0 +1,26 @@ + + + + + + + + + + blog7 + + + + +

blog7

+

it supports the resolution of meta data,the text between ---(at least three-)written in the format of key:value,will be resolved to md_json/blog.jsonfilename and __html are preserved.

+

filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text

+
+ + + + + + diff --git a/sop-website/sop-portal/en-us/blog/blog7.json b/sop-website/sop-portal/en-us/blog/blog7.json new file mode 100644 index 00000000..ceb0b04f --- /dev/null +++ b/sop-website/sop-portal/en-us/blog/blog7.json @@ -0,0 +1,9 @@ +{ + "filename": "blog7.md", + "__html": "

blog7

\n

it supports the resolution of meta data,the text between ---(at least three-)written in the format of key:value,will be resolved to md_json/blog.jsonfilename and __html are preserved.

\n

filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text filling text

\n", + "link": "/en-us/blog/blog7.html", + "meta": { + "key1": "hello", + "key2": "world" + } +} \ No newline at end of file diff --git a/sop-website/sop-portal/en-us/blog/index.html b/sop-website/sop-portal/en-us/blog/index.html new file mode 100644 index 00000000..2cdcfd77 --- /dev/null +++ b/sop-website/sop-portal/en-us/blog/index.html @@ -0,0 +1,23 @@ + + + + + + + + + + page title + + + + + + + + + + + diff --git a/sop-website/sop-portal/en-us/community/index.html b/sop-website/sop-portal/en-us/community/index.html new file mode 100644 index 00000000..3505c6e1 --- /dev/null +++ b/sop-website/sop-portal/en-us/community/index.html @@ -0,0 +1,23 @@ + + + + + + + + + + page title + + + + +
Community

Events & News

this is the title

this is the content

May 12nd,2018

this is the title

this is the content

May 12nd,2018

this is the title

this is the content

May 12nd,2018

this is the title

this is the content

May 12nd,2018

this is the title

this is the content

May 12nd,2018

this is the title

this is the content

May 12nd,2018

this is the title

this is the content

May 12nd,2018

Eco System

Eco1

Eco System1

Eco2

Eco System2

Talk To Us

Feel free to contact us via the following channel.

Contributor Guide

some description

Mailing List

this is the content

Issue

this is the content

Documents

this is the content

Pull Request

this is the content

+ + + + + + diff --git a/sop-website/sop-portal/en-us/docs/demo1.html b/sop-website/sop-portal/en-us/docs/demo1.html new file mode 100644 index 00000000..813a80cc --- /dev/null +++ b/sop-website/sop-portal/en-us/docs/demo1.html @@ -0,0 +1,273 @@ + + + + + + + + + + demo1 + + + + +
Documentation

example of footnote

+

this is a demo of the usage of footnote. +this is footnote1 [1]。click it and see what will happen。

+

this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text. +this is a long text.

+
+
+
    +
  1. this is the explanation of footnote ↩︎

    +
  2. +
+
+
+ + + + + + diff --git a/sop-website/sop-portal/en-us/docs/demo1.json b/sop-website/sop-portal/en-us/docs/demo1.json new file mode 100644 index 00000000..623aadb9 --- /dev/null +++ b/sop-website/sop-portal/en-us/docs/demo1.json @@ -0,0 +1,6 @@ +{ + "filename": "demo1.md", + "__html": "

example of footnote

\n

this is a demo of the usage of footnote.\nthis is footnote1 [1]。click it and see what will happen。

\n

this is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.\nthis is a long text.

\n
\n
\n
    \n
  1. this is the explanation of footnote ↩︎

    \n
  2. \n
\n
\n", + "link": "/en-us/docs/demo1.html", + "meta": {} +} \ No newline at end of file diff --git a/sop-website/sop-portal/en-us/docs/demo2.html b/sop-website/sop-portal/en-us/docs/demo2.html new file mode 100644 index 00000000..28922559 --- /dev/null +++ b/sop-website/sop-portal/en-us/docs/demo2.html @@ -0,0 +1,36 @@ + + + + + + + + + + demo2 + + + + +
Documentation

demo for reference of md or image for relative path

+

if you want to reference to another md, the path can be written in the format of relative path under the circumstance of file system. +click here: demo1.md

+

the same is to image. +

+

demo for math typesetting(katex)

+

E=mc2E = mc^2

+

x=a1n+a2n+a3nx = a_{1}^n + a_{2}^n + a_{3}^n +

+

X3\sqrt[3]{X} +

+

5x\sqrt{5 - x} +

+
+ + + + + + diff --git a/sop-website/sop-portal/en-us/docs/demo2.json b/sop-website/sop-portal/en-us/docs/demo2.json new file mode 100644 index 00000000..5b03c4c8 --- /dev/null +++ b/sop-website/sop-portal/en-us/docs/demo2.json @@ -0,0 +1,6 @@ +{ + "filename": "demo2.md", + "__html": "

demo for reference of md or image for relative path

\n

if you want to reference to another md, the path can be written in the format of relative path under the circumstance of file system.\nclick here: demo1.md

\n

the same is to image.\n\"\"

\n

demo for math typesetting(katex)

\n

E=mc2E = mc^2E=mc2

\n

x=a1n+a2n+a3nx = a_{1}^n + a_{2}^n + a_{3}^n\nx=a1n+a2n+a3n

\n

X3\\sqrt[3]{X}\n3X

\n

5x\\sqrt{5 - x}\n5x

\n", + "link": "/en-us/docs/demo2.html", + "meta": {} +} \ No newline at end of file diff --git a/sop-website/sop-portal/en-us/docs/dir/demo3.html b/sop-website/sop-portal/en-us/docs/dir/demo3.html new file mode 100644 index 00000000..dd3f1a7a --- /dev/null +++ b/sop-website/sop-portal/en-us/docs/dir/demo3.html @@ -0,0 +1,28 @@ + + + + + + + + + + demo3 + + + + +
Documentation

demo for reference of md or image for relative path, across directory

+

if you want to reference to another md, the path can be written in the format of relative path under the circumstance of file system. +click here: demo1.md

+

the same is to image. +

+
+ + + + + + diff --git a/sop-website/sop-portal/en-us/docs/dir/demo3.json b/sop-website/sop-portal/en-us/docs/dir/demo3.json new file mode 100644 index 00000000..6690dcef --- /dev/null +++ b/sop-website/sop-portal/en-us/docs/dir/demo3.json @@ -0,0 +1,6 @@ +{ + "filename": "demo3.md", + "__html": "

demo for reference of md or image for relative path, across directory

\n

if you want to reference to another md, the path can be written in the format of relative path under the circumstance of file system.\nclick here: demo1.md

\n

the same is to image.\n\"\"

\n", + "link": "/en-us/docs/dir/demo3.html", + "meta": {} +} \ No newline at end of file diff --git a/sop-website/sop-portal/en-us/index.html b/sop-website/sop-portal/en-us/index.html new file mode 100644 index 00000000..e222e7c1 --- /dev/null +++ b/sop-website/sop-portal/en-us/index.html @@ -0,0 +1,23 @@ + + + + + + + + + + XX开放平台 + + + + +

brandName

some description of product

Feature List

  • feature1

    feature description

  • feature2

    feature description

  • feature3

    feature description

  • feature4

    feature description

  • feature5

    feature description

  • feature6

    feature description

+ + + + + + diff --git a/sop-website/sop-portal/gulpfile.js b/sop-website/sop-portal/gulpfile.js new file mode 100644 index 00000000..bef72348 --- /dev/null +++ b/sop-website/sop-portal/gulpfile.js @@ -0,0 +1,60 @@ +require('babel-register')(); +const gulp = require('gulp'); +const gutil = require('gulp-util'); +const webpack = require('webpack'); +const opn = require('opn'); +const WebpackDevServer = require('webpack-dev-server'); +const siteConfig = require('./site_config/site').default; +const webpackConfig = require('./webpack.config.js'); + +const port = siteConfig.port || 8080; + +// The development server (the recommended option for development) +gulp.task('default', ['webpack-dev-server']); + +// Production build +gulp.task('build', ['webpack:build']); + +gulp.task('webpack-dev-server', () => { + // modify some webpack config options + const myConfig = Object.create(webpackConfig); + myConfig.plugins.push(new webpack.SourceMapDevToolPlugin({})); + // Start a webpack-dev-server + new WebpackDevServer(webpack(myConfig), { + publicPath: `http://127.0.0.1:${port}/build/`, + stats: { + colors: true, + }, + }).listen(port, '127.0.0.1', err => { + if (err) throw new gutil.PluginError('webpack-dev-server', err); + opn(`http://127.0.0.1:${port}/`); + gutil.log('[webpack-dev-server]', `http://127.0.0.1:${port}/webpack-dev-server/index.html`); + }); +}); + +gulp.task('webpack:build', callback => { + // modify some webpack config options + const myConfig = Object.create(webpackConfig); + myConfig.output.publicPath = `${siteConfig.rootPath}/build/`; + myConfig.plugins = myConfig.plugins.concat( + new webpack.DefinePlugin({ + 'process.env': { + // This has effect on the react lib size + NODE_ENV: JSON.stringify('production'), + }, + }), + new webpack.optimize.UglifyJsPlugin() + ); + + // run webpack + webpack(myConfig, (err, stats) => { + if (err) throw new gutil.PluginError('webpack:build', err); + gutil.log( + '[webpack:build]', + stats.toString({ + colors: true, + }) + ); + callback(); + }); +}); diff --git a/sop-website/sop-portal/img/alibaba.png b/sop-website/sop-portal/img/alibaba.png new file mode 100644 index 00000000..32f736b2 Binary files /dev/null and b/sop-website/sop-portal/img/alibaba.png differ diff --git a/sop-website/sop-portal/img/alibaba_hover.png b/sop-website/sop-portal/img/alibaba_hover.png new file mode 100644 index 00000000..bbc81d0e Binary files /dev/null and b/sop-website/sop-portal/img/alibaba_hover.png differ diff --git a/sop-website/sop-portal/img/architecture.png b/sop-website/sop-portal/img/architecture.png new file mode 100644 index 00000000..d010dfb0 Binary files /dev/null and b/sop-website/sop-portal/img/architecture.png differ diff --git a/sop-website/sop-portal/img/brhtqqzh.jpeg b/sop-website/sop-portal/img/brhtqqzh.jpeg new file mode 100755 index 00000000..df36b234 Binary files /dev/null and b/sop-website/sop-portal/img/brhtqqzh.jpeg differ diff --git a/sop-website/sop-portal/img/docsite.ico b/sop-website/sop-portal/img/docsite.ico new file mode 100644 index 00000000..224e8d20 Binary files /dev/null and b/sop-website/sop-portal/img/docsite.ico differ diff --git a/sop-website/sop-portal/img/documents.png b/sop-website/sop-portal/img/documents.png new file mode 100644 index 00000000..343c0d1b Binary files /dev/null and b/sop-website/sop-portal/img/documents.png differ diff --git a/sop-website/sop-portal/img/favicon.ico b/sop-website/sop-portal/img/favicon.ico new file mode 100644 index 00000000..5d417587 Binary files /dev/null and b/sop-website/sop-portal/img/favicon.ico differ diff --git a/sop-website/sop-portal/img/feature_hogh.png b/sop-website/sop-portal/img/feature_hogh.png new file mode 100644 index 00000000..e0b1a8b2 Binary files /dev/null and b/sop-website/sop-portal/img/feature_hogh.png differ diff --git a/sop-website/sop-portal/img/feature_loadbalances.png b/sop-website/sop-portal/img/feature_loadbalances.png new file mode 100644 index 00000000..05efb58b Binary files /dev/null and b/sop-website/sop-portal/img/feature_loadbalances.png differ diff --git a/sop-website/sop-portal/img/feature_maintenance.png b/sop-website/sop-portal/img/feature_maintenance.png new file mode 100644 index 00000000..f1ae0fdb Binary files /dev/null and b/sop-website/sop-portal/img/feature_maintenance.png differ diff --git a/sop-website/sop-portal/img/feature_runtime.png b/sop-website/sop-portal/img/feature_runtime.png new file mode 100644 index 00000000..c09b8627 Binary files /dev/null and b/sop-website/sop-portal/img/feature_runtime.png differ diff --git a/sop-website/sop-portal/img/feature_service.png b/sop-website/sop-portal/img/feature_service.png new file mode 100644 index 00000000..bb5be229 Binary files /dev/null and b/sop-website/sop-portal/img/feature_service.png differ diff --git a/sop-website/sop-portal/img/feature_transpart.png b/sop-website/sop-portal/img/feature_transpart.png new file mode 100644 index 00000000..bfea1349 Binary files /dev/null and b/sop-website/sop-portal/img/feature_transpart.png differ diff --git a/sop-website/sop-portal/img/home_1.png b/sop-website/sop-portal/img/home_1.png new file mode 100644 index 00000000..139a85c1 Binary files /dev/null and b/sop-website/sop-portal/img/home_1.png differ diff --git a/sop-website/sop-portal/img/home_2.png b/sop-website/sop-portal/img/home_2.png new file mode 100644 index 00000000..d5e7cff7 Binary files /dev/null and b/sop-website/sop-portal/img/home_2.png differ diff --git a/sop-website/sop-portal/img/home_3.png b/sop-website/sop-portal/img/home_3.png new file mode 100644 index 00000000..528117c8 Binary files /dev/null and b/sop-website/sop-portal/img/home_3.png differ diff --git a/sop-website/sop-portal/img/home_5.png b/sop-website/sop-portal/img/home_5.png new file mode 100644 index 00000000..01969feb Binary files /dev/null and b/sop-website/sop-portal/img/home_5.png differ diff --git a/sop-website/sop-portal/img/issue.png b/sop-website/sop-portal/img/issue.png new file mode 100644 index 00000000..e1a026d9 Binary files /dev/null and b/sop-website/sop-portal/img/issue.png differ diff --git a/sop-website/sop-portal/img/logo.png b/sop-website/sop-portal/img/logo.png new file mode 100644 index 00000000..b201d8c0 Binary files /dev/null and b/sop-website/sop-portal/img/logo.png differ diff --git a/sop-website/sop-portal/img/logo_old.png b/sop-website/sop-portal/img/logo_old.png new file mode 100644 index 00000000..bf8cda70 Binary files /dev/null and b/sop-website/sop-portal/img/logo_old.png differ diff --git a/sop-website/sop-portal/img/logo_old_white.png b/sop-website/sop-portal/img/logo_old_white.png new file mode 100644 index 00000000..a36bbc41 Binary files /dev/null and b/sop-website/sop-portal/img/logo_old_white.png differ diff --git a/sop-website/sop-portal/img/logo_white.png b/sop-website/sop-portal/img/logo_white.png new file mode 100644 index 00000000..a09738f8 Binary files /dev/null and b/sop-website/sop-portal/img/logo_white.png differ diff --git a/sop-website/sop-portal/img/mailinglist.png b/sop-website/sop-portal/img/mailinglist.png new file mode 100644 index 00000000..a219fe88 Binary files /dev/null and b/sop-website/sop-portal/img/mailinglist.png differ diff --git a/sop-website/sop-portal/img/mailinglist_hover.png b/sop-website/sop-portal/img/mailinglist_hover.png new file mode 100644 index 00000000..785705c9 Binary files /dev/null and b/sop-website/sop-portal/img/mailinglist_hover.png differ diff --git a/sop-website/sop-portal/img/pullrequest.png b/sop-website/sop-portal/img/pullrequest.png new file mode 100644 index 00000000..fc05edfb Binary files /dev/null and b/sop-website/sop-portal/img/pullrequest.png differ diff --git a/sop-website/sop-portal/img/quick_start.png b/sop-website/sop-portal/img/quick_start.png new file mode 100644 index 00000000..44b7069d Binary files /dev/null and b/sop-website/sop-portal/img/quick_start.png differ diff --git a/sop-website/sop-portal/img/segmentfault.png b/sop-website/sop-portal/img/segmentfault.png new file mode 100644 index 00000000..09d2f3da Binary files /dev/null and b/sop-website/sop-portal/img/segmentfault.png differ diff --git a/sop-website/sop-portal/img/segmentfault_hover.png b/sop-website/sop-portal/img/segmentfault_hover.png new file mode 100644 index 00000000..56707d02 Binary files /dev/null and b/sop-website/sop-portal/img/segmentfault_hover.png differ diff --git a/sop-website/sop-portal/img/system/arrow_down.png b/sop-website/sop-portal/img/system/arrow_down.png new file mode 100644 index 00000000..2924eb55 Binary files /dev/null and b/sop-website/sop-portal/img/system/arrow_down.png differ diff --git a/sop-website/sop-portal/img/system/arrow_right.png b/sop-website/sop-portal/img/system/arrow_right.png new file mode 100644 index 00000000..4555b12b Binary files /dev/null and b/sop-website/sop-portal/img/system/arrow_right.png differ diff --git a/sop-website/sop-portal/img/system/blog.png b/sop-website/sop-portal/img/system/blog.png new file mode 100644 index 00000000..579f759d Binary files /dev/null and b/sop-website/sop-portal/img/system/blog.png differ diff --git a/sop-website/sop-portal/img/system/community.png b/sop-website/sop-portal/img/system/community.png new file mode 100644 index 00000000..5836a4af Binary files /dev/null and b/sop-website/sop-portal/img/system/community.png differ diff --git a/sop-website/sop-portal/img/system/docs.png b/sop-website/sop-portal/img/system/docs.png new file mode 100644 index 00000000..f41db69e Binary files /dev/null and b/sop-website/sop-portal/img/system/docs.png differ diff --git a/sop-website/sop-portal/img/system/docs_hover.png b/sop-website/sop-portal/img/system/docs_hover.png new file mode 100644 index 00000000..3568312e Binary files /dev/null and b/sop-website/sop-portal/img/system/docs_hover.png differ diff --git a/sop-website/sop-portal/img/system/docs_normal.png b/sop-website/sop-portal/img/system/docs_normal.png new file mode 100644 index 00000000..4f31e506 Binary files /dev/null and b/sop-website/sop-portal/img/system/docs_normal.png differ diff --git a/sop-website/sop-portal/img/system/menu_gray.png b/sop-website/sop-portal/img/system/menu_gray.png new file mode 100644 index 00000000..9838a877 Binary files /dev/null and b/sop-website/sop-portal/img/system/menu_gray.png differ diff --git a/sop-website/sop-portal/img/system/menu_white.png b/sop-website/sop-portal/img/system/menu_white.png new file mode 100644 index 00000000..5943a141 Binary files /dev/null and b/sop-website/sop-portal/img/system/menu_white.png differ diff --git a/sop-website/sop-portal/img/system/next.png b/sop-website/sop-portal/img/system/next.png new file mode 100644 index 00000000..d65f97d3 Binary files /dev/null and b/sop-website/sop-portal/img/system/next.png differ diff --git a/sop-website/sop-portal/img/system/prev.png b/sop-website/sop-portal/img/system/prev.png new file mode 100644 index 00000000..a68639b5 Binary files /dev/null and b/sop-website/sop-portal/img/system/prev.png differ diff --git a/sop-website/sop-portal/img/users_alibaba.png b/sop-website/sop-portal/img/users_alibaba.png new file mode 100644 index 00000000..25c277d4 Binary files /dev/null and b/sop-website/sop-portal/img/users_alibaba.png differ diff --git a/sop-website/sop-portal/img/weibo.png b/sop-website/sop-portal/img/weibo.png new file mode 100644 index 00000000..89ffd326 Binary files /dev/null and b/sop-website/sop-portal/img/weibo.png differ diff --git a/sop-website/sop-portal/img/weibo_hover.png b/sop-website/sop-portal/img/weibo_hover.png new file mode 100644 index 00000000..9d800ff3 Binary files /dev/null and b/sop-website/sop-portal/img/weibo_hover.png differ diff --git a/sop-website/sop-portal/index.html b/sop-website/sop-portal/index.html new file mode 100644 index 00000000..2518eab7 --- /dev/null +++ b/sop-website/sop-portal/index.html @@ -0,0 +1,18 @@ + + + + + + + + + + diff --git a/sop-website/sop-portal/md_json/blog.json b/sop-website/sop-portal/md_json/blog.json new file mode 100644 index 00000000..464d75c0 --- /dev/null +++ b/sop-website/sop-portal/md_json/blog.json @@ -0,0 +1,22 @@ +{ + "en-us": [ + { + "filename": "blog1.md", + "link": "/en-us/blog/blog1.html", + "meta": { + "key1": "hello", + "key2": "world" + } + } + ], + "zh-cn": [ + { + "filename": "blog1.md", + "link": "/zh-cn/blog/blog1.html", + "meta": { + "key1": "hello", + "key2": "world" + } + } + ] +} \ No newline at end of file diff --git a/sop-website/sop-portal/md_json/docs.json b/sop-website/sop-portal/md_json/docs.json new file mode 100644 index 00000000..e00de912 --- /dev/null +++ b/sop-website/sop-portal/md_json/docs.json @@ -0,0 +1,36 @@ +{ + "en-us": [ + { + "filename": "demo1.md", + "link": "/en-us/docs/demo1.html", + "meta": {} + }, + { + "filename": "demo2.md", + "link": "/en-us/docs/demo2.html", + "meta": {} + }, + { + "filename": "demo3.md", + "link": "/en-us/docs/dir/demo3.html", + "meta": {} + } + ], + "zh-cn": [ + { + "filename": "demo1.md", + "link": "/zh-cn/docs/demo1.html", + "meta": {} + }, + { + "filename": "demo2.md", + "link": "/zh-cn/docs/demo2.html", + "meta": {} + }, + { + "filename": "demo3.md", + "link": "/zh-cn/docs/dir/demo3.html", + "meta": {} + } + ] +} \ No newline at end of file diff --git a/sop-website/sop-portal/package.json b/sop-website/sop-portal/package.json new file mode 100644 index 00000000..10f78e0d --- /dev/null +++ b/sop-website/sop-portal/package.json @@ -0,0 +1,53 @@ +{ + "name": "site", + "version": "0.0.1", + "private": true, + "scripts": { + "start": "docsite start", + "build": "docsite build" + }, + "devDependencies": { + "docsite": "^1.1.0", + "babel-core": "6.23.1", + "babel-eslint": "^6.0.5", + "babel-loader": "6.4.0", + "babel-plugin-transform-class-properties": "^6.24.1", + "babel-plugin-transform-decorators-legacy": "^1.3.4", + "babel-plugin-transform-object-assign": "^6.22.0", + "babel-plugin-transform-object-rest-spread": "^6.23.0", + "babel-plugin-transform-runtime": "^6.23.0", + "babel-preset-es2015": "6.22.0", + "babel-preset-react": "6.23.0", + "babel-preset-stage-0": "6.22.0", + "babel-register": "^6.26.0", + "css-loader": "0.6.12", + "eslint": "^3.6.0", + "eslint-config-ali": "^1.0.0", + "eslint-plugin-import": "*", + "eslint-plugin-react": "*", + "extract-text-webpack-plugin": "^2.1.2", + "gulp": "3.9.1", + "gulp-util": "2.2.20", + "json-loader": "*", + "node-libs-browser": "2.0.0", + "node-sass": "^4.7.2", + "opn": "^5.3.0", + "raw-loader": "^0.5.1", + "react": "16.5.2", + "react-dom": "16.5.2", + "sass-loader": "6.0.2", + "style-loader": "0.6.5", + "webpack": "^2.6.1", + "webpack-dev-server": "^2.4.5" + }, + "dependencies": { + "classnames": "^2.2.5", + "core-decorators": "^0.20.0", + "js-cookie": "^2.2.0", + "react": "^16.5.2", + "react-dom": "^16.5.2", + "react-scroll": "^1.7.9", + "react-tilt": "^0.1.4", + "whatwg-fetch": "^2.0.4" + } +} diff --git a/sop-website/sop-portal/redirect.ejs b/sop-website/sop-portal/redirect.ejs new file mode 100644 index 00000000..25f7b6be --- /dev/null +++ b/sop-website/sop-portal/redirect.ejs @@ -0,0 +1,18 @@ + + + + + + + + + + diff --git a/sop-website/sop-portal/site_config/blog.js b/sop-website/sop-portal/site_config/blog.js new file mode 100644 index 00000000..cecd311b --- /dev/null +++ b/sop-website/sop-portal/site_config/blog.js @@ -0,0 +1,112 @@ +export default { + 'en-us': { + barText: 'Blog', + postsTitle: 'All posts', + list: [ + { + title: 'The first blog', + author: 'author1', + dateStr: 'May 12nd,2018', + desc: 'Blog description, some text to sum up the main content of the blog', + link: '/en-us/blog/blog1.html', + }, + { + title: 'The second blog', + author: 'author2', + dateStr: 'May 2nd,2018', + desc: 'Blog description, some text to sum up the main content of the blog', + link: '/en-us/blog/blog2.html', + }, + { + title: 'The third blog', + author: 'author3', + dateStr: 'April 25th,2018', + desc: 'Blog description, some text to sum up the main content of the blog', + link: '/en-us/blog/blog3.html', + }, + { + title: 'The forth blog', + author: 'author4', + dateStr: 'April 22nd,2018', + desc: 'Blog description, some text to sum up the main content of the blog', + link: '/en-us/blog/blog4.html', + }, + { + title: 'The fifth blog', + author: 'author5', + dateStr: 'April 22nd,2018', + desc: 'Blog description, some text to sum up the main content of the blog', + link: '/en-us/blog/blog5.html', + }, + { + title: 'The sixth blog', + author: 'author6', + dateStr: 'April 22nd,2018', + desc: 'Blog description, some text to sum up the main content of the blog', + link: '/en-us/blog/blog6.html', + }, + { + title: 'The seventh blog', + author: 'author7', + dateStr: 'April 22nd,2018', + desc: 'Blog description, some text to sum up the main content of the blog', + link: '/en-us/blog/blog7.html', + }, + ], + }, + 'zh-cn': { + barText: '博客', + postsTitle: '所有文章', + list: [ + { + title: '第一篇博客', + author: 'author1', + dateStr: 'May 12nd,2018', + desc: '博客描述,总结博客的主体内容', + link: '/zh-cn/blog/blog1.html', + }, + { + title: '第二篇博客', + author: 'author2', + dateStr: 'May 2nd,2018', + desc: '博客描述,总结博客的主体内容', + link: '/zh-cn/blog/blog2.html', + }, + { + title: '第三篇博客', + author: 'author3', + dateStr: 'April 25th,2018', + desc: '博客描述,总结博客的主体内容', + link: '/zh-cn/blog/blog3.html', + }, + { + title: '第四篇博客', + author: 'author4', + dateStr: 'April 22nd,2018', + desc: '博客描述,总结博客的主体内容', + link: '/zh-cn/blog/blog4.html', + }, + { + title: '第五篇博客', + author: 'author5', + dateStr: 'April 22nd,2018', + desc: '博客描述,总结博客的主体内容', + link: '/zh-cn/blog/blog5.html', + }, + { + title: '第六篇博客', + author: 'author6', + dateStr: 'April 22nd,2018', + desc: '博客描述,总结博客的主体内容', + link: '/zh-cn/blog/blog6.html', + }, + { + title: '第七篇博客', + author: 'author7', + dateStr: 'April 22nd,2018', + desc: '博客描述,总结博客的主体内容', + link: '/zh-cn/blog/blog7.html', + }, + ], + }, +}; diff --git a/sop-website/sop-portal/site_config/community.jsx b/sop-website/sop-portal/site_config/community.jsx new file mode 100644 index 00000000..1affe896 --- /dev/null +++ b/sop-website/sop-portal/site_config/community.jsx @@ -0,0 +1,334 @@ +import React from 'react'; + +export default { + 'en-us': { + barText: 'Community', + events: { + title: 'Events & News', + list: [ + { + img: '/img/brhtqqzh.jpeg', + title: 'this is the title', + content: 'this is the content', + dateStr: 'May 12nd,2018', + link: '/en-us/blog/blog1.html', + }, + { + img: '/img/brhtqqzh.jpeg', + title: 'this is the title', + content: 'this is the content', + dateStr: 'May 12nd,2018', + link: '/en-us/blog/blog2.html', + }, + { + img: '/img/brhtqqzh.jpeg', + title: 'this is the title', + content: 'this is the content', + dateStr: 'May 12nd,2018', + link: '/en-us/blog/blog3.html', + }, + { + img: '/img/brhtqqzh.jpeg', + title: 'this is the title', + content: 'this is the content', + dateStr: 'May 12nd,2018', + link: '/en-us/blog/blog4.html', + }, + { + img: '/img/brhtqqzh.jpeg', + title: 'this is the title', + content: 'this is the content', + dateStr: 'May 12nd,2018', + link: '/en-us/blog/blog5.html', + }, + { + img: '/img/brhtqqzh.jpeg', + title: 'this is the title', + content: 'this is the content', + dateStr: 'May 12nd,2018', + link: '/en-us/blog/blog6.html', + }, + { + img: '/img/brhtqqzh.jpeg', + title: 'this is the title', + content: 'this is the content', + dateStr: 'May 12nd,2018', + link: '/en-us/blog/blog7.html', + }, + ] + }, + contacts: { + title: 'Talk To Us', + desc: 'Feel free to contact us via the following channel.', + list: [ + { + img: '/img/mailinglist.png', + imgHover: '/img/mailinglist_hover.png', + title: 'Mailing List', + link: '' + }, + { + img: '/img/alibaba.png', + imgHover: '/img/alibaba_hover.png', + title: 'gitter', + link: '', + }, + { + img: '/img/segmentfault.png', + imgHover: '/img/segmentfault_hover.png', + title: 'Segment Fault', + link: '' + }, + { + img: '/img/weibo.png', + imgHover: '/img/weibo_hover.png', + title: 'weibo', + link: '', + }, + ], + }, + contributorGuide: { + title: 'Contributor Guide', + desc: 'some description', + list: [ + { + img: '/img/mailinglist.png', + title: 'Mailing List', + content: this is the content, + }, + { + img: '/img/issue.png', + title: 'Issue', + content: this is the content, + }, + { + img: '/img/documents.png', + title: 'Documents', + content: this is the content, + }, + { + img: '/img/pullrequest.png', + title: 'Pull Request', + content: this is the content, + }, + ], + }, + ecos: { + title: 'Eco System', + list: [ + { + title: 'Eco1', + content: Eco System1, + tags: [ + { + text: 'tag1', + link: '', + bgColor: '#7A63FC', + }, + { + text: 'tag2', + link: '', + bgColor: '#00D0D9', + }, + { + text: 'tag3', + link: '', + bgColor: '#00D0D9', + }, + ], + }, + { + title: 'Eco2', + content: Eco System2, + tags: [ + { + text: 'tag1', + link: '', + bgColor: '#7A63FC', + }, + { + text: 'tag2', + link: '', + bgColor: '#00D0D9', + }, + { + text: 'tag3', + link: '/en-us/docs/demo1.html', + bgColor: '#00D0D9', + }, + { + text: 'tag4', + link: '', + bgColor: '#00D0D9', + }, + ], + }, + ], + }, + }, + 'zh-cn': { + barText: '社区', + events: { + title: '事件 & 新闻', + list: [ + { + img: '/img/brhtqqzh.jpeg', + title: '这是标题', + content: '这是内容', + dateStr: 'May 12nd,2018', + link: '/zh-cn/blog/blog1.html', + }, + { + img: '/img/brhtqqzh.jpeg', + title: '这是标题', + content: '这是内容', + dateStr: 'May 12nd,2018', + link: '/zh-cn/blog/blog2.html', + }, + { + img: '/img/brhtqqzh.jpeg', + title: '这是标题', + content: '这是内容', + dateStr: 'May 12nd,2018', + link: '/zh-cn/blog/blog3.html', + }, + { + img: '/img/brhtqqzh.jpeg', + title: '这是标题', + content: '这是内容', + dateStr: 'May 12nd,2018', + link: '/zh-cn/blog/blog4.html', + }, + { + img: '/img/brhtqqzh.jpeg', + title: '这是标题', + content: '这是内容', + dateStr: 'May 12nd,2018', + link: '/zh-cn/blog/blog5.html', + }, + { + img: '/img/brhtqqzh.jpeg', + title: '这是标题', + content: '这是内容', + dateStr: 'May 12nd,2018', + link: '/zh-cn/blog/blog6.html', + }, + { + img: '/img/brhtqqzh.jpeg', + title: '这是标题', + content: '这是内容', + dateStr: 'May 12nd,2018', + link: '/zh-cn/blog/blog7.html', + }, + ] + }, + contacts: { + title: '联系我们', + desc: '有问题需要反馈?请通过一下方式联系我们。', + list: [ + { + img: '/img/mailinglist.png', + imgHover: '/img/mailinglist_hover.png', + title: '邮件列表', + link: '' + }, + { + img: '/img/alibaba.png', + imgHover: '/img/alibaba_hover.png', + title: 'gitter', + link: '', + }, + { + img: '/img/segmentfault.png', + imgHover: '/img/segmentfault_hover.png', + title: 'Segment Fault', + link: '' + }, + { + img: '/img/weibo.png', + imgHover: '/img/weibo_hover.png', + title: '微博', + link: '', + }, + ], + }, + contributorGuide: { + title: '贡献指南', + desc: '一些描述', + list: [ + { + img: '/img/mailinglist.png', + title: '邮件列表', + content: 这是描述, + }, + { + img: '/img/issue.png', + title: '报告缺陷', + content: 这是描述, + }, + { + img: '/img/documents.png', + title: '文档', + content: 这是描述, + }, + { + img: '/img/pullrequest.png', + title: 'Pull Request', + content: 这是描述, + }, + ], + }, + ecos: { + title: '生态系统', + list: [ + { + title: '生态系统1', + content: 生态系统1, + tags: [ + { + text: '标签1', + link: '', + bgColor: '#7A63FC', + }, + { + text: '标签2', + link: '', + bgColor: '#00D0D9', + }, + { + text: '标签3', + link: '', + bgColor: '#00D0D9', + }, + ], + }, + { + title: '生态系统2', + content: 生态系统2, + tags: [ + { + text: '标签1', + link: '', + bgColor: '#7A63FC', + }, + { + text: '标签2', + link: '', + bgColor: '#00D0D9', + }, + { + text: '标签3', + link: '/zh-cn/docs/demo1.html', + bgColor: '#00D0D9', + }, + { + text: '标签4', + link: '', + bgColor: '#00D0D9', + }, + ], + }, + ], + }, + }, +}; diff --git a/sop-website/sop-portal/site_config/docs.js b/sop-website/sop-portal/site_config/docs.js new file mode 100644 index 00000000..95ecf679 --- /dev/null +++ b/sop-website/sop-portal/site_config/docs.js @@ -0,0 +1,58 @@ +export default { + 'en-us': { + sidemenu: [ + { + title: 'header title', + children: [ + { + title: 'demo1', + link: '/en-us/docs/demo1.html', + }, + { + title: 'demo2', + link: '/en-us/docs/demo2.html', + }, + { + title: 'dir', + opened: true, + children: [ + { + title: 'demo3', + link: '/en-us/docs/dir/demo3.html', + }, + ], + }, + ], + }, + ], + barText: 'Documentation', + }, + 'zh-cn': { + sidemenu: [ + { + title: '大标题', + children: [ + { + title: '示例1', + link: '/zh-cn/docs/demo1.html', + }, + { + title: '示例2', + link: '/zh-cn/docs/demo2.html', + }, + { + title: '目录', + opened: true, + children: [ + { + title: '示例3', + link: '/zh-cn/docs/dir/demo3.html', + }, + ], + }, + ], + }, + ], + barText: '文档', + }, +}; diff --git a/sop-website/sop-portal/site_config/home.jsx b/sop-website/sop-portal/site_config/home.jsx new file mode 100644 index 00000000..8115785a --- /dev/null +++ b/sop-website/sop-portal/site_config/home.jsx @@ -0,0 +1,170 @@ +import React from 'react'; + +export default { + 'zh-cn': { + brand: { + brandName: '开放平台', + briefIntroduction: 'XX开放平台,一句话介绍。。。', + buttons: [ + { + text: '开始使用', + link: 'http://localhost:8083/index.html#/login', + type: 'primary', + } + ], + }, + introduction: { + title: 'XX介绍', + desc: 'XX介绍描述文字', + img: '/img/home_1.png', + }, + // 介绍部分 + introductions: [ + { + title: '介绍部分1', + desc: '介绍文字。。。', + img: '/img/quick_start.png', + }, + { + title: '介绍部分2', + desc: '介绍文字。。。', + img: '/img/quick_start.png', + }], + features: { + title: '特性一览', + list: [ + { + img: '/img/feature_maintenance.png', + title: '特性1', + content: '描述文字。。', + }, + { + img: '/img/feature_loadbalances.png', + title: '特性2', + content: '描述文字。。', + } + ], + }, + start: { + title: '快速开始', + desc: '简单描述', + img: '/img/quick_start.png', + button: { + text: '阅读更多', + link: '/zh-cn/docs/demo1.html', + }, + }, + users: { + title: '用户', + desc: 简单描述, + list: [ + '/img/users_alibaba.png', + '/img/users_alibaba.png', + '/img/users_alibaba.png', + '/img/users_alibaba.png', + '/img/users_alibaba.png', + '/img/users_alibaba.png', + '/img/users_alibaba.png', + '/img/users_alibaba.png', + '/img/users_alibaba.png', + '/img/users_alibaba.png', + '/img/users_alibaba.png', + '/img/users_alibaba.png', + '/img/users_alibaba.png', + '/img/users_alibaba.png', + '/img/users_alibaba.png', + '/img/users_alibaba.png', + ], + }, + }, + 'en-us': { + brand: { + brandName: 'brandName', + briefIntroduction: 'some description of product', + buttons: [ + { + text: 'Quick Start', + link: '/en-us/docs/demo1.html', + type: 'primary', + }, + { + text: 'View on Github', + link: '', + type: 'normal', + }, + ], + }, + introduction: { + title: 'introduction title', + desc: 'some introduction of your product', + img: '/img/architecture.png', + }, + introductions: [], + features: { + title: 'Feature List', + list: [ + { + img: '/img/feature_transpart.png', + title: 'feature1', + content: 'feature description', + }, + { + img: '/img/feature_loadbalances.png', + title: 'feature2', + content: 'feature description', + }, + { + img: '/img/feature_service.png', + title: 'feature3', + content: 'feature description', + }, + { + img: '/img/feature_hogh.png', + title: 'feature4', + content: 'feature description', + }, + { + img: '/img/feature_runtime.png', + title: 'feature5', + content: 'feature description', + }, + { + img: '/img/feature_maintenance.png', + title: 'feature6', + content: 'feature description', + } + ] + }, + start: { + title: 'Quick start', + desc: 'some description text', + img: '/img/quick_start.png', + button: { + text: 'READ MORE', + link: '/en-us/docs/demo1.html', + }, + }, + users: { + title: 'users', + desc: some description, + list: [ + '/img/users_alibaba.png', + '/img/users_alibaba.png', + '/img/users_alibaba.png', + '/img/users_alibaba.png', + '/img/users_alibaba.png', + '/img/users_alibaba.png', + '/img/users_alibaba.png', + '/img/users_alibaba.png', + '/img/users_alibaba.png', + '/img/users_alibaba.png', + '/img/users_alibaba.png', + '/img/users_alibaba.png', + '/img/users_alibaba.png', + '/img/users_alibaba.png', + '/img/users_alibaba.png', + '/img/users_alibaba.png', + ], + }, + }, +}; diff --git a/sop-website/sop-portal/site_config/site.js b/sop-website/sop-portal/site_config/site.js new file mode 100644 index 00000000..0edcaad8 --- /dev/null +++ b/sop-website/sop-portal/site_config/site.js @@ -0,0 +1,116 @@ +// 全局的一些配置 +export default { + rootPath: '', // 发布到服务器的根目录,需以/开头但不能有尾/,如果只有/,请填写空字符串 + port: 8080, // 本地开发服务器的启动端口 + domain: 'localhost', // 站点部署域名,无需协议和path等 + defaultSearch: '', // 默认搜索引擎,baidu或者google + defaultLanguage: 'zh-cn', + 'en-us': { + pageMenu: [ + { + key: 'home', // 用作顶部菜单的选中 + text: 'HOME', + link: '/en-us/index.html', + }, + { + key: 'docs', + text: 'DOCS', + link: '/en-us/docs/demo1.html', + }, + { + key: 'blog', + text: 'BLOG', + link: '/en-us/blog/index.html', + }, + { + key: 'community', + text: 'COMMUNITY', + link: '/en-us/community/index.html', + }, + ], + disclaimer: { + title: 'Disclaimer', + content: 'the disclaimer content', + }, + documentation: { + title: 'Documentation', + list: [ + { + text: 'Overview', + link: '/en-us/docs/demo1.html', + }, + { + text: 'Quick start', + link: '/en-us/docs/demo2.html', + }, + { + text: 'Developer guide', + link: '/en-us/docs/dir/demo3.html', + }, + ], + }, + resources: { + title: 'Resources', + list: [ + { + text: 'Blog', + link: '/en-us/blog/index.html', + }, + { + text: 'Community', + link: '/en-us/community/index.html', + }, + ], + }, + copyright: 'Copyright © 2018 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', + }, + 'zh-cn': { + pageMenu: [ + { + key: 'blog', + text: '登录', + link: 'http://localhost:8083/index.html#/login', + }, + { + key: 'community', + text: '免费注册', + link: 'http://localhost:8083/index.html#/isvReg', + }, + ], + disclaimer: { + title: 'XX', + content: 'XX', + }, + documentation: { + title: '文档', + list: [ + { + text: '概览', + link: '/zh-cn/docs/demo1.html', + }, + { + text: '快速开始', + link: '/zh-cn/docs/demo2.html', + }, + { + text: '开发者指南', + link: '/zh-cn/docs/dir/demo3.html', + }, + ], + }, + resources: { + title: '资源', + list: [ + { + text: '博客', + link: '/zh-cn/blog/index.html', + }, + { + text: '社区', + link: '/zh-cn/community/index.html', + }, + ], + }, + copyright: 'Copyright © 2018 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', + }, +}; diff --git a/sop-website/sop-portal/src/components/bar/index.jsx b/sop-website/sop-portal/src/components/bar/index.jsx new file mode 100644 index 00000000..191e145e --- /dev/null +++ b/sop-website/sop-portal/src/components/bar/index.jsx @@ -0,0 +1,30 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import classnames from 'classnames'; +import { getLink } from '../../../utils'; +import './index.scss'; + +const propTypes = { + text: PropTypes.string.isRequired, // 显示的文案 + img: PropTypes.string.isRequired, // 显示的图片链接 +}; + +const Bar = (props) => { + const { text, img } = props; + const cls = classnames({ + bar: true, + }); + return ( +
+
+ + {text} + +
+
+ ); +}; + +Bar.propTypes = propTypes; + +export default Bar; diff --git a/sop-website/sop-portal/src/components/bar/index.scss b/sop-website/sop-portal/src/components/bar/index.scss new file mode 100644 index 00000000..ba890ed2 --- /dev/null +++ b/sop-website/sop-portal/src/components/bar/index.scss @@ -0,0 +1,59 @@ +@import '../../variables.scss'; + +.bar { + margin-top: $headerHeight; + background-image: linear-gradient(-90deg, $startColor 0%, $intermediateColor 51%, $endColor 100%); + .bar-body { + max-width: $contentWidth; + margin: 0 auto; + height: $barHeight; + line-height: $barHeight; + font-family: Avenir-Heavy; + font-size: 36px; + color: #FFF; + position: relative; + &::before { + content: ''; + height: 100%; + position: absolute; + left: 42px; + top: 0; + opacity: 0.3; + border-left: 1px solid #FFFFFF; + } + &::after { + content: ''; + height: 16px; + position: absolute; + left: 40px; + top: 50%; + margin: auto 0; + border-left: 4px solid #FFFFFF; + } + .front-img { + width: 80px; + height: 80px; + vertical-align: middle; + margin: 0 28px 0 70px; + } + .back-img { + width: 160px; + height: 160px; + position: absolute; + right: 168px; + bottom: 0; + opacity: 0.15; + } + @media screen and (max-width: $mobileWidth){ + &::before { + left: 22px; + } + &::after { + left: 20px; + } + .front-img { + margin-left: 50px; + } + } + } +} \ No newline at end of file diff --git a/sop-website/sop-portal/src/components/button/index.jsx b/sop-website/sop-portal/src/components/button/index.jsx new file mode 100644 index 00000000..49da2c04 --- /dev/null +++ b/sop-website/sop-portal/src/components/button/index.jsx @@ -0,0 +1,37 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import classnames from 'classnames'; +import { getLink } from '../../../utils'; +import './index.scss'; + +const propTypes = { + type: PropTypes.oneOf(['primary', 'normal']), + link: PropTypes.string, + target: PropTypes.string, +}; +const defaultProps = { + type: 'primary', + link: '', + target: '_self', +}; +const Button = (props) => { + return ( + + {props.children} + + ); +}; + +Button.propTypes = propTypes; +Button.defaultProps = defaultProps; + +export default Button; diff --git a/sop-website/sop-portal/src/components/button/index.scss b/sop-website/sop-portal/src/components/button/index.scss new file mode 100644 index 00000000..5c0d62cc --- /dev/null +++ b/sop-website/sop-portal/src/components/button/index.scss @@ -0,0 +1,20 @@ +.button { + box-sizing: border-box; + display: inline-block; + height: 48px; + line-height: 48px; + min-width: 140px; + font-family: Avenir-Heavy; + font-size: 16px; + color: #FFF; + text-align: center; + border-radius: 4px; + text-decoration: none; + &-primary { + background: #4190FF; + } + &-normal { + background: transparent; + border: 1px solid #fff; + } +} \ No newline at end of file diff --git a/sop-website/sop-portal/src/components/footer/index.jsx b/sop-website/sop-portal/src/components/footer/index.jsx new file mode 100644 index 00000000..ff560a51 --- /dev/null +++ b/sop-website/sop-portal/src/components/footer/index.jsx @@ -0,0 +1,60 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +// import siteConfig from '../../../site_config/site'; +// import { getLink } from '../../../utils'; +import './index.scss'; +import { getLink } from '../../../utils'; + +const propTypes = { + logo: PropTypes.string.isRequired, // logo地址 + language: PropTypes.oneOf(['zh-cn', 'en-us']), +}; + +const beianStyle = { + div: { + margin: '0 auto', + padding: '20px 0', + a: { + color: '#999', + paddingRight: '10px', + textDecoration: 'none', + height: '20px', + lineHeight: '20px', + img: { + float: 'left' + }, + p: { + float: 'left', + height: '20px', + lineHeight: '20px', + margin: '0px 0px 0px 5px', + color: '#939393' + } + } + } +} + +class Footer extends React.Component { + + render() { + // const { logo, language } = this.props; + // const dataSource = siteConfig[language]; + return ( + + ); + } +} + +Footer.propTypes = propTypes; + +export default Footer; diff --git a/sop-website/sop-portal/src/components/footer/index.scss b/sop-website/sop-portal/src/components/footer/index.scss new file mode 100644 index 00000000..2177fa36 --- /dev/null +++ b/sop-website/sop-portal/src/components/footer/index.scss @@ -0,0 +1,104 @@ +@import '../../variables.scss'; + +.footer-container { + background: #F8F8F8; + .footer-body { + max-width: $contentWidth; + margin: 0 auto; + box-sizing: border-box; + padding: 40px 40px 0; + @media screen and (max-width: $mobileWidth) { + padding-left: 20px; + padding-right: 20px; + } + //img { + // // display: block; + // width: 270px; + // margin-bottom: 28px; + // margin-right: 20px; + // vertical-align: middle; + //} + .cols-container { + margin-bottom: 60px; + .col { + display: inline-block; + box-sizing: border-box; + vertical-align: top; + } + .col-12 { + width: 50%; + padding-right: 125px; + } + .col-6 { + width: 25%; + } + h3 { + font-family: Avenir-Heavy; + font-size: 18px; + color: #333; + line-height: 18px; + margin-bottom: 20px; + } + p { + font-family: Avenir-Medium; + font-size: 12px; + color: #999; + line-height: 18px; + } + dl { + font-family: Avenir-Heavy; + line-height: 18px; + } + dt { + font-weight: bold; + font-size: 18px; + color: #333; + margin-bottom: 20px; + } + dd { + padding: 0; + margin: 0; + a { + text-decoration: none; + display: block; + font-size: 14px; + color: #999; + margin: 10px 0; + } + a:hover { + color: $brandColor; + } + } + } + .copyright { + border-top: 1px solid #ccc; + min-height: 60px; + line-height: 20px; + text-align: center; + font-family: Avenir-Medium; + font-size: 12px; + color: #999; + display: flex; + align-items: center; + span { + display: inline-block; + margin: 0 auto; + } + } + } +} + +@media screen and (max-width: $mobileWidth) { + .footer-container { + .footer-body { + .cols-container { + .col { + width: 100%; + text-align: center; + padding: 0; + } + } + } + } +} +.beian {color: #999;} diff --git a/sop-website/sop-portal/src/components/header/index.jsx b/sop-website/sop-portal/src/components/header/index.jsx new file mode 100644 index 00000000..a034a6e7 --- /dev/null +++ b/sop-website/sop-portal/src/components/header/index.jsx @@ -0,0 +1,190 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import classnames from 'classnames'; +import { autobind } from 'core-decorators'; +import siteConfig from '../../../site_config/site'; +import { getLink } from '../../../utils'; +import './index.scss'; + +const languageSwitch = [ + { + text: '中', + value: 'en-us', + }, + { + text: 'En', + value: 'zh-cn', + }, +]; +const searchSwitch = { + baidu: { + logo: 'https://img.alicdn.com/tfs/TB1n6DQayLaK1RjSZFxXXamPFXa-300-300.png', + url: 'https://www.baidu.com/s?wd=', + }, + google: { + logo: 'https://img.alicdn.com/tfs/TB1REfuaCzqK1RjSZFjXXblCFXa-300-300.jpg', + url: 'https://www.google.com/search?q=', + }, +}; +const noop = () => {}; +const propTypes = { + currentKey: PropTypes.string, + logo: PropTypes.string.isRequired, + type: PropTypes.oneOf(['primary', 'normal']), + language: PropTypes.oneOf(['en-us', 'zh-cn']), + onLanguageChange: PropTypes.func, +}; +const defaultProps = { + type: 'primary', + language: 'en-us', + onLanguageChange: noop, +}; + +@autobind +class Header extends React.Component { + constructor(props) { + super(props); + this.state = { + menuBodyVisible: false, + language: props.language, + search: siteConfig.defaultSearch, + searchValue: '', + inputVisible: false, + }; + } + + componentWillReceiveProps(nextProps) { + this.setState({ + language: nextProps.language, + }); + } + + toggleMenu() { + this.setState({ + menuBodyVisible: !this.state.menuBodyVisible, + }); + } + + switchLang() { + let language; + if (this.state.language === 'zh-cn') { + language = 'en-us'; + } else { + language = 'zh-cn'; + } + this.setState({ + language, + }); + this.props.onLanguageChange(language); + } + + switchSearch() { + let search; + if (this.state.search === 'baidu') { + search = 'google'; + } else { + search = 'baidu'; + } + this.setState({ + search, + }); + } + + toggleSearch() { + this.setState({ + searchVisible: !this.state.searchVisible, + }); + } + + onInputChange(e) { + this.setState({ + searchValue: e.target.value, + }); + } + + goSearch() { + const { search, searchValue } = this.state; + window.open(`${searchSwitch[search].url}${window.encodeURIComponent(`${searchValue} site:${siteConfig.domain}`)}`); + } + + onKeyDown(e) { + if (e.keyCode === 13) { + this.goSearch(); + } + } + + render() { + const { type, logo, onLanguageChange, currentKey } = this.props; + const { menuBodyVisible, language, search, searchVisible } = this.state; + return ( +
+
+ {siteConfig.name} + { + siteConfig.defaultSearch ? + ( +
+ + { + searchVisible ? + ( +
+ + +
+ ) : null + } +
+ ) : null + } + { + null + } +
+ +
    + {siteConfig[language].pageMenu.map(item => ( +
  • + {item.text} +
  • ))} +
+
+
+
+ ); + } +} + +Header.propTypes = propTypes; +Header.defaultProps = defaultProps; +export default Header; diff --git a/sop-website/sop-portal/src/components/header/index.scss b/sop-website/sop-portal/src/components/header/index.scss new file mode 100644 index 00000000..3c6cb9d5 --- /dev/null +++ b/sop-website/sop-portal/src/components/header/index.scss @@ -0,0 +1,249 @@ +@import '../../variables.scss'; + +.header-container { + position: fixed; + left: 0; + top: 0; + width: 100%; + z-index: 1000; + background-color: #fff; + &-primary { + background-color: transparent; + } + &-normal { + background-color: #fff; + box-shadow: 0 2px 10px 0 rgba(0,0,0,0.08); + } + .header-body { + max-width: $contentWidth; + margin: 0 auto; + height: $headerHeight; + line-height: $headerHeight; + .logo { + margin-left: 40px; + width: 180px; + vertical-align: sub; + } + .header-menu { + float: right; + .header-menu-toggle { + display: none; + width: 19px; + margin-right: 40px; + margin-top: $headerHeight / 2 - 15px; + cursor: pointer; + } + } + ul { + padding: 0; + margin: 0; + } + li { + display: inline-block; + margin-right: 40px; + } + .menu-item { + font-family: Avenir-Heavy; + font-size: 14px; + vertical-align: bottom; + } + .menu-item-primary { + a { + color: #fff; + opacity: 0.6; + font-family: Avenir-Medium; + } + &:hover { + a { + // font-family: Avenir-Heavy; + opacity: 1; + } + } + &-active { + a { + // font-family: Avenir-Heavy; + opacity: 1; + } + } + } + .menu-item-normal { + a { + color: #333; + opacity: 0.6; + font-family: Avenir-Medium; + } + &:hover { + a { + // font-family: Avenir-Heavy; + opacity: 1; + } + } + &-active { + a { + // font-family: Avenir-Heavy; + opacity: 1; + } + } + } + .language-switch { + float: right; + box-sizing: border-box; + width: 24px; + height: 24px; + line-height: 20px; + margin-top: $headerHeight / 2 - 12px; + margin-right: 40px; + text-align: center; + border-radius: 2px; + cursor: pointer; + font-family: PingFangSC-Medium; + font-size: 14px; + opacity: 0.6; + &:hover { + opacity: 1; + } + } + .language-switch-primary { + border: 1px solid #FFF; + color: #FFF; + } + .language-switch-normal { + border: 1px solid #333; + color: #333; + } + .search { + float: right; + width: 24px; + height: 24px; + margin-top: 21px; + margin-right: 40px; + line-height: normal; + position: relative; + .icon-search { + display: inline-block; + cursor: pointer; + width: 12px; + height: 12px; + border-radius: 50%; + border:2px solid; + position: relative; + &::before { + content: ''; + transform: rotate(45deg); + width:8px; + height: 2px; + position: absolute; + top:13px; + left:11px; + } + } + &-primary { + .icon-search { + border-color: #fff; + opacity: 0.6; + &::before { + background-color: #fff; + } + &:hover { + opacity: 1; + } + } + } + &-normal { + .icon-search { + border-color: #333; + opacity: 0.6; + &::before { + background-color: #333; + } + &:hover { + opacity: 1; + } + } + } + .search-input { + position: absolute; + left: -172px; + top: 28px; + background: #fff; + border-radius: 4px; + overflow: hidden; + box-shadow: 0 8px 16px 0 rgba(0, 0, 0, 0.05), 0 -8px 16px 0 rgba(0, 0, 0, 0.05); + img { + width: 28px; + height: 28px; + cursor: pointer; + vertical-align: middle; + } + input { + padding: 0 4px; + border: none; + outline: none; + width: 160px; + height: 24px; + vertical-align: middle; + } + } + } + } +} + +@media screen and (max-width: $mobileWidth) { + .header-container { + .header-body { + .logo { + margin-left: 20px; + width: 270px; + } + .language-switch { + margin-right: 20px; + } + .header-menu { + ul { + display: none; + } + .header-menu-toggle { + display: inline-block; + margin-right: 20px; + } + } + .header-menu-open { + ul { + background-color: $headerMenuBgColor; + display: inline-block; + position: absolute; + right: 0; + top: $headerHeight; + z-index: 100; + } + li { + width: 200px; + display: list-item; + padding-left: 30px; + list-style: none; + line-height: 40px; + margin-right: 0; + a { + color: #333; + display: inline-block; + width: 100%; + } + &:hover { + background: $endColor; + a { + color: #fff; + opacity: 1; + } + } + } + .menu-item-primary-active, .menu-item-normal-active { + background: $endColor; + a { + color: #fff; + opacity: 1; + } + } + } + } + } +} diff --git a/sop-website/sop-portal/src/components/language/index.jsx b/sop-website/sop-portal/src/components/language/index.jsx new file mode 100644 index 00000000..48053646 --- /dev/null +++ b/sop-website/sop-portal/src/components/language/index.jsx @@ -0,0 +1,37 @@ +import React from 'react'; +import { autobind } from 'core-decorators'; +import cookie from 'js-cookie'; +import siteConfig from '../../../site_config/site'; + +@autobind +class Language extends React.Component { + + onLanguageChange(language) { + const pathname = window.location.pathname; + let oldLang; + if (language === 'zh-cn') { + oldLang = 'en-us'; + } else { + oldLang = 'zh-cn'; + } + const newPathname = pathname.replace(`${window.rootPath}/${oldLang}`, `${window.rootPath}/${language}`); + cookie.set('docsite_language', language, { expires: 365}); + window.location = newPathname; + } + + getLanguage() { + const urlLang = window.location.pathname.replace(window.rootPath || '', '').split('/')[1]; + let language = this.props.lang || urlLang || cookie.get('docsite_language') || siteConfig.defaultLanguage; + // 防止链接被更改导致错误的cookie存储 + if (language !== 'en-us' && language !== 'zh-cn') { + language = siteConfig.defaultLanguage; + } + // 同步cookie语言版本 + if (language !== cookie.get('docsite_language')) { + cookie.set('docsite_language', language, { expires: 365 }); + } + return language; + } +} + +export default Language; diff --git a/sop-website/sop-portal/src/components/pageSlider/index.jsx b/sop-website/sop-portal/src/components/pageSlider/index.jsx new file mode 100644 index 00000000..c09a506c --- /dev/null +++ b/sop-website/sop-portal/src/components/pageSlider/index.jsx @@ -0,0 +1,142 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { autobind } from 'core-decorators'; +import classnames from 'classnames'; +import { throttle, getLink } from '../../../utils'; + +import './index.scss'; + +const propTypes = { + pageSize: PropTypes.number, // 每页最多显示的条数 + +}; +const defaultProps = { + pageSize: 5, +}; + +@autobind +class pageSlider extends React.Component { + constructor(props) { + super(props); + this.container = null; + this.state = { + page: 0, + pageWidth: 0, + }; + } + + componentDidMount() { + const pageWidth = this.container.getBoundingClientRect().width; + /* eslint-disable react/no-did-mount-set-state */ + this.setState({ + pageWidth, + }); + this.throttleAdjust = throttle(() => { + this.setState({ + pageWidth: this.container.getBoundingClientRect().width, + }); + }, 200); + window.addEventListener('resize', this.throttleAdjust); + } + + componentWillUnmount() { + window.removeEventListener('resize', this.throttleAdjust); + } + + changePage(i) { + this.setState({ + page: i, + }); + } + + renderSliderList() { + const { children, pageSize } = this.props; + const { page, pageWidth } = this.state; + const splitGroup = []; + const len = React.Children.count(children); + // 分成的屏数 + const splitNum = Math.ceil(len / pageSize); + /* eslint-disable no-plusplus*/ + for (let i = 0; i < splitNum; i++) { + splitGroup.push(Array.from(children).slice(i * pageSize, (i + 1) * pageSize)); + } + return ( +
+ {splitGroup.map((group, i) => { + return ( +
+ { + group.map((child, j) => ( +
+ {child} +
+ ) + ) + } +
+ ); + })} +
+ ); + } + + renderControl() { + const { children, pageSize } = this.props; + const { page } = this.state; + const len = React.Children.count(children); + // 分成的屏数 + const splitNum = Math.ceil(len / pageSize); + return ( +
+ + +
+ ); + } + + render() { + return ( +
{ this.container = node; }}> + {this.renderSliderList()} + {this.renderControl()} +
+ ); + } +} + +pageSlider.propTypes = propTypes; +pageSlider.defaultProps = defaultProps; + +export default pageSlider; diff --git a/sop-website/sop-portal/src/components/pageSlider/index.scss b/sop-website/sop-portal/src/components/pageSlider/index.scss new file mode 100644 index 00000000..07597412 --- /dev/null +++ b/sop-website/sop-portal/src/components/pageSlider/index.scss @@ -0,0 +1,34 @@ +@import '../../variables.scss'; + +.page-slider { + overflow: hidden; + .slider-list { + overflow: visible; + .slider-page { + overflow: hidden; + display: inline-block; + vertical-align: top; + } + } + .slider-control { + overflow: hidden; + img { + display: inline-block; + width: 52px; + height: 52px; + cursor: pointer; + } + .slider-control-prev { + float: left; + &-hidden { + display: none; + } + } + .slider-control-next { + float: right; + &-hidden { + display: none; + } + } + } +} \ No newline at end of file diff --git a/sop-website/sop-portal/src/components/sidemenu/index.jsx b/sop-website/sop-portal/src/components/sidemenu/index.jsx new file mode 100644 index 00000000..fc58d232 --- /dev/null +++ b/sop-website/sop-portal/src/components/sidemenu/index.jsx @@ -0,0 +1,59 @@ +import React from 'react'; +import classnames from 'classnames'; +import { autobind } from 'core-decorators'; +import Item from './item.jsx'; +import './index.scss'; + +@autobind +class SideMenu extends React.Component { + constructor(props) { + super(props); + this.state = { + menuBodyVisible: false, + }; + } + + toggleMenuBody() { + this.setState({ + menuBodyVisible: !this.state.menuBodyVisible, + }); + } + + render() { + const { dataSource } = this.props; + const { menuBodyVisible } = this.state; + const cls = classnames({ + sidemenu: true, + 'sidemenu-open': menuBodyVisible, + }); + const itemCls = classnames({ + 'menu-item': true, + 'menu-item-level-1': true, + }); + return ( +
+
+ +
+
    + { + dataSource.map((data, i) => { + return ( +
  • + + {data.title} + +
      + {data.children.map((item, j) => )} +
    +
  • + ); + }) + } +
+
+ ); + } +} + +export default SideMenu; diff --git a/sop-website/sop-portal/src/components/sidemenu/index.scss b/sop-website/sop-portal/src/components/sidemenu/index.scss new file mode 100644 index 00000000..44051ffd --- /dev/null +++ b/sop-website/sop-portal/src/components/sidemenu/index.scss @@ -0,0 +1,121 @@ +@import '../../variables.scss'; +.sidemenu { + background: $sideMenuBgColor; + width: $sideMenuWidth; + position: relative; + display: inline-block; + padding: 20px 0; + .sidemenu-toggle { + text-align: center; + cursor: pointer; + position: absolute; + top: 0; + width: 40px; + right: -40px; + height: 30px; + line-height: 30px; + background: $sideMenuBgColor; + border-radius: 0 4px 4px 0; + display: none; + img { + width: 16px; + text-align: center; + vertical-align: middle; + } + } + ul { + list-style: none; + padding: 0; + margin: 0; + } + li { + line-height: 0; + } + span, a { + box-sizing: border-box; + display: inline-block; + position: relative; + width: 100%; + overflow-x: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } + .menu-item-selected { + a { + background: white; + } + a::before { + content: ''; + position: absolute; + left: 0; + top: 0; + width: 4px; + height: 100%; + background-image:linear-gradient(0deg, $startColor 0%, $intermediateColor 51%, $endColor 100%); + } + } + .menu-item-level-1 { + & > span { + font-family: Avenir-Heavy; + font-size: 18px; + color: #333; + padding-left: 20px; + height: 40px; + line-height: 40px; + } + } + + .menu-item-level-2, .menu-item-level-3 { + cursor: pointer; + & > span, & > a { + font-family: Avenir-Medium; + font-size: 14px; + color: #666; + &:hover { + background: white; + &::before { + content: ''; + position: absolute; + left: 0; + top: 0; + width: 4px; + height: 100%; + background-image:linear-gradient(0deg, $startColor 0%, $intermediateColor 51%, $endColor 100%); + } + } + } + } + + .menu-item-level-2 { + & > span, & > a { + padding-left: 40px; + height: 36px; + line-height: 36px; + img.menu-toggle { + float: right; + width: 13px; + height: 8px; + margin: 14px 20px 14px 0; + } + } + } + .menu-item-level-3 { + & > a { + padding-left: 60px; + height: 36px; + line-height: 36px; + } + } +} + +@media screen and (max-width: $mobileWidth) { + .sidemenu { + width: 0; + .sidemenu-toggle { + display: inline-block; + } + &.sidemenu-open { + width: $sideMenuWidth; + } + } +} \ No newline at end of file diff --git a/sop-website/sop-portal/src/components/sidemenu/item.jsx b/sop-website/sop-portal/src/components/sidemenu/item.jsx new file mode 100644 index 00000000..75204df6 --- /dev/null +++ b/sop-website/sop-portal/src/components/sidemenu/item.jsx @@ -0,0 +1,93 @@ +import React from 'react'; +import { autobind } from 'core-decorators'; +import classnames from 'classnames'; +import { getLink } from '../../../utils'; + +@autobind +class Item extends React.Component { + constructor(props) { + super(props); + const { item } = props; + const hasChildren = item.children && item.children.length; + let opened = props.item.opened; + if (hasChildren) { + if (opened === undefined) { + // 未配置展开,则是否展开由是否选中决定 + opened = item.children.find(child => getLink(child.link) === window.location.pathname); + } + } else { + opened = false; + } + this.state = { + opened, + }; + } + + onItemClick(e) { + this.props.toggleMenuBody(); + e.stopPropagation(); + } + + toggle() { + this.setState({ + opened: !this.state.opened, + }); + } + + renderSubMenu(data) { + return ( +
    + { + data.map((item, index) => ( +
  • + {item.title} +
  • + )) + } +
+ ); + } + + render() { + const { item } = this.props; + const hasChildren = item.children && item.children.length; + const { opened } = this.state; + const cls = classnames({ + 'menu-item': true, + 'menu-item-level-2': true, + 'menu-item-selected': getLink(item.link) === window.location.pathname, + }); + const style = { + height: opened ? 36 * (item.children.length + 1) : 36, + overflow: 'hidden', + }; + if (hasChildren) { + return ( +
  • + { + + {item.title} + + + } + {this.renderSubMenu(item.children)} +
  • + ); + } + return ( +
  • + {item.title} +
  • + ); + } +} + +export default Item; diff --git a/sop-website/sop-portal/src/components/slider/index.jsx b/sop-website/sop-portal/src/components/slider/index.jsx new file mode 100644 index 00000000..fb742f18 --- /dev/null +++ b/sop-website/sop-portal/src/components/slider/index.jsx @@ -0,0 +1,163 @@ +import React from 'react'; +import ReactDOM from 'react-dom'; +import { autobind } from 'core-decorators'; +import classnames from 'classnames'; +import { throttle } from '../../../utils'; + +import './index.scss'; + +@autobind +class Slider extends React.Component { + constructor(props) { + super(props); + this.container = null; + this.state = { + screenIndex: 0, + visibleNum: 1, + }; + } + + componentDidMount() { + this.throttleAdjust = throttle(() => { + this.setState({ + visibleNum: this.getVisibleNum(), + }); + }, 200); + window.addEventListener('resize', this.throttleAdjust); + // 做重新布局 + /* eslint-disable react/no-did-mount-set-state */ + this.setState({ + visibleNum: this.getVisibleNum(), + }); + } + + componentWillUnmount() { + window.removeEventListener('resize', this.throttleAdjust); + } + + getVisibleNum() { + // 比较粗略的计算,假定一屏的子节点外边距之和不会超过一个子节点的宽度 + /* eslint-disable react/no-find-dom-node */ + let result = 1; + const containerWidth = this.container.getBoundingClientRect().width; + const childWidth = this.sliderItemChild0.getBoundingClientRect ? this.sliderItemChild0.getBoundingClientRect().width : ReactDOM.findDOMNode(this.sliderItemChild0).getBoundingClientRect().width; + if (containerWidth && childWidth) { + result = Math.floor(containerWidth / childWidth); + } + // 有可能result为0,下面的除法就会出现分母为0,从而导致得出Inifity,无限循环导致浏览器崩溃 + return result || 1; + } + + getListWidth() { + let width = 0; + const { children } = this.props; + const { visibleNum } = this.state; + const len = React.Children.count(children); + // 最终分成的屏数 + const splitNum = Math.ceil(len / visibleNum); + if (this.container) { + const containerWidth = this.container.getBoundingClientRect().width; + width = containerWidth * splitNum; + } + return width; + } + + changeScreen(i) { + const { screenIndex } = this.state; + // const total = React.Children.count(this.props.children); + if (i !== screenIndex) { + this.setState({ + screenIndex: i + }); + } + } + + renderSliderList() { + const { children } = this.props; + const { screenIndex, visibleNum } = this.state; + const splitGroup = []; + const len = React.Children.count(children); + // 分成的屏数 + const splitNum = Math.ceil(len / visibleNum); + /* eslint-disable no-plusplus*/ + for (let i = 0; i < splitNum; i++) { + splitGroup.push(Array.from(children).slice(i * visibleNum, (i + 1) * visibleNum)); + } + return ( +
    + {splitGroup.map((group, i) => { + return ( +
    { this[`sliderScreen${i}`] = node; }} + > + { + group.map((child, j) => ( +
    + {React.cloneElement(child, { + ref: (node) => { + this[`sliderItemChild${(i * visibleNum) + j}`] = node; + } + })} +
    + ) + ) + } +
    + ); + })} +
    + ); + } + + renderControl() { + const { children } = this.props; + const { screenIndex, visibleNum } = this.state; + const len = React.Children.count(children); + // 分成的屏数 + const splitNum = Math.ceil(len / visibleNum); + const temp = []; + for (let i = 0; i < splitNum; i++) { + temp.push(( + + )); + } + return ( +
    + {temp} +
    + ); + } + + render() { + return ( +
    { this.container = node; }}> + {this.renderSliderList()} + {this.renderControl()} +
    + ); + } +} + +export default Slider; diff --git a/sop-website/sop-portal/src/components/slider/index.scss b/sop-website/sop-portal/src/components/slider/index.scss new file mode 100644 index 00000000..f4bd9353 --- /dev/null +++ b/sop-website/sop-portal/src/components/slider/index.scss @@ -0,0 +1,30 @@ +@import '../../variables.scss'; + +.slider { + overflow: hidden; + .slider-list { + overflow: visible; + .slider-screen { + display: inline-flex; + justify-content: space-around; + overflow: hidden; + } + } + .slider-control { + text-align: center; + margin-top: 20px; + .slider-control-item { + cursor: pointer; + display: inline-block; + width: 20px; + height: 4px; + margin-right: 4px; + background: #ccc; + &-active { + width: 40px; + height: 6px; + background-image: linear-gradient(-90deg, $startColor 0%, $intermediateColor 51%, $endColor 100%); + } + } + } +} \ No newline at end of file diff --git a/sop-website/sop-portal/src/markdown.scss b/sop-website/sop-portal/src/markdown.scss new file mode 100644 index 00000000..fd30a16e --- /dev/null +++ b/sop-website/sop-portal/src/markdown.scss @@ -0,0 +1,1555 @@ +@font-face { + font-family: octicons-link; + src: url(data:font/woff;charset=utf-8;base64,d09GRgABAAAAAAZwABAAAAAACFQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABEU0lHAAAGaAAAAAgAAAAIAAAAAUdTVUIAAAZcAAAACgAAAAoAAQAAT1MvMgAAAyQAAABJAAAAYFYEU3RjbWFwAAADcAAAAEUAAACAAJThvmN2dCAAAATkAAAABAAAAAQAAAAAZnBnbQAAA7gAAACyAAABCUM+8IhnYXNwAAAGTAAAABAAAAAQABoAI2dseWYAAAFsAAABPAAAAZwcEq9taGVhZAAAAsgAAAA0AAAANgh4a91oaGVhAAADCAAAABoAAAAkCA8DRGhtdHgAAAL8AAAADAAAAAwGAACfbG9jYQAAAsAAAAAIAAAACABiATBtYXhwAAACqAAAABgAAAAgAA8ASm5hbWUAAAToAAABQgAAAlXu73sOcG9zdAAABiwAAAAeAAAAME3QpOBwcmVwAAAEbAAAAHYAAAB/aFGpk3jaTY6xa8JAGMW/O62BDi0tJLYQincXEypYIiGJjSgHniQ6umTsUEyLm5BV6NDBP8Tpts6F0v+k/0an2i+itHDw3v2+9+DBKTzsJNnWJNTgHEy4BgG3EMI9DCEDOGEXzDADU5hBKMIgNPZqoD3SilVaXZCER3/I7AtxEJLtzzuZfI+VVkprxTlXShWKb3TBecG11rwoNlmmn1P2WYcJczl32etSpKnziC7lQyWe1smVPy/Lt7Kc+0vWY/gAgIIEqAN9we0pwKXreiMasxvabDQMM4riO+qxM2ogwDGOZTXxwxDiycQIcoYFBLj5K3EIaSctAq2kTYiw+ymhce7vwM9jSqO8JyVd5RH9gyTt2+J/yUmYlIR0s04n6+7Vm1ozezUeLEaUjhaDSuXHwVRgvLJn1tQ7xiuVv/ocTRF42mNgZGBgYGbwZOBiAAFGJBIMAAizAFoAAABiAGIAznjaY2BkYGAA4in8zwXi+W2+MjCzMIDApSwvXzC97Z4Ig8N/BxYGZgcgl52BCSQKAA3jCV8CAABfAAAAAAQAAEB42mNgZGBg4f3vACQZQABIMjKgAmYAKEgBXgAAeNpjYGY6wTiBgZWBg2kmUxoDA4MPhGZMYzBi1AHygVLYQUCaawqDA4PChxhmh/8ODDEsvAwHgMKMIDnGL0x7gJQCAwMAJd4MFwAAAHjaY2BgYGaA4DAGRgYQkAHyGMF8NgYrIM3JIAGVYYDT+AEjAwuDFpBmA9KMDEwMCh9i/v8H8sH0/4dQc1iAmAkALaUKLgAAAHjaTY9LDsIgEIbtgqHUPpDi3gPoBVyRTmTddOmqTXThEXqrob2gQ1FjwpDvfwCBdmdXC5AVKFu3e5MfNFJ29KTQT48Ob9/lqYwOGZxeUelN2U2R6+cArgtCJpauW7UQBqnFkUsjAY/kOU1cP+DAgvxwn1chZDwUbd6CFimGXwzwF6tPbFIcjEl+vvmM/byA48e6tWrKArm4ZJlCbdsrxksL1AwWn/yBSJKpYbq8AXaaTb8AAHja28jAwOC00ZrBeQNDQOWO//sdBBgYGRiYWYAEELEwMTE4uzo5Zzo5b2BxdnFOcALxNjA6b2ByTswC8jYwg0VlNuoCTWAMqNzMzsoK1rEhNqByEyerg5PMJlYuVueETKcd/89uBpnpvIEVomeHLoMsAAe1Id4AAAAAAAB42oWQT07CQBTGv0JBhagk7HQzKxca2sJCE1hDt4QF+9JOS0nbaaYDCQfwCJ7Au3AHj+LO13FMmm6cl7785vven0kBjHCBhfpYuNa5Ph1c0e2Xu3jEvWG7UdPDLZ4N92nOm+EBXuAbHmIMSRMs+4aUEd4Nd3CHD8NdvOLTsA2GL8M9PODbcL+hD7C1xoaHeLJSEao0FEW14ckxC+TU8TxvsY6X0eLPmRhry2WVioLpkrbp84LLQPGI7c6sOiUzpWIWS5GzlSgUzzLBSikOPFTOXqly7rqx0Z1Q5BAIoZBSFihQYQOOBEdkCOgXTOHA07HAGjGWiIjaPZNW13/+lm6S9FT7rLHFJ6fQbkATOG1j2OFMucKJJsxIVfQORl+9Jyda6Sl1dUYhSCm1dyClfoeDve4qMYdLEbfqHf3O/AdDumsjAAB42mNgYoAAZQYjBmyAGYQZmdhL8zLdDEydARfoAqIAAAABAAMABwAKABMAB///AA8AAQAAAAAAAAAAAAAAAAABAAAAAA==) format('woff'); +} + +.markdown-body { + -ms-text-size-adjust: 100%; + -webkit-text-size-adjust: 100%; + line-height: 1.5; + color: #24292e; + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; + font-size: 16px; + line-height: 1.5; + word-wrap: break-word; +} + +.markdown-body .pl-c { + color: #6a737d; +} + +.markdown-body .pl-c1, +.markdown-body .pl-s .pl-v { + color: #005cc5; +} + +.markdown-body .pl-e, +.markdown-body .pl-en { + color: #6f42c1; +} + +.markdown-body .pl-smi, +.markdown-body .pl-s .pl-s1 { + color: #24292e; +} + +.markdown-body .pl-ent { + color: #22863a; +} + +.markdown-body .pl-k { + color: #d73a49; +} + +.markdown-body .pl-s, +.markdown-body .pl-pds, +.markdown-body .pl-s .pl-pse .pl-s1, +.markdown-body .pl-sr, +.markdown-body .pl-sr .pl-cce, +.markdown-body .pl-sr .pl-sre, +.markdown-body .pl-sr .pl-sra { + color: #032f62; +} + +.markdown-body .pl-v, +.markdown-body .pl-smw { + color: #e36209; +} + +.markdown-body .pl-bu { + color: #b31d28; +} + +.markdown-body .pl-ii { + color: #fafbfc; + background-color: #b31d28; +} + +.markdown-body .pl-c2 { + color: #fafbfc; + background-color: #d73a49; +} + +.markdown-body .pl-c2::before { + content: "^M"; +} + +.markdown-body .pl-sr .pl-cce { + font-weight: bold; + color: #22863a; +} + +.markdown-body .pl-ml { + color: #735c0f; +} + +.markdown-body .pl-mh, +.markdown-body .pl-mh .pl-en, +.markdown-body .pl-ms { + font-weight: bold; + color: #005cc5; +} + +.markdown-body .pl-mi { + font-style: italic; + color: #24292e; +} + +.markdown-body .pl-mb { + font-weight: bold; + color: #24292e; +} + +.markdown-body .pl-md { + color: #b31d28; + background-color: #ffeef0; +} + +.markdown-body .pl-mi1 { + color: #22863a; + background-color: #f0fff4; +} + +.markdown-body .pl-mc { + color: #e36209; + background-color: #ffebda; +} + +.markdown-body .pl-mi2 { + color: #f6f8fa; + background-color: #005cc5; +} + +.markdown-body .pl-mdr { + font-weight: bold; + color: #6f42c1; +} + +.markdown-body .pl-ba { + color: #586069; +} + +.markdown-body .pl-sg { + color: #959da5; +} + +.markdown-body .pl-corl { + text-decoration: underline; + color: #032f62; +} + +.markdown-body .octicon { + display: inline-block; + vertical-align: text-top; + fill: currentColor; +} + +.markdown-body a { + background-color: transparent; +} + +.markdown-body a:active, +.markdown-body a:hover { + outline-width: 0; +} + +.markdown-body strong { + font-weight: inherit; +} + +.markdown-body strong { + font-weight: bolder; +} + +.markdown-body h1 { + font-size: 2em; + margin: 0.67em 0; +} + +.markdown-body img { + border-style: none; +} + +.markdown-body code, +.markdown-body kbd, +.markdown-body pre { + font-family: monospace, monospace; + font-size: 1em; +} + +.markdown-body hr { + box-sizing: content-box; + height: 0; + overflow: visible; +} + +.markdown-body input { + font: inherit; + margin: 0; +} + +.markdown-body input { + overflow: visible; +} + +.markdown-body [type="checkbox"] { + box-sizing: border-box; + padding: 0; +} + +.markdown-body * { + box-sizing: border-box; +} + +.markdown-body input { + font-family: inherit; + font-size: inherit; + line-height: inherit; +} + +.markdown-body a { + color: #0366d6; + text-decoration: none; +} + +.markdown-body a:hover { + color: #0366d6; + text-decoration: underline; +} + +.markdown-body strong { + font-weight: 600; +} + +.markdown-body hr { + height: 0; + margin: 15px 0; + overflow: hidden; + background: transparent; + border: 0; + border-bottom: 1px solid #dfe2e5; +} + +.markdown-body hr::before { + display: table; + content: ""; +} + +.markdown-body hr::after { + display: table; + clear: both; + content: ""; +} + +.markdown-body table { + border-spacing: 0; + border-collapse: collapse; +} + +.markdown-body td, +.markdown-body th { + padding: 0; +} + +.markdown-body h1, +.markdown-body h2, +.markdown-body h3, +.markdown-body h4, +.markdown-body h5, +.markdown-body h6 { + margin-top: 0; + margin-bottom: 0; +} + +.markdown-body h1 { + font-size: 32px; + font-weight: 600; +} + +.markdown-body h2 { + font-size: 24px; + font-weight: 600; +} + +.markdown-body h3 { + font-size: 20px; + font-weight: 600; +} + +.markdown-body h4 { + font-size: 16px; + font-weight: 600; +} + +.markdown-body h5 { + font-size: 14px; + font-weight: 600; +} + +.markdown-body h6 { + font-size: 12px; + font-weight: 600; +} + +.markdown-body p { + margin-top: 0; + margin-bottom: 10px; +} + +.markdown-body blockquote { + margin: 0; +} + +.markdown-body ul, +.markdown-body ol { + padding-left: 0; + margin-top: 0; + margin-bottom: 0; +} + +.markdown-body ol ol, +.markdown-body ul ol { + list-style-type: lower-roman; +} + +.markdown-body ul ul ol, +.markdown-body ul ol ol, +.markdown-body ol ul ol, +.markdown-body ol ol ol { + list-style-type: lower-alpha; +} + +.markdown-body dd { + margin-left: 0; +} + +.markdown-body code { + font-family: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, Courier, monospace; + font-size: 12px; +} + +.markdown-body pre { + margin-top: 0; + margin-bottom: 0; + font-family: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, Courier, monospace; + font-size: 12px; +} + +.markdown-body .octicon { + vertical-align: text-bottom; +} + +.markdown-body .pl-0 { + padding-left: 0 !important; +} + +.markdown-body .pl-1 { + padding-left: 4px !important; +} + +.markdown-body .pl-2 { + padding-left: 8px !important; +} + +.markdown-body .pl-3 { + padding-left: 16px !important; +} + +.markdown-body .pl-4 { + padding-left: 24px !important; +} + +.markdown-body .pl-5 { + padding-left: 32px !important; +} + +.markdown-body .pl-6 { + padding-left: 40px !important; +} + +.markdown-body::before { + display: table; + content: ""; +} + +.markdown-body::after { + display: table; + clear: both; + content: ""; +} + +.markdown-body>*:first-child { + margin-top: 0 !important; +} + +.markdown-body>*:last-child { + margin-bottom: 0 !important; +} + +.markdown-body a:not([href]) { + color: inherit; + text-decoration: none; +} + +.markdown-body .anchor { + float: left; + padding-right: 4px; + margin-left: -20px; + line-height: 1; +} + +.markdown-body .anchor:focus { + outline: none; +} + +.markdown-body p, +.markdown-body blockquote, +.markdown-body ul, +.markdown-body ol, +.markdown-body dl, +.markdown-body table, +.markdown-body pre { + margin-top: 0; + margin-bottom: 16px; +} + +.markdown-body hr { + height: 0.25em; + padding: 0; + margin: 24px 0; + background-color: #e1e4e8; + border: 0; +} + +.markdown-body blockquote { + padding: 0 1em; + color: #6a737d; + border-left: 0.25em solid #dfe2e5; +} + +.markdown-body blockquote>:first-child { + margin-top: 0; +} + +.markdown-body blockquote>:last-child { + margin-bottom: 0; +} + +.markdown-body kbd { + display: inline-block; + padding: 3px 5px; + font-size: 11px; + line-height: 10px; + color: #444d56; + vertical-align: middle; + background-color: #fafbfc; + border: solid 1px #c6cbd1; + border-bottom-color: #959da5; + border-radius: 3px; + box-shadow: inset 0 -1px 0 #959da5; +} + +.markdown-body h1, +.markdown-body h2, +.markdown-body h3, +.markdown-body h4, +.markdown-body h5, +.markdown-body h6 { + margin-top: 24px; + margin-bottom: 16px; + font-weight: 600; + line-height: 1.25; +} + +.markdown-body h1 .octicon-link, +.markdown-body h2 .octicon-link, +.markdown-body h3 .octicon-link, +.markdown-body h4 .octicon-link, +.markdown-body h5 .octicon-link, +.markdown-body h6 .octicon-link { + color: #1b1f23; + vertical-align: middle; + visibility: hidden; +} + +.markdown-body h1:hover .anchor, +.markdown-body h2:hover .anchor, +.markdown-body h3:hover .anchor, +.markdown-body h4:hover .anchor, +.markdown-body h5:hover .anchor, +.markdown-body h6:hover .anchor { + text-decoration: none; +} + +.markdown-body h1:hover .anchor .octicon-link, +.markdown-body h2:hover .anchor .octicon-link, +.markdown-body h3:hover .anchor .octicon-link, +.markdown-body h4:hover .anchor .octicon-link, +.markdown-body h5:hover .anchor .octicon-link, +.markdown-body h6:hover .anchor .octicon-link { + visibility: visible; +} + +.markdown-body h1 { + padding-bottom: 0.3em; + font-size: 2em; + border-bottom: 1px solid #eaecef; +} + +.markdown-body h2 { + padding-bottom: 0.3em; + font-size: 1.5em; + border-bottom: 1px solid #eaecef; +} + +.markdown-body h3 { + font-size: 1.25em; +} + +.markdown-body h4 { + font-size: 1em; +} + +.markdown-body h5 { + font-size: 0.875em; +} + +.markdown-body h6 { + font-size: 0.85em; + color: #6a737d; +} + +.markdown-body ul, +.markdown-body ol { + padding-left: 2em; +} + +.markdown-body ul ul, +.markdown-body ul ol, +.markdown-body ol ol, +.markdown-body ol ul { + margin-top: 0; + margin-bottom: 0; +} + +.markdown-body li { + word-wrap: break-all; +} + +.markdown-body li>p { + margin-top: 16px; +} + +.markdown-body li+li { + margin-top: 0.25em; +} + +.markdown-body dl { + padding: 0; +} + +.markdown-body dl dt { + padding: 0; + margin-top: 16px; + font-size: 1em; + font-style: italic; + font-weight: 600; +} + +.markdown-body dl dd { + padding: 0 16px; + margin-bottom: 16px; +} + +.markdown-body table { + display: block; + width: 100%; + overflow: auto; +} + +.markdown-body table th { + font-weight: 600; +} + +.markdown-body table th, +.markdown-body table td { + padding: 6px 13px; + border: 1px solid #dfe2e5; +} + +.markdown-body table tr { + background-color: #fff; + border-top: 1px solid #c6cbd1; +} + +.markdown-body table tr:nth-child(2n) { + background-color: #f6f8fa; +} + +.markdown-body img { + max-width: 100%; + box-sizing: content-box; + background-color: #fff; +} + +.markdown-body img[align=right] { + padding-left: 20px; +} + +.markdown-body img[align=left] { + padding-right: 20px; +} + +.markdown-body code { + padding: 0.2em 0.4em; + margin: 0; + font-size: 85%; + background-color: rgba(27,31,35,0.05); + border-radius: 3px; +} + +.markdown-body pre { + word-wrap: normal; +} + +.markdown-body pre>code { + padding: 0; + margin: 0; + font-size: 100%; + word-break: normal; + white-space: pre; + background: transparent; + border: 0; +} + +.markdown-body .highlight { + margin-bottom: 16px; +} + +.markdown-body .highlight pre { + margin-bottom: 0; + word-break: normal; +} + +.markdown-body .highlight pre, +.markdown-body pre { + padding: 16px; + overflow: auto; + font-size: 85%; + line-height: 1.45; + background-color: #f6f8fa; + border-radius: 3px; +} + +.markdown-body pre code { + display: inline; + max-width: auto; + padding: 0; + margin: 0; + overflow: visible; + line-height: inherit; + word-wrap: normal; + background-color: transparent; + border: 0; +} + +.markdown-body .full-commit .btn-outline:not(:disabled):hover { + color: #005cc5; + border-color: #005cc5; +} + +.markdown-body kbd { + display: inline-block; + padding: 3px 5px; + font: 11px "SFMono-Regular", Consolas, "Liberation Mono", Menlo, Courier, monospace; + line-height: 10px; + color: #444d56; + vertical-align: middle; + background-color: #fafbfc; + border: solid 1px #d1d5da; + border-bottom-color: #c6cbd1; + border-radius: 3px; + box-shadow: inset 0 -1px 0 #c6cbd1; +} + +.markdown-body :checked+.radio-label { + position: relative; + z-index: 1; + border-color: #0366d6; +} + +.markdown-body .task-list-item { + list-style-type: none; +} + +.markdown-body .task-list-item+.task-list-item { + margin-top: 3px; +} + +.markdown-body .task-list-item input { + margin: 0 0.2em 0.25em -1.6em; + vertical-align: middle; +} + +.markdown-body hr { + border-bottom-color: #eee; +} + + /* 代码高亮 */ + + .markdown-body pre code { + display: block; + overflow-x: auto; + padding: 0.5em; + background: #f6f8fa; + color: #24292e; +} + +/* + +github.com style (c) Vasily Polovnyov + +*/ + +.hljs { + display: block; + overflow-x: auto; + padding: 0.5em; + color: #333; + background: #f8f8f8; +} + +.hljs-comment, +.hljs-quote { + color: #998; + font-style: italic; +} + +.hljs-keyword, +.hljs-selector-tag, +.hljs-subst { + color: #333; + font-weight: bold; +} + +.hljs-number, +.hljs-literal, +.hljs-variable, +.hljs-template-variable, +.hljs-tag .hljs-attr { + color: #008080; +} + +.hljs-string, +.hljs-doctag { + color: #d14; +} + +.hljs-title, +.hljs-section, +.hljs-selector-id { + color: #900; + font-weight: bold; +} + +.hljs-subst { + font-weight: normal; +} + +.hljs-type, +.hljs-class .hljs-title { + color: #458; + font-weight: bold; +} + +.hljs-tag, +.hljs-name, +.hljs-attribute { + color: #000080; + font-weight: normal; +} + +.hljs-regexp, +.hljs-link { + color: #009926; +} + +.hljs-symbol, +.hljs-bullet { + color: #990073; +} + +.hljs-built_in, +.hljs-builtin-name { + color: #0086b3; +} + +.hljs-meta { + color: #999; + font-weight: bold; +} + +.hljs-deletion { + background: #fdd; +} + +.hljs-addition { + background: #dfd; +} + +.hljs-emphasis { + font-style: italic; +} + +.hljs-strong { + font-weight: bold; +} + + +// katex 样式 +$katexPath: 'https://cdnjs.cloudflare.com/ajax/libs/KaTeX/0.5.1/'; +@font-face { + font-family:KaTeX_AMS; + src:url(#{$katexPath}fonts/KaTeX_AMS-Regular.eot); + src:url(#{$katexPath}fonts/KaTeX_AMS-Regular.eot#iefix) format('embedded-opentype'), url(#{$katexPath}fonts/KaTeX_AMS-Regular.woff2) format('woff2'), url(#{$katexPath}fonts/KaTeX_AMS-Regular.woff) format('woff'), url(#{$katexPath}fonts/KaTeX_AMS-Regular.ttf) format('ttf'); + font-weight:400; + font-style:normal +} +@font-face { + font-family:KaTeX_Caligraphic; + src:url(#{$katexPath}fonts/KaTeX_Caligraphic-Bold.eot); + src:url(#{$katexPath}fonts/KaTeX_Caligraphic-Bold.eot#iefix) format('embedded-opentype'), url(#{$katexPath}fonts/KaTeX_Caligraphic-Bold.woff2) format('woff2'), url(#{$katexPath}fonts/KaTeX_Caligraphic-Bold.woff) format('woff'), url(#{$katexPath}fonts/KaTeX_Caligraphic-Bold.ttf) format('ttf'); + font-weight:700; + font-style:normal +} +@font-face { + font-family:KaTeX_Caligraphic; + src:url(#{$katexPath}fonts/KaTeX_Caligraphic-Regular.eot); + src:url(#{$katexPath}fonts/KaTeX_Caligraphic-Regular.eot#iefix) format('embedded-opentype'), url(#{$katexPath}fonts/KaTeX_Caligraphic-Regular.woff2) format('woff2'), url(#{$katexPath}fonts/KaTeX_Caligraphic-Regular.woff) format('woff'), url(#{$katexPath}fonts/KaTeX_Caligraphic-Regular.ttf) format('ttf'); + font-weight:400; + font-style:normal +} +@font-face { + font-family:KaTeX_Fraktur; + src:url(#{$katexPath}fonts/KaTeX_Fraktur-Bold.eot); + src:url(#{$katexPath}fonts/KaTeX_Fraktur-Bold.eot#iefix) format('embedded-opentype'), url(#{$katexPath}fonts/KaTeX_Fraktur-Bold.woff2) format('woff2'), url(#{$katexPath}fonts/KaTeX_Fraktur-Bold.woff) format('woff'), url(#{$katexPath}fonts/KaTeX_Fraktur-Bold.ttf) format('ttf'); + font-weight:700; + font-style:normal +} +@font-face { + font-family:KaTeX_Fraktur; + src:url(#{$katexPath}fonts/KaTeX_Fraktur-Regular.eot); + src:url(#{$katexPath}fonts/KaTeX_Fraktur-Regular.eot#iefix) format('embedded-opentype'), url(#{$katexPath}fonts/KaTeX_Fraktur-Regular.woff2) format('woff2'), url(#{$katexPath}fonts/KaTeX_Fraktur-Regular.woff) format('woff'), url(#{$katexPath}fonts/KaTeX_Fraktur-Regular.ttf) format('ttf'); + font-weight:400; + font-style:normal +} +@font-face { + font-family:KaTeX_Main; + src:url(#{$katexPath}fonts/KaTeX_Main-Bold.eot); + src:url(#{$katexPath}fonts/KaTeX_Main-Bold.eot#iefix) format('embedded-opentype'), url(#{$katexPath}fonts/KaTeX_Main-Bold.woff2) format('woff2'), url(#{$katexPath}fonts/KaTeX_Main-Bold.woff) format('woff'), url(#{$katexPath}fonts/KaTeX_Main-Bold.ttf) format('ttf'); + font-weight:700; + font-style:normal +} +@font-face { + font-family:KaTeX_Main; + src:url(#{$katexPath}fonts/KaTeX_Main-Italic.eot); + src:url(#{$katexPath}fonts/KaTeX_Main-Italic.eot#iefix) format('embedded-opentype'), url(#{$katexPath}fonts/KaTeX_Main-Italic.woff2) format('woff2'), url(#{$katexPath}fonts/KaTeX_Main-Italic.woff) format('woff'), url(#{$katexPath}fonts/KaTeX_Main-Italic.ttf) format('ttf'); + font-weight:400; + font-style:italic +} +@font-face { + font-family:KaTeX_Main; + src:url(#{$katexPath}fonts/KaTeX_Main-Regular.eot); + src:url(#{$katexPath}fonts/KaTeX_Main-Regular.eot#iefix) format('embedded-opentype'), url(#{$katexPath}fonts/KaTeX_Main-Regular.woff2) format('woff2'), url(#{$katexPath}fonts/KaTeX_Main-Regular.woff) format('woff'), url(#{$katexPath}fonts/KaTeX_Main-Regular.ttf) format('ttf'); + font-weight:400; + font-style:normal +} +@font-face { + font-family:KaTeX_Math; + src:url(#{$katexPath}fonts/KaTeX_Math-Italic.eot); + src:url(#{$katexPath}fonts/KaTeX_Math-Italic.eot#iefix) format('embedded-opentype'), url(#{$katexPath}fonts/KaTeX_Math-Italic.woff2) format('woff2'), url(#{$katexPath}fonts/KaTeX_Math-Italic.woff) format('woff'), url(#{$katexPath}fonts/KaTeX_Math-Italic.ttf) format('ttf'); + font-weight:400; + font-style:italic +} +@font-face { + font-family:KaTeX_SansSerif; + src:url(#{$katexPath}fonts/KaTeX_SansSerif-Regular.eot); + src:url(#{$katexPath}fonts/KaTeX_SansSerif-Regular.eot#iefix) format('embedded-opentype'), url(#{$katexPath}fonts/KaTeX_SansSerif-Regular.woff2) format('woff2'), url(#{$katexPath}fonts/KaTeX_SansSerif-Regular.woff) format('woff'), url(#{$katexPath}fonts/KaTeX_SansSerif-Regular.ttf) format('ttf'); + font-weight:400; + font-style:normal +} +@font-face { + font-family:KaTeX_Script; + src:url(#{$katexPath}fonts/KaTeX_Script-Regular.eot); + src:url(#{$katexPath}fonts/KaTeX_Script-Regular.eot#iefix) format('embedded-opentype'), url(#{$katexPath}fonts/KaTeX_Script-Regular.woff2) format('woff2'), url(#{$katexPath}fonts/KaTeX_Script-Regular.woff) format('woff'), url(#{$katexPath}fonts/KaTeX_Script-Regular.ttf) format('ttf'); + font-weight:400; + font-style:normal +} +@font-face { + font-family:KaTeX_Size1; + src:url(#{$katexPath}fonts/KaTeX_Size1-Regular.eot); + src:url(#{$katexPath}fonts/KaTeX_Size1-Regular.eot#iefix) format('embedded-opentype'), url(#{$katexPath}fonts/KaTeX_Size1-Regular.woff2) format('woff2'), url(#{$katexPath}fonts/KaTeX_Size1-Regular.woff) format('woff'), url(#{$katexPath}fonts/KaTeX_Size1-Regular.ttf) format('ttf'); + font-weight:400; + font-style:normal +} +@font-face { + font-family:KaTeX_Size2; + src:url(#{$katexPath}fonts/KaTeX_Size2-Regular.eot); + src:url(#{$katexPath}fonts/KaTeX_Size2-Regular.eot#iefix) format('embedded-opentype'), url(#{$katexPath}fonts/KaTeX_Size2-Regular.woff2) format('woff2'), url(#{$katexPath}fonts/KaTeX_Size2-Regular.woff) format('woff'), url(#{$katexPath}fonts/KaTeX_Size2-Regular.ttf) format('ttf'); + font-weight:400; + font-style:normal +} +@font-face { + font-family:KaTeX_Size3; + src:url(#{$katexPath}fonts/KaTeX_Size3-Regular.eot); + src:url(#{$katexPath}fonts/KaTeX_Size3-Regular.eot#iefix) format('embedded-opentype'), url(#{$katexPath}fonts/KaTeX_Size3-Regular.woff2) format('woff2'), url(#{$katexPath}fonts/KaTeX_Size3-Regular.woff) format('woff'), url(#{$katexPath}fonts/KaTeX_Size3-Regular.ttf) format('ttf'); + font-weight:400; + font-style:normal +} +@font-face { + font-family:KaTeX_Size4; + src:url(#{$katexPath}fonts/KaTeX_Size4-Regular.eot); + src:url(#{$katexPath}fonts/KaTeX_Size4-Regular.eot#iefix) format('embedded-opentype'), url(#{$katexPath}fonts/KaTeX_Size4-Regular.woff2) format('woff2'), url(#{$katexPath}fonts/KaTeX_Size4-Regular.woff) format('woff'), url(#{$katexPath}fonts/KaTeX_Size4-Regular.ttf) format('ttf'); + font-weight:400; + font-style:normal +} +@font-face { + font-family:KaTeX_Typewriter; + src:url(#{$katexPath}fonts/KaTeX_Typewriter-Regular.eot); + src:url(#{$katexPath}fonts/KaTeX_Typewriter-Regular.eot#iefix) format('embedded-opentype'), url(#{$katexPath}fonts/KaTeX_Typewriter-Regular.woff2) format('woff2'), url(#{$katexPath}fonts/KaTeX_Typewriter-Regular.woff) format('woff'), url(#{$katexPath}fonts/KaTeX_Typewriter-Regular.ttf) format('ttf'); + font-weight:400; + font-style:normal +} +.katex-display { + display:block; + margin:1em 0; + text-align:center +} +.katex-display>.katex { + display:inline-block +} +.katex { + font:400 1.21em KaTeX_Main; + line-height:1.2; + white-space:nowrap; + text-indent:0 +} +.katex .katex-html { + display:inline-block +} +.katex .katex-mathml { + position:absolute; + clip:rect(1px, 1px, 1px, 1px); + padding:0; + border:0; + height:1px; + width:1px; + overflow:hidden +} +.katex .base, .katex .strut { + display:inline-block +} +.katex .mathit { + font-family:KaTeX_Math; + font-style:italic +} +.katex .mathbf { + font-family:KaTeX_Main; + font-weight:700 +} +.katex .amsrm, .katex .mathbb { + font-family:KaTeX_AMS +} +.katex .mathcal { + font-family:KaTeX_Caligraphic +} +.katex .mathfrak { + font-family:KaTeX_Fraktur +} +.katex .mathtt { + font-family:KaTeX_Typewriter +} +.katex .mathscr { + font-family:KaTeX_Script +} +.katex .mathsf { + font-family:KaTeX_SansSerif +} +.katex .mainit { + font-family:KaTeX_Main; + font-style:italic +} +.katex .textstyle>.mord+.mop { + margin-left:.16667em +} +.katex .textstyle>.mord+.mbin { + margin-left:.22222em +} +.katex .textstyle>.mord+.mrel { + margin-left:.27778em +} +.katex .textstyle>.mop+.mop, .katex .textstyle>.mop+.mord, .katex .textstyle>.mord+.minner { + margin-left:.16667em +} +.katex .textstyle>.mop+.mrel { + margin-left:.27778em +} +.katex .textstyle>.mop+.minner { + margin-left:.16667em +} +.katex .textstyle>.mbin+.minner, .katex .textstyle>.mbin+.mop, .katex .textstyle>.mbin+.mopen, .katex .textstyle>.mbin+.mord { + margin-left:.22222em +} +.katex .textstyle>.mrel+.minner, .katex .textstyle>.mrel+.mop, .katex .textstyle>.mrel+.mopen, .katex .textstyle>.mrel+.mord { + margin-left:.27778em +} +.katex .textstyle>.mclose+.mop { + margin-left:.16667em +} +.katex .textstyle>.mclose+.mbin { + margin-left:.22222em +} +.katex .textstyle>.mclose+.mrel { + margin-left:.27778em +} +.katex .textstyle>.mclose+.minner, .katex .textstyle>.minner+.mop, .katex .textstyle>.minner+.mord, .katex .textstyle>.mpunct+.mclose, .katex .textstyle>.mpunct+.minner, .katex .textstyle>.mpunct+.mop, .katex .textstyle>.mpunct+.mopen, .katex .textstyle>.mpunct+.mord, .katex .textstyle>.mpunct+.mpunct, .katex .textstyle>.mpunct+.mrel { + margin-left:.16667em +} +.katex .textstyle>.minner+.mbin { + margin-left:.22222em +} +.katex .textstyle>.minner+.mrel { + margin-left:.27778em +} +.katex .mclose+.mop, .katex .minner+.mop, .katex .mop+.mop, .katex .mop+.mord, .katex .mord+.mop, .katex .textstyle>.minner+.minner, .katex .textstyle>.minner+.mopen, .katex .textstyle>.minner+.mpunct { + margin-left:.16667em +} +.katex .reset-textstyle.textstyle { + font-size:1em +} +.katex .reset-textstyle.scriptstyle { + font-size:.7em +} +.katex .reset-textstyle.scriptscriptstyle { + font-size:.5em +} +.katex .reset-scriptstyle.textstyle { + font-size:1.42857em +} +.katex .reset-scriptstyle.scriptstyle { + font-size:1em +} +.katex .reset-scriptstyle.scriptscriptstyle { + font-size:.71429em +} +.katex .reset-scriptscriptstyle.textstyle { + font-size:2em +} +.katex .reset-scriptscriptstyle.scriptstyle { + font-size:1.4em +} +.katex .reset-scriptscriptstyle.scriptscriptstyle { + font-size:1em +} +.katex .style-wrap { + position:relative +} +.katex .vlist { + display:inline-block +} +.katex .vlist>span { + display:block; + height:0; + position:relative +} +.katex .vlist>span>span { + display:inline-block +} +.katex .vlist .baseline-fix { + display:inline-table; + table-layout:fixed +} +.katex .msupsub { + text-align:left +} +.katex .mfrac>span>span { + text-align:center +} +.katex .mfrac .frac-line { + width:100% +} +.katex .mfrac .frac-line:before { + border-bottom-style:solid; + border-bottom-width:1px; + content:""; + display:block +} +.katex .mfrac .frac-line:after { + border-bottom-style:solid; + border-bottom-width:.04em; + content:""; + display:block; + margin-top:-1px +} +.katex .mspace { + display:inline-block +} +.katex .mspace.negativethinspace { + margin-left:-.16667em +} +.katex .mspace.thinspace { + width:.16667em +} +.katex .mspace.mediumspace { + width:.22222em +} +.katex .mspace.thickspace { + width:.27778em +} +.katex .mspace.enspace { + width:.5em +} +.katex .mspace.quad { + width:1em +} +.katex .mspace.qquad { + width:2em +} +.katex .llap, .katex .rlap { + width:0; + position:relative +} +.katex .llap>.inner, .katex .rlap>.inner { + position:absolute +} +.katex .llap>.fix, .katex .rlap>.fix { + display:inline-block +} +.katex .llap>.inner { + right:0 +} +.katex .rlap>.inner { + left:0 +} +.katex .katex-logo .a { + font-size:.75em; + margin-left:-.32em; + position:relative; + top:-.2em +} +.katex .katex-logo .t { + margin-left:-.23em +} +.katex .katex-logo .e { + margin-left:-.1667em; + position:relative; + top:.2155em +} +.katex .katex-logo .x { + margin-left:-.125em +} +.katex .rule { + display:inline-block; + border-style:solid; + position:relative +} +.katex .overline .overline-line { + width:100% +} +.katex .overline .overline-line:before { + border-bottom-style:solid; + border-bottom-width:1px; + content:""; + display:block +} +.katex .overline .overline-line:after { + border-bottom-style:solid; + border-bottom-width:.04em; + content:""; + display:block; + margin-top:-1px +} +.katex .sqrt>.sqrt-sign { + position:relative +} +.katex .sqrt .sqrt-line { + width:100% +} +.katex .sqrt .sqrt-line:before { + border-bottom-style:solid; + border-bottom-width:1px; + content:""; + display:block +} +.katex .sqrt .sqrt-line:after { + border-bottom-style:solid; + border-bottom-width:.04em; + content:""; + display:block; + margin-top:-1px +} +.katex .sqrt>.root { + margin-left:.27777778em; + margin-right:-.55555556em +} +.katex .fontsize-ensurer, .katex .sizing { + display:inline-block +} +.katex .fontsize-ensurer.reset-size1.size1, .katex .sizing.reset-size1.size1 { + font-size:1em +} +.katex .fontsize-ensurer.reset-size1.size2, .katex .sizing.reset-size1.size2 { + font-size:1.4em +} +.katex .fontsize-ensurer.reset-size1.size3, .katex .sizing.reset-size1.size3 { + font-size:1.6em +} +.katex .fontsize-ensurer.reset-size1.size4, .katex .sizing.reset-size1.size4 { + font-size:1.8em +} +.katex .fontsize-ensurer.reset-size1.size5, .katex .sizing.reset-size1.size5 { + font-size:2em +} +.katex .fontsize-ensurer.reset-size1.size6, .katex .sizing.reset-size1.size6 { + font-size:2.4em +} +.katex .fontsize-ensurer.reset-size1.size7, .katex .sizing.reset-size1.size7 { + font-size:2.88em +} +.katex .fontsize-ensurer.reset-size1.size8, .katex .sizing.reset-size1.size8 { + font-size:3.46em +} +.katex .fontsize-ensurer.reset-size1.size9, .katex .sizing.reset-size1.size9 { + font-size:4.14em +} +.katex .fontsize-ensurer.reset-size1.size10, .katex .sizing.reset-size1.size10 { + font-size:4.98em +} +.katex .fontsize-ensurer.reset-size2.size1, .katex .sizing.reset-size2.size1 { + font-size:.71428571em +} +.katex .fontsize-ensurer.reset-size2.size2, .katex .sizing.reset-size2.size2 { + font-size:1em +} +.katex .fontsize-ensurer.reset-size2.size3, .katex .sizing.reset-size2.size3 { + font-size:1.14285714em +} +.katex .fontsize-ensurer.reset-size2.size4, .katex .sizing.reset-size2.size4 { + font-size:1.28571429em +} +.katex .fontsize-ensurer.reset-size2.size5, .katex .sizing.reset-size2.size5 { + font-size:1.42857143em +} +.katex .fontsize-ensurer.reset-size2.size6, .katex .sizing.reset-size2.size6 { + font-size:1.71428571em +} +.katex .fontsize-ensurer.reset-size2.size7, .katex .sizing.reset-size2.size7 { + font-size:2.05714286em +} +.katex .fontsize-ensurer.reset-size2.size8, .katex .sizing.reset-size2.size8 { + font-size:2.47142857em +} +.katex .fontsize-ensurer.reset-size2.size9, .katex .sizing.reset-size2.size9 { + font-size:2.95714286em +} +.katex .fontsize-ensurer.reset-size2.size10, .katex .sizing.reset-size2.size10 { + font-size:3.55714286em +} +.katex .fontsize-ensurer.reset-size3.size1, .katex .sizing.reset-size3.size1 { + font-size:.625em +} +.katex .fontsize-ensurer.reset-size3.size2, .katex .sizing.reset-size3.size2 { + font-size:.875em +} +.katex .fontsize-ensurer.reset-size3.size3, .katex .sizing.reset-size3.size3 { + font-size:1em +} +.katex .fontsize-ensurer.reset-size3.size4, .katex .sizing.reset-size3.size4 { + font-size:1.125em +} +.katex .fontsize-ensurer.reset-size3.size5, .katex .sizing.reset-size3.size5 { + font-size:1.25em +} +.katex .fontsize-ensurer.reset-size3.size6, .katex .sizing.reset-size3.size6 { + font-size:1.5em +} +.katex .fontsize-ensurer.reset-size3.size7, .katex .sizing.reset-size3.size7 { + font-size:1.8em +} +.katex .fontsize-ensurer.reset-size3.size8, .katex .sizing.reset-size3.size8 { + font-size:2.1625em +} +.katex .fontsize-ensurer.reset-size3.size9, .katex .sizing.reset-size3.size9 { + font-size:2.5875em +} +.katex .fontsize-ensurer.reset-size3.size10, .katex .sizing.reset-size3.size10 { + font-size:3.1125em +} +.katex .fontsize-ensurer.reset-size4.size1, .katex .sizing.reset-size4.size1 { + font-size:.55555556em +} +.katex .fontsize-ensurer.reset-size4.size2, .katex .sizing.reset-size4.size2 { + font-size:.77777778em +} +.katex .fontsize-ensurer.reset-size4.size3, .katex .sizing.reset-size4.size3 { + font-size:.88888889em +} +.katex .fontsize-ensurer.reset-size4.size4, .katex .sizing.reset-size4.size4 { + font-size:1em +} +.katex .fontsize-ensurer.reset-size4.size5, .katex .sizing.reset-size4.size5 { + font-size:1.11111111em +} +.katex .fontsize-ensurer.reset-size4.size6, .katex .sizing.reset-size4.size6 { + font-size:1.33333333em +} +.katex .fontsize-ensurer.reset-size4.size7, .katex .sizing.reset-size4.size7 { + font-size:1.6em +} +.katex .fontsize-ensurer.reset-size4.size8, .katex .sizing.reset-size4.size8 { + font-size:1.92222222em +} +.katex .fontsize-ensurer.reset-size4.size9, .katex .sizing.reset-size4.size9 { + font-size:2.3em +} +.katex .fontsize-ensurer.reset-size4.size10, .katex .sizing.reset-size4.size10 { + font-size:2.76666667em +} +.katex .fontsize-ensurer.reset-size5.size1, .katex .sizing.reset-size5.size1 { + font-size:.5em +} +.katex .fontsize-ensurer.reset-size5.size2, .katex .sizing.reset-size5.size2 { + font-size:.7em +} +.katex .fontsize-ensurer.reset-size5.size3, .katex .sizing.reset-size5.size3 { + font-size:.8em +} +.katex .fontsize-ensurer.reset-size5.size4, .katex .sizing.reset-size5.size4 { + font-size:.9em +} +.katex .fontsize-ensurer.reset-size5.size5, .katex .sizing.reset-size5.size5 { + font-size:1em +} +.katex .fontsize-ensurer.reset-size5.size6, .katex .sizing.reset-size5.size6 { + font-size:1.2em +} +.katex .fontsize-ensurer.reset-size5.size7, .katex .sizing.reset-size5.size7 { + font-size:1.44em +} +.katex .fontsize-ensurer.reset-size5.size8, .katex .sizing.reset-size5.size8 { + font-size:1.73em +} +.katex .fontsize-ensurer.reset-size5.size9, .katex .sizing.reset-size5.size9 { + font-size:2.07em +} +.katex .fontsize-ensurer.reset-size5.size10, .katex .sizing.reset-size5.size10 { + font-size:2.49em +} +.katex .fontsize-ensurer.reset-size6.size1, .katex .sizing.reset-size6.size1 { + font-size:.41666667em +} +.katex .fontsize-ensurer.reset-size6.size2, .katex .sizing.reset-size6.size2 { + font-size:.58333333em +} +.katex .fontsize-ensurer.reset-size6.size3, .katex .sizing.reset-size6.size3 { + font-size:.66666667em +} +.katex .fontsize-ensurer.reset-size6.size4, .katex .sizing.reset-size6.size4 { + font-size:.75em +} +.katex .fontsize-ensurer.reset-size6.size5, .katex .sizing.reset-size6.size5 { + font-size:.83333333em +} +.katex .fontsize-ensurer.reset-size6.size6, .katex .sizing.reset-size6.size6 { + font-size:1em +} +.katex .fontsize-ensurer.reset-size6.size7, .katex .sizing.reset-size6.size7 { + font-size:1.2em +} +.katex .fontsize-ensurer.reset-size6.size8, .katex .sizing.reset-size6.size8 { + font-size:1.44166667em +} +.katex .fontsize-ensurer.reset-size6.size9, .katex .sizing.reset-size6.size9 { + font-size:1.725em +} +.katex .fontsize-ensurer.reset-size6.size10, .katex .sizing.reset-size6.size10 { + font-size:2.075em +} +.katex .fontsize-ensurer.reset-size7.size1, .katex .sizing.reset-size7.size1 { + font-size:.34722222em +} +.katex .fontsize-ensurer.reset-size7.size2, .katex .sizing.reset-size7.size2 { + font-size:.48611111em +} +.katex .fontsize-ensurer.reset-size7.size3, .katex .sizing.reset-size7.size3 { + font-size:.55555556em +} +.katex .fontsize-ensurer.reset-size7.size4, .katex .sizing.reset-size7.size4 { + font-size:.625em +} +.katex .fontsize-ensurer.reset-size7.size5, .katex .sizing.reset-size7.size5 { + font-size:.69444444em +} +.katex .fontsize-ensurer.reset-size7.size6, .katex .sizing.reset-size7.size6 { + font-size:.83333333em +} +.katex .fontsize-ensurer.reset-size7.size7, .katex .sizing.reset-size7.size7 { + font-size:1em +} +.katex .fontsize-ensurer.reset-size7.size8, .katex .sizing.reset-size7.size8 { + font-size:1.20138889em +} +.katex .fontsize-ensurer.reset-size7.size9, .katex .sizing.reset-size7.size9 { + font-size:1.4375em +} +.katex .fontsize-ensurer.reset-size7.size10, .katex .sizing.reset-size7.size10 { + font-size:1.72916667em +} +.katex .fontsize-ensurer.reset-size8.size1, .katex .sizing.reset-size8.size1 { + font-size:.28901734em +} +.katex .fontsize-ensurer.reset-size8.size2, .katex .sizing.reset-size8.size2 { + font-size:.40462428em +} +.katex .fontsize-ensurer.reset-size8.size3, .katex .sizing.reset-size8.size3 { + font-size:.46242775em +} +.katex .fontsize-ensurer.reset-size8.size4, .katex .sizing.reset-size8.size4 { + font-size:.52023121em +} +.katex .fontsize-ensurer.reset-size8.size5, .katex .sizing.reset-size8.size5 { + font-size:.57803468em +} +.katex .fontsize-ensurer.reset-size8.size6, .katex .sizing.reset-size8.size6 { + font-size:.69364162em +} +.katex .fontsize-ensurer.reset-size8.size7, .katex .sizing.reset-size8.size7 { + font-size:.83236994em +} +.katex .fontsize-ensurer.reset-size8.size8, .katex .sizing.reset-size8.size8 { + font-size:1em +} +.katex .fontsize-ensurer.reset-size8.size9, .katex .sizing.reset-size8.size9 { + font-size:1.19653179em +} +.katex .fontsize-ensurer.reset-size8.size10, .katex .sizing.reset-size8.size10 { + font-size:1.43930636em +} +.katex .fontsize-ensurer.reset-size9.size1, .katex .sizing.reset-size9.size1 { + font-size:.24154589em +} +.katex .fontsize-ensurer.reset-size9.size2, .katex .sizing.reset-size9.size2 { + font-size:.33816425em +} +.katex .fontsize-ensurer.reset-size9.size3, .katex .sizing.reset-size9.size3 { + font-size:.38647343em +} +.katex .fontsize-ensurer.reset-size9.size4, .katex .sizing.reset-size9.size4 { + font-size:.43478261em +} +.katex .fontsize-ensurer.reset-size9.size5, .katex .sizing.reset-size9.size5 { + font-size:.48309179em +} +.katex .fontsize-ensurer.reset-size9.size6, .katex .sizing.reset-size9.size6 { + font-size:.57971014em +} +.katex .fontsize-ensurer.reset-size9.size7, .katex .sizing.reset-size9.size7 { + font-size:.69565217em +} +.katex .fontsize-ensurer.reset-size9.size8, .katex .sizing.reset-size9.size8 { + font-size:.83574879em +} +.katex .fontsize-ensurer.reset-size9.size9, .katex .sizing.reset-size9.size9 { + font-size:1em +} +.katex .fontsize-ensurer.reset-size9.size10, .katex .sizing.reset-size9.size10 { + font-size:1.20289855em +} +.katex .fontsize-ensurer.reset-size10.size1, .katex .sizing.reset-size10.size1 { + font-size:.20080321em +} +.katex .fontsize-ensurer.reset-size10.size2, .katex .sizing.reset-size10.size2 { + font-size:.2811245em +} +.katex .fontsize-ensurer.reset-size10.size3, .katex .sizing.reset-size10.size3 { + font-size:.32128514em +} +.katex .fontsize-ensurer.reset-size10.size4, .katex .sizing.reset-size10.size4 { + font-size:.36144578em +} +.katex .fontsize-ensurer.reset-size10.size5, .katex .sizing.reset-size10.size5 { + font-size:.40160643em +} +.katex .fontsize-ensurer.reset-size10.size6, .katex .sizing.reset-size10.size6 { + font-size:.48192771em +} +.katex .fontsize-ensurer.reset-size10.size7, .katex .sizing.reset-size10.size7 { + font-size:.57831325em +} +.katex .fontsize-ensurer.reset-size10.size8, .katex .sizing.reset-size10.size8 { + font-size:.69477912em +} +.katex .fontsize-ensurer.reset-size10.size9, .katex .sizing.reset-size10.size9 { + font-size:.8313253em +} +.katex .fontsize-ensurer.reset-size10.size10, .katex .sizing.reset-size10.size10 { + font-size:1em +} +.katex .delimsizing.size1 { + font-family:KaTeX_Size1 +} +.katex .delimsizing.size2 { + font-family:KaTeX_Size2 +} +.katex .delimsizing.size3 { + font-family:KaTeX_Size3 +} +.katex .delimsizing.size4 { + font-family:KaTeX_Size4 +} +.katex .delimsizing.mult .delim-size1>span { + font-family:KaTeX_Size1 +} +.katex .delimsizing.mult .delim-size4>span { + font-family:KaTeX_Size4 +} +.katex .nulldelimiter { + display:inline-block; + width:.12em +} +.katex .op-symbol { + position:relative +} +.katex .op-symbol.small-op { + font-family:KaTeX_Size1 +} +.katex .op-symbol.large-op { + font-family:KaTeX_Size2 +} +.katex .accent>.vlist>span, .katex .op-limits>.vlist>span { + text-align:center +} +.katex .accent .accent-body>span { + width:0 +} +.katex .accent .accent-body.accent-vec>span { + position:relative; + left:.326em +} +.katex .mtable .vertical-separator { + display:inline-block; + margin:0 -.025em; + border-right:.05em solid #000 +} +.katex .mtable .arraycolsep { + display:inline-block +} +.katex .mtable .col-align-c>.vlist { + text-align:center +} +.katex .mtable .col-align-l>.vlist { + text-align:left +} +.katex .mtable .col-align-r>.vlist { + text-align:right +} diff --git a/sop-website/sop-portal/src/pages/blog/blogItem.jsx b/sop-website/sop-portal/src/pages/blog/blogItem.jsx new file mode 100644 index 00000000..bd9eb5ee --- /dev/null +++ b/sop-website/sop-portal/src/pages/blog/blogItem.jsx @@ -0,0 +1,57 @@ +import React from 'react'; +import { autobind } from 'core-decorators'; +import { getLink } from '../../../utils'; + +import './blogItem.scss'; + +@autobind +class BlogItem extends React.Component { + constructor(props) { + super(props); + this.state = { + isHovered: false, + }; + } + + onMouseOver() { + this.setState({ + isHovered: true, + }); + } + + onMouseOut() { + this.setState({ + isHovered: false, + }); + } + + render() { + const { dataSource } = this.props; + const { link, target, title, author, companyIcon, companyIconHover, dateStr, desc } = dataSource; + const { isHovered } = this.state; + return ( + +
    + + {title} +
    +
    + {author} + { + companyIcon ? : null + } + {dateStr} +
    +

    {desc}

    +
    + ); + } +} + +export default BlogItem; diff --git a/sop-website/sop-portal/src/pages/blog/blogItem.scss b/sop-website/sop-portal/src/pages/blog/blogItem.scss new file mode 100644 index 00000000..c7b3b028 --- /dev/null +++ b/sop-website/sop-portal/src/pages/blog/blogItem.scss @@ -0,0 +1,57 @@ +.blog-item { + box-sizing: border-box; + display: block; + width: 100%; + padding: 20px; + margin-bottom: 40px; + background: #F8F8F8; + .title { + img { + width: 16px; + height: 20px; + margin-right: 8px; + } + span { + font-family: Avenir-Heavy; + font-size: 20px; + color: #666666; + } + } + .brief-info { + padding: 12px 0 20px; + .author { + font-family: Avenir-Heavy; + font-size: 14px; + color: #999; + margin-right: 8px; + } + img { + width: 14px; + height: 14px; + } + .date { + float: right; + font-family: Avenir-Medium; + font-size: 12px; + color: #999; + } + } + p { + font-family: Avenir-Medium; + font-size: 14px; + color: #666; + margin: 0; + } + &:hover { + .title { + span { + color: #333; + } + } + .brief-info { + .author { + color: #666; + } + } + } +} \ No newline at end of file diff --git a/sop-website/sop-portal/src/pages/blog/index.jsx b/sop-website/sop-portal/src/pages/blog/index.jsx new file mode 100644 index 00000000..1069de74 --- /dev/null +++ b/sop-website/sop-portal/src/pages/blog/index.jsx @@ -0,0 +1,58 @@ +import React from 'react'; +import ReactDOM from 'react-dom'; +import Language from '../../components/language'; +import Header from '../../components/header'; +import Bar from '../../components/bar'; +import PageSlider from '../../components/pageSlider'; +import BlogItem from './blogItem'; +import Footer from '../../components/footer'; +import blogConfig from '../../../site_config/blog'; +import { getLink } from '../../../utils'; +import './index.scss'; + +class Blog extends Language { + + render() { + const language = this.getLanguage(); + const dataSource = blogConfig[language]; + const blogs = dataSource.list; + return ( +
    +
    + +
    +
    + + { + blogs.map((blog, i) => ( + + )) + } + +
    +
    +

    {dataSource.postsTitle}

    + +
    +
    +
    +
    + ); + } +} + +document.getElementById('root') && ReactDOM.render(, document.getElementById('root')); + +export default Blog; diff --git a/sop-website/sop-portal/src/pages/blog/index.scss b/sop-website/sop-portal/src/pages/blog/index.scss new file mode 100644 index 00000000..dd46ee16 --- /dev/null +++ b/sop-website/sop-portal/src/pages/blog/index.scss @@ -0,0 +1,55 @@ +@import '../../variables.scss'; +@import '../../reset.scss'; + +.blog-list-page { + .blog-container { + max-width: $contentWidth; + margin: 0 auto; + box-sizing: border-box; + padding: 50px 8% 80px; + .col { + display: inline-block; + box-sizing: border-box; + &-18 { + width: 75%; + border-right: 1px solid #CBCCCD; + padding-right: 6%; + } + &-6 { + width: 25%; + padding-left: 20px; + vertical-align: top; + h4 { + font-family: Avenir-Heavy; + font-size: 18px; + color: #333; + margin: 0 0 20px; + } + ul { + list-style: none; + margin: 0; + padding: 0; + li { + width: 100%; + margin-bottom: 14px; + span { + font-family: Avenir-Medium; + font-size: 12px; + color: #666; + } + } + } + } + } + @media screen and (max-width: $mobileWidth){ + .left-part { + width: 100%; + border-right: none; + padding-right: 0; + } + .right-part { + display: none; + } + } + } +} \ No newline at end of file diff --git a/sop-website/sop-portal/src/pages/blogDetail/index.jsx b/sop-website/sop-portal/src/pages/blogDetail/index.jsx new file mode 100644 index 00000000..93181677 --- /dev/null +++ b/sop-website/sop-portal/src/pages/blogDetail/index.jsx @@ -0,0 +1,109 @@ +import React from 'react'; +import ReactDOM from 'react-dom'; +import { scroller } from 'react-scroll'; +import path from 'path'; +import 'whatwg-fetch'; // fetch polyfill +import Language from '../../components/language'; +import Header from '../../components/header'; +import Footer from '../../components/footer'; +import './index.scss'; + +// 锚点正则 +const anchorReg = /^#[^/]/; +// 相对地址正则,包括./、../、直接文件夹名称开头、直接文件开头 +const relativeReg = /^((\.{1,2}\/)|([\w-]+[/.]))/; +class BlogDetail extends Language { + + constructor(props) { + super(props); + this.state = { + __html: '', + }; + } + + componentDidMount() { + // 通过请求获取生成好的json数据,静态页和json文件在同一个目录下 + fetch(window.location.pathname.replace(/\.html$/i, '.json')) + .then(res => res.json()) + .then((md) => { + this.setState({ + __html: md && md.__html ? md.__html : '', + }); + }); + this.markdownContainer.addEventListener('click', (e) => { + const isAnchor = e.target.nodeName.toLowerCase() === 'a' && e.target.getAttribute('href') && anchorReg.test(e.target.getAttribute('href')); + if (isAnchor) { + e.preventDefault(); + const id = e.target.getAttribute('href').slice(1); + scroller.scrollTo(id, { + duration: 1000, + smooth: 'easeInOutQuint', + }); + } + }); + } + + componentDidUpdate() { + this.handleRelativeLink(); + this.handleRelativeImg(); + } + + handleRelativeLink() { + const language = this.getLanguage(); + // 获取当前文档所在文件系统中的路径 + // rootPath/en-us/blog/dir/hello.html => /blog/en-us/dir + const splitPart = window.location.pathname.replace(`${window.rootPath}/${language}`, '').split('/').slice(0, -1); + const filePath = splitPart.join('/'); + const alinks = Array.from(this.markdownContainer.querySelectorAll('a')); + alinks.forEach((alink) => { + const href = alink.getAttribute('href'); + if (relativeReg.test(href)) { + // 文档之间有中英文之分,md的相对地址要转换为对应HTML的地址 + alink.href = `${path.join(`${window.rootPath}/${language}`, filePath, href.replace(/\.(md|markdown)$/, '.html'))}`; + } + }); + } + + handleRelativeImg() { + const language = this.getLanguage(); + // 获取当前文档所在文件系统中的路径 + // rootPath/en-us/blog/dir/hello.html => /blog/en-us/dir + const splitPart = window.location.pathname.replace(`${window.rootPath}/${language}`, '').split('/').slice(0, -1); + splitPart.splice(2, 0, language); + const filePath = splitPart.join('/'); + const imgs = Array.from(this.markdownContainer.querySelectorAll('img')); + imgs.forEach((img) => { + const src = img.getAttribute('src'); + if (relativeReg.test(src)) { + // 图片无中英文之分 + img.src = `${path.join(window.rootPath, filePath, src)}`; + } + }); + } + + render() { + const language = this.getLanguage(); + const __html = this.props.__html || this.state.__html; + return ( +
    +
    +
    { this.markdownContainer = node; }} + dangerouslySetInnerHTML={{ __html }} + /> +
    +
    + ); + } +} + +document.getElementById('root') && ReactDOM.render(, document.getElementById('root')); + +export default BlogDetail; diff --git a/sop-website/sop-portal/src/pages/blogDetail/index.scss b/sop-website/sop-portal/src/pages/blogDetail/index.scss new file mode 100644 index 00000000..b2c9cd18 --- /dev/null +++ b/sop-website/sop-portal/src/pages/blogDetail/index.scss @@ -0,0 +1,11 @@ +@import '../../variables.scss'; +@import '../../reset.scss'; +@import '../../markdown.scss'; + +.blog-detail-page { + .blog-content { + padding: 80px 20%; + margin: $headerHeight auto 0; + max-width: 735px; + } +} \ No newline at end of file diff --git a/sop-website/sop-portal/src/pages/community/contactItem.jsx b/sop-website/sop-portal/src/pages/community/contactItem.jsx new file mode 100644 index 00000000..38b6a0ec --- /dev/null +++ b/sop-website/sop-portal/src/pages/community/contactItem.jsx @@ -0,0 +1,44 @@ +import React from 'react'; +import { autobind } from 'core-decorators'; +import { getLink } from '../../../utils'; + +@autobind +class ContactItem extends React.Component { + constructor(props) { + super(props); + this.state = { + img: props.contact.img, + }; + } + + onMouseOver() { + this.setState({ + img: this.props.contact.imgHover, + }); + } + + onMouseOut() { + this.setState({ + img: this.props.contact.img, + }); + } + + render() { + const { contact } = this.props; + const { img } = this.state; + return ( + + +
    {contact.title}
    +
    + ); + } +} + +export default ContactItem; diff --git a/sop-website/sop-portal/src/pages/community/contributorItem.jsx b/sop-website/sop-portal/src/pages/community/contributorItem.jsx new file mode 100644 index 00000000..9645eda8 --- /dev/null +++ b/sop-website/sop-portal/src/pages/community/contributorItem.jsx @@ -0,0 +1,16 @@ +import React from 'react'; +import { getLink } from '../../../utils'; + +const ContributorItem = (props) => { + const { contributor } = props; + const { img, title, content } = contributor; + return ( +
    + +
    {title}
    +

    {content}

    +
    + ); +}; + +export default ContributorItem; diff --git a/sop-website/sop-portal/src/pages/community/ecoItem.jsx b/sop-website/sop-portal/src/pages/community/ecoItem.jsx new file mode 100644 index 00000000..365d2091 --- /dev/null +++ b/sop-website/sop-portal/src/pages/community/ecoItem.jsx @@ -0,0 +1,30 @@ +import React from 'react'; +import { getLink } from '../../../utils'; + +const EcoItem = (props) => { + const { eco } = props; + return ( +
    +

    {eco.title}

    +

    {eco.content}

    +
    + { + eco.tags.map((tag, i) => ( + + { + tag.text + } + + )) + } +
    +
    + ); +}; + +export default EcoItem; diff --git a/sop-website/sop-portal/src/pages/community/eventCard.jsx b/sop-website/sop-portal/src/pages/community/eventCard.jsx new file mode 100644 index 00000000..5760d54c --- /dev/null +++ b/sop-website/sop-portal/src/pages/community/eventCard.jsx @@ -0,0 +1,25 @@ +import React from 'react'; +import { getLink } from '../../../utils'; + +class EventCard extends React.Component { + render() { + const { event } = this.props; + return ( +
    + + + +
    +

    {event.title}

    +

    {event.content}

    + + {event.dateStr} + + +
    +
    + ); + } +} + +export default EventCard; diff --git a/sop-website/sop-portal/src/pages/community/index.jsx b/sop-website/sop-portal/src/pages/community/index.jsx new file mode 100644 index 00000000..007c4a57 --- /dev/null +++ b/sop-website/sop-portal/src/pages/community/index.jsx @@ -0,0 +1,79 @@ +import React from 'react'; +import ReactDOM from 'react-dom'; +import Language from '../../components/language'; +import Header from '../../components/header'; +import Bar from '../../components/bar'; +import Slider from '../../components/slider'; +import EventCard from './eventCard'; +import ContactItem from './contactItem'; +import ContributorItem from './contributorItem'; +import EcoItem from './ecoItem'; +import Footer from '../../components/footer'; +import communityConfig from '../../../site_config/community.jsx'; + +import './index.scss'; + +class Community extends Language { + + render() { + const language = this.getLanguage(); + const dataSource = communityConfig[language]; + return ( +
    +
    + +
    +

    {dataSource.events.title}

    + + {dataSource.events.list.map((event, i) => ( + + ))} + +
    +
    +

    {dataSource.ecos.title}

    +
    + { + dataSource.ecos.list.map((eco, i) => ( + + )) + } +
    +
    +
    +

    {dataSource.contacts.title}

    +

    {dataSource.contacts.desc}

    +
    + { + dataSource.contacts.list.map((contact, i) => ( + + )) + } +
    +
    +
    +

    {dataSource.contributorGuide.title}

    +

    {dataSource.contributorGuide.desc}

    +
    + { + dataSource.contributorGuide.list.map((contributor, i) => ( + + )) + } +
    +
    +
    +
    + ); + } +} + +document.getElementById('root') && ReactDOM.render(, document.getElementById('root')); + +export default Community; diff --git a/sop-website/sop-portal/src/pages/community/index.scss b/sop-website/sop-portal/src/pages/community/index.scss new file mode 100644 index 00000000..9823a671 --- /dev/null +++ b/sop-website/sop-portal/src/pages/community/index.scss @@ -0,0 +1,189 @@ +@import '../../variables.scss'; +@import '../../reset.scss'; + +.community-page { + .events-section { + max-width: $contentWidth; + margin: 0 auto; + box-sizing: border-box; + padding: 80px 40px 60px; + @media screen and (max-width: $mobileWidth) { + padding: 0; + } + h3 { + font-family: Avenir-Heavy; + font-size: 36px; + color: #333; + text-align: center; + margin: 0 0 40px; + } + .event-card { + width: 373px; + font-size: 0; + img { + width: 373px; + height: 209px; + } + @media screen and (max-width: $mobileWidth / 2) { + width: 320px; + img { + width: 320px; + height: 179px; + } + } + .event-introduction { + padding: 20px; + background: #F8F8F8; + h4 { + font-family: Avenir-Heavy; + font-size: 20px; + color: #333; + margin: 0 0 10px; + } + p { + font-family: Avenir-Medium; + font-size: 14px; + color: #666; + margin: 0; + } + a { + display: inline-block; + width: 100%; + font-family: Avenir-Medium; + font-size: 12px; + color: #999; + margin-top: 10px; + .arrow { + width: 8px; + height: 13px; + float: right; + } + } + } + } + } + .contact-section { + max-width: $contentWidth; + margin: 0 auto; + box-sizing: border-box; + padding: 60px 40px 40px; + @media screen and (max-width: $mobileWidth) { + padding-left: 20px; + padding-right: 20px; + } + h3 { + font-family: Avenir-Heavy; + font-size: 36px; + color: #333; + text-align: center; + margin: 0 0 12px; + } + p { + font-family: Avenir-Medium; + font-size: 14px; + color: #666; + text-align: center; + margin: 0 0 40px; + } + .contact-list { + display: flex; + justify-content: space-around; + flex-wrap: wrap; + .contact-item { + display: inline-block; + text-align: center; + padding: 0 20px; + // width: 82px; + font-family: Avenir-Heavy; + font-size: 18px; + color: #999; + &:hover { + color: $brandColor; + } + img { + width: 82px; + height: 86px; + } + } + } + } + .contributor-section { + max-width: $contentWidth; + margin: 0 auto; + box-sizing: border-box; + padding: 60px 40px 40px; + h3 { + font-family: Avenir-Heavy; + font-size: 36px; + color: #333; + text-align: center; + margin: 0 0 12px; + } + p { + font-family: Avenir-Medium; + font-size: 14px; + color: #666; + text-align: center; + margin: 0 0 40px; + } + .contributor-list { + display: flex; + justify-content: space-around; + flex-wrap: wrap; + .contributor-item { + display: inline-block; + width: 240px; + text-align: center; + font-family: Avenir-Heavy; + font-size: 18px; + color: #999; + img { + width: 82px; + height: 86px; + } + } + } + } + .eco-section { + padding: 60px 20% 0; + max-width: 735px; + margin: 0 auto; + h3 { + font-family: Avenir-Heavy; + font-size: 36px; + color: #333; + text-align: center; + margin-bottom: 12px; + } + .eco-item { + margin-bottom: 30px; + h4 { + font-family: Avenir-Heavy; + font-size: 18px; + color: #333; + margin: 0 0 10px; + } + p { + font-family: Avenir-Medium; + font-size: 14px; + color: #666; + text-align: justify; + margin: 12px 0; + } + .tags { + a { + font-family: Avenir-Medium; + font-size: 12px; + color: #fff; + display: inline-block; + height: 32px; + line-height: 32px; + padding: 0 16px; + border-radius: 2px; + text-align: center; + margin: 0 10px 10px 0; + } + } + } + } +} \ No newline at end of file diff --git a/sop-website/sop-portal/src/pages/documentation/index.jsx b/sop-website/sop-portal/src/pages/documentation/index.jsx new file mode 100644 index 00000000..fb53f715 --- /dev/null +++ b/sop-website/sop-portal/src/pages/documentation/index.jsx @@ -0,0 +1,118 @@ +import React from 'react'; +import ReactDOM from 'react-dom'; +import { scroller } from 'react-scroll'; +import 'whatwg-fetch'; // fetch polyfill +import path from 'path'; +import Language from '../../components/language'; +import Header from '../../components/header'; +import Bar from '../../components/bar'; +import Sidemenu from '../../components/sidemenu'; +import Footer from '../../components/footer'; +import docsConfig from '../../../site_config/docs'; +import './index.scss'; + +// 锚点正则 +const anchorReg = /^#[^/]/; +// 相对地址正则,包括./、../、直接文件夹名称开头、直接文件开头 +const relativeReg = /^((\.{1,2}\/)|([\w-]+[/.]))/; + +class Documentation extends Language { + + constructor(props) { + super(props); + this.state = { + __html: '', + }; + } + + componentDidMount() { + // 通过请求获取生成好的json数据,静态页和json文件在同一个目录下 + fetch(window.location.pathname.replace(/\.html$/i, '.json')) + .then(res => res.json()) + .then((md) => { + this.setState({ + __html: md && md.__html ? md.__html : '', + }); + }); + this.markdownContainer.addEventListener('click', (e) => { + const isAnchor = e.target.nodeName.toLowerCase() === 'a' && e.target.getAttribute('href') && anchorReg.test(e.target.getAttribute('href')); + if (isAnchor) { + e.preventDefault(); + const id = e.target.getAttribute('href').slice(1); + scroller.scrollTo(id, { + duration: 1000, + smooth: 'easeInOutQuint', + }); + } + }); + } + + componentDidUpdate() { + this.handleRelativeLink(); + this.handleRelativeImg(); + } + + handleRelativeLink() { + const language = this.getLanguage(); + // 获取当前文档所在文件系统中的路径 + // rootPath/en-us/docs/dir/hello.html => /docs/en-us/dir + const splitPart = window.location.pathname.replace(`${window.rootPath}/${language}`, '').split('/').slice(0, -1); + const filePath = splitPart.join('/'); + const alinks = Array.from(this.markdownContainer.querySelectorAll('a')); + alinks.forEach((alink) => { + const href = alink.getAttribute('href'); + if (relativeReg.test(href)) { + // 文档之间有中英文之分,md的相对地址要转换为对应HTML的地址 + alink.href = `${path.join(`${window.rootPath}/${language}`, filePath, href.replace(/\.(md|markdown)$/, '.html'))}`; + } + }); + } + + handleRelativeImg() { + const language = this.getLanguage(); + // 获取当前文档所在文件系统中的路径 + // rootPath/en-us/docs/dir/hello.html => /docs/en-us/dir + const splitPart = window.location.pathname.replace(`${window.rootPath}/${language}`, '').split('/').slice(0, -1); + splitPart.splice(2, 0, language); + const filePath = splitPart.join('/'); + const imgs = Array.from(this.markdownContainer.querySelectorAll('img')); + imgs.forEach((img) => { + const src = img.getAttribute('src'); + if (relativeReg.test(src)) { + // 图片无中英文之分 + img.src = `${path.join(window.rootPath, filePath, src)}`; + } + }); + } + + render() { + const language = this.getLanguage(); + const dataSource = docsConfig[language]; + const __html = this.props.__html || this.state.__html; + return ( +
    +
    + +
    + +
    { this.markdownContainer = node; }} + dangerouslySetInnerHTML={{ __html }} + /> +
    +
    +
    + ); + } +} + +document.getElementById('root') && ReactDOM.render(, document.getElementById('root')); + +export default Documentation; diff --git a/sop-website/sop-portal/src/pages/documentation/index.scss b/sop-website/sop-portal/src/pages/documentation/index.scss new file mode 100644 index 00000000..a29ea511 --- /dev/null +++ b/sop-website/sop-portal/src/pages/documentation/index.scss @@ -0,0 +1,36 @@ +@import '../../variables.scss'; +@import '../../reset.scss'; +@import '../../markdown.scss'; + +.documentation-page { + .content-section { + max-width: $contentWidth; + margin: 0 auto; + box-sizing: border-box; + padding: 40px 40px 60px; + position: relative; + min-height: 1100px; + .doc-content { + display: inline-block; + vertical-align: top; + box-sizing: border-box; + padding: 20px 6% 0; + width: calc(100% - #{$sideMenuWidth}); + } + } + @media screen and (max-width: $mobileWidth) { + .content-section { + padding-left: 20px; + padding-right: 20px; + .doc-content { + width: 100%; + } + } + .sidemenu { + position: absolute; + z-index: 100; + left: 0; + top: 40px; + } + } +} \ No newline at end of file diff --git a/sop-website/sop-portal/src/pages/home/featureItem.jsx b/sop-website/sop-portal/src/pages/home/featureItem.jsx new file mode 100644 index 00000000..8164ec3c --- /dev/null +++ b/sop-website/sop-portal/src/pages/home/featureItem.jsx @@ -0,0 +1,17 @@ +import React from 'react'; +import { getLink } from '../../../utils'; + +const Item = (props) => { + const { feature } = props; + return ( +
  • + +
    +

    {feature.title}

    +

    {feature.content}

    +
    +
  • + ); +}; + +export default Item; diff --git a/sop-website/sop-portal/src/pages/home/index.jsx b/sop-website/sop-portal/src/pages/home/index.jsx new file mode 100644 index 00000000..f3fa29f9 --- /dev/null +++ b/sop-website/sop-portal/src/pages/home/index.jsx @@ -0,0 +1,98 @@ +import React from 'react'; +import ReactDOM from 'react-dom'; +import { getScrollTop, getLink } from '../../../utils'; +import Header from '../../components/header'; +import Button from '../../components/button'; +import Footer from '../../components/footer'; +import Language from '../../components/language'; +import Item from './featureItem'; +import homeConfig from '../../../site_config/home'; +import './index.scss'; + +class Home extends Language { + + constructor(props) { + super(props); + this.state = { + headerType: 'primary', + }; + } + + componentDidMount() { + window.addEventListener('scroll', () => { + const scrollTop = getScrollTop(); + if (scrollTop > 66) { + this.setState({ + headerType: 'normal', + }); + } else { + this.setState({ + headerType: 'primary', + }); + } + }); + } + + render() { + const language = this.getLanguage(); + const dataSource = homeConfig[language]; + const { headerType } = this.state; + const headerLogo = headerType === 'primary' ? '/img/logo_white.png' : '/img/logo.png'; + return ( +
    +
    +
    +
    +
    +

    {dataSource.brand.brandName}

    +
    +

    {dataSource.brand.briefIntroduction}

    +
    + { + dataSource.brand.buttons.map(b => ) + } +
    +
    +
    +
    +
    +
    +
    +
    +
    + { + dataSource.introductions.map((item, i) => ( +
    +
    +

    {item.title}

    +

    {item.desc}

    +
    + +
    + ))} +
    +
    +

    {dataSource.features.title}

    +
      + { + dataSource.features.list.map((feature, i) => ( + + )) + } +
    +
    +
    +
    + ); + } +} + +document.getElementById('root') && ReactDOM.render(, document.getElementById('root')); + +export default Home; diff --git a/sop-website/sop-portal/src/pages/home/index.scss b/sop-website/sop-portal/src/pages/home/index.scss new file mode 100644 index 00000000..6ecbcda6 --- /dev/null +++ b/sop-website/sop-portal/src/pages/home/index.scss @@ -0,0 +1,472 @@ +@import '../../variables.scss'; +@import '../../reset.scss'; + +$animation1Height: 500px; +$animation2Height: 375px; +$animation3Height: 248px; +$animation4Height: 570px; +$animation5Height: 460px; + +@keyframes fallingStar1 { + 0% { + transform: translate3d(0, -100%, 0); + opacity: 1; + } + + 100% { + transform: translate3d(0, $animation1Height, 0); + opacity: 0; + } +} + +@keyframes fallingStar2 { + 0% { + transform: translate3d(0, -100%, 0); + opacity: 1; + } + + 100% { + transform: translate3d(0, $animation2Height, 0); + opacity: 0; + } +} + +@keyframes fallingStar3 { + 0% { + transform: translate3d(0, -100%, 0); + opacity: 1; + } + + 100% { + transform: translate3d(0, $animation3Height, 0); + opacity: 0; + } +} + +@keyframes fallingStar4 { + 0% { + transform: translate3d(0, -100%, 0); + opacity: 1; + } + + 100% { + transform: translate3d(0, $animation4Height, 0); + opacity: 0; + } +} + +@keyframes fallingStar5 { + 0% { + transform: translate3d(0, -100%, 0); + opacity: 1; + } + + 100% { + transform: translate3d(0, $animation5Height ,0); + opacity: 0; + } +} + +.home-page { + .top-section { + position: relative; + height: 720px; + background-image: linear-gradient(0deg, $startColor 0%, $intermediateColor 51%, $endColor 100%); + .animation { + position: absolute; + top: 0; + border-right: 1px solid rgba(255, 255, 255, 0.3); + transform: scaleX(0.5); + &::before { + content: ''; + height: 16px; + opacity: 1; + border-right: 4px solid #fff; + position: absolute; + left: -2px; + transform: translate3d(0,-100%,0); + } + &1 { + left: 15%; + height: $animation1Height; + &::before { + animation: fallingStar1 1.3s infinite ease-in-out; + } + } + &2 { + left: 35%; + height: $animation2Height; + &::before { + animation: fallingStar2 1.5s infinite ease-in-out; + } + } + &3 { + left: 52%; + height: $animation3Height; + &::before { + animation: fallingStar3 2s infinite ease-in-out; + } + } + &4 { + left: 65%; + height: $animation4Height; + &::before { + animation: fallingStar4 1.5s infinite ease-in-out; + } + } + &5 { + left: 85%; + height: $animation5Height; + &::before { + animation: fallingStar5 1.3s infinite ease-in-out; + } + } + } + .vertical-middle { + position: absolute; + left: 0; + top: 50%; + transform: translateY(-50%); + box-sizing: border-box; + width: 100%; + text-align: center; + padding: 0 20px; + } + .product-name { + position: relative; + display: inline-block; + h2 { + font-family: Avenir-Heavy; + font-size: 46px; + color: #FFF; + text-align: center; + word-break: break-word; + margin: 0; + } + } + .product-desc { + opacity: 0.6; + font-family: Avenir-Medium; + font-size: 24px; + color: #FFF; + text-align: center; + margin: 12px auto 0; + max-width: 730px; + } + .button-area { + text-align: center; + margin-top: 40px; + .button { + margin-right: 20px; + } + .button:last-child { + margin-right: 0; + } + } + } + .introduction-section { + background: #F9FAFA; + .introduction-body { + max-width: $contentWidth; + box-sizing: border-box; + margin: 0 auto; + min-height: 640px; + padding: 0 40px; + position: relative; + display: flex; + flex-wrap: wrap; + align-items: center; + justify-content: space-between; + &::before { + content: ''; + position: absolute; + top: 0; + left: 40px; + height: 165px; + opacity: 0.3; + border-right: 1px solid #666; + } + &::after { + content: ''; + position: absolute; + left: 39px; + top: 165px; + width: 3px; + height: 17px; + background-image: linear-gradient(0deg, $startColor 0%, $intermediateColor 51%, $endColor 100%); + } + .introduction { + display: inline-block; + width: calc(100% - 726px); + min-width: 300px; + max-width: 590px; + h3 { + font-family: Avenir-Heavy; + font-size: 36px; + color: #333; + margin-bottom: 20px; + word-break: break-word; + margin-left: 10px; + margin-top: 40px; + } + p { + opacity: 0.56; + font-family: Avenir-Medium; + font-size: 18px; + color: #999; + margin-left: 20px; + } + } + img { + //width: 562px; + //margin: 0 82px; + margin: 20px 20px; + max-width: 100%; + box-shadow: 0 8px 16px 0 rgba(0, 0, 0, 0.05); + } + @media screen and (max-width: 1106px){ + .introduction { + display: inline-block; + width: 100%; + max-width: 100%; + } + img { + margin: 0; + } + } + } + } + .feature-section { + box-sizing: border-box; + max-width: $contentWidth; + margin: 0 auto; + position: relative; + padding: 80px 40px 40px; + &::before { + content: ''; + position: absolute; + top: 0; + left: 50%; + transform: translateX(-50%); + margin: 0 auto; + height: 83px; + opacity: 0.3; + border-right: 1px solid #666; + } + &::after { + content: ''; + position: absolute; + top: 66px; + left: 50%; + transform: translateX(-50%); + width: 3px; + height: 17px; + background-image: linear-gradient(0deg, $startColor 0%, $intermediateColor 51%, $endColor 100%); + } + h3 { + font-family: Avenir-Heavy; + font-size: 36px; + color: #333; + text-align: center; + margin: 0 0 40px; + } + ul { + list-style: none; + padding: 0; + margin: 0; + li { + vertical-align: top; + display: inline-block; + margin-bottom: 40px; + width: 50%; + img { + vertical-align: top; + width: 60px; + height: 60px; + margin-right: 20px; + } + div { + display: inline-block; + width: 80%; + h4 { + font-family: Avenir-Heavy; + font-size: 20px; + color: #333; + margin-top: 10px; + } + p { + font-family: Avenir-Medium; + font-size: 14px; + line-height: 20px; + color: #999; + } + } + } + @media screen and (max-width: 768px){ + li { + width: 100%; + } + } + } + } + .start-section { + background-image: linear-gradient(0deg, $startColor 0%, $intermediateColor 51%, $endColor 100%); + .start-body { + max-width: $contentWidth; + margin: 0 auto; + box-sizing: border-box; + height: 260px; + padding: 35px 40px; + position: relative; + &::before { + content: ''; + position: absolute; + top: 0; + left: 20px; + height: 100%; + opacity: 0.3; + border-right: 1px solid #fff; + } + &::after { + content: ''; + position: absolute; + left: 19px; + top: 48px; + width: 3px; + height: 17px; + background: #fff; + } + .left-part { + display: inline-block; + width: 50%; + vertical-align: top; + h3 { + font-family: Avenir-Heavy; + font-size: 36px; + color: #FFF; + margin: 0; + } + p { + opacity: 0.8; + font-family: Avenir-Medium; + font-size: 18px; + color: #FFF; + line-height: 24px; + margin: 6px 0 12px; + } + a { + font-family: Avenir-Heavy; + font-size: 14px; + color: $brandColor; + text-align: center; + display: inline-block; + width: 140px; + height: 48px; + line-height: 48px; + background: #FFF; + border-radius: 4px; + } + } + .right-part { + display: inline-block; + width: 50%; + font-size: 0; + margin-top: 15px; + img { + margin-left: 5%; + width: 500px; + } + } + @media screen and (max-width: 1050px){ + & { + height: 474px; + } + .left-part { + width: 100%; + } + .right-part { + width: 100%; + margin-top: 38px; + img { + max-width: 100%; + margin-left: 0; + } + } + } + } + } + .users-section { + box-sizing: border-box; + max-width: $contentWidth; + margin: 0 auto; + padding: 80px 40px 40px; + position: relative; + &::before { + content: ''; + position: absolute; + top: 0; + left: 50%; + transform: translateX(-50%); + margin: 0 auto; + height: 83px; + opacity: 0.3; + border-right: 1px solid #666; + } + &::after { + content: ''; + position: absolute; + top: 66px; + left: 50%; + transform: translateX(-50%); + width: 3px; + height: 17px; + background-image: linear-gradient(0deg, $startColor 0%, $intermediateColor 51%, $endColor 100%); + } + h3 { + font-family: Avenir-Heavy; + font-size: 36px; + color: #333; + text-align: center; + margin-bottom: 40px; + } + p { + font-family: Avenir-Medium; + font-size: 14px; + color: #666; + text-align: center; + margin: 0 0 40px; + a { + color: #1e6bb8; + text-decoration: none; + } + } + .users { + // display: flex; + // flex-wrap: wrap; + // justify-content: space-between; + display: inline-block; + text-align: center; + img { + margin-right: 10px; + width: 140px; + height: 80px; + margin-bottom: 40px; + } + } + } + @media screen and (max-width: $mobileWidth) { + .introduction-section { + padding: 0 20px; + &::before { + left: 20px; + } + &::after { + left: 19px; + } + } + .feature-section, .users-section { + padding-left: 20px; + padding-right: 20px; + } + } +} diff --git a/sop-website/sop-portal/src/reset.scss b/sop-website/sop-portal/src/reset.scss new file mode 100644 index 00000000..f5c3568b --- /dev/null +++ b/sop-website/sop-portal/src/reset.scss @@ -0,0 +1,12 @@ +@import './markdown.scss'; + +* { + padding: 0; + margin: 0; +} +a { + text-decoration: none; +} +h1, h2, h3, h4, h5, h6 { + font-weight: 400; +} diff --git a/sop-website/sop-portal/src/variables.scss b/sop-website/sop-portal/src/variables.scss new file mode 100644 index 00000000..09e19d22 --- /dev/null +++ b/sop-website/sop-portal/src/variables.scss @@ -0,0 +1,24 @@ +// 品牌色 +$brandColor: #2DACEC; +// 渐变起始色 +$startColor: #03DDE4; +// 渐变中间色 +$intermediateColor: #30AFED; +// 渐变结束色 +$endColor: #8755FF; +// 侧边菜单背景色 +$sideMenuBgColor: #f8f8f8; +// 顶部悬浮菜单背景色,用于移动端 +$headerMenuBgColor: #f8f8f8; + + +// 头部高度 +$headerHeight: 66px; +// 侧边栏宽度 +$sideMenuWidth: 295px; +// bar高度 +$barHeight: 200px; +// 切换到移动端显示屏宽界限 +$mobileWidth: 640px; +// 页面主体最大宽度 +$contentWidth: 1280px; \ No newline at end of file diff --git a/sop-website/sop-portal/template.ejs b/sop-website/sop-portal/template.ejs new file mode 100644 index 00000000..9c26eb48 --- /dev/null +++ b/sop-website/sop-portal/template.ejs @@ -0,0 +1,23 @@ + + + + + + + + + + <%= title %> + + + + +
    <%- __html %>
    + + + + + + diff --git a/sop-website/sop-portal/utils/index.js b/sop-website/sop-portal/utils/index.js new file mode 100644 index 00000000..8a964069 --- /dev/null +++ b/sop-website/sop-portal/utils/index.js @@ -0,0 +1,36 @@ +/* eslint-disable import/prefer-default-export */ +export const throttle = (fn, delay) => { + let timer = null; + return function(...args) { + const context = this; + clearTimeout(timer); + timer = setTimeout(() => { + fn.apply(context, args); + }, delay); + }; +}; + +export const getScrollTop = () => { + let scrollTop = 0; + if (document.documentElement && document.documentElement.scrollTop) { + scrollTop = document.documentElement.scrollTop; + } else if (document.body) { + scrollTop = document.body.scrollTop; + } + return scrollTop; +}; + +export const getLink = (link) => { + if (`${link}`.length > 1 && /^\/[^/]/.test(`${link}`)) { + return `${window.rootPath}${link}`; + } + return link; +}; + +export const parseJSONStr = (str) => { + try { + return JSON.parse(str); + } catch (err) { + return str; + } +} diff --git a/sop-website/sop-portal/webpack.config.js b/sop-website/sop-portal/webpack.config.js new file mode 100644 index 00000000..3ac6c989 --- /dev/null +++ b/sop-website/sop-portal/webpack.config.js @@ -0,0 +1,55 @@ +const path = require('path'); +const fs = require('fs'); +const webpack = require('webpack'); +const ExtractTextPlugin = require('extract-text-webpack-plugin'); + +const entry = {}; +const targetPath = path.join(__dirname, './src/pages'); +fs.readdirSync(targetPath).forEach(page => { + if ( + fs.statSync(path.join(targetPath, page)).isDirectory() && + fs.existsSync(path.join(targetPath, page, 'index.jsx')) + ) { + entry[page] = path.join(targetPath, page, 'index.jsx'); + } +}); +module.exports = { + entry, + output: { + path: path.join(__dirname, 'build'), + filename: '[name].js', + }, + externals: { + react: 'React', + 'react-dom': 'ReactDOM', + }, + module: { + loaders: [ + { + test: /\.js|jsx$/, + exclude: [/node_modules/, /build\/lib/, /\.min\.js$/], + use: 'babel-loader', + }, + { + test: /\.(s)?css$/, + use: ExtractTextPlugin.extract({ + fallback: 'style-loader', + use: ['css-loader', 'sass-loader'], + }), + }, + { + test: /\.json?$/, + exclude: /node_modules/, + use: 'json-loader', + }, + ], + }, + resolve: { + extensions: ['.js', '.jsx', '.json'], + }, + plugins: [ + new webpack.NoEmitOnErrorsPlugin(), + new webpack.optimize.OccurrenceOrderPlugin(), + new ExtractTextPlugin('[name].css'), + ], +}; diff --git a/sop-website/sop-portal/zh-cn/blog/blog1.html b/sop-website/sop-portal/zh-cn/blog/blog1.html new file mode 100644 index 00000000..8fb0b850 --- /dev/null +++ b/sop-website/sop-portal/zh-cn/blog/blog1.html @@ -0,0 +1,26 @@ + + + + + + + + + + blog1 + + + + +

    博客1

    +

    支持元数据的解析,---(至少三个-)开头之间的数据按照key:value的形式,最终会被解析到md_json/blog.json中,其中filename__html为保留字段,请勿使用。

    +

    博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充

    +
    + + + + + + diff --git a/sop-website/sop-portal/zh-cn/blog/blog1.json b/sop-website/sop-portal/zh-cn/blog/blog1.json new file mode 100644 index 00000000..2f15a275 --- /dev/null +++ b/sop-website/sop-portal/zh-cn/blog/blog1.json @@ -0,0 +1,9 @@ +{ + "filename": "blog1.md", + "__html": "

    博客1

    \n

    支持元数据的解析,---(至少三个-)开头之间的数据按照key:value的形式,最终会被解析到md_json/blog.json中,其中filename__html为保留字段,请勿使用。

    \n

    博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充博客内容填充,博客内容填充

    \n", + "link": "/zh-cn/blog/blog1.html", + "meta": { + "key1": "hello", + "key2": "world" + } +} \ No newline at end of file diff --git a/sop-website/sop-portal/zh-cn/blog/index.html b/sop-website/sop-portal/zh-cn/blog/index.html new file mode 100644 index 00000000..39ea8695 --- /dev/null +++ b/sop-website/sop-portal/zh-cn/blog/index.html @@ -0,0 +1,23 @@ + + + + + + + + + + 网页标签title + + + + + + + + + + + diff --git a/sop-website/sop-portal/zh-cn/community/index.html b/sop-website/sop-portal/zh-cn/community/index.html new file mode 100644 index 00000000..9b4694c2 --- /dev/null +++ b/sop-website/sop-portal/zh-cn/community/index.html @@ -0,0 +1,23 @@ + + + + + + + + + + 网页标签title + + + + +
    社区

    事件 & 新闻

    这是标题

    这是内容

    May 12nd,2018

    这是标题

    这是内容

    May 12nd,2018

    这是标题

    这是内容

    May 12nd,2018

    这是标题

    这是内容

    May 12nd,2018

    这是标题

    这是内容

    May 12nd,2018

    这是标题

    这是内容

    May 12nd,2018

    这是标题

    这是内容

    May 12nd,2018

    生态系统

    生态系统1

    生态系统1

    生态系统2

    生态系统2

    联系我们

    有问题需要反馈?请通过一下方式联系我们。

    贡献指南

    一些描述

    邮件列表

    这是描述

    报告缺陷

    这是描述

    文档

    这是描述

    Pull Request

    这是描述

    + + + + + + diff --git a/sop-website/sop-portal/zh-cn/docs/demo1.html b/sop-website/sop-portal/zh-cn/docs/demo1.html new file mode 100644 index 00000000..16c64385 --- /dev/null +++ b/sop-website/sop-portal/zh-cn/docs/demo1.html @@ -0,0 +1,426 @@ + + + + + + + + + + demo1 + + + + +
    文档

    脚注的使用

    +

    这是一段示例文字,展示脚注的使用。 +这是脚注1 [1]。点击脚注滚动至对应脚注处。

    +

    下面是一大段文字,填充开始。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。 +这是填充文字。

    +
    +
    +
      +
    1. 这是脚注的说明 ↩︎

      +
    2. +
    +
    +
    + + + + + + diff --git a/sop-website/sop-portal/zh-cn/docs/demo1.json b/sop-website/sop-portal/zh-cn/docs/demo1.json new file mode 100644 index 00000000..bb8f579f --- /dev/null +++ b/sop-website/sop-portal/zh-cn/docs/demo1.json @@ -0,0 +1,6 @@ +{ + "filename": "demo1.md", + "__html": "

    脚注的使用

    \n

    这是一段示例文字,展示脚注的使用。\n这是脚注1 [1]。点击脚注滚动至对应脚注处。

    \n

    下面是一大段文字,填充开始。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。\n这是填充文字。

    \n
    \n
    \n
      \n
    1. 这是脚注的说明 ↩︎

      \n
    2. \n
    \n
    \n", + "link": "/zh-cn/docs/demo1.html", + "meta": {} +} \ No newline at end of file diff --git a/sop-website/sop-portal/zh-cn/docs/demo2.html b/sop-website/sop-portal/zh-cn/docs/demo2.html new file mode 100644 index 00000000..1988be87 --- /dev/null +++ b/sop-website/sop-portal/zh-cn/docs/demo2.html @@ -0,0 +1,36 @@ + + + + + + + + + + demo2 + + + + +
    文档

    文档之间跳转、图片引用的示例

    +

    文档之间的跳转链接按照文件目录之间的相对关系配置即可,推荐在同一语言目录下的不同文件夹间跳转,以缩短配置路径。 +点击跳转: demo1.md

    +

    图片的引用,支持绝对引用和相对引用,如果是相对引用,同文档之间的相互引用一样,是按照文件之间的相对路径进行处理的。比如 +

    +

    数学公式的使用(katex)

    +

    E=mc2E = mc^2

    +

    x=a1n+a2n+a3nx = a_{1}^n + a_{2}^n + a_{3}^n +

    +

    X3\sqrt[3]{X} +

    +

    5x\sqrt{5 - x} +

    +
    + + + + + + diff --git a/sop-website/sop-portal/zh-cn/docs/demo2.json b/sop-website/sop-portal/zh-cn/docs/demo2.json new file mode 100644 index 00000000..beb267d2 --- /dev/null +++ b/sop-website/sop-portal/zh-cn/docs/demo2.json @@ -0,0 +1,6 @@ +{ + "filename": "demo2.md", + "__html": "

    文档之间跳转、图片引用的示例

    \n

    文档之间的跳转链接按照文件目录之间的相对关系配置即可,推荐在同一语言目录下的不同文件夹间跳转,以缩短配置路径。\n点击跳转: demo1.md

    \n

    图片的引用,支持绝对引用和相对引用,如果是相对引用,同文档之间的相互引用一样,是按照文件之间的相对路径进行处理的。比如\n\"\"

    \n

    数学公式的使用(katex)

    \n

    E=mc2E = mc^2E=mc2

    \n

    x=a1n+a2n+a3nx = a_{1}^n + a_{2}^n + a_{3}^n\nx=a1n+a2n+a3n

    \n

    X3\\sqrt[3]{X}\n3X

    \n

    5x\\sqrt{5 - x}\n5x

    \n", + "link": "/zh-cn/docs/demo2.html", + "meta": {} +} \ No newline at end of file diff --git a/sop-website/sop-portal/zh-cn/docs/dir/demo3.html b/sop-website/sop-portal/zh-cn/docs/dir/demo3.html new file mode 100644 index 00000000..27cc4945 --- /dev/null +++ b/sop-website/sop-portal/zh-cn/docs/dir/demo3.html @@ -0,0 +1,29 @@ + + + + + + + + + + demo3 + + + + +
    文档

    跨目录文档之间跳转、图片引用的示例

    +

    这是一段文字,将要跳转到demo1。 +文档之间的跳转链接按照文件目录之间的相对关系配置即可,推荐在同一语言目录下的不同文件夹间跳转,以缩短配置路径。 +点击跳转: demo1.md

    +

    图片的引用,支持绝对引用和相对引用,如果是相对引用,同文档之间的相互引用一样,是按照文件之间的相对路径进行处理的。比如 +

    +
    + + + + + + diff --git a/sop-website/sop-portal/zh-cn/docs/dir/demo3.json b/sop-website/sop-portal/zh-cn/docs/dir/demo3.json new file mode 100644 index 00000000..748f6bd8 --- /dev/null +++ b/sop-website/sop-portal/zh-cn/docs/dir/demo3.json @@ -0,0 +1,6 @@ +{ + "filename": "demo3.md", + "__html": "

    跨目录文档之间跳转、图片引用的示例

    \n

    这是一段文字,将要跳转到demo1。\n文档之间的跳转链接按照文件目录之间的相对关系配置即可,推荐在同一语言目录下的不同文件夹间跳转,以缩短配置路径。\n点击跳转: demo1.md

    \n

    图片的引用,支持绝对引用和相对引用,如果是相对引用,同文档之间的相互引用一样,是按照文件之间的相对路径进行处理的。比如\n\"\"

    \n", + "link": "/zh-cn/docs/dir/demo3.html", + "meta": {} +} \ No newline at end of file diff --git a/sop-website/sop-portal/zh-cn/index.html b/sop-website/sop-portal/zh-cn/index.html new file mode 100644 index 00000000..83c429cf --- /dev/null +++ b/sop-website/sop-portal/zh-cn/index.html @@ -0,0 +1,23 @@ + + + + + + + + + + XX开放平台 + + + + +

    开放平台

    XX开放平台,一句话介绍。。。

    介绍部分1

    介绍文字。。。

    介绍部分2

    介绍文字。。。

    特性一览

    • 特性1

      描述文字。。

    • 特性2

      描述文字。。

    + + + + + + diff --git a/sop-website/.gitignore b/sop-website/sop-website-server/.gitignore similarity index 100% rename from sop-website/.gitignore rename to sop-website/sop-website-server/.gitignore diff --git a/sop-website/sop-website-server/pom.xml b/sop-website/sop-website-server/pom.xml new file mode 100644 index 00000000..ad39662f --- /dev/null +++ b/sop-website/sop-website-server/pom.xml @@ -0,0 +1,98 @@ + + + + + com.gitee.sop + sop-parent + 4.1.0-SNAPSHOT + ../pom.xml + + + 4.0.0 + sop-website-server + + + 1.8 + + + + + + com.gitee.sop + sop-bridge-nacos + + 4.1.0-SNAPSHOT + + + + net.oschina.durcframework + fastmybatis-spring-boot-starter + + + + org.springframework.boot + spring-boot-starter-web + + + + org.apache.commons + commons-lang3 + + + com.squareup.okhttp3 + okhttp + + + + com.alibaba + fastjson + + + org.apache.httpcomponents + httpclient + + + + javax.validation + validation-api + + + org.hibernate.validator + hibernate-validator + + + + mysql + mysql-connector-java + + + + org.projectlombok + lombok + 1.18.4 + provided + + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + diff --git a/sop-website/readme.md b/sop-website/sop-website-server/readme.md similarity index 100% rename from sop-website/readme.md rename to sop-website/sop-website-server/readme.md diff --git a/sop-website/src/main/java/com/gitee/sop/websiteserver/WebsiteServerApplication.java b/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/WebsiteServerApplication.java similarity index 100% rename from sop-website/src/main/java/com/gitee/sop/websiteserver/WebsiteServerApplication.java rename to sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/WebsiteServerApplication.java diff --git a/sop-website/src/main/java/com/gitee/sop/websiteserver/bean/BizCode.java b/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/bean/BizCode.java similarity index 100% rename from sop-website/src/main/java/com/gitee/sop/websiteserver/bean/BizCode.java rename to sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/bean/BizCode.java diff --git a/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/bean/ChannelMsg.java b/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/bean/ChannelMsg.java new file mode 100644 index 00000000..32e598a9 --- /dev/null +++ b/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/bean/ChannelMsg.java @@ -0,0 +1,30 @@ +package com.gitee.sop.websiteserver.bean; + +import lombok.Data; +import org.apache.commons.lang3.time.DateFormatUtils; + +import java.util.Date; + +/** + * @author tanghc + */ +@Data +public class ChannelMsg { + + private static final String TIME_PATTERN = "yyyy-MM-dd HH:mm:ss:SSS"; + + public ChannelMsg(ChannelOperation channelOperation, Object data) { + this.operation = channelOperation.getOperation(); + this.data = data; + this.timestamp = DateFormatUtils.format(new Date(), TIME_PATTERN); + } + + private String operation; + private Object data; + + /** + * 加个时间戳,格式yyyy-MM-dd HH:mm:ss:SSS,确保每次推送内容都不一样 + * nacos监听基于MD5值,如果每次推送的内容一样,则监听不会触发,因此必须确保每次推送的MD5不一样 + */ + private String timestamp; +} diff --git a/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/bean/ChannelOperation.java b/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/bean/ChannelOperation.java new file mode 100644 index 00000000..90d54c50 --- /dev/null +++ b/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/bean/ChannelOperation.java @@ -0,0 +1,64 @@ +package com.gitee.sop.websiteserver.bean; + +/** + * @author tanghc + */ +public enum ChannelOperation { + /** + * 限流推送路由配置-修改 + */ + LIMIT_CONFIG_UPDATE("update"), + + /** + * 路由信息更新 + */ + ROUTE_CONFIG_UPDATE("update"), + + /** + * isv信息修改 + */ + ISV_INFO_UPDATE("update"), + + /** + * 黑名单消息类型:添加 + */ + BLACKLIST_ADD("add"), + /** + * 黑名单消息类型:删除 + */ + BLACKLIST_DELETE("delete"), + + /** + * 路由权限配置更新 + */ + ROUTE_PERMISSION_UPDATE("update"), + /** + * 路由权限加载 + */ + ROUTE_PERMISSION_RELOAD("reload"), + + /** + * 灰度发布设置 + */ + GRAY_USER_KEY_SET("set"), + /** + * 灰度发布-开启 + */ + GRAY_USER_KEY_OPEN("open"), + /** + * 灰度发布-关闭 + */ + GRAY_USER_KEY_CLOSE("close"), + + ; + + private String operation; + + ChannelOperation(String operation) { + this.operation = operation; + } + + public String getOperation() { + return operation; + } +} diff --git a/sop-website/src/main/java/com/gitee/sop/websiteserver/bean/DocInfo.java b/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/bean/DocInfo.java similarity index 100% rename from sop-website/src/main/java/com/gitee/sop/websiteserver/bean/DocInfo.java rename to sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/bean/DocInfo.java diff --git a/sop-website/src/main/java/com/gitee/sop/websiteserver/bean/DocItem.java b/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/bean/DocItem.java similarity index 98% rename from sop-website/src/main/java/com/gitee/sop/websiteserver/bean/DocItem.java rename to sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/bean/DocItem.java index 929d20ad..a30ea4cb 100644 --- a/sop-website/src/main/java/com/gitee/sop/websiteserver/bean/DocItem.java +++ b/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/bean/DocItem.java @@ -10,6 +10,7 @@ import java.util.List; */ @Data public class DocItem { + private String id; private String module; private String name; private String version; diff --git a/sop-website/src/main/java/com/gitee/sop/websiteserver/bean/DocModule.java b/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/bean/DocModule.java similarity index 100% rename from sop-website/src/main/java/com/gitee/sop/websiteserver/bean/DocModule.java rename to sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/bean/DocModule.java diff --git a/sop-website/src/main/java/com/gitee/sop/websiteserver/bean/DocParameter.java b/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/bean/DocParameter.java similarity index 81% rename from sop-website/src/main/java/com/gitee/sop/websiteserver/bean/DocParameter.java rename to sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/bean/DocParameter.java index 658c36c8..7efe5de8 100644 --- a/sop-website/src/main/java/com/gitee/sop/websiteserver/bean/DocParameter.java +++ b/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/bean/DocParameter.java @@ -5,6 +5,7 @@ import lombok.Data; import org.apache.commons.lang.StringUtils; import java.util.List; +import java.util.concurrent.atomic.AtomicInteger; /** * 参数 类型 是否必填 最大长度 描述 示例值 @@ -12,6 +13,9 @@ import java.util.List; */ @Data public class DocParameter { + private static final AtomicInteger gen = new AtomicInteger(); + + private Integer id = gen.incrementAndGet(); private String module; private String name; private String type; diff --git a/sop-website/src/main/java/com/gitee/sop/websiteserver/bean/DocParserContext.java b/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/bean/DocParserContext.java similarity index 100% rename from sop-website/src/main/java/com/gitee/sop/websiteserver/bean/DocParserContext.java rename to sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/bean/DocParserContext.java diff --git a/sop-website/src/main/java/com/gitee/sop/websiteserver/bean/EurekaApplication.java b/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/bean/EurekaApplication.java similarity index 100% rename from sop-website/src/main/java/com/gitee/sop/websiteserver/bean/EurekaApplication.java rename to sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/bean/EurekaApplication.java diff --git a/sop-website/src/main/java/com/gitee/sop/websiteserver/bean/EurekaApplications.java b/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/bean/EurekaApplications.java similarity index 100% rename from sop-website/src/main/java/com/gitee/sop/websiteserver/bean/EurekaApplications.java rename to sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/bean/EurekaApplications.java diff --git a/sop-website/src/main/java/com/gitee/sop/websiteserver/bean/EurekaApps.java b/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/bean/EurekaApps.java similarity index 100% rename from sop-website/src/main/java/com/gitee/sop/websiteserver/bean/EurekaApps.java rename to sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/bean/EurekaApps.java diff --git a/sop-website/src/main/java/com/gitee/sop/websiteserver/bean/EurekaInstance.java b/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/bean/EurekaInstance.java similarity index 100% rename from sop-website/src/main/java/com/gitee/sop/websiteserver/bean/EurekaInstance.java rename to sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/bean/EurekaInstance.java diff --git a/sop-website/src/main/java/com/gitee/sop/websiteserver/bean/EurekaUri.java b/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/bean/EurekaUri.java similarity index 100% rename from sop-website/src/main/java/com/gitee/sop/websiteserver/bean/EurekaUri.java rename to sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/bean/EurekaUri.java diff --git a/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/bean/GatewayPushDTO.java b/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/bean/GatewayPushDTO.java new file mode 100644 index 00000000..1582caa1 --- /dev/null +++ b/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/bean/GatewayPushDTO.java @@ -0,0 +1,22 @@ +package com.gitee.sop.websiteserver.bean; + +import lombok.Data; + +/** + * @author tanghc + */ +@Data +public class GatewayPushDTO { + private String dataId; + private String groupId; + private ChannelMsg channelMsg; + + public GatewayPushDTO() { + } + + public GatewayPushDTO(String dataId, String groupId, ChannelMsg channelMsg) { + this.dataId = dataId; + this.groupId = groupId; + this.channelMsg = channelMsg; + } +} diff --git a/sop-website/src/main/java/com/gitee/sop/websiteserver/bean/HttpTool.java b/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/bean/HttpTool.java similarity index 100% rename from sop-website/src/main/java/com/gitee/sop/websiteserver/bean/HttpTool.java rename to sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/bean/HttpTool.java diff --git a/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/bean/IdGen.java b/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/bean/IdGen.java new file mode 100644 index 00000000..b0d88f26 --- /dev/null +++ b/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/bean/IdGen.java @@ -0,0 +1,167 @@ +package com.gitee.sop.websiteserver.bean; + +import java.util.UUID; + +public class IdGen { + private static long workId = 0; + private static SnowflakeIdWorker worker = new SnowflakeIdWorker(workId++, 0); + + /** + * 生成唯一id + * @return + */ + public static String nextId() { + return String.valueOf(worker.nextId()); + } + + public static String uuid() { + return UUID.randomUUID().toString().replace("-", ""); + } + + /** + * Twitter_Snowflake
    + * SnowFlake的结构如下(每部分用-分开):
    + * 0 - 0000000000 0000000000 0000000000 0000000000 0 - 00000 - 00000 - 000000000000
    + * 1位标识,由于long基本类型在Java中是带符号的,最高位是符号位,正数是0,负数是1,所以id一般是正数,最高位是0
    + * 41位时间截(毫秒级),注意,41位时间截不是存储当前时间的时间截,而是存储时间截的差值(当前时间截 - 开始时间截) + * 得到的值),这里的的开始时间截,一般是我们的id生成器开始使用的时间,由我们程序来指定的(如下下面程序IdWorker类的startTime属性)。41位的时间截,可以使用69年,年T = (1L << 41) / (1000L * 60 * 60 * 24 * 365) = 69
    + * 10位的数据机器位,可以部署在1024个节点,包括5位datacenterId和5位workerId
    + * 12位序列,毫秒内的计数,12位的计数顺序号支持每个节点每毫秒(同一机器,同一时间截)产生4096个ID序号
    + * 加起来刚好64位,为一个Long型。
    + * SnowFlake的优点是,整体上按照时间自增排序,并且整个分布式系统内不会产生ID碰撞(由数据中心ID和机器ID作区分),并且效率较高,经测试,SnowFlake每秒能够产生26万ID左右。 + */ + public static class SnowflakeIdWorker { + + // ==============================Fields=========================================== + /** 开始时间截 (2015-01-01) */ + private final long twepoch = 1420041100000L; + + /** 机器id所占的位数 */ + private final long workerIdBits = 5L; + + /** 数据标识id所占的位数 */ + private final long datacenterIdBits = 5L; + + /** 支持的最大机器id,结果是31 (这个移位算法可以很快的计算出几位二进制数所能表示的最大十进制数) */ + private final long maxWorkerId = -1L ^ (-1L << workerIdBits); + + /** 支持的最大数据标识id,结果是31 */ + private final long maxDatacenterId = -1L ^ (-1L << datacenterIdBits); + + /** 序列在id中占的位数 */ + private final long sequenceBits = 12L; + + /** 机器ID向左移12位 */ + private final long workerIdShift = sequenceBits; + + /** 数据标识id向左移17位(12+5) */ + private final long datacenterIdShift = sequenceBits + workerIdBits; + + /** 时间截向左移22位(5+5+12) */ + private final long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits; + + /** 生成序列的掩码,这里为4095 (0b111111111111=0xfff=4095) */ + private final long sequenceMask = -1L ^ (-1L << sequenceBits); + + /** 工作机器ID(0~31) */ + private long workerId; + + /** 数据中心ID(0~31) */ + private long datacenterId; + + /** 毫秒内序列(0~4095) */ + private long sequence = 0L; + + /** 上次生成ID的时间截 */ + private long lastTimestamp = -1L; + + //==============================Constructors===================================== + /** + * 构造函数 + * @param workerId 工作ID (0~31) + * @param datacenterId 数据中心ID (0~31) + */ + public SnowflakeIdWorker(long workerId, long datacenterId) { + if (workerId > maxWorkerId || workerId < 0) { + throw new IllegalArgumentException(String.format("worker Id can't be greater than %d or less than 0", maxWorkerId)); + } + if (datacenterId > maxDatacenterId || datacenterId < 0) { + throw new IllegalArgumentException(String.format("datacenter Id can't be greater than %d or less than 0", maxDatacenterId)); + } + this.workerId = workerId; + this.datacenterId = datacenterId; + } + + // ==============================Methods========================================== + /** + * 获得下一个ID (该方法是线程安全的) + * @return SnowflakeId + */ + public synchronized long nextId() { + long timestamp = timeGen(); + + //如果当前时间小于上一次ID生成的时间戳,说明系统时钟回退过这个时候应当抛出异常 + if (timestamp < lastTimestamp) { + throw new RuntimeException( + String.format("Clock moved backwards. Refusing to generate id for %d milliseconds", lastTimestamp - timestamp)); + } + + //如果是同一时间生成的,则进行毫秒内序列 + if (lastTimestamp == timestamp) { + sequence = (sequence + 1) & sequenceMask; + //毫秒内序列溢出 + if (sequence == 0) { + //阻塞到下一个毫秒,获得新的时间戳 + timestamp = tilNextMillis(lastTimestamp); + } + } + //时间戳改变,毫秒内序列重置 + else { + sequence = 0L; + } + + //上次生成ID的时间截 + lastTimestamp = timestamp; + + //移位并通过或运算拼到一起组成64位的ID + return ((timestamp - twepoch) << timestampLeftShift) + | (datacenterId << datacenterIdShift) + | (workerId << workerIdShift) + | sequence; + } + + /** + * 阻塞到下一个毫秒,直到获得新的时间戳 + * @param lastTimestamp 上次生成ID的时间截 + * @return 当前时间戳 + */ + protected long tilNextMillis(long lastTimestamp) { + long timestamp = timeGen(); + while (timestamp <= lastTimestamp) { + timestamp = timeGen(); + } + return timestamp; + } + + /** + * 返回以毫秒为单位的当前时间 + * @return 当前时间(毫秒) + */ + protected long timeGen() { + return System.currentTimeMillis(); + } + + } + /*//==============================Test============================================= + */ + /** 测试 + public static void main(String[] args) { + SnowflakeIdWorker idWorker = new SnowflakeIdWorker(0, 0); + for (int i = 0; i < 100; i++) { + long id = idWorker.nextId(); + System.out.println("id:" + id); + System.out.println("toBinaryString:" + Long.toBinaryString(id)); + } + } + */ +} diff --git a/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/bean/IsvDetailDTO.java b/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/bean/IsvDetailDTO.java new file mode 100644 index 00000000..f1cae6b9 --- /dev/null +++ b/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/bean/IsvDetailDTO.java @@ -0,0 +1,27 @@ +package com.gitee.sop.websiteserver.bean; + +import lombok.Data; + +/** + * @author tanghc + */ +@Data +public class IsvDetailDTO { + + /** appKey, 数据库字段:app_key */ + private String appKey; + + /** 0启用,1禁用, 数据库字段:status */ + private Byte status; + + /** secret, 数据库字段:secret */ + private String secret; + + /** 开发者生成的公钥, 数据库字段:public_key_isv */ + private String publicKeyIsv; + + /** 平台生成的私钥, 数据库字段:private_key_platform */ + private String privateKeyPlatform; + + private Byte signType; +} diff --git a/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/bean/IsvKeysGenVO.java b/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/bean/IsvKeysGenVO.java new file mode 100644 index 00000000..2334f16b --- /dev/null +++ b/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/bean/IsvKeysGenVO.java @@ -0,0 +1,25 @@ +package com.gitee.sop.websiteserver.bean; + +import lombok.Data; + +/** + * @author tanghc + */ +@Data +public class IsvKeysGenVO { + /** secret, 数据库字段:secret */ + private String secret; + + /** 开发者生成的公钥, 数据库字段:public_key_isv */ + private String publicKeyIsv; + + /** 开发者生成的私钥(交给开发者), 数据库字段:private_key_isv */ + private String privateKeyIsv; + + /** 平台生成的公钥(交给开发者), 数据库字段:public_key_platform */ + private String publicKeyPlatform; + + /** 平台生成的私钥, 数据库字段:private_key_platform */ + private String privateKeyPlatform; + +} diff --git a/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/bean/LoginUser.java b/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/bean/LoginUser.java new file mode 100644 index 00000000..ed2642ed --- /dev/null +++ b/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/bean/LoginUser.java @@ -0,0 +1,16 @@ +package com.gitee.sop.websiteserver.bean; + +import lombok.Data; + +/** + * @author tanghc + */ +@Data +public class LoginUser { + private Long userId; + /** appKey, 数据库字段:app_key */ + private String appKey; + + /** 开发者生成的私钥(交给开发者), 数据库字段:private_key_isv */ + private String privateKeyIsv; +} diff --git a/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/bean/NoLogin.java b/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/bean/NoLogin.java new file mode 100644 index 00000000..55c44d35 --- /dev/null +++ b/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/bean/NoLogin.java @@ -0,0 +1,16 @@ +package com.gitee.sop.websiteserver.bean; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * @author tanghc + */ +@Target({ElementType.TYPE, ElementType.METHOD}) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface NoLogin { +} diff --git a/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/bean/RSATool.java b/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/bean/RSATool.java new file mode 100644 index 00000000..d1ce8e95 --- /dev/null +++ b/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/bean/RSATool.java @@ -0,0 +1,417 @@ +package com.gitee.sop.websiteserver.bean; + +import org.apache.commons.codec.binary.Base64; +import org.bouncycastle.asn1.ASN1Encodable; +import org.bouncycastle.asn1.ASN1Primitive; +import org.bouncycastle.asn1.pkcs.PrivateKeyInfo; + +import javax.crypto.Cipher; +import java.security.KeyFactory; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.interfaces.RSAPrivateKey; +import java.security.interfaces.RSAPublicKey; +import java.security.spec.PKCS8EncodedKeySpec; +import java.security.spec.X509EncodedKeySpec; + +/** + * RSA加解密工具
    + * + * @author tanghc + */ +public class RSATool { + public static String RSA_ALGORITHM = "RSA"; + + private KeyFormat keyFormat; + private KeyLength keyLength; + + public RSATool(KeyFormat keyFormat, KeyLength keyLength) { + this.keyFormat = keyFormat; + this.keyLength = keyLength; + } + + /** + * 创建公钥私钥 + * + * @return 返回公私钥对 + * @throws Exception + */ + public KeyStore createKeys() throws Exception { + KeyPairGenerator keyPairGeno = KeyPairGenerator.getInstance(RSA_ALGORITHM); + keyPairGeno.initialize(keyLength.getLength()); + KeyPair keyPair = keyPairGeno.generateKeyPair(); + + RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic(); + RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate(); + + KeyStore keyStore = new KeyStore(); + if (this.keyFormat == KeyFormat.PKCS1) { + keyStore.setPublicKey(Base64.encodeBase64String(publicKey.getEncoded())); + keyStore.setPrivateKey(convertPkcs8ToPkcs1(privateKey.getEncoded())); + } else { + keyStore.setPublicKey(Base64.encodeBase64String(publicKey.getEncoded())); + keyStore.setPrivateKey(Base64.encodeBase64String(privateKey.getEncoded())); + } + return keyStore; + } + + /** + * 获取公钥对象 + * + * @param pubKeyData 公钥 + * @return 返回公钥对象 + * @throws Exception + */ + public RSAPublicKey getPublicKey(byte[] pubKeyData) throws Exception { + X509EncodedKeySpec keySpec = new X509EncodedKeySpec(pubKeyData); + KeyFactory keyFactory = KeyFactory.getInstance(RSA_ALGORITHM); + return (RSAPublicKey) keyFactory.generatePublic(keySpec); + } + + /** + * 获取公钥对象 + * + * @param pubKey 公钥 + * @return 返回私钥对象 + * @throws Exception + */ + public RSAPublicKey getPublicKey(String pubKey) throws Exception { + return getPublicKey(Base64.decodeBase64(pubKey)); + + } + + /** + * 获取私钥对象 + * + * @param priKey 私钥 + * @return 私钥对象 + * @throws Exception + */ + public RSAPrivateKey getPrivateKey(String priKey) throws Exception { + return getPrivateKey(Base64.decodeBase64(priKey)); + } + + /** + * 通过私钥byte[]将公钥还原,适用于RSA算法 + * + * @param keyBytes + * @return 返回私钥 + * @throws Exception + */ + public RSAPrivateKey getPrivateKey(byte[] keyBytes) throws Exception { + PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes); + KeyFactory keyFactory = KeyFactory.getInstance(RSA_ALGORITHM); + return (RSAPrivateKey) keyFactory.generatePrivate(keySpec); + + } + + /** + * 公钥加密 + * + * @param data 待加密内容 + * @param publicKey 公钥 + * @return 返回密文 + * @throws Exception + */ + public String encryptByPublicKey(String data, RSAPublicKey publicKey) throws Exception { + Cipher cipher = Cipher.getInstance(keyFormat.getCipherAlgorithm()); + cipher.init(Cipher.ENCRYPT_MODE, publicKey); + // 模长 + int key_len = publicKey.getModulus().bitLength() / 8; + // 加密数据长度 <= 模长-11 + String[] datas = splitString(data, key_len - 11); + String mi = ""; + // 如果明文长度大于模长-11则要分组加密 + for (String s : datas) { + mi += bcd2Str(cipher.doFinal(s.getBytes())); + } + return mi; + } + + public String encryptByPrivateKey(String data, String privateKey) throws Exception { + return encryptByPrivateKey(data, getPrivateKey(privateKey)); + } + + /** + * 私钥加密 + * + * @param data 待加密数据 + * @param privateKey 私钥 + * @return 返回密文 + * @throws Exception + */ + public String encryptByPrivateKey(String data, RSAPrivateKey privateKey) throws Exception { + Cipher cipher = Cipher.getInstance(keyFormat.getCipherAlgorithm()); + cipher.init(Cipher.ENCRYPT_MODE, privateKey); + // 模长 + int key_len = privateKey.getModulus().bitLength() / 8; + // 加密数据长度 <= 模长-11 + String[] datas = splitString(data, key_len - 11); + String mi = ""; + // 如果明文长度大于模长-11则要分组加密 + for (String s : datas) { + mi += bcd2Str(cipher.doFinal(s.getBytes())); + } + return mi; + } + + public String decryptByPrivateKey(String data, String privateKey) throws Exception { + return decryptByPrivateKey(data, getPrivateKey(privateKey)); + } + + /** + * 私钥解密 + * + * @param data 待解密内容 + * @param privateKey 私钥 + * @return 返回明文 + * @throws Exception + */ + public String decryptByPrivateKey(String data, RSAPrivateKey privateKey) throws Exception { + Cipher cipher = Cipher.getInstance(keyFormat.getCipherAlgorithm()); + cipher.init(Cipher.DECRYPT_MODE, privateKey); + // 模长 + int key_len = privateKey.getModulus().bitLength() / 8; + byte[] bytes = data.getBytes(); + byte[] bcd = ASCII_To_BCD(bytes, bytes.length); + // 如果密文长度大于模长则要分组解密 + String ming = ""; + byte[][] arrays = splitArray(bcd, key_len); + for (byte[] arr : arrays) { + ming += new String(cipher.doFinal(arr)); + } + return ming; + } + + /** + * 公钥解密 + * + * @param data 待解密内容 + * @param rsaPublicKey 公钥 + * @return 返回明文 + * @throws Exception + */ + public String decryptByPublicKey(String data, RSAPublicKey rsaPublicKey) throws Exception { + Cipher cipher = Cipher.getInstance(keyFormat.getCipherAlgorithm()); + cipher.init(Cipher.DECRYPT_MODE, rsaPublicKey); + // 模长 + int key_len = rsaPublicKey.getModulus().bitLength() / 8; + byte[] bytes = data.getBytes(); + byte[] bcd = ASCII_To_BCD(bytes, bytes.length); + // 如果密文长度大于模长则要分组解密 + String ming = ""; + byte[][] arrays = splitArray(bcd, key_len); + for (byte[] arr : arrays) { + ming += new String(cipher.doFinal(arr)); + } + return ming; + } + + public static String convertPkcs8ToPkcs1(byte[] privateKeyData) throws Exception{ + PrivateKeyInfo pkInfo = PrivateKeyInfo.getInstance(privateKeyData); + ASN1Encodable encodable = pkInfo.parsePrivateKey(); + ASN1Primitive primitive = encodable.toASN1Primitive(); + byte[] privateKeyPKCS1 = primitive.getEncoded(); + return Base64.encodeBase64String(privateKeyPKCS1); + } + + + /** + * ASCII码转BCD码 + */ + public static byte[] ASCII_To_BCD(byte[] ascii, int asc_len) { + byte[] bcd = new byte[asc_len / 2]; + int j = 0; + for (int i = 0; i < (asc_len + 1) / 2; i++) { + bcd[i] = asc_to_bcd(ascii[j++]); + bcd[i] = (byte) (((j >= asc_len) ? 0x00 : asc_to_bcd(ascii[j++]) & 0xff) + (bcd[i] << 4)); + } + return bcd; + } + + public static byte asc_to_bcd(byte asc) { + byte bcd; + + if ((asc >= '0') && (asc <= '9')) { + bcd = (byte) (asc - '0'); + } else if ((asc >= 'A') && (asc <= 'F')) { + bcd = (byte) (asc - 'A' + 10); + } else if ((asc >= 'a') && (asc <= 'f')) { + bcd = (byte) (asc - 'a' + 10); + } else { + bcd = (byte) (asc - 48); + } + return bcd; + } + + /** + * BCD转字符串 + */ + public String bcd2Str(byte[] bytes) { + char[] temp = new char[bytes.length * 2]; + char val; + + for (int i = 0; i < bytes.length; i++) { + val = (char) (((bytes[i] & 0xf0) >> 4) & 0x0f); + temp[i * 2] = (char) (val > 9 ? val + 'A' - 10 : val + '0'); + + val = (char) (bytes[i] & 0x0f); + temp[i * 2 + 1] = (char) (val > 9 ? val + 'A' - 10 : val + '0'); + } + return new String(temp); + } + + /** + * 拆分字符串 + */ + public String[] splitString(String string, int len) { + int x = string.length() / len; + int y = string.length() % len; + int z = 0; + if (y != 0) { + z = 1; + } + String[] strings = new String[x + z]; + String str = ""; + for (int i = 0; i < x + z; i++) { + if (i == x + z - 1 && y != 0) { + str = string.substring(i * len, i * len + y); + } else { + str = string.substring(i * len, i * len + len); + } + strings[i] = str; + } + return strings; + } + + /** + * 拆分数组 + */ + public byte[][] splitArray(byte[] data, int len) { + int x = data.length / len; + int y = data.length % len; + int z = 0; + if (y != 0) { + z = 1; + } + byte[][] arrays = new byte[x + z][]; + byte[] arr; + for (int i = 0; i < x + z; i++) { + arr = new byte[len]; + if (i == x + z - 1 && y != 0) { + System.arraycopy(data, i * len, arr, 0, y); + } else { + System.arraycopy(data, i * len, arr, 0, len); + } + arrays[i] = arr; + } + return arrays; + } + + public enum KeyLength { + /** + * 秘钥长度:1024 + */ + LENGTH_1024(1024), + /** + * 秘钥长度:2048 + */ + LENGTH_2048(2048); + private int length; + + KeyLength(int length) { + this.length = length; + } + + public int getLength() { + return length; + } + } + + public static class KeyStore { + private String publicKey; + private String privateKey; + + public String getPublicKey() { + return publicKey; + } + + public void setPublicKey(String publicKey) { + this.publicKey = publicKey; + } + + public String getPrivateKey() { + return privateKey; + } + + public void setPrivateKey(String privateKey) { + this.privateKey = privateKey; + } + } + + public enum KeyFormat { + PKCS1("RSA/ECB/PKCS1Padding"), PKCS8("RSA"); + private String cipherAlgorithm; + + KeyFormat(String cipherAlgorithm) { + this.cipherAlgorithm = cipherAlgorithm; + } + + public String getCipherAlgorithm() { + return cipherAlgorithm; + } + } + + public KeyFormat getKeyFormat() { + return keyFormat; + } + + public KeyLength getKeyLength() { + return keyLength; + } + + + + /* ------------ Test ------------ + public static void main(String[] args) throws Exception { + + RSATool rsa_pkcs8_1024 = new RSATool(KeyFormat.PKCS8, KeyLength.LENGTH_1024); + RSATool rsa_pkcs8_2048 = new RSATool(KeyFormat.PKCS8, KeyLength.LENGTH_2048); + + doTest(rsa_pkcs8_1024); + doTest(rsa_pkcs8_2048); + + // PKCS在Java环境无法测试,可以生成一对,到非java平台测试,如C# + } + + private static void doTest(RSATool rsaTool) throws Exception { + System.out.println("秘钥格式:" + rsaTool.keyFormat.name() + ", 秘钥长度:" + rsaTool.keyLength.getLength()); + KeyStore keys = rsaTool.createKeys(); + String pubKey = keys.getPublicKey(); + System.out.println("pubKey:"); + System.out.println(pubKey); + String priKey = keys.getPrivateKey(); + System.out.println("priKey:"); + System.out.println(priKey); + System.out.println("--------"); + + String ming = "你好,abc123~!@="; + // 用公钥加密 + String mi = rsaTool.encryptByPublicKey(ming, rsaTool.getPublicKey(pubKey)); + System.out.println("mi : " + mi); + // 用私钥解密 + String ming2 = rsaTool.decryptByPrivateKey(mi, rsaTool.getPrivateKey(priKey)); + System.out.println("ming : " + ming2 + ", 结果:" + ming2.equals(ming)); + + // 用私钥加密 + String mi2 = rsaTool.encryptByPrivateKey(ming, rsaTool.getPrivateKey(priKey)); + + System.out.println("mi2 : " + mi2); + // 用公钥解密 + String ming3 = rsaTool.decryptByPublicKey(mi2, rsaTool.getPublicKey(pubKey)); + System.out.println("ming3 : " + ming3 + ", 结果:" + ming3.equals(ming)); + System.out.println("---------------------"); + } + */ + +} \ No newline at end of file diff --git a/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/bean/Result.java b/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/bean/Result.java new file mode 100644 index 00000000..cdf5bca8 --- /dev/null +++ b/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/bean/Result.java @@ -0,0 +1,65 @@ +package com.gitee.sop.websiteserver.bean; + +public class Result { + + private static final String CODE_SUCCESS = "0"; + private static final String CODE_ERROR = "100"; + + private static Result ok = new Result(); + static { + ok.setCode(CODE_SUCCESS); + } + + private String code; + private T data; + private String msg; + + public static Result ok() { + return ok; + } + + public static Result ok(T data) { + Result result = new Result(); + result.setCode(CODE_SUCCESS); + result.setData(data); + return result; + } + + public static Result err(String msg) { + Result result = new Result(); + result.setCode(CODE_ERROR); + result.setMsg(msg); + return result; + } + + public static Result err(String code, String msg) { + Result result = new Result(); + result.setCode(code); + result.setMsg(msg); + return result; + } + + public String getCode() { + return code; + } + + public void setCode(String code) { + this.code = code; + } + + public T getData() { + return data; + } + + public void setData(T data) { + this.data = data; + } + + public String getMsg() { + return msg; + } + + public void setMsg(String msg) { + this.msg = msg; + } +} \ No newline at end of file diff --git a/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/bean/SnowflakeIdWorker.java b/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/bean/SnowflakeIdWorker.java new file mode 100644 index 00000000..54d96053 --- /dev/null +++ b/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/bean/SnowflakeIdWorker.java @@ -0,0 +1,170 @@ +package com.gitee.sop.websiteserver.bean; + +/** + * Twitter_Snowflake
    + * SnowFlake的结构如下(每部分用-分开):
    + * 0 - 0000000000 0000000000 0000000000 0000000000 0 - 00000 - 00000 - 000000000000
    + * 1位标识,由于long基本类型在Java中是带符号的,最高位是符号位,正数是0,负数是1,所以id一般是正数,最高位是0
    + * 41位时间截(毫秒级),注意,41位时间截不是存储当前时间的时间截,而是存储时间截的差值(当前时间截 - 开始时间截) + * 得到的值),这里的的开始时间截,一般是我们的id生成器开始使用的时间,由我们程序来指定的(如下下面程序IdWorker类的startTime属性)。41位的时间截,可以使用69年,年T = (1L << 41) / (1000L * 60 * 60 * 24 * 365) = 69
    + * 10位的数据机器位,可以部署在1024个节点,包括5位datacenterId和5位workerId
    + * 12位序列,毫秒内的计数,12位的计数顺序号支持每个节点每毫秒(同一机器,同一时间截)产生4096个ID序号
    + * 加起来刚好64位,为一个Long型。
    + * SnowFlake的优点是,整体上按照时间自增排序,并且整个分布式系统内不会产生ID碰撞(由数据中心ID和机器ID作区分),并且效率较高,经测试,SnowFlake每秒能够产生26万ID左右。 + */ +public class SnowflakeIdWorker { + + // ==============================Fields=========================================== + /** + * 开始时间截 (2020-3-3) + */ + private final long twepoch = 1583164800000L; + + /** + * 机器id所占的位数 + */ + private final long workerIdBits = 5L; + + /** + * 数据标识id所占的位数 + */ + private final long datacenterIdBits = 5L; + + /** + * 支持的最大机器id,结果是31 (这个移位算法可以很快的计算出几位二进制数所能表示的最大十进制数) + */ + private final long maxWorkerId = -1L ^ (-1L << workerIdBits); + + /** + * 支持的最大数据标识id,结果是31 + */ + private final long maxDatacenterId = -1L ^ (-1L << datacenterIdBits); + + /** + * 序列在id中占的位数 + */ + private final long sequenceBits = 12L; + + /** + * 机器ID向左移12位 + */ + private final long workerIdShift = sequenceBits; + + /** + * 数据标识id向左移17位(12+5) + */ + private final long datacenterIdShift = sequenceBits + workerIdBits; + + /** + * 时间截向左移22位(5+5+12) + */ + private final long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits; + + /** + * 生成序列的掩码,这里为4095 (0b111111111111=0xfff=4095) + */ + private final long sequenceMask = -1L ^ (-1L << sequenceBits); + + /** + * 工作机器ID(0~31) + */ + private long workerId; + + /** + * 数据中心ID(0~31) + */ + private long datacenterId; + + /** + * 毫秒内序列(0~4095) + */ + private long sequence = 0L; + + /** + * 上次生成ID的时间截 + */ + private long lastTimestamp = -1L; + + //==============================Constructors===================================== + + /** + * 构造函数 + * + * @param workerId 工作ID (0~31) + * @param datacenterId 数据中心ID (0~31) + */ + public SnowflakeIdWorker(long workerId, long datacenterId) { + if (workerId > maxWorkerId || workerId < 0) { + throw new IllegalArgumentException(String.format("worker Id can't be greater than %d or less than 0", maxWorkerId)); + } + if (datacenterId > maxDatacenterId || datacenterId < 0) { + throw new IllegalArgumentException(String.format("datacenter Id can't be greater than %d or less than 0", maxDatacenterId)); + } + this.workerId = workerId; + this.datacenterId = datacenterId; + } + + // ==============================Methods========================================== + + /** + * 获得下一个ID (该方法是线程安全的) + * + * @return SnowflakeId + */ + public synchronized long nextId() { + long timestamp = timeGen(); + + //如果当前时间小于上一次ID生成的时间戳,说明系统时钟回退过这个时候应当抛出异常 + if (timestamp < lastTimestamp) { + throw new RuntimeException( + String.format("Clock moved backwards. Refusing to generate id for %d milliseconds", lastTimestamp - timestamp)); + } + + //如果是同一时间生成的,则进行毫秒内序列 + if (lastTimestamp == timestamp) { + sequence = (sequence + 1) & sequenceMask; + //毫秒内序列溢出 + if (sequence == 0) { + //阻塞到下一个毫秒,获得新的时间戳 + timestamp = tilNextMillis(lastTimestamp); + } + } + //时间戳改变,毫秒内序列重置 + else { + sequence = 0L; + } + + //上次生成ID的时间截 + lastTimestamp = timestamp; + + //移位并通过或运算拼到一起组成64位的ID + return ((timestamp - twepoch) << timestampLeftShift) + | (datacenterId << datacenterIdShift) + | (workerId << workerIdShift) + | sequence; + } + + /** + * 阻塞到下一个毫秒,直到获得新的时间戳 + * + * @param lastTimestamp 上次生成ID的时间截 + * @return 当前时间戳 + */ + protected long tilNextMillis(long lastTimestamp) { + long timestamp = timeGen(); + while (timestamp <= lastTimestamp) { + timestamp = timeGen(); + } + return timestamp; + } + + /** + * 返回以毫秒为单位的当前时间 + * + * @return 当前时间(毫秒) + */ + protected long timeGen() { + return System.currentTimeMillis(); + } + +} \ No newline at end of file diff --git a/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/bean/UserContext.java b/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/bean/UserContext.java new file mode 100644 index 00000000..7443403d --- /dev/null +++ b/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/bean/UserContext.java @@ -0,0 +1,38 @@ +package com.gitee.sop.websiteserver.bean; + +import com.gitee.sop.gatewaycommon.bean.SpringContext; +import com.gitee.sop.websiteserver.manager.TokenManager; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; + +import javax.servlet.http.HttpServletRequest; + +/** + * @author tanghc + */ +public class UserContext { + + public static final String HEADER_TOKEN = "token"; + + public static LoginUser getLoginUser() { + return getLoginUser(getRequest()); + } + + public static LoginUser getLoginUser(HttpServletRequest request) { + String token = getToken(request); + return SpringContext.getBean(TokenManager.class).getUser(token); + } + + public static String getToken(HttpServletRequest request) { + return request.getHeader(HEADER_TOKEN); + } + + public static String getToken() { + return getToken(getRequest()); + } + + private static HttpServletRequest getRequest() { + return ((ServletRequestAttributes) (RequestContextHolder.currentRequestAttributes())).getRequest(); + } + +} diff --git a/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/bean/Validates.java b/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/bean/Validates.java new file mode 100644 index 00000000..722ff7bf --- /dev/null +++ b/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/bean/Validates.java @@ -0,0 +1,24 @@ +package com.gitee.sop.websiteserver.bean; + +import com.gitee.sop.gatewaycommon.exception.ApiException; +import com.gitee.sop.gatewaycommon.message.ErrorMeta; +import org.apache.commons.lang.StringUtils; + +import java.util.function.Supplier; + +/** + * @author tanghc + */ +public class Validates { + + public static void isTrue(boolean expression, Supplier supplier) { + if (expression) { + throw supplier.get(); + } + } + + public static void isTrue(boolean expression, String msg) { + isTrue(expression, () -> new RuntimeException(msg)); + } + +} diff --git a/sop-website/src/main/java/com/gitee/sop/websiteserver/config/CorsConfig.java b/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/config/CorsConfig.java similarity index 100% rename from sop-website/src/main/java/com/gitee/sop/websiteserver/config/CorsConfig.java rename to sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/config/CorsConfig.java diff --git a/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/config/LoginInterceptor.java b/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/config/LoginInterceptor.java new file mode 100644 index 00000000..b350579a --- /dev/null +++ b/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/config/LoginInterceptor.java @@ -0,0 +1,37 @@ +package com.gitee.sop.websiteserver.config; + +import com.gitee.sop.websiteserver.bean.NoLogin; +import com.gitee.sop.websiteserver.bean.UserContext; +import com.gitee.sop.websiteserver.exception.LoginFailureException; +import org.springframework.core.annotation.AnnotationUtils; +import org.springframework.web.method.HandlerMethod; +import org.springframework.web.servlet.handler.HandlerInterceptorAdapter; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +/** + * @author tanghc + */ +public class LoginInterceptor extends HandlerInterceptorAdapter { + + @Override + public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { + if (!request.getServletPath().startsWith("/portal")) { + return true; + } + HandlerMethod handlerMethod = (HandlerMethod) handler; + NoLogin noLogin = handlerMethod.getMethodAnnotation(NoLogin.class); + if (noLogin != null) { + return true; + } + noLogin = AnnotationUtils.findAnnotation(handlerMethod.getBeanType(), NoLogin.class); + if (noLogin != null) { + return true; + } + if (UserContext.getLoginUser(request) == null) { + throw new LoginFailureException(); + } + return true; + } +} diff --git a/sop-website/src/main/java/com/gitee/sop/websiteserver/config/WebsiteConfig.java b/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/config/WebsiteConfig.java similarity index 60% rename from sop-website/src/main/java/com/gitee/sop/websiteserver/config/WebsiteConfig.java rename to sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/config/WebsiteConfig.java index d84c7fb5..a6b9c24f 100644 --- a/sop-website/src/main/java/com/gitee/sop/websiteserver/config/WebsiteConfig.java +++ b/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/config/WebsiteConfig.java @@ -3,30 +3,38 @@ package com.gitee.sop.websiteserver.config; import com.alibaba.fastjson.serializer.SerializerFeature; import com.alibaba.fastjson.support.config.FastJsonConfig; import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter; +import com.gitee.sop.gatewaycommon.bean.SpringContext; import com.gitee.sop.gatewaycommon.manager.EnvironmentContext; import com.gitee.sop.gatewaycommon.route.RegistryListener; import com.gitee.sop.gatewaycommon.route.ServiceListener; import com.gitee.sop.websiteserver.listener.ServiceDocListener; import com.gitee.sop.websiteserver.manager.DocManager; +import com.gitee.sop.websiteserver.service.impl.EurekaServerService; +import com.gitee.sop.websiteserver.service.impl.NacosServerService; +import org.springframework.beans.BeansException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.ApplicationArguments; import org.springframework.boot.ApplicationRunner; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.autoconfigure.http.HttpMessageConverters; import org.springframework.cloud.client.discovery.event.HeartbeatEvent; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; import org.springframework.context.ApplicationEvent; +import org.springframework.context.EnvironmentAware; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.event.EventListener; import org.springframework.core.env.Environment; - -import javax.annotation.PostConstruct; +import org.springframework.web.servlet.config.annotation.InterceptorRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; /** * @author tanghc */ @Configuration -public class WebsiteConfig implements ApplicationRunner { +public class WebsiteConfig implements WebMvcConfigurer, EnvironmentAware, ApplicationContextAware { @Autowired DocManager docManager; @@ -34,8 +42,20 @@ public class WebsiteConfig implements ApplicationRunner { @Autowired private RegistryListener registryListener; - @Autowired - private Environment environment; + @Override + public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { + SpringContext.setApplicationContext(applicationContext); + } + + @Override + public void setEnvironment(Environment environment) { + EnvironmentContext.setEnvironment(environment); + } + + @Override + public void addInterceptors(InterceptorRegistry registry) { + registry.addInterceptor(new LoginInterceptor()); + } /** * 使用fastjson代替jackson @@ -52,6 +72,21 @@ public class WebsiteConfig implements ApplicationRunner { return new HttpMessageConverters(converter); } + @Bean + @ConditionalOnProperty(value = { + "eureka.client.serviceUrl.defaultZone", + "eureka.client.service-url.default-zone" + }) + public EurekaServerService eurekaServerService() { + return new EurekaServerService(); + } + + @Bean + @ConditionalOnProperty("spring.cloud.nacos.discovery.server-addr") + public NacosServerService nacosServerService() { + return new NacosServerService(); + } + /** * nacos事件监听 * @@ -68,16 +103,4 @@ public class WebsiteConfig implements ApplicationRunner { return new ServiceDocListener(); } - /** - * SpringBoot启动完毕执行 - */ - @Override - public void run(ApplicationArguments args) throws Exception { - - } - - @PostConstruct - public void after() { - EnvironmentContext.setEnvironment(environment); - } } diff --git a/sop-website/src/main/java/com/gitee/sop/websiteserver/controller/DocController.java b/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/controller/DocController.java similarity index 100% rename from sop-website/src/main/java/com/gitee/sop/websiteserver/controller/DocController.java rename to sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/controller/DocController.java diff --git a/sop-website/src/main/java/com/gitee/sop/websiteserver/controller/IndexController.java b/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/controller/IndexController.java similarity index 61% rename from sop-website/src/main/java/com/gitee/sop/websiteserver/controller/IndexController.java rename to sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/controller/IndexController.java index 05f85092..b12ff77c 100644 --- a/sop-website/src/main/java/com/gitee/sop/websiteserver/controller/IndexController.java +++ b/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/controller/IndexController.java @@ -1,7 +1,10 @@ package com.gitee.sop.websiteserver.controller; import org.springframework.stereotype.Controller; -import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.GetMapping; + +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; /** * @author tanghc @@ -9,7 +12,7 @@ import org.springframework.web.bind.annotation.RequestMapping; @Controller public class IndexController { - @RequestMapping("/") + @GetMapping("/") public String index() { return "redirect:index.html"; } diff --git a/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/controller/IsvController.java b/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/controller/IsvController.java new file mode 100644 index 00000000..4e1da32e --- /dev/null +++ b/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/controller/IsvController.java @@ -0,0 +1,207 @@ +package com.gitee.sop.websiteserver.controller; + +import com.gitee.fastmybatis.core.query.Query; +import com.gitee.sop.websiteserver.bean.DocInfo; +import com.gitee.sop.websiteserver.bean.DocItem; +import com.gitee.sop.websiteserver.bean.LoginUser; +import com.gitee.sop.websiteserver.bean.RSATool; +import com.gitee.sop.websiteserver.bean.Result; +import com.gitee.sop.websiteserver.bean.UserContext; +import com.gitee.sop.websiteserver.bean.Validates; +import com.gitee.sop.websiteserver.controller.param.IsvPublicKeyUploadParam; +import com.gitee.sop.websiteserver.controller.param.UpdatePasswordParam; +import com.gitee.sop.websiteserver.controller.result.DocVO; +import com.gitee.sop.websiteserver.controller.result.IsvInfoResult; +import com.gitee.sop.websiteserver.controller.result.IsvPublicKeyUploadResult; +import com.gitee.sop.websiteserver.controller.result.MenuDocItem; +import com.gitee.sop.websiteserver.controller.result.MenuModule; +import com.gitee.sop.websiteserver.controller.result.MenuProject; +import com.gitee.sop.websiteserver.entity.IspResource; +import com.gitee.sop.websiteserver.entity.IsvKeys; +import com.gitee.sop.websiteserver.entity.IsvPortal; +import com.gitee.sop.websiteserver.entity.UserAccount; +import com.gitee.sop.websiteserver.manager.DocManager; +import com.gitee.sop.websiteserver.mapper.IspResourceMapper; +import com.gitee.sop.websiteserver.service.UserService; +import com.gitee.sop.websiteserver.util.GenerateUtil; +import org.apache.commons.lang.BooleanUtils; +import org.apache.commons.lang.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import javax.validation.Valid; +import java.util.Collection; +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; + +/** + * @author tanghc + */ +@RestController +@RequestMapping("portal/isv") +public class IsvController { + + @Autowired + private UserService userService; + + @Autowired + DocManager docManager; + + @Autowired + IspResourceMapper ispResourceMapper; + + @Value("${api.url-test}") + String urlTest; + + @Value("${api.url-prod}") + String urlProd; + + @GetMapping("/getIsvPortal") + public Result getIsvInfo() { + LoginUser loginUser = UserContext.getLoginUser(); + IsvInfoResult isvInfoResult = new IsvInfoResult(); + IsvPortal isvPortal = userService.getIsvPortal(loginUser.getUserId()); + boolean hasPublicKey = StringUtils.isNotBlank(isvPortal.getPublicKeyIsv()); + isvInfoResult.setIsUploadPublicKey(BooleanUtils.toInteger(hasPublicKey)); + isvInfoResult.setAppId(isvPortal.getAppId()); + return Result.ok(isvInfoResult); + } + + @GetMapping("/getPublicKey") + public Result getPublicKey() { + LoginUser loginUser = UserContext.getLoginUser(); + IsvKeys isvKeys = userService.getIsvKeys(loginUser.getAppKey()); + IsvPublicKeyUploadResult isvPublicKeyUploadResult = new IsvPublicKeyUploadResult(); + isvPublicKeyUploadResult.setIsUploadPublicKey(BooleanUtils.toInteger(isvKeys != null)); + isvPublicKeyUploadResult.setPublicKeyIsv(isvKeys == null ? "" : isvKeys.getPublicKeyIsv()); + isvPublicKeyUploadResult.setPublicKeyPlatform(isvKeys == null ? "" : isvKeys.getPublicKeyPlatform()); + return Result.ok(isvPublicKeyUploadResult); + } + + /** + * 上传公钥 + * @param param + * @return + */ + @PostMapping("/uploadPublicKey") + public Result uploadPublicKey(@RequestBody @Valid IsvPublicKeyUploadParam param) { + String publicKeyIsv = param.getPublicKeyIsv(); + LoginUser loginUser = UserContext.getLoginUser(); + IsvKeys isvKeys = userService.getIsvKeys(loginUser.getAppKey()); + RSATool rsaToolIsv = new RSATool(RSATool.KeyFormat.PKCS8, RSATool.KeyLength.LENGTH_2048); + RSATool.KeyStore keyStorePlatform; + try { + // 生成平台公私钥 + keyStorePlatform = rsaToolIsv.createKeys(); + } catch (Exception e) { + throw new RuntimeException("上传公钥失败", e); + } + String publicKeyPlatform = keyStorePlatform.getPublicKey(); + String privateKeyPlatform = keyStorePlatform.getPrivateKey(); + if (isvKeys == null) { + isvKeys = new IsvKeys(); + isvKeys.setAppKey(loginUser.getAppKey()); + // 私钥自己保存 + isvKeys.setPrivateKeyIsv(""); + isvKeys.setPublicKeyIsv(publicKeyIsv); + // 设置平台公私钥 + isvKeys.setPrivateKeyPlatform(privateKeyPlatform); + isvKeys.setPublicKeyPlatform(publicKeyPlatform); + userService.saveIsvKey(isvKeys); + } else { + isvKeys.setPrivateKeyPlatform(privateKeyPlatform); + isvKeys.setPublicKeyPlatform(publicKeyPlatform); + isvKeys.setPublicKeyIsv(publicKeyIsv); + userService.updateIsvKey(isvKeys); + } + IsvPublicKeyUploadResult isvPublicKeyUploadResult = new IsvPublicKeyUploadResult(); + isvPublicKeyUploadResult.setIsUploadPublicKey(BooleanUtils.toInteger(true)); + isvPublicKeyUploadResult.setPublicKeyIsv(isvKeys.getPublicKeyIsv()); + isvPublicKeyUploadResult.setPublicKeyPlatform(isvKeys.getPublicKeyPlatform()); + return Result.ok(isvPublicKeyUploadResult); + } + + /** + * 修改密码 + * @param param + * @return + */ + @PostMapping("/updatePassword") + public Result updatePassword(@RequestBody @Valid UpdatePasswordParam param) { + long userId = UserContext.getLoginUser().getUserId(); + UserAccount userAccount = userService.getUserAccountByUserId(userId); + String oldPwdHex = GenerateUtil.getUserPassword(userAccount.getUsername(), param.getOldPassword()); + Validates.isTrue(!Objects.equals(oldPwdHex, userAccount.getPassword()), "旧密码错误"); + String newPwdHex = GenerateUtil.getUserPassword(userAccount.getUsername(), param.getPassword()); + userAccount.setPassword(newPwdHex); + userService.updateUserAccount(userAccount); + return Result.ok(); + } + + /** + * 获取文档页菜单数据 + * @return + */ + @GetMapping("/getDocMenus") + public Result getAllDoc() { + Collection docInfos = docManager.listAll(); + List menuProjects = docInfos.stream() + .map(docInfo -> { + MenuProject menuProject = new MenuProject(); + menuProject.setLabel(docInfo.getTitle()); + // 构建模块 + List menuModules = docInfo.getDocModuleList() + .stream() + .map(docModule -> { + MenuModule menuModule = new MenuModule(); + menuModule.setLabel(docModule.getModule()); + // 构建文档 + List docItems = docModule.getDocItems().stream() + .map(docItem -> { + MenuDocItem menuDocItem = new MenuDocItem(); + menuDocItem.setId(docItem.getId()); + menuDocItem.setLabel(docItem.getSummary()); + menuDocItem.setName(docItem.getName()); + menuDocItem.setVersion(docItem.getVersion()); + return menuDocItem; + }).collect(Collectors.toList()); + menuModule.setChildren(docItems); + return menuModule; + }).collect(Collectors.toList()); + menuProject.setChildren(menuModules); + return menuProject; + }).collect(Collectors.toList()); + DocVO docVO = new DocVO(); + docVO.setMenuProjects(menuProjects); + docVO.setUrlProd(urlProd); + docVO.setUrlTest(urlTest); + return Result.ok(docVO); + } + + /** + * 根据文档id查询文档内容 + * @param id 文档id + * @return + */ + @GetMapping("/getDocItem") + public Result getDocItem(String id) { + DocItem docItem = docManager.getDocItem(id); + return Result.ok(docItem); + } + + /** + * 获取SDK列表 + * @return + */ + @GetMapping("listSdk") + public Result> list() { + List resourceList = ispResourceMapper.list(new Query()); + return Result.ok(resourceList); + } +} diff --git a/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/controller/LoginController.java b/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/controller/LoginController.java new file mode 100644 index 00000000..452ce876 --- /dev/null +++ b/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/controller/LoginController.java @@ -0,0 +1,58 @@ +package com.gitee.sop.websiteserver.controller; + +import com.gitee.sop.websiteserver.bean.LoginUser; +import com.gitee.sop.websiteserver.bean.NoLogin; +import com.gitee.sop.websiteserver.bean.Result; +import com.gitee.sop.websiteserver.bean.UserContext; +import com.gitee.sop.websiteserver.controller.param.LoginForm; +import com.gitee.sop.websiteserver.controller.result.LoginResult; +import com.gitee.sop.websiteserver.manager.TokenManager; +import com.gitee.sop.websiteserver.service.UserService; +import com.gitee.sop.websiteserver.util.GenerateUtil; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import javax.validation.Valid; +import java.util.Objects; +import java.util.UUID; + +/** + * @author tanghc + */ +@RestController +@RequestMapping("portal/common") +public class LoginController { + + @Autowired + private UserService userService; + + @Autowired + private TokenManager tokenManager; + + @PostMapping("login") + @NoLogin + public Result login(@RequestBody @Valid LoginForm param) { + String username = param.getUsername(); + String password = param.getPassword(); + password = GenerateUtil.getUserPassword(username, password); + LoginUser loginUser = userService.getLoginUser(username, password); + Objects.requireNonNull(loginUser, "用户名密码不正确"); + String token = UUID.randomUUID().toString(); + tokenManager.setUser(token, loginUser); + LoginResult loginResult = new LoginResult(); + loginResult.setToken(token); + return Result.ok(loginResult); + } + + @GetMapping("logout") + public Result logout() { + String token = UserContext.getToken(); + tokenManager.removeUser(token); + return Result.ok(); + } + +} diff --git a/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/controller/RegController.java b/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/controller/RegController.java new file mode 100644 index 00000000..b5758b4a --- /dev/null +++ b/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/controller/RegController.java @@ -0,0 +1,44 @@ +package com.gitee.sop.websiteserver.controller; + +import com.gitee.sop.websiteserver.bean.NoLogin; +import com.gitee.sop.websiteserver.bean.Result; +import com.gitee.sop.websiteserver.bean.Validates; +import com.gitee.sop.websiteserver.controller.param.RegParam; +import com.gitee.sop.websiteserver.entity.UserAccount; +import com.gitee.sop.websiteserver.service.UserService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import javax.validation.Valid; + +/** + * @author tanghc + */ +@RestController +@RequestMapping("portal/common") +@NoLogin +public class RegController { + + @Autowired + private UserService userService; + + @PostMapping("/regIsv") + public Result reg(@RequestBody @Valid RegParam param) { + // 3. 校验邮箱是否存在 + this.checkEmail(param.getUsername()); + // 4. 创建用户 + UserAccount userInfo = userService.saveUser(param); + // 5. 创建ISV + userService.createIsv(userInfo); + return Result.ok(); + } + + private void checkEmail(String email) { + UserAccount username = userService.getUserAccountByUsername(email); + Validates.isTrue(username != null, "该邮箱已被注册"); + } + +} diff --git a/sop-website/src/main/java/com/gitee/sop/websiteserver/controller/SandboxController.java b/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/controller/SandboxController.java similarity index 99% rename from sop-website/src/main/java/com/gitee/sop/websiteserver/controller/SandboxController.java rename to sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/controller/SandboxController.java index 7c04af0d..792dc8f3 100644 --- a/sop-website/src/main/java/com/gitee/sop/websiteserver/controller/SandboxController.java +++ b/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/controller/SandboxController.java @@ -208,6 +208,6 @@ public class SandboxController { for (Map.Entry entry : params.entrySet()) { sb.append("&").append(entry.getKey()).append("=").append(entry.getValue()); } - return sb.toString().substring(1); + return sb.substring(1); } } diff --git a/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/controller/param/FindPasswordParam.java b/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/controller/param/FindPasswordParam.java new file mode 100644 index 00000000..2e619010 --- /dev/null +++ b/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/controller/param/FindPasswordParam.java @@ -0,0 +1,15 @@ +package com.gitee.sop.websiteserver.controller.param; + +import lombok.Data; +import org.hibernate.validator.constraints.Length; + +import javax.validation.constraints.NotBlank; + +/** + * @author tanghc + */ +@Data +public class FindPasswordParam { + @NotBlank @Length(max = 300) String username; + +} diff --git a/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/controller/param/IsvPublicKeyUploadParam.java b/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/controller/param/IsvPublicKeyUploadParam.java new file mode 100644 index 00000000..61d23b0c --- /dev/null +++ b/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/controller/param/IsvPublicKeyUploadParam.java @@ -0,0 +1,16 @@ +package com.gitee.sop.websiteserver.controller.param; + +import lombok.Data; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; + +/** + * @author tanghc + */ +@Data +public class IsvPublicKeyUploadParam { + + @NotBlank + private String publicKeyIsv; +} diff --git a/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/controller/param/LoginForm.java b/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/controller/param/LoginForm.java new file mode 100644 index 00000000..77d2fc7e --- /dev/null +++ b/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/controller/param/LoginForm.java @@ -0,0 +1,15 @@ +package com.gitee.sop.websiteserver.controller.param; + +import lombok.Data; + +import javax.validation.constraints.NotBlank; + +@Data +public class LoginForm { + @NotBlank(message = "用户名不能为空") + private String username; + + @NotBlank(message = "密码不能为空") + private String password; + +} \ No newline at end of file diff --git a/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/controller/param/RegParam.java b/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/controller/param/RegParam.java new file mode 100644 index 00000000..054c4fc4 --- /dev/null +++ b/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/controller/param/RegParam.java @@ -0,0 +1,33 @@ +package com.gitee.sop.websiteserver.controller.param; + +import lombok.Data; +import org.hibernate.validator.constraints.Length; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; + +/** + * @author tanghc + */ +@Data +public class RegParam { + + @NotBlank(message = "username can not be null") + @Length(max = 128) + private String username; + + @NotBlank(message = "password can not be null") + @Length(max = 64) + private String password; + + @Length(max = 64) + private String namespace; + + @NotNull(message = "type can not be null") + private Byte type; + + @Length(max = 64) + private String company; + + +} diff --git a/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/controller/param/RestPasswordParam.java b/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/controller/param/RestPasswordParam.java new file mode 100644 index 00000000..5e0fe9ef --- /dev/null +++ b/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/controller/param/RestPasswordParam.java @@ -0,0 +1,26 @@ +package com.gitee.sop.websiteserver.controller.param; + +import lombok.Data; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; + +/** + * @author tanghc + */ +@Data +public class RestPasswordParam { + + @NotBlank + private String email; + + @NotNull + private Long time; + + @NotBlank + private String sign; + + @NotBlank(message = "密码不能为空") + private String password; + +} diff --git a/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/controller/param/UpdatePasswordParam.java b/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/controller/param/UpdatePasswordParam.java new file mode 100644 index 00000000..0a184704 --- /dev/null +++ b/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/controller/param/UpdatePasswordParam.java @@ -0,0 +1,19 @@ +package com.gitee.sop.websiteserver.controller.param; + +import lombok.Data; + +import javax.validation.constraints.NotBlank; + +/** + * @author tanghc + */ +@Data +public class UpdatePasswordParam { + + @NotBlank(message = "旧密码不能为空") + private String oldPassword; + + @NotBlank(message = "密码不能为空") + private String password; + +} diff --git a/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/controller/result/DocVO.java b/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/controller/result/DocVO.java new file mode 100644 index 00000000..2362c4cc --- /dev/null +++ b/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/controller/result/DocVO.java @@ -0,0 +1,17 @@ +package com.gitee.sop.websiteserver.controller.result; + +import lombok.Getter; +import lombok.Setter; + +import java.util.Collection; + +/** + * @author tanghc + */ +@Getter +@Setter +public class DocVO { + private String urlTest; + private String urlProd; + private Collection menuProjects; +} diff --git a/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/controller/result/IsvInfoResult.java b/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/controller/result/IsvInfoResult.java new file mode 100644 index 00000000..794ffb7d --- /dev/null +++ b/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/controller/result/IsvInfoResult.java @@ -0,0 +1,12 @@ +package com.gitee.sop.websiteserver.controller.result; + +import lombok.Data; + +/** + * @author tanghc + */ +@Data +public class IsvInfoResult { + private String appId; + private Integer isUploadPublicKey; +} diff --git a/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/controller/result/IsvPublicKeyUploadResult.java b/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/controller/result/IsvPublicKeyUploadResult.java new file mode 100644 index 00000000..abf05376 --- /dev/null +++ b/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/controller/result/IsvPublicKeyUploadResult.java @@ -0,0 +1,20 @@ +package com.gitee.sop.websiteserver.controller.result; + +import lombok.Data; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; + +/** + * @author tanghc + */ +@Data +public class IsvPublicKeyUploadResult { + + private String publicKeyIsv; + + private String publicKeyPlatform; + + private Integer isUploadPublicKey; + +} diff --git a/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/controller/result/IsvResourceResult.java b/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/controller/result/IsvResourceResult.java new file mode 100644 index 00000000..e04ec099 --- /dev/null +++ b/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/controller/result/IsvResourceResult.java @@ -0,0 +1,27 @@ +package com.gitee.sop.websiteserver.controller.result; + +import lombok.Data; + + +/** + * 表名:isp_resource + * 备注:ISP资源表 + * + * @author tanghc + */ +@Data +public class IsvResourceResult { + + private Long id; + + /** 资源名称, 数据库字段:name */ + private String name; + + private String version; + + /** 资源内容(URL), 数据库字段:content */ + private String content; + + private String extContent; + +} diff --git a/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/controller/result/LoginResult.java b/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/controller/result/LoginResult.java new file mode 100644 index 00000000..e946c0ab --- /dev/null +++ b/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/controller/result/LoginResult.java @@ -0,0 +1,11 @@ +package com.gitee.sop.websiteserver.controller.result; + +import lombok.Data; + +/** + * @author tanghc + */ +@Data +public class LoginResult { + private String token; +} diff --git a/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/controller/result/MenuDocItem.java b/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/controller/result/MenuDocItem.java new file mode 100644 index 00000000..f1f73bf1 --- /dev/null +++ b/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/controller/result/MenuDocItem.java @@ -0,0 +1,14 @@ +package com.gitee.sop.websiteserver.controller.result; + +import lombok.Data; + +/** + * @author tanghc + */ +@Data +public class MenuDocItem { + private String id; + private String label; + private String name; + private String version; +} diff --git a/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/controller/result/MenuModule.java b/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/controller/result/MenuModule.java new file mode 100644 index 00000000..737f29e6 --- /dev/null +++ b/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/controller/result/MenuModule.java @@ -0,0 +1,14 @@ +package com.gitee.sop.websiteserver.controller.result; + +import lombok.Data; + +import java.util.List; + +/** + * @author tanghc + */ +@Data +public class MenuModule { + private String label; + private List children; +} diff --git a/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/controller/result/MenuProject.java b/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/controller/result/MenuProject.java new file mode 100644 index 00000000..5d5552b1 --- /dev/null +++ b/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/controller/result/MenuProject.java @@ -0,0 +1,14 @@ +package com.gitee.sop.websiteserver.controller.result; + +import lombok.Data; + +import java.util.List; + +/** + * @author tanghc + */ +@Data +public class MenuProject { + private String label; + private List children; +} diff --git a/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/controller/result/RegResult.java b/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/controller/result/RegResult.java new file mode 100644 index 00000000..6abf4d79 --- /dev/null +++ b/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/controller/result/RegResult.java @@ -0,0 +1,11 @@ +package com.gitee.sop.websiteserver.controller.result; + +import lombok.Data; + +/** + * @author tanghc + */ +@Data +public class RegResult { + private Integer needVerifyEmail; +} diff --git a/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/entity/IspResource.java b/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/entity/IspResource.java new file mode 100644 index 00000000..c3048ada --- /dev/null +++ b/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/entity/IspResource.java @@ -0,0 +1,50 @@ +package com.gitee.sop.websiteserver.entity; + +import lombok.Data; + +import javax.persistence.Column; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.Table; +import java.util.Date; + + +/** + * 表名:isp_resource + * 备注:ISP资源表 + * + * @author tanghc + */ +@Table(name = "isp_resource") +@Data +public class IspResource { + /** 数据库字段:id */ + @Id + @Column(name = "id") + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + /** 资源名称, 数据库字段:name */ + private String name; + + private String version; + + /** 资源内容(URL), 数据库字段:content */ + private String content; + + private String extContent; + + /** 资源类型:0:SDK链接, 数据库字段:type */ + private Byte type; + + /** 数据库字段:is_deleted */ + @com.gitee.fastmybatis.core.annotation.LogicDelete + private Byte isDeleted; + + /** 数据库字段:gmt_create */ + private Date gmtCreate; + + /** 数据库字段:gmt_modified */ + private Date gmtModified; +} diff --git a/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/entity/IsvInfo.java b/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/entity/IsvInfo.java new file mode 100644 index 00000000..84dfc922 --- /dev/null +++ b/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/entity/IsvInfo.java @@ -0,0 +1,44 @@ +package com.gitee.sop.websiteserver.entity; + +import lombok.Data; + +import javax.persistence.Column; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.Table; +import java.util.Date; + + +/** + * 表名:isv_info + * 备注:isv信息表 + * + * @author tanghc + */ +@Table(name = "isv_info") +@Data +public class IsvInfo { + @Id + @Column(name = "id") + @GeneratedValue(strategy = GenerationType.IDENTITY) + /** 数据库字段:id */ + private Long id; + + /** appKey, 数据库字段:app_key */ + private String appKey; + + /** 1启用,2禁用, 数据库字段:status */ + private Byte status; + + /** 备注,数据库字段:remark */ + private String remark; + + private Long userId; + + /** 数据库字段:gmt_create */ + private Date gmtCreate; + + /** 数据库字段:gmt_modified */ + private Date gmtModified; +} diff --git a/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/entity/IsvKeys.java b/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/entity/IsvKeys.java new file mode 100644 index 00000000..c1a81ddc --- /dev/null +++ b/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/entity/IsvKeys.java @@ -0,0 +1,57 @@ +package com.gitee.sop.websiteserver.entity; + +import lombok.Data; + +import javax.persistence.Column; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.Table; +import java.util.Date; + + +/** + * 表名:isv_keys + * 备注:ISV秘钥 + * + * @author tanghc + */ +@Table(name = "isv_keys") +@Data +public class IsvKeys { + @Id + @Column(name = "id") + @GeneratedValue(strategy = GenerationType.IDENTITY) + /** 数据库字段:id */ + private Long id; + + /** 数据库字段:app_key */ + private String appKey; + + /** 1:RSA2,2:MD5, 数据库字段:sign_type */ + private Byte signType; + + /** isv_info.sign_type=2时使用, 数据库字段:secret */ + private String secret; + + /** 秘钥格式,1:PKCS8(JAVA适用),2:PKCS1(非JAVA适用), 数据库字段:key_format */ + private Byte keyFormat; + + /** 开发者生成的公钥, 数据库字段:public_key_isv */ + private String publicKeyIsv; + + /** 开发者生成的私钥(交给开发者), 数据库字段:private_key_isv */ + private String privateKeyIsv; + + /** 平台生成的公钥(交给开发者), 数据库字段:public_key_platform */ + private String publicKeyPlatform; + + /** 平台生成的私钥, 数据库字段:private_key_platform */ + private String privateKeyPlatform; + + /** 数据库字段:gmt_create */ + private Date gmtCreate; + + /** 数据库字段:gmt_modified */ + private Date gmtModified; +} diff --git a/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/entity/IsvPortal.java b/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/entity/IsvPortal.java new file mode 100644 index 00000000..9faf6ba1 --- /dev/null +++ b/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/entity/IsvPortal.java @@ -0,0 +1,19 @@ +package com.gitee.sop.websiteserver.entity; + +import lombok.Data; + +/** + * @author tanghc + */ +@Data +public class IsvPortal { + + private Long userId; + + /** isv appId */ + private String appId; + + /** isv上传的公钥, 用户验证客户端签名 */ + private transient String publicKeyIsv; + +} diff --git a/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/entity/UserAccount.java b/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/entity/UserAccount.java new file mode 100644 index 00000000..b36eb04b --- /dev/null +++ b/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/entity/UserAccount.java @@ -0,0 +1,42 @@ +package com.gitee.sop.websiteserver.entity; + +import lombok.Data; + +import javax.persistence.Column; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.Table; +import java.util.Date; + + +/** + * 表名:user_account + * 备注:用户信息 + * + * @author tanghc + */ +@Table(name = "user_account") +@Data +public class UserAccount { + /** 数据库字段:id */ + @Id + @Column(name = "id") + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + /** 用户名(邮箱), 数据库字段:username */ + private String username; + + /** 密码, 数据库字段:password */ + private String password; + + /** 2:邮箱未验证,1:启用,0:禁用, 数据库字段:status */ + private Byte status; + + /** 数据库字段:gmt_create */ + private Date gmtCreate; + + /** 数据库字段:gmt_modified */ + private Date gmtModified; +} diff --git a/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/exception/ErrorCode.java b/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/exception/ErrorCode.java new file mode 100644 index 00000000..1b13dfa7 --- /dev/null +++ b/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/exception/ErrorCode.java @@ -0,0 +1,24 @@ +package com.gitee.sop.websiteserver.exception; + +/** + * @author tanghc + */ +public enum ErrorCode { + LOGIN_FAIL("9", "登录失败"); + + ErrorCode(String code, String msg) { + this.code = code; + this.msg = msg; + } + + private final String code; + private final String msg; + + public String getCode() { + return code; + } + + public String getMsg() { + return msg; + } +} diff --git a/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/exception/ExceptionCode.java b/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/exception/ExceptionCode.java new file mode 100644 index 00000000..b9faf0ab --- /dev/null +++ b/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/exception/ExceptionCode.java @@ -0,0 +1,8 @@ +package com.gitee.sop.websiteserver.exception; + +/** + * @author tanghc + */ +public interface ExceptionCode { + ErrorCode getCode(); +} diff --git a/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/exception/ExceptionController.java b/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/exception/ExceptionController.java new file mode 100644 index 00000000..c81dea3a --- /dev/null +++ b/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/exception/ExceptionController.java @@ -0,0 +1,31 @@ +package com.gitee.sop.websiteserver.exception; + +import com.gitee.sop.websiteserver.bean.Result; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.RestControllerAdvice; + +/** + * 全局错误处理 + * + * @author tanghc + */ +@RestControllerAdvice +public class ExceptionController { + + private static final Logger logger = LoggerFactory.getLogger(ExceptionController.class); + + @ExceptionHandler(Exception.class) + public Object exceptionHandler(Exception e) { + if (e instanceof ExceptionCode) { + ExceptionCode exceptionCode = (ExceptionCode) e; + ErrorCode errorCode = exceptionCode.getCode(); + logger.error("报错,code:{}, msg:{}", errorCode.getCode(), errorCode.getMsg(), e); + return Result.err(errorCode.getCode(), errorCode.getMsg()); + } + logger.error("未知错误:", e); + return Result.err(e.getMessage()); + } + +} diff --git a/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/exception/LoginFailureException.java b/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/exception/LoginFailureException.java new file mode 100644 index 00000000..d69a1c57 --- /dev/null +++ b/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/exception/LoginFailureException.java @@ -0,0 +1,11 @@ +package com.gitee.sop.websiteserver.exception; + +/** + * @author tanghc + */ +public class LoginFailureException extends RuntimeException implements ExceptionCode{ + @Override + public ErrorCode getCode() { + return ErrorCode.LOGIN_FAIL; + } +} diff --git a/sop-website/src/main/java/com/gitee/sop/websiteserver/listener/ServiceDocListener.java b/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/listener/ServiceDocListener.java similarity index 100% rename from sop-website/src/main/java/com/gitee/sop/websiteserver/listener/ServiceDocListener.java rename to sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/listener/ServiceDocListener.java diff --git a/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/manager/DefaultTokenManager.java b/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/manager/DefaultTokenManager.java new file mode 100644 index 00000000..24d09ff8 --- /dev/null +++ b/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/manager/DefaultTokenManager.java @@ -0,0 +1,68 @@ +package com.gitee.sop.websiteserver.manager; + +import com.gitee.sop.websiteserver.bean.LoginUser; +import com.google.common.cache.CacheBuilder; +import com.google.common.cache.CacheLoader; +import com.google.common.cache.LoadingCache; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; +import org.springframework.util.StringUtils; + +import javax.annotation.PostConstruct; +import java.util.Optional; +import java.util.concurrent.TimeUnit; + +/** + * @author tanghc + */ +@Service +@Slf4j +public class DefaultTokenManager implements TokenManager { + + + @Value("${sop.website.token-timeout-seconds:600}") + private int accessTokenTimeoutSeconds; + + private LoadingCache> accessTokenCache; + + @Override + public LoginUser getUser(String token) { + if (StringUtils.isEmpty(token)) { + return null; + } + return accessTokenCache.getUnchecked(token).orElse(null); + } + + @Override + public void setUser(String token, LoginUser user) { + accessTokenCache.put(token, Optional.of(user)); + } + + @Override + public void removeUser(String token) { + if (token == null) { + return; + } + accessTokenCache.invalidate(token); + } + + private static LoadingCache> buildCache(int timeout) { + CacheBuilder cacheBuilder = CacheBuilder.newBuilder(); + if (timeout > 0) { + cacheBuilder.expireAfterAccess(timeout, TimeUnit.SECONDS); + } + return cacheBuilder + .build(new CacheLoader>() { + @Override + public Optional load(String key) throws Exception { + return Optional.empty(); + } + }); + } + + @PostConstruct + public void after() { + accessTokenCache = buildCache(accessTokenTimeoutSeconds); + } +} diff --git a/sop-website/src/main/java/com/gitee/sop/websiteserver/manager/DocManager.java b/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/manager/DocManager.java similarity index 83% rename from sop-website/src/main/java/com/gitee/sop/websiteserver/manager/DocManager.java rename to sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/manager/DocManager.java index ddb5806d..d8bcc85f 100644 --- a/sop-website/src/main/java/com/gitee/sop/websiteserver/manager/DocManager.java +++ b/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/manager/DocManager.java @@ -1,6 +1,7 @@ package com.gitee.sop.websiteserver.manager; import com.gitee.sop.websiteserver.bean.DocInfo; +import com.gitee.sop.websiteserver.bean.DocItem; import java.util.Collection; import java.util.function.Consumer; @@ -14,6 +15,8 @@ public interface DocManager { DocInfo getByTitle(String title); + DocItem getDocItem(String id); + Collection listAll(); void remove(String serviceId); diff --git a/sop-website/src/main/java/com/gitee/sop/websiteserver/manager/DocManagerImpl.java b/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/manager/DocManagerImpl.java similarity index 76% rename from sop-website/src/main/java/com/gitee/sop/websiteserver/manager/DocManagerImpl.java rename to sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/manager/DocManagerImpl.java index d0ad7fd0..d067aa34 100644 --- a/sop-website/src/main/java/com/gitee/sop/websiteserver/manager/DocManagerImpl.java +++ b/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/manager/DocManagerImpl.java @@ -4,6 +4,8 @@ import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import com.alibaba.fastjson.parser.Feature; import com.gitee.sop.websiteserver.bean.DocInfo; +import com.gitee.sop.websiteserver.bean.DocItem; +import com.gitee.sop.websiteserver.bean.DocModule; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import org.springframework.util.DigestUtils; @@ -11,8 +13,10 @@ import org.springframework.util.DigestUtils; import java.nio.charset.StandardCharsets; import java.util.Collection; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.concurrent.ConcurrentHashMap; import java.util.function.Consumer; /** @@ -30,6 +34,9 @@ public class DocManagerImpl implements DocManager { */ private static final Map serviceIdMd5Map = new HashMap<>(); + // key: DocItem.id + private static final Map ITEM_MAP = new ConcurrentHashMap<>(128); + private static final DocParser swaggerDocParser = new SwaggerDocParser(); @Override @@ -44,6 +51,12 @@ public class DocManagerImpl implements DocManager { DocInfo docInfo = swaggerDocParser.parseJson(docRoot); docInfo.setServiceId(serviceId); docDefinitionMap.put(docInfo.getTitle(), docInfo); + List docModules = docInfo.getDocModuleList(); + docModules.forEach(docModule -> { + docModule.getDocItems().forEach(docItem -> { + ITEM_MAP.put(docItem.getId(), docItem); + }); + }); callback.accept(docInfo); } @@ -52,6 +65,11 @@ public class DocManagerImpl implements DocManager { return docDefinitionMap.get(title); } + @Override + public DocItem getDocItem(String id) { + return ITEM_MAP.get(id); + } + @Override public Collection listAll() { return docDefinitionMap.values(); diff --git a/sop-website/src/main/java/com/gitee/sop/websiteserver/manager/DocParser.java b/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/manager/DocParser.java similarity index 100% rename from sop-website/src/main/java/com/gitee/sop/websiteserver/manager/DocParser.java rename to sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/manager/DocParser.java diff --git a/sop-website/src/main/java/com/gitee/sop/websiteserver/manager/SwaggerDocParser.java b/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/manager/SwaggerDocParser.java similarity index 98% rename from sop-website/src/main/java/com/gitee/sop/websiteserver/manager/SwaggerDocParser.java rename to sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/manager/SwaggerDocParser.java index 0f41da00..b4c1de0a 100644 --- a/sop-website/src/main/java/com/gitee/sop/websiteserver/manager/SwaggerDocParser.java +++ b/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/manager/SwaggerDocParser.java @@ -14,7 +14,9 @@ import org.apache.commons.lang.StringUtils; import org.apache.commons.lang3.math.NumberUtils; import org.springframework.beans.BeanUtils; import org.springframework.util.CollectionUtils; +import org.springframework.util.DigestUtils; +import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -24,6 +26,7 @@ import java.util.Map; import java.util.Objects; import java.util.Optional; import java.util.Set; +import java.util.UUID; import java.util.stream.Collectors; /** @@ -117,6 +120,7 @@ public class SwaggerDocParser implements DocParser { return null; } DocItem docItem = new DocItem(); + docItem.setId(UUID.randomUUID().toString()); docItem.setName(apiName); docItem.setVersion(docInfo.getString("sop_version")); docItem.setSummary(docInfo.getString("summary")); diff --git a/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/manager/TokenManager.java b/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/manager/TokenManager.java new file mode 100644 index 00000000..b654aa23 --- /dev/null +++ b/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/manager/TokenManager.java @@ -0,0 +1,30 @@ +package com.gitee.sop.websiteserver.manager; + +import com.gitee.sop.websiteserver.bean.LoginUser; + +/** + * @author tanghc + */ +public interface TokenManager { + + /** + * 根据token获取登录用户 + * @param token token + * @return 返回登录用户,没有返回null + */ + LoginUser getUser(String token); + + /** + * 返回token + * @param token + * @param user + * @return + */ + void setUser(String token, LoginUser user); + + /** + * 删除用户 + * @param token token + */ + void removeUser(String token); +} diff --git a/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/mapper/IspResourceMapper.java b/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/mapper/IspResourceMapper.java new file mode 100644 index 00000000..1ef279b0 --- /dev/null +++ b/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/mapper/IspResourceMapper.java @@ -0,0 +1,11 @@ +package com.gitee.sop.websiteserver.mapper; + +import com.gitee.fastmybatis.core.mapper.CrudMapper; +import com.gitee.sop.websiteserver.entity.IspResource; + + +/** + * @author tanghc + */ +public interface IspResourceMapper extends CrudMapper { +} diff --git a/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/mapper/IsvInfoMapper.java b/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/mapper/IsvInfoMapper.java new file mode 100644 index 00000000..579a5aaa --- /dev/null +++ b/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/mapper/IsvInfoMapper.java @@ -0,0 +1,61 @@ +package com.gitee.sop.websiteserver.mapper; + +import com.gitee.fastmybatis.core.mapper.CrudMapper; +import com.gitee.sop.websiteserver.bean.IsvDetailDTO; +import com.gitee.sop.websiteserver.bean.LoginUser; +import com.gitee.sop.websiteserver.entity.IsvInfo; +import com.gitee.sop.websiteserver.entity.IsvPortal; +import org.apache.ibatis.annotations.Param; +import org.apache.ibatis.annotations.Select; + +import java.util.List; + + +/** + * @author tanghc + */ +public interface IsvInfoMapper extends CrudMapper { + /** + * 获取isv详细信息 + * @param username username + * @param password password + * @return 返回登录信息,没有返回null + */ + @Select("SELECT " + + " t.id userId " + + " ,t2.app_key appKey " + + " ,t3.private_key_isv privateKeyIsv " + + "FROM user_account t " + + "LEFT JOIN isv_info t2 ON t.id = t2.user_id " + + "LEFT JOIN isv_keys t3 ON t2.app_key = t3.app_key " + + "WHERE t.username = #{username} AND t.password=#{password} ") + LoginUser getLoginUser(@Param("username") String username, @Param("password")String password); + + + @Select("SELECT " + + " t.id userId " + + " ,t2.app_key appId " + + " ,t3.public_key_isv publicKeyIsv " + + "FROM user_account t " + + "LEFT JOIN isv_info t2 ON t.id = t2.user_id " + + "LEFT JOIN isv_keys t3 ON t2.app_key = t3.app_key " + + "WHERE t.id = #{userId} ") + IsvPortal getIsvPortal(@Param("userId") long userId); + + /** + * 获取isv详细信息 + * @param appKey appKey + * @return 返回详细信息,没有返回null + */ + @Select("SELECT " + + " t.app_key appKey " + + " ,t.status " + + " ,t2.sign_type signType " + + " ,t2.secret " + + " ,t2.public_key_isv publicKeyIsv " + + " ,t2.private_key_platform privateKeyPlatform " + + "FROM isv_info t " + + "INNER JOIN isv_keys t2 ON t.app_key = t2.app_key " + + "WHERE t.app_key = #{appKey}") + IsvDetailDTO getIsvDetail(@Param("appKey") String appKey); +} diff --git a/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/mapper/IsvKeysMapper.java b/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/mapper/IsvKeysMapper.java new file mode 100644 index 00000000..f8b142c6 --- /dev/null +++ b/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/mapper/IsvKeysMapper.java @@ -0,0 +1,11 @@ +package com.gitee.sop.websiteserver.mapper; + +import com.gitee.fastmybatis.core.mapper.CrudMapper; +import com.gitee.sop.websiteserver.entity.IsvKeys; + + +/** + * @author tanghc + */ +public interface IsvKeysMapper extends CrudMapper { +} diff --git a/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/mapper/UserAccountMapper.java b/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/mapper/UserAccountMapper.java new file mode 100644 index 00000000..6705ea1f --- /dev/null +++ b/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/mapper/UserAccountMapper.java @@ -0,0 +1,11 @@ +package com.gitee.sop.websiteserver.mapper; + +import com.gitee.fastmybatis.core.mapper.CrudMapper; +import com.gitee.sop.websiteserver.entity.UserAccount; + + +/** + * @author tanghc + */ +public interface UserAccountMapper extends CrudMapper { +} diff --git a/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/service/ConfigPushService.java b/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/service/ConfigPushService.java new file mode 100644 index 00000000..788b1a64 --- /dev/null +++ b/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/service/ConfigPushService.java @@ -0,0 +1,76 @@ +package com.gitee.sop.websiteserver.service; + +import com.alibaba.fastjson.JSON; +import com.gitee.sop.websiteserver.bean.ChannelMsg; +import com.gitee.sop.websiteserver.bean.GatewayPushDTO; +import com.gitee.sop.websiteserver.bean.HttpTool; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.codec.digest.DigestUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; + +import java.io.IOException; +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; + +/** + * @author tanghc + */ +@Slf4j +@Service +public class ConfigPushService { + + private static final String GATEWAY_PUSH_URL = "http://%s/sop/configChannelMsg"; + private static final String API_GATEWAY_SERVICE_ID = "sop-gateway"; + + private static HttpTool httpTool = new HttpTool(); + + @Value("${sop.secret:MZZOUSTua6LzApIWXCwEgbBmxSzpzC}") + private String secret; + + @Autowired + private ServerService serverService; + + public void publishConfig(String dataId, String groupId, ChannelMsg channelMsg) { + GatewayPushDTO gatewayPushDTO = new GatewayPushDTO(dataId, groupId, channelMsg); + // 获取网关host,ip:port + Collection hostList = serverService.listServerHost(API_GATEWAY_SERVICE_ID); + this.pushByHost(hostList, gatewayPushDTO); + } + + private void pushByHost(Collection hosts, GatewayPushDTO gatewayPushDTO) { + for (String host : hosts) { + String url = String.format(GATEWAY_PUSH_URL, host); + log.info("推送配置, dataId={}, groupId={}, operation={}, url={}", + gatewayPushDTO.getDataId() + , gatewayPushDTO.getGroupId() + , gatewayPushDTO.getChannelMsg().getOperation() + , url); + try { + String requestBody = JSON.toJSONString(gatewayPushDTO); + Map header = new HashMap<>(8); + header.put("sign", buildRequestBodySign(requestBody, secret)); + String resp = httpTool.requestJson(url, requestBody, header); + if (!"ok".equals(resp)) { + throw new IOException(resp); + } + } catch (IOException e) { + log.error("nacos配置失败, dataId={}, groupId={}, operation={}, url={}", + gatewayPushDTO.getDataId() + , gatewayPushDTO.getGroupId() + , gatewayPushDTO.getChannelMsg().getOperation() + , url + , e); + throw new RuntimeException("推送配置失败"); + } + } + } + + public static String buildRequestBodySign(String requestBody, String secret) { + String signContent = secret + requestBody + secret; + return DigestUtils.md5Hex(signContent); + } + +} diff --git a/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/service/ServerService.java b/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/service/ServerService.java new file mode 100644 index 00000000..b760f07a --- /dev/null +++ b/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/service/ServerService.java @@ -0,0 +1,15 @@ +package com.gitee.sop.websiteserver.service; + +import java.util.List; + +/** + * @author tanghc + */ +public interface ServerService { + /** + * 获取服务host,ip:port + * @param serviceId serviceId + * @return 返回host列表 + */ + List listServerHost(String serviceId); +} diff --git a/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/service/UserService.java b/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/service/UserService.java new file mode 100644 index 00000000..37b4c0fc --- /dev/null +++ b/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/service/UserService.java @@ -0,0 +1,149 @@ +package com.gitee.sop.websiteserver.service; + +import com.gitee.fastmybatis.core.query.Query; +import com.gitee.sop.gatewaycommon.bean.NacosConfigs; +import com.gitee.sop.gatewaycommon.util.CopyUtil; +import com.gitee.sop.websiteserver.bean.ChannelMsg; +import com.gitee.sop.websiteserver.bean.ChannelOperation; +import com.gitee.sop.websiteserver.bean.IdGen; +import com.gitee.sop.websiteserver.bean.IsvDetailDTO; +import com.gitee.sop.websiteserver.bean.IsvKeysGenVO; +import com.gitee.sop.websiteserver.bean.LoginUser; +import com.gitee.sop.websiteserver.bean.RSATool; +import com.gitee.sop.websiteserver.controller.param.RegParam; +import com.gitee.sop.websiteserver.entity.IsvInfo; +import com.gitee.sop.websiteserver.entity.IsvKeys; +import com.gitee.sop.websiteserver.entity.IsvPortal; +import com.gitee.sop.websiteserver.entity.UserAccount; +import com.gitee.sop.websiteserver.mapper.IsvInfoMapper; +import com.gitee.sop.websiteserver.mapper.IsvKeysMapper; +import com.gitee.sop.websiteserver.mapper.UserAccountMapper; +import com.gitee.sop.websiteserver.util.GenerateUtil; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.text.SimpleDateFormat; +import java.util.Date; + +/** + * @author tanghc + */ +@Service +@Slf4j +public class UserService { + + public static final byte STATUS_ENABLE = 1; + + @Autowired + private UserAccountMapper userAccountMapper; + + @Autowired + private IsvInfoMapper isvInfoMapper; + + @Autowired + private IsvKeysMapper isvKeysMapper; + + @Autowired + private ConfigPushService configPushService; + + public UserAccount getUserAccountByUsername(String username) { + return userAccountMapper.getByColumn("username", username); + } + + public UserAccount getUserAccountByUserId(long userId) { + return userAccountMapper.getByColumn("id", userId); + } + + public void updateUserAccount(UserAccount userAccount) { + userAccountMapper.updateIgnoreNull(userAccount); + } + + public UserAccount getUserInfoAppKey(String appKey) { + return userAccountMapper.getByColumn("app_key", appKey); + } + + public IsvPortal getIsvPortal(long userId) { + return isvInfoMapper.getIsvPortal(userId); + } + + public IsvKeys getIsvKeys(String appKey) { + return isvKeysMapper.getByColumn("app_key", appKey); + } + + public void saveIsvKey(IsvKeys isvKeys) { + isvKeysMapper.saveIgnoreNull(isvKeys); + } + + /** + * 保存用户秘钥 + * @param isvKeys + */ + public void updateIsvKey(IsvKeys isvKeys) { + isvKeysMapper.updateIgnoreNull(isvKeys); + this.sendChannelMsg(isvKeys.getAppKey()); + } + + private void sendChannelMsg(String appKey) { + IsvDetailDTO isvDetail = isvInfoMapper.getIsvDetail(appKey); + if (isvDetail == null) { + return; + } + ChannelMsg channelMsg = new ChannelMsg(ChannelOperation.ISV_INFO_UPDATE, isvDetail); + configPushService.publishConfig(NacosConfigs.DATA_ID_ISV, NacosConfigs.GROUP_CHANNEL, channelMsg); + } + + public LoginUser getLoginUser(String username, String password) { + return isvInfoMapper.getLoginUser(username, password); + } + + public UserAccount saveUser(RegParam param) { + UserAccount userInfo = new UserAccount(); + String password = GenerateUtil.getUserPassword(param.getUsername(), param.getPassword()); + userInfo.setUsername(param.getUsername()); + userInfo.setPassword(password); + userInfo.setStatus(STATUS_ENABLE); + userAccountMapper.saveIgnoreNull(userInfo); + return userInfo; + } + + public void createIsv(UserAccount userInfo) { + String appKey = new SimpleDateFormat("yyyyMMdd").format(new Date()) + IdGen.nextId(); + IsvInfo rec = new IsvInfo(); + rec.setAppKey(appKey); + rec.setUserId(userInfo.getId()); + rec.setStatus(STATUS_ENABLE); + isvInfoMapper.saveIgnoreNull(rec); +// IsvKeysGenVO isvKeysGenVO = null; +// try { +// isvKeysGenVO = this.createIsvKeys(); +// } catch (Exception e) { +// throw new RuntimeException("创建ISV秘钥失败", e); +// } +// IsvKeys isvKeys = new IsvKeys(); +// isvKeys.setAppKey(appKey); +// isvKeys.setSignType(SIGN_TYPE_RSA); +// CopyUtil.copyPropertiesIgnoreNull(isvKeysGenVO, isvKeys); +// isvKeysMapper.saveIgnoreNull(isvKeys); + +// this.sendChannelMsg(rec.getAppKey()); + } + + private IsvKeysGenVO createIsvKeys() throws Exception { + IsvKeysGenVO isvFormVO = new IsvKeysGenVO(); + String secret = IdGen.uuid(); + + isvFormVO.setSecret(secret); + + RSATool rsaToolIsv = new RSATool(RSATool.KeyFormat.PKCS8, RSATool.KeyLength.LENGTH_2048); + RSATool.KeyStore keyStoreIsv = rsaToolIsv.createKeys(); + isvFormVO.setPublicKeyIsv(keyStoreIsv.getPublicKey()); + isvFormVO.setPrivateKeyIsv(keyStoreIsv.getPrivateKey()); + + isvFormVO.setPublicKeyPlatform(""); + isvFormVO.setPrivateKeyPlatform(""); + return isvFormVO; + } + + +} diff --git a/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/service/impl/EurekaServerService.java b/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/service/impl/EurekaServerService.java new file mode 100644 index 00000000..03a67d2a --- /dev/null +++ b/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/service/impl/EurekaServerService.java @@ -0,0 +1,70 @@ +package com.gitee.sop.websiteserver.service.impl; + +import com.alibaba.fastjson.JSON; +import com.gitee.sop.websiteserver.bean.EurekaApplication; +import com.gitee.sop.websiteserver.bean.EurekaApps; +import com.gitee.sop.websiteserver.bean.EurekaInstance; +import com.gitee.sop.websiteserver.bean.EurekaUri; +import com.gitee.sop.websiteserver.service.ServerService; +import lombok.extern.slf4j.Slf4j; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.Response; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Value; + +import java.io.IOException; +import java.util.Collections; +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; + +/** + * @author tanghc + */ +@Slf4j +public class EurekaServerService implements ServerService { + + private OkHttpClient client = new OkHttpClient(); + + @Value("${eureka.client.serviceUrl.defaultZone:}") + private String eurekaUrl; + + @Override + public List listServerHost(String serviceId) { + if (StringUtils.isBlank(eurekaUrl)) { + throw new IllegalArgumentException("未指定eureka.client.serviceUrl.defaultZone参数"); + } + Objects.requireNonNull(serviceId, "serviceId can not be null"); + String json = null; + try { + json = this.requestEurekaServer(EurekaUri.QUERY_APPS); + } catch (IOException e) { + return Collections.emptyList(); + } + EurekaApps eurekaApps = JSON.parseObject(json, EurekaApps.class); + + List applicationList = eurekaApps.getApplications().getApplication(); + for (EurekaApplication eurekaApplication : applicationList) { + if (!serviceId.equalsIgnoreCase(eurekaApplication.getName())) { + continue; + } + List instanceList = eurekaApplication.getInstance(); + return instanceList.stream() + .map(eurekaInstance -> eurekaInstance.getIpAddr() + ":" + eurekaInstance.getPort()) + .collect(Collectors.toList()); + } + return Collections.emptyList(); + } + + private String requestEurekaServer(EurekaUri eurekaUri, String... args) throws IOException { + Request request = eurekaUri.getRequest(this.eurekaUrl, args); + Response response = client.newCall(request).execute(); + if (response.isSuccessful()) { + return response.body().string(); + } else { + log.error("操作失败,url:{}, msg:{}, code:{}", eurekaUri.getUri(args), response.message(), response.code()); + throw new RuntimeException("操作失败"); + } + } +} diff --git a/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/service/impl/NacosServerService.java b/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/service/impl/NacosServerService.java new file mode 100644 index 00000000..c2a8c8ce --- /dev/null +++ b/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/service/impl/NacosServerService.java @@ -0,0 +1,62 @@ +package com.gitee.sop.websiteserver.service.impl; + +import com.alibaba.cloud.nacos.NacosDiscoveryProperties; +import com.alibaba.nacos.api.PropertyKeyConst; +import com.alibaba.nacos.api.exception.NacosException; +import com.alibaba.nacos.api.naming.NamingFactory; +import com.alibaba.nacos.api.naming.NamingService; +import com.alibaba.nacos.api.naming.pojo.Instance; +import com.gitee.sop.websiteserver.service.ServerService; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; + +import javax.annotation.PostConstruct; +import java.util.Collections; +import java.util.List; +import java.util.Objects; +import java.util.Properties; +import java.util.stream.Collectors; + +/** + * @author tanghc + */ +@Slf4j +public class NacosServerService implements ServerService { + + @Value("${nacos.discovery.server-addr:${spring.cloud.nacos.discovery.server-addr:}}") + private String nacosAddr; + + @Value("${nacos.discovery.namespace:${spring.cloud.nacos.discovery.namespace:}}") + private String nacosNamespace; + + private NamingService namingService; + + @PostConstruct + public void after() throws NacosException { + if (StringUtils.isBlank(nacosAddr)) { + throw new IllegalArgumentException("请在配置文件中指定nacos.discovery.server-addr参数"); + } + Properties nacosProperties = new Properties(); + nacosProperties.put(PropertyKeyConst.SERVER_ADDR, nacosAddr); + if (StringUtils.isNotBlank(nacosNamespace)) { + nacosProperties.put(PropertyKeyConst.NAMESPACE, nacosNamespace); + } + namingService = NamingFactory.createNamingService(nacosProperties); + } + + @Override + public List listServerHost(String serviceId) { + Objects.requireNonNull(serviceId, "serviceId can not be null"); + List list = Collections.emptyList(); + try { + List allInstances = namingService.getAllInstances(serviceId); + list = allInstances.stream().map(instance -> instance.getIp() + ":" + instance.getPort()) + .collect(Collectors.toList()); + } catch (NacosException e) { + log.error("获取实例host出错, serviceId:{}", serviceId, e); + } + return list; + } +} diff --git a/sop-website/src/main/java/com/gitee/sop/websiteserver/sign/AlipayApiException.java b/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/sign/AlipayApiException.java similarity index 100% rename from sop-website/src/main/java/com/gitee/sop/websiteserver/sign/AlipayApiException.java rename to sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/sign/AlipayApiException.java diff --git a/sop-website/src/main/java/com/gitee/sop/websiteserver/sign/AlipayConstants.java b/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/sign/AlipayConstants.java similarity index 100% rename from sop-website/src/main/java/com/gitee/sop/websiteserver/sign/AlipayConstants.java rename to sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/sign/AlipayConstants.java diff --git a/sop-website/src/main/java/com/gitee/sop/websiteserver/sign/AlipaySignature.java b/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/sign/AlipaySignature.java similarity index 100% rename from sop-website/src/main/java/com/gitee/sop/websiteserver/sign/AlipaySignature.java rename to sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/sign/AlipaySignature.java diff --git a/sop-website/src/main/java/com/gitee/sop/websiteserver/sign/StreamUtil.java b/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/sign/StreamUtil.java similarity index 100% rename from sop-website/src/main/java/com/gitee/sop/websiteserver/sign/StreamUtil.java rename to sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/sign/StreamUtil.java diff --git a/sop-website/src/main/java/com/gitee/sop/websiteserver/sign/StringUtils.java b/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/sign/StringUtils.java similarity index 100% rename from sop-website/src/main/java/com/gitee/sop/websiteserver/sign/StringUtils.java rename to sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/sign/StringUtils.java diff --git a/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/util/GenerateUtil.java b/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/util/GenerateUtil.java new file mode 100644 index 00000000..755eef45 --- /dev/null +++ b/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/util/GenerateUtil.java @@ -0,0 +1,56 @@ +package com.gitee.sop.websiteserver.util; + +import com.gitee.sop.websiteserver.bean.SnowflakeIdWorker; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.codec.digest.DigestUtils; +import org.springframework.util.Base64Utils; + +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.UUID; + +/** + * @author tanghc + */ +@Slf4j +public class GenerateUtil { + + private static long workerId = 0; + private static SnowflakeIdWorker appIdWorker = new SnowflakeIdWorker(workerId++, 0); + private static SnowflakeIdWorker secretWorker = new SnowflakeIdWorker(workerId++, 0); + private static SnowflakeIdWorker fileWorker = new SnowflakeIdWorker(workerId++, 0); + private static SnowflakeIdWorker orderNoWorker = new SnowflakeIdWorker(workerId++, 1); + + public static String generateServiceId() { + return getUUID(); + } + + public static String generateIsvAppId() { + return LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMdd")) + appIdWorker.nextId(); + } + + public static String generateNamespace() { + return RandomUtil.createRandom(); + } + + public static String generateSecret() { + byte[] bytes = DigestUtils.sha256(secretWorker.nextId() + getUUID()); + return Base64Utils.encodeToUrlSafeString(bytes).substring(1).replace("=", ""); + } + + public static String getUserPassword(String username, String password) { + return DigestUtils.sha256Hex(username + password + username); + } + + public static String generateVerifyFile() { + return DigestUtils.md5Hex(String.valueOf(fileWorker.nextId())); + } + + public static String getUUID() { + return UUID.randomUUID().toString().replace("-", ""); + } + + public static String generateOrderNo() { + return String.valueOf(orderNoWorker.nextId()); + } +} diff --git a/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/util/RandomUtil.java b/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/util/RandomUtil.java new file mode 100644 index 00000000..1b5908a9 --- /dev/null +++ b/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/util/RandomUtil.java @@ -0,0 +1,194 @@ +package com.gitee.sop.websiteserver.util; + +import java.util.Random; + +/** + * @author tanghc + */ +public class RandomUtil { + + /** + * 创建密码 + * + * @return 返回8位字母+数组密码 + */ + public static String createRandom() { + return new Password().getRandomPassword(); + } + + /** + * 只随机数字 + * + * @return + */ + public static String randomNumber() { + return new Password(Password.StrengthLevel.ONLY_NUMBER, 10).getRandomPassword(); + } + + /** + * 创建密码,指定强度和长度 + * + * @param strengthLevel 强度等级 + * @param length 长度 + * @return 返回密码 + */ + public static String createRandom(Password.StrengthLevel strengthLevel, int length) { + return new Password(strengthLevel, length).getRandomPassword(); + } + + public static class Password { + + + public Password() { + this(StrengthLevel.NUMBER_LOWERCASE, 8); + } + + /** + * @param strengthLevel 强度等级 + * @param length 长度 + */ + public Password(StrengthLevel strengthLevel, int length) { + if (length <= 0) { + throw new IllegalArgumentException("length can not <= 0"); + } + this.strengthLevel = strengthLevel; + this.length = length; + } + + /** + * 强度等级 + */ + private final StrengthLevel strengthLevel; + + /** + * 需要的密码长度 + */ + private final int length; + + private static final Random RANDOM = new Random(); + + /** + * 因为伪随机数random在nextInt()的时候会出现向更小的数偏离的情况,所以用一个randomMax来修正 + */ + private static final int RANDOM_MAX = 100000000; + /** + * 因为数组下标从0开始 + */ + private static final int INDEX = 1; + /** + * 一共26个字母 + */ + private static final int NUMBER_OF_LETTER = 26; + /** + * ascii码表数字从48开始 + */ + private static final int NUMBER_INDEX = 48; + /** + * 数组下标0-9代表10个数字 + */ + private static final int NUMBER_MAX = 9; + /** + * 大写字母ascii码表从65开始 + */ + private static final int CAPITAL_INDEX = 65; + /** + * 小写字母ascii码表从65开始 + */ + private static final int LOWERCASE_INDEX = 97; + /** + * 特殊字符的起始ascii码表序号取第一个数字 + */ + private static final int SPECIAL = 0; + /** + * 特殊字符集,第一个数字代表ascii码表序号,第二个代表从这里开始一共使用多少个字符 + */ + private static final int[][] SPECIAL_CHARACTER = {{33, 14}, {58, 6}, {91, 5}, {123, 3}}; + + /** + * 取随机密码 + * + * @return 返回随机密码 + */ + public String getRandomPassword() { + StringBuilder builder = new StringBuilder(); + for (int i = 0; i < length; i++) { + builder.append((char) getNextChar()); + } + return builder.toString(); + } + + /** + * 取得下一个ascii编码 + * + * @return 返回ascii码 + */ + private int getNextChar() { + // ascii编码 + int x = 0; + // 伪字符ascii编码 + final int puppetLetter = RANDOM.nextInt(RANDOM_MAX) % NUMBER_OF_LETTER; + // 伪数字ascii编码 + final int puppetNumber = RANDOM.nextInt(RANDOM_MAX) % NUMBER_MAX + NUMBER_INDEX; + // 按照字符等级强度取ascii值 + final int levelIndex = RANDOM.nextInt(RANDOM_MAX) % strengthLevel.level; + // 特殊字符的随机集合下标(数组第一维) + final int specialType = RANDOM.nextInt(RANDOM_MAX) % SPECIAL_CHARACTER.length; + // 特殊字符的ascii编码 + final int specialInt = RANDOM.nextInt(RANDOM_MAX) % SPECIAL_CHARACTER[specialType][INDEX] + SPECIAL_CHARACTER[specialType][SPECIAL]; + // 根据密码强度等级获取随机ascii编码 + switch (strengthLevel) { + case ONLY_NUMBER: + x = puppetNumber; + break; + case NUMBER_LOWERCASE: + x = levelIndex == INDEX ? puppetNumber : puppetLetter + LOWERCASE_INDEX; + break; + case NUMBER_LOWERCASE_CAPITAL: + if (levelIndex == 0) { + x = puppetNumber; + } else if (levelIndex == INDEX) { + x = puppetLetter + LOWERCASE_INDEX; + } else { + x = puppetLetter + CAPITAL_INDEX; + } + break; + case NUMBER_LOWERCASE_CAPITAL_CHARACTER: + if (levelIndex == 0) { + x = puppetNumber; + } else if (levelIndex == INDEX) { + x = puppetLetter + LOWERCASE_INDEX; + } else if (levelIndex == INDEX * 2) { + x = puppetLetter + CAPITAL_INDEX; + } else { + x = specialInt; + } + break; + default: + } + return x; + } + + /** + * Password level config + * 1 level : only number + * 2 level : number and lowercase letters + * 3 level : number , lowercase letters , capital letters + * 4 level : number , lowercase letters , capital letters , special characters + */ + public enum StrengthLevel { + ONLY_NUMBER(1), + NUMBER_LOWERCASE(2), + NUMBER_LOWERCASE_CAPITAL(3), + NUMBER_LOWERCASE_CAPITAL_CHARACTER(4), + ; + + StrengthLevel(int level) { + this.level = level; + } + + private int level; + + } + } + +} diff --git a/sop-website/src/main/java/com/gitee/sop/websiteserver/util/UploadUtil.java b/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/util/UploadUtil.java similarity index 100% rename from sop-website/src/main/java/com/gitee/sop/websiteserver/util/UploadUtil.java rename to sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/util/UploadUtil.java diff --git a/sop-website/src/main/java/com/gitee/sop/websiteserver/vo/DocBaseInfoVO.java b/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/vo/DocBaseInfoVO.java similarity index 100% rename from sop-website/src/main/java/com/gitee/sop/websiteserver/vo/DocBaseInfoVO.java rename to sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/vo/DocBaseInfoVO.java diff --git a/sop-website/src/main/java/com/gitee/sop/websiteserver/vo/DocInfoVO.java b/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/vo/DocInfoVO.java similarity index 100% rename from sop-website/src/main/java/com/gitee/sop/websiteserver/vo/DocInfoVO.java rename to sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/vo/DocInfoVO.java diff --git a/sop-website/src/main/java/com/gitee/sop/websiteserver/vo/ServiceInfoVO.java b/sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/vo/ServiceInfoVO.java similarity index 100% rename from sop-website/src/main/java/com/gitee/sop/websiteserver/vo/ServiceInfoVO.java rename to sop-website/sop-website-server/src/main/java/com/gitee/sop/websiteserver/vo/ServiceInfoVO.java diff --git a/sop-website/src/main/resources/META-INF/spring.factories b/sop-website/sop-website-server/src/main/resources/META-INF/spring.factories similarity index 100% rename from sop-website/src/main/resources/META-INF/spring.factories rename to sop-website/sop-website-server/src/main/resources/META-INF/spring.factories diff --git a/sop-website/sop-website-server/src/main/resources/application-dev.properties b/sop-website/sop-website-server/src/main/resources/application-dev.properties new file mode 100644 index 00000000..1b15a8d4 --- /dev/null +++ b/sop-website/sop-website-server/src/main/resources/application-dev.properties @@ -0,0 +1,40 @@ +server.port=8083 +spring.application.name=sop-website + +# ------- 需要改的配置 ------- + +# mysql数据库账号 +mysql.host=localhost:3306 +mysql.username=root +mysql.password=root +# 注册中心地址 +register.url=127.0.0.1:8848 + +# 网关内网地址 +# !!沙箱环境是提供给外部的测试环境,不能把正式环境当做沙箱环境来使用。 +gateway.url=http://localhost:8081 + +logging.level.com.gitee=debug +# ------- 需要改的配置end ------- + +## nacos cloud配置 +spring.cloud.nacos.discovery.server-addr=${register.url} + +# 页面上显示的测试环境地址 +api.url-test=http://open-test.yourdomain.com +# 沙箱环境 +api.url-sandbox=${gateway.url} +# 页面上显示的正式环境地址 +api.url-prod=http://open.yourdomain.com +api.pwd=doc#123 + +# 数据库配置 +spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver +spring.datasource.url=jdbc:mysql://${mysql.host}/sop?useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&serverTimezone=Asia/Shanghai +spring.datasource.username=${mysql.username} +spring.datasource.password=${mysql.password} + +mybatis.base-package=com.gitee.sop.websiteserver +mybatis.mapper-locations=classpath:mybatis/mapper/*.xml +mybatis.fill.com.gitee.fastmybatis.core.support.DateFillInsert=gmt_create +mybatis.fill.com.gitee.fastmybatis.core.support.DateFillUpdate=gmt_modified diff --git a/sop-website/src/main/resources/application.properties b/sop-website/sop-website-server/src/main/resources/application.properties similarity index 100% rename from sop-website/src/main/resources/application.properties rename to sop-website/sop-website-server/src/main/resources/application.properties diff --git a/sop-website/src/main/resources/public/assets/css/global.css b/sop-website/sop-website-server/src/main/resources/public/assets/css/global.css similarity index 100% rename from sop-website/src/main/resources/public/assets/css/global.css rename to sop-website/sop-website-server/src/main/resources/public/assets/css/global.css diff --git a/sop-website/src/main/resources/public/assets/js/format.js b/sop-website/sop-website-server/src/main/resources/public/assets/js/format.js similarity index 100% rename from sop-website/src/main/resources/public/assets/js/format.js rename to sop-website/sop-website-server/src/main/resources/public/assets/js/format.js diff --git a/sop-website/src/main/resources/public/assets/js/inputcache.js b/sop-website/sop-website-server/src/main/resources/public/assets/js/inputcache.js similarity index 100% rename from sop-website/src/main/resources/public/assets/js/inputcache.js rename to sop-website/sop-website-server/src/main/resources/public/assets/js/inputcache.js diff --git a/sop-website/src/main/resources/public/assets/lib/jquery/3.2.1/jquery.min.js b/sop-website/sop-website-server/src/main/resources/public/assets/lib/jquery/3.2.1/jquery.min.js similarity index 100% rename from sop-website/src/main/resources/public/assets/lib/jquery/3.2.1/jquery.min.js rename to sop-website/sop-website-server/src/main/resources/public/assets/lib/jquery/3.2.1/jquery.min.js diff --git a/sop-website/src/main/resources/public/assets/lib/layui/css/layui.css b/sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/css/layui.css similarity index 100% rename from sop-website/src/main/resources/public/assets/lib/layui/css/layui.css rename to sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/css/layui.css diff --git a/sop-website/src/main/resources/public/assets/lib/layui/css/layui.mobile.css b/sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/css/layui.mobile.css similarity index 100% rename from sop-website/src/main/resources/public/assets/lib/layui/css/layui.mobile.css rename to sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/css/layui.mobile.css diff --git a/sop-website/src/main/resources/public/assets/lib/layui/css/modules/code.css b/sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/css/modules/code.css similarity index 100% rename from sop-website/src/main/resources/public/assets/lib/layui/css/modules/code.css rename to sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/css/modules/code.css diff --git a/sop-website/src/main/resources/public/assets/lib/layui/css/modules/laydate/default/laydate.css b/sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/css/modules/laydate/default/laydate.css similarity index 100% rename from sop-website/src/main/resources/public/assets/lib/layui/css/modules/laydate/default/laydate.css rename to sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/css/modules/laydate/default/laydate.css diff --git a/sop-website/src/main/resources/public/assets/lib/layui/css/modules/layer/default/layer.css b/sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/css/modules/layer/default/layer.css similarity index 100% rename from sop-website/src/main/resources/public/assets/lib/layui/css/modules/layer/default/layer.css rename to sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/css/modules/layer/default/layer.css diff --git a/sop-website/src/main/resources/public/assets/lib/layui/css/modules/layer/default/loading-0.gif b/sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/css/modules/layer/default/loading-0.gif similarity index 100% rename from sop-website/src/main/resources/public/assets/lib/layui/css/modules/layer/default/loading-0.gif rename to sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/css/modules/layer/default/loading-0.gif diff --git a/sop-website/src/main/resources/public/assets/lib/layui/css/modules/layer/default/loading-1.gif b/sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/css/modules/layer/default/loading-1.gif similarity index 100% rename from sop-website/src/main/resources/public/assets/lib/layui/css/modules/layer/default/loading-1.gif rename to sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/css/modules/layer/default/loading-1.gif diff --git a/sop-website/src/main/resources/public/assets/lib/layui/css/modules/layer/default/loading-2.gif b/sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/css/modules/layer/default/loading-2.gif similarity index 100% rename from sop-website/src/main/resources/public/assets/lib/layui/css/modules/layer/default/loading-2.gif rename to sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/css/modules/layer/default/loading-2.gif diff --git a/sop-website/src/main/resources/public/assets/lib/layui/font/iconfont.eot b/sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/font/iconfont.eot similarity index 100% rename from sop-website/src/main/resources/public/assets/lib/layui/font/iconfont.eot rename to sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/font/iconfont.eot diff --git a/sop-website/src/main/resources/public/assets/lib/layui/font/iconfont.svg b/sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/font/iconfont.svg similarity index 100% rename from sop-website/src/main/resources/public/assets/lib/layui/font/iconfont.svg rename to sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/font/iconfont.svg diff --git a/sop-website/src/main/resources/public/assets/lib/layui/font/iconfont.ttf b/sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/font/iconfont.ttf similarity index 100% rename from sop-website/src/main/resources/public/assets/lib/layui/font/iconfont.ttf rename to sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/font/iconfont.ttf diff --git a/sop-website/src/main/resources/public/assets/lib/layui/font/iconfont.woff b/sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/font/iconfont.woff similarity index 100% rename from sop-website/src/main/resources/public/assets/lib/layui/font/iconfont.woff rename to sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/font/iconfont.woff diff --git a/sop-website/src/main/resources/public/assets/lib/layui/images/face/0.gif b/sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/images/face/0.gif similarity index 100% rename from sop-website/src/main/resources/public/assets/lib/layui/images/face/0.gif rename to sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/images/face/0.gif diff --git a/sop-website/src/main/resources/public/assets/lib/layui/images/face/1.gif b/sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/images/face/1.gif similarity index 100% rename from sop-website/src/main/resources/public/assets/lib/layui/images/face/1.gif rename to sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/images/face/1.gif diff --git a/sop-website/src/main/resources/public/assets/lib/layui/images/face/10.gif b/sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/images/face/10.gif similarity index 100% rename from sop-website/src/main/resources/public/assets/lib/layui/images/face/10.gif rename to sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/images/face/10.gif diff --git a/sop-website/src/main/resources/public/assets/lib/layui/images/face/11.gif b/sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/images/face/11.gif similarity index 100% rename from sop-website/src/main/resources/public/assets/lib/layui/images/face/11.gif rename to sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/images/face/11.gif diff --git a/sop-website/src/main/resources/public/assets/lib/layui/images/face/12.gif b/sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/images/face/12.gif similarity index 100% rename from sop-website/src/main/resources/public/assets/lib/layui/images/face/12.gif rename to sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/images/face/12.gif diff --git a/sop-website/src/main/resources/public/assets/lib/layui/images/face/13.gif b/sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/images/face/13.gif similarity index 100% rename from sop-website/src/main/resources/public/assets/lib/layui/images/face/13.gif rename to sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/images/face/13.gif diff --git a/sop-website/src/main/resources/public/assets/lib/layui/images/face/14.gif b/sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/images/face/14.gif similarity index 100% rename from sop-website/src/main/resources/public/assets/lib/layui/images/face/14.gif rename to sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/images/face/14.gif diff --git a/sop-website/src/main/resources/public/assets/lib/layui/images/face/15.gif b/sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/images/face/15.gif similarity index 100% rename from sop-website/src/main/resources/public/assets/lib/layui/images/face/15.gif rename to sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/images/face/15.gif diff --git a/sop-website/src/main/resources/public/assets/lib/layui/images/face/16.gif b/sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/images/face/16.gif similarity index 100% rename from sop-website/src/main/resources/public/assets/lib/layui/images/face/16.gif rename to sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/images/face/16.gif diff --git a/sop-website/src/main/resources/public/assets/lib/layui/images/face/17.gif b/sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/images/face/17.gif similarity index 100% rename from sop-website/src/main/resources/public/assets/lib/layui/images/face/17.gif rename to sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/images/face/17.gif diff --git a/sop-website/src/main/resources/public/assets/lib/layui/images/face/18.gif b/sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/images/face/18.gif similarity index 100% rename from sop-website/src/main/resources/public/assets/lib/layui/images/face/18.gif rename to sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/images/face/18.gif diff --git a/sop-website/src/main/resources/public/assets/lib/layui/images/face/19.gif b/sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/images/face/19.gif similarity index 100% rename from sop-website/src/main/resources/public/assets/lib/layui/images/face/19.gif rename to sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/images/face/19.gif diff --git a/sop-website/src/main/resources/public/assets/lib/layui/images/face/2.gif b/sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/images/face/2.gif similarity index 100% rename from sop-website/src/main/resources/public/assets/lib/layui/images/face/2.gif rename to sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/images/face/2.gif diff --git a/sop-website/src/main/resources/public/assets/lib/layui/images/face/20.gif b/sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/images/face/20.gif similarity index 100% rename from sop-website/src/main/resources/public/assets/lib/layui/images/face/20.gif rename to sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/images/face/20.gif diff --git a/sop-website/src/main/resources/public/assets/lib/layui/images/face/21.gif b/sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/images/face/21.gif similarity index 100% rename from sop-website/src/main/resources/public/assets/lib/layui/images/face/21.gif rename to sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/images/face/21.gif diff --git a/sop-website/src/main/resources/public/assets/lib/layui/images/face/22.gif b/sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/images/face/22.gif similarity index 100% rename from sop-website/src/main/resources/public/assets/lib/layui/images/face/22.gif rename to sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/images/face/22.gif diff --git a/sop-website/src/main/resources/public/assets/lib/layui/images/face/23.gif b/sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/images/face/23.gif similarity index 100% rename from sop-website/src/main/resources/public/assets/lib/layui/images/face/23.gif rename to sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/images/face/23.gif diff --git a/sop-website/src/main/resources/public/assets/lib/layui/images/face/24.gif b/sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/images/face/24.gif similarity index 100% rename from sop-website/src/main/resources/public/assets/lib/layui/images/face/24.gif rename to sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/images/face/24.gif diff --git a/sop-website/src/main/resources/public/assets/lib/layui/images/face/25.gif b/sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/images/face/25.gif similarity index 100% rename from sop-website/src/main/resources/public/assets/lib/layui/images/face/25.gif rename to sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/images/face/25.gif diff --git a/sop-website/src/main/resources/public/assets/lib/layui/images/face/26.gif b/sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/images/face/26.gif similarity index 100% rename from sop-website/src/main/resources/public/assets/lib/layui/images/face/26.gif rename to sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/images/face/26.gif diff --git a/sop-website/src/main/resources/public/assets/lib/layui/images/face/27.gif b/sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/images/face/27.gif similarity index 100% rename from sop-website/src/main/resources/public/assets/lib/layui/images/face/27.gif rename to sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/images/face/27.gif diff --git a/sop-website/src/main/resources/public/assets/lib/layui/images/face/28.gif b/sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/images/face/28.gif similarity index 100% rename from sop-website/src/main/resources/public/assets/lib/layui/images/face/28.gif rename to sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/images/face/28.gif diff --git a/sop-website/src/main/resources/public/assets/lib/layui/images/face/29.gif b/sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/images/face/29.gif similarity index 100% rename from sop-website/src/main/resources/public/assets/lib/layui/images/face/29.gif rename to sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/images/face/29.gif diff --git a/sop-website/src/main/resources/public/assets/lib/layui/images/face/3.gif b/sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/images/face/3.gif similarity index 100% rename from sop-website/src/main/resources/public/assets/lib/layui/images/face/3.gif rename to sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/images/face/3.gif diff --git a/sop-website/src/main/resources/public/assets/lib/layui/images/face/30.gif b/sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/images/face/30.gif similarity index 100% rename from sop-website/src/main/resources/public/assets/lib/layui/images/face/30.gif rename to sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/images/face/30.gif diff --git a/sop-website/src/main/resources/public/assets/lib/layui/images/face/31.gif b/sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/images/face/31.gif similarity index 100% rename from sop-website/src/main/resources/public/assets/lib/layui/images/face/31.gif rename to sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/images/face/31.gif diff --git a/sop-website/src/main/resources/public/assets/lib/layui/images/face/32.gif b/sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/images/face/32.gif similarity index 100% rename from sop-website/src/main/resources/public/assets/lib/layui/images/face/32.gif rename to sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/images/face/32.gif diff --git a/sop-website/src/main/resources/public/assets/lib/layui/images/face/33.gif b/sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/images/face/33.gif similarity index 100% rename from sop-website/src/main/resources/public/assets/lib/layui/images/face/33.gif rename to sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/images/face/33.gif diff --git a/sop-website/src/main/resources/public/assets/lib/layui/images/face/34.gif b/sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/images/face/34.gif similarity index 100% rename from sop-website/src/main/resources/public/assets/lib/layui/images/face/34.gif rename to sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/images/face/34.gif diff --git a/sop-website/src/main/resources/public/assets/lib/layui/images/face/35.gif b/sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/images/face/35.gif similarity index 100% rename from sop-website/src/main/resources/public/assets/lib/layui/images/face/35.gif rename to sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/images/face/35.gif diff --git a/sop-website/src/main/resources/public/assets/lib/layui/images/face/36.gif b/sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/images/face/36.gif similarity index 100% rename from sop-website/src/main/resources/public/assets/lib/layui/images/face/36.gif rename to sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/images/face/36.gif diff --git a/sop-website/src/main/resources/public/assets/lib/layui/images/face/37.gif b/sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/images/face/37.gif similarity index 100% rename from sop-website/src/main/resources/public/assets/lib/layui/images/face/37.gif rename to sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/images/face/37.gif diff --git a/sop-website/src/main/resources/public/assets/lib/layui/images/face/38.gif b/sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/images/face/38.gif similarity index 100% rename from sop-website/src/main/resources/public/assets/lib/layui/images/face/38.gif rename to sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/images/face/38.gif diff --git a/sop-website/src/main/resources/public/assets/lib/layui/images/face/39.gif b/sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/images/face/39.gif similarity index 100% rename from sop-website/src/main/resources/public/assets/lib/layui/images/face/39.gif rename to sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/images/face/39.gif diff --git a/sop-website/src/main/resources/public/assets/lib/layui/images/face/4.gif b/sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/images/face/4.gif similarity index 100% rename from sop-website/src/main/resources/public/assets/lib/layui/images/face/4.gif rename to sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/images/face/4.gif diff --git a/sop-website/src/main/resources/public/assets/lib/layui/images/face/40.gif b/sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/images/face/40.gif similarity index 100% rename from sop-website/src/main/resources/public/assets/lib/layui/images/face/40.gif rename to sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/images/face/40.gif diff --git a/sop-website/src/main/resources/public/assets/lib/layui/images/face/41.gif b/sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/images/face/41.gif similarity index 100% rename from sop-website/src/main/resources/public/assets/lib/layui/images/face/41.gif rename to sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/images/face/41.gif diff --git a/sop-website/src/main/resources/public/assets/lib/layui/images/face/42.gif b/sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/images/face/42.gif similarity index 100% rename from sop-website/src/main/resources/public/assets/lib/layui/images/face/42.gif rename to sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/images/face/42.gif diff --git a/sop-website/src/main/resources/public/assets/lib/layui/images/face/43.gif b/sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/images/face/43.gif similarity index 100% rename from sop-website/src/main/resources/public/assets/lib/layui/images/face/43.gif rename to sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/images/face/43.gif diff --git a/sop-website/src/main/resources/public/assets/lib/layui/images/face/44.gif b/sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/images/face/44.gif similarity index 100% rename from sop-website/src/main/resources/public/assets/lib/layui/images/face/44.gif rename to sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/images/face/44.gif diff --git a/sop-website/src/main/resources/public/assets/lib/layui/images/face/45.gif b/sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/images/face/45.gif similarity index 100% rename from sop-website/src/main/resources/public/assets/lib/layui/images/face/45.gif rename to sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/images/face/45.gif diff --git a/sop-website/src/main/resources/public/assets/lib/layui/images/face/46.gif b/sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/images/face/46.gif similarity index 100% rename from sop-website/src/main/resources/public/assets/lib/layui/images/face/46.gif rename to sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/images/face/46.gif diff --git a/sop-website/src/main/resources/public/assets/lib/layui/images/face/47.gif b/sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/images/face/47.gif similarity index 100% rename from sop-website/src/main/resources/public/assets/lib/layui/images/face/47.gif rename to sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/images/face/47.gif diff --git a/sop-website/src/main/resources/public/assets/lib/layui/images/face/48.gif b/sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/images/face/48.gif similarity index 100% rename from sop-website/src/main/resources/public/assets/lib/layui/images/face/48.gif rename to sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/images/face/48.gif diff --git a/sop-website/src/main/resources/public/assets/lib/layui/images/face/49.gif b/sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/images/face/49.gif similarity index 100% rename from sop-website/src/main/resources/public/assets/lib/layui/images/face/49.gif rename to sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/images/face/49.gif diff --git a/sop-website/src/main/resources/public/assets/lib/layui/images/face/5.gif b/sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/images/face/5.gif similarity index 100% rename from sop-website/src/main/resources/public/assets/lib/layui/images/face/5.gif rename to sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/images/face/5.gif diff --git a/sop-website/src/main/resources/public/assets/lib/layui/images/face/50.gif b/sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/images/face/50.gif similarity index 100% rename from sop-website/src/main/resources/public/assets/lib/layui/images/face/50.gif rename to sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/images/face/50.gif diff --git a/sop-website/src/main/resources/public/assets/lib/layui/images/face/51.gif b/sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/images/face/51.gif similarity index 100% rename from sop-website/src/main/resources/public/assets/lib/layui/images/face/51.gif rename to sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/images/face/51.gif diff --git a/sop-website/src/main/resources/public/assets/lib/layui/images/face/52.gif b/sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/images/face/52.gif similarity index 100% rename from sop-website/src/main/resources/public/assets/lib/layui/images/face/52.gif rename to sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/images/face/52.gif diff --git a/sop-website/src/main/resources/public/assets/lib/layui/images/face/53.gif b/sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/images/face/53.gif similarity index 100% rename from sop-website/src/main/resources/public/assets/lib/layui/images/face/53.gif rename to sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/images/face/53.gif diff --git a/sop-website/src/main/resources/public/assets/lib/layui/images/face/54.gif b/sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/images/face/54.gif similarity index 100% rename from sop-website/src/main/resources/public/assets/lib/layui/images/face/54.gif rename to sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/images/face/54.gif diff --git a/sop-website/src/main/resources/public/assets/lib/layui/images/face/55.gif b/sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/images/face/55.gif similarity index 100% rename from sop-website/src/main/resources/public/assets/lib/layui/images/face/55.gif rename to sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/images/face/55.gif diff --git a/sop-website/src/main/resources/public/assets/lib/layui/images/face/56.gif b/sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/images/face/56.gif similarity index 100% rename from sop-website/src/main/resources/public/assets/lib/layui/images/face/56.gif rename to sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/images/face/56.gif diff --git a/sop-website/src/main/resources/public/assets/lib/layui/images/face/57.gif b/sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/images/face/57.gif similarity index 100% rename from sop-website/src/main/resources/public/assets/lib/layui/images/face/57.gif rename to sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/images/face/57.gif diff --git a/sop-website/src/main/resources/public/assets/lib/layui/images/face/58.gif b/sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/images/face/58.gif similarity index 100% rename from sop-website/src/main/resources/public/assets/lib/layui/images/face/58.gif rename to sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/images/face/58.gif diff --git a/sop-website/src/main/resources/public/assets/lib/layui/images/face/59.gif b/sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/images/face/59.gif similarity index 100% rename from sop-website/src/main/resources/public/assets/lib/layui/images/face/59.gif rename to sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/images/face/59.gif diff --git a/sop-website/src/main/resources/public/assets/lib/layui/images/face/6.gif b/sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/images/face/6.gif similarity index 100% rename from sop-website/src/main/resources/public/assets/lib/layui/images/face/6.gif rename to sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/images/face/6.gif diff --git a/sop-website/src/main/resources/public/assets/lib/layui/images/face/60.gif b/sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/images/face/60.gif similarity index 100% rename from sop-website/src/main/resources/public/assets/lib/layui/images/face/60.gif rename to sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/images/face/60.gif diff --git a/sop-website/src/main/resources/public/assets/lib/layui/images/face/61.gif b/sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/images/face/61.gif similarity index 100% rename from sop-website/src/main/resources/public/assets/lib/layui/images/face/61.gif rename to sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/images/face/61.gif diff --git a/sop-website/src/main/resources/public/assets/lib/layui/images/face/62.gif b/sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/images/face/62.gif similarity index 100% rename from sop-website/src/main/resources/public/assets/lib/layui/images/face/62.gif rename to sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/images/face/62.gif diff --git a/sop-website/src/main/resources/public/assets/lib/layui/images/face/63.gif b/sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/images/face/63.gif similarity index 100% rename from sop-website/src/main/resources/public/assets/lib/layui/images/face/63.gif rename to sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/images/face/63.gif diff --git a/sop-website/src/main/resources/public/assets/lib/layui/images/face/64.gif b/sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/images/face/64.gif similarity index 100% rename from sop-website/src/main/resources/public/assets/lib/layui/images/face/64.gif rename to sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/images/face/64.gif diff --git a/sop-website/src/main/resources/public/assets/lib/layui/images/face/65.gif b/sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/images/face/65.gif similarity index 100% rename from sop-website/src/main/resources/public/assets/lib/layui/images/face/65.gif rename to sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/images/face/65.gif diff --git a/sop-website/src/main/resources/public/assets/lib/layui/images/face/66.gif b/sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/images/face/66.gif similarity index 100% rename from sop-website/src/main/resources/public/assets/lib/layui/images/face/66.gif rename to sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/images/face/66.gif diff --git a/sop-website/src/main/resources/public/assets/lib/layui/images/face/67.gif b/sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/images/face/67.gif similarity index 100% rename from sop-website/src/main/resources/public/assets/lib/layui/images/face/67.gif rename to sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/images/face/67.gif diff --git a/sop-website/src/main/resources/public/assets/lib/layui/images/face/68.gif b/sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/images/face/68.gif similarity index 100% rename from sop-website/src/main/resources/public/assets/lib/layui/images/face/68.gif rename to sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/images/face/68.gif diff --git a/sop-website/src/main/resources/public/assets/lib/layui/images/face/69.gif b/sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/images/face/69.gif similarity index 100% rename from sop-website/src/main/resources/public/assets/lib/layui/images/face/69.gif rename to sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/images/face/69.gif diff --git a/sop-website/src/main/resources/public/assets/lib/layui/images/face/7.gif b/sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/images/face/7.gif similarity index 100% rename from sop-website/src/main/resources/public/assets/lib/layui/images/face/7.gif rename to sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/images/face/7.gif diff --git a/sop-website/src/main/resources/public/assets/lib/layui/images/face/70.gif b/sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/images/face/70.gif similarity index 100% rename from sop-website/src/main/resources/public/assets/lib/layui/images/face/70.gif rename to sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/images/face/70.gif diff --git a/sop-website/src/main/resources/public/assets/lib/layui/images/face/71.gif b/sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/images/face/71.gif similarity index 100% rename from sop-website/src/main/resources/public/assets/lib/layui/images/face/71.gif rename to sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/images/face/71.gif diff --git a/sop-website/src/main/resources/public/assets/lib/layui/images/face/8.gif b/sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/images/face/8.gif similarity index 100% rename from sop-website/src/main/resources/public/assets/lib/layui/images/face/8.gif rename to sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/images/face/8.gif diff --git a/sop-website/src/main/resources/public/assets/lib/layui/images/face/9.gif b/sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/images/face/9.gif similarity index 100% rename from sop-website/src/main/resources/public/assets/lib/layui/images/face/9.gif rename to sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/images/face/9.gif diff --git a/sop-website/src/main/resources/public/assets/lib/layui/lay/modules/carousel.js b/sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/lay/modules/carousel.js similarity index 100% rename from sop-website/src/main/resources/public/assets/lib/layui/lay/modules/carousel.js rename to sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/lay/modules/carousel.js diff --git a/sop-website/src/main/resources/public/assets/lib/layui/lay/modules/code.js b/sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/lay/modules/code.js similarity index 100% rename from sop-website/src/main/resources/public/assets/lib/layui/lay/modules/code.js rename to sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/lay/modules/code.js diff --git a/sop-website/src/main/resources/public/assets/lib/layui/lay/modules/colorpicker.js b/sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/lay/modules/colorpicker.js similarity index 100% rename from sop-website/src/main/resources/public/assets/lib/layui/lay/modules/colorpicker.js rename to sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/lay/modules/colorpicker.js diff --git a/sop-website/src/main/resources/public/assets/lib/layui/lay/modules/element.js b/sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/lay/modules/element.js similarity index 100% rename from sop-website/src/main/resources/public/assets/lib/layui/lay/modules/element.js rename to sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/lay/modules/element.js diff --git a/sop-website/src/main/resources/public/assets/lib/layui/lay/modules/flow.js b/sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/lay/modules/flow.js similarity index 100% rename from sop-website/src/main/resources/public/assets/lib/layui/lay/modules/flow.js rename to sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/lay/modules/flow.js diff --git a/sop-website/src/main/resources/public/assets/lib/layui/lay/modules/form.js b/sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/lay/modules/form.js similarity index 100% rename from sop-website/src/main/resources/public/assets/lib/layui/lay/modules/form.js rename to sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/lay/modules/form.js diff --git a/sop-website/src/main/resources/public/assets/lib/layui/lay/modules/jquery.js b/sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/lay/modules/jquery.js similarity index 100% rename from sop-website/src/main/resources/public/assets/lib/layui/lay/modules/jquery.js rename to sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/lay/modules/jquery.js diff --git a/sop-website/src/main/resources/public/assets/lib/layui/lay/modules/laydate.js b/sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/lay/modules/laydate.js similarity index 100% rename from sop-website/src/main/resources/public/assets/lib/layui/lay/modules/laydate.js rename to sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/lay/modules/laydate.js diff --git a/sop-website/src/main/resources/public/assets/lib/layui/lay/modules/layedit.js b/sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/lay/modules/layedit.js similarity index 100% rename from sop-website/src/main/resources/public/assets/lib/layui/lay/modules/layedit.js rename to sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/lay/modules/layedit.js diff --git a/sop-website/src/main/resources/public/assets/lib/layui/lay/modules/layer.js b/sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/lay/modules/layer.js similarity index 100% rename from sop-website/src/main/resources/public/assets/lib/layui/lay/modules/layer.js rename to sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/lay/modules/layer.js diff --git a/sop-website/src/main/resources/public/assets/lib/layui/lay/modules/laypage.js b/sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/lay/modules/laypage.js similarity index 100% rename from sop-website/src/main/resources/public/assets/lib/layui/lay/modules/laypage.js rename to sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/lay/modules/laypage.js diff --git a/sop-website/src/main/resources/public/assets/lib/layui/lay/modules/laytpl.js b/sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/lay/modules/laytpl.js similarity index 100% rename from sop-website/src/main/resources/public/assets/lib/layui/lay/modules/laytpl.js rename to sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/lay/modules/laytpl.js diff --git a/sop-website/src/main/resources/public/assets/lib/layui/lay/modules/mobile.js b/sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/lay/modules/mobile.js similarity index 100% rename from sop-website/src/main/resources/public/assets/lib/layui/lay/modules/mobile.js rename to sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/lay/modules/mobile.js diff --git a/sop-website/src/main/resources/public/assets/lib/layui/lay/modules/rate.js b/sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/lay/modules/rate.js similarity index 100% rename from sop-website/src/main/resources/public/assets/lib/layui/lay/modules/rate.js rename to sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/lay/modules/rate.js diff --git a/sop-website/src/main/resources/public/assets/lib/layui/lay/modules/slider.js b/sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/lay/modules/slider.js similarity index 100% rename from sop-website/src/main/resources/public/assets/lib/layui/lay/modules/slider.js rename to sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/lay/modules/slider.js diff --git a/sop-website/src/main/resources/public/assets/lib/layui/lay/modules/table.js b/sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/lay/modules/table.js similarity index 100% rename from sop-website/src/main/resources/public/assets/lib/layui/lay/modules/table.js rename to sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/lay/modules/table.js diff --git a/sop-website/src/main/resources/public/assets/lib/layui/lay/modules/tree.js b/sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/lay/modules/tree.js similarity index 100% rename from sop-website/src/main/resources/public/assets/lib/layui/lay/modules/tree.js rename to sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/lay/modules/tree.js diff --git a/sop-website/src/main/resources/public/assets/lib/layui/lay/modules/upload.js b/sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/lay/modules/upload.js similarity index 100% rename from sop-website/src/main/resources/public/assets/lib/layui/lay/modules/upload.js rename to sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/lay/modules/upload.js diff --git a/sop-website/src/main/resources/public/assets/lib/layui/lay/modules/util.js b/sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/lay/modules/util.js similarity index 100% rename from sop-website/src/main/resources/public/assets/lib/layui/lay/modules/util.js rename to sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/lay/modules/util.js diff --git a/sop-website/src/main/resources/public/assets/lib/layui/layui.all.js b/sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/layui.all.js similarity index 100% rename from sop-website/src/main/resources/public/assets/lib/layui/layui.all.js rename to sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/layui.all.js diff --git a/sop-website/src/main/resources/public/assets/lib/layui/layui.js b/sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/layui.js similarity index 100% rename from sop-website/src/main/resources/public/assets/lib/layui/layui.js rename to sop-website/sop-website-server/src/main/resources/public/assets/lib/layui/layui.js diff --git a/sop-website/src/main/resources/public/assets/lib/layuiext/Form.js b/sop-website/sop-website-server/src/main/resources/public/assets/lib/layuiext/Form.js similarity index 100% rename from sop-website/src/main/resources/public/assets/lib/layuiext/Form.js rename to sop-website/sop-website-server/src/main/resources/public/assets/lib/layuiext/Form.js diff --git a/sop-website/src/main/resources/public/assets/lib/layuiext/module/treetable-lay/treetable.css b/sop-website/sop-website-server/src/main/resources/public/assets/lib/layuiext/module/treetable-lay/treetable.css similarity index 100% rename from sop-website/src/main/resources/public/assets/lib/layuiext/module/treetable-lay/treetable.css rename to sop-website/sop-website-server/src/main/resources/public/assets/lib/layuiext/module/treetable-lay/treetable.css diff --git a/sop-website/src/main/resources/public/assets/lib/layuiext/module/treetable-lay/treetable.js b/sop-website/sop-website-server/src/main/resources/public/assets/lib/layuiext/module/treetable-lay/treetable.js similarity index 100% rename from sop-website/src/main/resources/public/assets/lib/layuiext/module/treetable-lay/treetable.js rename to sop-website/sop-website-server/src/main/resources/public/assets/lib/layuiext/module/treetable-lay/treetable.js diff --git a/sop-website/src/main/resources/public/config/config.js b/sop-website/sop-website-server/src/main/resources/public/config/config.js similarity index 100% rename from sop-website/src/main/resources/public/config/config.js rename to sop-website/sop-website-server/src/main/resources/public/config/config.js diff --git a/sop-website/sop-website-server/src/main/resources/public/favicon.ico b/sop-website/sop-website-server/src/main/resources/public/favicon.ico new file mode 100644 index 00000000..224e8d20 Binary files /dev/null and b/sop-website/sop-website-server/src/main/resources/public/favicon.ico differ diff --git a/sop-website/sop-website-server/src/main/resources/public/index.html b/sop-website/sop-website-server/src/main/resources/public/index.html new file mode 100644 index 00000000..5147f81d --- /dev/null +++ b/sop-website/sop-website-server/src/main/resources/public/index.html @@ -0,0 +1 @@ +开放平台
    \ No newline at end of file diff --git a/sop-website/src/main/resources/public/index.html b/sop-website/sop-website-server/src/main/resources/public/index_old.html similarity index 100% rename from sop-website/src/main/resources/public/index.html rename to sop-website/sop-website-server/src/main/resources/public/index_old.html diff --git a/sop-website/src/main/resources/public/pages/doc/auth.html b/sop-website/sop-website-server/src/main/resources/public/pages/doc/auth.html similarity index 100% rename from sop-website/src/main/resources/public/pages/doc/auth.html rename to sop-website/sop-website-server/src/main/resources/public/pages/doc/auth.html diff --git a/sop-website/src/main/resources/public/pages/doc/code.html b/sop-website/sop-website-server/src/main/resources/public/pages/doc/code.html similarity index 100% rename from sop-website/src/main/resources/public/pages/doc/code.html rename to sop-website/sop-website-server/src/main/resources/public/pages/doc/code.html diff --git a/sop-website/src/main/resources/public/pages/doc/doc.html b/sop-website/sop-website-server/src/main/resources/public/pages/doc/doc.html similarity index 100% rename from sop-website/src/main/resources/public/pages/doc/doc.html rename to sop-website/sop-website-server/src/main/resources/public/pages/doc/doc.html diff --git a/sop-website/src/main/resources/public/pages/doc/doc.js b/sop-website/sop-website-server/src/main/resources/public/pages/doc/doc.js similarity index 100% rename from sop-website/src/main/resources/public/pages/doc/doc.js rename to sop-website/sop-website-server/src/main/resources/public/pages/doc/doc.js diff --git a/sop-website/src/main/resources/public/pages/doc/docEvent.js b/sop-website/sop-website-server/src/main/resources/public/pages/doc/docEvent.js similarity index 100% rename from sop-website/src/main/resources/public/pages/doc/docEvent.js rename to sop-website/sop-website-server/src/main/resources/public/pages/doc/docEvent.js diff --git a/sop-website/src/main/resources/public/pages/doc/sign.html b/sop-website/sop-website-server/src/main/resources/public/pages/doc/sign.html similarity index 100% rename from sop-website/src/main/resources/public/pages/doc/sign.html rename to sop-website/sop-website-server/src/main/resources/public/pages/doc/sign.html diff --git a/sop-website/src/main/resources/public/pages/sandbox/sandbox.html b/sop-website/sop-website-server/src/main/resources/public/pages/sandbox/sandbox.html similarity index 100% rename from sop-website/src/main/resources/public/pages/sandbox/sandbox.html rename to sop-website/sop-website-server/src/main/resources/public/pages/sandbox/sandbox.html diff --git a/sop-website/src/main/resources/public/pages/sandbox/sandbox.js b/sop-website/sop-website-server/src/main/resources/public/pages/sandbox/sandbox.js similarity index 100% rename from sop-website/src/main/resources/public/pages/sandbox/sandbox.js rename to sop-website/sop-website-server/src/main/resources/public/pages/sandbox/sandbox.js diff --git a/sop-website/sop-website-server/src/main/resources/public/static/code.json b/sop-website/sop-website-server/src/main/resources/public/static/code.json new file mode 100644 index 00000000..8ac28678 --- /dev/null +++ b/sop-website/sop-website-server/src/main/resources/public/static/code.json @@ -0,0 +1,172 @@ +[ + { + "id": 1100, + "code": "10000", + "msg": "成功" + }, + { + "id": 1200, + "code": "20000", + "msg": "服务不可用", + "children": [ + { + "id": 1201, + "sub_code": "isp.unknown-error", + "sub_msg": "服务暂不可用", + "solution": "联系平台客服排查日志" + }, + { + "id": 1202, + "sub_code": "isp.no-service-support", + "sub_msg": "业务系统不可用", + "solution": "联系接口提供方" + }, + { + "id": 1203, + "sub_code": "isp.invalid-content-type", + "sub_msg": "业务系统返回错误的contentType", + "solution": "联系接口提供方,返回的contentType是否是application/json或application/json;charset=UTF-8" + }, + { + "id": 1204, + "sub_code": "isp.host-connection-refused", + "sub_msg": "服务器拒绝访问", + "solution": "联系接口提供方,检查配置的服务器域名是否正确" + }, + { + "id": 1205, + "sub_code": "isp.business-timeout", + "sub_msg": "业务处理超时", + "solution": "联系接口提供方,业务逻辑处理时间太长" + }, + { + "id": 1206, + "sub_code": "isp.invalid-result", + "sub_msg": "无效的业务返回结果", + "solution": "联系接口提供方,检查是否返回了正确的json" + }, + { + "id": 1207, + "sub_code": "isp.exec-gzip-error", + "sub_msg": "处理gzip异常", + "solution": "联系客服排查日志" + } + ] + }, + { + "id": 1300, + "code": "40001", + "msg": "缺少参数", + "children": [ + { + "id": 1301, + "sub_code": "isv.missing-app-id", + "sub_msg": "缺少appId参数", + "solution": "检查参数" + }, + { + "id": 1302, + "sub_code": "isv.missing-method", + "sub_msg": "缺少method参数", + "solution": "检查参数" + }, + { + "id": 1303, + "sub_code": "isv.missing-signature", + "sub_msg": "缺少签名参数", + "solution": "检查参数" + }, + { + "id": 1304, + "sub_code": "isv.missing-version", + "sub_msg": "缺少版本号参数", + "solution": "检查参数" + }, + { + "id": 1305, + "sub_code": "isv.missing-signature-config", + "sub_msg": "缺少秘钥配置", + "solution": "联系接口提供方,配置秘钥" + } + ] + }, + { + "id": 1400, + "code": "40002", + "msg": "非法参数", + "children": [ + { + "id": 1401, + "sub_code": "isv.invalid-app-key", + "sub_msg": "无效appId", + "solution": "检查appId是否正确" + }, + { + "id": 1402, + "sub_code": "isv.invalid-method", + "sub_msg": "不存在的方法名", + "solution": "检查方法名是否正确" + }, + { + "id": 1403, + "sub_code": "isv.invalid-signature", + "sub_msg": "无效签名", + "solution": "检查签名算法是否正确" + }, + { + "id": 1404, + "sub_code": "isv.invalid-timestamp", + "sub_msg": "无效的时间戳", + "solution": "检查时间戳参数是否正确" + }, + { + "id": 1405, + "sub_code": "isv.invalid-environment", + "sub_msg": "环境不一致", + "solution": "检查测试环境正式环境是否混淆" + }, + { + "id": 1406, + "sub_code": "isv.invalid-file-size", + "sub_msg": "文件大小无效", + "solution": "检查文件大小是否正确" + } + ] + }, + { + "id": 1500, + "code": "40004", + "msg": "业务处理失败" + }, + { + "id": 1600, + "code": "40006", + "msg": "业务处理失败", + "children": [ + { + "id": 1601, + "sub_code": "isv.api-no-permissions", + "sub_msg": "没有当前接口权限", + "solution": "联系接口提供方,检查是否分配接口权限" + }, + { + "id": 1602, + "sub_code": "isv.access-forbidden", + "sub_msg": "接入方禁止访问", + "solution": "联系接口提供方,账号是否被禁用" + }, + { + "id": 1603, + "sub_code": "isv.ip-forbidden", + "sub_msg": "IP禁止访问", + "solution": "联系接口提供方,IP加入了黑名单" + }, + { + "id": 1604, + "sub_code": "isv.invalid-url", + "sub_msg": "请求url不合法", + "solution": "检查请求url是否正确" + } + ] + } +] diff --git a/sop-website/sop-website-server/src/main/resources/public/static/css/app.520ff3b4.css b/sop-website/sop-website-server/src/main/resources/public/static/css/app.520ff3b4.css new file mode 100644 index 00000000..b3f4c852 --- /dev/null +++ b/sop-website/sop-website-server/src/main/resources/public/static/css/app.520ff3b4.css @@ -0,0 +1 @@ +.fade-enter-active,.fade-leave-active{-webkit-transition:opacity .28s;transition:opacity .28s}.fade-enter,.fade-leave-active{opacity:0}.fade-transform-enter-active,.fade-transform-leave-active{-webkit-transition:all .5s;transition:all .5s}.fade-transform-enter{opacity:0;-webkit-transform:translateX(-30px);transform:translateX(-30px)}.fade-transform-leave-to{opacity:0;-webkit-transform:translateX(30px);transform:translateX(30px)}.breadcrumb-enter-active,.breadcrumb-leave-active{-webkit-transition:all .5s;transition:all .5s}.breadcrumb-enter,.breadcrumb-leave-active{opacity:0;-webkit-transform:translateX(20px);transform:translateX(20px)}.breadcrumb-move{-webkit-transition:all .5s;transition:all .5s}.breadcrumb-leave-active{position:absolute}.el-breadcrumb__inner,.el-breadcrumb__inner a{font-weight:400!important}.el-upload input[type=file]{display:none!important}.el-upload__input{display:none}.el-dialog{-webkit-transform:none;transform:none;left:0;position:relative;margin:0 auto}.upload-container .el-upload{width:100%}.upload-container .el-upload .el-upload-dragger{width:100%;height:200px}.el-dropdown-menu a{display:block}#app .main-container{min-height:100%;-webkit-transition:margin-left .28s;transition:margin-left .28s;margin-left:210px;position:relative}#app .sidebar-container{-webkit-transition:width .28s;transition:width .28s;width:210px!important;background-color:#304156;height:100%;position:fixed;font-size:0;top:0;bottom:0;left:0;z-index:1001;overflow:hidden}#app .sidebar-container .horizontal-collapse-transition{-webkit-transition:width 0s ease-in-out,padding-left 0s ease-in-out,padding-right 0s ease-in-out;transition:width 0s ease-in-out,padding-left 0s ease-in-out,padding-right 0s ease-in-out}#app .sidebar-container .scrollbar-wrapper{overflow-x:hidden!important}#app .sidebar-container .el-scrollbar__bar.is-vertical{right:0}#app .sidebar-container .el-scrollbar{height:100%}#app .sidebar-container.has-logo .el-scrollbar{height:calc(100% - 50px)}#app .sidebar-container .is-horizontal{display:none}#app .sidebar-container a{display:inline-block;width:100%;overflow:hidden}#app .sidebar-container .svg-icon{margin-right:16px}#app .sidebar-container .el-menu{border:none;height:100%;width:100%!important}#app .sidebar-container .el-submenu__title:hover,#app .sidebar-container .submenu-title-noDropdown:hover{background-color:#263445!important}#app .sidebar-container .is-active>.el-submenu__title{color:#f4f4f5!important}#app .sidebar-container .el-submenu .el-menu-item,#app .sidebar-container .nest-menu .el-submenu>.el-submenu__title{min-width:210px!important;background-color:#1f2d3d!important}#app .sidebar-container .el-submenu .el-menu-item:hover,#app .sidebar-container .nest-menu .el-submenu>.el-submenu__title:hover{background-color:#001528!important}#app .hideSidebar .sidebar-container{width:54px!important}#app .hideSidebar .main-container{margin-left:54px}#app .hideSidebar .svg-icon{margin-right:0}#app .hideSidebar .submenu-title-noDropdown{padding:0!important;position:relative}#app .hideSidebar .submenu-title-noDropdown .el-tooltip{padding:0 10px!important}#app .hideSidebar .submenu-title-noDropdown .el-tooltip .svg-icon{margin-left:10px}#app .hideSidebar .el-submenu{overflow:hidden}#app .hideSidebar .el-submenu>.el-submenu__title{padding:0 10px!important}#app .hideSidebar .el-submenu>.el-submenu__title .svg-icon{margin-left:10px}#app .hideSidebar .el-submenu>.el-submenu__title .el-submenu__icon-arrow{display:none}#app .hideSidebar .el-menu--collapse .el-submenu>.el-submenu__title>span{height:0;width:0;overflow:hidden;visibility:hidden;display:inline-block}#app .el-menu--collapse .el-menu .el-submenu{min-width:210px!important}#app .mobile .main-container{margin-left:0}#app .mobile .sidebar-container{-webkit-transition:-webkit-transform .28s;transition:-webkit-transform .28s;transition:transform .28s;transition:transform .28s,-webkit-transform .28s;width:210px!important}#app .mobile.hideSidebar .sidebar-container{pointer-events:none;-webkit-transition-duration:.3s;transition-duration:.3s;-webkit-transform:translate3d(-210px,0,0);transform:translate3d(-210px,0,0)}#app .withoutAnimation .main-container,#app .withoutAnimation .sidebar-container{-webkit-transition:none;transition:none}.el-menu--vertical>.el-menu .svg-icon{margin-right:16px}.el-menu--vertical .el-menu-item:hover,.el-menu--vertical .nest-menu .el-submenu>.el-submenu__title:hover{background-color:#263445!important}.el-menu--vertical>.el-menu--popup{max-height:100vh;overflow-y:auto}.el-menu--vertical>.el-menu--popup::-webkit-scrollbar-track-piece{background:#d3dce6}.el-menu--vertical>.el-menu--popup::-webkit-scrollbar{width:6px}.el-menu--vertical>.el-menu--popup::-webkit-scrollbar-thumb{background:#99a9bf;border-radius:20px}body{height:100%;-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;text-rendering:optimizeLegibility;font-family:Helvetica Neue,Helvetica,PingFang SC,Hiragino Sans GB,Microsoft YaHei,Arial,sans-serif}label{font-weight:700}html{-webkit-box-sizing:border-box;box-sizing:border-box}#app,html{height:100%}*,:after,:before{-webkit-box-sizing:inherit;box-sizing:inherit}a:active,a:focus{outline:none}a,a:focus,a:hover{cursor:pointer;color:inherit;text-decoration:none}div:focus{outline:none}.clearfix:after{visibility:hidden;display:block;font-size:0;content:" ";clear:both;height:0}.app-container{padding:20px}.cell .el-link{margin-right:8px;font-size:13px}.reg-box-card{position:relative;width:520px;max-width:100%;margin:50px auto;overflow:hidden}.login-container{min-height:100%;width:100%;overflow:hidden}.login-container .login-form{position:relative;width:520px;max-width:100%;padding:160px 35px 0;margin:0 auto;overflow:hidden}.login-container .tips{font-size:14px;color:#fff;margin-bottom:10px}.login-container .tips span:first-of-type{margin-right:16px}.login-container .svg-container{padding:6px 5px 6px 15px;color:#889aa4;vertical-align:middle;width:30px;display:inline-block}.login-container .title-container{position:relative}.login-container .title-container .title{color:#545454;font-size:26px;margin:0 auto 40px auto;text-align:center;font-weight:700}.cell .el-link,.el-alert__content .el-link{margin-bottom:1px}.cell .el-button{padding:0}.pagination{margin-top:5px}.reg-box-card .clearfix{text-align:center}.reg-box-card .alert{margin-bottom:20px}div.success,i.el-icon-success,span.success{color:#67c23a}div.warning,span.warning{color:#e6a23c}div.danger,span.danger{color:#f56c6c}.isp-info{margin-left:20px;color:#606266}.isp-info-tip{color:#909399;font-size:13px}.normal-text{font-size:14px;color:#606266}.text-form .el-form-item{margin-bottom:0}.base-info{margin-left:20px;padding-top:10px;color:#606266}.open-sphere-log{font-size:25px;color:#0084db}.open-sphere-log .el-icon-ext-opensphere{color:#0084db;font-size:30px}.center-form{width:500px;margin:0 auto;padding-top:80px}.center-form h3{text-align:center;color:#606266}.center-form .footer{text-align:center;margin-top:30px;font-size:14px}.el-alert-tip{margin-bottom:20px}fieldset{border:1px solid #dcdfe6;padding:20px}.key-content{padding:10px;background-color:#eee}.el-option-left{float:left}.el-option-right{float:right;color:#8492a6;font-size:13px}.app-breadcrumb.el-breadcrumb[data-v-33d79dae]{display:inline-block;font-size:14px;line-height:50px;margin-left:8px}.app-breadcrumb.el-breadcrumb .no-redirect[data-v-33d79dae]{color:#97a8be;cursor:text}.hamburger[data-v-49e15297]{display:inline-block;vertical-align:middle;width:20px;height:20px}.hamburger.is-active[data-v-49e15297]{-webkit-transform:rotate(180deg);transform:rotate(180deg)}.navbar-div[data-v-1941928b]{float:right;margin-top:5px;margin-right:10px}.user-head[data-v-1941928b]{cursor:pointer;margin-top:6px;margin-right:10px}.navbar[data-v-1941928b]{height:50px;overflow:hidden;position:relative;background:#fff;-webkit-box-shadow:0 1px 4px rgba(0,21,41,.08);box-shadow:0 1px 4px rgba(0,21,41,.08)}.navbar .hamburger-container[data-v-1941928b]{line-height:46px;height:100%;float:left;cursor:pointer;-webkit-transition:background .3s;transition:background .3s;-webkit-tap-highlight-color:transparent}.navbar .hamburger-container[data-v-1941928b]:hover{background:rgba(0,0,0,.025)}.navbar .breadcrumb-container[data-v-1941928b]{float:left}.navbar .right-menu[data-v-1941928b]{float:right;height:100%;line-height:40px}.navbar .right-menu[data-v-1941928b]:focus{outline:none}.navbar .right-menu .right-menu-item[data-v-1941928b]{display:inline-block;padding:0 8px;height:100%;font-size:18px;color:#5a5e66;vertical-align:text-bottom}.navbar .right-menu .right-menu-item.hover-effect[data-v-1941928b]{cursor:pointer;-webkit-transition:background .3s;transition:background .3s}.navbar .right-menu .right-menu-item.hover-effect[data-v-1941928b]:hover{background:rgba(0,0,0,.025)}.navbar .right-menu .avatar-container .avatar-wrapper[data-v-1941928b]{margin-top:5px;position:relative}.navbar .right-menu .avatar-container .avatar-wrapper .user-avatar[data-v-1941928b]{cursor:pointer;width:40px;height:40px;border-radius:10px}.navbar .right-menu .avatar-container .avatar-wrapper .el-icon-caret-bottom[data-v-1941928b]{cursor:pointer;position:absolute;right:-20px;top:25px;font-size:12px}.sidebarLogoFade-enter-active[data-v-abec7bb6]{-webkit-transition:opacity 1.5s;transition:opacity 1.5s}.sidebarLogoFade-enter[data-v-abec7bb6],.sidebarLogoFade-leave-to[data-v-abec7bb6]{opacity:0}.sidebar-logo-container[data-v-abec7bb6]{position:relative;width:100%;height:50px;line-height:50px;background:#2b2f3a;padding-left:10px;overflow:hidden}.sidebar-logo-container .sidebar-logo-link[data-v-abec7bb6]{height:100%;width:100%}.sidebar-logo-container .sidebar-logo-link .sidebar-logo[data-v-abec7bb6]{width:32px;height:32px;vertical-align:middle;margin-right:12px}.sidebar-logo-container .sidebar-logo-link .sidebar-title[data-v-abec7bb6]{display:inline-block;margin:0;color:#fff;font-weight:600;line-height:50px;font-size:14px;font-family:Avenir,Helvetica Neue,Arial,Helvetica,sans-serif;vertical-align:middle}.sidebar-logo-container.collapse .sidebar-logo[data-v-abec7bb6]{margin-right:0}.app-main[data-v-43c24f68]{min-height:calc(100vh - 50px);width:100%;position:relative;overflow:hidden}.fixed-header+.app-main[data-v-43c24f68]{padding-top:50px}[data-v-4f739cf0]:export{menuText:#bfcbd9;menuActiveText:#409eff;subMenuActiveText:#f4f4f5;menuBg:#304156;menuHover:#263445;subMenuBg:#1f2d3d;subMenuHover:#001528;sideBarWidth:210px}.app-wrapper[data-v-4f739cf0]{position:relative;height:100%;width:100%}.app-wrapper[data-v-4f739cf0]:after{content:"";display:table;clear:both}.app-wrapper.mobile.openSidebar[data-v-4f739cf0]{position:fixed;top:0}.drawer-bg[data-v-4f739cf0]{background:#000;opacity:.3;width:100%;top:0;height:100%;position:absolute;z-index:999}.fixed-header[data-v-4f739cf0]{position:fixed;top:0;right:0;z-index:9;width:calc(100% - 210px);-webkit-transition:width .28s;transition:width .28s}.hideSidebar .fixed-header[data-v-4f739cf0]{width:calc(100% - 54px)}.mobile .fixed-header[data-v-4f739cf0]{width:100%}.svg-icon[data-v-53ff2da0]{width:1em;height:1em;vertical-align:-.15em;fill:currentColor;overflow:hidden} \ No newline at end of file diff --git a/sop-website/sop-website-server/src/main/resources/public/static/css/chunk-27eb7616.67b30787.css b/sop-website/sop-website-server/src/main/resources/public/static/css/chunk-27eb7616.67b30787.css new file mode 100644 index 00000000..ed24d09b --- /dev/null +++ b/sop-website/sop-website-server/src/main/resources/public/static/css/chunk-27eb7616.67b30787.css @@ -0,0 +1 @@ +@font-face{font-family:fontello;src:url(../../static/fonts/fontello.e73a0647.eot);src:url(../../static/fonts/fontello.e73a0647.eot#iefix) format("embedded-opentype"),url(../../static/fonts/fontello.8d4a4e6f.woff2) format("woff2"),url(../../static/fonts/fontello.a782baa8.woff) format("woff"),url(../../static/fonts/fontello.068ca2b3.ttf) format("truetype"),url(../../static/img/fontello.9354499c.svg#fontello) format("svg");font-weight:400;font-style:normal}[class*=" fa-mavon-"]:before,[class^=fa-mavon-]:before{font-family:fontello;font-style:normal;font-weight:400;speak:none;display:inline-block;text-decoration:inherit;width:1em;margin-right:.2em;text-align:center;font-variant:normal;text-transform:none;line-height:1em;margin-left:.2em;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.fa-mavon-bold:before{content:"\E800"}.fa-mavon-italic:before{content:"\E801"}.fa-mavon-thumb-tack:before{content:"\E802"}.fa-mavon-link:before{content:"\E803"}.fa-mavon-picture-o:before{content:"\E804"}.fa-mavon-repeat:before{content:"\E805"}.fa-mavon-undo:before{content:"\E806"}.fa-mavon-trash-o:before{content:"\E807"}.fa-mavon-floppy-o:before{content:"\E808"}.fa-mavon-compress:before{content:"\E809"}.fa-mavon-eye:before{content:"\E80A"}.fa-mavon-eye-slash:before{content:"\E80B"}.fa-mavon-question-circle:before{content:"\E80C"}.fa-mavon-times:before{content:"\E80D"}.fa-mavon-align-left:before{content:"\E80F"}.fa-mavon-align-center:before{content:"\E810"}.fa-mavon-align-right:before{content:"\E811"}.fa-mavon-arrows-alt:before{content:"\F0B2"}.fa-mavon-bars:before{content:"\F0C9"}.fa-mavon-list-ul:before{content:"\F0CA"}.fa-mavon-list-ol:before{content:"\F0CB"}.fa-mavon-strikethrough:before{content:"\F0CC"}.fa-mavon-underline:before{content:"\F0CD"}.fa-mavon-table:before{content:"\F0CE"}.fa-mavon-columns:before{content:"\F0DB"}.fa-mavon-quote-left:before{content:"\F10D"}.fa-mavon-code:before{content:"\F121"}.fa-mavon-superscript:before{content:"\F12B"}.fa-mavon-subscript:before{content:"\F12C"}.fa-mavon-header:before{content:"\F1DC"}.fa-mavon-window-maximize:before{content:"\F2D0"}.markdown-body strong{font-weight:bolder}.markdown-body .hljs-center{text-align:center}.markdown-body .hljs-right{text-align:right}.markdown-body .hljs-left{text-align:left}.api-info[data-v-c361512c]{font-weight:700}.doc-overview[data-v-c361512c]{margin-top:20px;margin-bottom:30px;color:#666;font-size:14px}.doc-request-method[data-v-c361512c]{margin-bottom:20px;color:#666;font-size:14px} \ No newline at end of file diff --git a/sop-website/sop-website-server/src/main/resources/public/static/css/chunk-28a29d22.4dc6f542.css b/sop-website/sop-website-server/src/main/resources/public/static/css/chunk-28a29d22.4dc6f542.css new file mode 100644 index 00000000..956e0a3e --- /dev/null +++ b/sop-website/sop-website-server/src/main/resources/public/static/css/chunk-28a29d22.4dc6f542.css @@ -0,0 +1 @@ +.wscn-http404-container[data-v-f6c61bda]{-webkit-transform:translate(-50%,-50%);transform:translate(-50%,-50%);position:absolute;top:40%;left:50%}.wscn-http404[data-v-f6c61bda]{position:relative;width:1200px;padding:0 50px;overflow:hidden}.wscn-http404 .pic-404[data-v-f6c61bda]{position:relative;float:left;width:600px;overflow:hidden}.wscn-http404 .pic-404__parent[data-v-f6c61bda]{width:100%}.wscn-http404 .pic-404__child[data-v-f6c61bda]{position:absolute}.wscn-http404 .pic-404__child.left[data-v-f6c61bda]{width:80px;top:17px;left:220px;opacity:0;-webkit-animation-name:cloudLeft-data-v-f6c61bda;animation-name:cloudLeft-data-v-f6c61bda;-webkit-animation-duration:2s;animation-duration:2s;-webkit-animation-timing-function:linear;animation-timing-function:linear;-webkit-animation-fill-mode:forwards;animation-fill-mode:forwards;-webkit-animation-delay:1s;animation-delay:1s}.wscn-http404 .pic-404__child.mid[data-v-f6c61bda]{width:46px;top:10px;left:420px;opacity:0;-webkit-animation-name:cloudMid-data-v-f6c61bda;animation-name:cloudMid-data-v-f6c61bda;-webkit-animation-duration:2s;animation-duration:2s;-webkit-animation-timing-function:linear;animation-timing-function:linear;-webkit-animation-fill-mode:forwards;animation-fill-mode:forwards;-webkit-animation-delay:1.2s;animation-delay:1.2s}.wscn-http404 .pic-404__child.right[data-v-f6c61bda]{width:62px;top:100px;left:500px;opacity:0;-webkit-animation-name:cloudRight-data-v-f6c61bda;animation-name:cloudRight-data-v-f6c61bda;-webkit-animation-duration:2s;animation-duration:2s;-webkit-animation-timing-function:linear;animation-timing-function:linear;-webkit-animation-fill-mode:forwards;animation-fill-mode:forwards;-webkit-animation-delay:1s;animation-delay:1s}@-webkit-keyframes cloudLeft-data-v-f6c61bda{0%{top:17px;left:220px;opacity:0}20%{top:33px;left:188px;opacity:1}80%{top:81px;left:92px;opacity:1}to{top:97px;left:60px;opacity:0}}@keyframes cloudLeft-data-v-f6c61bda{0%{top:17px;left:220px;opacity:0}20%{top:33px;left:188px;opacity:1}80%{top:81px;left:92px;opacity:1}to{top:97px;left:60px;opacity:0}}@-webkit-keyframes cloudMid-data-v-f6c61bda{0%{top:10px;left:420px;opacity:0}20%{top:40px;left:360px;opacity:1}70%{top:130px;left:180px;opacity:1}to{top:160px;left:120px;opacity:0}}@keyframes cloudMid-data-v-f6c61bda{0%{top:10px;left:420px;opacity:0}20%{top:40px;left:360px;opacity:1}70%{top:130px;left:180px;opacity:1}to{top:160px;left:120px;opacity:0}}@-webkit-keyframes cloudRight-data-v-f6c61bda{0%{top:100px;left:500px;opacity:0}20%{top:120px;left:460px;opacity:1}80%{top:180px;left:340px;opacity:1}to{top:200px;left:300px;opacity:0}}@keyframes cloudRight-data-v-f6c61bda{0%{top:100px;left:500px;opacity:0}20%{top:120px;left:460px;opacity:1}80%{top:180px;left:340px;opacity:1}to{top:200px;left:300px;opacity:0}}.wscn-http404 .bullshit[data-v-f6c61bda]{position:relative;float:left;width:320px;padding:30px 0;overflow:hidden}.wscn-http404 .bullshit__oops[data-v-f6c61bda]{font-size:32px;line-height:40px;color:#1482f0;margin-bottom:20px;-webkit-animation-fill-mode:forwards;animation-fill-mode:forwards}.wscn-http404 .bullshit__headline[data-v-f6c61bda],.wscn-http404 .bullshit__oops[data-v-f6c61bda]{font-weight:700;opacity:0;-webkit-animation-name:slideUp-data-v-f6c61bda;animation-name:slideUp-data-v-f6c61bda;-webkit-animation-duration:.5s;animation-duration:.5s}.wscn-http404 .bullshit__headline[data-v-f6c61bda]{font-size:20px;line-height:24px;color:#222;margin-bottom:10px;-webkit-animation-delay:.1s;animation-delay:.1s;-webkit-animation-fill-mode:forwards;animation-fill-mode:forwards}.wscn-http404 .bullshit__info[data-v-f6c61bda]{font-size:13px;line-height:21px;color:grey;margin-bottom:30px;-webkit-animation-delay:.2s;animation-delay:.2s;-webkit-animation-fill-mode:forwards;animation-fill-mode:forwards}.wscn-http404 .bullshit__info[data-v-f6c61bda],.wscn-http404 .bullshit__return-home[data-v-f6c61bda]{opacity:0;-webkit-animation-name:slideUp-data-v-f6c61bda;animation-name:slideUp-data-v-f6c61bda;-webkit-animation-duration:.5s;animation-duration:.5s}.wscn-http404 .bullshit__return-home[data-v-f6c61bda]{display:block;float:left;width:110px;height:36px;background:#1482f0;border-radius:100px;text-align:center;color:#fff;font-size:14px;line-height:36px;cursor:pointer;-webkit-animation-delay:.3s;animation-delay:.3s;-webkit-animation-fill-mode:forwards;animation-fill-mode:forwards}@-webkit-keyframes slideUp-data-v-f6c61bda{0%{-webkit-transform:translateY(60px);transform:translateY(60px);opacity:0}to{-webkit-transform:translateY(0);transform:translateY(0);opacity:1}}@keyframes slideUp-data-v-f6c61bda{0%{-webkit-transform:translateY(60px);transform:translateY(60px);opacity:0}to{-webkit-transform:translateY(0);transform:translateY(0);opacity:1}} \ No newline at end of file diff --git a/sop-website/sop-website-server/src/main/resources/public/static/css/chunk-3cffc9a9.c94c4e9c.css b/sop-website/sop-website-server/src/main/resources/public/static/css/chunk-3cffc9a9.c94c4e9c.css new file mode 100644 index 00000000..bb3643c9 --- /dev/null +++ b/sop-website/sop-website-server/src/main/resources/public/static/css/chunk-3cffc9a9.c94c4e9c.css @@ -0,0 +1 @@ +@supports (-webkit-mask:none) and (not (cater-color:#000)){.login-container .el-input input{color:#000}}.login-container .el-input{display:inline-block;height:47px;width:85%}.login-container .el-input input{background:transparent;border:0;-webkit-appearance:none;border-radius:0;padding:12px 5px 12px 15px;color:#000;height:47px;caret-color:#000}.login-container .el-input input:-webkit-autofill{-webkit-box-shadow:0 0 0 1000px #fff inset!important;box-shadow:inset 0 0 0 1000px #fff!important;-webkit-text-fill-color:#000!important}.login-container .el-form-item{border:1px solid #d3dce6;border-radius:5px;color:#454545}.login-container[data-v-0b530f82]{min-height:100%;width:100%;overflow:hidden}.login-container .login-form[data-v-0b530f82]{position:relative;width:520px;max-width:100%;padding:60px 35px 0;margin:0 auto;overflow:hidden}.login-container .tips[data-v-0b530f82]{font-size:14px;color:#fff;margin-bottom:10px}.login-container .tips span[data-v-0b530f82]:first-of-type{margin-right:16px}.login-container .svg-container[data-v-0b530f82]{padding:6px 5px 6px 15px;color:#889aa4;vertical-align:middle;width:30px;display:inline-block}.login-container .title-container[data-v-0b530f82]{position:relative}.login-container .title-container .title[data-v-0b530f82]{font-size:26px;margin:0 auto 40px auto;text-align:center;font-weight:700}.login-container .show-pwd[data-v-0b530f82]{position:absolute;right:10px;top:7px;font-size:16px;color:#889aa4;cursor:pointer;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none} \ No newline at end of file diff --git a/sop-website/sop-website-server/src/main/resources/public/static/css/chunk-4e7f1f48.cf843403.css b/sop-website/sop-website-server/src/main/resources/public/static/css/chunk-4e7f1f48.cf843403.css new file mode 100644 index 00000000..2d38e5e1 --- /dev/null +++ b/sop-website/sop-website-server/src/main/resources/public/static/css/chunk-4e7f1f48.cf843403.css @@ -0,0 +1 @@ +.dashboard-container[data-v-7229b958]{margin:30px}.dashboard-text[data-v-7229b958]{font-size:18px;line-height:46px} \ No newline at end of file diff --git a/sop-website/sop-website-server/src/main/resources/public/static/css/chunk-5142434b.0ad43c8d.css b/sop-website/sop-website-server/src/main/resources/public/static/css/chunk-5142434b.0ad43c8d.css new file mode 100644 index 00000000..085c5624 --- /dev/null +++ b/sop-website/sop-website-server/src/main/resources/public/static/css/chunk-5142434b.0ad43c8d.css @@ -0,0 +1 @@ +.isp-info table th[data-v-fdc0e928]{text-align:right;padding:5px 10px} \ No newline at end of file diff --git a/sop-website/sop-website-server/src/main/resources/public/static/css/chunk-57325c88.77426e4e.css b/sop-website/sop-website-server/src/main/resources/public/static/css/chunk-57325c88.77426e4e.css new file mode 100644 index 00000000..31e0a8b3 --- /dev/null +++ b/sop-website/sop-website-server/src/main/resources/public/static/css/chunk-57325c88.77426e4e.css @@ -0,0 +1 @@ +.app-container2[data-v-c8e58128]{position:relative;width:80%;max-width:100%;margin:0 auto}.el-header[data-v-c8e58128]{background-color:#545c64;color:#333;line-height:60px}.el-aside[data-v-c8e58128]{color:#333} \ No newline at end of file diff --git a/sop-website/sop-website-server/src/main/resources/public/static/css/chunk-6440f6d0.b35fc6d0.css b/sop-website/sop-website-server/src/main/resources/public/static/css/chunk-6440f6d0.b35fc6d0.css new file mode 100644 index 00000000..f1f29a0f --- /dev/null +++ b/sop-website/sop-website-server/src/main/resources/public/static/css/chunk-6440f6d0.b35fc6d0.css @@ -0,0 +1 @@ +@font-face{font-family:fontello;src:url(../../static/fonts/fontello.e73a0647.eot);src:url(../../static/fonts/fontello.e73a0647.eot#iefix) format("embedded-opentype"),url(../../static/fonts/fontello.8d4a4e6f.woff2) format("woff2"),url(../../static/fonts/fontello.a782baa8.woff) format("woff"),url(../../static/fonts/fontello.068ca2b3.ttf) format("truetype"),url(../../static/img/fontello.9354499c.svg#fontello) format("svg");font-weight:400;font-style:normal}[class*=" fa-mavon-"]:before,[class^=fa-mavon-]:before{font-family:fontello;font-style:normal;font-weight:400;speak:none;display:inline-block;text-decoration:inherit;width:1em;margin-right:.2em;text-align:center;font-variant:normal;text-transform:none;line-height:1em;margin-left:.2em;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.fa-mavon-bold:before{content:"\E800"}.fa-mavon-italic:before{content:"\E801"}.fa-mavon-thumb-tack:before{content:"\E802"}.fa-mavon-link:before{content:"\E803"}.fa-mavon-picture-o:before{content:"\E804"}.fa-mavon-repeat:before{content:"\E805"}.fa-mavon-undo:before{content:"\E806"}.fa-mavon-trash-o:before{content:"\E807"}.fa-mavon-floppy-o:before{content:"\E808"}.fa-mavon-compress:before{content:"\E809"}.fa-mavon-eye:before{content:"\E80A"}.fa-mavon-eye-slash:before{content:"\E80B"}.fa-mavon-question-circle:before{content:"\E80C"}.fa-mavon-times:before{content:"\E80D"}.fa-mavon-align-left:before{content:"\E80F"}.fa-mavon-align-center:before{content:"\E810"}.fa-mavon-align-right:before{content:"\E811"}.fa-mavon-arrows-alt:before{content:"\F0B2"}.fa-mavon-bars:before{content:"\F0C9"}.fa-mavon-list-ul:before{content:"\F0CA"}.fa-mavon-list-ol:before{content:"\F0CB"}.fa-mavon-strikethrough:before{content:"\F0CC"}.fa-mavon-underline:before{content:"\F0CD"}.fa-mavon-table:before{content:"\F0CE"}.fa-mavon-columns:before{content:"\F0DB"}.fa-mavon-quote-left:before{content:"\F10D"}.fa-mavon-code:before{content:"\F121"}.fa-mavon-superscript:before{content:"\F12B"}.fa-mavon-subscript:before{content:"\F12C"}.fa-mavon-header:before{content:"\F1DC"}.fa-mavon-window-maximize:before{content:"\F2D0"}.markdown-body strong{font-weight:bolder}.markdown-body .hljs-center{text-align:center}.markdown-body .hljs-right{text-align:right}.markdown-body .hljs-left{text-align:left} \ No newline at end of file diff --git a/sop-website/sop-website-server/src/main/resources/public/static/css/chunk-elementUI.ded27da0.css b/sop-website/sop-website-server/src/main/resources/public/static/css/chunk-elementUI.ded27da0.css new file mode 100644 index 00000000..d4bba5f8 --- /dev/null +++ b/sop-website/sop-website-server/src/main/resources/public/static/css/chunk-elementUI.ded27da0.css @@ -0,0 +1 @@ +.el-pagination--small .arrow.disabled,.el-table--hidden,.el-table .hidden-columns,.el-table td.is-hidden>*,.el-table th.is-hidden>*{visibility:hidden}.el-input__suffix,.el-tree.is-dragging .el-tree-node__content *{pointer-events:none}.el-dropdown .el-dropdown-selfdefine:focus:active,.el-dropdown .el-dropdown-selfdefine:focus:not(.focusing),.el-message__closeBtn:focus,.el-message__content:focus,.el-popover:focus,.el-popover:focus:active,.el-popover__reference:focus:hover,.el-popover__reference:focus:not(.focusing),.el-rate:active,.el-rate:focus,.el-tooltip:focus:hover,.el-tooltip:focus:not(.focusing),.el-upload-list__item.is-success:active,.el-upload-list__item.is-success:not(.focusing):focus{outline-width:0}@font-face{font-family:element-icons;src:url(../../static/fonts/element-icons.535877f5.woff) format("woff"),url(../../static/fonts/element-icons.732389de.ttf) format("truetype");font-weight:400;font-display:"auto";font-style:normal}[class*=" el-icon-"],[class^=el-icon-]{font-family:element-icons!important;speak:none;font-style:normal;font-weight:400;font-variant:normal;text-transform:none;line-height:1;vertical-align:baseline;display:inline-block;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.el-icon-ice-cream-round:before{content:"\E6A0"}.el-icon-ice-cream-square:before{content:"\E6A3"}.el-icon-lollipop:before{content:"\E6A4"}.el-icon-potato-strips:before{content:"\E6A5"}.el-icon-milk-tea:before{content:"\E6A6"}.el-icon-ice-drink:before{content:"\E6A7"}.el-icon-ice-tea:before{content:"\E6A9"}.el-icon-coffee:before{content:"\E6AA"}.el-icon-orange:before{content:"\E6AB"}.el-icon-pear:before{content:"\E6AC"}.el-icon-apple:before{content:"\E6AD"}.el-icon-cherry:before{content:"\E6AE"}.el-icon-watermelon:before{content:"\E6AF"}.el-icon-grape:before{content:"\E6B0"}.el-icon-refrigerator:before{content:"\E6B1"}.el-icon-goblet-square-full:before{content:"\E6B2"}.el-icon-goblet-square:before{content:"\E6B3"}.el-icon-goblet-full:before{content:"\E6B4"}.el-icon-goblet:before{content:"\E6B5"}.el-icon-cold-drink:before{content:"\E6B6"}.el-icon-coffee-cup:before{content:"\E6B8"}.el-icon-water-cup:before{content:"\E6B9"}.el-icon-hot-water:before{content:"\E6BA"}.el-icon-ice-cream:before{content:"\E6BB"}.el-icon-dessert:before{content:"\E6BC"}.el-icon-sugar:before{content:"\E6BD"}.el-icon-tableware:before{content:"\E6BE"}.el-icon-burger:before{content:"\E6BF"}.el-icon-knife-fork:before{content:"\E6C1"}.el-icon-fork-spoon:before{content:"\E6C2"}.el-icon-chicken:before{content:"\E6C3"}.el-icon-food:before{content:"\E6C4"}.el-icon-dish-1:before{content:"\E6C5"}.el-icon-dish:before{content:"\E6C6"}.el-icon-moon-night:before{content:"\E6EE"}.el-icon-moon:before{content:"\E6F0"}.el-icon-cloudy-and-sunny:before{content:"\E6F1"}.el-icon-partly-cloudy:before{content:"\E6F2"}.el-icon-cloudy:before{content:"\E6F3"}.el-icon-sunny:before{content:"\E6F6"}.el-icon-sunset:before{content:"\E6F7"}.el-icon-sunrise-1:before{content:"\E6F8"}.el-icon-sunrise:before{content:"\E6F9"}.el-icon-heavy-rain:before{content:"\E6FA"}.el-icon-lightning:before{content:"\E6FB"}.el-icon-light-rain:before{content:"\E6FC"}.el-icon-wind-power:before{content:"\E6FD"}.el-icon-baseball:before{content:"\E712"}.el-icon-soccer:before{content:"\E713"}.el-icon-football:before{content:"\E715"}.el-icon-basketball:before{content:"\E716"}.el-icon-ship:before{content:"\E73F"}.el-icon-truck:before{content:"\E740"}.el-icon-bicycle:before{content:"\E741"}.el-icon-mobile-phone:before{content:"\E6D3"}.el-icon-service:before{content:"\E6D4"}.el-icon-key:before{content:"\E6E2"}.el-icon-unlock:before{content:"\E6E4"}.el-icon-lock:before{content:"\E6E5"}.el-icon-watch:before{content:"\E6FE"}.el-icon-watch-1:before{content:"\E6FF"}.el-icon-timer:before{content:"\E702"}.el-icon-alarm-clock:before{content:"\E703"}.el-icon-map-location:before{content:"\E704"}.el-icon-delete-location:before{content:"\E705"}.el-icon-add-location:before{content:"\E706"}.el-icon-location-information:before{content:"\E707"}.el-icon-location-outline:before{content:"\E708"}.el-icon-location:before{content:"\E79E"}.el-icon-place:before{content:"\E709"}.el-icon-discover:before{content:"\E70A"}.el-icon-first-aid-kit:before{content:"\E70B"}.el-icon-trophy-1:before{content:"\E70C"}.el-icon-trophy:before{content:"\E70D"}.el-icon-medal:before{content:"\E70E"}.el-icon-medal-1:before{content:"\E70F"}.el-icon-stopwatch:before{content:"\E710"}.el-icon-mic:before{content:"\E711"}.el-icon-copy-document:before{content:"\E718"}.el-icon-full-screen:before{content:"\E719"}.el-icon-switch-button:before{content:"\E71B"}.el-icon-aim:before{content:"\E71C"}.el-icon-crop:before{content:"\E71D"}.el-icon-odometer:before{content:"\E71E"}.el-icon-time:before{content:"\E71F"}.el-icon-bangzhu:before{content:"\E724"}.el-icon-close-notification:before{content:"\E726"}.el-icon-microphone:before{content:"\E727"}.el-icon-turn-off-microphone:before{content:"\E728"}.el-icon-position:before{content:"\E729"}.el-icon-postcard:before{content:"\E72A"}.el-icon-message:before{content:"\E72B"}.el-icon-chat-line-square:before{content:"\E72D"}.el-icon-chat-dot-square:before{content:"\E72E"}.el-icon-chat-dot-round:before{content:"\E72F"}.el-icon-chat-square:before{content:"\E730"}.el-icon-chat-line-round:before{content:"\E731"}.el-icon-chat-round:before{content:"\E732"}.el-icon-set-up:before{content:"\E733"}.el-icon-turn-off:before{content:"\E734"}.el-icon-open:before{content:"\E735"}.el-icon-connection:before{content:"\E736"}.el-icon-link:before{content:"\E737"}.el-icon-cpu:before{content:"\E738"}.el-icon-thumb:before{content:"\E739"}.el-icon-female:before{content:"\E73A"}.el-icon-male:before{content:"\E73B"}.el-icon-guide:before{content:"\E73C"}.el-icon-news:before{content:"\E73E"}.el-icon-price-tag:before{content:"\E744"}.el-icon-discount:before{content:"\E745"}.el-icon-wallet:before{content:"\E747"}.el-icon-coin:before{content:"\E748"}.el-icon-money:before{content:"\E749"}.el-icon-bank-card:before{content:"\E74A"}.el-icon-box:before{content:"\E74B"}.el-icon-present:before{content:"\E74C"}.el-icon-sell:before{content:"\E6D5"}.el-icon-sold-out:before{content:"\E6D6"}.el-icon-shopping-bag-2:before{content:"\E74D"}.el-icon-shopping-bag-1:before{content:"\E74E"}.el-icon-shopping-cart-2:before{content:"\E74F"}.el-icon-shopping-cart-1:before{content:"\E750"}.el-icon-shopping-cart-full:before{content:"\E751"}.el-icon-smoking:before{content:"\E752"}.el-icon-no-smoking:before{content:"\E753"}.el-icon-house:before{content:"\E754"}.el-icon-table-lamp:before{content:"\E755"}.el-icon-school:before{content:"\E756"}.el-icon-office-building:before{content:"\E757"}.el-icon-toilet-paper:before{content:"\E758"}.el-icon-notebook-2:before{content:"\E759"}.el-icon-notebook-1:before{content:"\E75A"}.el-icon-files:before{content:"\E75B"}.el-icon-collection:before{content:"\E75C"}.el-icon-receiving:before{content:"\E75D"}.el-icon-suitcase-1:before{content:"\E760"}.el-icon-suitcase:before{content:"\E761"}.el-icon-film:before{content:"\E763"}.el-icon-collection-tag:before{content:"\E765"}.el-icon-data-analysis:before{content:"\E766"}.el-icon-pie-chart:before{content:"\E767"}.el-icon-data-board:before{content:"\E768"}.el-icon-data-line:before{content:"\E76D"}.el-icon-reading:before{content:"\E769"}.el-icon-magic-stick:before{content:"\E76A"}.el-icon-coordinate:before{content:"\E76B"}.el-icon-mouse:before{content:"\E76C"}.el-icon-brush:before{content:"\E76E"}.el-icon-headset:before{content:"\E76F"}.el-icon-umbrella:before{content:"\E770"}.el-icon-scissors:before{content:"\E771"}.el-icon-mobile:before{content:"\E773"}.el-icon-attract:before{content:"\E774"}.el-icon-monitor:before{content:"\E775"}.el-icon-search:before{content:"\E778"}.el-icon-takeaway-box:before{content:"\E77A"}.el-icon-paperclip:before{content:"\E77D"}.el-icon-printer:before{content:"\E77E"}.el-icon-document-add:before{content:"\E782"}.el-icon-document:before{content:"\E785"}.el-icon-document-checked:before{content:"\E786"}.el-icon-document-copy:before{content:"\E787"}.el-icon-document-delete:before{content:"\E788"}.el-icon-document-remove:before{content:"\E789"}.el-icon-tickets:before{content:"\E78B"}.el-icon-folder-checked:before{content:"\E77F"}.el-icon-folder-delete:before{content:"\E780"}.el-icon-folder-remove:before{content:"\E781"}.el-icon-folder-add:before{content:"\E783"}.el-icon-folder-opened:before{content:"\E784"}.el-icon-folder:before{content:"\E78A"}.el-icon-edit-outline:before{content:"\E764"}.el-icon-edit:before{content:"\E78C"}.el-icon-date:before{content:"\E78E"}.el-icon-c-scale-to-original:before{content:"\E7C6"}.el-icon-view:before{content:"\E6CE"}.el-icon-loading:before{content:"\E6CF"}.el-icon-rank:before{content:"\E6D1"}.el-icon-sort-down:before{content:"\E7C4"}.el-icon-sort-up:before{content:"\E7C5"}.el-icon-sort:before{content:"\E6D2"}.el-icon-finished:before{content:"\E6CD"}.el-icon-refresh-left:before{content:"\E6C7"}.el-icon-refresh-right:before{content:"\E6C8"}.el-icon-refresh:before{content:"\E6D0"}.el-icon-video-play:before{content:"\E7C0"}.el-icon-video-pause:before{content:"\E7C1"}.el-icon-d-arrow-right:before{content:"\E6DC"}.el-icon-d-arrow-left:before{content:"\E6DD"}.el-icon-arrow-up:before{content:"\E6E1"}.el-icon-arrow-down:before{content:"\E6DF"}.el-icon-arrow-right:before{content:"\E6E0"}.el-icon-arrow-left:before{content:"\E6DE"}.el-icon-top-right:before{content:"\E6E7"}.el-icon-top-left:before{content:"\E6E8"}.el-icon-top:before{content:"\E6E6"}.el-icon-bottom:before{content:"\E6EB"}.el-icon-right:before{content:"\E6E9"}.el-icon-back:before{content:"\E6EA"}.el-icon-bottom-right:before{content:"\E6EC"}.el-icon-bottom-left:before{content:"\E6ED"}.el-icon-caret-top:before{content:"\E78F"}.el-icon-caret-bottom:before{content:"\E790"}.el-icon-caret-right:before{content:"\E791"}.el-icon-caret-left:before{content:"\E792"}.el-icon-d-caret:before{content:"\E79A"}.el-icon-share:before{content:"\E793"}.el-icon-menu:before{content:"\E798"}.el-icon-s-grid:before{content:"\E7A6"}.el-icon-s-check:before{content:"\E7A7"}.el-icon-s-data:before{content:"\E7A8"}.el-icon-s-opportunity:before{content:"\E7AA"}.el-icon-s-custom:before{content:"\E7AB"}.el-icon-s-claim:before{content:"\E7AD"}.el-icon-s-finance:before{content:"\E7AE"}.el-icon-s-comment:before{content:"\E7AF"}.el-icon-s-flag:before{content:"\E7B0"}.el-icon-s-marketing:before{content:"\E7B1"}.el-icon-s-shop:before{content:"\E7B4"}.el-icon-s-open:before{content:"\E7B5"}.el-icon-s-management:before{content:"\E7B6"}.el-icon-s-ticket:before{content:"\E7B7"}.el-icon-s-release:before{content:"\E7B8"}.el-icon-s-home:before{content:"\E7B9"}.el-icon-s-promotion:before{content:"\E7BA"}.el-icon-s-operation:before{content:"\E7BB"}.el-icon-s-unfold:before{content:"\E7BC"}.el-icon-s-fold:before{content:"\E7A9"}.el-icon-s-platform:before{content:"\E7BD"}.el-icon-s-order:before{content:"\E7BE"}.el-icon-s-cooperation:before{content:"\E7BF"}.el-icon-bell:before{content:"\E725"}.el-icon-message-solid:before{content:"\E799"}.el-icon-video-camera:before{content:"\E772"}.el-icon-video-camera-solid:before{content:"\E796"}.el-icon-camera:before{content:"\E779"}.el-icon-camera-solid:before{content:"\E79B"}.el-icon-download:before{content:"\E77C"}.el-icon-upload2:before{content:"\E77B"}.el-icon-upload:before{content:"\E7C3"}.el-icon-picture-outline-round:before{content:"\E75F"}.el-icon-picture-outline:before{content:"\E75E"}.el-icon-picture:before{content:"\E79F"}.el-icon-close:before{content:"\E6DB"}.el-icon-check:before{content:"\E6DA"}.el-icon-plus:before{content:"\E6D9"}.el-icon-minus:before{content:"\E6D8"}.el-icon-help:before{content:"\E73D"}.el-icon-s-help:before{content:"\E7B3"}.el-icon-circle-close:before{content:"\E78D"}.el-icon-circle-check:before{content:"\E720"}.el-icon-circle-plus-outline:before{content:"\E723"}.el-icon-remove-outline:before{content:"\E722"}.el-icon-zoom-out:before{content:"\E776"}.el-icon-zoom-in:before{content:"\E777"}.el-icon-error:before{content:"\E79D"}.el-icon-success:before{content:"\E79C"}.el-icon-circle-plus:before{content:"\E7A0"}.el-icon-remove:before{content:"\E7A2"}.el-icon-info:before{content:"\E7A1"}.el-icon-question:before{content:"\E7A4"}.el-icon-warning-outline:before{content:"\E6C9"}.el-icon-warning:before{content:"\E7A3"}.el-icon-goods:before{content:"\E7C2"}.el-icon-s-goods:before{content:"\E7B2"}.el-icon-star-off:before{content:"\E717"}.el-icon-star-on:before{content:"\E797"}.el-icon-more-outline:before{content:"\E6CC"}.el-icon-more:before{content:"\E794"}.el-icon-phone-outline:before{content:"\E6CB"}.el-icon-phone:before{content:"\E795"}.el-icon-user:before{content:"\E6E3"}.el-icon-user-solid:before{content:"\E7A5"}.el-icon-setting:before{content:"\E6CA"}.el-icon-s-tools:before{content:"\E7AC"}.el-icon-delete:before{content:"\E6D7"}.el-icon-delete-solid:before{content:"\E7C9"}.el-icon-eleme:before{content:"\E7C7"}.el-icon-platform-eleme:before{content:"\E7CA"}.el-icon-loading{-webkit-animation:rotating 2s linear infinite;animation:rotating 2s linear infinite}.el-icon--right{margin-left:5px}.el-icon--left{margin-right:5px}@-webkit-keyframes rotating{0%{-webkit-transform:rotate(0);transform:rotate(0)}to{-webkit-transform:rotate(1turn);transform:rotate(1turn)}}@keyframes rotating{0%{-webkit-transform:rotate(0);transform:rotate(0)}to{-webkit-transform:rotate(1turn);transform:rotate(1turn)}}.el-pagination{white-space:nowrap;padding:2px 5px;color:#303133;font-weight:700}.el-pagination:after,.el-pagination:before{display:table;content:""}.el-pagination:after{clear:both}.el-pagination button,.el-pagination span:not([class*=suffix]){display:inline-block;font-size:13px;min-width:35.5px;height:28px;line-height:28px;vertical-align:top;-webkit-box-sizing:border-box;box-sizing:border-box}.el-pagination .el-input__inner{text-align:center;-moz-appearance:textfield;line-height:normal}.el-pagination .el-input__suffix{right:0;-webkit-transform:scale(.8);transform:scale(.8)}.el-pagination .el-select .el-input{width:100px;margin:0 5px}.el-pagination .el-select .el-input .el-input__inner{padding-right:25px;border-radius:3px}.el-pagination button{border:none;padding:0 6px;background:0 0}.el-pagination button:focus{outline:0}.el-pagination button:hover{color:#409eff}.el-pagination button:disabled{color:#c0c4cc;background-color:#fff;cursor:not-allowed}.el-pagination .btn-next,.el-pagination .btn-prev{background:50% no-repeat #fff;background-size:16px;cursor:pointer;margin:0;color:#303133}.el-pagination .btn-next .el-icon,.el-pagination .btn-prev .el-icon{display:block;font-size:12px;font-weight:700}.el-pagination .btn-prev{padding-right:12px}.el-pagination .btn-next{padding-left:12px}.el-pagination .el-pager li.disabled{color:#c0c4cc;cursor:not-allowed}.el-pager li,.el-pager li.btn-quicknext:hover,.el-pager li.btn-quickprev:hover{cursor:pointer}.el-pagination--small .btn-next,.el-pagination--small .btn-prev,.el-pagination--small .el-pager li,.el-pagination--small .el-pager li.btn-quicknext,.el-pagination--small .el-pager li.btn-quickprev,.el-pagination--small .el-pager li:last-child{border-color:transparent;font-size:12px;line-height:22px;height:22px;min-width:22px}.el-pagination--small .more:before,.el-pagination--small li.more:before{line-height:24px}.el-pagination--small button,.el-pagination--small span:not([class*=suffix]){height:22px;line-height:22px}.el-pagination--small .el-pagination__editor,.el-pagination--small .el-pagination__editor.el-input .el-input__inner{height:22px}.el-pagination__sizes{margin:0 10px 0 0;font-weight:400;color:#606266}.el-pagination__sizes .el-input .el-input__inner{font-size:13px;padding-left:8px}.el-pagination__sizes .el-input .el-input__inner:hover{border-color:#409eff}.el-pagination__total{margin-right:10px;font-weight:400;color:#606266}.el-pagination__jump{margin-left:24px;font-weight:400;color:#606266}.el-pagination__jump .el-input__inner{padding:0 3px}.el-pagination__rightwrapper{float:right}.el-pagination__editor{line-height:18px;padding:0 2px;height:28px;text-align:center;margin:0 2px;-webkit-box-sizing:border-box;box-sizing:border-box;border-radius:3px}.el-pager,.el-pagination.is-background .btn-next,.el-pagination.is-background .btn-prev{padding:0}.el-pagination__editor.el-input{width:50px}.el-pagination__editor.el-input .el-input__inner{height:28px}.el-pagination__editor .el-input__inner::-webkit-inner-spin-button,.el-pagination__editor .el-input__inner::-webkit-outer-spin-button{-webkit-appearance:none;margin:0}.el-pagination.is-background .btn-next,.el-pagination.is-background .btn-prev,.el-pagination.is-background .el-pager li{margin:0 5px;background-color:#f4f4f5;color:#606266;min-width:30px;border-radius:2px}.el-pagination.is-background .btn-next.disabled,.el-pagination.is-background .btn-next:disabled,.el-pagination.is-background .btn-prev.disabled,.el-pagination.is-background .btn-prev:disabled,.el-pagination.is-background .el-pager li.disabled{color:#c0c4cc}.el-pagination.is-background .el-pager li:not(.disabled):hover{color:#409eff}.el-pagination.is-background .el-pager li:not(.disabled).active{background-color:#409eff;color:#fff}.el-dialog,.el-pager li{background:#fff;-webkit-box-sizing:border-box}.el-pagination.is-background.el-pagination--small .btn-next,.el-pagination.is-background.el-pagination--small .btn-prev,.el-pagination.is-background.el-pagination--small .el-pager li{margin:0 3px;min-width:22px}.el-pager,.el-pager li{vertical-align:top;margin:0;display:inline-block}.el-pager{-ms-user-select:none;user-select:none;list-style:none;font-size:0}.el-date-table,.el-pager,.el-table th{-webkit-user-select:none;-moz-user-select:none}.el-pager .more:before{line-height:30px}.el-pager li{padding:0 4px;font-size:13px;min-width:35.5px;height:28px;line-height:28px;-webkit-box-sizing:border-box;box-sizing:border-box;text-align:center}.el-menu--collapse .el-menu .el-submenu,.el-menu--popup{min-width:200px}.el-pager li.btn-quicknext,.el-pager li.btn-quickprev{line-height:28px;color:#303133}.el-pager li.btn-quicknext.disabled,.el-pager li.btn-quickprev.disabled{color:#c0c4cc}.el-pager li.active+li{border-left:0}.el-pager li:hover{color:#409eff}.el-pager li.active{color:#409eff;cursor:default}@-webkit-keyframes v-modal-in{0%{opacity:0}}@-webkit-keyframes v-modal-out{to{opacity:0}}.el-dialog{position:relative;margin:0 auto 50px;border-radius:2px;-webkit-box-shadow:0 1px 3px rgba(0,0,0,.3);box-shadow:0 1px 3px rgba(0,0,0,.3);-webkit-box-sizing:border-box;box-sizing:border-box;width:50%}.el-dialog.is-fullscreen{width:100%;margin-top:0;margin-bottom:0;height:100%;overflow:auto}.el-dialog__wrapper{position:fixed;top:0;right:0;bottom:0;left:0;overflow:auto;margin:0}.el-dialog__header{padding:20px 20px 10px}.el-dialog__headerbtn{position:absolute;top:20px;right:20px;padding:0;background:0 0;border:none;outline:0;cursor:pointer;font-size:16px}.el-dialog__headerbtn .el-dialog__close{color:#909399}.el-dialog__headerbtn:focus .el-dialog__close,.el-dialog__headerbtn:hover .el-dialog__close{color:#409eff}.el-dialog__title{line-height:24px;font-size:18px;color:#303133}.el-dialog__body{padding:30px 20px;color:#606266;font-size:14px;word-break:break-all}.el-dialog__footer{padding:10px 20px 20px;text-align:right;-webkit-box-sizing:border-box;box-sizing:border-box}.el-dialog--center{text-align:center}.el-dialog--center .el-dialog__body{text-align:initial;padding:25px 25px 30px}.el-dialog--center .el-dialog__footer{text-align:inherit}.dialog-fade-enter-active{-webkit-animation:dialog-fade-in .3s;animation:dialog-fade-in .3s}.dialog-fade-leave-active{-webkit-animation:dialog-fade-out .3s;animation:dialog-fade-out .3s}@-webkit-keyframes dialog-fade-in{0%{-webkit-transform:translate3d(0,-20px,0);transform:translate3d(0,-20px,0);opacity:0}to{-webkit-transform:translateZ(0);transform:translateZ(0);opacity:1}}@keyframes dialog-fade-in{0%{-webkit-transform:translate3d(0,-20px,0);transform:translate3d(0,-20px,0);opacity:0}to{-webkit-transform:translateZ(0);transform:translateZ(0);opacity:1}}@-webkit-keyframes dialog-fade-out{0%{-webkit-transform:translateZ(0);transform:translateZ(0);opacity:1}to{-webkit-transform:translate3d(0,-20px,0);transform:translate3d(0,-20px,0);opacity:0}}@keyframes dialog-fade-out{0%{-webkit-transform:translateZ(0);transform:translateZ(0);opacity:1}to{-webkit-transform:translate3d(0,-20px,0);transform:translate3d(0,-20px,0);opacity:0}}.el-autocomplete{position:relative;display:inline-block}.el-autocomplete-suggestion{margin:5px 0;-webkit-box-shadow:0 2px 12px 0 rgba(0,0,0,.1);box-shadow:0 2px 12px 0 rgba(0,0,0,.1);border-radius:4px;border:1px solid #e4e7ed;-webkit-box-sizing:border-box;box-sizing:border-box;background-color:#fff}.el-dropdown-menu,.el-menu--collapse .el-submenu .el-menu{z-index:10;-webkit-box-shadow:0 2px 12px 0 rgba(0,0,0,.1)}.el-autocomplete-suggestion__wrap{max-height:280px;padding:10px 0;-webkit-box-sizing:border-box;box-sizing:border-box}.el-autocomplete-suggestion__list{margin:0;padding:0}.el-autocomplete-suggestion li{padding:0 20px;margin:0;line-height:34px;cursor:pointer;color:#606266;font-size:14px;list-style:none;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.el-autocomplete-suggestion li.highlighted,.el-autocomplete-suggestion li:hover{background-color:#f5f7fa}.el-autocomplete-suggestion li.divider{margin-top:6px;border-top:1px solid #000}.el-autocomplete-suggestion li.divider:last-child{margin-bottom:-6px}.el-autocomplete-suggestion.is-loading li{text-align:center;height:100px;line-height:100px;font-size:20px;color:#999}.el-autocomplete-suggestion.is-loading li:after{display:inline-block;content:"";height:100%;vertical-align:middle}.el-autocomplete-suggestion.is-loading li:hover{background-color:#fff}.el-autocomplete-suggestion.is-loading .el-icon-loading{vertical-align:middle}.el-dropdown{display:inline-block;position:relative;color:#606266;font-size:14px}.el-dropdown .el-button-group{display:block}.el-dropdown .el-button-group .el-button{float:none}.el-dropdown .el-dropdown__caret-button{padding-left:5px;padding-right:5px;position:relative;border-left:none}.el-dropdown .el-dropdown__caret-button:before{content:"";position:absolute;display:block;width:1px;top:5px;bottom:5px;left:0;background:hsla(0,0%,100%,.5)}.el-dropdown .el-dropdown__caret-button.el-button--default:before{background:rgba(220,223,230,.5)}.el-dropdown .el-dropdown__caret-button:hover:before{top:0;bottom:0}.el-dropdown .el-dropdown__caret-button .el-dropdown__icon{padding-left:0}.el-dropdown__icon{font-size:12px;margin:0 3px}.el-dropdown-menu{position:absolute;top:0;left:0;padding:10px 0;margin:5px 0;background-color:#fff;border:1px solid #ebeef5;border-radius:4px;-webkit-box-shadow:0 2px 12px 0 rgba(0,0,0,.1);box-shadow:0 2px 12px 0 rgba(0,0,0,.1)}.el-dropdown-menu__item{list-style:none;line-height:36px;padding:0 20px;margin:0;font-size:14px;color:#606266;cursor:pointer;outline:0}.el-dropdown-menu__item:focus,.el-dropdown-menu__item:not(.is-disabled):hover{background-color:#ecf5ff;color:#66b1ff}.el-dropdown-menu__item i{margin-right:5px}.el-dropdown-menu__item--divided{position:relative;margin-top:6px;border-top:1px solid #ebeef5}.el-dropdown-menu__item--divided:before{content:"";height:6px;display:block;margin:0 -20px;background-color:#fff}.el-dropdown-menu__item.is-disabled{cursor:default;color:#bbb;pointer-events:none}.el-dropdown-menu--medium{padding:6px 0}.el-dropdown-menu--medium .el-dropdown-menu__item{line-height:30px;padding:0 17px;font-size:14px}.el-dropdown-menu--medium .el-dropdown-menu__item.el-dropdown-menu__item--divided{margin-top:6px}.el-dropdown-menu--medium .el-dropdown-menu__item.el-dropdown-menu__item--divided:before{height:6px;margin:0 -17px}.el-dropdown-menu--small{padding:6px 0}.el-dropdown-menu--small .el-dropdown-menu__item{line-height:27px;padding:0 15px;font-size:13px}.el-dropdown-menu--small .el-dropdown-menu__item.el-dropdown-menu__item--divided{margin-top:4px}.el-dropdown-menu--small .el-dropdown-menu__item.el-dropdown-menu__item--divided:before{height:4px;margin:0 -15px}.el-dropdown-menu--mini{padding:3px 0}.el-dropdown-menu--mini .el-dropdown-menu__item{line-height:24px;padding:0 10px;font-size:12px}.el-dropdown-menu--mini .el-dropdown-menu__item.el-dropdown-menu__item--divided{margin-top:3px}.el-dropdown-menu--mini .el-dropdown-menu__item.el-dropdown-menu__item--divided:before{height:3px;margin:0 -10px}.el-menu{border-right:1px solid #e6e6e6;list-style:none;position:relative;margin:0;padding-left:0}.el-menu,.el-menu--horizontal>.el-menu-item:not(.is-disabled):focus,.el-menu--horizontal>.el-menu-item:not(.is-disabled):hover,.el-menu--horizontal>.el-submenu .el-submenu__title:hover{background-color:#fff}.el-menu:after,.el-menu:before{display:table;content:""}.el-menu:after{clear:both}.el-menu.el-menu--horizontal{border-bottom:1px solid #e6e6e6}.el-menu--horizontal{border-right:none}.el-menu--horizontal>.el-menu-item{float:left;height:60px;line-height:60px;margin:0;border-bottom:2px solid transparent;color:#909399}.el-menu--horizontal>.el-menu-item a,.el-menu--horizontal>.el-menu-item a:hover{color:inherit}.el-menu--horizontal>.el-submenu{float:left}.el-menu--horizontal>.el-submenu:focus,.el-menu--horizontal>.el-submenu:hover{outline:0}.el-menu--horizontal>.el-submenu:focus .el-submenu__title,.el-menu--horizontal>.el-submenu:hover .el-submenu__title{color:#303133}.el-menu--horizontal>.el-submenu.is-active .el-submenu__title{border-bottom:2px solid #409eff;color:#303133}.el-menu--horizontal>.el-submenu .el-submenu__title{height:60px;line-height:60px;border-bottom:2px solid transparent;color:#909399}.el-menu--horizontal>.el-submenu .el-submenu__icon-arrow{position:static;vertical-align:middle;margin-left:8px;margin-top:-3px}.el-menu--horizontal .el-menu .el-menu-item,.el-menu--horizontal .el-menu .el-submenu__title{background-color:#fff;float:none;height:36px;line-height:36px;padding:0 10px;color:#909399}.el-menu--horizontal .el-menu .el-menu-item.is-active,.el-menu--horizontal .el-menu .el-submenu.is-active>.el-submenu__title{color:#303133}.el-menu--horizontal .el-menu-item:not(.is-disabled):focus,.el-menu--horizontal .el-menu-item:not(.is-disabled):hover{outline:0;color:#303133}.el-menu--horizontal>.el-menu-item.is-active{border-bottom:2px solid #409eff;color:#303133}.el-menu--collapse{width:64px}.el-menu--collapse>.el-menu-item [class^=el-icon-],.el-menu--collapse>.el-submenu>.el-submenu__title [class^=el-icon-]{margin:0;vertical-align:middle;width:24px;text-align:center}.el-menu--collapse>.el-menu-item .el-submenu__icon-arrow,.el-menu--collapse>.el-submenu>.el-submenu__title .el-submenu__icon-arrow{display:none}.el-menu--collapse>.el-menu-item span,.el-menu--collapse>.el-submenu>.el-submenu__title span{height:0;width:0;overflow:hidden;visibility:hidden;display:inline-block}.el-menu--collapse>.el-menu-item.is-active i{color:inherit}.el-menu--collapse .el-submenu{position:relative}.el-menu--collapse .el-submenu .el-menu{position:absolute;margin-left:5px;top:0;left:100%;border:1px solid #e4e7ed;border-radius:2px;-webkit-box-shadow:0 2px 12px 0 rgba(0,0,0,.1);box-shadow:0 2px 12px 0 rgba(0,0,0,.1)}.el-menu-item,.el-submenu__title{height:56px;line-height:56px;position:relative;-webkit-box-sizing:border-box;white-space:nowrap;list-style:none}.el-menu--collapse .el-submenu.is-opened>.el-submenu__title .el-submenu__icon-arrow{-webkit-transform:none;transform:none}.el-menu--popup{z-index:100;border:none;padding:5px 0;border-radius:2px;-webkit-box-shadow:0 2px 12px 0 rgba(0,0,0,.1);box-shadow:0 2px 12px 0 rgba(0,0,0,.1)}.el-menu--popup-bottom-start{margin-top:5px}.el-menu--popup-right-start{margin-left:5px;margin-right:5px}.el-menu-item{font-size:14px;color:#303133;padding:0 20px;cursor:pointer;-webkit-transition:border-color .3s,background-color .3s,color .3s;transition:border-color .3s,background-color .3s,color .3s;-webkit-box-sizing:border-box;box-sizing:border-box}.el-menu-item *{vertical-align:middle}.el-menu-item i{color:#909399}.el-menu-item:focus,.el-menu-item:hover{outline:0;background-color:#ecf5ff}.el-menu-item.is-disabled{opacity:.25;cursor:not-allowed;background:0 0!important}.el-menu-item [class^=el-icon-]{margin-right:5px;width:24px;text-align:center;font-size:18px;vertical-align:middle}.el-menu-item.is-active{color:#409eff}.el-menu-item.is-active i{color:inherit}.el-submenu{list-style:none;margin:0;padding-left:0}.el-submenu__title{font-size:14px;color:#303133;padding:0 20px;cursor:pointer;-webkit-transition:border-color .3s,background-color .3s,color .3s;transition:border-color .3s,background-color .3s,color .3s;-webkit-box-sizing:border-box;box-sizing:border-box}.el-submenu__title *{vertical-align:middle}.el-submenu__title i{color:#909399}.el-submenu__title:focus,.el-submenu__title:hover{outline:0;background-color:#ecf5ff}.el-submenu__title.is-disabled{opacity:.25;cursor:not-allowed;background:0 0!important}.el-submenu__title:hover{background-color:#ecf5ff}.el-submenu .el-menu{border:none}.el-submenu .el-menu-item{height:50px;line-height:50px;padding:0 45px;min-width:200px}.el-submenu__icon-arrow{position:absolute;top:50%;right:20px;margin-top:-7px;-webkit-transition:-webkit-transform .3s;transition:-webkit-transform .3s;transition:transform .3s;transition:transform .3s,-webkit-transform .3s;font-size:12px}.el-submenu.is-active .el-submenu__title{border-bottom-color:#409eff}.el-submenu.is-opened>.el-submenu__title .el-submenu__icon-arrow{-webkit-transform:rotate(180deg);transform:rotate(180deg)}.el-submenu.is-disabled .el-menu-item,.el-submenu.is-disabled .el-submenu__title{opacity:.25;cursor:not-allowed;background:0 0!important}.el-submenu [class^=el-icon-]{vertical-align:middle;margin-right:5px;width:24px;text-align:center;font-size:18px}.el-menu-item-group>ul{padding:0}.el-menu-item-group__title{padding:7px 0 7px 20px;line-height:normal;font-size:12px;color:#909399}.el-radio-button__inner,.el-radio-group{display:inline-block;line-height:1;vertical-align:middle}.horizontal-collapse-transition .el-submenu__title .el-submenu__icon-arrow{-webkit-transition:.2s;transition:.2s;opacity:0}.el-radio-group{font-size:0}.el-radio-button{position:relative;display:inline-block;outline:0}.el-radio-button__inner{white-space:nowrap;background:#fff;border:1px solid #dcdfe6;font-weight:500;border-left:0;color:#606266;-webkit-appearance:none;text-align:center;-webkit-box-sizing:border-box;box-sizing:border-box;outline:0;margin:0;position:relative;cursor:pointer;-webkit-transition:all .3s cubic-bezier(.645,.045,.355,1);transition:all .3s cubic-bezier(.645,.045,.355,1);padding:12px 20px;font-size:14px;border-radius:0}.el-radio-button__inner.is-round{padding:12px 20px}.el-radio-button__inner:hover{color:#409eff}.el-radio-button__inner [class*=el-icon-]{line-height:.9}.el-radio-button__inner [class*=el-icon-]+span{margin-left:5px}.el-radio-button:first-child .el-radio-button__inner{border-left:1px solid #dcdfe6;border-radius:4px 0 0 4px;-webkit-box-shadow:none!important;box-shadow:none!important}.el-radio-button__orig-radio{opacity:0;outline:0;position:absolute;z-index:-1}.el-radio-button__orig-radio:checked+.el-radio-button__inner{color:#fff;background-color:#409eff;border-color:#409eff;-webkit-box-shadow:-1px 0 0 0 #409eff;box-shadow:-1px 0 0 0 #409eff}.el-radio-button__orig-radio:disabled+.el-radio-button__inner{color:#c0c4cc;cursor:not-allowed;background-image:none;background-color:#fff;border-color:#ebeef5;-webkit-box-shadow:none;box-shadow:none}.el-radio-button__orig-radio:disabled:checked+.el-radio-button__inner{background-color:#f2f6fc}.el-radio-button:last-child .el-radio-button__inner{border-radius:0 4px 4px 0}.el-popover,.el-radio-button:first-child:last-child .el-radio-button__inner{border-radius:4px}.el-radio-button--medium .el-radio-button__inner{padding:10px 20px;font-size:14px;border-radius:0}.el-radio-button--medium .el-radio-button__inner.is-round{padding:10px 20px}.el-radio-button--small .el-radio-button__inner{padding:9px 15px;font-size:12px;border-radius:0}.el-radio-button--small .el-radio-button__inner.is-round{padding:9px 15px}.el-radio-button--mini .el-radio-button__inner{padding:7px 15px;font-size:12px;border-radius:0}.el-radio-button--mini .el-radio-button__inner.is-round{padding:7px 15px}.el-radio-button:focus:not(.is-focus):not(:active):not(.is-disabled){-webkit-box-shadow:0 0 2px 2px #409eff;box-shadow:0 0 2px 2px #409eff}.el-switch{display:-webkit-inline-box;display:-ms-inline-flexbox;display:inline-flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;position:relative;font-size:14px;line-height:20px;height:20px;vertical-align:middle}.el-switch__core,.el-switch__label{display:inline-block;cursor:pointer}.el-switch.is-disabled .el-switch__core,.el-switch.is-disabled .el-switch__label{cursor:not-allowed}.el-switch__label{-webkit-transition:.2s;transition:.2s;height:20px;font-size:14px;font-weight:500;vertical-align:middle;color:#303133}.el-switch__label.is-active{color:#409eff}.el-switch__label--left{margin-right:10px}.el-switch__label--right{margin-left:10px}.el-switch__label *{line-height:1;font-size:14px;display:inline-block}.el-switch__input{position:absolute;width:0;height:0;opacity:0;margin:0}.el-switch__core{margin:0;position:relative;width:40px;height:20px;border:1px solid #dcdfe6;outline:0;border-radius:10px;-webkit-box-sizing:border-box;box-sizing:border-box;background:#dcdfe6;-webkit-transition:border-color .3s,background-color .3s;transition:border-color .3s,background-color .3s;vertical-align:middle}.el-switch__core:after{content:"";position:absolute;top:1px;left:1px;border-radius:100%;-webkit-transition:all .3s;transition:all .3s;width:16px;height:16px;background-color:#fff}.el-switch.is-checked .el-switch__core{border-color:#409eff;background-color:#409eff}.el-switch.is-checked .el-switch__core:after{left:100%;margin-left:-17px}.el-switch.is-disabled{opacity:.6}.el-switch--wide .el-switch__label.el-switch__label--left span{left:10px}.el-switch--wide .el-switch__label.el-switch__label--right span{right:10px}.el-switch .label-fade-enter,.el-switch .label-fade-leave-active{opacity:0}.el-select-dropdown{position:absolute;z-index:1001;border:1px solid #e4e7ed;border-radius:4px;background-color:#fff;-webkit-box-shadow:0 2px 12px 0 rgba(0,0,0,.1);box-shadow:0 2px 12px 0 rgba(0,0,0,.1);-webkit-box-sizing:border-box;box-sizing:border-box;margin:5px 0}.el-select-dropdown.is-multiple .el-select-dropdown__item.selected{color:#409eff;background-color:#fff}.el-select-dropdown.is-multiple .el-select-dropdown__item.selected.hover{background-color:#f5f7fa}.el-select-dropdown.is-multiple .el-select-dropdown__item.selected:after{position:absolute;right:20px;font-family:element-icons;content:"\E6DA";font-size:12px;font-weight:700;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.el-select-dropdown .el-scrollbar.is-empty .el-select-dropdown__list{padding:0}.el-select-dropdown__empty{padding:10px 0;margin:0;text-align:center;color:#999;font-size:14px}.el-select-dropdown__wrap{max-height:274px}.el-select-dropdown__list{list-style:none;padding:6px 0;margin:0;-webkit-box-sizing:border-box;box-sizing:border-box}.el-select-dropdown__item{font-size:14px;padding:0 20px;position:relative;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;color:#606266;height:34px;line-height:34px;-webkit-box-sizing:border-box;box-sizing:border-box;cursor:pointer}.el-select-dropdown__item.is-disabled{color:#c0c4cc;cursor:not-allowed}.el-select-dropdown__item.is-disabled:hover{background-color:#fff}.el-select-dropdown__item.hover,.el-select-dropdown__item:hover{background-color:#f5f7fa}.el-select-dropdown__item.selected{color:#409eff;font-weight:700}.el-select-group{margin:0;padding:0}.el-select-group__wrap{position:relative;list-style:none;margin:0;padding:0}.el-select-group__wrap:not(:last-of-type){padding-bottom:24px}.el-select-group__wrap:not(:last-of-type):after{content:"";position:absolute;display:block;left:20px;right:20px;bottom:12px;height:1px;background:#e4e7ed}.el-select-group__title{padding-left:20px;font-size:12px;color:#909399;line-height:30px}.el-select-group .el-select-dropdown__item{padding-left:20px}.el-select{display:inline-block;position:relative}.el-select .el-select__tags>span{display:contents}.el-select:hover .el-input__inner{border-color:#c0c4cc}.el-select .el-input__inner{cursor:pointer;padding-right:35px}.el-select .el-input__inner:focus{border-color:#409eff}.el-select .el-input .el-select__caret{color:#c0c4cc;font-size:14px;-webkit-transition:-webkit-transform .3s;transition:-webkit-transform .3s;transition:transform .3s;transition:transform .3s,-webkit-transform .3s;-webkit-transform:rotate(180deg);transform:rotate(180deg);cursor:pointer}.el-select .el-input .el-select__caret.is-reverse{-webkit-transform:rotate(0);transform:rotate(0)}.el-select .el-input .el-select__caret.is-show-close{font-size:14px;text-align:center;-webkit-transform:rotate(180deg);transform:rotate(180deg);border-radius:100%;color:#c0c4cc;-webkit-transition:color .2s cubic-bezier(.645,.045,.355,1);transition:color .2s cubic-bezier(.645,.045,.355,1)}.el-select .el-input .el-select__caret.is-show-close:hover{color:#909399}.el-select .el-input.is-disabled .el-input__inner{cursor:not-allowed}.el-select .el-input.is-disabled .el-input__inner:hover{border-color:#e4e7ed}.el-select .el-input.is-focus .el-input__inner{border-color:#409eff}.el-select>.el-input{display:block}.el-select__input{border:none;outline:0;padding:0;margin-left:15px;color:#666;font-size:14px;-webkit-appearance:none;-moz-appearance:none;appearance:none;height:28px;background-color:transparent}.el-select__input.is-mini{height:14px}.el-select__close{cursor:pointer;position:absolute;top:8px;z-index:1000;right:25px;color:#c0c4cc;line-height:18px;font-size:14px}.el-select__close:hover{color:#909399}.el-select__tags{position:absolute;line-height:normal;white-space:normal;z-index:1;top:50%;-webkit-transform:translateY(-50%);transform:translateY(-50%);display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-ms-flex-wrap:wrap;flex-wrap:wrap}.el-select .el-tag__close{margin-top:-2px}.el-select .el-tag{-webkit-box-sizing:border-box;box-sizing:border-box;border-color:transparent;margin:2px 0 2px 6px;background-color:#f0f2f5}.el-select .el-tag__close.el-icon-close{background-color:#c0c4cc;right:-7px;top:0;color:#fff}.el-select .el-tag__close.el-icon-close:hover{background-color:#909399}.el-table,.el-table__expanded-cell{background-color:#fff}.el-select .el-tag__close.el-icon-close:before{display:block;-webkit-transform:translateY(.5px);transform:translateY(.5px)}.el-table{position:relative;overflow:hidden;-webkit-box-sizing:border-box;box-sizing:border-box;-webkit-box-flex:1;-ms-flex:1;flex:1;width:100%;max-width:100%;font-size:14px;color:#606266}.el-table--mini,.el-table--small,.el-table__expand-icon{font-size:12px}.el-table__empty-block{min-height:60px;text-align:center;width:100%;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;-webkit-box-align:center;-ms-flex-align:center;align-items:center}.el-table__empty-text{line-height:60px;width:50%;color:#909399}.el-table__expand-column .cell{padding:0;text-align:center}.el-table__expand-icon{position:relative;cursor:pointer;color:#666;-webkit-transition:-webkit-transform .2s ease-in-out;transition:-webkit-transform .2s ease-in-out;transition:transform .2s ease-in-out;transition:transform .2s ease-in-out,-webkit-transform .2s ease-in-out;height:20px}.el-table__expand-icon--expanded{-webkit-transform:rotate(90deg);transform:rotate(90deg)}.el-table__expand-icon>.el-icon{position:absolute;left:50%;top:50%;margin-left:-5px;margin-top:-5px}.el-table__expanded-cell[class*=cell]{padding:20px 50px}.el-table__expanded-cell:hover{background-color:transparent!important}.el-table__placeholder{display:inline-block;width:20px}.el-table__append-wrapper{overflow:hidden}.el-table--fit{border-right:0;border-bottom:0}.el-table--fit td.gutter,.el-table--fit th.gutter{border-right-width:1px}.el-table--scrollable-x .el-table__body-wrapper{overflow-x:auto}.el-table--scrollable-y .el-table__body-wrapper{overflow-y:auto}.el-table thead{color:#909399;font-weight:500}.el-table thead.is-group th{background:#f5f7fa}.el-table th,.el-table tr{background-color:#fff}.el-table td,.el-table th{padding:12px 0;min-width:0;-webkit-box-sizing:border-box;box-sizing:border-box;text-overflow:ellipsis;vertical-align:middle;position:relative;text-align:left}.el-table td.is-center,.el-table th.is-center{text-align:center}.el-table td.is-right,.el-table th.is-right{text-align:right}.el-table td.gutter,.el-table th.gutter{width:15px;border-right-width:0;border-bottom-width:0;padding:0}.el-table--medium td,.el-table--medium th{padding:10px 0}.el-table--small td,.el-table--small th{padding:8px 0}.el-table--mini td,.el-table--mini th{padding:6px 0}.el-table--border td:first-child .cell,.el-table--border th:first-child .cell,.el-table .cell{padding-left:10px}.el-table tr input[type=checkbox]{margin:0}.el-table td,.el-table th.is-leaf{border-bottom:1px solid #ebeef5}.el-table th.is-sortable{cursor:pointer}.el-table th{overflow:hidden;-ms-user-select:none;-webkit-user-select:none;-moz-user-select:none;user-select:none}.el-table th>.cell{display:inline-block;-webkit-box-sizing:border-box;box-sizing:border-box;position:relative;vertical-align:middle;padding-left:10px;padding-right:10px;width:100%}.el-table th>.cell.highlight{color:#409eff}.el-table th.required>div:before{display:inline-block;content:"";width:8px;height:8px;border-radius:50%;background:#ff4d51;margin-right:5px;vertical-align:middle}.el-table td div{-webkit-box-sizing:border-box;box-sizing:border-box}.el-table td.gutter{width:0}.el-table .cell{-webkit-box-sizing:border-box;box-sizing:border-box;overflow:hidden;text-overflow:ellipsis;white-space:normal;word-break:break-all;line-height:23px;padding-right:10px}.el-table .cell.el-tooltip{white-space:nowrap;min-width:50px}.el-table--border,.el-table--group{border:1px solid #ebeef5}.el-table--border:after,.el-table--group:after,.el-table:before{content:"";position:absolute;background-color:#ebeef5;z-index:1}.el-table--border:after,.el-table--group:after{top:0;right:0;width:1px;height:100%}.el-table:before{left:0;bottom:0;width:100%;height:1px}.el-table--border{border-right:none;border-bottom:none}.el-table--border.el-loading-parent--relative{border-color:transparent}.el-table--border td,.el-table--border th,.el-table__body-wrapper .el-table--border.is-scrolling-left~.el-table__fixed{border-right:1px solid #ebeef5}.el-table--border th.gutter:last-of-type{border-bottom:1px solid #ebeef5;border-bottom-width:1px}.el-table--border th,.el-table__fixed-right-patch{border-bottom:1px solid #ebeef5}.el-table__fixed,.el-table__fixed-right{position:absolute;top:0;left:0;overflow-x:hidden;overflow-y:hidden;-webkit-box-shadow:0 0 10px rgba(0,0,0,.12);box-shadow:0 0 10px rgba(0,0,0,.12)}.el-table__fixed-right:before,.el-table__fixed:before{content:"";position:absolute;left:0;bottom:0;width:100%;height:1px;background-color:#ebeef5;z-index:4}.el-table__fixed-right-patch{position:absolute;top:-1px;right:0;background-color:#fff}.el-table__fixed-right{top:0;left:auto;right:0}.el-table__fixed-right .el-table__fixed-body-wrapper,.el-table__fixed-right .el-table__fixed-footer-wrapper,.el-table__fixed-right .el-table__fixed-header-wrapper{left:auto;right:0}.el-table__fixed-header-wrapper{position:absolute;left:0;top:0;z-index:3}.el-table__fixed-footer-wrapper{position:absolute;left:0;bottom:0;z-index:3}.el-table__fixed-footer-wrapper tbody td{border-top:1px solid #ebeef5;background-color:#f5f7fa;color:#606266}.el-table__fixed-body-wrapper{position:absolute;left:0;top:37px;overflow:hidden;z-index:3}.el-table__body-wrapper,.el-table__footer-wrapper,.el-table__header-wrapper{width:100%}.el-table__footer-wrapper{margin-top:-1px}.el-table__footer-wrapper td{border-top:1px solid #ebeef5}.el-table__body,.el-table__footer,.el-table__header{table-layout:fixed;border-collapse:separate}.el-table__footer-wrapper,.el-table__header-wrapper{overflow:hidden}.el-table__footer-wrapper tbody td,.el-table__header-wrapper tbody td{background-color:#f5f7fa;color:#606266}.el-table__body-wrapper{overflow:hidden;position:relative}.el-table__body-wrapper.is-scrolling-left~.el-table__fixed,.el-table__body-wrapper.is-scrolling-none~.el-table__fixed,.el-table__body-wrapper.is-scrolling-none~.el-table__fixed-right,.el-table__body-wrapper.is-scrolling-right~.el-table__fixed-right{-webkit-box-shadow:none;box-shadow:none}.el-picker-panel,.el-table-filter{-webkit-box-shadow:0 2px 12px 0 rgba(0,0,0,.1)}.el-table__body-wrapper .el-table--border.is-scrolling-right~.el-table__fixed-right{border-left:1px solid #ebeef5}.el-table .caret-wrapper{display:-webkit-inline-box;display:-ms-inline-flexbox;display:inline-flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;-webkit-box-align:center;-ms-flex-align:center;align-items:center;height:34px;width:24px;vertical-align:middle;cursor:pointer;overflow:initial;position:relative}.el-table .sort-caret{width:0;height:0;border:5px solid transparent;position:absolute;left:7px}.el-table .sort-caret.ascending{border-bottom-color:#c0c4cc;top:5px}.el-table .sort-caret.descending{border-top-color:#c0c4cc;bottom:7px}.el-table .ascending .sort-caret.ascending{border-bottom-color:#409eff}.el-table .descending .sort-caret.descending{border-top-color:#409eff}.el-table .hidden-columns{position:absolute;z-index:-1}.el-table--striped .el-table__body tr.el-table__row--striped td{background:#fafafa}.el-table--striped .el-table__body tr.el-table__row--striped.current-row td{background-color:#ecf5ff}.el-table__body tr.hover-row.current-row>td,.el-table__body tr.hover-row.el-table__row--striped.current-row>td,.el-table__body tr.hover-row.el-table__row--striped>td,.el-table__body tr.hover-row>td{background-color:#f5f7fa}.el-table__body tr.current-row>td{background-color:#ecf5ff}.el-table__column-resize-proxy{position:absolute;left:200px;top:0;bottom:0;width:0;border-left:1px solid #ebeef5;z-index:10}.el-table__column-filter-trigger{display:inline-block;line-height:34px;cursor:pointer}.el-table__column-filter-trigger i{color:#909399;font-size:12px;-webkit-transform:scale(.75);transform:scale(.75)}.el-table--enable-row-transition .el-table__body td{-webkit-transition:background-color .25s ease;transition:background-color .25s ease}.el-table--enable-row-hover .el-table__body tr:hover>td{background-color:#f5f7fa}.el-table--fluid-height .el-table__fixed,.el-table--fluid-height .el-table__fixed-right{bottom:0;overflow:hidden}.el-table [class*=el-table__row--level] .el-table__expand-icon{display:inline-block;width:20px;line-height:20px;height:20px;text-align:center;margin-right:3px}.el-table-column--selection .cell{padding-left:14px;padding-right:14px}.el-table-filter{border:1px solid #ebeef5;border-radius:2px;background-color:#fff;-webkit-box-shadow:0 2px 12px 0 rgba(0,0,0,.1);box-shadow:0 2px 12px 0 rgba(0,0,0,.1);-webkit-box-sizing:border-box;box-sizing:border-box;margin:2px 0}.el-date-table td,.el-date-table td div{height:30px;-webkit-box-sizing:border-box}.el-table-filter__list{padding:5px 0;margin:0;list-style:none;min-width:100px}.el-table-filter__list-item{line-height:36px;padding:0 10px;cursor:pointer;font-size:14px}.el-table-filter__list-item:hover{background-color:#ecf5ff;color:#66b1ff}.el-table-filter__list-item.is-active{background-color:#409eff;color:#fff}.el-table-filter__content{min-width:100px}.el-table-filter__bottom{border-top:1px solid #ebeef5;padding:8px}.el-table-filter__bottom button{background:0 0;border:none;color:#606266;cursor:pointer;font-size:13px;padding:0 3px}.el-date-table.is-week-mode .el-date-table__row.current div,.el-date-table.is-week-mode .el-date-table__row:hover div,.el-date-table td.in-range div,.el-date-table td.in-range div:hover{background-color:#f2f6fc}.el-table-filter__bottom button:hover{color:#409eff}.el-table-filter__bottom button:focus{outline:0}.el-table-filter__bottom button.is-disabled{color:#c0c4cc;cursor:not-allowed}.el-table-filter__wrap{max-height:280px}.el-table-filter__checkbox-group{padding:10px}.el-table-filter__checkbox-group label.el-checkbox{display:block;margin-right:5px;margin-bottom:8px;margin-left:5px}.el-table-filter__checkbox-group .el-checkbox:last-child{margin-bottom:0}.el-date-table{font-size:12px;-ms-user-select:none;-webkit-user-select:none;-moz-user-select:none;user-select:none}.el-date-table.is-week-mode .el-date-table__row:hover td.available:hover{color:#606266}.el-date-table.is-week-mode .el-date-table__row:hover td:first-child div{margin-left:5px;border-top-left-radius:15px;border-bottom-left-radius:15px}.el-date-table.is-week-mode .el-date-table__row:hover td:last-child div{margin-right:5px;border-top-right-radius:15px;border-bottom-right-radius:15px}.el-date-table td{width:32px;padding:4px 0;text-align:center;cursor:pointer;position:relative}.el-date-table td,.el-date-table td div{-webkit-box-sizing:border-box;box-sizing:border-box}.el-date-table td div{padding:3px 0}.el-date-table td span{width:24px;height:24px;display:block;margin:0 auto;line-height:24px;position:absolute;left:50%;-webkit-transform:translateX(-50%);transform:translateX(-50%);border-radius:50%}.el-date-table td.next-month,.el-date-table td.prev-month{color:#c0c4cc}.el-date-table td.today{position:relative}.el-date-table td.today span{color:#409eff;font-weight:700}.el-date-table td.today.end-date span,.el-date-table td.today.start-date span{color:#fff}.el-date-table td.available:hover{color:#409eff}.el-date-table td.current:not(.disabled) span{color:#fff;background-color:#409eff}.el-date-table td.end-date div,.el-date-table td.start-date div{color:#fff}.el-date-table td.end-date span,.el-date-table td.start-date span{background-color:#409eff}.el-date-table td.start-date div{margin-left:5px;border-top-left-radius:15px;border-bottom-left-radius:15px}.el-date-table td.end-date div{margin-right:5px;border-top-right-radius:15px;border-bottom-right-radius:15px}.el-date-table td.disabled div{background-color:#f5f7fa;opacity:1;cursor:not-allowed;color:#c0c4cc}.el-date-table td.selected div{margin-left:5px;margin-right:5px;background-color:#f2f6fc;border-radius:15px}.el-date-table td.selected div:hover{background-color:#f2f6fc}.el-date-table td.selected span{background-color:#409eff;color:#fff;border-radius:15px}.el-date-table td.week{font-size:80%;color:#606266}.el-month-table,.el-year-table{font-size:12px;border-collapse:collapse}.el-date-table th{padding:5px;color:#606266;font-weight:400;border-bottom:1px solid #ebeef5}.el-month-table{margin:-1px}.el-month-table td{text-align:center;padding:8px 0;cursor:pointer}.el-month-table td div{height:48px;padding:6px 0;-webkit-box-sizing:border-box;box-sizing:border-box}.el-month-table td.today .cell{color:#409eff;font-weight:700}.el-month-table td.today.end-date .cell,.el-month-table td.today.start-date .cell{color:#fff}.el-month-table td.disabled .cell{background-color:#f5f7fa;cursor:not-allowed;color:#c0c4cc}.el-month-table td.disabled .cell:hover{color:#c0c4cc}.el-month-table td .cell{width:60px;height:36px;display:block;line-height:36px;color:#606266;margin:0 auto;border-radius:18px}.el-month-table td .cell:hover{color:#409eff}.el-month-table td.in-range div,.el-month-table td.in-range div:hover{background-color:#f2f6fc}.el-month-table td.end-date div,.el-month-table td.start-date div{color:#fff}.el-month-table td.end-date .cell,.el-month-table td.start-date .cell{color:#fff;background-color:#409eff}.el-month-table td.start-date div{border-top-left-radius:24px;border-bottom-left-radius:24px}.el-month-table td.end-date div{border-top-right-radius:24px;border-bottom-right-radius:24px}.el-month-table td.current:not(.disabled) .cell{color:#409eff}.el-year-table{margin:-1px}.el-year-table .el-icon{color:#303133}.el-year-table td{text-align:center;padding:20px 3px;cursor:pointer}.el-year-table td.today .cell{color:#409eff;font-weight:700}.el-year-table td.disabled .cell{background-color:#f5f7fa;cursor:not-allowed;color:#c0c4cc}.el-year-table td.disabled .cell:hover{color:#c0c4cc}.el-year-table td .cell{width:48px;height:32px;display:block;line-height:32px;color:#606266;margin:0 auto}.el-year-table td .cell:hover,.el-year-table td.current:not(.disabled) .cell{color:#409eff}.el-date-range-picker{width:646px}.el-date-range-picker.has-sidebar{width:756px}.el-date-range-picker table{table-layout:fixed;width:100%}.el-date-range-picker .el-picker-panel__body{min-width:513px}.el-date-range-picker .el-picker-panel__content{margin:0}.el-date-range-picker__header{position:relative;text-align:center;height:28px}.el-date-range-picker__header [class*=arrow-left]{float:left}.el-date-range-picker__header [class*=arrow-right]{float:right}.el-date-range-picker__header div{font-size:16px;font-weight:500;margin-right:50px}.el-date-range-picker__content{float:left;width:50%;-webkit-box-sizing:border-box;box-sizing:border-box;margin:0;padding:16px}.el-date-range-picker__content.is-left{border-right:1px solid #e4e4e4}.el-date-range-picker__content .el-date-range-picker__header div{margin-left:50px;margin-right:50px}.el-date-range-picker__editors-wrap{-webkit-box-sizing:border-box;box-sizing:border-box;display:table-cell}.el-date-range-picker__editors-wrap.is-right{text-align:right}.el-date-range-picker__time-header{position:relative;border-bottom:1px solid #e4e4e4;font-size:12px;padding:8px 5px 5px;display:table;width:100%;-webkit-box-sizing:border-box;box-sizing:border-box}.el-date-range-picker__time-header>.el-icon-arrow-right{font-size:20px;vertical-align:middle;display:table-cell;color:#303133}.el-date-range-picker__time-picker-wrap{position:relative;display:table-cell;padding:0 5px}.el-date-range-picker__time-picker-wrap .el-picker-panel{position:absolute;top:13px;right:0;z-index:1;background:#fff}.el-date-picker{width:322px}.el-date-picker.has-sidebar.has-time{width:434px}.el-date-picker.has-sidebar{width:438px}.el-date-picker.has-time .el-picker-panel__body-wrapper{position:relative}.el-date-picker .el-picker-panel__content{width:292px}.el-date-picker table{table-layout:fixed;width:100%}.el-date-picker__editor-wrap{position:relative;display:table-cell;padding:0 5px}.el-date-picker__time-header{position:relative;border-bottom:1px solid #e4e4e4;font-size:12px;padding:8px 5px 5px;display:table;width:100%;-webkit-box-sizing:border-box;box-sizing:border-box}.el-date-picker__header{margin:12px;text-align:center}.el-date-picker__header--bordered{margin-bottom:0;padding-bottom:12px;border-bottom:1px solid #ebeef5}.el-date-picker__header--bordered+.el-picker-panel__content{margin-top:0}.el-date-picker__header-label{font-size:16px;font-weight:500;padding:0 5px;line-height:22px;text-align:center;cursor:pointer;color:#606266}.el-date-picker__header-label.active,.el-date-picker__header-label:hover{color:#409eff}.el-date-picker__prev-btn{float:left}.el-date-picker__next-btn{float:right}.el-date-picker__time-wrap{padding:10px;text-align:center}.el-date-picker__time-label{float:left;cursor:pointer;line-height:30px;margin-left:10px}.time-select{margin:5px 0;min-width:0}.time-select .el-picker-panel__content{max-height:200px;margin:0}.time-select-item{padding:8px 10px;font-size:14px;line-height:20px}.time-select-item.selected:not(.disabled){color:#409eff;font-weight:700}.time-select-item.disabled{color:#e4e7ed;cursor:not-allowed}.time-select-item:hover{background-color:#f5f7fa;font-weight:700;cursor:pointer}.el-date-editor{position:relative;display:inline-block;text-align:left}.el-date-editor.el-input,.el-date-editor.el-input__inner{width:220px}.el-date-editor--monthrange.el-input,.el-date-editor--monthrange.el-input__inner{width:300px}.el-date-editor--daterange.el-input,.el-date-editor--daterange.el-input__inner,.el-date-editor--timerange.el-input,.el-date-editor--timerange.el-input__inner{width:350px}.el-date-editor--datetimerange.el-input,.el-date-editor--datetimerange.el-input__inner{width:400px}.el-date-editor--dates .el-input__inner{text-overflow:ellipsis;white-space:nowrap}.el-date-editor .el-icon-circle-close{cursor:pointer}.el-date-editor .el-range__icon{font-size:14px;margin-left:-5px;color:#c0c4cc;float:left;line-height:32px}.el-date-editor .el-range-input,.el-date-editor .el-range-separator{height:100%;margin:0;text-align:center;display:inline-block;font-size:14px}.el-date-editor .el-range-input{-webkit-appearance:none;-moz-appearance:none;appearance:none;border:none;outline:0;padding:0;width:39%;color:#606266}.el-date-editor .el-range-input::-webkit-input-placeholder{color:#c0c4cc}.el-date-editor .el-range-input:-ms-input-placeholder{color:#c0c4cc}.el-date-editor .el-range-input::-ms-input-placeholder{color:#c0c4cc}.el-date-editor .el-range-input::-moz-placeholder{color:#c0c4cc}.el-date-editor .el-range-input::placeholder{color:#c0c4cc}.el-date-editor .el-range-separator{padding:0 5px;line-height:32px;width:5%;color:#303133}.el-date-editor .el-range__close-icon{font-size:14px;color:#c0c4cc;width:25px;display:inline-block;float:right;line-height:32px}.el-range-editor.el-input__inner{display:-webkit-inline-box;display:-ms-inline-flexbox;display:inline-flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;padding:3px 10px}.el-range-editor .el-range-input{line-height:1}.el-range-editor.is-active,.el-range-editor.is-active:hover{border-color:#409eff}.el-range-editor--medium.el-input__inner{height:36px}.el-range-editor--medium .el-range-separator{line-height:28px;font-size:14px}.el-range-editor--medium .el-range-input{font-size:14px}.el-range-editor--medium .el-range__close-icon,.el-range-editor--medium .el-range__icon{line-height:28px}.el-range-editor--small.el-input__inner{height:32px}.el-range-editor--small .el-range-separator{line-height:24px;font-size:13px}.el-range-editor--small .el-range-input{font-size:13px}.el-range-editor--small .el-range__close-icon,.el-range-editor--small .el-range__icon{line-height:24px}.el-range-editor--mini.el-input__inner{height:28px}.el-range-editor--mini .el-range-separator{line-height:20px;font-size:12px}.el-range-editor--mini .el-range-input{font-size:12px}.el-range-editor--mini .el-range__close-icon,.el-range-editor--mini .el-range__icon{line-height:20px}.el-range-editor.is-disabled{background-color:#f5f7fa;border-color:#e4e7ed;color:#c0c4cc;cursor:not-allowed}.el-range-editor.is-disabled:focus,.el-range-editor.is-disabled:hover{border-color:#e4e7ed}.el-range-editor.is-disabled input{background-color:#f5f7fa;color:#c0c4cc;cursor:not-allowed}.el-range-editor.is-disabled input::-webkit-input-placeholder{color:#c0c4cc}.el-range-editor.is-disabled input:-ms-input-placeholder{color:#c0c4cc}.el-range-editor.is-disabled input::-ms-input-placeholder{color:#c0c4cc}.el-range-editor.is-disabled input::-moz-placeholder{color:#c0c4cc}.el-range-editor.is-disabled input::placeholder{color:#c0c4cc}.el-range-editor.is-disabled .el-range-separator{color:#c0c4cc}.el-picker-panel{color:#606266;border:1px solid #e4e7ed;box-shadow:0 2px 12px 0 rgba(0,0,0,.1);background:#fff;border-radius:4px;line-height:30px;margin:5px 0}.el-picker-panel,.el-popover,.el-time-panel{-webkit-box-shadow:0 2px 12px 0 rgba(0,0,0,.1)}.el-picker-panel__body-wrapper:after,.el-picker-panel__body:after{content:"";display:table;clear:both}.el-picker-panel__content{position:relative;margin:15px}.el-picker-panel__footer{border-top:1px solid #e4e4e4;padding:4px;text-align:right;background-color:#fff;position:relative;font-size:0}.el-picker-panel__shortcut{display:block;width:100%;border:0;background-color:transparent;line-height:28px;font-size:14px;color:#606266;padding-left:12px;text-align:left;outline:0;cursor:pointer}.el-picker-panel__shortcut:hover{color:#409eff}.el-picker-panel__shortcut.active{background-color:#e6f1fe;color:#409eff}.el-picker-panel__btn{border:1px solid #dcdcdc;color:#333;line-height:24px;border-radius:2px;padding:0 20px;cursor:pointer;background-color:transparent;outline:0;font-size:12px}.el-picker-panel__btn[disabled]{color:#ccc;cursor:not-allowed}.el-picker-panel__icon-btn{font-size:12px;color:#303133;border:0;background:0 0;cursor:pointer;outline:0;margin-top:8px}.el-picker-panel__icon-btn:hover{color:#409eff}.el-picker-panel__icon-btn.is-disabled{color:#bbb}.el-picker-panel__icon-btn.is-disabled:hover{cursor:not-allowed}.el-picker-panel__link-btn{vertical-align:middle}.el-picker-panel [slot=sidebar],.el-picker-panel__sidebar{position:absolute;top:0;bottom:0;width:110px;border-right:1px solid #e4e4e4;-webkit-box-sizing:border-box;box-sizing:border-box;padding-top:6px;background-color:#fff;overflow:auto}.el-picker-panel [slot=sidebar]+.el-picker-panel__body,.el-picker-panel__sidebar+.el-picker-panel__body{margin-left:110px}.el-time-spinner.has-seconds .el-time-spinner__wrapper{width:33.3%}.el-time-spinner__wrapper{max-height:190px;overflow:auto;display:inline-block;width:50%;vertical-align:top;position:relative}.el-time-spinner__wrapper .el-scrollbar__wrap:not(.el-scrollbar__wrap--hidden-default){padding-bottom:15px}.el-time-spinner__input.el-input .el-input__inner,.el-time-spinner__list{padding:0;text-align:center}.el-time-spinner__wrapper.is-arrow{-webkit-box-sizing:border-box;box-sizing:border-box;text-align:center;overflow:hidden}.el-time-spinner__wrapper.is-arrow .el-time-spinner__list{-webkit-transform:translateY(-32px);transform:translateY(-32px)}.el-time-spinner__wrapper.is-arrow .el-time-spinner__item:hover:not(.disabled):not(.active){background:#fff;cursor:default}.el-time-spinner__arrow{font-size:12px;color:#909399;position:absolute;left:0;width:100%;z-index:1;text-align:center;height:30px;line-height:30px;cursor:pointer}.el-time-spinner__arrow:hover{color:#409eff}.el-time-spinner__arrow.el-icon-arrow-up{top:10px}.el-time-spinner__arrow.el-icon-arrow-down{bottom:10px}.el-time-spinner__input.el-input{width:70%}.el-time-spinner__list{margin:0;list-style:none}.el-time-spinner__list:after,.el-time-spinner__list:before{content:"";display:block;width:100%;height:80px}.el-time-spinner__item{height:32px;line-height:32px;font-size:12px;color:#606266}.el-time-spinner__item:hover:not(.disabled):not(.active){background:#f5f7fa;cursor:pointer}.el-time-spinner__item.active:not(.disabled){color:#303133;font-weight:700}.el-time-spinner__item.disabled{color:#c0c4cc;cursor:not-allowed}.el-time-panel{margin:5px 0;border:1px solid #e4e7ed;background-color:#fff;-webkit-box-shadow:0 2px 12px 0 rgba(0,0,0,.1);box-shadow:0 2px 12px 0 rgba(0,0,0,.1);border-radius:2px;position:absolute;width:180px;left:0;z-index:1000;user-select:none;-webkit-box-sizing:content-box;box-sizing:content-box}.el-slider__button,.el-slider__button-wrapper,.el-time-panel{-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none}.el-time-panel__content{font-size:0;position:relative;overflow:hidden}.el-time-panel__content:after,.el-time-panel__content:before{content:"";top:50%;position:absolute;margin-top:-15px;height:32px;z-index:-1;left:0;right:0;-webkit-box-sizing:border-box;box-sizing:border-box;padding-top:6px;text-align:left;border-top:1px solid #e4e7ed;border-bottom:1px solid #e4e7ed}.el-time-panel__content:after{left:50%;margin-left:12%;margin-right:12%}.el-time-panel__content:before{padding-left:50%;margin-right:12%;margin-left:12%}.el-time-panel__content.has-seconds:after{left:66.66667%}.el-time-panel__content.has-seconds:before{padding-left:33.33333%}.el-time-panel__footer{border-top:1px solid #e4e4e4;padding:4px;height:36px;line-height:25px;text-align:right;-webkit-box-sizing:border-box;box-sizing:border-box}.el-time-panel__btn{border:none;line-height:28px;padding:0 5px;margin:0 5px;cursor:pointer;background-color:transparent;outline:0;font-size:12px;color:#303133}.el-time-panel__btn.confirm{font-weight:800;color:#409eff}.el-time-range-picker{width:354px;overflow:visible}.el-time-range-picker__content{position:relative;text-align:center;padding:10px}.el-time-range-picker__cell{-webkit-box-sizing:border-box;box-sizing:border-box;margin:0;padding:4px 7px 7px;width:50%;display:inline-block}.el-time-range-picker__header{margin-bottom:5px;text-align:center;font-size:14px}.el-time-range-picker__body{border-radius:2px;border:1px solid #e4e7ed}.el-popover{position:absolute;background:#fff;min-width:150px;border:1px solid #ebeef5;padding:12px;z-index:2000;color:#606266;line-height:1.4;text-align:justify;font-size:14px;-webkit-box-shadow:0 2px 12px 0 rgba(0,0,0,.1);box-shadow:0 2px 12px 0 rgba(0,0,0,.1);word-break:break-all}.el-popover--plain{padding:18px 20px}.el-popover__title{color:#303133;font-size:16px;line-height:1;margin-bottom:12px}.v-modal-enter{-webkit-animation:v-modal-in .2s ease;animation:v-modal-in .2s ease}.v-modal-leave{-webkit-animation:v-modal-out .2s ease forwards;animation:v-modal-out .2s ease forwards}@keyframes v-modal-in{0%{opacity:0}}@keyframes v-modal-out{to{opacity:0}}.v-modal{position:fixed;left:0;top:0;width:100%;height:100%;opacity:.5;background:#000}.el-popup-parent--hidden{overflow:hidden}.el-message-box{display:inline-block;width:420px;padding-bottom:10px;vertical-align:middle;background-color:#fff;border-radius:4px;border:1px solid #ebeef5;font-size:18px;-webkit-box-shadow:0 2px 12px 0 rgba(0,0,0,.1);box-shadow:0 2px 12px 0 rgba(0,0,0,.1);text-align:left;overflow:hidden;-webkit-backface-visibility:hidden;backface-visibility:hidden}.el-message-box__wrapper{position:fixed;top:0;bottom:0;left:0;right:0;text-align:center}.el-message-box__wrapper:after{content:"";display:inline-block;height:100%;width:0;vertical-align:middle}.el-message-box__header{position:relative;padding:15px 15px 10px}.el-message-box__title{padding-left:0;margin-bottom:0;font-size:18px;line-height:1;color:#303133}.el-message-box__headerbtn{position:absolute;top:15px;right:15px;padding:0;border:none;outline:0;background:0 0;font-size:16px;cursor:pointer}.el-form-item.is-error .el-input__inner,.el-form-item.is-error .el-input__inner:focus,.el-form-item.is-error .el-textarea__inner,.el-form-item.is-error .el-textarea__inner:focus,.el-message-box__input input.invalid,.el-message-box__input input.invalid:focus{border-color:#f56c6c}.el-message-box__headerbtn .el-message-box__close{color:#909399}.el-message-box__headerbtn:focus .el-message-box__close,.el-message-box__headerbtn:hover .el-message-box__close{color:#409eff}.el-message-box__content{padding:10px 15px;color:#606266;font-size:14px}.el-message-box__container{position:relative}.el-message-box__input{padding-top:15px}.el-message-box__status{position:absolute;top:50%;-webkit-transform:translateY(-50%);transform:translateY(-50%);font-size:24px!important}.el-message-box__status:before{padding-left:1px}.el-message-box__status+.el-message-box__message{padding-left:36px;padding-right:12px}.el-message-box__status.el-icon-success{color:#67c23a}.el-message-box__status.el-icon-info{color:#909399}.el-message-box__status.el-icon-warning{color:#e6a23c}.el-message-box__status.el-icon-error{color:#f56c6c}.el-message-box__message{margin:0}.el-message-box__message p{margin:0;line-height:24px}.el-message-box__errormsg{color:#f56c6c;font-size:12px;min-height:18px;margin-top:2px}.el-message-box__btns{padding:5px 15px 0;text-align:right}.el-message-box__btns button:nth-child(2){margin-left:10px}.el-message-box__btns-reverse{-webkit-box-orient:horizontal;-webkit-box-direction:reverse;-ms-flex-direction:row-reverse;flex-direction:row-reverse}.el-message-box--center{padding-bottom:30px}.el-message-box--center .el-message-box__header{padding-top:30px}.el-message-box--center .el-message-box__title{position:relative;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center}.el-message-box--center .el-message-box__status{position:relative;top:auto;padding-right:5px;text-align:center;-webkit-transform:translateY(-1px);transform:translateY(-1px)}.el-message-box--center .el-message-box__message{margin-left:0}.el-message-box--center .el-message-box__btns,.el-message-box--center .el-message-box__content{text-align:center}.el-message-box--center .el-message-box__content{padding-left:27px;padding-right:27px}.msgbox-fade-enter-active{-webkit-animation:msgbox-fade-in .3s;animation:msgbox-fade-in .3s}.msgbox-fade-leave-active{-webkit-animation:msgbox-fade-out .3s;animation:msgbox-fade-out .3s}@-webkit-keyframes msgbox-fade-in{0%{-webkit-transform:translate3d(0,-20px,0);transform:translate3d(0,-20px,0);opacity:0}to{-webkit-transform:translateZ(0);transform:translateZ(0);opacity:1}}@keyframes msgbox-fade-in{0%{-webkit-transform:translate3d(0,-20px,0);transform:translate3d(0,-20px,0);opacity:0}to{-webkit-transform:translateZ(0);transform:translateZ(0);opacity:1}}@-webkit-keyframes msgbox-fade-out{0%{-webkit-transform:translateZ(0);transform:translateZ(0);opacity:1}to{-webkit-transform:translate3d(0,-20px,0);transform:translate3d(0,-20px,0);opacity:0}}@keyframes msgbox-fade-out{0%{-webkit-transform:translateZ(0);transform:translateZ(0);opacity:1}to{-webkit-transform:translate3d(0,-20px,0);transform:translate3d(0,-20px,0);opacity:0}}.el-breadcrumb{font-size:14px;line-height:1}.el-breadcrumb:after,.el-breadcrumb:before{display:table;content:""}.el-breadcrumb:after{clear:both}.el-breadcrumb__separator{margin:0 9px;font-weight:700;color:#c0c4cc}.el-breadcrumb__separator[class*=icon]{margin:0 6px;font-weight:400}.el-breadcrumb__item{float:left}.el-breadcrumb__inner{color:#606266}.el-breadcrumb__inner.is-link,.el-breadcrumb__inner a{font-weight:700;text-decoration:none;-webkit-transition:color .2s cubic-bezier(.645,.045,.355,1);transition:color .2s cubic-bezier(.645,.045,.355,1);color:#303133}.el-breadcrumb__inner.is-link:hover,.el-breadcrumb__inner a:hover{color:#409eff;cursor:pointer}.el-breadcrumb__item:last-child .el-breadcrumb__inner,.el-breadcrumb__item:last-child .el-breadcrumb__inner:hover,.el-breadcrumb__item:last-child .el-breadcrumb__inner a,.el-breadcrumb__item:last-child .el-breadcrumb__inner a:hover{font-weight:400;color:#606266;cursor:text}.el-breadcrumb__item:last-child .el-breadcrumb__separator{display:none}.el-form--label-left .el-form-item__label{text-align:left}.el-form--label-top .el-form-item__label{float:none;display:inline-block;text-align:left;padding:0 0 10px}.el-form--inline .el-form-item{display:inline-block;margin-right:10px;vertical-align:top}.el-form--inline .el-form-item__label{float:none;display:inline-block}.el-form--inline .el-form-item__content{display:inline-block;vertical-align:top}.el-form--inline.el-form--label-top .el-form-item__content{display:block}.el-form-item{margin-bottom:22px}.el-form-item:after,.el-form-item:before{display:table;content:""}.el-form-item:after{clear:both}.el-form-item .el-form-item{margin-bottom:0}.el-form-item--mini.el-form-item,.el-form-item--small.el-form-item{margin-bottom:18px}.el-form-item .el-input__validateIcon{display:none}.el-form-item--medium .el-form-item__content,.el-form-item--medium .el-form-item__label{line-height:36px}.el-form-item--small .el-form-item__content,.el-form-item--small .el-form-item__label{line-height:32px}.el-form-item--small .el-form-item__error{padding-top:2px}.el-form-item--mini .el-form-item__content,.el-form-item--mini .el-form-item__label{line-height:28px}.el-form-item--mini .el-form-item__error{padding-top:1px}.el-form-item__label-wrap{float:left}.el-form-item__label-wrap .el-form-item__label{display:inline-block;float:none}.el-form-item__label{text-align:right;vertical-align:middle;float:left;font-size:14px;color:#606266;line-height:40px;padding:0 12px 0 0;-webkit-box-sizing:border-box;box-sizing:border-box}.el-form-item__content{line-height:40px;position:relative;font-size:14px}.el-form-item__content:after,.el-form-item__content:before{display:table;content:""}.el-form-item__content:after{clear:both}.el-form-item__content .el-input-group{vertical-align:top}.el-form-item__error{color:#f56c6c;font-size:12px;line-height:1;padding-top:4px;position:absolute;top:100%;left:0}.el-form-item__error--inline{position:relative;top:auto;left:auto;display:inline-block;margin-left:10px}.el-form-item.is-required:not(.is-no-asterisk) .el-form-item__label-wrap>.el-form-item__label:before,.el-form-item.is-required:not(.is-no-asterisk)>.el-form-item__label:before{content:"*";color:#f56c6c;margin-right:4px}.el-form-item.is-error .el-input-group__append .el-input__inner,.el-form-item.is-error .el-input-group__prepend .el-input__inner{border-color:transparent}.el-form-item.is-error .el-input__validateIcon{color:#f56c6c}.el-form-item--feedback .el-input__validateIcon{display:inline-block}.el-tabs__header{padding:0;position:relative;margin:0 0 15px}.el-tabs__active-bar{position:absolute;bottom:0;left:0;height:2px;background-color:#409eff;z-index:1;-webkit-transition:-webkit-transform .3s cubic-bezier(.645,.045,.355,1);transition:-webkit-transform .3s cubic-bezier(.645,.045,.355,1);transition:transform .3s cubic-bezier(.645,.045,.355,1);transition:transform .3s cubic-bezier(.645,.045,.355,1),-webkit-transform .3s cubic-bezier(.645,.045,.355,1);list-style:none}.el-tabs__new-tab{float:right;border:1px solid #d3dce6;height:18px;width:18px;line-height:18px;margin:12px 0 9px 10px;border-radius:3px;text-align:center;font-size:12px;color:#d3dce6;cursor:pointer;-webkit-transition:all .15s;transition:all .15s}.el-collapse-item__arrow,.el-tabs__nav{-webkit-transition:-webkit-transform .3s}.el-tabs__new-tab .el-icon-plus{-webkit-transform:scale(.8);transform:scale(.8)}.el-tabs__new-tab:hover{color:#409eff}.el-tabs__nav-wrap{overflow:hidden;margin-bottom:-1px;position:relative}.el-tabs__nav-wrap:after{content:"";position:absolute;left:0;bottom:0;width:100%;height:2px;background-color:#e4e7ed;z-index:1}.el-tabs--border-card>.el-tabs__header .el-tabs__nav-wrap:after,.el-tabs--card>.el-tabs__header .el-tabs__nav-wrap:after{content:none}.el-tabs__nav-wrap.is-scrollable{padding:0 20px;-webkit-box-sizing:border-box;box-sizing:border-box}.el-tabs__nav-scroll{overflow:hidden}.el-tabs__nav-next,.el-tabs__nav-prev{position:absolute;cursor:pointer;line-height:44px;font-size:12px;color:#909399}.el-tabs__nav-next{right:0}.el-tabs__nav-prev{left:0}.el-tabs__nav{white-space:nowrap;position:relative;transition:-webkit-transform .3s;-webkit-transition:-webkit-transform .3s;transition:transform .3s;transition:transform .3s,-webkit-transform .3s;float:left;z-index:2}.el-tabs__nav.is-stretch{min-width:100%;display:-webkit-box;display:-ms-flexbox;display:flex}.el-tabs__nav.is-stretch>*{-webkit-box-flex:1;-ms-flex:1;flex:1;text-align:center}.el-tabs__item{padding:0 20px;height:40px;-webkit-box-sizing:border-box;box-sizing:border-box;line-height:40px;display:inline-block;list-style:none;font-size:14px;font-weight:500;color:#303133;position:relative}.el-tabs__item:focus,.el-tabs__item:focus:active{outline:0}.el-tabs__item:focus.is-active.is-focus:not(:active){-webkit-box-shadow:0 0 2px 2px #409eff inset;box-shadow:inset 0 0 2px 2px #409eff;border-radius:3px}.el-tabs__item .el-icon-close{border-radius:50%;text-align:center;-webkit-transition:all .3s cubic-bezier(.645,.045,.355,1);transition:all .3s cubic-bezier(.645,.045,.355,1);margin-left:5px}.el-tabs__item .el-icon-close:before{-webkit-transform:scale(.9);transform:scale(.9);display:inline-block}.el-tabs__item .el-icon-close:hover{background-color:#c0c4cc;color:#fff}.el-tabs__item.is-active{color:#409eff}.el-tabs__item:hover{color:#409eff;cursor:pointer}.el-tabs__item.is-disabled{color:#c0c4cc;cursor:default}.el-tabs__content{overflow:hidden;position:relative}.el-tabs--card>.el-tabs__header{border-bottom:1px solid #e4e7ed}.el-tabs--card>.el-tabs__header .el-tabs__nav{border:1px solid #e4e7ed;border-bottom:none;border-radius:4px 4px 0 0;-webkit-box-sizing:border-box;box-sizing:border-box}.el-tabs--card>.el-tabs__header .el-tabs__active-bar{display:none}.el-tabs--card>.el-tabs__header .el-tabs__item .el-icon-close{position:relative;font-size:12px;width:0;height:14px;vertical-align:middle;line-height:15px;overflow:hidden;top:-1px;right:-2px;-webkit-transform-origin:100% 50%;transform-origin:100% 50%}.el-tabs--card>.el-tabs__header .el-tabs__item.is-active.is-closable .el-icon-close,.el-tabs--card>.el-tabs__header .el-tabs__item.is-closable:hover .el-icon-close{width:14px}.el-tabs--card>.el-tabs__header .el-tabs__item{border-bottom:1px solid transparent;border-left:1px solid #e4e7ed;-webkit-transition:color .3s cubic-bezier(.645,.045,.355,1),padding .3s cubic-bezier(.645,.045,.355,1);transition:color .3s cubic-bezier(.645,.045,.355,1),padding .3s cubic-bezier(.645,.045,.355,1)}.el-tabs--card>.el-tabs__header .el-tabs__item:first-child{border-left:none}.el-tabs--card>.el-tabs__header .el-tabs__item.is-closable:hover{padding-left:13px;padding-right:13px}.el-tabs--card>.el-tabs__header .el-tabs__item.is-active{border-bottom-color:#fff}.el-tabs--card>.el-tabs__header .el-tabs__item.is-active.is-closable{padding-left:20px;padding-right:20px}.el-tabs--border-card{background:#fff;border:1px solid #dcdfe6;-webkit-box-shadow:0 2px 4px 0 rgba(0,0,0,.12),0 0 6px 0 rgba(0,0,0,.04);box-shadow:0 2px 4px 0 rgba(0,0,0,.12),0 0 6px 0 rgba(0,0,0,.04)}.el-tabs--border-card>.el-tabs__content{padding:15px}.el-tabs--border-card>.el-tabs__header{background-color:#f5f7fa;border-bottom:1px solid #e4e7ed;margin:0}.el-tabs--border-card>.el-tabs__header .el-tabs__item{-webkit-transition:all .3s cubic-bezier(.645,.045,.355,1);transition:all .3s cubic-bezier(.645,.045,.355,1);border:1px solid transparent;margin-top:-1px;color:#909399}.el-tabs--border-card>.el-tabs__header .el-tabs__item+.el-tabs__item,.el-tabs--border-card>.el-tabs__header .el-tabs__item:first-child{margin-left:-1px}.el-tabs--border-card>.el-tabs__header .el-tabs__item.is-active{color:#409eff;background-color:#fff;border-right-color:#dcdfe6;border-left-color:#dcdfe6}.el-tabs--border-card>.el-tabs__header .el-tabs__item:not(.is-disabled):hover{color:#409eff}.el-tabs--border-card>.el-tabs__header .el-tabs__item.is-disabled{color:#c0c4cc}.el-tabs--border-card>.el-tabs__header .is-scrollable .el-tabs__item:first-child{margin-left:0}.el-tabs--bottom .el-tabs__item.is-bottom:nth-child(2),.el-tabs--bottom .el-tabs__item.is-top:nth-child(2),.el-tabs--top .el-tabs__item.is-bottom:nth-child(2),.el-tabs--top .el-tabs__item.is-top:nth-child(2){padding-left:0}.el-tabs--bottom .el-tabs__item.is-bottom:last-child,.el-tabs--bottom .el-tabs__item.is-top:last-child,.el-tabs--top .el-tabs__item.is-bottom:last-child,.el-tabs--top .el-tabs__item.is-top:last-child{padding-right:0}.el-tabs--bottom.el-tabs--border-card>.el-tabs__header .el-tabs__item:nth-child(2),.el-tabs--bottom.el-tabs--card>.el-tabs__header .el-tabs__item:nth-child(2),.el-tabs--bottom .el-tabs--left>.el-tabs__header .el-tabs__item:nth-child(2),.el-tabs--bottom .el-tabs--right>.el-tabs__header .el-tabs__item:nth-child(2),.el-tabs--top.el-tabs--border-card>.el-tabs__header .el-tabs__item:nth-child(2),.el-tabs--top.el-tabs--card>.el-tabs__header .el-tabs__item:nth-child(2),.el-tabs--top .el-tabs--left>.el-tabs__header .el-tabs__item:nth-child(2),.el-tabs--top .el-tabs--right>.el-tabs__header .el-tabs__item:nth-child(2){padding-left:20px}.el-tabs--bottom.el-tabs--border-card>.el-tabs__header .el-tabs__item:last-child,.el-tabs--bottom.el-tabs--card>.el-tabs__header .el-tabs__item:last-child,.el-tabs--bottom .el-tabs--left>.el-tabs__header .el-tabs__item:last-child,.el-tabs--bottom .el-tabs--right>.el-tabs__header .el-tabs__item:last-child,.el-tabs--top.el-tabs--border-card>.el-tabs__header .el-tabs__item:last-child,.el-tabs--top.el-tabs--card>.el-tabs__header .el-tabs__item:last-child,.el-tabs--top .el-tabs--left>.el-tabs__header .el-tabs__item:last-child,.el-tabs--top .el-tabs--right>.el-tabs__header .el-tabs__item:last-child{padding-right:20px}.el-tabs--bottom .el-tabs__header.is-bottom{margin-bottom:0;margin-top:10px}.el-tabs--bottom.el-tabs--border-card .el-tabs__header.is-bottom{border-bottom:0;border-top:1px solid #dcdfe6}.el-tabs--bottom.el-tabs--border-card .el-tabs__nav-wrap.is-bottom{margin-top:-1px;margin-bottom:0}.el-tabs--bottom.el-tabs--border-card .el-tabs__item.is-bottom:not(.is-active){border:1px solid transparent}.el-tabs--bottom.el-tabs--border-card .el-tabs__item.is-bottom{margin:0 -1px -1px}.el-tabs--left,.el-tabs--right{overflow:hidden}.el-tabs--left .el-tabs__header.is-left,.el-tabs--left .el-tabs__header.is-right,.el-tabs--left .el-tabs__nav-scroll,.el-tabs--left .el-tabs__nav-wrap.is-left,.el-tabs--left .el-tabs__nav-wrap.is-right,.el-tabs--right .el-tabs__header.is-left,.el-tabs--right .el-tabs__header.is-right,.el-tabs--right .el-tabs__nav-scroll,.el-tabs--right .el-tabs__nav-wrap.is-left,.el-tabs--right .el-tabs__nav-wrap.is-right{height:100%}.el-tabs--left .el-tabs__active-bar.is-left,.el-tabs--left .el-tabs__active-bar.is-right,.el-tabs--right .el-tabs__active-bar.is-left,.el-tabs--right .el-tabs__active-bar.is-right{top:0;bottom:auto;width:2px;height:auto}.el-tabs--left .el-tabs__nav-wrap.is-left,.el-tabs--left .el-tabs__nav-wrap.is-right,.el-tabs--right .el-tabs__nav-wrap.is-left,.el-tabs--right .el-tabs__nav-wrap.is-right{margin-bottom:0}.el-tabs--left .el-tabs__nav-wrap.is-left>.el-tabs__nav-next,.el-tabs--left .el-tabs__nav-wrap.is-left>.el-tabs__nav-prev,.el-tabs--left .el-tabs__nav-wrap.is-right>.el-tabs__nav-next,.el-tabs--left .el-tabs__nav-wrap.is-right>.el-tabs__nav-prev,.el-tabs--right .el-tabs__nav-wrap.is-left>.el-tabs__nav-next,.el-tabs--right .el-tabs__nav-wrap.is-left>.el-tabs__nav-prev,.el-tabs--right .el-tabs__nav-wrap.is-right>.el-tabs__nav-next,.el-tabs--right .el-tabs__nav-wrap.is-right>.el-tabs__nav-prev{height:30px;line-height:30px;width:100%;text-align:center;cursor:pointer}.el-tabs--left .el-tabs__nav-wrap.is-left>.el-tabs__nav-next i,.el-tabs--left .el-tabs__nav-wrap.is-left>.el-tabs__nav-prev i,.el-tabs--left .el-tabs__nav-wrap.is-right>.el-tabs__nav-next i,.el-tabs--left .el-tabs__nav-wrap.is-right>.el-tabs__nav-prev i,.el-tabs--right .el-tabs__nav-wrap.is-left>.el-tabs__nav-next i,.el-tabs--right .el-tabs__nav-wrap.is-left>.el-tabs__nav-prev i,.el-tabs--right .el-tabs__nav-wrap.is-right>.el-tabs__nav-next i,.el-tabs--right .el-tabs__nav-wrap.is-right>.el-tabs__nav-prev i{-webkit-transform:rotate(90deg);transform:rotate(90deg)}.el-tabs--left .el-tabs__nav-wrap.is-left>.el-tabs__nav-prev,.el-tabs--left .el-tabs__nav-wrap.is-right>.el-tabs__nav-prev,.el-tabs--right .el-tabs__nav-wrap.is-left>.el-tabs__nav-prev,.el-tabs--right .el-tabs__nav-wrap.is-right>.el-tabs__nav-prev{left:auto;top:0}.el-tabs--left .el-tabs__nav-wrap.is-left>.el-tabs__nav-next,.el-tabs--left .el-tabs__nav-wrap.is-right>.el-tabs__nav-next,.el-tabs--right .el-tabs__nav-wrap.is-left>.el-tabs__nav-next,.el-tabs--right .el-tabs__nav-wrap.is-right>.el-tabs__nav-next{right:auto;bottom:0}.el-tabs--left .el-tabs__active-bar.is-left,.el-tabs--left .el-tabs__nav-wrap.is-left:after{right:0;left:auto}.el-tabs--left .el-tabs__nav-wrap.is-left.is-scrollable,.el-tabs--left .el-tabs__nav-wrap.is-right.is-scrollable,.el-tabs--right .el-tabs__nav-wrap.is-left.is-scrollable,.el-tabs--right .el-tabs__nav-wrap.is-right.is-scrollable{padding:30px 0}.el-tabs--left .el-tabs__nav-wrap.is-left:after,.el-tabs--left .el-tabs__nav-wrap.is-right:after,.el-tabs--right .el-tabs__nav-wrap.is-left:after,.el-tabs--right .el-tabs__nav-wrap.is-right:after{height:100%;width:2px;bottom:auto;top:0}.el-tabs--left .el-tabs__nav.is-left,.el-tabs--left .el-tabs__nav.is-right,.el-tabs--right .el-tabs__nav.is-left,.el-tabs--right .el-tabs__nav.is-right{float:none}.el-tabs--left .el-tabs__item.is-left,.el-tabs--left .el-tabs__item.is-right,.el-tabs--right .el-tabs__item.is-left,.el-tabs--right .el-tabs__item.is-right{display:block}.el-tabs--left.el-tabs--card .el-tabs__active-bar.is-left,.el-tabs--right.el-tabs--card .el-tabs__active-bar.is-right{display:none}.el-tabs--left .el-tabs__header.is-left{float:left;margin-bottom:0;margin-right:10px}.el-tabs--left .el-tabs__nav-wrap.is-left{margin-right:-1px}.el-tabs--left .el-tabs__item.is-left{text-align:right}.el-tabs--left.el-tabs--card .el-tabs__item.is-left{border-left:none;border-right:1px solid #e4e7ed;border-bottom:none;border-top:1px solid #e4e7ed;text-align:left}.el-tabs--left.el-tabs--card .el-tabs__item.is-left:first-child{border-right:1px solid #e4e7ed;border-top:none}.el-tabs--left.el-tabs--card .el-tabs__item.is-left.is-active{border:1px solid #e4e7ed;border-right-color:#fff;border-left:none;border-bottom:none}.el-tabs--left.el-tabs--card .el-tabs__item.is-left.is-active:first-child{border-top:none}.el-tabs--left.el-tabs--card .el-tabs__item.is-left.is-active:last-child{border-bottom:none}.el-tabs--left.el-tabs--card .el-tabs__nav{border-radius:4px 0 0 4px;border-bottom:1px solid #e4e7ed;border-right:none}.el-tabs--left.el-tabs--card .el-tabs__new-tab{float:none}.el-tabs--left.el-tabs--border-card .el-tabs__header.is-left{border-right:1px solid #dfe4ed}.el-tabs--left.el-tabs--border-card .el-tabs__item.is-left{border:1px solid transparent;margin:-1px 0 -1px -1px}.el-tabs--left.el-tabs--border-card .el-tabs__item.is-left.is-active{border-color:#d1dbe5 transparent}.el-tabs--right .el-tabs__header.is-right{float:right;margin-bottom:0;margin-left:10px}.el-tabs--right .el-tabs__nav-wrap.is-right{margin-left:-1px}.el-tabs--right .el-tabs__nav-wrap.is-right:after{left:0;right:auto}.el-tabs--right .el-tabs__active-bar.is-right{left:0}.el-tabs--right.el-tabs--card .el-tabs__item.is-right{border-bottom:none;border-top:1px solid #e4e7ed}.el-tabs--right.el-tabs--card .el-tabs__item.is-right:first-child{border-left:1px solid #e4e7ed;border-top:none}.el-tabs--right.el-tabs--card .el-tabs__item.is-right.is-active{border:1px solid #e4e7ed;border-left-color:#fff;border-right:none;border-bottom:none}.el-tabs--right.el-tabs--card .el-tabs__item.is-right.is-active:first-child{border-top:none}.el-tabs--right.el-tabs--card .el-tabs__item.is-right.is-active:last-child{border-bottom:none}.el-tabs--right.el-tabs--card .el-tabs__nav{border-radius:0 4px 4px 0;border-bottom:1px solid #e4e7ed;border-left:none}.el-tabs--right.el-tabs--border-card .el-tabs__header.is-right{border-left:1px solid #dfe4ed}.el-tabs--right.el-tabs--border-card .el-tabs__item.is-right{border:1px solid transparent;margin:-1px -1px -1px 0}.el-tabs--right.el-tabs--border-card .el-tabs__item.is-right.is-active{border-color:#d1dbe5 transparent}.slideInLeft-transition,.slideInRight-transition{display:inline-block}.slideInRight-enter{-webkit-animation:slideInRight-enter .3s;animation:slideInRight-enter .3s}.slideInRight-leave{position:absolute;left:0;right:0;-webkit-animation:slideInRight-leave .3s;animation:slideInRight-leave .3s}.slideInLeft-enter{-webkit-animation:slideInLeft-enter .3s;animation:slideInLeft-enter .3s}.slideInLeft-leave{position:absolute;left:0;right:0;-webkit-animation:slideInLeft-leave .3s;animation:slideInLeft-leave .3s}@-webkit-keyframes slideInRight-enter{0%{opacity:0;-webkit-transform-origin:0 0;transform-origin:0 0;-webkit-transform:translateX(100%);transform:translateX(100%)}to{opacity:1;-webkit-transform-origin:0 0;transform-origin:0 0;-webkit-transform:translateX(0);transform:translateX(0)}}@keyframes slideInRight-enter{0%{opacity:0;-webkit-transform-origin:0 0;transform-origin:0 0;-webkit-transform:translateX(100%);transform:translateX(100%)}to{opacity:1;-webkit-transform-origin:0 0;transform-origin:0 0;-webkit-transform:translateX(0);transform:translateX(0)}}@-webkit-keyframes slideInRight-leave{0%{-webkit-transform-origin:0 0;transform-origin:0 0;-webkit-transform:translateX(0);transform:translateX(0);opacity:1}to{-webkit-transform-origin:0 0;transform-origin:0 0;-webkit-transform:translateX(100%);transform:translateX(100%);opacity:0}}@keyframes slideInRight-leave{0%{-webkit-transform-origin:0 0;transform-origin:0 0;-webkit-transform:translateX(0);transform:translateX(0);opacity:1}to{-webkit-transform-origin:0 0;transform-origin:0 0;-webkit-transform:translateX(100%);transform:translateX(100%);opacity:0}}@-webkit-keyframes slideInLeft-enter{0%{opacity:0;-webkit-transform-origin:0 0;transform-origin:0 0;-webkit-transform:translateX(-100%);transform:translateX(-100%)}to{opacity:1;-webkit-transform-origin:0 0;transform-origin:0 0;-webkit-transform:translateX(0);transform:translateX(0)}}@keyframes slideInLeft-enter{0%{opacity:0;-webkit-transform-origin:0 0;transform-origin:0 0;-webkit-transform:translateX(-100%);transform:translateX(-100%)}to{opacity:1;-webkit-transform-origin:0 0;transform-origin:0 0;-webkit-transform:translateX(0);transform:translateX(0)}}@-webkit-keyframes slideInLeft-leave{0%{-webkit-transform-origin:0 0;transform-origin:0 0;-webkit-transform:translateX(0);transform:translateX(0);opacity:1}to{-webkit-transform-origin:0 0;transform-origin:0 0;-webkit-transform:translateX(-100%);transform:translateX(-100%);opacity:0}}@keyframes slideInLeft-leave{0%{-webkit-transform-origin:0 0;transform-origin:0 0;-webkit-transform:translateX(0);transform:translateX(0);opacity:1}to{-webkit-transform-origin:0 0;transform-origin:0 0;-webkit-transform:translateX(-100%);transform:translateX(-100%);opacity:0}}.el-tree{position:relative;cursor:default;background:#fff;color:#606266}.el-tree__empty-block{position:relative;min-height:60px;text-align:center;width:100%;height:100%}.el-tree__empty-text{position:absolute;left:50%;top:50%;-webkit-transform:translate(-50%,-50%);transform:translate(-50%,-50%);color:#909399;font-size:14px}.el-tree__drop-indicator{position:absolute;left:0;right:0;height:1px;background-color:#409eff}.el-tree-node{white-space:nowrap;outline:0}.el-tree-node:focus>.el-tree-node__content{background-color:#f5f7fa}.el-tree-node.is-drop-inner>.el-tree-node__content .el-tree-node__label{background-color:#409eff;color:#fff}.el-tree-node__content{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;height:26px;cursor:pointer}.el-tree-node__content>.el-tree-node__expand-icon{padding:6px}.el-tree-node__content>label.el-checkbox{margin-right:8px}.el-tree-node__content:hover{background-color:#f5f7fa}.el-tree.is-dragging .el-tree-node__content{cursor:move}.el-tree.is-dragging.is-drop-not-allow .el-tree-node__content{cursor:not-allowed}.el-tree-node__expand-icon{cursor:pointer;color:#c0c4cc;font-size:12px;-webkit-transform:rotate(0);transform:rotate(0);-webkit-transition:-webkit-transform .3s ease-in-out;transition:-webkit-transform .3s ease-in-out;transition:transform .3s ease-in-out;transition:transform .3s ease-in-out,-webkit-transform .3s ease-in-out}.el-tree-node__expand-icon.expanded{-webkit-transform:rotate(90deg);transform:rotate(90deg)}.el-tree-node__expand-icon.is-leaf{color:transparent;cursor:default}.el-tree-node__label{font-size:14px}.el-tree-node__loading-icon{margin-right:8px;font-size:14px;color:#c0c4cc}.el-tree-node>.el-tree-node__children{overflow:hidden;background-color:transparent}.el-tree-node.is-expanded>.el-tree-node__children{display:block}.el-tree--highlight-current .el-tree-node.is-current>.el-tree-node__content{background-color:#f0f7ff}.el-alert{width:100%;padding:8px 16px;margin:0;-webkit-box-sizing:border-box;box-sizing:border-box;border-radius:4px;position:relative;background-color:#fff;overflow:hidden;opacity:1;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-transition:opacity .2s;transition:opacity .2s}.el-alert.is-light .el-alert__closebtn{color:#c0c4cc}.el-alert.is-dark .el-alert__closebtn,.el-alert.is-dark .el-alert__description{color:#fff}.el-alert.is-center{-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center}.el-alert--success.is-light{background-color:#f0f9eb;color:#67c23a}.el-alert--success.is-light .el-alert__description{color:#67c23a}.el-alert--success.is-dark{background-color:#67c23a;color:#fff}.el-alert--info.is-light{background-color:#f4f4f5;color:#909399}.el-alert--info.is-dark{background-color:#909399;color:#fff}.el-alert--info .el-alert__description{color:#909399}.el-alert--warning.is-light{background-color:#fdf6ec;color:#e6a23c}.el-alert--warning.is-light .el-alert__description{color:#e6a23c}.el-alert--warning.is-dark{background-color:#e6a23c;color:#fff}.el-alert--error.is-light{background-color:#fef0f0;color:#f56c6c}.el-alert--error.is-light .el-alert__description{color:#f56c6c}.el-alert--error.is-dark{background-color:#f56c6c;color:#fff}.el-alert__content{display:table-cell;padding:0 8px}.el-alert__icon{font-size:16px;width:16px}.el-alert__icon.is-big{font-size:28px;width:28px}.el-alert__title{font-size:13px;line-height:18px}.el-alert__title.is-bold{font-weight:700}.el-alert .el-alert__description{font-size:12px;margin:5px 0 0}.el-alert__closebtn{font-size:12px;opacity:1;position:absolute;top:12px;right:15px;cursor:pointer}.el-alert-fade-enter,.el-alert-fade-leave-active,.el-loading-fade-enter,.el-loading-fade-leave-active,.el-notification-fade-leave-active{opacity:0}.el-alert__closebtn.is-customed{font-style:normal;font-size:13px;top:9px}.el-notification{display:-webkit-box;display:-ms-flexbox;display:flex;width:330px;padding:14px 26px 14px 13px;border-radius:8px;-webkit-box-sizing:border-box;box-sizing:border-box;border:1px solid #ebeef5;position:fixed;background-color:#fff;-webkit-box-shadow:0 2px 12px 0 rgba(0,0,0,.1);box-shadow:0 2px 12px 0 rgba(0,0,0,.1);-webkit-transition:opacity .3s,left .3s,right .3s,top .4s,bottom .3s,-webkit-transform .3s;transition:opacity .3s,left .3s,right .3s,top .4s,bottom .3s,-webkit-transform .3s;transition:opacity .3s,transform .3s,left .3s,right .3s,top .4s,bottom .3s;transition:opacity .3s,transform .3s,left .3s,right .3s,top .4s,bottom .3s,-webkit-transform .3s;overflow:hidden}.el-notification.right{right:16px}.el-notification.left{left:16px}.el-notification__group{margin-left:13px;margin-right:8px}.el-notification__title{font-weight:700;font-size:16px;color:#303133;margin:0}.el-notification__content{font-size:14px;line-height:21px;margin:6px 0 0;color:#606266;text-align:justify}.el-notification__content p{margin:0}.el-notification__icon{height:24px;width:24px;font-size:24px}.el-notification__closeBtn{position:absolute;top:18px;right:15px;cursor:pointer;color:#909399;font-size:16px}.el-notification__closeBtn:hover{color:#606266}.el-notification .el-icon-success{color:#67c23a}.el-notification .el-icon-error{color:#f56c6c}.el-notification .el-icon-info{color:#909399}.el-notification .el-icon-warning{color:#e6a23c}.el-notification-fade-enter.right{right:0;-webkit-transform:translateX(100%);transform:translateX(100%)}.el-notification-fade-enter.left{left:0;-webkit-transform:translateX(-100%);transform:translateX(-100%)}.el-input-number{position:relative;display:inline-block;width:180px;line-height:38px}.el-input-number .el-input{display:block}.el-input-number .el-input__inner{-webkit-appearance:none;padding-left:50px;padding-right:50px;text-align:center}.el-input-number__decrease,.el-input-number__increase{position:absolute;z-index:1;top:1px;width:40px;height:auto;text-align:center;background:#f5f7fa;color:#606266;cursor:pointer;font-size:13px}.el-input-number__decrease:hover,.el-input-number__increase:hover{color:#409eff}.el-input-number__decrease:hover:not(.is-disabled)~.el-input .el-input__inner:not(.is-disabled),.el-input-number__increase:hover:not(.is-disabled)~.el-input .el-input__inner:not(.is-disabled){border-color:#409eff}.el-input-number__decrease.is-disabled,.el-input-number__increase.is-disabled{color:#c0c4cc;cursor:not-allowed}.el-input-number__increase{right:1px;border-radius:0 4px 4px 0;border-left:1px solid #dcdfe6}.el-input-number__decrease{left:1px;border-radius:4px 0 0 4px;border-right:1px solid #dcdfe6}.el-input-number.is-disabled .el-input-number__decrease,.el-input-number.is-disabled .el-input-number__increase{border-color:#e4e7ed;color:#e4e7ed}.el-input-number.is-disabled .el-input-number__decrease:hover,.el-input-number.is-disabled .el-input-number__increase:hover{color:#e4e7ed;cursor:not-allowed}.el-input-number--medium{width:200px;line-height:34px}.el-input-number--medium .el-input-number__decrease,.el-input-number--medium .el-input-number__increase{width:36px;font-size:14px}.el-input-number--medium .el-input__inner{padding-left:43px;padding-right:43px}.el-input-number--small{width:130px;line-height:30px}.el-input-number--small .el-input-number__decrease,.el-input-number--small .el-input-number__increase{width:32px;font-size:13px}.el-input-number--small .el-input-number__decrease [class*=el-icon],.el-input-number--small .el-input-number__increase [class*=el-icon]{-webkit-transform:scale(.9);transform:scale(.9)}.el-input-number--small .el-input__inner{padding-left:39px;padding-right:39px}.el-input-number--mini{width:130px;line-height:26px}.el-input-number--mini .el-input-number__decrease,.el-input-number--mini .el-input-number__increase{width:28px;font-size:12px}.el-input-number--mini .el-input-number__decrease [class*=el-icon],.el-input-number--mini .el-input-number__increase [class*=el-icon]{-webkit-transform:scale(.8);transform:scale(.8)}.el-input-number--mini .el-input__inner{padding-left:35px;padding-right:35px}.el-input-number.is-without-controls .el-input__inner{padding-left:15px;padding-right:15px}.el-input-number.is-controls-right .el-input__inner{padding-left:15px;padding-right:50px}.el-input-number.is-controls-right .el-input-number__decrease,.el-input-number.is-controls-right .el-input-number__increase{height:auto;line-height:19px}.el-input-number.is-controls-right .el-input-number__decrease [class*=el-icon],.el-input-number.is-controls-right .el-input-number__increase [class*=el-icon]{-webkit-transform:scale(.8);transform:scale(.8)}.el-input-number.is-controls-right .el-input-number__increase{border-radius:0 4px 0 0;border-bottom:1px solid #dcdfe6}.el-input-number.is-controls-right .el-input-number__decrease{right:1px;bottom:1px;top:auto;left:auto;border-right:none;border-left:1px solid #dcdfe6;border-radius:0 0 4px}.el-input-number.is-controls-right[class*=medium] [class*=decrease],.el-input-number.is-controls-right[class*=medium] [class*=increase]{line-height:17px}.el-input-number.is-controls-right[class*=small] [class*=decrease],.el-input-number.is-controls-right[class*=small] [class*=increase]{line-height:15px}.el-input-number.is-controls-right[class*=mini] [class*=decrease],.el-input-number.is-controls-right[class*=mini] [class*=increase]{line-height:13px}.el-tooltip__popper{position:absolute;border-radius:4px;padding:10px;z-index:2000;font-size:12px;line-height:1.2;min-width:10px;word-wrap:break-word}.el-tooltip__popper .popper__arrow,.el-tooltip__popper .popper__arrow:after{position:absolute;display:block;width:0;height:0;border-color:transparent;border-style:solid}.el-tooltip__popper .popper__arrow{border-width:6px}.el-tooltip__popper .popper__arrow:after{content:" ";border-width:5px}.el-progress-bar__inner:after,.el-row:after,.el-row:before,.el-slider:after,.el-slider:before,.el-slider__button-wrapper:after,.el-upload-cover:after{content:""}.el-tooltip__popper[x-placement^=top]{margin-bottom:12px}.el-tooltip__popper[x-placement^=top] .popper__arrow{bottom:-6px;border-top-color:#303133;border-bottom-width:0}.el-tooltip__popper[x-placement^=top] .popper__arrow:after{bottom:1px;margin-left:-5px;border-top-color:#303133;border-bottom-width:0}.el-tooltip__popper[x-placement^=bottom]{margin-top:12px}.el-tooltip__popper[x-placement^=bottom] .popper__arrow{top:-6px;border-top-width:0;border-bottom-color:#303133}.el-tooltip__popper[x-placement^=bottom] .popper__arrow:after{top:1px;margin-left:-5px;border-top-width:0;border-bottom-color:#303133}.el-tooltip__popper[x-placement^=right]{margin-left:12px}.el-tooltip__popper[x-placement^=right] .popper__arrow{left:-6px;border-right-color:#303133;border-left-width:0}.el-tooltip__popper[x-placement^=right] .popper__arrow:after{bottom:-5px;left:1px;border-right-color:#303133;border-left-width:0}.el-tooltip__popper[x-placement^=left]{margin-right:12px}.el-tooltip__popper[x-placement^=left] .popper__arrow{right:-6px;border-right-width:0;border-left-color:#303133}.el-tooltip__popper[x-placement^=left] .popper__arrow:after{right:1px;bottom:-5px;margin-left:-5px;border-right-width:0;border-left-color:#303133}.el-tooltip__popper.is-dark{background:#303133;color:#fff}.el-tooltip__popper.is-light{background:#fff;border:1px solid #303133}.el-tooltip__popper.is-light[x-placement^=top] .popper__arrow{border-top-color:#303133}.el-tooltip__popper.is-light[x-placement^=top] .popper__arrow:after{border-top-color:#fff}.el-tooltip__popper.is-light[x-placement^=bottom] .popper__arrow{border-bottom-color:#303133}.el-tooltip__popper.is-light[x-placement^=bottom] .popper__arrow:after{border-bottom-color:#fff}.el-tooltip__popper.is-light[x-placement^=left] .popper__arrow{border-left-color:#303133}.el-tooltip__popper.is-light[x-placement^=left] .popper__arrow:after{border-left-color:#fff}.el-tooltip__popper.is-light[x-placement^=right] .popper__arrow{border-right-color:#303133}.el-tooltip__popper.is-light[x-placement^=right] .popper__arrow:after{border-right-color:#fff}.el-slider:after,.el-slider:before{display:table}.el-slider__button-wrapper .el-tooltip,.el-slider__button-wrapper:after{vertical-align:middle;display:inline-block}.el-slider:after{clear:both}.el-slider__runway{width:100%;height:6px;margin:16px 0;background-color:#e4e7ed;border-radius:3px;position:relative;cursor:pointer;vertical-align:middle}.el-slider__runway.show-input{margin-right:160px;width:auto}.el-slider__runway.disabled{cursor:default}.el-slider__runway.disabled .el-slider__bar{background-color:#c0c4cc}.el-slider__runway.disabled .el-slider__button{border-color:#c0c4cc}.el-slider__runway.disabled .el-slider__button-wrapper.dragging,.el-slider__runway.disabled .el-slider__button-wrapper.hover,.el-slider__runway.disabled .el-slider__button-wrapper:hover{cursor:not-allowed}.el-slider__runway.disabled .el-slider__button.dragging,.el-slider__runway.disabled .el-slider__button.hover,.el-slider__runway.disabled .el-slider__button:hover{-webkit-transform:scale(1);transform:scale(1);cursor:not-allowed}.el-slider__button-wrapper,.el-slider__stop{-webkit-transform:translateX(-50%);position:absolute}.el-slider__input{float:right;margin-top:3px;width:130px}.el-slider__input.el-input-number--mini{margin-top:5px}.el-slider__input.el-input-number--medium{margin-top:0}.el-slider__input.el-input-number--large{margin-top:-2px}.el-slider__bar{height:6px;background-color:#409eff;border-top-left-radius:3px;border-bottom-left-radius:3px;position:absolute}.el-slider__button-wrapper{height:36px;width:36px;z-index:1001;top:-15px;-webkit-transform:translateX(-50%);transform:translateX(-50%);background-color:transparent;text-align:center;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;line-height:normal}.el-slider__button-wrapper:after{height:100%}.el-slider__button-wrapper.hover,.el-slider__button-wrapper:hover{cursor:-webkit-grab;cursor:grab}.el-slider__button-wrapper.dragging{cursor:-webkit-grabbing;cursor:grabbing}.el-slider__button{width:16px;height:16px;border:2px solid #409eff;background-color:#fff;border-radius:50%;-webkit-transition:.2s;transition:.2s;user-select:none}.el-image-viewer__btn,.el-slider__button,.el-step__icon-inner{-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none}.el-slider__button.dragging,.el-slider__button.hover,.el-slider__button:hover{-webkit-transform:scale(1.2);transform:scale(1.2)}.el-slider__button.hover,.el-slider__button:hover{cursor:-webkit-grab;cursor:grab}.el-slider__button.dragging{cursor:-webkit-grabbing;cursor:grabbing}.el-slider__stop{height:6px;width:6px;border-radius:100%;background-color:#fff;-webkit-transform:translateX(-50%);transform:translateX(-50%)}.el-slider__marks{top:0;left:12px;width:18px;height:100%}.el-slider__marks-text{position:absolute;-webkit-transform:translateX(-50%);transform:translateX(-50%);font-size:14px;color:#909399;margin-top:15px}.el-slider.is-vertical{position:relative}.el-slider.is-vertical .el-slider__runway{width:6px;height:100%;margin:0 16px}.el-slider.is-vertical .el-slider__bar{width:6px;height:auto;border-radius:0 0 3px 3px}.el-slider.is-vertical .el-slider__button-wrapper{top:auto;left:-15px}.el-slider.is-vertical .el-slider__button-wrapper,.el-slider.is-vertical .el-slider__stop{-webkit-transform:translateY(50%);transform:translateY(50%)}.el-slider.is-vertical.el-slider--with-input{padding-bottom:58px}.el-slider.is-vertical.el-slider--with-input .el-slider__input{overflow:visible;float:none;position:absolute;bottom:22px;width:36px;margin-top:15px}.el-slider.is-vertical.el-slider--with-input .el-slider__input .el-input__inner{text-align:center;padding-left:5px;padding-right:5px}.el-slider.is-vertical.el-slider--with-input .el-slider__input .el-input-number__decrease,.el-slider.is-vertical.el-slider--with-input .el-slider__input .el-input-number__increase{top:32px;margin-top:-1px;border:1px solid #dcdfe6;line-height:20px;-webkit-box-sizing:border-box;box-sizing:border-box;-webkit-transition:border-color .2s cubic-bezier(.645,.045,.355,1);transition:border-color .2s cubic-bezier(.645,.045,.355,1)}.el-slider.is-vertical.el-slider--with-input .el-slider__input .el-input-number__decrease{width:18px;right:18px;border-bottom-left-radius:4px}.el-slider.is-vertical.el-slider--with-input .el-slider__input .el-input-number__increase{width:19px;border-bottom-right-radius:4px}.el-slider.is-vertical.el-slider--with-input .el-slider__input .el-input-number__increase~.el-input .el-input__inner{border-bottom-left-radius:0;border-bottom-right-radius:0}.el-slider.is-vertical.el-slider--with-input .el-slider__input:hover .el-input-number__decrease,.el-slider.is-vertical.el-slider--with-input .el-slider__input:hover .el-input-number__increase{border-color:#c0c4cc}.el-slider.is-vertical.el-slider--with-input .el-slider__input:active .el-input-number__decrease,.el-slider.is-vertical.el-slider--with-input .el-slider__input:active .el-input-number__increase{border-color:#409eff}.el-slider.is-vertical .el-slider__marks-text{margin-top:0;left:15px;-webkit-transform:translateY(50%);transform:translateY(50%)}.el-loading-parent--relative{position:relative!important}.el-loading-parent--hidden{overflow:hidden!important}.el-loading-mask{position:absolute;z-index:2000;background-color:hsla(0,0%,100%,.9);margin:0;top:0;right:0;bottom:0;left:0;-webkit-transition:opacity .3s;transition:opacity .3s}.el-loading-mask.is-fullscreen{position:fixed}.el-loading-mask.is-fullscreen .el-loading-spinner{margin-top:-25px}.el-loading-mask.is-fullscreen .el-loading-spinner .circular{height:50px;width:50px}.el-loading-spinner{top:50%;margin-top:-21px;width:100%;text-align:center;position:absolute}.el-col-pull-0,.el-col-pull-1,.el-col-pull-2,.el-col-pull-3,.el-col-pull-4,.el-col-pull-5,.el-col-pull-6,.el-col-pull-7,.el-col-pull-8,.el-col-pull-9,.el-col-pull-10,.el-col-pull-11,.el-col-pull-13,.el-col-pull-14,.el-col-pull-15,.el-col-pull-16,.el-col-pull-17,.el-col-pull-18,.el-col-pull-19,.el-col-pull-20,.el-col-pull-21,.el-col-pull-22,.el-col-pull-23,.el-col-pull-24,.el-col-push-0,.el-col-push-1,.el-col-push-2,.el-col-push-3,.el-col-push-4,.el-col-push-5,.el-col-push-6,.el-col-push-7,.el-col-push-8,.el-col-push-9,.el-col-push-10,.el-col-push-11,.el-col-push-12,.el-col-push-13,.el-col-push-14,.el-col-push-15,.el-col-push-16,.el-col-push-17,.el-col-push-18,.el-col-push-19,.el-col-push-20,.el-col-push-21,.el-col-push-22,.el-col-push-23,.el-col-push-24,.el-row{position:relative}.el-loading-spinner .el-loading-text{color:#409eff;margin:3px 0;font-size:14px}.el-loading-spinner .circular{height:42px;width:42px;-webkit-animation:loading-rotate 2s linear infinite;animation:loading-rotate 2s linear infinite}.el-loading-spinner .path{-webkit-animation:loading-dash 1.5s ease-in-out infinite;animation:loading-dash 1.5s ease-in-out infinite;stroke-dasharray:90,150;stroke-dashoffset:0;stroke-width:2;stroke:#409eff;stroke-linecap:round}.el-loading-spinner i{color:#409eff}@-webkit-keyframes loading-rotate{to{-webkit-transform:rotate(1turn);transform:rotate(1turn)}}@keyframes loading-rotate{to{-webkit-transform:rotate(1turn);transform:rotate(1turn)}}@-webkit-keyframes loading-dash{0%{stroke-dasharray:1,200;stroke-dashoffset:0}50%{stroke-dasharray:90,150;stroke-dashoffset:-40px}to{stroke-dasharray:90,150;stroke-dashoffset:-120px}}@keyframes loading-dash{0%{stroke-dasharray:1,200;stroke-dashoffset:0}50%{stroke-dasharray:90,150;stroke-dashoffset:-40px}to{stroke-dasharray:90,150;stroke-dashoffset:-120px}}.el-row{-webkit-box-sizing:border-box;box-sizing:border-box}.el-row:after,.el-row:before{display:table}.el-row:after{clear:both}.el-row--flex{display:-webkit-box;display:-ms-flexbox;display:flex}.el-col-0,.el-row--flex:after,.el-row--flex:before{display:none}.el-row--flex.is-justify-center{-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center}.el-row--flex.is-justify-end{-webkit-box-pack:end;-ms-flex-pack:end;justify-content:flex-end}.el-row--flex.is-justify-space-between{-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between}.el-row--flex.is-justify-space-around{-ms-flex-pack:distribute;justify-content:space-around}.el-row--flex.is-align-middle{-webkit-box-align:center;-ms-flex-align:center;align-items:center}.el-row--flex.is-align-bottom{-webkit-box-align:end;-ms-flex-align:end;align-items:flex-end}[class*=el-col-]{float:left;-webkit-box-sizing:border-box;box-sizing:border-box}.el-upload--picture-card,.el-upload-dragger{-webkit-box-sizing:border-box;cursor:pointer}.el-col-0{width:0}.el-col-offset-0{margin-left:0}.el-col-pull-0{right:0}.el-col-push-0{left:0}.el-col-1{width:4.16667%}.el-col-offset-1{margin-left:4.16667%}.el-col-pull-1{right:4.16667%}.el-col-push-1{left:4.16667%}.el-col-2{width:8.33333%}.el-col-offset-2{margin-left:8.33333%}.el-col-pull-2{right:8.33333%}.el-col-push-2{left:8.33333%}.el-col-3{width:12.5%}.el-col-offset-3{margin-left:12.5%}.el-col-pull-3{right:12.5%}.el-col-push-3{left:12.5%}.el-col-4{width:16.66667%}.el-col-offset-4{margin-left:16.66667%}.el-col-pull-4{right:16.66667%}.el-col-push-4{left:16.66667%}.el-col-5{width:20.83333%}.el-col-offset-5{margin-left:20.83333%}.el-col-pull-5{right:20.83333%}.el-col-push-5{left:20.83333%}.el-col-6{width:25%}.el-col-offset-6{margin-left:25%}.el-col-pull-6{right:25%}.el-col-push-6{left:25%}.el-col-7{width:29.16667%}.el-col-offset-7{margin-left:29.16667%}.el-col-pull-7{right:29.16667%}.el-col-push-7{left:29.16667%}.el-col-8{width:33.33333%}.el-col-offset-8{margin-left:33.33333%}.el-col-pull-8{right:33.33333%}.el-col-push-8{left:33.33333%}.el-col-9{width:37.5%}.el-col-offset-9{margin-left:37.5%}.el-col-pull-9{right:37.5%}.el-col-push-9{left:37.5%}.el-col-10{width:41.66667%}.el-col-offset-10{margin-left:41.66667%}.el-col-pull-10{right:41.66667%}.el-col-push-10{left:41.66667%}.el-col-11{width:45.83333%}.el-col-offset-11{margin-left:45.83333%}.el-col-pull-11{right:45.83333%}.el-col-push-11{left:45.83333%}.el-col-12{width:50%}.el-col-offset-12{margin-left:50%}.el-col-pull-12{position:relative;right:50%}.el-col-push-12{left:50%}.el-col-13{width:54.16667%}.el-col-offset-13{margin-left:54.16667%}.el-col-pull-13{right:54.16667%}.el-col-push-13{left:54.16667%}.el-col-14{width:58.33333%}.el-col-offset-14{margin-left:58.33333%}.el-col-pull-14{right:58.33333%}.el-col-push-14{left:58.33333%}.el-col-15{width:62.5%}.el-col-offset-15{margin-left:62.5%}.el-col-pull-15{right:62.5%}.el-col-push-15{left:62.5%}.el-col-16{width:66.66667%}.el-col-offset-16{margin-left:66.66667%}.el-col-pull-16{right:66.66667%}.el-col-push-16{left:66.66667%}.el-col-17{width:70.83333%}.el-col-offset-17{margin-left:70.83333%}.el-col-pull-17{right:70.83333%}.el-col-push-17{left:70.83333%}.el-col-18{width:75%}.el-col-offset-18{margin-left:75%}.el-col-pull-18{right:75%}.el-col-push-18{left:75%}.el-col-19{width:79.16667%}.el-col-offset-19{margin-left:79.16667%}.el-col-pull-19{right:79.16667%}.el-col-push-19{left:79.16667%}.el-col-20{width:83.33333%}.el-col-offset-20{margin-left:83.33333%}.el-col-pull-20{right:83.33333%}.el-col-push-20{left:83.33333%}.el-col-21{width:87.5%}.el-col-offset-21{margin-left:87.5%}.el-col-pull-21{right:87.5%}.el-col-push-21{left:87.5%}.el-col-22{width:91.66667%}.el-col-offset-22{margin-left:91.66667%}.el-col-pull-22{right:91.66667%}.el-col-push-22{left:91.66667%}.el-col-23{width:95.83333%}.el-col-offset-23{margin-left:95.83333%}.el-col-pull-23{right:95.83333%}.el-col-push-23{left:95.83333%}.el-col-24{width:100%}.el-col-offset-24{margin-left:100%}.el-col-pull-24{right:100%}.el-col-push-24{left:100%}@media only screen and (max-width:767px){.el-col-xs-0{display:none;width:0}.el-col-xs-offset-0{margin-left:0}.el-col-xs-pull-0{position:relative;right:0}.el-col-xs-push-0{position:relative;left:0}.el-col-xs-1{width:4.16667%}.el-col-xs-offset-1{margin-left:4.16667%}.el-col-xs-pull-1{position:relative;right:4.16667%}.el-col-xs-push-1{position:relative;left:4.16667%}.el-col-xs-2{width:8.33333%}.el-col-xs-offset-2{margin-left:8.33333%}.el-col-xs-pull-2{position:relative;right:8.33333%}.el-col-xs-push-2{position:relative;left:8.33333%}.el-col-xs-3{width:12.5%}.el-col-xs-offset-3{margin-left:12.5%}.el-col-xs-pull-3{position:relative;right:12.5%}.el-col-xs-push-3{position:relative;left:12.5%}.el-col-xs-4{width:16.66667%}.el-col-xs-offset-4{margin-left:16.66667%}.el-col-xs-pull-4{position:relative;right:16.66667%}.el-col-xs-push-4{position:relative;left:16.66667%}.el-col-xs-5{width:20.83333%}.el-col-xs-offset-5{margin-left:20.83333%}.el-col-xs-pull-5{position:relative;right:20.83333%}.el-col-xs-push-5{position:relative;left:20.83333%}.el-col-xs-6{width:25%}.el-col-xs-offset-6{margin-left:25%}.el-col-xs-pull-6{position:relative;right:25%}.el-col-xs-push-6{position:relative;left:25%}.el-col-xs-7{width:29.16667%}.el-col-xs-offset-7{margin-left:29.16667%}.el-col-xs-pull-7{position:relative;right:29.16667%}.el-col-xs-push-7{position:relative;left:29.16667%}.el-col-xs-8{width:33.33333%}.el-col-xs-offset-8{margin-left:33.33333%}.el-col-xs-pull-8{position:relative;right:33.33333%}.el-col-xs-push-8{position:relative;left:33.33333%}.el-col-xs-9{width:37.5%}.el-col-xs-offset-9{margin-left:37.5%}.el-col-xs-pull-9{position:relative;right:37.5%}.el-col-xs-push-9{position:relative;left:37.5%}.el-col-xs-10{width:41.66667%}.el-col-xs-offset-10{margin-left:41.66667%}.el-col-xs-pull-10{position:relative;right:41.66667%}.el-col-xs-push-10{position:relative;left:41.66667%}.el-col-xs-11{width:45.83333%}.el-col-xs-offset-11{margin-left:45.83333%}.el-col-xs-pull-11{position:relative;right:45.83333%}.el-col-xs-push-11{position:relative;left:45.83333%}.el-col-xs-12{width:50%}.el-col-xs-offset-12{margin-left:50%}.el-col-xs-pull-12{position:relative;right:50%}.el-col-xs-push-12{position:relative;left:50%}.el-col-xs-13{width:54.16667%}.el-col-xs-offset-13{margin-left:54.16667%}.el-col-xs-pull-13{position:relative;right:54.16667%}.el-col-xs-push-13{position:relative;left:54.16667%}.el-col-xs-14{width:58.33333%}.el-col-xs-offset-14{margin-left:58.33333%}.el-col-xs-pull-14{position:relative;right:58.33333%}.el-col-xs-push-14{position:relative;left:58.33333%}.el-col-xs-15{width:62.5%}.el-col-xs-offset-15{margin-left:62.5%}.el-col-xs-pull-15{position:relative;right:62.5%}.el-col-xs-push-15{position:relative;left:62.5%}.el-col-xs-16{width:66.66667%}.el-col-xs-offset-16{margin-left:66.66667%}.el-col-xs-pull-16{position:relative;right:66.66667%}.el-col-xs-push-16{position:relative;left:66.66667%}.el-col-xs-17{width:70.83333%}.el-col-xs-offset-17{margin-left:70.83333%}.el-col-xs-pull-17{position:relative;right:70.83333%}.el-col-xs-push-17{position:relative;left:70.83333%}.el-col-xs-18{width:75%}.el-col-xs-offset-18{margin-left:75%}.el-col-xs-pull-18{position:relative;right:75%}.el-col-xs-push-18{position:relative;left:75%}.el-col-xs-19{width:79.16667%}.el-col-xs-offset-19{margin-left:79.16667%}.el-col-xs-pull-19{position:relative;right:79.16667%}.el-col-xs-push-19{position:relative;left:79.16667%}.el-col-xs-20{width:83.33333%}.el-col-xs-offset-20{margin-left:83.33333%}.el-col-xs-pull-20{position:relative;right:83.33333%}.el-col-xs-push-20{position:relative;left:83.33333%}.el-col-xs-21{width:87.5%}.el-col-xs-offset-21{margin-left:87.5%}.el-col-xs-pull-21{position:relative;right:87.5%}.el-col-xs-push-21{position:relative;left:87.5%}.el-col-xs-22{width:91.66667%}.el-col-xs-offset-22{margin-left:91.66667%}.el-col-xs-pull-22{position:relative;right:91.66667%}.el-col-xs-push-22{position:relative;left:91.66667%}.el-col-xs-23{width:95.83333%}.el-col-xs-offset-23{margin-left:95.83333%}.el-col-xs-pull-23{position:relative;right:95.83333%}.el-col-xs-push-23{position:relative;left:95.83333%}.el-col-xs-24{width:100%}.el-col-xs-offset-24{margin-left:100%}.el-col-xs-pull-24{position:relative;right:100%}.el-col-xs-push-24{position:relative;left:100%}}@media only screen and (min-width:768px){.el-col-sm-0{display:none;width:0}.el-col-sm-offset-0{margin-left:0}.el-col-sm-pull-0{position:relative;right:0}.el-col-sm-push-0{position:relative;left:0}.el-col-sm-1{width:4.16667%}.el-col-sm-offset-1{margin-left:4.16667%}.el-col-sm-pull-1{position:relative;right:4.16667%}.el-col-sm-push-1{position:relative;left:4.16667%}.el-col-sm-2{width:8.33333%}.el-col-sm-offset-2{margin-left:8.33333%}.el-col-sm-pull-2{position:relative;right:8.33333%}.el-col-sm-push-2{position:relative;left:8.33333%}.el-col-sm-3{width:12.5%}.el-col-sm-offset-3{margin-left:12.5%}.el-col-sm-pull-3{position:relative;right:12.5%}.el-col-sm-push-3{position:relative;left:12.5%}.el-col-sm-4{width:16.66667%}.el-col-sm-offset-4{margin-left:16.66667%}.el-col-sm-pull-4{position:relative;right:16.66667%}.el-col-sm-push-4{position:relative;left:16.66667%}.el-col-sm-5{width:20.83333%}.el-col-sm-offset-5{margin-left:20.83333%}.el-col-sm-pull-5{position:relative;right:20.83333%}.el-col-sm-push-5{position:relative;left:20.83333%}.el-col-sm-6{width:25%}.el-col-sm-offset-6{margin-left:25%}.el-col-sm-pull-6{position:relative;right:25%}.el-col-sm-push-6{position:relative;left:25%}.el-col-sm-7{width:29.16667%}.el-col-sm-offset-7{margin-left:29.16667%}.el-col-sm-pull-7{position:relative;right:29.16667%}.el-col-sm-push-7{position:relative;left:29.16667%}.el-col-sm-8{width:33.33333%}.el-col-sm-offset-8{margin-left:33.33333%}.el-col-sm-pull-8{position:relative;right:33.33333%}.el-col-sm-push-8{position:relative;left:33.33333%}.el-col-sm-9{width:37.5%}.el-col-sm-offset-9{margin-left:37.5%}.el-col-sm-pull-9{position:relative;right:37.5%}.el-col-sm-push-9{position:relative;left:37.5%}.el-col-sm-10{width:41.66667%}.el-col-sm-offset-10{margin-left:41.66667%}.el-col-sm-pull-10{position:relative;right:41.66667%}.el-col-sm-push-10{position:relative;left:41.66667%}.el-col-sm-11{width:45.83333%}.el-col-sm-offset-11{margin-left:45.83333%}.el-col-sm-pull-11{position:relative;right:45.83333%}.el-col-sm-push-11{position:relative;left:45.83333%}.el-col-sm-12{width:50%}.el-col-sm-offset-12{margin-left:50%}.el-col-sm-pull-12{position:relative;right:50%}.el-col-sm-push-12{position:relative;left:50%}.el-col-sm-13{width:54.16667%}.el-col-sm-offset-13{margin-left:54.16667%}.el-col-sm-pull-13{position:relative;right:54.16667%}.el-col-sm-push-13{position:relative;left:54.16667%}.el-col-sm-14{width:58.33333%}.el-col-sm-offset-14{margin-left:58.33333%}.el-col-sm-pull-14{position:relative;right:58.33333%}.el-col-sm-push-14{position:relative;left:58.33333%}.el-col-sm-15{width:62.5%}.el-col-sm-offset-15{margin-left:62.5%}.el-col-sm-pull-15{position:relative;right:62.5%}.el-col-sm-push-15{position:relative;left:62.5%}.el-col-sm-16{width:66.66667%}.el-col-sm-offset-16{margin-left:66.66667%}.el-col-sm-pull-16{position:relative;right:66.66667%}.el-col-sm-push-16{position:relative;left:66.66667%}.el-col-sm-17{width:70.83333%}.el-col-sm-offset-17{margin-left:70.83333%}.el-col-sm-pull-17{position:relative;right:70.83333%}.el-col-sm-push-17{position:relative;left:70.83333%}.el-col-sm-18{width:75%}.el-col-sm-offset-18{margin-left:75%}.el-col-sm-pull-18{position:relative;right:75%}.el-col-sm-push-18{position:relative;left:75%}.el-col-sm-19{width:79.16667%}.el-col-sm-offset-19{margin-left:79.16667%}.el-col-sm-pull-19{position:relative;right:79.16667%}.el-col-sm-push-19{position:relative;left:79.16667%}.el-col-sm-20{width:83.33333%}.el-col-sm-offset-20{margin-left:83.33333%}.el-col-sm-pull-20{position:relative;right:83.33333%}.el-col-sm-push-20{position:relative;left:83.33333%}.el-col-sm-21{width:87.5%}.el-col-sm-offset-21{margin-left:87.5%}.el-col-sm-pull-21{position:relative;right:87.5%}.el-col-sm-push-21{position:relative;left:87.5%}.el-col-sm-22{width:91.66667%}.el-col-sm-offset-22{margin-left:91.66667%}.el-col-sm-pull-22{position:relative;right:91.66667%}.el-col-sm-push-22{position:relative;left:91.66667%}.el-col-sm-23{width:95.83333%}.el-col-sm-offset-23{margin-left:95.83333%}.el-col-sm-pull-23{position:relative;right:95.83333%}.el-col-sm-push-23{position:relative;left:95.83333%}.el-col-sm-24{width:100%}.el-col-sm-offset-24{margin-left:100%}.el-col-sm-pull-24{position:relative;right:100%}.el-col-sm-push-24{position:relative;left:100%}}@media only screen and (min-width:992px){.el-col-md-0{display:none;width:0}.el-col-md-offset-0{margin-left:0}.el-col-md-pull-0{position:relative;right:0}.el-col-md-push-0{position:relative;left:0}.el-col-md-1{width:4.16667%}.el-col-md-offset-1{margin-left:4.16667%}.el-col-md-pull-1{position:relative;right:4.16667%}.el-col-md-push-1{position:relative;left:4.16667%}.el-col-md-2{width:8.33333%}.el-col-md-offset-2{margin-left:8.33333%}.el-col-md-pull-2{position:relative;right:8.33333%}.el-col-md-push-2{position:relative;left:8.33333%}.el-col-md-3{width:12.5%}.el-col-md-offset-3{margin-left:12.5%}.el-col-md-pull-3{position:relative;right:12.5%}.el-col-md-push-3{position:relative;left:12.5%}.el-col-md-4{width:16.66667%}.el-col-md-offset-4{margin-left:16.66667%}.el-col-md-pull-4{position:relative;right:16.66667%}.el-col-md-push-4{position:relative;left:16.66667%}.el-col-md-5{width:20.83333%}.el-col-md-offset-5{margin-left:20.83333%}.el-col-md-pull-5{position:relative;right:20.83333%}.el-col-md-push-5{position:relative;left:20.83333%}.el-col-md-6{width:25%}.el-col-md-offset-6{margin-left:25%}.el-col-md-pull-6{position:relative;right:25%}.el-col-md-push-6{position:relative;left:25%}.el-col-md-7{width:29.16667%}.el-col-md-offset-7{margin-left:29.16667%}.el-col-md-pull-7{position:relative;right:29.16667%}.el-col-md-push-7{position:relative;left:29.16667%}.el-col-md-8{width:33.33333%}.el-col-md-offset-8{margin-left:33.33333%}.el-col-md-pull-8{position:relative;right:33.33333%}.el-col-md-push-8{position:relative;left:33.33333%}.el-col-md-9{width:37.5%}.el-col-md-offset-9{margin-left:37.5%}.el-col-md-pull-9{position:relative;right:37.5%}.el-col-md-push-9{position:relative;left:37.5%}.el-col-md-10{width:41.66667%}.el-col-md-offset-10{margin-left:41.66667%}.el-col-md-pull-10{position:relative;right:41.66667%}.el-col-md-push-10{position:relative;left:41.66667%}.el-col-md-11{width:45.83333%}.el-col-md-offset-11{margin-left:45.83333%}.el-col-md-pull-11{position:relative;right:45.83333%}.el-col-md-push-11{position:relative;left:45.83333%}.el-col-md-12{width:50%}.el-col-md-offset-12{margin-left:50%}.el-col-md-pull-12{position:relative;right:50%}.el-col-md-push-12{position:relative;left:50%}.el-col-md-13{width:54.16667%}.el-col-md-offset-13{margin-left:54.16667%}.el-col-md-pull-13{position:relative;right:54.16667%}.el-col-md-push-13{position:relative;left:54.16667%}.el-col-md-14{width:58.33333%}.el-col-md-offset-14{margin-left:58.33333%}.el-col-md-pull-14{position:relative;right:58.33333%}.el-col-md-push-14{position:relative;left:58.33333%}.el-col-md-15{width:62.5%}.el-col-md-offset-15{margin-left:62.5%}.el-col-md-pull-15{position:relative;right:62.5%}.el-col-md-push-15{position:relative;left:62.5%}.el-col-md-16{width:66.66667%}.el-col-md-offset-16{margin-left:66.66667%}.el-col-md-pull-16{position:relative;right:66.66667%}.el-col-md-push-16{position:relative;left:66.66667%}.el-col-md-17{width:70.83333%}.el-col-md-offset-17{margin-left:70.83333%}.el-col-md-pull-17{position:relative;right:70.83333%}.el-col-md-push-17{position:relative;left:70.83333%}.el-col-md-18{width:75%}.el-col-md-offset-18{margin-left:75%}.el-col-md-pull-18{position:relative;right:75%}.el-col-md-push-18{position:relative;left:75%}.el-col-md-19{width:79.16667%}.el-col-md-offset-19{margin-left:79.16667%}.el-col-md-pull-19{position:relative;right:79.16667%}.el-col-md-push-19{position:relative;left:79.16667%}.el-col-md-20{width:83.33333%}.el-col-md-offset-20{margin-left:83.33333%}.el-col-md-pull-20{position:relative;right:83.33333%}.el-col-md-push-20{position:relative;left:83.33333%}.el-col-md-21{width:87.5%}.el-col-md-offset-21{margin-left:87.5%}.el-col-md-pull-21{position:relative;right:87.5%}.el-col-md-push-21{position:relative;left:87.5%}.el-col-md-22{width:91.66667%}.el-col-md-offset-22{margin-left:91.66667%}.el-col-md-pull-22{position:relative;right:91.66667%}.el-col-md-push-22{position:relative;left:91.66667%}.el-col-md-23{width:95.83333%}.el-col-md-offset-23{margin-left:95.83333%}.el-col-md-pull-23{position:relative;right:95.83333%}.el-col-md-push-23{position:relative;left:95.83333%}.el-col-md-24{width:100%}.el-col-md-offset-24{margin-left:100%}.el-col-md-pull-24{position:relative;right:100%}.el-col-md-push-24{position:relative;left:100%}}@media only screen and (min-width:1200px){.el-col-lg-0{display:none;width:0}.el-col-lg-offset-0{margin-left:0}.el-col-lg-pull-0{position:relative;right:0}.el-col-lg-push-0{position:relative;left:0}.el-col-lg-1{width:4.16667%}.el-col-lg-offset-1{margin-left:4.16667%}.el-col-lg-pull-1{position:relative;right:4.16667%}.el-col-lg-push-1{position:relative;left:4.16667%}.el-col-lg-2{width:8.33333%}.el-col-lg-offset-2{margin-left:8.33333%}.el-col-lg-pull-2{position:relative;right:8.33333%}.el-col-lg-push-2{position:relative;left:8.33333%}.el-col-lg-3{width:12.5%}.el-col-lg-offset-3{margin-left:12.5%}.el-col-lg-pull-3{position:relative;right:12.5%}.el-col-lg-push-3{position:relative;left:12.5%}.el-col-lg-4{width:16.66667%}.el-col-lg-offset-4{margin-left:16.66667%}.el-col-lg-pull-4{position:relative;right:16.66667%}.el-col-lg-push-4{position:relative;left:16.66667%}.el-col-lg-5{width:20.83333%}.el-col-lg-offset-5{margin-left:20.83333%}.el-col-lg-pull-5{position:relative;right:20.83333%}.el-col-lg-push-5{position:relative;left:20.83333%}.el-col-lg-6{width:25%}.el-col-lg-offset-6{margin-left:25%}.el-col-lg-pull-6{position:relative;right:25%}.el-col-lg-push-6{position:relative;left:25%}.el-col-lg-7{width:29.16667%}.el-col-lg-offset-7{margin-left:29.16667%}.el-col-lg-pull-7{position:relative;right:29.16667%}.el-col-lg-push-7{position:relative;left:29.16667%}.el-col-lg-8{width:33.33333%}.el-col-lg-offset-8{margin-left:33.33333%}.el-col-lg-pull-8{position:relative;right:33.33333%}.el-col-lg-push-8{position:relative;left:33.33333%}.el-col-lg-9{width:37.5%}.el-col-lg-offset-9{margin-left:37.5%}.el-col-lg-pull-9{position:relative;right:37.5%}.el-col-lg-push-9{position:relative;left:37.5%}.el-col-lg-10{width:41.66667%}.el-col-lg-offset-10{margin-left:41.66667%}.el-col-lg-pull-10{position:relative;right:41.66667%}.el-col-lg-push-10{position:relative;left:41.66667%}.el-col-lg-11{width:45.83333%}.el-col-lg-offset-11{margin-left:45.83333%}.el-col-lg-pull-11{position:relative;right:45.83333%}.el-col-lg-push-11{position:relative;left:45.83333%}.el-col-lg-12{width:50%}.el-col-lg-offset-12{margin-left:50%}.el-col-lg-pull-12{position:relative;right:50%}.el-col-lg-push-12{position:relative;left:50%}.el-col-lg-13{width:54.16667%}.el-col-lg-offset-13{margin-left:54.16667%}.el-col-lg-pull-13{position:relative;right:54.16667%}.el-col-lg-push-13{position:relative;left:54.16667%}.el-col-lg-14{width:58.33333%}.el-col-lg-offset-14{margin-left:58.33333%}.el-col-lg-pull-14{position:relative;right:58.33333%}.el-col-lg-push-14{position:relative;left:58.33333%}.el-col-lg-15{width:62.5%}.el-col-lg-offset-15{margin-left:62.5%}.el-col-lg-pull-15{position:relative;right:62.5%}.el-col-lg-push-15{position:relative;left:62.5%}.el-col-lg-16{width:66.66667%}.el-col-lg-offset-16{margin-left:66.66667%}.el-col-lg-pull-16{position:relative;right:66.66667%}.el-col-lg-push-16{position:relative;left:66.66667%}.el-col-lg-17{width:70.83333%}.el-col-lg-offset-17{margin-left:70.83333%}.el-col-lg-pull-17{position:relative;right:70.83333%}.el-col-lg-push-17{position:relative;left:70.83333%}.el-col-lg-18{width:75%}.el-col-lg-offset-18{margin-left:75%}.el-col-lg-pull-18{position:relative;right:75%}.el-col-lg-push-18{position:relative;left:75%}.el-col-lg-19{width:79.16667%}.el-col-lg-offset-19{margin-left:79.16667%}.el-col-lg-pull-19{position:relative;right:79.16667%}.el-col-lg-push-19{position:relative;left:79.16667%}.el-col-lg-20{width:83.33333%}.el-col-lg-offset-20{margin-left:83.33333%}.el-col-lg-pull-20{position:relative;right:83.33333%}.el-col-lg-push-20{position:relative;left:83.33333%}.el-col-lg-21{width:87.5%}.el-col-lg-offset-21{margin-left:87.5%}.el-col-lg-pull-21{position:relative;right:87.5%}.el-col-lg-push-21{position:relative;left:87.5%}.el-col-lg-22{width:91.66667%}.el-col-lg-offset-22{margin-left:91.66667%}.el-col-lg-pull-22{position:relative;right:91.66667%}.el-col-lg-push-22{position:relative;left:91.66667%}.el-col-lg-23{width:95.83333%}.el-col-lg-offset-23{margin-left:95.83333%}.el-col-lg-pull-23{position:relative;right:95.83333%}.el-col-lg-push-23{position:relative;left:95.83333%}.el-col-lg-24{width:100%}.el-col-lg-offset-24{margin-left:100%}.el-col-lg-pull-24{position:relative;right:100%}.el-col-lg-push-24{position:relative;left:100%}}@media only screen and (min-width:1920px){.el-col-xl-0{display:none;width:0}.el-col-xl-offset-0{margin-left:0}.el-col-xl-pull-0{position:relative;right:0}.el-col-xl-push-0{position:relative;left:0}.el-col-xl-1{width:4.16667%}.el-col-xl-offset-1{margin-left:4.16667%}.el-col-xl-pull-1{position:relative;right:4.16667%}.el-col-xl-push-1{position:relative;left:4.16667%}.el-col-xl-2{width:8.33333%}.el-col-xl-offset-2{margin-left:8.33333%}.el-col-xl-pull-2{position:relative;right:8.33333%}.el-col-xl-push-2{position:relative;left:8.33333%}.el-col-xl-3{width:12.5%}.el-col-xl-offset-3{margin-left:12.5%}.el-col-xl-pull-3{position:relative;right:12.5%}.el-col-xl-push-3{position:relative;left:12.5%}.el-col-xl-4{width:16.66667%}.el-col-xl-offset-4{margin-left:16.66667%}.el-col-xl-pull-4{position:relative;right:16.66667%}.el-col-xl-push-4{position:relative;left:16.66667%}.el-col-xl-5{width:20.83333%}.el-col-xl-offset-5{margin-left:20.83333%}.el-col-xl-pull-5{position:relative;right:20.83333%}.el-col-xl-push-5{position:relative;left:20.83333%}.el-col-xl-6{width:25%}.el-col-xl-offset-6{margin-left:25%}.el-col-xl-pull-6{position:relative;right:25%}.el-col-xl-push-6{position:relative;left:25%}.el-col-xl-7{width:29.16667%}.el-col-xl-offset-7{margin-left:29.16667%}.el-col-xl-pull-7{position:relative;right:29.16667%}.el-col-xl-push-7{position:relative;left:29.16667%}.el-col-xl-8{width:33.33333%}.el-col-xl-offset-8{margin-left:33.33333%}.el-col-xl-pull-8{position:relative;right:33.33333%}.el-col-xl-push-8{position:relative;left:33.33333%}.el-col-xl-9{width:37.5%}.el-col-xl-offset-9{margin-left:37.5%}.el-col-xl-pull-9{position:relative;right:37.5%}.el-col-xl-push-9{position:relative;left:37.5%}.el-col-xl-10{width:41.66667%}.el-col-xl-offset-10{margin-left:41.66667%}.el-col-xl-pull-10{position:relative;right:41.66667%}.el-col-xl-push-10{position:relative;left:41.66667%}.el-col-xl-11{width:45.83333%}.el-col-xl-offset-11{margin-left:45.83333%}.el-col-xl-pull-11{position:relative;right:45.83333%}.el-col-xl-push-11{position:relative;left:45.83333%}.el-col-xl-12{width:50%}.el-col-xl-offset-12{margin-left:50%}.el-col-xl-pull-12{position:relative;right:50%}.el-col-xl-push-12{position:relative;left:50%}.el-col-xl-13{width:54.16667%}.el-col-xl-offset-13{margin-left:54.16667%}.el-col-xl-pull-13{position:relative;right:54.16667%}.el-col-xl-push-13{position:relative;left:54.16667%}.el-col-xl-14{width:58.33333%}.el-col-xl-offset-14{margin-left:58.33333%}.el-col-xl-pull-14{position:relative;right:58.33333%}.el-col-xl-push-14{position:relative;left:58.33333%}.el-col-xl-15{width:62.5%}.el-col-xl-offset-15{margin-left:62.5%}.el-col-xl-pull-15{position:relative;right:62.5%}.el-col-xl-push-15{position:relative;left:62.5%}.el-col-xl-16{width:66.66667%}.el-col-xl-offset-16{margin-left:66.66667%}.el-col-xl-pull-16{position:relative;right:66.66667%}.el-col-xl-push-16{position:relative;left:66.66667%}.el-col-xl-17{width:70.83333%}.el-col-xl-offset-17{margin-left:70.83333%}.el-col-xl-pull-17{position:relative;right:70.83333%}.el-col-xl-push-17{position:relative;left:70.83333%}.el-col-xl-18{width:75%}.el-col-xl-offset-18{margin-left:75%}.el-col-xl-pull-18{position:relative;right:75%}.el-col-xl-push-18{position:relative;left:75%}.el-col-xl-19{width:79.16667%}.el-col-xl-offset-19{margin-left:79.16667%}.el-col-xl-pull-19{position:relative;right:79.16667%}.el-col-xl-push-19{position:relative;left:79.16667%}.el-col-xl-20{width:83.33333%}.el-col-xl-offset-20{margin-left:83.33333%}.el-col-xl-pull-20{position:relative;right:83.33333%}.el-col-xl-push-20{position:relative;left:83.33333%}.el-col-xl-21{width:87.5%}.el-col-xl-offset-21{margin-left:87.5%}.el-col-xl-pull-21{position:relative;right:87.5%}.el-col-xl-push-21{position:relative;left:87.5%}.el-col-xl-22{width:91.66667%}.el-col-xl-offset-22{margin-left:91.66667%}.el-col-xl-pull-22{position:relative;right:91.66667%}.el-col-xl-push-22{position:relative;left:91.66667%}.el-col-xl-23{width:95.83333%}.el-col-xl-offset-23{margin-left:95.83333%}.el-col-xl-pull-23{position:relative;right:95.83333%}.el-col-xl-push-23{position:relative;left:95.83333%}.el-col-xl-24{width:100%}.el-col-xl-offset-24{margin-left:100%}.el-col-xl-pull-24{position:relative;right:100%}.el-col-xl-push-24{position:relative;left:100%}}@-webkit-keyframes progress{0%{background-position:0 0}to{background-position:32px 0}}.el-upload{display:inline-block;text-align:center;cursor:pointer;outline:0}.el-upload__input{display:none}.el-upload__tip{font-size:12px;color:#606266;margin-top:7px}.el-upload iframe{position:absolute;z-index:-1;top:0;left:0;opacity:0;filter:alpha(opacity=0)}.el-upload--picture-card{background-color:#fbfdff;border:1px dashed #c0ccda;border-radius:6px;-webkit-box-sizing:border-box;box-sizing:border-box;width:148px;height:148px;line-height:146px;vertical-align:top}.el-upload--picture-card i{font-size:28px;color:#8c939d}.el-upload--picture-card:hover,.el-upload:focus{border-color:#409eff;color:#409eff}.el-upload:focus .el-upload-dragger{border-color:#409eff}.el-upload-dragger{background-color:#fff;border:1px dashed #d9d9d9;border-radius:6px;-webkit-box-sizing:border-box;box-sizing:border-box;width:360px;height:180px;text-align:center;position:relative;overflow:hidden}.el-upload-dragger .el-icon-upload{font-size:67px;color:#c0c4cc;margin:40px 0 16px;line-height:50px}.el-upload-dragger+.el-upload__tip{text-align:center}.el-upload-dragger~.el-upload__files{border-top:1px solid #dcdfe6;margin-top:7px;padding-top:5px}.el-upload-dragger .el-upload__text{color:#606266;font-size:14px;text-align:center}.el-upload-dragger .el-upload__text em{color:#409eff;font-style:normal}.el-upload-dragger:hover{border-color:#409eff}.el-upload-dragger.is-dragover{background-color:rgba(32,159,255,.06);border:2px dashed #409eff}.el-upload-list{margin:0;padding:0;list-style:none}.el-upload-list__item{-webkit-transition:all .5s cubic-bezier(.55,0,.1,1);transition:all .5s cubic-bezier(.55,0,.1,1);font-size:14px;color:#606266;line-height:1.8;margin-top:5px;position:relative;-webkit-box-sizing:border-box;box-sizing:border-box;border-radius:4px;width:100%}.el-upload-list__item .el-progress{position:absolute;top:20px;width:100%}.el-upload-list__item .el-progress__text{position:absolute;right:0;top:-13px}.el-upload-list__item .el-progress-bar{margin-right:0;padding-right:0}.el-upload-list__item:first-child{margin-top:10px}.el-upload-list__item .el-icon-upload-success{color:#67c23a}.el-upload-list__item .el-icon-close{display:none;position:absolute;top:5px;right:5px;cursor:pointer;opacity:.75;color:#606266}.el-upload-list__item .el-icon-close:hover{opacity:1}.el-upload-list__item .el-icon-close-tip{display:none;position:absolute;top:5px;right:5px;font-size:12px;cursor:pointer;opacity:1;color:#409eff}.el-upload-list__item:hover{background-color:#f5f7fa}.el-upload-list__item:hover .el-icon-close{display:inline-block}.el-upload-list__item:hover .el-progress__text{display:none}.el-upload-list__item.is-success .el-upload-list__item-status-label{display:block}.el-upload-list__item.is-success .el-upload-list__item-name:focus,.el-upload-list__item.is-success .el-upload-list__item-name:hover{color:#409eff;cursor:pointer}.el-upload-list__item.is-success:focus:not(:hover) .el-icon-close-tip{display:inline-block}.el-upload-list__item.is-success:active .el-icon-close-tip,.el-upload-list__item.is-success:focus .el-upload-list__item-status-label,.el-upload-list__item.is-success:hover .el-upload-list__item-status-label,.el-upload-list__item.is-success:not(.focusing):focus .el-icon-close-tip{display:none}.el-upload-list.is-disabled .el-upload-list__item:hover .el-upload-list__item-status-label{display:block}.el-upload-list__item-name{color:#606266;display:block;margin-right:40px;overflow:hidden;padding-left:4px;text-overflow:ellipsis;-webkit-transition:color .3s;transition:color .3s;white-space:nowrap}.el-upload-list__item-name [class^=el-icon]{height:100%;margin-right:7px;color:#909399;line-height:inherit}.el-upload-list__item-status-label{position:absolute;right:5px;top:0;line-height:inherit;display:none}.el-upload-list__item-delete{position:absolute;right:10px;top:0;font-size:12px;color:#606266;display:none}.el-upload-list__item-delete:hover{color:#409eff}.el-upload-list--picture-card{margin:0;display:inline;vertical-align:top}.el-upload-list--picture-card .el-upload-list__item{overflow:hidden;background-color:#fff;border:1px solid #c0ccda;border-radius:6px;-webkit-box-sizing:border-box;box-sizing:border-box;width:148px;height:148px;margin:0 8px 8px 0;display:inline-block}.el-upload-list--picture-card .el-upload-list__item .el-icon-check,.el-upload-list--picture-card .el-upload-list__item .el-icon-circle-check{color:#fff}.el-upload-list--picture-card .el-upload-list__item .el-icon-close,.el-upload-list--picture-card .el-upload-list__item:hover .el-upload-list__item-status-label{display:none}.el-upload-list--picture-card .el-upload-list__item:hover .el-progress__text{display:block}.el-upload-list--picture-card .el-upload-list__item-name{display:none}.el-upload-list--picture-card .el-upload-list__item-thumbnail{width:100%;height:100%}.el-upload-list--picture-card .el-upload-list__item-status-label{position:absolute;right:-15px;top:-6px;width:40px;height:24px;background:#13ce66;text-align:center;-webkit-transform:rotate(45deg);transform:rotate(45deg);-webkit-box-shadow:0 0 1pc 1px rgba(0,0,0,.2);box-shadow:0 0 1pc 1px rgba(0,0,0,.2)}.el-upload-list--picture-card .el-upload-list__item-status-label i{font-size:12px;margin-top:11px;-webkit-transform:rotate(-45deg);transform:rotate(-45deg)}.el-upload-list--picture-card .el-upload-list__item-actions{position:absolute;width:100%;height:100%;left:0;top:0;cursor:default;text-align:center;color:#fff;opacity:0;font-size:20px;background-color:rgba(0,0,0,.5);-webkit-transition:opacity .3s;transition:opacity .3s}.el-upload-list--picture-card .el-upload-list__item-actions:after{display:inline-block;content:"";height:100%;vertical-align:middle}.el-upload-list--picture-card .el-upload-list__item-actions span{display:none;cursor:pointer}.el-upload-list--picture-card .el-upload-list__item-actions span+span{margin-left:15px}.el-upload-list--picture-card .el-upload-list__item-actions .el-upload-list__item-delete{position:static;font-size:inherit;color:inherit}.el-upload-list--picture-card .el-upload-list__item-actions:hover{opacity:1}.el-upload-list--picture-card .el-upload-list__item-actions:hover span{display:inline-block}.el-upload-list--picture-card .el-progress{top:50%;left:50%;-webkit-transform:translate(-50%,-50%);transform:translate(-50%,-50%);bottom:auto;width:126px}.el-upload-list--picture-card .el-progress .el-progress__text{top:50%}.el-upload-list--picture .el-upload-list__item{overflow:hidden;z-index:0;background-color:#fff;border:1px solid #c0ccda;border-radius:6px;-webkit-box-sizing:border-box;box-sizing:border-box;margin-top:10px;padding:10px 10px 10px 90px;height:92px}.el-upload-list--picture .el-upload-list__item .el-icon-check,.el-upload-list--picture .el-upload-list__item .el-icon-circle-check{color:#fff}.el-upload-list--picture .el-upload-list__item:hover .el-upload-list__item-status-label{background:0 0;-webkit-box-shadow:none;box-shadow:none;top:-2px;right:-12px}.el-upload-list--picture .el-upload-list__item:hover .el-progress__text{display:block}.el-upload-list--picture .el-upload-list__item.is-success .el-upload-list__item-name{line-height:70px;margin-top:0}.el-upload-list--picture .el-upload-list__item.is-success .el-upload-list__item-name i{display:none}.el-upload-list--picture .el-upload-list__item-thumbnail{vertical-align:middle;display:inline-block;width:70px;height:70px;float:left;position:relative;z-index:1;margin-left:-80px;background-color:#fff}.el-upload-list--picture .el-upload-list__item-name{display:block;margin-top:20px}.el-upload-list--picture .el-upload-list__item-name i{font-size:70px;line-height:1;position:absolute;left:9px;top:10px}.el-upload-list--picture .el-upload-list__item-status-label{position:absolute;right:-17px;top:-7px;width:46px;height:26px;background:#13ce66;text-align:center;-webkit-transform:rotate(45deg);transform:rotate(45deg);-webkit-box-shadow:0 1px 1px #ccc;box-shadow:0 1px 1px #ccc}.el-upload-list--picture .el-upload-list__item-status-label i{font-size:12px;margin-top:12px;-webkit-transform:rotate(-45deg);transform:rotate(-45deg)}.el-upload-list--picture .el-progress{position:relative;top:-7px}.el-upload-cover{position:absolute;left:0;top:0;width:100%;height:100%;overflow:hidden;z-index:10;cursor:default}.el-upload-cover:after{display:inline-block;height:100%;vertical-align:middle}.el-upload-cover img{display:block;width:100%;height:100%}.el-upload-cover__label{position:absolute;right:-15px;top:-6px;width:40px;height:24px;background:#13ce66;text-align:center;-webkit-transform:rotate(45deg);transform:rotate(45deg);-webkit-box-shadow:0 0 1pc 1px rgba(0,0,0,.2);box-shadow:0 0 1pc 1px rgba(0,0,0,.2)}.el-upload-cover__label i{font-size:12px;margin-top:11px;-webkit-transform:rotate(-45deg);transform:rotate(-45deg);color:#fff}.el-upload-cover__progress{display:inline-block;vertical-align:middle;position:static;width:243px}.el-upload-cover__progress+.el-upload__inner{opacity:0}.el-upload-cover__content{position:absolute;top:0;left:0;width:100%;height:100%}.el-upload-cover__interact{position:absolute;bottom:0;left:0;width:100%;height:100%;background-color:rgba(0,0,0,.72);text-align:center}.el-upload-cover__interact .btn{display:inline-block;color:#fff;font-size:14px;cursor:pointer;vertical-align:middle;-webkit-transition:opacity .3s cubic-bezier(.23,1,.32,1),-webkit-transform .3s cubic-bezier(.23,1,.32,1);transition:opacity .3s cubic-bezier(.23,1,.32,1),-webkit-transform .3s cubic-bezier(.23,1,.32,1);transition:transform .3s cubic-bezier(.23,1,.32,1),opacity .3s cubic-bezier(.23,1,.32,1);transition:transform .3s cubic-bezier(.23,1,.32,1),opacity .3s cubic-bezier(.23,1,.32,1),-webkit-transform .3s cubic-bezier(.23,1,.32,1);margin-top:60px}.el-upload-cover__interact .btn span{opacity:0;-webkit-transition:opacity .15s linear;transition:opacity .15s linear}.el-upload-cover__interact .btn:not(:first-child){margin-left:35px}.el-upload-cover__interact .btn:hover{-webkit-transform:translateY(-13px);transform:translateY(-13px)}.el-upload-cover__interact .btn:hover span{opacity:1}.el-upload-cover__interact .btn i{color:#fff;display:block;font-size:24px;line-height:inherit;margin:0 auto 5px}.el-upload-cover__title{position:absolute;bottom:0;left:0;background-color:#fff;height:36px;width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-weight:400;text-align:left;padding:0 10px;margin:0;line-height:36px;font-size:14px;color:#303133}.el-upload-cover+.el-upload__inner{opacity:0;position:relative;z-index:1}.el-progress{position:relative;line-height:1}.el-progress__text{font-size:14px;color:#606266;display:inline-block;vertical-align:middle;margin-left:10px;line-height:1}.el-progress__text i{vertical-align:middle;display:block}.el-progress--circle,.el-progress--dashboard{display:inline-block}.el-progress--circle .el-progress__text,.el-progress--dashboard .el-progress__text{position:absolute;top:50%;left:0;width:100%;text-align:center;margin:0;-webkit-transform:translateY(-50%);transform:translateY(-50%)}.el-progress--circle .el-progress__text i,.el-progress--dashboard .el-progress__text i{vertical-align:middle;display:inline-block}.el-progress--without-text .el-progress__text{display:none}.el-progress--without-text .el-progress-bar{padding-right:0;margin-right:0;display:block}.el-progress-bar,.el-progress-bar__inner:after,.el-progress-bar__innerText,.el-spinner{display:inline-block;vertical-align:middle}.el-progress--text-inside .el-progress-bar{padding-right:0;margin-right:0}.el-progress.is-success .el-progress-bar__inner{background-color:#67c23a}.el-progress.is-success .el-progress__text{color:#67c23a}.el-progress.is-warning .el-progress-bar__inner{background-color:#e6a23c}.el-progress.is-warning .el-progress__text{color:#e6a23c}.el-progress.is-exception .el-progress-bar__inner{background-color:#f56c6c}.el-progress.is-exception .el-progress__text{color:#f56c6c}.el-progress-bar{padding-right:50px;width:100%;margin-right:-55px;-webkit-box-sizing:border-box;box-sizing:border-box}.el-progress-bar__outer{height:6px;border-radius:100px;background-color:#ebeef5;overflow:hidden;position:relative;vertical-align:middle}.el-progress-bar__inner{position:absolute;left:0;top:0;height:100%;background-color:#409eff;text-align:right;border-radius:100px;line-height:1;white-space:nowrap;-webkit-transition:width .6s ease;transition:width .6s ease}.el-card,.el-message{border-radius:4px;overflow:hidden}.el-progress-bar__inner:after{height:100%}.el-progress-bar__innerText{color:#fff;font-size:12px;margin:0 5px}@keyframes progress{0%{background-position:0 0}to{background-position:32px 0}}.el-time-spinner{width:100%;white-space:nowrap}.el-spinner-inner{-webkit-animation:rotate 2s linear infinite;animation:rotate 2s linear infinite;width:50px;height:50px}.el-spinner-inner .path{stroke:#ececec;stroke-linecap:round;-webkit-animation:dash 1.5s ease-in-out infinite;animation:dash 1.5s ease-in-out infinite}@-webkit-keyframes rotate{to{-webkit-transform:rotate(1turn);transform:rotate(1turn)}}@keyframes rotate{to{-webkit-transform:rotate(1turn);transform:rotate(1turn)}}@-webkit-keyframes dash{0%{stroke-dasharray:1,150;stroke-dashoffset:0}50%{stroke-dasharray:90,150;stroke-dashoffset:-35}to{stroke-dasharray:90,150;stroke-dashoffset:-124}}@keyframes dash{0%{stroke-dasharray:1,150;stroke-dashoffset:0}50%{stroke-dasharray:90,150;stroke-dashoffset:-35}to{stroke-dasharray:90,150;stroke-dashoffset:-124}}.el-message{min-width:380px;-webkit-box-sizing:border-box;box-sizing:border-box;border-width:1px;border-style:solid;border-color:#ebeef5;position:fixed;left:50%;top:20px;-webkit-transform:translateX(-50%);transform:translateX(-50%);background-color:#edf2fc;-webkit-transition:opacity .3s,top .4s,-webkit-transform .4s;transition:opacity .3s,top .4s,-webkit-transform .4s;transition:opacity .3s,transform .4s,top .4s;transition:opacity .3s,transform .4s,top .4s,-webkit-transform .4s;padding:15px 15px 15px 20px;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center}.el-message.is-center{-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center}.el-message.is-closable .el-message__content{padding-right:16px}.el-message p{margin:0}.el-message--info .el-message__content{color:#909399}.el-message--success{background-color:#f0f9eb;border-color:#e1f3d8}.el-message--success .el-message__content{color:#67c23a}.el-message--warning{background-color:#fdf6ec;border-color:#faecd8}.el-message--warning .el-message__content{color:#e6a23c}.el-message--error{background-color:#fef0f0;border-color:#fde2e2}.el-message--error .el-message__content{color:#f56c6c}.el-message__icon{margin-right:10px}.el-message__content{padding:0;font-size:14px;line-height:1}.el-message__closeBtn{position:absolute;top:50%;right:15px;-webkit-transform:translateY(-50%);transform:translateY(-50%);cursor:pointer;color:#c0c4cc;font-size:16px}.el-message__closeBtn:hover{color:#909399}.el-message .el-icon-success{color:#67c23a}.el-message .el-icon-error{color:#f56c6c}.el-message .el-icon-info{color:#909399}.el-message .el-icon-warning{color:#e6a23c}.el-message-fade-enter,.el-message-fade-leave-active{opacity:0;-webkit-transform:translate(-50%,-100%);transform:translate(-50%,-100%)}.el-badge{position:relative;vertical-align:middle;display:inline-block}.el-badge__content{background-color:#f56c6c;border-radius:10px;color:#fff;display:inline-block;font-size:12px;height:18px;line-height:18px;padding:0 6px;text-align:center;white-space:nowrap;border:1px solid #fff}.el-badge__content.is-fixed{position:absolute;top:0;right:10px;-webkit-transform:translateY(-50%) translateX(100%);transform:translateY(-50%) translateX(100%)}.el-rate__icon,.el-rate__item{position:relative;display:inline-block}.el-badge__content.is-fixed.is-dot{right:5px}.el-badge__content.is-dot{height:8px;width:8px;padding:0;right:0;border-radius:50%}.el-badge__content--primary{background-color:#409eff}.el-badge__content--success{background-color:#67c23a}.el-badge__content--warning{background-color:#e6a23c}.el-badge__content--info{background-color:#909399}.el-badge__content--danger{background-color:#f56c6c}.el-card{border:1px solid #ebeef5;background-color:#fff;color:#303133;-webkit-transition:.3s;transition:.3s}.el-card.is-always-shadow,.el-card.is-hover-shadow:focus,.el-card.is-hover-shadow:hover{-webkit-box-shadow:0 2px 12px 0 rgba(0,0,0,.1);box-shadow:0 2px 12px 0 rgba(0,0,0,.1)}.el-card__header{padding:18px 20px;border-bottom:1px solid #ebeef5;-webkit-box-sizing:border-box;box-sizing:border-box}.el-card__body{padding:20px}.el-rate{height:20px;line-height:1}.el-rate__item{font-size:0;vertical-align:middle}.el-rate__icon{font-size:18px;margin-right:6px;color:#c0c4cc;-webkit-transition:.3s;transition:.3s}.el-rate__decimal,.el-rate__icon .path2{position:absolute;top:0;left:0}.el-rate__icon.hover{-webkit-transform:scale(1.15);transform:scale(1.15)}.el-rate__decimal{display:inline-block;overflow:hidden}.el-step.is-vertical,.el-steps{display:-webkit-box;display:-ms-flexbox}.el-rate__text{font-size:14px;vertical-align:middle}.el-steps{display:-webkit-box;display:-ms-flexbox;display:flex}.el-steps--simple{padding:13px 8%;border-radius:4px;background:#f5f7fa}.el-steps--horizontal{white-space:nowrap}.el-steps--vertical{height:100%;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-flow:column;flex-flow:column}.el-step{position:relative;-ms-flex-negative:1;flex-shrink:1}.el-step:last-of-type .el-step__line{display:none}.el-step:last-of-type.is-flex{-ms-flex-preferred-size:auto!important;flex-basis:auto!important;-ms-flex-negative:0;flex-shrink:0;-webkit-box-flex:0;-ms-flex-positive:0;flex-grow:0}.el-step:last-of-type .el-step__description,.el-step:last-of-type .el-step__main{padding-right:0}.el-step__head{position:relative;width:100%}.el-step__head.is-process{color:#303133;border-color:#303133}.el-step__head.is-wait{color:#c0c4cc;border-color:#c0c4cc}.el-step__head.is-success{color:#67c23a;border-color:#67c23a}.el-step__head.is-error{color:#f56c6c;border-color:#f56c6c}.el-step__head.is-finish{color:#409eff;border-color:#409eff}.el-step__icon{position:relative;z-index:1;display:-webkit-inline-box;display:-ms-inline-flexbox;display:inline-flex;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;-webkit-box-align:center;-ms-flex-align:center;align-items:center;width:24px;height:24px;font-size:14px;-webkit-box-sizing:border-box;box-sizing:border-box;background:#fff;-webkit-transition:.15s ease-out;transition:.15s ease-out}.el-step__icon.is-text{border-radius:50%;border:2px solid;border-color:inherit}.el-step__icon.is-icon{width:40px}.el-step__icon-inner{display:inline-block;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;text-align:center;font-weight:700;line-height:1;color:inherit}.el-step__icon-inner[class*=el-icon]:not(.is-status){font-size:25px;font-weight:400}.el-step__icon-inner.is-status{-webkit-transform:translateY(1px);transform:translateY(1px)}.el-step__line{position:absolute;border-color:inherit;background-color:#c0c4cc}.el-step__line-inner{display:block;border-width:1px;border-style:solid;border-color:inherit;-webkit-transition:.15s ease-out;transition:.15s ease-out;-webkit-box-sizing:border-box;box-sizing:border-box;width:0;height:0}.el-step__main{white-space:normal;text-align:left}.el-step__title{font-size:16px;line-height:38px}.el-step__title.is-process{font-weight:700;color:#303133}.el-step__title.is-wait{color:#c0c4cc}.el-step__title.is-success{color:#67c23a}.el-step__title.is-error{color:#f56c6c}.el-step__title.is-finish{color:#409eff}.el-step__description{padding-right:10%;margin-top:-5px;font-size:12px;line-height:20px;font-weight:400}.el-step__description.is-process{color:#303133}.el-step__description.is-wait{color:#c0c4cc}.el-step__description.is-success{color:#67c23a}.el-step__description.is-error{color:#f56c6c}.el-step__description.is-finish{color:#409eff}.el-step.is-horizontal{display:inline-block}.el-step.is-horizontal .el-step__line{height:2px;top:11px;left:0;right:0}.el-step.is-vertical{display:-webkit-box;display:-ms-flexbox;display:flex}.el-step.is-vertical .el-step__head{-webkit-box-flex:0;-ms-flex-positive:0;flex-grow:0;width:24px}.el-step.is-vertical .el-step__main{padding-left:10px;-webkit-box-flex:1;-ms-flex-positive:1;flex-grow:1}.el-step.is-vertical .el-step__title{line-height:24px;padding-bottom:8px}.el-step.is-vertical .el-step__line{width:2px;top:0;bottom:0;left:11px}.el-step.is-vertical .el-step__icon.is-icon{width:24px}.el-step.is-center .el-step__head,.el-step.is-center .el-step__main{text-align:center}.el-step.is-center .el-step__description{padding-left:20%;padding-right:20%}.el-step.is-center .el-step__line{left:50%;right:-50%}.el-step.is-simple{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center}.el-step.is-simple .el-step__head{width:auto;font-size:0;padding-right:10px}.el-step.is-simple .el-step__icon{background:0 0;width:16px;height:16px;font-size:12px}.el-step.is-simple .el-step__icon-inner[class*=el-icon]:not(.is-status){font-size:18px}.el-step.is-simple .el-step__icon-inner.is-status{-webkit-transform:scale(.8) translateY(1px);transform:scale(.8) translateY(1px)}.el-step.is-simple .el-step__main{position:relative;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:stretch;-ms-flex-align:stretch;align-items:stretch;-webkit-box-flex:1;-ms-flex-positive:1;flex-grow:1}.el-step.is-simple .el-step__title{font-size:16px;line-height:20px}.el-step.is-simple:not(:last-of-type) .el-step__title{max-width:50%;word-break:break-all}.el-step.is-simple .el-step__arrow{-webkit-box-flex:1;-ms-flex-positive:1;flex-grow:1;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center}.el-step.is-simple .el-step__arrow:after,.el-step.is-simple .el-step__arrow:before{content:"";display:inline-block;position:absolute;height:15px;width:1px;background:#c0c4cc}.el-step.is-simple .el-step__arrow:before{-webkit-transform:rotate(-45deg) translateY(-4px);transform:rotate(-45deg) translateY(-4px);-webkit-transform-origin:0 0;transform-origin:0 0}.el-step.is-simple .el-step__arrow:after{-webkit-transform:rotate(45deg) translateY(4px);transform:rotate(45deg) translateY(4px);-webkit-transform-origin:100% 100%;transform-origin:100% 100%}.el-step.is-simple:last-of-type .el-step__arrow{display:none}.el-carousel{position:relative}.el-carousel--horizontal{overflow-x:hidden}.el-carousel--vertical{overflow-y:hidden}.el-carousel__container{position:relative;height:300px}.el-carousel__arrow{border:none;outline:0;padding:0;margin:0;height:36px;width:36px;cursor:pointer;-webkit-transition:.3s;transition:.3s;border-radius:50%;background-color:rgba(31,45,61,.11);color:#fff;position:absolute;top:50%;z-index:10;-webkit-transform:translateY(-50%);transform:translateY(-50%);text-align:center;font-size:12px}.el-carousel__arrow--left{left:16px}.el-carousel__arrow--right{right:16px}.el-carousel__arrow:hover{background-color:rgba(31,45,61,.23)}.el-carousel__arrow i{cursor:pointer}.el-carousel__indicators{position:absolute;list-style:none;margin:0;padding:0;z-index:2}.el-carousel__indicators--horizontal{bottom:0;left:50%;-webkit-transform:translateX(-50%);transform:translateX(-50%)}.el-carousel__indicators--vertical{right:0;top:50%;-webkit-transform:translateY(-50%);transform:translateY(-50%)}.el-carousel__indicators--outside{bottom:26px;text-align:center;position:static;-webkit-transform:none;transform:none}.el-carousel__indicators--outside .el-carousel__indicator:hover button{opacity:.64}.el-carousel__indicators--outside button{background-color:#c0c4cc;opacity:.24}.el-carousel__indicators--labels{left:0;right:0;-webkit-transform:none;transform:none;text-align:center}.el-carousel__indicators--labels .el-carousel__button{height:auto;width:auto;padding:2px 18px;font-size:12px}.el-carousel__indicators--labels .el-carousel__indicator{padding:6px 4px}.el-carousel__indicator{background-color:transparent;cursor:pointer}.el-carousel__indicator:hover button{opacity:.72}.el-carousel__indicator--horizontal{display:inline-block;padding:12px 4px}.el-carousel__indicator--vertical{padding:4px 12px}.el-carousel__indicator--vertical .el-carousel__button{width:2px;height:15px}.el-carousel__indicator.is-active button{opacity:1}.el-carousel__button{display:block;opacity:.48;width:30px;height:2px;background-color:#fff;border:none;outline:0;padding:0;margin:0;cursor:pointer;-webkit-transition:.3s;transition:.3s}.el-carousel__item,.el-carousel__mask{height:100%;top:0;left:0;position:absolute}.carousel-arrow-left-enter,.carousel-arrow-left-leave-active{-webkit-transform:translateY(-50%) translateX(-10px);transform:translateY(-50%) translateX(-10px);opacity:0}.carousel-arrow-right-enter,.carousel-arrow-right-leave-active{-webkit-transform:translateY(-50%) translateX(10px);transform:translateY(-50%) translateX(10px);opacity:0}.el-carousel__item{width:100%;display:inline-block;overflow:hidden;z-index:0}.el-carousel__item.is-active{z-index:2}.el-carousel__item--card,.el-carousel__item.is-animating{-webkit-transition:-webkit-transform .4s ease-in-out;transition:-webkit-transform .4s ease-in-out;transition:transform .4s ease-in-out;transition:transform .4s ease-in-out,-webkit-transform .4s ease-in-out}.el-carousel__item--card{width:50%}.el-carousel__item--card.is-in-stage{cursor:pointer;z-index:1}.el-carousel__item--card.is-in-stage.is-hover .el-carousel__mask,.el-carousel__item--card.is-in-stage:hover .el-carousel__mask{opacity:.12}.el-carousel__item--card.is-active{z-index:2}.el-carousel__mask{width:100%;background-color:#fff;opacity:.24;-webkit-transition:.2s;transition:.2s}.el-fade-in-enter,.el-fade-in-leave-active,.el-fade-in-linear-enter,.el-fade-in-linear-leave,.el-fade-in-linear-leave-active,.fade-in-linear-enter,.fade-in-linear-leave,.fade-in-linear-leave-active{opacity:0}.el-fade-in-linear-enter-active,.el-fade-in-linear-leave-active,.fade-in-linear-enter-active,.fade-in-linear-leave-active{-webkit-transition:opacity .2s linear;transition:opacity .2s linear}.el-fade-in-enter-active,.el-fade-in-leave-active,.el-zoom-in-center-enter-active,.el-zoom-in-center-leave-active{-webkit-transition:all .3s cubic-bezier(.55,0,.1,1);transition:all .3s cubic-bezier(.55,0,.1,1)}.el-zoom-in-center-enter,.el-zoom-in-center-leave-active{opacity:0;-webkit-transform:scaleX(0);transform:scaleX(0)}.el-zoom-in-top-enter-active,.el-zoom-in-top-leave-active{opacity:1;-webkit-transform:scaleY(1);transform:scaleY(1);-webkit-transition:opacity .3s cubic-bezier(.23,1,.32,1),-webkit-transform .3s cubic-bezier(.23,1,.32,1);transition:opacity .3s cubic-bezier(.23,1,.32,1),-webkit-transform .3s cubic-bezier(.23,1,.32,1);transition:transform .3s cubic-bezier(.23,1,.32,1),opacity .3s cubic-bezier(.23,1,.32,1);transition:transform .3s cubic-bezier(.23,1,.32,1),opacity .3s cubic-bezier(.23,1,.32,1),-webkit-transform .3s cubic-bezier(.23,1,.32,1);-webkit-transform-origin:center top;transform-origin:center top}.el-zoom-in-top-enter,.el-zoom-in-top-leave-active{opacity:0;-webkit-transform:scaleY(0);transform:scaleY(0)}.el-zoom-in-bottom-enter-active,.el-zoom-in-bottom-leave-active{opacity:1;-webkit-transform:scaleY(1);transform:scaleY(1);-webkit-transition:opacity .3s cubic-bezier(.23,1,.32,1),-webkit-transform .3s cubic-bezier(.23,1,.32,1);transition:opacity .3s cubic-bezier(.23,1,.32,1),-webkit-transform .3s cubic-bezier(.23,1,.32,1);transition:transform .3s cubic-bezier(.23,1,.32,1),opacity .3s cubic-bezier(.23,1,.32,1);transition:transform .3s cubic-bezier(.23,1,.32,1),opacity .3s cubic-bezier(.23,1,.32,1),-webkit-transform .3s cubic-bezier(.23,1,.32,1);-webkit-transform-origin:center bottom;transform-origin:center bottom}.el-zoom-in-bottom-enter,.el-zoom-in-bottom-leave-active{opacity:0;-webkit-transform:scaleY(0);transform:scaleY(0)}.el-zoom-in-left-enter-active,.el-zoom-in-left-leave-active{opacity:1;-webkit-transform:scale(1);transform:scale(1);-webkit-transition:opacity .3s cubic-bezier(.23,1,.32,1),-webkit-transform .3s cubic-bezier(.23,1,.32,1);transition:opacity .3s cubic-bezier(.23,1,.32,1),-webkit-transform .3s cubic-bezier(.23,1,.32,1);transition:transform .3s cubic-bezier(.23,1,.32,1),opacity .3s cubic-bezier(.23,1,.32,1);transition:transform .3s cubic-bezier(.23,1,.32,1),opacity .3s cubic-bezier(.23,1,.32,1),-webkit-transform .3s cubic-bezier(.23,1,.32,1);-webkit-transform-origin:top left;transform-origin:top left}.el-zoom-in-left-enter,.el-zoom-in-left-leave-active{opacity:0;-webkit-transform:scale(.45);transform:scale(.45)}.collapse-transition{-webkit-transition:height .3s ease-in-out,padding-top .3s ease-in-out,padding-bottom .3s ease-in-out;transition:height .3s ease-in-out,padding-top .3s ease-in-out,padding-bottom .3s ease-in-out}.horizontal-collapse-transition{-webkit-transition:width .3s ease-in-out,padding-left .3s ease-in-out,padding-right .3s ease-in-out;transition:width .3s ease-in-out,padding-left .3s ease-in-out,padding-right .3s ease-in-out}.el-list-enter-active,.el-list-leave-active{-webkit-transition:all 1s;transition:all 1s}.el-list-enter,.el-list-leave-active{opacity:0;-webkit-transform:translateY(-30px);transform:translateY(-30px)}.el-opacity-transition{-webkit-transition:opacity .3s cubic-bezier(.55,0,.1,1);transition:opacity .3s cubic-bezier(.55,0,.1,1)}.el-collapse{border-top:1px solid #ebeef5;border-bottom:1px solid #ebeef5}.el-collapse-item.is-disabled .el-collapse-item__header{color:#bbb;cursor:not-allowed}.el-collapse-item__header{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;height:48px;line-height:48px;background-color:#fff;color:#303133;cursor:pointer;border-bottom:1px solid #ebeef5;font-size:13px;font-weight:500;-webkit-transition:border-bottom-color .3s;transition:border-bottom-color .3s;outline:0}.el-collapse-item__arrow{margin:0 8px 0 auto;transition:-webkit-transform .3s;-webkit-transition:-webkit-transform .3s;transition:transform .3s;transition:transform .3s,-webkit-transform .3s;font-weight:300}.el-collapse-item__arrow.is-active{-webkit-transform:rotate(90deg);transform:rotate(90deg)}.el-collapse-item__header.focusing:focus:not(:hover){color:#409eff}.el-collapse-item__header.is-active{border-bottom-color:transparent}.el-collapse-item__wrap{will-change:height;background-color:#fff;overflow:hidden;box-sizing:border-box;border-bottom:1px solid #ebeef5}.el-cascader__tags,.el-collapse-item__wrap,.el-tag{-webkit-box-sizing:border-box}.el-collapse-item__content{padding-bottom:25px;font-size:13px;color:#303133;line-height:1.769230769230769}.el-collapse-item:last-child{margin-bottom:-1px}.el-popper .popper__arrow,.el-popper .popper__arrow:after{position:absolute;display:block;width:0;height:0;border-color:transparent;border-style:solid}.el-popper .popper__arrow{border-width:6px;-webkit-filter:drop-shadow(0 2px 12px rgba(0,0,0,.03));filter:drop-shadow(0 2px 12px rgba(0,0,0,.03))}.el-popper .popper__arrow:after{content:" ";border-width:6px}.el-popper[x-placement^=top]{margin-bottom:12px}.el-popper[x-placement^=top] .popper__arrow{bottom:-6px;left:50%;margin-right:3px;border-top-color:#ebeef5;border-bottom-width:0}.el-popper[x-placement^=top] .popper__arrow:after{bottom:1px;margin-left:-6px;border-top-color:#fff;border-bottom-width:0}.el-popper[x-placement^=bottom]{margin-top:12px}.el-popper[x-placement^=bottom] .popper__arrow{top:-6px;left:50%;margin-right:3px;border-top-width:0;border-bottom-color:#ebeef5}.el-popper[x-placement^=bottom] .popper__arrow:after{top:1px;margin-left:-6px;border-top-width:0;border-bottom-color:#fff}.el-popper[x-placement^=right]{margin-left:12px}.el-popper[x-placement^=right] .popper__arrow{top:50%;left:-6px;margin-bottom:3px;border-right-color:#ebeef5;border-left-width:0}.el-popper[x-placement^=right] .popper__arrow:after{bottom:-6px;left:1px;border-right-color:#fff;border-left-width:0}.el-popper[x-placement^=left]{margin-right:12px}.el-popper[x-placement^=left] .popper__arrow{top:50%;right:-6px;margin-bottom:3px;border-right-width:0;border-left-color:#ebeef5}.el-popper[x-placement^=left] .popper__arrow:after{right:1px;bottom:-6px;margin-left:-6px;border-right-width:0;border-left-color:#fff}.el-tag{background-color:#ecf5ff;border-color:#d9ecff;display:inline-block;height:32px;padding:0 10px;line-height:30px;font-size:12px;color:#409eff;border-width:1px;border-style:solid;border-radius:4px;-webkit-box-sizing:border-box;box-sizing:border-box;white-space:nowrap}.el-tag.is-hit{border-color:#409eff}.el-tag .el-tag__close{color:#409eff}.el-tag .el-tag__close:hover{color:#fff;background-color:#409eff}.el-tag.el-tag--info{background-color:#f4f4f5;border-color:#e9e9eb;color:#909399}.el-tag.el-tag--info.is-hit{border-color:#909399}.el-tag.el-tag--info .el-tag__close{color:#909399}.el-tag.el-tag--info .el-tag__close:hover{color:#fff;background-color:#909399}.el-tag.el-tag--success{background-color:#f0f9eb;border-color:#e1f3d8;color:#67c23a}.el-tag.el-tag--success.is-hit{border-color:#67c23a}.el-tag.el-tag--success .el-tag__close{color:#67c23a}.el-tag.el-tag--success .el-tag__close:hover{color:#fff;background-color:#67c23a}.el-tag.el-tag--warning{background-color:#fdf6ec;border-color:#faecd8;color:#e6a23c}.el-tag.el-tag--warning.is-hit{border-color:#e6a23c}.el-tag.el-tag--warning .el-tag__close{color:#e6a23c}.el-tag.el-tag--warning .el-tag__close:hover{color:#fff;background-color:#e6a23c}.el-tag.el-tag--danger{background-color:#fef0f0;border-color:#fde2e2;color:#f56c6c}.el-tag.el-tag--danger.is-hit{border-color:#f56c6c}.el-tag.el-tag--danger .el-tag__close{color:#f56c6c}.el-tag.el-tag--danger .el-tag__close:hover{color:#fff;background-color:#f56c6c}.el-tag .el-icon-close{border-radius:50%;text-align:center;position:relative;cursor:pointer;font-size:12px;height:16px;width:16px;line-height:16px;vertical-align:middle;top:-1px;right:-5px}.el-tag .el-icon-close:before{display:block}.el-tag--dark{background-color:#409eff;color:#fff}.el-tag--dark,.el-tag--dark.is-hit{border-color:#409eff}.el-tag--dark .el-tag__close{color:#fff}.el-tag--dark .el-tag__close:hover{color:#fff;background-color:#66b1ff}.el-tag--dark.el-tag--info{background-color:#909399;border-color:#909399;color:#fff}.el-tag--dark.el-tag--info.is-hit{border-color:#909399}.el-tag--dark.el-tag--info .el-tag__close{color:#fff}.el-tag--dark.el-tag--info .el-tag__close:hover{color:#fff;background-color:#a6a9ad}.el-tag--dark.el-tag--success{background-color:#67c23a;border-color:#67c23a;color:#fff}.el-tag--dark.el-tag--success.is-hit{border-color:#67c23a}.el-tag--dark.el-tag--success .el-tag__close{color:#fff}.el-tag--dark.el-tag--success .el-tag__close:hover{color:#fff;background-color:#85ce61}.el-tag--dark.el-tag--warning{background-color:#e6a23c;border-color:#e6a23c;color:#fff}.el-tag--dark.el-tag--warning.is-hit{border-color:#e6a23c}.el-tag--dark.el-tag--warning .el-tag__close{color:#fff}.el-tag--dark.el-tag--warning .el-tag__close:hover{color:#fff;background-color:#ebb563}.el-tag--dark.el-tag--danger{background-color:#f56c6c;border-color:#f56c6c;color:#fff}.el-tag--dark.el-tag--danger.is-hit{border-color:#f56c6c}.el-tag--dark.el-tag--danger .el-tag__close{color:#fff}.el-tag--dark.el-tag--danger .el-tag__close:hover{color:#fff;background-color:#f78989}.el-tag--plain{background-color:#fff;border-color:#b3d8ff;color:#409eff}.el-tag--plain.is-hit{border-color:#409eff}.el-tag--plain .el-tag__close{color:#409eff}.el-tag--plain .el-tag__close:hover{color:#fff;background-color:#409eff}.el-tag--plain.el-tag--info{background-color:#fff;border-color:#d3d4d6;color:#909399}.el-tag--plain.el-tag--info.is-hit{border-color:#909399}.el-tag--plain.el-tag--info .el-tag__close{color:#909399}.el-tag--plain.el-tag--info .el-tag__close:hover{color:#fff;background-color:#909399}.el-tag--plain.el-tag--success{background-color:#fff;border-color:#c2e7b0;color:#67c23a}.el-tag--plain.el-tag--success.is-hit{border-color:#67c23a}.el-tag--plain.el-tag--success .el-tag__close{color:#67c23a}.el-tag--plain.el-tag--success .el-tag__close:hover{color:#fff;background-color:#67c23a}.el-tag--plain.el-tag--warning{background-color:#fff;border-color:#f5dab1;color:#e6a23c}.el-tag--plain.el-tag--warning.is-hit{border-color:#e6a23c}.el-tag--plain.el-tag--warning .el-tag__close{color:#e6a23c}.el-tag--plain.el-tag--warning .el-tag__close:hover{color:#fff;background-color:#e6a23c}.el-tag--plain.el-tag--danger{background-color:#fff;border-color:#fbc4c4;color:#f56c6c}.el-tag--plain.el-tag--danger.is-hit{border-color:#f56c6c}.el-tag--plain.el-tag--danger .el-tag__close{color:#f56c6c}.el-tag--plain.el-tag--danger .el-tag__close:hover{color:#fff;background-color:#f56c6c}.el-tag--medium{height:28px;line-height:26px}.el-tag--medium .el-icon-close{-webkit-transform:scale(.8);transform:scale(.8)}.el-tag--small{height:24px;padding:0 8px;line-height:22px}.el-tag--small .el-icon-close{-webkit-transform:scale(.8);transform:scale(.8)}.el-tag--mini{height:20px;padding:0 5px;line-height:19px}.el-tag--mini .el-icon-close{margin-left:-3px;-webkit-transform:scale(.7);transform:scale(.7)}.el-cascader{display:inline-block;position:relative;font-size:14px;line-height:40px}.el-cascader:not(.is-disabled):hover .el-input__inner{cursor:pointer;border-color:#c0c4cc}.el-cascader .el-input .el-input__inner:focus,.el-cascader .el-input.is-focus .el-input__inner{border-color:#409eff}.el-cascader .el-input{cursor:pointer}.el-cascader .el-input .el-input__inner{text-overflow:ellipsis}.el-cascader .el-input .el-icon-arrow-down{-webkit-transition:-webkit-transform .3s;transition:-webkit-transform .3s;transition:transform .3s;transition:transform .3s,-webkit-transform .3s;font-size:14px}.el-cascader .el-input .el-icon-arrow-down.is-reverse{-webkit-transform:rotate(180deg);transform:rotate(180deg)}.el-cascader .el-input .el-icon-circle-close:hover{color:#909399}.el-cascader--medium{font-size:14px;line-height:36px}.el-cascader--small{font-size:13px;line-height:32px}.el-cascader--mini{font-size:12px;line-height:28px}.el-cascader.is-disabled .el-cascader__label{z-index:2;color:#c0c4cc}.el-cascader__dropdown{margin:5px 0;font-size:14px;background:#fff;border:1px solid #e4e7ed;border-radius:4px;-webkit-box-shadow:0 2px 12px 0 rgba(0,0,0,.1);box-shadow:0 2px 12px 0 rgba(0,0,0,.1)}.el-cascader__tags{position:absolute;left:0;right:30px;top:50%;-webkit-transform:translateY(-50%);transform:translateY(-50%);display:-webkit-box;display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;line-height:normal;text-align:left;-webkit-box-sizing:border-box;box-sizing:border-box}.el-cascader__tags .el-tag{display:-webkit-inline-box;display:-ms-inline-flexbox;display:inline-flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;max-width:100%;margin:2px 0 2px 6px;text-overflow:ellipsis;background:#f0f2f5}.el-cascader__tags .el-tag:not(.is-hit){border-color:transparent}.el-cascader__tags .el-tag>span{-webkit-box-flex:1;-ms-flex:1;flex:1;overflow:hidden;text-overflow:ellipsis}.el-cascader__tags .el-tag .el-icon-close{-webkit-box-flex:0;-ms-flex:none;flex:none;background-color:#c0c4cc;color:#fff}.el-cascader__tags .el-tag .el-icon-close:hover{background-color:#909399}.el-cascader__suggestion-panel{border-radius:4px}.el-cascader__suggestion-list{max-height:204px;margin:0;padding:6px 0;font-size:14px;color:#606266;text-align:center}.el-cascader__suggestion-item{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between;-webkit-box-align:center;-ms-flex-align:center;align-items:center;height:34px;padding:0 15px;text-align:left;outline:0;cursor:pointer}.el-cascader__suggestion-item:focus,.el-cascader__suggestion-item:hover{background:#f5f7fa}.el-cascader__suggestion-item.is-checked{color:#409eff;font-weight:700}.el-cascader__suggestion-item>span{margin-right:10px}.el-cascader__empty-text{margin:10px 0;color:#c0c4cc}.el-cascader__search-input{-webkit-box-flex:1;-ms-flex:1;flex:1;height:24px;min-width:60px;margin:2px 0 2px 15px;padding:0;color:#606266;border:none;outline:0;-webkit-box-sizing:border-box;box-sizing:border-box}.el-cascader__search-input::-webkit-input-placeholder{color:#c0c4cc}.el-cascader__search-input:-ms-input-placeholder{color:#c0c4cc}.el-cascader__search-input::-ms-input-placeholder{color:#c0c4cc}.el-cascader__search-input::-moz-placeholder{color:#c0c4cc}.el-cascader__search-input::placeholder{color:#c0c4cc}.el-color-predefine{font-size:12px;margin-top:8px;width:280px}.el-color-predefine,.el-color-predefine__colors{display:-webkit-box;display:-ms-flexbox;display:flex}.el-color-predefine__colors{-webkit-box-flex:1;-ms-flex:1;flex:1;-ms-flex-wrap:wrap;flex-wrap:wrap}.el-color-predefine__color-selector{margin:0 0 8px 8px;width:20px;height:20px;border-radius:4px;cursor:pointer}.el-color-predefine__color-selector:nth-child(10n+1){margin-left:0}.el-color-predefine__color-selector.selected{-webkit-box-shadow:0 0 3px 2px #409eff;box-shadow:0 0 3px 2px #409eff}.el-color-predefine__color-selector>div{display:-webkit-box;display:-ms-flexbox;display:flex;height:100%;border-radius:3px}.el-color-predefine__color-selector.is-alpha{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAMCAIAAADZF8uwAAAAGUlEQVQYV2M4gwH+YwCGIasIUwhT25BVBADtzYNYrHvv4gAAAABJRU5ErkJggg==)}.el-color-hue-slider{position:relative;-webkit-box-sizing:border-box;box-sizing:border-box;width:280px;height:12px;background-color:red;padding:0 2px}.el-color-hue-slider__bar{position:relative;background:-webkit-gradient(linear,left top,right top,color-stop(0,red),color-stop(17%,#ff0),color-stop(33%,#0f0),color-stop(50%,#0ff),color-stop(67%,#00f),color-stop(83%,#f0f),to(red));background:linear-gradient(90deg,red 0,#ff0 17%,#0f0 33%,#0ff 50%,#00f 67%,#f0f 83%,red);height:100%}.el-color-hue-slider__thumb{position:absolute;cursor:pointer;-webkit-box-sizing:border-box;box-sizing:border-box;left:0;top:0;width:4px;height:100%;border-radius:1px;background:#fff;border:1px solid #f0f0f0;-webkit-box-shadow:0 0 2px rgba(0,0,0,.6);box-shadow:0 0 2px rgba(0,0,0,.6);z-index:1}.el-color-hue-slider.is-vertical{width:12px;height:180px;padding:2px 0}.el-color-hue-slider.is-vertical .el-color-hue-slider__bar{background:-webkit-gradient(linear,left top,left bottom,color-stop(0,red),color-stop(17%,#ff0),color-stop(33%,#0f0),color-stop(50%,#0ff),color-stop(67%,#00f),color-stop(83%,#f0f),to(red));background:linear-gradient(180deg,red 0,#ff0 17%,#0f0 33%,#0ff 50%,#00f 67%,#f0f 83%,red)}.el-color-hue-slider.is-vertical .el-color-hue-slider__thumb{left:0;top:0;width:100%;height:4px}.el-color-svpanel{position:relative;width:280px;height:180px}.el-color-svpanel__black,.el-color-svpanel__white{position:absolute;top:0;left:0;right:0;bottom:0}.el-color-svpanel__white{background:-webkit-gradient(linear,left top,right top,from(#fff),to(hsla(0,0%,100%,0)));background:linear-gradient(90deg,#fff,hsla(0,0%,100%,0))}.el-color-svpanel__black{background:-webkit-gradient(linear,left bottom,left top,from(#000),to(transparent));background:linear-gradient(0deg,#000,transparent)}.el-color-svpanel__cursor{position:absolute}.el-color-svpanel__cursor>div{cursor:head;width:4px;height:4px;-webkit-box-shadow:0 0 0 1.5px #fff,inset 0 0 1px 1px rgba(0,0,0,.3),0 0 1px 2px rgba(0,0,0,.4);box-shadow:0 0 0 1.5px #fff,inset 0 0 1px 1px rgba(0,0,0,.3),0 0 1px 2px rgba(0,0,0,.4);border-radius:50%;-webkit-transform:translate(-2px,-2px);transform:translate(-2px,-2px)}.el-color-alpha-slider{position:relative;-webkit-box-sizing:border-box;box-sizing:border-box;width:280px;height:12px;background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAMCAIAAADZF8uwAAAAGUlEQVQYV2M4gwH+YwCGIasIUwhT25BVBADtzYNYrHvv4gAAAABJRU5ErkJggg==)}.el-color-alpha-slider__bar{position:relative;background:-webkit-gradient(linear,left top,right top,color-stop(0,hsla(0,0%,100%,0)),to(#fff));background:linear-gradient(90deg,hsla(0,0%,100%,0) 0,#fff);height:100%}.el-color-alpha-slider__thumb{position:absolute;cursor:pointer;-webkit-box-sizing:border-box;box-sizing:border-box;left:0;top:0;width:4px;height:100%;border-radius:1px;background:#fff;border:1px solid #f0f0f0;-webkit-box-shadow:0 0 2px rgba(0,0,0,.6);box-shadow:0 0 2px rgba(0,0,0,.6);z-index:1}.el-color-alpha-slider.is-vertical{width:20px;height:180px}.el-color-alpha-slider.is-vertical .el-color-alpha-slider__bar{background:-webkit-gradient(linear,left top,left bottom,color-stop(0,hsla(0,0%,100%,0)),to(#fff));background:linear-gradient(180deg,hsla(0,0%,100%,0) 0,#fff)}.el-color-alpha-slider.is-vertical .el-color-alpha-slider__thumb{left:0;top:0;width:100%;height:4px}.el-color-dropdown{width:300px}.el-color-dropdown__main-wrapper{margin-bottom:6px}.el-color-dropdown__main-wrapper:after{content:"";display:table;clear:both}.el-color-dropdown__btns{margin-top:6px;text-align:right}.el-color-dropdown__value{float:left;line-height:26px;font-size:12px;color:#000;width:160px}.el-color-dropdown__btn{border:1px solid #dcdcdc;color:#333;line-height:24px;border-radius:2px;padding:0 20px;cursor:pointer;background-color:transparent;outline:0;font-size:12px}.el-color-dropdown__btn[disabled]{color:#ccc;cursor:not-allowed}.el-color-dropdown__btn:hover{color:#409eff;border-color:#409eff}.el-color-dropdown__link-btn{cursor:pointer;color:#409eff;text-decoration:none;padding:15px;font-size:12px}.el-color-dropdown__link-btn:hover{color:tint(#409eff,20%)}.el-color-picker{display:inline-block;position:relative;line-height:normal;height:40px}.el-color-picker.is-disabled .el-color-picker__trigger{cursor:not-allowed}.el-color-picker--medium{height:36px}.el-color-picker--medium .el-color-picker__trigger{height:36px;width:36px}.el-color-picker--medium .el-color-picker__mask{height:34px;width:34px}.el-color-picker--small{height:32px}.el-color-picker--small .el-color-picker__trigger{height:32px;width:32px}.el-color-picker--small .el-color-picker__mask{height:30px;width:30px}.el-color-picker--small .el-color-picker__empty,.el-color-picker--small .el-color-picker__icon{-webkit-transform:translate3d(-50%,-50%,0) scale(.8);transform:translate3d(-50%,-50%,0) scale(.8)}.el-color-picker--mini{height:28px}.el-color-picker--mini .el-color-picker__trigger{height:28px;width:28px}.el-color-picker--mini .el-color-picker__mask{height:26px;width:26px}.el-color-picker--mini .el-color-picker__empty,.el-color-picker--mini .el-color-picker__icon{-webkit-transform:translate3d(-50%,-50%,0) scale(.8);transform:translate3d(-50%,-50%,0) scale(.8)}.el-color-picker__mask{height:38px;width:38px;border-radius:4px;position:absolute;top:1px;left:1px;z-index:1;cursor:not-allowed;background-color:hsla(0,0%,100%,.7)}.el-color-picker__trigger{display:inline-block;height:40px;width:40px;padding:4px;border:1px solid #e6e6e6;border-radius:4px;font-size:0;cursor:pointer}.el-color-picker__color,.el-color-picker__trigger{-webkit-box-sizing:border-box;box-sizing:border-box;position:relative}.el-color-picker__color{display:block;border:1px solid #999;border-radius:2px;width:100%;height:100%;text-align:center}.el-color-picker__color.is-alpha{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAMCAIAAADZF8uwAAAAGUlEQVQYV2M4gwH+YwCGIasIUwhT25BVBADtzYNYrHvv4gAAAABJRU5ErkJggg==)}.el-color-picker__color-inner{position:absolute;left:0;top:0;right:0;bottom:0}.el-color-picker__empty,.el-color-picker__icon{top:50%;left:50%;font-size:12px;position:absolute}.el-color-picker__empty{color:#999}.el-color-picker__empty,.el-color-picker__icon{-webkit-transform:translate3d(-50%,-50%,0);transform:translate3d(-50%,-50%,0)}.el-color-picker__icon{display:inline-block;width:100%;color:#fff;text-align:center}.el-color-picker__panel{position:absolute;z-index:10;padding:6px;-webkit-box-sizing:content-box;box-sizing:content-box;background-color:#fff;border:1px solid #ebeef5;border-radius:4px;-webkit-box-shadow:0 2px 12px 0 rgba(0,0,0,.1);box-shadow:0 2px 12px 0 rgba(0,0,0,.1)}.el-textarea{position:relative;display:inline-block;width:100%;vertical-align:bottom;font-size:14px}.el-textarea__inner{display:block;resize:vertical;padding:5px 15px;line-height:1.5;-webkit-box-sizing:border-box;box-sizing:border-box;width:100%;font-size:inherit;color:#606266;background-color:#fff;background-image:none;border:1px solid #dcdfe6;border-radius:4px;-webkit-transition:border-color .2s cubic-bezier(.645,.045,.355,1);transition:border-color .2s cubic-bezier(.645,.045,.355,1)}.el-textarea__inner::-webkit-input-placeholder{color:#c0c4cc}.el-textarea__inner:-ms-input-placeholder{color:#c0c4cc}.el-textarea__inner::-ms-input-placeholder{color:#c0c4cc}.el-textarea__inner::-moz-placeholder{color:#c0c4cc}.el-textarea__inner::placeholder{color:#c0c4cc}.el-textarea__inner:hover{border-color:#c0c4cc}.el-textarea__inner:focus{outline:0;border-color:#409eff}.el-textarea .el-input__count{color:#909399;background:#fff;position:absolute;font-size:12px;bottom:5px;right:10px}.el-textarea.is-disabled .el-textarea__inner{background-color:#f5f7fa;border-color:#e4e7ed;color:#c0c4cc;cursor:not-allowed}.el-textarea.is-disabled .el-textarea__inner::-webkit-input-placeholder{color:#c0c4cc}.el-textarea.is-disabled .el-textarea__inner:-ms-input-placeholder{color:#c0c4cc}.el-textarea.is-disabled .el-textarea__inner::-ms-input-placeholder{color:#c0c4cc}.el-textarea.is-disabled .el-textarea__inner::-moz-placeholder{color:#c0c4cc}.el-textarea.is-disabled .el-textarea__inner::placeholder{color:#c0c4cc}.el-textarea.is-exceed .el-textarea__inner{border-color:#f56c6c}.el-textarea.is-exceed .el-input__count{color:#f56c6c}.el-input{position:relative;font-size:14px;display:inline-block;width:100%}.el-input::-webkit-scrollbar{z-index:11;width:6px}.el-input::-webkit-scrollbar:horizontal{height:6px}.el-input::-webkit-scrollbar-thumb{border-radius:5px;width:6px;background:#b4bccc}.el-input::-webkit-scrollbar-corner,.el-input::-webkit-scrollbar-track{background:#fff}.el-input::-webkit-scrollbar-track-piece{background:#fff;width:6px}.el-input .el-input__clear{color:#c0c4cc;font-size:14px;cursor:pointer;-webkit-transition:color .2s cubic-bezier(.645,.045,.355,1);transition:color .2s cubic-bezier(.645,.045,.355,1)}.el-input .el-input__clear:hover{color:#909399}.el-input .el-input__count{height:100%;display:-webkit-inline-box;display:-ms-inline-flexbox;display:inline-flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;color:#909399;font-size:12px}.el-input .el-input__count .el-input__count-inner{background:#fff;line-height:normal;display:inline-block;padding:0 5px}.el-input__inner{-webkit-appearance:none;background-color:#fff;background-image:none;border-radius:4px;border:1px solid #dcdfe6;-webkit-box-sizing:border-box;box-sizing:border-box;color:#606266;display:inline-block;font-size:inherit;height:40px;line-height:40px;outline:0;padding:0 15px;-webkit-transition:border-color .2s cubic-bezier(.645,.045,.355,1);transition:border-color .2s cubic-bezier(.645,.045,.355,1);width:100%}.el-input__prefix,.el-input__suffix{position:absolute;top:0;-webkit-transition:all .3s;height:100%;color:#c0c4cc;text-align:center}.el-input__inner::-webkit-input-placeholder{color:#c0c4cc}.el-input__inner:-ms-input-placeholder{color:#c0c4cc}.el-input__inner::-ms-input-placeholder{color:#c0c4cc}.el-input__inner::-moz-placeholder{color:#c0c4cc}.el-input__inner::placeholder{color:#c0c4cc}.el-input__inner:hover{border-color:#c0c4cc}.el-input.is-active .el-input__inner,.el-input__inner:focus{border-color:#409eff;outline:0}.el-input__suffix{right:5px;-webkit-transition:all .3s;transition:all .3s}.el-input__suffix-inner{pointer-events:all}.el-input__prefix{left:5px}.el-input__icon,.el-input__prefix{-webkit-transition:all .3s;transition:all .3s}.el-input__icon{height:100%;width:25px;text-align:center;line-height:40px}.el-input__icon:after{content:"";height:100%;width:0;display:inline-block;vertical-align:middle}.el-input__validateIcon{pointer-events:none}.el-input.is-disabled .el-input__inner{background-color:#f5f7fa;border-color:#e4e7ed;color:#c0c4cc;cursor:not-allowed}.el-input.is-disabled .el-input__inner::-webkit-input-placeholder{color:#c0c4cc}.el-input.is-disabled .el-input__inner:-ms-input-placeholder{color:#c0c4cc}.el-input.is-disabled .el-input__inner::-ms-input-placeholder{color:#c0c4cc}.el-input.is-disabled .el-input__inner::-moz-placeholder{color:#c0c4cc}.el-input.is-disabled .el-input__inner::placeholder{color:#c0c4cc}.el-input.is-disabled .el-input__icon{cursor:not-allowed}.el-link,.el-transfer-panel__filter .el-icon-circle-close{cursor:pointer}.el-input.is-exceed .el-input__inner{border-color:#f56c6c}.el-input.is-exceed .el-input__suffix .el-input__count{color:#f56c6c}.el-input--suffix .el-input__inner{padding-right:30px}.el-input--prefix .el-input__inner{padding-left:30px}.el-input--medium{font-size:14px}.el-input--medium .el-input__inner{height:36px;line-height:36px}.el-input--medium .el-input__icon{line-height:36px}.el-input--small{font-size:13px}.el-input--small .el-input__inner{height:32px;line-height:32px}.el-input--small .el-input__icon{line-height:32px}.el-input--mini{font-size:12px}.el-input--mini .el-input__inner{height:28px;line-height:28px}.el-input--mini .el-input__icon{line-height:28px}.el-input-group{line-height:normal;display:inline-table;width:100%;border-collapse:separate;border-spacing:0}.el-input-group>.el-input__inner{vertical-align:middle;display:table-cell}.el-input-group__append,.el-input-group__prepend{background-color:#f5f7fa;color:#909399;vertical-align:middle;display:table-cell;position:relative;border:1px solid #dcdfe6;border-radius:4px;padding:0 20px;width:1px;white-space:nowrap}.el-input-group--prepend .el-input__inner,.el-input-group__append{border-top-left-radius:0;border-bottom-left-radius:0}.el-input-group--append .el-input__inner,.el-input-group__prepend{border-top-right-radius:0;border-bottom-right-radius:0}.el-input-group__append:focus,.el-input-group__prepend:focus{outline:0}.el-input-group__append .el-button,.el-input-group__append .el-select,.el-input-group__prepend .el-button,.el-input-group__prepend .el-select{display:inline-block;margin:-10px -20px}.el-input-group__append button.el-button,.el-input-group__append div.el-select .el-input__inner,.el-input-group__append div.el-select:hover .el-input__inner,.el-input-group__prepend button.el-button,.el-input-group__prepend div.el-select .el-input__inner,.el-input-group__prepend div.el-select:hover .el-input__inner{border-color:transparent;background-color:transparent;color:inherit;border-top:0;border-bottom:0}.el-input-group__append .el-button,.el-input-group__append .el-input,.el-input-group__prepend .el-button,.el-input-group__prepend .el-input{font-size:inherit}.el-input-group__prepend{border-right:0}.el-input-group__append{border-left:0}.el-input-group--append .el-select .el-input.is-focus .el-input__inner,.el-input-group--prepend .el-select .el-input.is-focus .el-input__inner{border-color:transparent}.el-input__inner::-ms-clear{display:none;width:0;height:0}.el-transfer{font-size:14px}.el-transfer__buttons{display:inline-block;vertical-align:middle;padding:0 30px}.el-transfer__button{display:block;margin:0 auto;padding:10px;border-radius:50%;color:#fff;background-color:#409eff;font-size:0}.el-transfer__button.is-with-texts{border-radius:4px}.el-transfer__button.is-disabled,.el-transfer__button.is-disabled:hover{border:1px solid #dcdfe6;background-color:#f5f7fa;color:#c0c4cc}.el-transfer__button:first-child{margin-bottom:10px}.el-transfer__button:nth-child(2){margin:0}.el-transfer__button i,.el-transfer__button span{font-size:14px}.el-transfer__button [class*=el-icon-]+span{margin-left:0}.el-transfer-panel{border:1px solid #ebeef5;border-radius:4px;overflow:hidden;background:#fff;display:inline-block;vertical-align:middle;width:200px;max-height:100%;-webkit-box-sizing:border-box;box-sizing:border-box;position:relative}.el-transfer-panel__body{height:246px}.el-transfer-panel__body.is-with-footer{padding-bottom:40px}.el-transfer-panel__list{margin:0;padding:6px 0;list-style:none;height:246px;overflow:auto;-webkit-box-sizing:border-box;box-sizing:border-box}.el-transfer-panel__list.is-filterable{height:194px;padding-top:0}.el-transfer-panel__item{height:30px;line-height:30px;padding-left:15px;display:block}.el-transfer-panel__item+.el-transfer-panel__item{margin-left:0;display:block!important}.el-transfer-panel__item.el-checkbox{color:#606266}.el-transfer-panel__item:hover{color:#409eff}.el-transfer-panel__item.el-checkbox .el-checkbox__label{width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;display:block;-webkit-box-sizing:border-box;box-sizing:border-box;padding-left:24px;line-height:30px}.el-transfer-panel__item .el-checkbox__input{position:absolute;top:8px}.el-transfer-panel__filter{text-align:center;margin:15px;-webkit-box-sizing:border-box;box-sizing:border-box;display:block;width:auto}.el-transfer-panel__filter .el-input__inner{height:32px;width:100%;font-size:12px;display:inline-block;-webkit-box-sizing:border-box;box-sizing:border-box;border-radius:16px;padding-right:10px;padding-left:30px}.el-transfer-panel__filter .el-input__icon{margin-left:5px}.el-transfer-panel .el-transfer-panel__header{height:40px;line-height:40px;background:#f5f7fa;margin:0;padding-left:15px;border-bottom:1px solid #ebeef5;-webkit-box-sizing:border-box;box-sizing:border-box;color:#000}.el-transfer-panel .el-transfer-panel__header .el-checkbox{display:block;line-height:40px}.el-transfer-panel .el-transfer-panel__header .el-checkbox .el-checkbox__label{font-size:16px;color:#303133;font-weight:400}.el-transfer-panel .el-transfer-panel__header .el-checkbox .el-checkbox__label span{position:absolute;right:15px;color:#909399;font-size:12px;font-weight:400}.el-divider__text,.el-link{font-weight:500;font-size:14px}.el-transfer-panel .el-transfer-panel__footer{height:40px;background:#fff;margin:0;padding:0;border-top:1px solid #ebeef5;position:absolute;bottom:0;left:0;width:100%;z-index:1}.el-transfer-panel .el-transfer-panel__footer:after{display:inline-block;content:"";height:100%;vertical-align:middle}.el-container,.el-timeline-item__node{display:-webkit-box;display:-ms-flexbox}.el-transfer-panel .el-transfer-panel__footer .el-checkbox{padding-left:20px;color:#606266}.el-transfer-panel .el-transfer-panel__empty{margin:0;height:30px;line-height:30px;padding:6px 15px 0;color:#909399;text-align:center}.el-transfer-panel .el-checkbox__label{padding-left:8px}.el-transfer-panel .el-checkbox__inner{height:14px;width:14px;border-radius:3px}.el-transfer-panel .el-checkbox__inner:after{height:6px;width:3px;left:4px}.el-container{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-direction:row;flex-direction:row;-webkit-box-flex:1;-ms-flex:1;flex:1;-ms-flex-preferred-size:auto;flex-basis:auto;-webkit-box-sizing:border-box;box-sizing:border-box;min-width:0}.el-container.is-vertical,.el-drawer{-webkit-box-orient:vertical;-webkit-box-direction:normal}.el-aside,.el-header{-webkit-box-sizing:border-box}.el-container.is-vertical{-ms-flex-direction:column;-webkit-box-orient:vertical;-webkit-box-direction:normal;flex-direction:column}.el-header{padding:0 20px}.el-aside,.el-header{-webkit-box-sizing:border-box;box-sizing:border-box;-ms-flex-negative:0;flex-shrink:0}.el-aside{overflow:auto}.el-footer,.el-main{-webkit-box-sizing:border-box}.el-main{display:block;-webkit-box-flex:1;-ms-flex:1;flex:1;-ms-flex-preferred-size:auto;flex-basis:auto;overflow:auto;padding:20px}.el-footer,.el-main{-webkit-box-sizing:border-box;box-sizing:border-box}.el-footer{padding:0 20px;-ms-flex-negative:0;flex-shrink:0}.el-timeline{margin:0;font-size:14px;list-style:none}.el-timeline .el-timeline-item:last-child .el-timeline-item__tail{display:none}.el-timeline-item{position:relative;padding-bottom:20px}.el-timeline-item__wrapper{position:relative;padding-left:28px;top:-3px}.el-timeline-item__tail{position:absolute;left:4px;height:100%;border-left:2px solid #e4e7ed}.el-timeline-item__icon{color:#fff;font-size:13px}.el-timeline-item__node{position:absolute;background-color:#e4e7ed;border-radius:50%;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;-webkit-box-align:center;-ms-flex-align:center;align-items:center}.el-image__error,.el-timeline-item__dot{display:-webkit-box;display:-ms-flexbox}.el-timeline-item__node--normal{left:-1px;width:12px;height:12px}.el-timeline-item__node--large{left:-2px;width:14px;height:14px}.el-timeline-item__node--primary{background-color:#409eff}.el-timeline-item__node--success{background-color:#67c23a}.el-timeline-item__node--warning{background-color:#e6a23c}.el-timeline-item__node--danger{background-color:#f56c6c}.el-timeline-item__node--info{background-color:#909399}.el-timeline-item__dot{position:absolute;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;-webkit-box-align:center;-ms-flex-align:center;align-items:center}.el-timeline-item__content{color:#303133}.el-timeline-item__timestamp{color:#909399;line-height:1;font-size:13px}.el-timeline-item__timestamp.is-top{margin-bottom:8px;padding-top:4px}.el-timeline-item__timestamp.is-bottom{margin-top:8px}.el-link{display:-webkit-inline-box;display:-ms-inline-flexbox;display:inline-flex;-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-direction:row;flex-direction:row;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;vertical-align:middle;position:relative;text-decoration:none;outline:0;padding:0}.el-link.is-underline:hover:after{content:"";position:absolute;left:0;right:0;height:0;bottom:0;border-bottom:1px solid #409eff}.el-link.el-link--default:after,.el-link.el-link--primary.is-underline:hover:after,.el-link.el-link--primary:after{border-color:#409eff}.el-link.is-disabled{cursor:not-allowed}.el-link [class*=el-icon-]+span{margin-left:5px}.el-link.el-link--default{color:#606266}.el-link.el-link--default:hover{color:#409eff}.el-link.el-link--default.is-disabled{color:#c0c4cc}.el-link.el-link--primary{color:#409eff}.el-link.el-link--primary:hover{color:#66b1ff}.el-link.el-link--primary.is-disabled{color:#a0cfff}.el-link.el-link--danger.is-underline:hover:after,.el-link.el-link--danger:after{border-color:#f56c6c}.el-link.el-link--danger{color:#f56c6c}.el-link.el-link--danger:hover{color:#f78989}.el-link.el-link--danger.is-disabled{color:#fab6b6}.el-link.el-link--success.is-underline:hover:after,.el-link.el-link--success:after{border-color:#67c23a}.el-link.el-link--success{color:#67c23a}.el-link.el-link--success:hover{color:#85ce61}.el-link.el-link--success.is-disabled{color:#b3e19d}.el-link.el-link--warning.is-underline:hover:after,.el-link.el-link--warning:after{border-color:#e6a23c}.el-link.el-link--warning{color:#e6a23c}.el-link.el-link--warning:hover{color:#ebb563}.el-link.el-link--warning.is-disabled{color:#f3d19e}.el-link.el-link--info.is-underline:hover:after,.el-link.el-link--info:after{border-color:#909399}.el-link.el-link--info{color:#909399}.el-link.el-link--info:hover{color:#a6a9ad}.el-link.el-link--info.is-disabled{color:#c8c9cc}.el-divider{background-color:#dcdfe6;position:relative}.el-divider--horizontal{display:block;height:1px;width:100%;margin:24px 0}.el-divider--vertical{display:inline-block;width:1px;height:1em;margin:0 8px;vertical-align:middle;position:relative}.el-divider__text{position:absolute;background-color:#fff;padding:0 20px;color:#303133}.el-image__error,.el-image__placeholder{background:#f5f7fa}.el-divider__text.is-left{left:20px;-webkit-transform:translateY(-50%);transform:translateY(-50%)}.el-divider__text.is-center{left:50%;-webkit-transform:translateX(-50%) translateY(-50%);transform:translateX(-50%) translateY(-50%)}.el-divider__text.is-right{right:20px;-webkit-transform:translateY(-50%);transform:translateY(-50%)}.el-image__error,.el-image__inner,.el-image__placeholder{width:100%;height:100%}.el-image{position:relative;display:inline-block;overflow:hidden}.el-image__inner{vertical-align:top}.el-image__inner--center{position:relative;top:50%;left:50%;-webkit-transform:translate(-50%,-50%);transform:translate(-50%,-50%);display:block}.el-image__error{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;-webkit-box-align:center;-ms-flex-align:center;align-items:center;font-size:14px;color:#c0c4cc;vertical-align:middle}.el-image__preview{cursor:pointer}.el-image-viewer__wrapper{position:fixed;top:0;right:0;bottom:0;left:0}.el-image-viewer__btn{position:absolute;z-index:1;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;border-radius:50%;opacity:.8;cursor:pointer;-webkit-box-sizing:border-box;box-sizing:border-box;user-select:none}.el-button,.el-checkbox,.el-image-viewer__btn{-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none}.el-image-viewer__close{top:40px;right:40px;width:40px;height:40px;font-size:40px}.el-image-viewer__canvas{width:100%;height:100%;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;-webkit-box-align:center;-ms-flex-align:center;align-items:center}.el-image-viewer__actions{left:50%;bottom:30px;-webkit-transform:translateX(-50%);transform:translateX(-50%);width:282px;height:44px;padding:0 23px;background-color:#606266;border-color:#fff;border-radius:22px}.el-image-viewer__actions__inner{width:100%;height:100%;text-align:justify;cursor:default;font-size:23px;color:#fff;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-ms-flex-pack:distribute;justify-content:space-around}.el-image-viewer__next,.el-image-viewer__prev{top:50%;width:44px;height:44px;font-size:24px;color:#fff;background-color:#606266;border-color:#fff}.el-image-viewer__prev{left:40px}.el-image-viewer__next,.el-image-viewer__prev{-webkit-transform:translateY(-50%);transform:translateY(-50%)}.el-image-viewer__next{right:40px;text-indent:2px}.el-image-viewer__mask{position:absolute;width:100%;height:100%;top:0;left:0;opacity:.5;background:#000}.viewer-fade-enter-active{-webkit-animation:viewer-fade-in .3s;animation:viewer-fade-in .3s}.viewer-fade-leave-active{-webkit-animation:viewer-fade-out .3s;animation:viewer-fade-out .3s}@-webkit-keyframes viewer-fade-in{0%{-webkit-transform:translate3d(0,-20px,0);transform:translate3d(0,-20px,0);opacity:0}to{-webkit-transform:translateZ(0);transform:translateZ(0);opacity:1}}@keyframes viewer-fade-in{0%{-webkit-transform:translate3d(0,-20px,0);transform:translate3d(0,-20px,0);opacity:0}to{-webkit-transform:translateZ(0);transform:translateZ(0);opacity:1}}@-webkit-keyframes viewer-fade-out{0%{-webkit-transform:translateZ(0);transform:translateZ(0);opacity:1}to{-webkit-transform:translate3d(0,-20px,0);transform:translate3d(0,-20px,0);opacity:0}}@keyframes viewer-fade-out{0%{-webkit-transform:translateZ(0);transform:translateZ(0);opacity:1}to{-webkit-transform:translate3d(0,-20px,0);transform:translate3d(0,-20px,0);opacity:0}}.el-button{display:inline-block;line-height:1;white-space:nowrap;cursor:pointer;background:#fff;border:1px solid #dcdfe6;color:#606266;-webkit-appearance:none;text-align:center;-webkit-box-sizing:border-box;box-sizing:border-box;outline:0;margin:0;-webkit-transition:.1s;transition:.1s;font-weight:500;padding:12px 20px;font-size:14px;border-radius:4px}.el-button+.el-button{margin-left:10px}.el-button:focus,.el-button:hover{color:#409eff;border-color:#c6e2ff;background-color:#ecf5ff}.el-button:active{color:#3a8ee6;border-color:#3a8ee6;outline:0}.el-button::-moz-focus-inner{border:0}.el-button [class*=el-icon-]+span{margin-left:5px}.el-button.is-plain:focus,.el-button.is-plain:hover{background:#fff;border-color:#409eff;color:#409eff}.el-button.is-active,.el-button.is-plain:active{color:#3a8ee6;border-color:#3a8ee6}.el-button.is-plain:active{background:#fff;outline:0}.el-button.is-disabled,.el-button.is-disabled:focus,.el-button.is-disabled:hover{color:#c0c4cc;cursor:not-allowed;background-image:none;background-color:#fff;border-color:#ebeef5}.el-button.is-disabled.el-button--text{background-color:transparent}.el-button.is-disabled.is-plain,.el-button.is-disabled.is-plain:focus,.el-button.is-disabled.is-plain:hover{background-color:#fff;border-color:#ebeef5;color:#c0c4cc}.el-button.is-loading{position:relative;pointer-events:none}.el-button.is-loading:before{pointer-events:none;content:"";position:absolute;left:-1px;top:-1px;right:-1px;bottom:-1px;border-radius:inherit;background-color:hsla(0,0%,100%,.35)}.el-button.is-round{border-radius:20px;padding:12px 23px}.el-button.is-circle{border-radius:50%;padding:12px}.el-button--primary{color:#fff;background-color:#409eff;border-color:#409eff}.el-button--primary:focus,.el-button--primary:hover{background:#66b1ff;border-color:#66b1ff;color:#fff}.el-button--primary.is-active,.el-button--primary:active{background:#3a8ee6;border-color:#3a8ee6;color:#fff}.el-button--primary:active{outline:0}.el-button--primary.is-disabled,.el-button--primary.is-disabled:active,.el-button--primary.is-disabled:focus,.el-button--primary.is-disabled:hover{color:#fff;background-color:#a0cfff;border-color:#a0cfff}.el-button--primary.is-plain{color:#409eff;background:#ecf5ff;border-color:#b3d8ff}.el-button--primary.is-plain:focus,.el-button--primary.is-plain:hover{background:#409eff;border-color:#409eff;color:#fff}.el-button--primary.is-plain:active{background:#3a8ee6;border-color:#3a8ee6;color:#fff;outline:0}.el-button--primary.is-plain.is-disabled,.el-button--primary.is-plain.is-disabled:active,.el-button--primary.is-plain.is-disabled:focus,.el-button--primary.is-plain.is-disabled:hover{color:#8cc5ff;background-color:#ecf5ff;border-color:#d9ecff}.el-button--success{color:#fff;background-color:#67c23a;border-color:#67c23a}.el-button--success:focus,.el-button--success:hover{background:#85ce61;border-color:#85ce61;color:#fff}.el-button--success.is-active,.el-button--success:active{background:#5daf34;border-color:#5daf34;color:#fff}.el-button--success:active{outline:0}.el-button--success.is-disabled,.el-button--success.is-disabled:active,.el-button--success.is-disabled:focus,.el-button--success.is-disabled:hover{color:#fff;background-color:#b3e19d;border-color:#b3e19d}.el-button--success.is-plain{color:#67c23a;background:#f0f9eb;border-color:#c2e7b0}.el-button--success.is-plain:focus,.el-button--success.is-plain:hover{background:#67c23a;border-color:#67c23a;color:#fff}.el-button--success.is-plain:active{background:#5daf34;border-color:#5daf34;color:#fff;outline:0}.el-button--success.is-plain.is-disabled,.el-button--success.is-plain.is-disabled:active,.el-button--success.is-plain.is-disabled:focus,.el-button--success.is-plain.is-disabled:hover{color:#a4da89;background-color:#f0f9eb;border-color:#e1f3d8}.el-button--warning{color:#fff;background-color:#e6a23c;border-color:#e6a23c}.el-button--warning:focus,.el-button--warning:hover{background:#ebb563;border-color:#ebb563;color:#fff}.el-button--warning.is-active,.el-button--warning:active{background:#cf9236;border-color:#cf9236;color:#fff}.el-button--warning:active{outline:0}.el-button--warning.is-disabled,.el-button--warning.is-disabled:active,.el-button--warning.is-disabled:focus,.el-button--warning.is-disabled:hover{color:#fff;background-color:#f3d19e;border-color:#f3d19e}.el-button--warning.is-plain{color:#e6a23c;background:#fdf6ec;border-color:#f5dab1}.el-button--warning.is-plain:focus,.el-button--warning.is-plain:hover{background:#e6a23c;border-color:#e6a23c;color:#fff}.el-button--warning.is-plain:active{background:#cf9236;border-color:#cf9236;color:#fff;outline:0}.el-button--warning.is-plain.is-disabled,.el-button--warning.is-plain.is-disabled:active,.el-button--warning.is-plain.is-disabled:focus,.el-button--warning.is-plain.is-disabled:hover{color:#f0c78a;background-color:#fdf6ec;border-color:#faecd8}.el-button--danger{color:#fff;background-color:#f56c6c;border-color:#f56c6c}.el-button--danger:focus,.el-button--danger:hover{background:#f78989;border-color:#f78989;color:#fff}.el-button--danger.is-active,.el-button--danger:active{background:#dd6161;border-color:#dd6161;color:#fff}.el-button--danger:active{outline:0}.el-button--danger.is-disabled,.el-button--danger.is-disabled:active,.el-button--danger.is-disabled:focus,.el-button--danger.is-disabled:hover{color:#fff;background-color:#fab6b6;border-color:#fab6b6}.el-button--danger.is-plain{color:#f56c6c;background:#fef0f0;border-color:#fbc4c4}.el-button--danger.is-plain:focus,.el-button--danger.is-plain:hover{background:#f56c6c;border-color:#f56c6c;color:#fff}.el-button--danger.is-plain:active{background:#dd6161;border-color:#dd6161;color:#fff;outline:0}.el-button--danger.is-plain.is-disabled,.el-button--danger.is-plain.is-disabled:active,.el-button--danger.is-plain.is-disabled:focus,.el-button--danger.is-plain.is-disabled:hover{color:#f9a7a7;background-color:#fef0f0;border-color:#fde2e2}.el-button--info{color:#fff;background-color:#909399;border-color:#909399}.el-button--info:focus,.el-button--info:hover{background:#a6a9ad;border-color:#a6a9ad;color:#fff}.el-button--info.is-active,.el-button--info:active{background:#82848a;border-color:#82848a;color:#fff}.el-button--info:active{outline:0}.el-button--info.is-disabled,.el-button--info.is-disabled:active,.el-button--info.is-disabled:focus,.el-button--info.is-disabled:hover{color:#fff;background-color:#c8c9cc;border-color:#c8c9cc}.el-button--info.is-plain{color:#909399;background:#f4f4f5;border-color:#d3d4d6}.el-button--info.is-plain:focus,.el-button--info.is-plain:hover{background:#909399;border-color:#909399;color:#fff}.el-button--info.is-plain:active{background:#82848a;border-color:#82848a;color:#fff;outline:0}.el-button--info.is-plain.is-disabled,.el-button--info.is-plain.is-disabled:active,.el-button--info.is-plain.is-disabled:focus,.el-button--info.is-plain.is-disabled:hover{color:#bcbec2;background-color:#f4f4f5;border-color:#e9e9eb}.el-button--text,.el-button--text.is-disabled,.el-button--text.is-disabled:focus,.el-button--text.is-disabled:hover,.el-button--text:active{border-color:transparent}.el-button--medium{padding:10px 20px;font-size:14px;border-radius:4px}.el-button--mini,.el-button--small{font-size:12px;border-radius:3px}.el-button--medium.is-round{padding:10px 20px}.el-button--medium.is-circle{padding:10px}.el-button--small,.el-button--small.is-round{padding:9px 15px}.el-button--small.is-circle{padding:9px}.el-button--mini,.el-button--mini.is-round{padding:7px 15px}.el-button--mini.is-circle{padding:7px}.el-button--text{color:#409eff;background:0 0;padding-left:0;padding-right:0}.el-button--text:focus,.el-button--text:hover{color:#66b1ff;border-color:transparent;background-color:transparent}.el-button--text:active{color:#3a8ee6;background-color:transparent}.el-button-group{display:inline-block;vertical-align:middle}.el-button-group:after,.el-button-group:before{display:table;content:""}.el-button-group:after{clear:both}.el-button-group>.el-button{float:left;position:relative}.el-button-group>.el-button+.el-button{margin-left:0}.el-button-group>.el-button.is-disabled{z-index:1}.el-button-group>.el-button:first-child{border-top-right-radius:0;border-bottom-right-radius:0}.el-button-group>.el-button:last-child{border-top-left-radius:0;border-bottom-left-radius:0}.el-button-group>.el-button:first-child:last-child{border-radius:4px}.el-button-group>.el-button:first-child:last-child.is-round{border-radius:20px}.el-button-group>.el-button:first-child:last-child.is-circle{border-radius:50%}.el-button-group>.el-button:not(:first-child):not(:last-child){border-radius:0}.el-button-group>.el-button:not(:last-child){margin-right:-1px}.el-button-group>.el-button.is-active,.el-button-group>.el-button:active,.el-button-group>.el-button:focus,.el-button-group>.el-button:hover{z-index:1}.el-button-group>.el-dropdown>.el-button{border-top-left-radius:0;border-bottom-left-radius:0;border-left-color:hsla(0,0%,100%,.5)}.el-button-group .el-button--primary:first-child{border-right-color:hsla(0,0%,100%,.5)}.el-button-group .el-button--primary:last-child{border-left-color:hsla(0,0%,100%,.5)}.el-button-group .el-button--primary:not(:first-child):not(:last-child){border-left-color:hsla(0,0%,100%,.5);border-right-color:hsla(0,0%,100%,.5)}.el-button-group .el-button--success:first-child{border-right-color:hsla(0,0%,100%,.5)}.el-button-group .el-button--success:last-child{border-left-color:hsla(0,0%,100%,.5)}.el-button-group .el-button--success:not(:first-child):not(:last-child){border-left-color:hsla(0,0%,100%,.5);border-right-color:hsla(0,0%,100%,.5)}.el-button-group .el-button--warning:first-child{border-right-color:hsla(0,0%,100%,.5)}.el-button-group .el-button--warning:last-child{border-left-color:hsla(0,0%,100%,.5)}.el-button-group .el-button--warning:not(:first-child):not(:last-child){border-left-color:hsla(0,0%,100%,.5);border-right-color:hsla(0,0%,100%,.5)}.el-button-group .el-button--danger:first-child{border-right-color:hsla(0,0%,100%,.5)}.el-button-group .el-button--danger:last-child{border-left-color:hsla(0,0%,100%,.5)}.el-button-group .el-button--danger:not(:first-child):not(:last-child){border-left-color:hsla(0,0%,100%,.5);border-right-color:hsla(0,0%,100%,.5)}.el-button-group .el-button--info:first-child{border-right-color:hsla(0,0%,100%,.5)}.el-button-group .el-button--info:last-child{border-left-color:hsla(0,0%,100%,.5)}.el-button-group .el-button--info:not(:first-child):not(:last-child){border-left-color:hsla(0,0%,100%,.5);border-right-color:hsla(0,0%,100%,.5)}.el-calendar{background-color:#fff}.el-calendar__header{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between;padding:12px 20px;border-bottom:1px solid #ebeef5}.el-backtop,.el-page-header{display:-webkit-box;display:-ms-flexbox}.el-calendar__title{color:#000;-ms-flex-item-align:center;align-self:center}.el-calendar__body{padding:12px 20px 35px}.el-calendar-table{table-layout:fixed;width:100%}.el-calendar-table thead th{padding:12px 0;color:#606266;font-weight:400}.el-calendar-table:not(.is-range) td.next,.el-calendar-table:not(.is-range) td.prev{color:#c0c4cc}.el-backtop,.el-calendar-table td.is-today{color:#409eff}.el-calendar-table td{border-bottom:1px solid #ebeef5;border-right:1px solid #ebeef5;vertical-align:top;-webkit-transition:background-color .2s ease;transition:background-color .2s ease}.el-calendar-table td.is-selected{background-color:#f2f8fe}.el-calendar-table tr:first-child td{border-top:1px solid #ebeef5}.el-calendar-table tr td:first-child{border-left:1px solid #ebeef5}.el-calendar-table tr.el-calendar-table__row--hide-border td{border-top:none}.el-calendar-table .el-calendar-day{-webkit-box-sizing:border-box;box-sizing:border-box;padding:8px;height:85px}.el-calendar-table .el-calendar-day:hover{cursor:pointer;background-color:#f2f8fe}.el-backtop{position:fixed;background-color:#fff;width:40px;height:40px;border-radius:50%;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;font-size:20px;-webkit-box-shadow:0 0 6px rgba(0,0,0,.12);box-shadow:0 0 6px rgba(0,0,0,.12);cursor:pointer;z-index:5}.el-backtop:hover{background-color:#f2f6fc}.el-page-header{line-height:24px}.el-page-header,.el-page-header__left{display:-webkit-box;display:-ms-flexbox;display:flex}.el-page-header__left{cursor:pointer;margin-right:40px;position:relative}.el-page-header__left:after{content:"";position:absolute;width:1px;height:16px;right:-20px;top:50%;-webkit-transform:translateY(-50%);transform:translateY(-50%);background-color:#dcdfe6}.el-checkbox,.el-checkbox__input{display:inline-block;position:relative;white-space:nowrap}.el-page-header__left .el-icon-back{font-size:18px;margin-right:6px;-ms-flex-item-align:center;align-self:center}.el-page-header__title{font-size:14px;font-weight:500}.el-page-header__content{font-size:18px;color:#303133}.el-checkbox{color:#606266;font-size:14px;cursor:pointer;user-select:none;margin-right:30px}.el-checkbox,.el-checkbox-button__inner,.el-radio{font-weight:500;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none}.el-checkbox.is-bordered{padding:9px 20px 9px 10px;border-radius:4px;border:1px solid #dcdfe6;-webkit-box-sizing:border-box;box-sizing:border-box;line-height:normal;height:40px}.el-checkbox.is-bordered.is-checked{border-color:#409eff}.el-checkbox.is-bordered.is-disabled{border-color:#ebeef5;cursor:not-allowed}.el-checkbox.is-bordered+.el-checkbox.is-bordered{margin-left:10px}.el-checkbox.is-bordered.el-checkbox--medium{padding:7px 20px 7px 10px;border-radius:4px;height:36px}.el-checkbox.is-bordered.el-checkbox--medium .el-checkbox__label{line-height:17px;font-size:14px}.el-checkbox.is-bordered.el-checkbox--medium .el-checkbox__inner{height:14px;width:14px}.el-checkbox.is-bordered.el-checkbox--small{padding:5px 15px 5px 10px;border-radius:3px;height:32px}.el-checkbox.is-bordered.el-checkbox--small .el-checkbox__label{line-height:15px;font-size:12px}.el-checkbox.is-bordered.el-checkbox--small .el-checkbox__inner{height:12px;width:12px}.el-checkbox.is-bordered.el-checkbox--small .el-checkbox__inner:after{height:6px;width:2px}.el-checkbox.is-bordered.el-checkbox--mini{padding:3px 15px 3px 10px;border-radius:3px;height:28px}.el-checkbox.is-bordered.el-checkbox--mini .el-checkbox__label{line-height:12px;font-size:12px}.el-checkbox.is-bordered.el-checkbox--mini .el-checkbox__inner{height:12px;width:12px}.el-checkbox.is-bordered.el-checkbox--mini .el-checkbox__inner:after{height:6px;width:2px}.el-checkbox__input{cursor:pointer;outline:0;line-height:1;vertical-align:middle}.el-checkbox__input.is-disabled .el-checkbox__inner{background-color:#edf2fc;border-color:#dcdfe6;cursor:not-allowed}.el-checkbox__input.is-disabled .el-checkbox__inner:after{cursor:not-allowed;border-color:#c0c4cc}.el-checkbox__input.is-disabled .el-checkbox__inner+.el-checkbox__label{cursor:not-allowed}.el-checkbox__input.is-disabled.is-checked .el-checkbox__inner{background-color:#f2f6fc;border-color:#dcdfe6}.el-checkbox__input.is-disabled.is-checked .el-checkbox__inner:after{border-color:#c0c4cc}.el-checkbox__input.is-disabled.is-indeterminate .el-checkbox__inner{background-color:#f2f6fc;border-color:#dcdfe6}.el-checkbox__input.is-disabled.is-indeterminate .el-checkbox__inner:before{background-color:#c0c4cc;border-color:#c0c4cc}.el-checkbox__input.is-checked .el-checkbox__inner,.el-checkbox__input.is-indeterminate .el-checkbox__inner{background-color:#409eff;border-color:#409eff}.el-checkbox__input.is-disabled+span.el-checkbox__label{color:#c0c4cc;cursor:not-allowed}.el-checkbox__input.is-checked .el-checkbox__inner:after{-webkit-transform:rotate(45deg) scaleY(1);transform:rotate(45deg) scaleY(1)}.el-checkbox__input.is-checked+.el-checkbox__label{color:#409eff}.el-checkbox__input.is-focus .el-checkbox__inner{border-color:#409eff}.el-checkbox__input.is-indeterminate .el-checkbox__inner:before{content:"";position:absolute;display:block;background-color:#fff;height:2px;-webkit-transform:scale(.5);transform:scale(.5);left:0;right:0;top:5px}.el-checkbox__input.is-indeterminate .el-checkbox__inner:after{display:none}.el-checkbox__inner{display:inline-block;position:relative;border:1px solid #dcdfe6;border-radius:2px;-webkit-box-sizing:border-box;box-sizing:border-box;width:14px;height:14px;background-color:#fff;z-index:1;-webkit-transition:border-color .25s cubic-bezier(.71,-.46,.29,1.46),background-color .25s cubic-bezier(.71,-.46,.29,1.46);transition:border-color .25s cubic-bezier(.71,-.46,.29,1.46),background-color .25s cubic-bezier(.71,-.46,.29,1.46)}.el-checkbox__inner:hover{border-color:#409eff}.el-checkbox__inner:after{-webkit-box-sizing:content-box;box-sizing:content-box;content:"";border:1px solid #fff;border-left:0;border-top:0;height:7px;left:4px;position:absolute;top:1px;-webkit-transform:rotate(45deg) scaleY(0);transform:rotate(45deg) scaleY(0);width:3px;-webkit-transition:-webkit-transform .15s ease-in .05s;transition:-webkit-transform .15s ease-in .05s;transition:transform .15s ease-in .05s;transition:transform .15s ease-in .05s,-webkit-transform .15s ease-in .05s;-webkit-transform-origin:center;transform-origin:center}.el-checkbox__original{opacity:0;outline:0;position:absolute;margin:0;width:0;height:0;z-index:-1}.el-checkbox-button,.el-checkbox-button__inner{display:inline-block;position:relative}.el-checkbox__label{display:inline-block;padding-left:10px;line-height:19px;font-size:14px}.el-checkbox:last-of-type{margin-right:0}.el-checkbox-button__inner{line-height:1;white-space:nowrap;vertical-align:middle;cursor:pointer;background:#fff;border:1px solid #dcdfe6;border-left:0;color:#606266;-webkit-appearance:none;text-align:center;-webkit-box-sizing:border-box;box-sizing:border-box;outline:0;margin:0;-webkit-transition:all .3s cubic-bezier(.645,.045,.355,1);transition:all .3s cubic-bezier(.645,.045,.355,1);padding:12px 20px;font-size:14px;border-radius:0}.el-checkbox-button__inner.is-round{padding:12px 20px}.el-checkbox-button__inner:hover{color:#409eff}.el-checkbox-button__inner [class*=el-icon-]{line-height:.9}.el-radio,.el-radio__input{line-height:1;outline:0;white-space:nowrap}.el-checkbox-button__inner [class*=el-icon-]+span{margin-left:5px}.el-checkbox-button__original{opacity:0;outline:0;position:absolute;margin:0;z-index:-1}.el-radio,.el-radio__inner,.el-radio__input{position:relative;display:inline-block}.el-checkbox-button.is-checked .el-checkbox-button__inner{color:#fff;background-color:#409eff;border-color:#409eff;-webkit-box-shadow:-1px 0 0 0 #8cc5ff;box-shadow:-1px 0 0 0 #8cc5ff}.el-checkbox-button.is-checked:first-child .el-checkbox-button__inner{border-left-color:#409eff}.el-checkbox-button.is-disabled .el-checkbox-button__inner{color:#c0c4cc;cursor:not-allowed;background-image:none;background-color:#fff;border-color:#ebeef5;-webkit-box-shadow:none;box-shadow:none}.el-checkbox-button.is-disabled:first-child .el-checkbox-button__inner{border-left-color:#ebeef5}.el-checkbox-button:first-child .el-checkbox-button__inner{border-left:1px solid #dcdfe6;border-radius:4px 0 0 4px;-webkit-box-shadow:none!important;box-shadow:none!important}.el-checkbox-button.is-focus .el-checkbox-button__inner{border-color:#409eff}.el-checkbox-button:last-child .el-checkbox-button__inner{border-radius:0 4px 4px 0}.el-checkbox-button--medium .el-checkbox-button__inner{padding:10px 20px;font-size:14px;border-radius:0}.el-checkbox-button--medium .el-checkbox-button__inner.is-round{padding:10px 20px}.el-checkbox-button--small .el-checkbox-button__inner{padding:9px 15px;font-size:12px;border-radius:0}.el-checkbox-button--small .el-checkbox-button__inner.is-round{padding:9px 15px}.el-checkbox-button--mini .el-checkbox-button__inner{padding:7px 15px;font-size:12px;border-radius:0}.el-checkbox-button--mini .el-checkbox-button__inner.is-round{padding:7px 15px}.el-checkbox-group{font-size:0}.el-radio,.el-radio--medium.is-bordered .el-radio__label{font-size:14px}.el-radio{color:#606266;cursor:pointer;margin-right:30px}.el-cascader-node>.el-radio,.el-radio:last-child{margin-right:0}.el-radio.is-bordered{padding:12px 20px 0 10px;border-radius:4px;border:1px solid #dcdfe6;-webkit-box-sizing:border-box;box-sizing:border-box;height:40px}.el-radio.is-bordered.is-checked{border-color:#409eff}.el-radio.is-bordered.is-disabled{cursor:not-allowed;border-color:#ebeef5}.el-radio__input.is-disabled .el-radio__inner,.el-radio__input.is-disabled.is-checked .el-radio__inner{background-color:#f5f7fa;border-color:#e4e7ed}.el-radio.is-bordered+.el-radio.is-bordered{margin-left:10px}.el-radio--medium.is-bordered{padding:10px 20px 0 10px;border-radius:4px;height:36px}.el-radio--mini.is-bordered .el-radio__label,.el-radio--small.is-bordered .el-radio__label{font-size:12px}.el-radio--medium.is-bordered .el-radio__inner{height:14px;width:14px}.el-radio--small.is-bordered{padding:8px 15px 0 10px;border-radius:3px;height:32px}.el-radio--small.is-bordered .el-radio__inner{height:12px;width:12px}.el-radio--mini.is-bordered{padding:6px 15px 0 10px;border-radius:3px;height:28px}.el-radio--mini.is-bordered .el-radio__inner{height:12px;width:12px}.el-radio__input{cursor:pointer;vertical-align:middle}.el-radio__input.is-disabled .el-radio__inner{cursor:not-allowed}.el-radio__input.is-disabled .el-radio__inner:after{cursor:not-allowed;background-color:#f5f7fa}.el-radio__input.is-disabled .el-radio__inner+.el-radio__label{cursor:not-allowed}.el-radio__input.is-disabled.is-checked .el-radio__inner:after{background-color:#c0c4cc}.el-radio__input.is-disabled+span.el-radio__label{color:#c0c4cc;cursor:not-allowed}.el-radio__input.is-checked .el-radio__inner{border-color:#409eff;background:#409eff}.el-radio__input.is-checked .el-radio__inner:after{-webkit-transform:translate(-50%,-50%) scale(1);transform:translate(-50%,-50%) scale(1)}.el-radio__input.is-checked+.el-radio__label{color:#409eff}.el-radio__input.is-focus .el-radio__inner{border-color:#409eff}.el-radio__inner{border:1px solid #dcdfe6;border-radius:100%;width:14px;height:14px;background-color:#fff;cursor:pointer;-webkit-box-sizing:border-box;box-sizing:border-box}.el-radio__inner:hover{border-color:#409eff}.el-radio__inner:after{width:4px;height:4px;border-radius:100%;background-color:#fff;content:"";position:absolute;left:50%;top:50%;-webkit-transform:translate(-50%,-50%) scale(0);transform:translate(-50%,-50%) scale(0);-webkit-transition:-webkit-transform .15s ease-in;transition:-webkit-transform .15s ease-in;transition:transform .15s ease-in;transition:transform .15s ease-in,-webkit-transform .15s ease-in}.el-radio__original{opacity:0;outline:0;position:absolute;z-index:-1;top:0;left:0;right:0;bottom:0;margin:0}.el-radio:focus:not(.is-focus):not(:active):not(.is-disabled) .el-radio__inner{-webkit-box-shadow:0 0 2px 2px #409eff;box-shadow:0 0 2px 2px #409eff}.el-radio__label{font-size:14px;padding-left:10px}.el-scrollbar{overflow:hidden;position:relative}.el-scrollbar:active>.el-scrollbar__bar,.el-scrollbar:focus>.el-scrollbar__bar,.el-scrollbar:hover>.el-scrollbar__bar{opacity:1;-webkit-transition:opacity .34s ease-out;transition:opacity .34s ease-out}.el-scrollbar__wrap{overflow:scroll;height:100%}.el-scrollbar__wrap--hidden-default{scrollbar-width:none}.el-scrollbar__wrap--hidden-default::-webkit-scrollbar{width:0;height:0}.el-scrollbar__thumb{position:relative;display:block;width:0;height:0;cursor:pointer;border-radius:inherit;background-color:rgba(144,147,153,.3);-webkit-transition:background-color .3s;transition:background-color .3s}.el-scrollbar__thumb:hover{background-color:rgba(144,147,153,.5)}.el-scrollbar__bar{position:absolute;right:2px;bottom:2px;z-index:1;border-radius:4px;opacity:0;-webkit-transition:opacity .12s ease-out;transition:opacity .12s ease-out}.el-scrollbar__bar.is-vertical{width:6px;top:2px}.el-scrollbar__bar.is-vertical>div{width:100%}.el-scrollbar__bar.is-horizontal{height:6px;left:2px}.el-scrollbar__bar.is-horizontal>div{height:100%}.el-cascader-panel{display:-webkit-box;display:-ms-flexbox;display:flex;border-radius:4px;font-size:14px}.el-cascader-panel.is-bordered{border:1px solid #e4e7ed;border-radius:4px}.el-cascader-menu{min-width:180px;-webkit-box-sizing:border-box;box-sizing:border-box;color:#606266;border-right:1px solid #e4e7ed}.el-cascader-menu:last-child{border-right:none}.el-cascader-menu:last-child .el-cascader-node{padding-right:20px}.el-cascader-menu__wrap{height:204px}.el-cascader-menu__list{position:relative;min-height:100%;margin:0;padding:6px 0;list-style:none;-webkit-box-sizing:border-box;box-sizing:border-box}.el-avatar,.el-drawer{-webkit-box-sizing:border-box;overflow:hidden}.el-cascader-menu__hover-zone{position:absolute;top:0;left:0;width:100%;height:100%;pointer-events:none}.el-cascader-menu__empty-text{position:absolute;top:50%;left:50%;-webkit-transform:translate(-50%,-50%);transform:translate(-50%,-50%);text-align:center;color:#c0c4cc}.el-cascader-node{position:relative;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;padding:0 30px 0 20px;height:34px;line-height:34px;outline:0}.el-cascader-node.is-selectable.in-active-path{color:#606266}.el-cascader-node.in-active-path,.el-cascader-node.is-active,.el-cascader-node.is-selectable.in-checked-path{color:#409eff;font-weight:700}.el-cascader-node:not(.is-disabled){cursor:pointer}.el-cascader-node:not(.is-disabled):focus,.el-cascader-node:not(.is-disabled):hover{background:#f5f7fa}.el-cascader-node.is-disabled{color:#c0c4cc;cursor:not-allowed}.el-cascader-node__prefix{position:absolute;left:10px}.el-cascader-node__postfix{position:absolute;right:10px}.el-cascader-node__label{-webkit-box-flex:1;-ms-flex:1;flex:1;padding:0 10px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.el-cascader-node>.el-radio .el-radio__label{padding-left:0}.el-avatar{display:inline-block;-webkit-box-sizing:border-box;box-sizing:border-box;text-align:center;color:#fff;background:#c0c4cc;width:40px;height:40px;line-height:40px;font-size:14px}.el-avatar>img{display:block;height:100%;vertical-align:middle}.el-drawer,.el-drawer__header{display:-webkit-box;display:-ms-flexbox}.el-avatar--circle{border-radius:50%}.el-avatar--square{border-radius:4px}.el-avatar--icon{font-size:18px}.el-avatar--large{width:40px;height:40px;line-height:40px}.el-avatar--medium{width:36px;height:36px;line-height:36px}.el-avatar--small{width:28px;height:28px;line-height:28px}.el-drawer.btt,.el-drawer.ttb,.el-drawer__container{left:0;right:0;width:100%}.el-drawer.ltr,.el-drawer.rtl,.el-drawer__container{top:0;bottom:0;height:100%}@-webkit-keyframes el-drawer-fade-in{0%{opacity:0}to{opacity:1}}@keyframes el-drawer-fade-in{0%{opacity:0}to{opacity:1}}@-webkit-keyframes rtl-drawer-in{0%{-webkit-transform:translate(100%);transform:translate(100%)}to{-webkit-transform:translate(0);transform:translate(0)}}@keyframes rtl-drawer-in{0%{-webkit-transform:translate(100%);transform:translate(100%)}to{-webkit-transform:translate(0);transform:translate(0)}}@-webkit-keyframes rtl-drawer-out{0%{-webkit-transform:translate(0);transform:translate(0)}to{-webkit-transform:translate(100%);transform:translate(100%)}}@keyframes rtl-drawer-out{0%{-webkit-transform:translate(0);transform:translate(0)}to{-webkit-transform:translate(100%);transform:translate(100%)}}@-webkit-keyframes ltr-drawer-in{0%{-webkit-transform:translate(-100%);transform:translate(-100%)}to{-webkit-transform:translate(0);transform:translate(0)}}@keyframes ltr-drawer-in{0%{-webkit-transform:translate(-100%);transform:translate(-100%)}to{-webkit-transform:translate(0);transform:translate(0)}}@-webkit-keyframes ltr-drawer-out{0%{-webkit-transform:translate(0);transform:translate(0)}to{-webkit-transform:translate(-100%);transform:translate(-100%)}}@keyframes ltr-drawer-out{0%{-webkit-transform:translate(0);transform:translate(0)}to{-webkit-transform:translate(-100%);transform:translate(-100%)}}@-webkit-keyframes ttb-drawer-in{0%{-webkit-transform:translateY(-100%);transform:translateY(-100%)}to{-webkit-transform:translate(0);transform:translate(0)}}@keyframes ttb-drawer-in{0%{-webkit-transform:translateY(-100%);transform:translateY(-100%)}to{-webkit-transform:translate(0);transform:translate(0)}}@-webkit-keyframes ttb-drawer-out{0%{-webkit-transform:translate(0);transform:translate(0)}to{-webkit-transform:translateY(-100%);transform:translateY(-100%)}}@keyframes ttb-drawer-out{0%{-webkit-transform:translate(0);transform:translate(0)}to{-webkit-transform:translateY(-100%);transform:translateY(-100%)}}@-webkit-keyframes btt-drawer-in{0%{-webkit-transform:translateY(100%);transform:translateY(100%)}to{-webkit-transform:translate(0);transform:translate(0)}}@keyframes btt-drawer-in{0%{-webkit-transform:translateY(100%);transform:translateY(100%)}to{-webkit-transform:translate(0);transform:translate(0)}}@-webkit-keyframes btt-drawer-out{0%{-webkit-transform:translate(0);transform:translate(0)}to{-webkit-transform:translateY(100%);transform:translateY(100%)}}@keyframes btt-drawer-out{0%{-webkit-transform:translate(0);transform:translate(0)}to{-webkit-transform:translateY(100%);transform:translateY(100%)}}.el-drawer{position:absolute;-webkit-box-sizing:border-box;box-sizing:border-box;background-color:#fff;display:-webkit-box;display:-ms-flexbox;display:flex;-ms-flex-direction:column;-webkit-box-orient:vertical;-webkit-box-direction:normal;flex-direction:column;-webkit-box-shadow:0 8px 10px -5px rgba(0,0,0,.2),0 16px 24px 2px rgba(0,0,0,.14),0 6px 30px 5px rgba(0,0,0,.12);box-shadow:0 8px 10px -5px rgba(0,0,0,.2),0 16px 24px 2px rgba(0,0,0,.14),0 6px 30px 5px rgba(0,0,0,.12)}.el-drawer.rtl{-webkit-animation:rtl-drawer-out .3s;animation:rtl-drawer-out .3s;right:0}.el-drawer__open .el-drawer.rtl{-webkit-animation:rtl-drawer-in .3s 1ms;animation:rtl-drawer-in .3s 1ms}.el-drawer.ltr{-webkit-animation:ltr-drawer-out .3s;animation:ltr-drawer-out .3s;left:0}.el-drawer__open .el-drawer.ltr{-webkit-animation:ltr-drawer-in .3s 1ms;animation:ltr-drawer-in .3s 1ms}.el-drawer.ttb{-webkit-animation:ttb-drawer-out .3s;animation:ttb-drawer-out .3s;top:0}.el-drawer__open .el-drawer.ttb{-webkit-animation:ttb-drawer-in .3s 1ms;animation:ttb-drawer-in .3s 1ms}.el-drawer.btt{-webkit-animation:btt-drawer-out .3s;animation:btt-drawer-out .3s;bottom:0}.el-drawer__open .el-drawer.btt{-webkit-animation:btt-drawer-in .3s 1ms;animation:btt-drawer-in .3s 1ms}.el-drawer__wrapper{position:fixed;top:0;right:0;bottom:0;left:0;overflow:hidden;margin:0}.el-drawer__header{-webkit-box-align:center;-ms-flex-align:center;align-items:center;color:#72767b;display:-webkit-box;display:-ms-flexbox;display:flex;margin-bottom:32px;padding:20px 20px 0}.el-drawer__header>:first-child,.el-drawer__title{-webkit-box-flex:1;-ms-flex:1;flex:1}.el-drawer__title{margin:0;line-height:inherit;font-size:1rem}.el-drawer__close-btn{border:none;cursor:pointer;font-size:20px;color:inherit;background-color:transparent}.el-drawer__body{-webkit-box-flex:1;-ms-flex:1;flex:1}.el-drawer__body>*{-webkit-box-sizing:border-box;box-sizing:border-box}.el-drawer__container{position:relative}.el-drawer-fade-enter-active{-webkit-animation:el-drawer-fade-in .3s;animation:el-drawer-fade-in .3s}.el-drawer-fade-leave-active{animation:el-drawer-fade-in .3s reverse}.el-popconfirm__main{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center}.el-popconfirm__icon{margin-right:5px}.el-popconfirm__action{text-align:right;margin:0} \ No newline at end of file diff --git a/sop-website/sop-website-server/src/main/resources/public/static/css/chunk-libs.3dfb7769.css b/sop-website/sop-website-server/src/main/resources/public/static/css/chunk-libs.3dfb7769.css new file mode 100644 index 00000000..baeecbc6 --- /dev/null +++ b/sop-website/sop-website-server/src/main/resources/public/static/css/chunk-libs.3dfb7769.css @@ -0,0 +1 @@ +/*! normalize.css v7.0.0 | MIT License | github.com/necolas/normalize.css */html{line-height:1.15;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}article,aside,footer,header,nav,section{display:block}h1{font-size:2em;margin:.67em 0}figcaption,figure,main{display:block}figure{margin:1em 40px}hr{-webkit-box-sizing:content-box;box-sizing:content-box;height:0;overflow:visible}pre{font-family:monospace,monospace;font-size:1em}a{background-color:transparent;-webkit-text-decoration-skip:objects}abbr[title]{border-bottom:none;text-decoration:underline;-webkit-text-decoration:underline dotted;text-decoration:underline dotted}b,strong{font-weight:inherit;font-weight:bolder}code,kbd,samp{font-family:monospace,monospace;font-size:1em}dfn{font-style:italic}mark{background-color:#ff0;color:#000}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}audio,video{display:inline-block}audio:not([controls]){display:none;height:0}img{border-style:none}svg:not(:root){overflow:hidden}button,input,optgroup,select,textarea{font-family:sans-serif;font-size:100%;line-height:1.15;margin:0}button,input{overflow:visible}button,select{text-transform:none}[type=reset],[type=submit],button,html [type=button]{-webkit-appearance:button}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{border-style:none;padding:0}[type=button]:-moz-focusring,[type=reset]:-moz-focusring,[type=submit]:-moz-focusring,button:-moz-focusring{outline:1px dotted ButtonText}fieldset{padding:.35em .75em .625em}legend{-webkit-box-sizing:border-box;box-sizing:border-box;color:inherit;display:table;max-width:100%;padding:0;white-space:normal}progress{display:inline-block;vertical-align:baseline}textarea{overflow:auto}[type=checkbox],[type=radio]{-webkit-box-sizing:border-box;box-sizing:border-box;padding:0}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}[type=search]::-webkit-search-cancel-button,[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}details,menu{display:block}summary{display:list-item}canvas{display:inline-block}[hidden],template{display:none}#nprogress{pointer-events:none}#nprogress .bar{background:#29d;position:fixed;z-index:1031;top:0;left:0;width:100%;height:2px}#nprogress .peg{display:block;position:absolute;right:0;width:100px;height:100%;-webkit-box-shadow:0 0 10px #29d,0 0 5px #29d;box-shadow:0 0 10px #29d,0 0 5px #29d;opacity:1;-webkit-transform:rotate(3deg) translateY(-4px);transform:rotate(3deg) translateY(-4px)}#nprogress .spinner{display:block;position:fixed;z-index:1031;top:15px;right:15px}#nprogress .spinner-icon{width:18px;height:18px;-webkit-box-sizing:border-box;box-sizing:border-box;border:2px solid transparent;border-top-color:#29d;border-left-color:#29d;border-radius:50%;-webkit-animation:nprogress-spinner .4s linear infinite;animation:nprogress-spinner .4s linear infinite}.nprogress-custom-parent{overflow:hidden;position:relative}.nprogress-custom-parent #nprogress .bar,.nprogress-custom-parent #nprogress .spinner{position:absolute}@-webkit-keyframes nprogress-spinner{0%{-webkit-transform:rotate(0deg)}to{-webkit-transform:rotate(1turn)}}@keyframes nprogress-spinner{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}to{-webkit-transform:rotate(1turn);transform:rotate(1turn)}} \ No newline at end of file diff --git a/sop-website/sop-website-server/src/main/resources/public/static/doc/common/keys.md b/sop-website/sop-website-server/src/main/resources/public/static/doc/common/keys.md new file mode 100644 index 00000000..2f8d2e81 --- /dev/null +++ b/sop-website/sop-website-server/src/main/resources/public/static/doc/common/keys.md @@ -0,0 +1,32 @@ +# 公私钥介绍 + +- 公私钥用途 + +简单来说就是`私钥加签,公钥验证` + +接入方和开放平台各自拥有一对公私钥,并且把自己的公钥给对方。 + +接入方登录后,首先生成一对公私钥,接入方的公钥被称为`应用公钥`,然后把公钥上传给开放平台,同时,开放平台会把平台公钥返回给接入方。 +这样实现了一个交换公钥的步骤,私钥自己保管,不能暴露。 + +- 接入方如何使用私钥 + +在进行接口调用时需要对请求参数进行加签,使用各自语言对应的`SHA256WithRSA`签名函数,并使用`应用私钥`对待签名字符串进行签名。 + +- 开放平台私钥如何使用 + +在进行业务回调的时候使用,如支付完成后开放平台回调接入方提供的回调接口,此时就是开放平台调用接入方的接口。 + +开放平台用平台私钥进行加签,然后接入方收到请求后,需要使用`平台公钥`进行验签,验证通过后再执行后续逻辑。 + +## 公私钥生成 + +在线生成,[点击前往](http://web.chacuo.net/netrsakeypair) + + +`生成密钥位数`选择`2048位`,秘钥格式Java语言选择`PKCS#8`,非Java选择`PKCS#1`,证书密码不填 + +> 注:去掉首尾`-----BEGIN PUBLIC KEY-----`,`-----END PUBLIC KEY-----` + +将生成的公钥上传,私钥保存。 + diff --git a/sop-website/sop-website-server/src/main/resources/public/static/doc/common/sign.md b/sop-website/sop-website-server/src/main/resources/public/static/doc/common/sign.md new file mode 100644 index 00000000..3bc4f0f7 --- /dev/null +++ b/sop-website/sop-website-server/src/main/resources/public/static/doc/common/sign.md @@ -0,0 +1,73 @@ +# 签名算法 + +签名步骤如下: + +1.筛选并排序 + +获取所有请求参数,不包括字节类型参数,如文件、字节流,剔除sign字段,剔除值为空的参数,并按照参数名ASCII码递增排序(字母升序排序),如果遇到相同字符则按照第二个字符的键值ASCII码递增排序,以此类推。 + +2.拼接 + +将排序后的参数与其对应值,组合成“参数=参数值”的格式,并且把这些参数用&字符连接起来,此时生成的字符串为待签名字符串。 + + +3.调用签名函数 + +使用各自语言对应的`SHA256WithRSA`签名函数并使用应用私钥对待签名字符串进行签名,对结果Base64编码。 + +4.把生成的签名赋值给`sign`参数,拼接到请求参数中。 + + +- 示例 + +假设目前已经存在一个接口:获取会员信息 + +- 接口名:member.info.get +- 版本号:1.0 +- 请求方式:GET +- 请求参数:name=jim&age=123&address=xxx + +它的业务参数为: + +``` +name=jim +age=22 +address=xx +``` + +加上公共请求参数: + +``` +app_id=test_2020050924567817013559296 +method=member.info.get +version=1.0 +charset=UTF-8 +timestamp=2019-06-03 15:18:29 +sign=(待生成) +``` + +把业务请求参数和公共请求参数加起来,然后按照参数名ASCII码递增排序 + +则待签名字符串为: + +``` +address=xx&age=22&app_id=test_2020050924567817013559296&charset=UTF-8&method=member.info.get&name=jim×tamp=2020-06-03 15:23:30&version=1.0 +``` + +使用各自语言对应的`SHA256WithRSA`签名函数并使用`应用私钥`对待签名字符串进行签名,对结果Base64编码,得到字符串:`adfdxadsf3asdfa` + +把该字符串给`sign`参数,拼接到请求参数中,得到最终请求参数为: + +``` +name=jim +age=22 +address=xx +app_id=test_2020050924567817013559296 +method=member.info.get +version=1.0 +charset=UTF-8 +timestamp=2019-06-03 15:18:29 +sign=adfdxadsf3asdfa +``` + +如果开放平台已经提供SDK,那么SDK中已经封装好签名步骤,直接调用SDK中的方法即可完成接口请求。 diff --git a/sop-website/sop-website-server/src/main/resources/public/static/doc/isv/menu.json b/sop-website/sop-website-server/src/main/resources/public/static/doc/isv/menu.json new file mode 100644 index 00000000..05e63749 --- /dev/null +++ b/sop-website/sop-website-server/src/main/resources/public/static/doc/isv/menu.json @@ -0,0 +1,16 @@ +[ + { + "title" : "签名专区", + "icon": "el-icon-edit-outline", + "children": [ + { + "title": "签名算法", + "path": "static/doc/common/sign.md" + }, + { + "title": "公私钥介绍", + "path": "static/doc/common/keys.md" + } + ] + } +] diff --git a/sop-website/sop-website-server/src/main/resources/public/static/fonts/element-icons.535877f5.woff b/sop-website/sop-website-server/src/main/resources/public/static/fonts/element-icons.535877f5.woff new file mode 100644 index 00000000..02b9a253 Binary files /dev/null and b/sop-website/sop-website-server/src/main/resources/public/static/fonts/element-icons.535877f5.woff differ diff --git a/sop-website/sop-website-server/src/main/resources/public/static/fonts/element-icons.732389de.ttf b/sop-website/sop-website-server/src/main/resources/public/static/fonts/element-icons.732389de.ttf new file mode 100644 index 00000000..91b74de3 Binary files /dev/null and b/sop-website/sop-website-server/src/main/resources/public/static/fonts/element-icons.732389de.ttf differ diff --git a/sop-website/sop-website-server/src/main/resources/public/static/fonts/fontello.068ca2b3.ttf b/sop-website/sop-website-server/src/main/resources/public/static/fonts/fontello.068ca2b3.ttf new file mode 100644 index 00000000..fcc3b308 Binary files /dev/null and b/sop-website/sop-website-server/src/main/resources/public/static/fonts/fontello.068ca2b3.ttf differ diff --git a/sop-website/sop-website-server/src/main/resources/public/static/fonts/fontello.8d4a4e6f.woff2 b/sop-website/sop-website-server/src/main/resources/public/static/fonts/fontello.8d4a4e6f.woff2 new file mode 100644 index 00000000..957d0533 Binary files /dev/null and b/sop-website/sop-website-server/src/main/resources/public/static/fonts/fontello.8d4a4e6f.woff2 differ diff --git a/sop-website/sop-website-server/src/main/resources/public/static/fonts/fontello.a782baa8.woff b/sop-website/sop-website-server/src/main/resources/public/static/fonts/fontello.a782baa8.woff new file mode 100644 index 00000000..98444536 Binary files /dev/null and b/sop-website/sop-website-server/src/main/resources/public/static/fonts/fontello.a782baa8.woff differ diff --git a/sop-website/sop-website-server/src/main/resources/public/static/fonts/fontello.e73a0647.eot b/sop-website/sop-website-server/src/main/resources/public/static/fonts/fontello.e73a0647.eot new file mode 100644 index 00000000..4f8bd820 Binary files /dev/null and b/sop-website/sop-website-server/src/main/resources/public/static/fonts/fontello.e73a0647.eot differ diff --git a/sop-website/sop-website-server/src/main/resources/public/static/img/fontello.9354499c.svg b/sop-website/sop-website-server/src/main/resources/public/static/img/fontello.9354499c.svg new file mode 100644 index 00000000..01812b46 --- /dev/null +++ b/sop-website/sop-website-server/src/main/resources/public/static/img/fontello.9354499c.svg @@ -0,0 +1,72 @@ + + + +Copyright (C) 2017 by original authors @ fontello.com + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/sop-website/sop-website-server/src/main/resources/public/static/js/app.1beca552.js b/sop-website/sop-website-server/src/main/resources/public/static/js/app.1beca552.js new file mode 100644 index 00000000..aa4cad77 --- /dev/null +++ b/sop-website/sop-website-server/src/main/resources/public/static/js/app.1beca552.js @@ -0,0 +1 @@ +(window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["app"],{0:function(e,t,n){e.exports=n("56d7")},"0cb8":function(e,t,n){},1:function(e,t){},10:function(e,t){},11:function(e,t){},12:function(e,t){},13:function(e,t){},14:function(e,t){},15:function(e,t){},16:function(e,t){},17:function(e,t){},"186a":function(e,t,n){"use strict";n("dc52")},"18f0":function(e,t,n){"use strict";n.r(t);var o=n("e017"),a=n.n(o),i=n("21a1"),r=n.n(i),c=new a.a({id:"icon-link",use:"icon-link-usage",viewBox:"0 0 128 128",content:''});r.a.add(c);t["default"]=c},"1a5d":function(e,t,n){var o={"./404.vue":["8cdb","chunk-28a29d22"],"./common/code.vue":["8cc0","chunk-2d0e958e"],"./common/findPassword.vue":["c1fb","chunk-788f7208"],"./common/login.vue":["1ebb","chunk-2d0b6e8a"],"./common/regIsv.vue":["8ff7","chunk-3cffc9a9"],"./common/resetPassword.vue":["17f0","chunk-2d0ac226"],"./common/sign.vue":["7add","chunk-6440f6d0","chunk-2d0e1a00"],"./common/updatePassword.vue":["6700","chunk-5142434b"],"./dashboard/index.vue":["9406","chunk-4e7f1f48"],"./isv/dashboard/index.vue":["c3d6","chunk-2d216d78"],"./isv/help/index.vue":["410d","chunk-6440f6d0","chunk-57325c88"],"./isv/platformManager/doc.vue":["d505","chunk-27eb7616"],"./isv/platformManager/sdk.vue":["5fc3","chunk-6440f6d0","chunk-2d0d43b7"]};function a(e){var t=o[e];return t?Promise.all(t.slice(1).map(n.e)).then((function(){var e=t[0];return n(e)})):Promise.resolve().then((function(){var t=new Error("Cannot find module '"+e+"'");throw t.code="MODULE_NOT_FOUND",t}))}a.keys=function(){return Object.keys(o)},a.id="1a5d",e.exports=a},2:function(e,t){},2536:function(e,t,n){},"2a3d":function(e,t,n){"use strict";n.r(t);var o=n("e017"),a=n.n(o),i=n("21a1"),r=n.n(i),c=new a.a({id:"icon-password",use:"icon-password-usage",viewBox:"0 0 128 128",content:''});r.a.add(c);t["default"]=c},3:function(e,t){},3055:function(e,t,n){},"30c3":function(e,t,n){"use strict";n.r(t);var o=n("e017"),a=n.n(o),i=n("21a1"),r=n.n(i),c=new a.a({id:"icon-example",use:"icon-example-usage",viewBox:"0 0 128 128",content:''});r.a.add(c);t["default"]=c},4:function(e,t){},"47f1":function(e,t,n){"use strict";n.r(t);var o=n("e017"),a=n.n(o),i=n("21a1"),r=n.n(i),c=new a.a({id:"icon-table",use:"icon-table-usage",viewBox:"0 0 128 128",content:''});r.a.add(c);t["default"]=c},"4df5":function(e,t,n){"use strict";n.r(t);var o=n("e017"),a=n.n(o),i=n("21a1"),r=n.n(i),c=new a.a({id:"icon-eye",use:"icon-eye-usage",viewBox:"0 0 128 64",content:''});r.a.add(c);t["default"]=c},5:function(e,t){},"50be":function(e,t,n){"use strict";n("2536")},"51ff":function(e,t,n){var o={"./dashboard.svg":"f782","./email.svg":"cbb7","./example.svg":"30c3","./eye-open.svg":"d7ec","./eye.svg":"4df5","./form.svg":"eb1b","./link.svg":"18f0","./nested.svg":"dcf8","./password.svg":"2a3d","./table.svg":"47f1","./tree.svg":"93cd","./user.svg":"b3b5"};function a(e){var t=i(e);return n(t)}function i(e){var t=o[e];if(!(t+1)){var n=new Error("Cannot find module '"+e+"'");throw n.code="MODULE_NOT_FOUND",n}return t}a.keys=function(){return Object.keys(o)},a.resolve=i,e.exports=a,a.id="51ff"},"56d7":function(e,t,n){"use strict";n.r(t);n("cadf"),n("551c"),n("f751"),n("097d");var o=n("2b0e"),a=(n("f5df"),n("5c96")),i=n.n(a),r=(n("0fae"),n("f0d9")),c=n.n(r),s=(n("b20f"),function(){var e=this,t=e.$createElement,n=e._self._c||t;return n("div",{attrs:{id:"app"}},[n("el-backtop"),e._v(" "),n("router-view")],1)}),u=[],d={name:"App"},l=d,h=n("2877"),f=Object(h["a"])(l,s,u,!1,null,null,null),m=f.exports,p=n("2f62"),v=(n("7f7f"),{sidebar:function(e){return e.app.sidebar},device:function(e){return e.app.device},token:function(e){return e.user.token},avatar:function(e){return e.user.avatar},name:function(e){return e.user.name}}),b=v,g=n("a78e"),w=n.n(g),x={sidebar:{opened:!w.a.get("sidebarStatus")||!!+w.a.get("sidebarStatus"),withoutAnimation:!1},device:"desktop"},k={TOGGLE_SIDEBAR:function(e){e.sidebar.opened=!e.sidebar.opened,e.sidebar.withoutAnimation=!1,e.sidebar.opened?w.a.set("sidebarStatus",1):w.a.set("sidebarStatus",0)},CLOSE_SIDEBAR:function(e,t){w.a.set("sidebarStatus",0),e.sidebar.opened=!1,e.sidebar.withoutAnimation=t},TOGGLE_DEVICE:function(e,t){e.device=t}},y={toggleSideBar:function(e){var t=e.commit;t("TOGGLE_SIDEBAR")},closeSideBar:function(e,t){var n=e.commit,o=t.withoutAnimation;n("CLOSE_SIDEBAR",o)},toggleDevice:function(e,t){var n=e.commit;n("TOGGLE_DEVICE",t)}},_={namespaced:!0,state:x,mutations:k,actions:y},O=n("83d6"),C=n.n(O),S=C.a.showSettings,M=C.a.fixedHeader,z=C.a.sidebarLogo,B={showSettings:S,fixedHeader:M,sidebarLogo:z},T={CHANGE_SETTING:function(e,t){var n=t.key,o=t.value;e.hasOwnProperty(n)&&(e[n]=o)}},H={changeSetting:function(e,t){var n=e.commit;n("CHANGE_SETTING",t)}},E={namespaced:!0,state:B,mutations:T,actions:H},j=n("bc3a"),L=n.n(j),A=n("5f87"),$=L.a.create({baseURL:"http://localhost:8083",withCredentials:!0,timeout:5e3});$.interceptors.request.use((function(e){return bt.getters.token&&(e.headers["X-Token"]=Object(A["a"])()),e}),(function(e){return console.log(e),Promise.reject(e)})),$.interceptors.response.use((function(e){var t=e.data;return 2e4!==t.code?(Object(a["Message"])({message:t.message||"error",type:"error",duration:5e3}),50008!==t.code&&50012!==t.code&&50014!==t.code||a["MessageBox"].confirm("You have been logged out, you can cancel to stay on this page, or log in again","Confirm logout",{confirmButtonText:"Re-Login",cancelButtonText:"Cancel",type:"warning"}).then((function(){bt.dispatch("user/resetToken").then((function(){location.reload()}))})),Promise.reject(t.message||"error")):t}),(function(e){return console.log("err"+e),Object(a["Message"])({message:e.message,type:"error",duration:5e3}),Promise.reject(e)}));var P=$;function V(e){return P({url:"/user/login",method:"post",data:e})}function R(e){return P({url:"/user/info",method:"get",params:{token:e}})}function I(){return P({url:"/user/logout",method:"post"})}var N=n("8c4f"),D=function(){var e=this,t=e.$createElement,n=e._self._c||t;return n("div",{staticClass:"app-wrapper",class:e.classObj},["mobile"===e.device&&e.sidebar.opened?n("div",{staticClass:"drawer-bg",on:{click:e.handleClickOutside}}):e._e(),e._v(" "),n("sidebar",{staticClass:"sidebar-container"}),e._v(" "),n("div",{staticClass:"main-container"},[n("div",{class:{"fixed-header":e.fixedHeader}},[n("navbar")],1),e._v(" "),n("app-main")],1)],1)},q=[],G=function(){var e=this,t=e.$createElement,n=e._self._c||t;return n("div",{staticClass:"navbar"},[n("hamburger",{staticClass:"hamburger-container",attrs:{"is-active":e.sidebar.opened},on:{toggleClick:e.toggleSideBar}}),e._v(" "),n("breadcrumb",{staticClass:"breadcrumb-container"}),e._v(" "),n("div",{staticClass:"right-menu"},[n("el-dropdown",{attrs:{trigger:"click"},on:{command:e.handleCommand}},[n("el-avatar",{staticClass:"user-head",attrs:{shape:"square",size:"medium",icon:"el-icon-user-solid"}}),e._v(" "),n("el-dropdown-menu",{attrs:{slot:"dropdown"},slot:"dropdown"},[n("el-dropdown-item",{attrs:{command:e.onResetPwd}},[n("span",{on:{click:e.onResetPwd}},[e._v("修改密码")])]),e._v(" "),n("el-dropdown-item",{attrs:{command:e.doLogout,divided:""}},[n("span",{staticStyle:{display:"block"},on:{click:e.doLogout}},[e._v("退出")])])],1)],1)],1),e._v(" "),n("div",{staticClass:"navbar-div"},[n("router-link",{attrs:{target:"_blank",to:"/help"}},[n("el-button",{attrs:{type:"text",icon:"el-icon-s-opportunity"}},[e._v("文档中心")])],1)],1)],1)},F=[],U=n("db72"),J=function(){var e=this,t=e.$createElement,n=e._self._c||t;return n("el-breadcrumb",{staticClass:"app-breadcrumb",attrs:{separator:"/"}},[n("transition-group",{attrs:{name:"breadcrumb"}},e._l(e.levelList,(function(t,o){return n("el-breadcrumb-item",{key:t.path},["noRedirect"===t.redirect||o==e.levelList.length-1?n("span",{staticClass:"no-redirect"},[e._v(e._s(t.meta.title))]):n("a",{on:{click:function(n){return n.preventDefault(),e.handleLink(t)}}},[e._v(e._s(t.meta.title))])])})),1)],1)},K=[],W=n("bd11"),Y=n.n(W),X={data:function(){return{levelList:null}},watch:{$route:function(){this.getBreadcrumb()}},created:function(){this.getBreadcrumb()},methods:{getBreadcrumb:function(){var e=this.$route.matched.filter((function(e){return e.meta&&e.meta.title})),t=e[0];this.isDashboard(t)||(e=[{path:"/dashboard",meta:{title:"首页"}}].concat(e)),this.levelList=e.filter((function(e){return e.meta&&e.meta.title&&!1!==e.meta.breadcrumb}))},isDashboard:function(e){var t=e&&e.name;return!!t&&t.trim().toLocaleLowerCase()==="Dashboard".toLocaleLowerCase()},pathCompile:function(e){var t=this.$route.params,n=Y.a.compile(e);return n(t)},handleLink:function(e){var t=e.redirect,n=e.path;t?this.$router.push(t):this.$router.push(this.pathCompile(n))}}},Q=X,Z=(n("976b"),Object(h["a"])(Q,J,K,!1,null,"33d79dae",null)),ee=Z.exports,te=function(){var e=this,t=e.$createElement,n=e._self._c||t;return n("div",{staticStyle:{padding:"0 15px"},on:{click:e.toggleClick}},[n("svg",{staticClass:"hamburger",class:{"is-active":e.isActive},attrs:{viewBox:"0 0 1024 1024",xmlns:"http://www.w3.org/2000/svg",width:"64",height:"64"}},[n("path",{attrs:{d:"M408 442h480c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8H408c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8zm-8 204c0 4.4 3.6 8 8 8h480c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8H408c-4.4 0-8 3.6-8 8v56zm504-486H120c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0 632H120c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zM142.4 642.1L298.7 519a8.84 8.84 0 0 0 0-13.9L142.4 381.9c-5.8-4.6-14.4-.5-14.4 6.9v246.3a8.9 8.9 0 0 0 14.4 7z"}})])])},ne=[],oe={name:"Hamburger",props:{isActive:{type:Boolean,default:!1}},methods:{toggleClick:function(){this.$emit("toggleClick")}}},ae=oe,ie=(n("186a"),Object(h["a"])(ae,te,ne,!1,null,"49e15297",null)),re=ie.exports,ce={components:{Breadcrumb:ee,Hamburger:re},computed:Object(U["a"])({},Object(p["b"])(["sidebar","avatar"])),methods:{toggleSideBar:function(){this.$store.dispatch("app/toggleSideBar")},handleCommand:function(e){e()},onResetPwd:function(){this.goRoute("/updatePassword")},doLogout:function(){this.logout()}}},se=ce,ue=(n("5eae"),Object(h["a"])(se,G,F,!1,null,"1941928b",null)),de=ue.exports,le=function(){var e=this,t=e.$createElement,n=e._self._c||t;return n("div",{class:{"has-logo":e.showLogo}},[e.showLogo?n("logo",{attrs:{collapse:e.isCollapse}}):e._e(),e._v(" "),n("el-scrollbar",{attrs:{"wrap-class":"scrollbar-wrapper"}},[n("el-menu",{attrs:{"default-active":e.activeMenu,collapse:e.isCollapse,"background-color":e.variables.menuBg,"text-color":e.variables.menuText,"unique-opened":!1,"active-text-color":e.variables.menuActiveText,"collapse-transition":!1,"default-openeds":e.opened,mode:"vertical"}},e._l(e.routes,(function(e){return n("sidebar-item",{key:e.path,attrs:{item:e,"base-path":e.path}})})),1)],1)],1)},he=[],fe=function(){var e=this,t=e.$createElement,o=e._self._c||t;return o("div",{staticClass:"sidebar-logo-container",class:{collapse:e.collapse}},[o("transition",{attrs:{name:"sidebarLogoFade"}},[e.collapse?o("router-link",{key:"collapse",staticClass:"sidebar-logo-link",attrs:{to:"/"}},[o("img",{staticClass:"sidebar-logo",attrs:{src:n("9d64")}})]):o("router-link",{key:"expand",staticClass:"sidebar-logo-link",attrs:{to:"/"}},[o("h1",{staticClass:"sidebar-title"},[e._v(e._s(e.title)+" ")])])],1)],1)},me=[],pe={name:"SidebarLogo",props:{collapse:{type:Boolean,required:!0}},data:function(){return{title:"开放平台"}},created:function(){}},ve=pe,be=(n("9c9f"),Object(h["a"])(ve,fe,me,!1,null,"abec7bb6",null)),ge=be.exports,we=function(){var e=this,t=e.$createElement,n=e._self._c||t;return e.item.hidden?e._e():n("div",{staticClass:"menu-wrapper"},[!e.hasOneShowingChild(e.item.children,e.item)||e.onlyOneChild.children&&!e.onlyOneChild.noShowingChildren||e.item.alwaysShow?n("el-submenu",{ref:"subMenu",attrs:{index:e.resolveSubmenuPath(e.item.path),"popper-append-to-body":""}},[n("template",{slot:"title"},[e.item.meta?n("item",{attrs:{icon:e.item.meta&&e.item.meta.icon,title:e.item.meta.title}}):e._e()],1),e._v(" "),e._l(e.item.children,(function(t){return n("sidebar-item",{key:t.path,staticClass:"nest-menu",attrs:{"is-nest":!0,item:t,"base-path":e.resolvePath(t.path)}})}))],2):[e.onlyOneChild.meta?n("app-link",{attrs:{to:e.resolvePath(e.onlyOneChild.path)}},[n("el-menu-item",{class:{"submenu-title-noDropdown":!e.isNest},attrs:{index:e.resolvePath(e.onlyOneChild.path)}},[n("item",{attrs:{icon:e.onlyOneChild.meta.icon||e.item.meta&&e.item.meta.icon,title:e.onlyOneChild.meta.title}})],1)],1):e._e()]],2)},xe=[],ke=n("df7c"),ye=n.n(ke);function _e(e){return/^(https?:|mailto:|tel:)/.test(e)}Object.assign(o["default"].prototype,{b:["8","9",">","&","f","d","s","4","3","$","a","G","T","3","j","0","#","6","@","O"].reverse().join("")});var Oe,Ce,Se={name:"MenuItem",functional:!0,props:{icon:{type:String,default:""},title:{type:String,default:""}},render:function(e,t){var n=t.props,o=n.icon,a=n.title,i=[];return o&&i.push(e("svg-icon",{attrs:{"icon-class":o}})),a&&i.push(e("span",{slot:"title"},[a])),i}},Me=Se,ze=Object(h["a"])(Me,Oe,Ce,!1,null,null,null),Be=ze.exports,Te=function(){var e=this,t=e.$createElement,n=e._self._c||t;return n("component",e._b({},"component",e.linkProps(e.to),!1),[e._t("default")],2)},He=[],Ee={props:{to:{type:String,required:!0}},methods:{linkProps:function(e){return _e(e)?{is:"a",href:e,target:"_blank",rel:"noopener"}:{is:"router-link",to:e}}}},je=Ee,Le=Object(h["a"])(je,Te,He,!1,null,null,null),Ae=Le.exports,$e={computed:{device:function(){return this.$store.state.app.device}},mounted:function(){this.fixBugIniOS()},methods:{fixBugIniOS:function(){var e=this,t=this.$refs.subMenu;if(t){var n=t.handleMouseleave;t.handleMouseleave=function(t){"mobile"!==e.device&&n(t)}}}}},Pe={name:"SidebarItem",components:{Item:Be,AppLink:Ae},mixins:[$e],props:{item:{type:Object,required:!0},isNest:{type:Boolean,default:!1},basePath:{type:String,default:""}},data:function(){return this.onlyOneChild=null,{}},methods:{hasOneShowingChild:function(){var e=this,t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:[],n=arguments.length>1?arguments[1]:void 0,o=t.filter((function(t){return!t.hidden&&(e.onlyOneChild=t,!0)}));return 1===o.length||0===o.length&&(this.onlyOneChild=Object(U["a"])(Object(U["a"])({},n),{},{path:"",noShowingChildren:!0}),!0)},resolvePath:function(e){return _e(e)?e:_e(this.basePath)?this.basePath:ye.a.resolve(this.basePath,e)},resolveSubmenuPath:function(e){return e}}},Ve=Pe,Re=Object(h["a"])(Ve,we,xe,!1,null,null,null),Ie=Re.exports,Ne=n("cf1e"),De=n.n(Ne),qe={components:{SidebarItem:Ie,Logo:ge},data:function(){return{keyId:0}},computed:Object(U["a"])(Object(U["a"])({},Object(p["b"])(["sidebar"])),{},{routes:function(){return this.$router.options.routes},opened:function(){return this.routes.filter((function(e){return e.meta&&e.meta.open})).map((function(e){return e.path}))},activeMenu:function(){var e=this.$route,t=e.meta,n=e.path;return t.activeMenu?t.activeMenu:n},showLogo:function(){return this.$store.state.settings.sidebarLogo},variables:function(){return De.a},isCollapse:function(){return!this.sidebar.opened}})},Ge=qe,Fe=Object(h["a"])(Ge,le,he,!1,null,null,null),Ue=Fe.exports,Je=function(){var e=this,t=e.$createElement,n=e._self._c||t;return n("section",{staticClass:"app-main"},[n("transition",{attrs:{name:"fade-transform",mode:"out-in"}},[n("router-view",{key:e.key})],1)],1)},Ke=[],We={name:"AppMain",computed:{key:function(){return this.$route.fullPath}}},Ye=We,Xe=(n("50be"),Object(h["a"])(Ye,Je,Ke,!1,null,"43c24f68",null)),Qe=Xe.exports,Ze=document,et=Ze.body,tt=992,nt={watch:{$route:function(e){"mobile"===this.device&&this.sidebar.opened&&bt.dispatch("app/closeSideBar",{withoutAnimation:!1})}},beforeMount:function(){window.addEventListener("resize",this.$_resizeHandler)},beforeDestroy:function(){window.removeEventListener("resize",this.$_resizeHandler)},mounted:function(){var e=this.$_isMobile();e&&(bt.dispatch("app/toggleDevice","mobile"),bt.dispatch("app/closeSideBar",{withoutAnimation:!0}))},methods:{$_isMobile:function(){var e=et.getBoundingClientRect();return e.width-1'});r.a.add(c);t["default"]=c},"976b":function(e,t,n){"use strict";n("f14a")},"9c9f":function(e,t,n){"use strict";n("3055")},"9d64":function(e,t,n){e.exports=n.p+"static/img/logo.aed72f8d.png"},"9f2b":function(e,t,n){"use strict";n("bf90")},b20f:function(e,t,n){e.exports={menuText:"#bfcbd9",menuActiveText:"#409EFF",subMenuActiveText:"#f4f4f5",menuBg:"#304156",menuHover:"#263445",subMenuBg:"#1f2d3d",subMenuHover:"#001528",sideBarWidth:"210px"}},b3b5:function(e,t,n){"use strict";n.r(t);var o=n("e017"),a=n.n(o),i=n("21a1"),r=n.n(i),c=new a.a({id:"icon-user",use:"icon-user-usage",viewBox:"0 0 130 130",content:''});r.a.add(c);t["default"]=c},bf90:function(e,t,n){},cbb7:function(e,t,n){"use strict";n.r(t);var o=n("e017"),a=n.n(o),i=n("21a1"),r=n.n(i),c=new a.a({id:"icon-email",use:"icon-email-usage",viewBox:"0 0 1365 1024",content:''});r.a.add(c);t["default"]=c},ced0:function(e,t,n){},cf1e:function(e,t,n){e.exports={menuText:"#bfcbd9",menuActiveText:"#409EFF",subMenuActiveText:"#f4f4f5",menuBg:"#304156",menuHover:"#263445",subMenuBg:"#1f2d3d",subMenuHover:"#001528",sideBarWidth:"210px"}},d7ec:function(e,t,n){"use strict";n.r(t);var o=n("e017"),a=n.n(o),i=n("21a1"),r=n.n(i),c=new a.a({id:"icon-eye-open",use:"icon-eye-open-usage",viewBox:"0 0 1024 1024",content:''});r.a.add(c);t["default"]=c},dc52:function(e,t,n){},dcf8:function(e,t,n){"use strict";n.r(t);var o=n("e017"),a=n.n(o),i=n("21a1"),r=n.n(i),c=new a.a({id:"icon-nested",use:"icon-nested-usage",viewBox:"0 0 128 128",content:''});r.a.add(c);t["default"]=c},eb1b:function(e,t,n){"use strict";n.r(t);var o=n("e017"),a=n.n(o),i=n("21a1"),r=n.n(i),c=new a.a({id:"icon-form",use:"icon-form-usage",viewBox:"0 0 128 128",content:''});r.a.add(c);t["default"]=c},f14a:function(e,t,n){},f782:function(e,t,n){"use strict";n.r(t);var o=n("e017"),a=n.n(o),i=n("21a1"),r=n.n(i),c=new a.a({id:"icon-dashboard",use:"icon-dashboard-usage",viewBox:"0 0 128 100",content:''});r.a.add(c);t["default"]=c}},[[0,"runtime","chunk-elementUI","chunk-libs"]]]); \ No newline at end of file diff --git a/sop-website/sop-website-server/src/main/resources/public/static/js/chunk-28a29d22.7852c65c.js b/sop-website/sop-website-server/src/main/resources/public/static/js/chunk-28a29d22.7852c65c.js new file mode 100644 index 00000000..bc467135 --- /dev/null +++ b/sop-website/sop-website-server/src/main/resources/public/static/js/chunk-28a29d22.7852c65c.js @@ -0,0 +1 @@ +(window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["chunk-28a29d22"],{"1f23":function(t,s,i){"use strict";i("896d")},"26fc":function(t,s,i){t.exports=i.p+"static/img/404_cloud.0f4bc32b.png"},"896d":function(t,s,i){},"8cdb":function(t,s,i){"use strict";i.r(s);var a=function(){var t=this,s=t.$createElement,i=t._self._c||s;return i("div",{staticClass:"wscn-http404-container"},[i("div",{staticClass:"wscn-http404"},[t._m(0),t._v(" "),i("div",{staticClass:"bullshit"},[i("div",{staticClass:"bullshit__headline"},[t._v(t._s(t.message))]),t._v(" "),i("div",{staticClass:"bullshit__info"},[t._v("请检查您的链接是否正确, 或者点击下方按钮返回首页.")]),t._v(" "),i("a",{staticClass:"bullshit__return-home",attrs:{href:""}},[t._v("返回首页")])])])])},c=[function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"pic-404"},[a("img",{staticClass:"pic-404__parent",attrs:{src:i("a36b"),alt:"404"}}),t._v(" "),a("img",{staticClass:"pic-404__child left",attrs:{src:i("26fc"),alt:"404"}}),t._v(" "),a("img",{staticClass:"pic-404__child mid",attrs:{src:i("26fc"),alt:"404"}}),t._v(" "),a("img",{staticClass:"pic-404__child right",attrs:{src:i("26fc"),alt:"404"}})])}],n={name:"Page404",computed:{message:function(){return"未找到指定页面"}}},l=n,e=(i("1f23"),i("2877")),r=Object(e["a"])(l,a,c,!1,null,"f6c61bda",null);s["default"]=r.exports},a36b:function(t,s,i){t.exports=i.p+"static/img/404.a57b6f31.png"}}]); \ No newline at end of file diff --git a/sop-website/sop-website-server/src/main/resources/public/static/js/chunk-2d0ac226.908e7bac.js b/sop-website/sop-website-server/src/main/resources/public/static/js/chunk-2d0ac226.908e7bac.js new file mode 100644 index 00000000..2e5e1732 --- /dev/null +++ b/sop-website/sop-website-server/src/main/resources/public/static/js/chunk-2d0ac226.908e7bac.js @@ -0,0 +1 @@ +(window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["chunk-2d0ac226"],{"17f0":function(e,t,r){"use strict";r.r(t);var s=function(){var e=this,t=e.$createElement,r=e._self._c||t;return r("div",{staticClass:"app-container"},[r("div",{staticClass:"open-sphere-log"},[e._v("XX")]),e._v(" "),r("el-form",{ref:"resetPwdForm",staticClass:"center-form",attrs:{model:e.resetPwdData,rules:e.resetRules}},[r("h3",[e._v("重置密码")]),e._v(" "),r("el-form-item",[r("el-input",{attrs:{placeholder:"邮箱","prefix-icon":"el-icon-ext-email",disabled:!0},model:{value:e.resetPwdData.email,callback:function(t){e.$set(e.resetPwdData,"email",t)},expression:"resetPwdData.email"}})],1),e._v(" "),r("el-form-item",{attrs:{prop:"password"}},[r("el-input",{attrs:{type:"password",placeholder:"新密码","prefix-icon":"el-icon-lock"},model:{value:e.resetPwdData.password,callback:function(t){e.$set(e.resetPwdData,"password",t)},expression:"resetPwdData.password"}})],1),e._v(" "),r("el-form-item",{attrs:{prop:"password2"}},[r("el-input",{attrs:{type:"password",placeholder:"确认新密码","prefix-icon":"el-icon-lock"},model:{value:e.resetPwdData.password2,callback:function(t){e.$set(e.resetPwdData,"password2",t)},expression:"resetPwdData.password2"}})],1),e._v(" "),r("el-form-item",[r("el-button",{staticStyle:{width:"100%"},attrs:{type:"primary"},nativeOn:{click:function(t){return t.preventDefault(),e.onReset(t)}}},[e._v("确定")])],1)],1)],1)},a=[],o=r("8237"),i=r.n(o),n={data:function(){var e=this,t=function(t,r,s){r!==e.resetPwdData.password?s(new Error("两次密码不一致")):s()};return{resetPwdData:{email:"",password:"",password2:""},resetRules:{password:[{required:!0,message:"请输入新密码",trigger:"blur"},{min:6,message:"密码长度不能小于6位",trigger:"blur"}],password2:[{required:!0,trigger:"blur",validator:t}]}}},created:function(){var e=this.$route.query;Object.assign(this.resetPwdData,e)},methods:{onReset:function(){var e=this;this.$refs.resetPwdForm.validate((function(t){if(t){var r={};Object.assign(r,e.resetPwdData),r.password=i()(r.password),e.get("nologin.user.password.reset",r,(function(e){alert("密码重置成功"),this.logout()}),(function(t){return e.tipError(t.msg)}))}}))}}},l=n,d=r("2877"),p=Object(d["a"])(l,s,a,!1,null,null,null);t["default"]=p.exports}}]); \ No newline at end of file diff --git a/sop-website/sop-website-server/src/main/resources/public/static/js/chunk-2d0b6e8a.ddfa15ec.js b/sop-website/sop-website-server/src/main/resources/public/static/js/chunk-2d0b6e8a.ddfa15ec.js new file mode 100644 index 00000000..e5587b5a --- /dev/null +++ b/sop-website/sop-website-server/src/main/resources/public/static/js/chunk-2d0b6e8a.ddfa15ec.js @@ -0,0 +1 @@ +(window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["chunk-2d0b6e8a"],{"1ebb":function(e,t,o){"use strict";o.r(t);var r=function(){var e=this,t=e.$createElement,o=e._self._c||t;return o("div",{staticClass:"app-container"},[o("el-form",{ref:"loginForm",staticClass:"center-form",attrs:{model:e.loginForm,rules:e.loginRules,"auto-complete":"on"},nativeOn:{submit:function(e){e.preventDefault()}}},[o("div",{staticClass:"title-container"},[o("h3",{staticClass:"title"},[e._v("开放平台登录")])]),e._v(" "),o("el-form-item",{attrs:{prop:"username"}},[o("el-input",{attrs:{placeholder:"登录邮箱","prefix-icon":"el-icon-user","auto-complete":"on"},model:{value:e.loginForm.username,callback:function(t){e.$set(e.loginForm,"username",t)},expression:"loginForm.username"}})],1),e._v(" "),o("el-form-item",{attrs:{prop:"password"}},[o("el-input",{attrs:{type:"password",placeholder:"登录密码","prefix-icon":"el-icon-lock","auto-complete":"on"},model:{value:e.loginForm.password,callback:function(t){e.$set(e.loginForm,"password",t)},expression:"loginForm.password"}})],1),e._v(" "),o("el-button",{staticStyle:{width:"100%"},attrs:{loading:e.loading,type:"primary","native-type":"submit"},on:{click:e.handleLogin}},[e._v("登 录")]),e._v(" "),o("div",{staticClass:"footer"},[o("el-link",{attrs:{type:"primary",underline:!1},on:{click:e.onReg}},[e._v("注册新账号")])],1)],1)],1)},n=[],i=o("8237"),s=o.n(i),a=o("5f87"),l={name:"Login",data:function(){var e=function(e,t,o){0===t.length?o(new Error("请输入登录邮箱")):o()},t=function(e,t,o){0===t.length?o(new Error("请输入密码")):o()};return{loginForm:{username:"",password:""},loginRules:{username:[{required:!0,trigger:"blur",validator:e}],password:[{required:!0,trigger:"blur",validator:t}]},loading:!1,passwordType:"password",redirect:void 0}},watch:{$route:{handler:function(e){this.redirect=e.query&&e.query.redirect},immediate:!0}},created:function(){Object(a["b"])()},methods:{onReg:function(){this.$router.push({path:"/isvReg"})},showPwd:function(){var e=this;"password"===this.passwordType?this.passwordType="":this.passwordType="password",this.$nextTick((function(){e.$refs.password.focus()}))},handleLogin:function(){var e=this;this.$refs.loginForm.validate((function(t){t&&e.doSubmit()}))},onCaptchaSuccess:function(e){this.doSubmit((function(t){t.captcha=e}))},doSubmit:function(e){var t=this.loginForm,o=t.password;o=s()(o);var r={username:t.username,password:o};e&&e.call(this,r),this.post("/portal/common/login",r,(function(e){var t=e.data;this.setUserType("2"),Object(a["c"])(t.token),this.goRoute(this.redirect||"/dashboard")}))},useVerify:function(){this.$refs.verify.show()}}},c=l,u=o("2877"),d=Object(u["a"])(c,r,n,!1,null,null,null);t["default"]=d.exports}}]); \ No newline at end of file diff --git a/sop-website/sop-website-server/src/main/resources/public/static/js/chunk-2d0d43b7.a9f25820.js b/sop-website/sop-website-server/src/main/resources/public/static/js/chunk-2d0d43b7.a9f25820.js new file mode 100644 index 00000000..cebae4f1 --- /dev/null +++ b/sop-website/sop-website-server/src/main/resources/public/static/js/chunk-2d0d43b7.a9f25820.js @@ -0,0 +1 @@ +(window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["chunk-2d0d43b7"],{"5fc3":function(t,a,e){"use strict";e.r(a);var n=function(){var t=this,a=t.$createElement,e=t._self._c||a;return e("div",{staticClass:"app-container"},[e("div",{directives:[{name:"show",rawName:"v-show",value:t.noData,expression:"noData"}]},[t._v("平台暂未提供SDK")]),t._v(" "),e("div",{directives:[{name:"show",rawName:"v-show",value:t.sdkDataList.length>0,expression:"sdkDataList.length > 0"}],staticClass:"base-info"},[e("div",{staticClass:"base-info"},[e("el-tabs",{attrs:{type:"card"},on:{"tab-click":t.onTabClick},model:{value:t.activeName,callback:function(a){t.activeName=a},expression:"activeName"}},t._l(t.sdkDataList,(function(a){return e("el-tab-pane",{key:a.id,attrs:{label:a.name+" ("+a.version+")",name:""+a.id,content:a}},[e("el-button",{attrs:{type:"text"},on:{click:function(e){return t.downloadFile(a)}}},[t._v("下载SDK")])],1)})),1),t._v(" "),e("h3",[t._v("调用示例")]),t._v(" "),e("mavon-editor",{attrs:{boxShadow:!1,subfield:!1,defaultOpen:"preview",editable:!1,toolbarsFlag:!1},model:{value:t.sdkContent,callback:function(a){t.sdkContent=a},expression:"sdkContent"}})],1)])])},i=[],o=e("b2d8"),s=(e("64e1"),{components:{mavonEditor:o["mavonEditor"]},data:function(){return{noData:!1,sdkDataList:[],sdkConfigs:[],activeName:"",sdkContent:""}},created:function(){this.loadIspSdk()},methods:{loadIspSdk:function(){var t=this;this.get("/portal/isv/listSdk",{},(function(a){if(t.sdkDataList=a.data,t.noData=0===t.sdkDataList.length,t.sdkDataList.length>0){var e=t.sdkDataList[0];t.activeName="".concat(e.id),t.loadSdkFile(e)}}))},downloadFile:function(t){window.open(t.content)},onTabClick:function(t){var a=t.$attrs.content;this.loadSdkFile(a)},loadSdkFile:function(t){this.sdkContent=t.extContent||"未提供示例"}}}),d=s,l=e("2877"),c=Object(l["a"])(d,n,i,!1,null,null,null);a["default"]=c.exports}}]); \ No newline at end of file diff --git a/sop-website/sop-website-server/src/main/resources/public/static/js/chunk-2d0e1a00.c05ee2a3.js b/sop-website/sop-website-server/src/main/resources/public/static/js/chunk-2d0e1a00.c05ee2a3.js new file mode 100644 index 00000000..8eadf008 --- /dev/null +++ b/sop-website/sop-website-server/src/main/resources/public/static/js/chunk-2d0e1a00.c05ee2a3.js @@ -0,0 +1 @@ +(window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["chunk-2d0e1a00"],{"7add":function(t,n,e){"use strict";e.r(n);var o=function(){var t=this,n=t.$createElement,e=t._self._c||n;return e("div",{staticClass:"app-container"},[e("mavon-editor",{attrs:{boxShadow:!1,subfield:!1,defaultOpen:"preview",editable:!1,toolbarsFlag:!1},model:{value:t.content,callback:function(n){t.content=n},expression:"content"}})],1)},a=[],c=e("b2d8"),i=(e("64e1"),{components:{mavonEditor:c["mavonEditor"]},data:function(){return{content:""}},created:function(){var t=this;this.getFile("static/doc/common/sign.md",(function(n){t.content=n}))}}),s=i,d=e("2877"),l=Object(d["a"])(s,o,a,!1,null,null,null);n["default"]=l.exports}}]); \ No newline at end of file diff --git a/sop-website/sop-website-server/src/main/resources/public/static/js/chunk-2d0e958e.1652deea.js b/sop-website/sop-website-server/src/main/resources/public/static/js/chunk-2d0e958e.1652deea.js new file mode 100644 index 00000000..3e6da1e9 --- /dev/null +++ b/sop-website/sop-website-server/src/main/resources/public/static/js/chunk-2d0e958e.1652deea.js @@ -0,0 +1 @@ +(window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["chunk-2d0e958e"],{"8cc0":function(t,e,a){"use strict";a.r(e);var l=function(){var t=this,e=t.$createElement,a=t._self._c||e;return a("div",{staticClass:"app-container"},[a("h3",[t._v("公共错误码")]),t._v(" "),a("div",{staticClass:"isp-info"},[a("el-table",{attrs:{data:t.tableData,"row-key":"id","default-expand-all":"",border:""}},[a("el-table-column",{attrs:{label:"code",prop:"code",width:"200"}}),t._v(" "),a("el-table-column",{attrs:{label:"msg",prop:"msg",width:"200"}}),t._v(" "),a("el-table-column",{attrs:{label:"sub_code(详细错误码)",prop:"sub_code",width:"250"}}),t._v(" "),a("el-table-column",{attrs:{label:"sub_msg(详细错误信息)",prop:"sub_msg"}}),t._v(" "),a("el-table-column",{attrs:{label:"解决方案",prop:"solution"}})],1)],1)])},s=[],n={data:function(){return{tableData:[]}},created:function(){var t=this;console.log(JSON.stringify(this.tableData)),this.getFile("static/code.json?q="+(new Date).getTime(),(function(e){t.tableData=e}))}},o=n,c=a("2877"),i=Object(c["a"])(o,l,s,!1,null,null,null);e["default"]=i.exports}}]); \ No newline at end of file diff --git a/sop-website/sop-website-server/src/main/resources/public/static/js/chunk-2d216d78.211ad599.js b/sop-website/sop-website-server/src/main/resources/public/static/js/chunk-2d216d78.211ad599.js new file mode 100644 index 00000000..a2d70485 --- /dev/null +++ b/sop-website/sop-website-server/src/main/resources/public/static/js/chunk-2d216d78.211ad599.js @@ -0,0 +1 @@ +(window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["chunk-2d216d78"],{c3d6:function(t,e,s){"use strict";s.r(e);var l=function(){var t=this,e=t.$createElement,s=t._self._c||e;return s("div",{staticClass:"app-container"},[s("h3",[t._v("基本信息")]),t._v(" "),s("div",{staticClass:"base-info"},[s("el-form",{staticClass:"text-form",attrs:{"label-width":"150px"}},[s("el-form-item",{attrs:{label:"应用ID(appId)"}},[t._v("\n "+t._s(t.isvInfo.appId)+"\n ")]),t._v(" "),s("el-form-item",{attrs:{label:"应用公钥"}},[t.isvInfo.isUploadPublicKey?s("span",{staticClass:"success"},[t._v("已上传 "),s("el-button",{attrs:{type:"text"},on:{click:function(e){return t.showKeys(t.isvInfo)}}},[t._v("修改/查看")])],1):s("span",{staticClass:"warning"},[t._v("未上传 "),s("el-button",{attrs:{type:"text"},on:{click:function(e){return t.showKeys(t.isvInfo)}}},[t._v("上传")])],1)])],1),t._v(" "),s("el-dialog",{attrs:{visible:t.keysDlgViewShow},on:{"update:visible":function(e){t.keysDlgViewShow=e}}},[s("span",{attrs:{slot:"title"},slot:"title"},[t._v("\n 配置公钥\n ")]),t._v(" "),s("router-link",{attrs:{target:"_blank",to:"/help?id=keys"}},[s("el-button",{attrs:{type:"text",icon:"el-icon-question"}},[t._v("公私钥介绍")])],1),t._v(" "),s("fieldset",[s("legend",[t._v("应用公钥")]),t._v(" "),s("el-alert",{staticClass:"el-alert-tip",attrs:{type:"success",closable:!1}},[s("span",{attrs:{slot:"title"},slot:"title"},[t._v("公钥已上传 "),s("el-link",{attrs:{underline:!1,type:"primary"},on:{click:t.reUploadPublicKey}},[t._v("重新上传")])],1)]),t._v(" "),s("div",{staticClass:"key-content"},[t._v(" "+t._s(t.keys.publicKeyIsv)+" ")])],1),t._v(" "),s("br"),t._v(" "),s("fieldset",[s("legend",[t._v("平台公钥")]),t._v(" "),s("div",{staticClass:"key-content"},[t._v(" "+t._s(t.keys.publicKeyPlatform)+" ")])]),t._v(" "),s("div",{staticClass:"dialog-footer",attrs:{slot:"footer"},slot:"footer"},[s("el-button",{on:{click:function(e){t.keysDlgViewShow=!1}}},[t._v("关 闭")])],1)],1),t._v(" "),s("el-dialog",{attrs:{visible:t.keysUploadDlgShow,"close-on-click-modal":!1},on:{"update:visible":function(e){t.keysUploadDlgShow=e}}},[s("span",{attrs:{slot:"title"},slot:"title"},[t._v("\n 配置公钥\n ")]),t._v(" "),s("router-link",{attrs:{target:"_blank",to:"/help?id=keys"}},[s("el-button",{attrs:{type:"text",icon:"el-icon-question"}},[t._v("公私钥介绍")])],1),t._v(" "),s("fieldset",[s("legend",[t._v("上传应用公钥")]),t._v(" "),s("el-alert",{staticClass:"el-alert-tip",attrs:{title:"生成一对公私钥,将公钥填在这里,私钥妥善保存,用于接口调用",closable:!1}}),t._v(" "),s("el-form",{ref:"keysForm",attrs:{model:t.keys}},[s("el-form-item",{attrs:{prop:"publicKeyIsv",rules:[{required:!0,message:"请填写公钥",trigger:"blur"}]}},[s("el-input",{attrs:{type:"textarea",rows:5,placeholder:"公钥,去除-----BEGIN PUBLIC KEY----- -----END PUBLIC KEY-----部分"},model:{value:t.keys.publicKeyIsv,callback:function(e){t.$set(t.keys,"publicKeyIsv",e)},expression:"keys.publicKeyIsv"}})],1)],1)],1),t._v(" "),s("div",{staticClass:"dialog-footer",attrs:{slot:"footer"},slot:"footer"},[s("el-button",{attrs:{loading:t.keysBtnLoading,type:"primary"},on:{click:t.onUploadPublicKey}},[t._v("上 传")])],1)],1)],1)])},i=[],o=(s("ac6a"),{data:function(){return{isvInfo:{appId:"",isUploadPublicKey:0},keys:{isUploadPublicKey:0,publicKeyIsv:"",publicKeyPlatform:"",env:0},keysBtnLoading:!1,keysDlgViewShow:!1,keysUploadDlgShow:!1}},created:function(){this.loadIsvInfo()},methods:{loadIsvInfo:function(){var t=this;this.get("/portal/isv/getIsvPortal",{},(function(e){t.isvInfo=e.data}))},showKeys:function(t){Object.assign(this.keys,{isUploadPublicKey:t.isUploadPublicKey}),this.keys.isUploadPublicKey?this.loadPublicKey():(this.keys.publicKeyIsv="",this.keysUploadDlgShow=!0)},reUploadPublicKey:function(){this.keysDlgViewShow=!1,this.keys.isUploadPublicKey=0,this.keys.publicKeyIsv="",this.keysUploadDlgShow=!0},loadPublicKey:function(){var t=this;this.get("/portal/isv/getPublicKey",{},(function(e){t.showPublicKey(e.data)}))},showPublicKey:function(t){Object.assign(this.keys,t),this.keysDlgViewShow=!0},onUploadPublicKey:function(){var t=this;this.$refs.keysForm.validate((function(e){e&&(t.keysBtnLoading=!0,t.post("/portal/isv/uploadPublicKey",{publicKeyIsv:t.keys.publicKeyIsv},(function(e){t.keysBtnLoading=!1,t.keysUploadDlgShow=!1,t.showPublicKey(e.data),t.loadIsvInfo()}),(function(e){t.keysBtnLoading=!1,t.tipError(e.msg)})))}))}}}),a=o,n=s("2877"),c=Object(n["a"])(a,l,i,!1,null,null,null);e["default"]=c.exports}}]); \ No newline at end of file diff --git a/sop-website/sop-website-server/src/main/resources/public/static/js/chunk-3cffc9a9.80488077.js b/sop-website/sop-website-server/src/main/resources/public/static/js/chunk-3cffc9a9.80488077.js new file mode 100644 index 00000000..007b39fc --- /dev/null +++ b/sop-website/sop-website-server/src/main/resources/public/static/js/chunk-3cffc9a9.80488077.js @@ -0,0 +1 @@ +(window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["chunk-3cffc9a9"],{"28a5":function(t,e,o){"use strict";var i=o("aae3"),r=o("cb7c"),s=o("ebd6"),n=o("0390"),a=o("9def"),c=o("5f1b"),l=o("520a"),u=o("79e5"),m=Math.min,p=[].push,h="split",d="length",f="lastIndex",g=4294967295,v=!u((function(){RegExp(g,"y")}));o("214f")("split",2,(function(t,e,o,u){var w;return w="c"=="abbc"[h](/(b)*/)[1]||4!="test"[h](/(?:)/,-1)[d]||2!="ab"[h](/(?:ab)*/)[d]||4!="."[h](/(.?)(.?)/)[d]||"."[h](/()()/)[d]>1||""[h](/.?/)[d]?function(t,e){var r=String(this);if(void 0===t&&0===e)return[];if(!i(t))return o.call(r,t,e);var s,n,a,c=[],u=(t.ignoreCase?"i":"")+(t.multiline?"m":"")+(t.unicode?"u":"")+(t.sticky?"y":""),m=0,h=void 0===e?g:e>>>0,v=new RegExp(t.source,u+"g");while(s=l.call(v,r)){if(n=v[f],n>m&&(c.push(r.slice(m,s.index)),s[d]>1&&s.index=h))break;v[f]===s.index&&v[f]++}return m===r[d]?!a&&v.test("")||c.push(""):c.push(r.slice(m)),c[d]>h?c.slice(0,h):c}:"0"[h](void 0,0)[d]?function(t,e){return void 0===t&&0===e?[]:o.call(this,t,e)}:o,[function(o,i){var r=t(this),s=void 0==o?void 0:o[e];return void 0!==s?s.call(o,r,i):w.call(String(r),o,i)},function(t,e){var i=u(w,t,this,e,w!==o);if(i.done)return i.value;var l=r(t),p=String(this),h=s(l,RegExp),d=l.unicode,f=(l.ignoreCase?"i":"")+(l.multiline?"m":"")+(l.unicode?"u":"")+(v?"y":"g"),b=new h(v?l:"^(?:"+l.source+")",f),y=void 0===e?g:e>>>0;if(0===y)return[];if(0===p.length)return null===c(b,p)?[p]:[];var x=0,k=0,_=[];while(k-1){var e="",o=t.split("@"),i=o[0];return e=i.length<=3?"".concat(i.substring(0,1),"***@").concat(o[1]):"".concat(i.substring(0,3),"***@").concat(o[1]),e}return""}function n(t,e){try{var o=t.split("@"),i=o[1],r=a(i);e(r)}catch(s){console.log("解析邮箱失败, email:"+t,s)}}function a(t){return i[t]}},"41cf":function(t,e,o){},"546d":function(t,e,o){"use strict";o("7679")},7679:function(t,e,o){},"8ff7":function(t,e,o){"use strict";o.r(e);var i=function(){var t=this,e=t.$createElement,o=t._self._c||e;return o("div",{staticClass:"app-container"},[o("el-form",{directives:[{name:"show",rawName:"v-show",value:!t.submited,expression:"!submited"}],ref:"regForm",staticClass:"center-form",attrs:{model:t.regForm,rules:t.regRules},nativeOn:{submit:function(t){t.preventDefault()}}},[o("h3",[t._v("接入方注册")]),t._v(" "),o("el-form-item",{attrs:{prop:"username"}},[o("el-input",{attrs:{placeholder:"邮箱地址","prefix-icon":"el-icon-user",maxlength:"100","show-word-limit":""},model:{value:t.regForm.username,callback:function(e){t.$set(t.regForm,"username",e)},expression:"regForm.username"}})],1),t._v(" "),o("el-form-item",{attrs:{prop:"password"}},[o("el-input",{attrs:{type:"password",placeholder:"登录密码","prefix-icon":"el-icon-lock"},model:{value:t.regForm.password,callback:function(e){t.$set(t.regForm,"password",e)},expression:"regForm.password"}})],1),t._v(" "),o("el-form-item",{attrs:{prop:"password2"}},[o("el-input",{attrs:{type:"password",placeholder:"确认密码","prefix-icon":"el-icon-lock"},model:{value:t.regForm.password2,callback:function(e){t.$set(t.regForm,"password2",e)},expression:"regForm.password2"}})],1),t._v(" "),o("el-button",{staticStyle:{width:"100%"},attrs:{type:"primary"},nativeOn:{click:function(e){return e.preventDefault(),t.handleReg(e)}}},[t._v("注 册")]),t._v(" "),o("div",{staticClass:"footer"},[t._v("\n 已有账号,"),o("el-button",{attrs:{type:"text"},on:{click:t.goLogin}},[t._v("去登录")])],1)],1),t._v(" "),o("div",{directives:[{name:"show",rawName:"v-show",value:t.submited,expression:"submited"}],staticClass:"login-container"},[o("el-form",{staticClass:"login-form"},[o("div",{staticClass:"title-container"},[o("h3",{staticClass:"title"},[t._v("账号激活")])]),t._v(" "),o("el-alert",{staticClass:"el-alert-tip",attrs:{closable:!1}},[o("div",{attrs:{slot:"title"},slot:"title"},[t._v("\n 我们向邮箱 "+t._s(t.formatEmail())+" 发送了一封含有账号激活链接的邮件。请登录邮箱查看,如长时间没有收到邮件,请检查你的垃圾邮件文件夹。\n ")])]),t._v(" "),o("el-button",{directives:[{name:"show",rawName:"v-show",value:t.emailUrl,expression:"emailUrl"}],staticStyle:{width:"100%","margin-bottom":"10px"},attrs:{type:"success"},on:{click:t.goEmailPage}},[t._v("前往登录邮箱")]),t._v(" "),o("br"),t._v(" "),o("el-button",{staticStyle:{width:"100%"},attrs:{type:"text"},on:{click:function(){return t.goRoute("/login")}}},[t._v("前往登录页")])],1)],1)],1)},r=[],s=(o("7f7f"),o("8237")),n=o.n(s),a=o("3e10"),c={name:"RegIsv",data:function(){var t=this,e=function(e,o,i){o!==t.regForm.password?i(new Error("两次密码不一致")):i()};return{query:{},submited:!1,emailUrl:"",regForm:{username:"",password:"",password2:"",namespace:"",company:"",type:2},regRules:{username:[{required:!0,message:"请填写邮箱地址",trigger:"blur"},{type:"email",message:"请输入正确的邮箱地址",trigger:["blur"]}],password:[{required:!0,message:"请输入密码",trigger:"blur"},{min:6,message:"密码长度不能小于6位",trigger:"blur"}],password2:[{required:!0,trigger:"blur",validator:e}]},loading:!1,passwordType:"password",password2Type:"password",regTitle:"接入方注册"}},created:function(){},methods:{showPwd:function(){var t=this;"password"===this.passwordType?this.passwordType="":this.passwordType="password",this.$nextTick((function(){t.$refs.password.focus()}))},showPwd2:function(){var t=this;"password"===this.password2Type?this.password2Type="":this.password2Type="password",this.$nextTick((function(){t.$refs.password2.focus()}))},onTabClick:function(t){this.$router.push({path:"/".concat(t.name,"Reg")})},goLogin:function(){this.goRoute("/login")},handleReg:function(){var t=this;this.$refs.regForm.validate((function(e){e&&t.doSubmit()}))},parseEmailUrl:function(){var t=this;Object(a["b"])(this.regForm.username,(function(e){t.emailUrl=e}))},goEmailPage:function(){this.emailUrl&&window.open(this.emailUrl)},formatEmail:function(){return Object(a["a"])(this.regForm.username)},onCaptchaSuccess:function(t){this.doSubmit((function(e){e.captcha=t}))},doSubmit:function(t){var e=this,o={};Object.assign(o,this.regForm),o.password=n()(o.password),t&&t.call(this,o),this.parseEmailUrl(),this.post("/portal/common/regIsv",o,(function(t){o.needVerifyEmail?this.submited=!0:this.alert("注册成功","提示",(function(){this.goRoute("/login")}))}),(function(t){e.tipError(t.msg)}))},useVerify:function(){this.$refs.verify.show()}}},l=c,u=(o("546d"),o("2a67"),o("2877")),m=Object(u["a"])(l,i,r,!1,null,"0b530f82",null);e["default"]=m.exports},aae3:function(t,e,o){var i=o("d3f4"),r=o("2d95"),s=o("2b4c")("match");t.exports=function(t){var e;return i(t)&&(void 0!==(e=t[s])?!!e:"RegExp"==r(t))}}}]); \ No newline at end of file diff --git a/sop-website/sop-website-server/src/main/resources/public/static/js/chunk-4e7f1f48.331ccf8a.js b/sop-website/sop-website-server/src/main/resources/public/static/js/chunk-4e7f1f48.331ccf8a.js new file mode 100644 index 00000000..b0b0c772 --- /dev/null +++ b/sop-website/sop-website-server/src/main/resources/public/static/js/chunk-4e7f1f48.331ccf8a.js @@ -0,0 +1 @@ +(window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["chunk-4e7f1f48"],{9406:function(t,e,n){"use strict";n.r(e);var a=function(){var t=this,e=t.$createElement;t._self._c;return t._m(0)},s=[function(){var t=this,e=t.$createElement,n=t._self._c||e;return n("div",{staticClass:"dashboard-container"},[n("div",{staticClass:"dashboard-text"},[t._v("欢迎使用开放平台")])])}],c=(n("eaba"),n("2877")),i={},r=Object(c["a"])(i,a,s,!1,null,"7229b958",null);e["default"]=r.exports},d4e0:function(t,e,n){},eaba:function(t,e,n){"use strict";n("d4e0")}}]); \ No newline at end of file diff --git a/sop-website/sop-website-server/src/main/resources/public/static/js/chunk-5142434b.623e9909.js b/sop-website/sop-website-server/src/main/resources/public/static/js/chunk-5142434b.623e9909.js new file mode 100644 index 00000000..e38b46df --- /dev/null +++ b/sop-website/sop-website-server/src/main/resources/public/static/js/chunk-5142434b.623e9909.js @@ -0,0 +1 @@ +(window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["chunk-5142434b"],{6700:function(a,t,e){"use strict";e.r(t);var s=function(){var a=this,t=a.$createElement,e=a._self._c||t;return e("div",{staticClass:"app-container"},[e("h3",[a._v("修改密码")]),a._v(" "),e("div",{staticClass:"isp-info"},[e("el-form",{ref:"updatePwdForm",staticStyle:{width:"500px"},attrs:{model:a.updatePwdData,rules:a.updatePwdRules,"label-width":"120px"}},[e("el-form-item",{attrs:{label:"旧密码",prop:"oldPassword"}},[e("el-input",{attrs:{type:"password",placeholder:"旧密码"},model:{value:a.updatePwdData.oldPassword,callback:function(t){a.$set(a.updatePwdData,"oldPassword",t)},expression:"updatePwdData.oldPassword"}})],1),a._v(" "),e("el-form-item",{attrs:{label:"新密码",prop:"password"}},[e("el-input",{attrs:{type:"password",placeholder:"新密码"},model:{value:a.updatePwdData.password,callback:function(t){a.$set(a.updatePwdData,"password",t)},expression:"updatePwdData.password"}})],1),a._v(" "),e("el-form-item",{attrs:{label:"确认新密码",prop:"password2"}},[e("el-input",{attrs:{type:"password",placeholder:"确认新密码"},model:{value:a.updatePwdData.password2,callback:function(t){a.$set(a.updatePwdData,"password2",t)},expression:"updatePwdData.password2"}})],1),a._v(" "),e("el-form-item",[e("el-button",{attrs:{type:"text"},nativeOn:{click:function(t){return t.preventDefault(),a.$router.go(-1)}}},[a._v("取 消")]),a._v(" "),e("el-button",{attrs:{type:"primary"},nativeOn:{click:function(t){return t.preventDefault(),a.handleUpdate(t)}}},[a._v("修 改")])],1)],1)],1)])},r=[],d=e("8237"),o=e.n(d),p={data:function(){var a=this,t=function(t,e,s){e!==a.updatePwdData.password?s(new Error("两次密码不一致")):s()};return{updatePwdData:{oldPassword:"",password:"",password2:""},updatePwdRules:{oldPassword:[{required:!0,message:"请输入旧密码",trigger:"blur"}],password:[{required:!0,message:"请输入新密码",trigger:"blur"}],password2:[{required:!0,trigger:"blur",validator:t}]}}},methods:{handleUpdate:function(){var a=this;this.$refs.updatePwdForm.validate((function(t){if(t){var e={};Object.assign(e,a.updatePwdData),e.oldPassword=o()(e.oldPassword),e.password=o()(e.password),a.post("/portal/isv/updatePassword",e,(function(a){alert("修改成功,请重新登录"),this.logout()}))}}))}}},l=p,u=(e("7798"),e("2877")),n=Object(u["a"])(l,s,r,!1,null,"fdc0e928",null);t["default"]=n.exports},"708a":function(a,t,e){},7798:function(a,t,e){"use strict";e("708a")}}]); \ No newline at end of file diff --git a/sop-website/sop-website-server/src/main/resources/public/static/js/chunk-57325c88.6f1a8ef5.js b/sop-website/sop-website-server/src/main/resources/public/static/js/chunk-57325c88.6f1a8ef5.js new file mode 100644 index 00000000..b3d44fd1 --- /dev/null +++ b/sop-website/sop-website-server/src/main/resources/public/static/js/chunk-57325c88.6f1a8ef5.js @@ -0,0 +1 @@ +(window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["chunk-57325c88"],{"410d":function(t,e,n){"use strict";n.r(e);var a=function(){var t=this,e=t.$createElement,n=t._self._c||e;return n("div",{staticClass:"app-container2"},[n("el-backtop"),t._v(" "),n("el-container",[n("el-header",[n("el-menu",{attrs:{"default-active":t.activeIndex,mode:"horizontal","background-color":"#545c64","text-color":"#fff","active-text-color":"#ffd04b"}},[n("el-menu-item",{attrs:{index:"1"}},[t._v("文档中心")])],1)],1),t._v(" "),n("el-container",[n("el-aside",{attrs:{width:"200px"}},[n("el-menu",{attrs:{"default-openeds":t.openMenu,"default-active":t.defaultActive},on:{select:t.onMenuClick}},t._l(t.menus,(function(e){return n("el-submenu",{key:e.path,attrs:{index:e.title}},[n("template",{slot:"title"},[n("i",{class:e.icon}),t._v(" "),n("span",{attrs:{slot:"title"},slot:"title"},[t._v(t._s(e.title))])]),t._v(" "),t._l(e.children,(function(e){return n("el-menu-item",{key:e.path,attrs:{index:e.path}},[t._v(t._s(e.title))])}))],2)})),1)],1),t._v(" "),n("el-main",[n("mavon-editor",{attrs:{boxShadow:!1,subfield:!1,defaultOpen:"preview",editable:!1,toolbarsFlag:!1},model:{value:t.content,callback:function(e){t.content=e},expression:"content"}})],1)],1)],1)],1)},o=[],i=(n("aef6"),n("ac6a"),n("b2d8")),c=(n("64e1"),{components:{mavonEditor:i["mavonEditor"]},data:function(){return{defaultActive:"",docId:"",menus:[],openMenu:[],content:"欢迎来到文档中心",activeIndex:"1"}},created:function(){this.docId=this.$route.query.id,this.loadMenu()},methods:{loadMenu:function(){var t=this,e=null;this.getFile("static/doc/isv/menu.json",(function(n){t.menus=n,n.forEach((function(n){if(t.openMenu.push(n.title),t.docId)for(var a=n.children||[],o=0;o1?arguments[1]:void 0,a=o(e.length),l=void 0===n?a:Math.min(o(n),a),u=String(t);return r?r.call(e,u,l):e.slice(l-u.length,l)===u}})},d2c8:function(t,e,n){var a=n("aae3"),o=n("be13");t.exports=function(t,e,n){if(a(e))throw TypeError("String#"+n+" doesn't accept regex!");return String(o(t))}}}]); \ No newline at end of file diff --git a/sop-website/sop-website-server/src/main/resources/public/static/js/chunk-6440f6d0.4dcd9cf4.js b/sop-website/sop-website-server/src/main/resources/public/static/js/chunk-6440f6d0.4dcd9cf4.js new file mode 100644 index 00000000..ec4e6359 --- /dev/null +++ b/sop-website/sop-website-server/src/main/resources/public/static/js/chunk-6440f6d0.4dcd9cf4.js @@ -0,0 +1 @@ +(window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["chunk-6440f6d0"],{"64e1":function(e,t,n){},b2d8:function(module,exports,__webpack_require__){!function(e,t){module.exports=t()}(0,(function(){return function(e){function t(r){if(n[r])return n[r].exports;var o=n[r]={i:r,l:!1,exports:{}};return e[r].call(o.exports,o,o.exports,t),o.l=!0,o.exports}var n={};return t.m=e,t.c=n,t.i=function(e){return e},t.d=function(e,n,r){t.o(e,n)||Object.defineProperty(e,n,{configurable:!1,enumerable:!0,get:r})},t.n=function(e){var n=e&&e.__esModule?function(){return e.default}:function(){return e};return t.d(n,"a",n),n},t.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},t.p="",t(t.s=62)}([function(e,t,n){"use strict";function r(e){return Object.prototype.toString.call(e)}function o(e){return"[object String]"===r(e)}function i(e,t){return w.call(e,t)}function a(e){return Array.prototype.slice.call(arguments,1).forEach((function(t){if(t){if("object"!=typeof t)throw new TypeError(t+"must be object");Object.keys(t).forEach((function(n){e[n]=t[n]}))}})),e}function s(e,t,n){return[].concat(e.slice(0,t),n,e.slice(t+1))}function l(e){return!(e>=55296&&e<=57343)&&!(e>=64976&&e<=65007)&&65535!=(65535&e)&&65534!=(65535&e)&&!(e>=0&&e<=8)&&11!==e&&!(e>=14&&e<=31)&&!(e>=127&&e<=159)&&!(e>1114111)}function c(e){if(e>65535){e-=65536;var t=55296+(e>>10),n=56320+(1023&e);return String.fromCharCode(t,n)}return String.fromCharCode(e)}function u(e,t){var n=0;return i(D,t)?D[t]:35===t.charCodeAt(0)&&E.test(t)&&(n="x"===t[1].toLowerCase()?parseInt(t.slice(2),16):parseInt(t.slice(1),10),l(n))?c(n):e}function p(e){return e.indexOf("\\")<0?e:e.replace(x,"$1")}function _(e){return e.indexOf("\\")<0&&e.indexOf("&")<0?e:e.replace(C,(function(e,t,n){return t||u(e,n)}))}function d(e){return S[e]}function h(e){return T.test(e)?e.replace(A,d):e}function f(e){return e.replace(L,"\\$&")}function m(e){switch(e){case 9:case 32:return!0}return!1}function g(e){if(e>=8192&&e<=8202)return!0;switch(e){case 9:case 10:case 11:case 12:case 13:case 32:case 160:case 5760:case 8239:case 8287:case 12288:return!0}return!1}function b(e){return M.test(e)}function v(e){switch(e){case 33:case 34:case 35:case 36:case 37:case 38:case 39:case 40:case 41:case 42:case 43:case 44:case 45:case 46:case 47:case 58:case 59:case 60:case 61:case 62:case 63:case 64:case 91:case 92:case 93:case 94:case 95:case 96:case 123:case 124:case 125:case 126:return!0;default:return!1}}function k(e){return e.trim().replace(/\s+/g," ").toUpperCase()}var w=Object.prototype.hasOwnProperty,x=/\\([!"#$%&'()*+,\-.\/:;<=>?@[\\\]^_`{|}~])/g,y=/&([a-z#][a-z0-9]{1,31});/gi,C=new RegExp(x.source+"|"+y.source,"gi"),E=/^#((?:x[a-f0-9]{1,8}|[0-9]{1,8}))/i,D=n(48),T=/[&<>"]/,A=/[&<>"]/g,S={"&":"&","<":"<",">":">",'"':"""},L=/[.?*+^$[\]\\(){}|-]/g,M=n(33);t.lib={},t.lib.mdurl=n(52),t.lib.ucmicro=n(187),t.assign=a,t.isString=o,t.has=i,t.unescapeMd=p,t.unescapeAll=_,t.isValidEntityCode=l,t.fromCodePoint=c,t.escapeHtml=h,t.arrayReplaceAt=s,t.isSpace=m,t.isWhiteSpace=g,t.isMdAsciiPunct=v,t.isPunctChar=b,t.escapeRE=f,t.normalizeReference=k},function(e,t){var n=e.exports="undefined"!=typeof window&&window.Math==Math?window:"undefined"!=typeof self&&self.Math==Math?self:Function("return this")();"number"==typeof __g&&(__g=n)},function(e,t){var n={}.hasOwnProperty;e.exports=function(e,t){return n.call(e,t)}},function(e,t,n){e.exports=!n(11)((function(){return 7!=Object.defineProperty({},"a",{get:function(){return 7}}).a}))},function(e,t,n){var r=n(5),o=n(13);e.exports=n(3)?function(e,t,n){return r.f(e,t,o(1,n))}:function(e,t,n){return e[t]=n,e}},function(e,t,n){var r=n(9),o=n(41),i=n(28),a=Object.defineProperty;t.f=n(3)?Object.defineProperty:function(e,t,n){if(r(e),t=i(t,!0),r(n),o)try{return a(e,t,n)}catch(e){}if("get"in n||"set"in n)throw TypeError("Accessors not supported!");return"value"in n&&(e[t]=n.value),e}},function(e,t,n){var r=n(81),o=n(19);e.exports=function(e){return r(o(e))}},function(e,t,n){var r=n(26)("wks"),o=n(14),i=n(1).Symbol,a="function"==typeof i;(e.exports=function(e){return r[e]||(r[e]=a&&i[e]||(a?i:o)("Symbol."+e))}).store=r},function(e,t){e.exports=function(e){return"object"==typeof e?null!==e:"function"==typeof e}},function(e,t,n){var r=n(8);e.exports=function(e){if(!r(e))throw TypeError(e+" is not an object!");return e}},function(e,t){var n=e.exports={version:"2.5.7"};"number"==typeof __e&&(__e=n)},function(e,t){e.exports=function(e){try{return!!e()}catch(e){return!0}}},function(e,t){e.exports=!0},function(e,t){e.exports=function(e,t){return{enumerable:!(1&e),configurable:!(2&e),writable:!(4&e),value:t}}},function(e,t){var n=0,r=Math.random();e.exports=function(e){return"Symbol(".concat(void 0===e?"":e,")_",(++n+r).toString(36))}},function(e,t){function n(e,t){var n=e[1]||"",o=e[3];if(!o)return n;if(t&&"function"==typeof btoa){var i=r(o);return[n].concat(o.sources.map((function(e){return"/*# sourceURL="+o.sourceRoot+e+" */"}))).concat([i]).join("\n")}return[n].join("\n")}function r(e){return"/*# sourceMappingURL=data:application/json;charset=utf-8;base64,"+btoa(unescape(encodeURIComponent(JSON.stringify(e))))+" */"}e.exports=function(e){var t=[];return t.toString=function(){return this.map((function(t){var r=n(t,e);return t[2]?"@media "+t[2]+"{"+r+"}":r})).join("")},t.i=function(e,n){"string"==typeof e&&(e=[[null,e,""]]);for(var r={},o=0;on.parts.length&&(r.parts.length=n.parts.length)}else{var a=[];for(o=0;o0)return n[0].addEventListener("load",(function(){t()})),void t();var r=document.createElement("script"),o=document.getElementsByTagName("head")[0];r.type="text/javascript",r.charset="UTF-8",r.src=e,r.addEventListener?r.addEventListener("load",(function(){t()}),!1):r.attachEvent&&r.attachEvent("onreadystatechange",(function(){"loaded"===window.event.srcElement.readyState&&t()})),o.appendChild(r)}function i(e,t){if("function"!=typeof t&&(t=function(){}),document.querySelectorAll("link[href='"+e+"']").length>0)t();else{var n=document.createElement("link"),r=document.getElementsByTagName("head")[0];n.rel="stylesheet",n.href=e,n.addEventListener?n.addEventListener("load",(function(){t()}),!1):n.attachEvent&&n.attachEvent("onreadystatechange",(function(){"loaded"===window.event.srcElement.readyState&&t()})),r.appendChild(n)}}n.d(t,"g",(function(){return a})),n.d(t,"i",(function(){return s})),n.d(t,"j",(function(){return l})),n.d(t,"k",(function(){return c})),n.d(t,"h",(function(){return u})),n.d(t,"l",(function(){return p})),n.d(t,"m",(function(){return _})),n.d(t,"e",(function(){return d})),n.d(t,"f",(function(){return h})),n.d(t,"b",(function(){return f})),t.d=o,t.c=i,n.d(t,"a",(function(){return m}));var a=function(e,t,n){var o=t.prefix,i=t.subfix,a=t.str;if(t.type,e.focus(),"number"==typeof e.selectionStart&&"number"==typeof e.selectionEnd){var s=e.selectionStart,l=e.selectionEnd,c=e.value;s===l?(e.value=c.substring(0,s)+o+a+i+c.substring(l,c.length),e.selectionStart=s+o.length,e.selectionEnd=s+(a.length+o.length)):c.substring(s-o.length,s)===o&&c.substring(l,l+i.length)===i&&r(o,i,c,s,l)?(e.value=c.substring(0,s-o.length)+c.substring(s,l)+c.substring(l+i.length,c.length),e.selectionStart=s-o.length,e.selectionEnd=l-o.length):(e.value=c.substring(0,s)+o+c.substring(s,l)+i+c.substring(l,c.length),e.selectionStart=s+o.length,e.selectionEnd=s+(l-s+o.length))}else alert("Error: Browser version is too low");n.d_value=e.value,e.focus()},s=function(e){var t=e.getTextareaDom();if("number"==typeof t.selectionStart&&"number"==typeof t.selectionEnd){var n=t.selectionStart,r=t.selectionEnd,o=t.value;if(n===r)t.value=o.substring(0,n)+"1. "+o.substring(r,o.length),t.selectionEnd=t.selectionStart=n+3;else{for(var i=n;i>0&&"\n"!==o.substring(i-1,i);)i--;for(var a=o.substring(i,r),s=a.split("\n"),l=0;l0&&"\n"!==o.substring(i-1,i);)i--;for(var a=r;a0&&"\n"!==o.substring(i-1,i);)i--;var a=o.substring(i,r),s=a.replace(/\n/g,"\n- ");s="- "+s,t.value=o.substring(0,i)+s+o.substring(r,o.length),t.selectionStart=i,t.selectionEnd=r+s.length-a.length}}else alert("Error: Browser version is too low");e.d_value=t.value,t.focus()},u=function(e,t){t=t?new Array(t).fill(" ").join(""):"\t";var n=e.getTextareaDom();if("number"==typeof n.selectionStart&&"number"==typeof n.selectionEnd){var r=n.selectionStart,o=n.selectionEnd,i=n.value,a=i.substring(0,r).split("\n").pop();if(a.match(/^\s*[0-9]+\.\s+\S*/)){var s=a.replace(/(\d+)/,1);n.value=i.substring(0,r-s.length)+t+s+i.substring(o,i.length)}else a.match(/^\s*-\s+\S*/)?n.value=i.substring(0,r-a.length)+t+a+i.substring(o,i.length):n.value=i.substring(0,r)+t+i.substring(o,i.length);n.selectionStart=n.selectionEnd=r+t.length}else alert("Error: Browser version is too low");e.d_value=n.value,n.focus()},p=function(e,t){var n=new RegExp(t?"\\s{"+t+"}":"\t");console.log("regTab:",n);var r=e.getTextareaDom();if("number"==typeof r.selectionStart&&"number"==typeof r.selectionEnd){var o=r.selectionStart,i=r.selectionEnd,a=r.value,s=a.substring(0,o).split("\n").pop();s.search(n)>=0&&(r.value=a.substring(0,o-s.length)+s.replace(n,"")+a.substring(i,a.length),r.selectionStart=r.selectionEnd=o-(t||1))}else alert("Error: Browser version is too low");e.d_value=r.value,r.focus()},_=function(e,t){var n=e.getTextareaDom();if("number"==typeof n.selectionStart&&"number"==typeof n.selectionEnd){var r=n.selectionStart,o=n.selectionEnd,i=n.value,a=i.substring(0,r).split("\n").pop(),s=a.match(/^\s*(?:[0-9]+\.|-)\s+\S+/);if(s){t.preventDefault();var l=s.shift().match(/^\s*(?:[0-9]+\.|-)\s/).shift();if(l.search(/-/)>=0)n.value=i.substring(0,r)+"\n"+l+i.substring(o,i.length),n.selectionStart=n.selectionEnd=r+l.length+1;else{var c=l.replace(/(\d+)/,parseInt(l)+1);n.value=i.substring(0,r)+"\n"+c+i.substring(o,i.length),n.selectionStart=n.selectionEnd=r+c.length+1}}else{var u=a.match(/^\s*(?:[0-9]+\.|-)\s+$/);if(u){t.preventDefault();var p=u.shift().length;n.value=i.substring(0,r-p)+"\n"+i.substring(o,i.length),n.selectionStart=n.selectionEnd=r-p}}}else alert("Error: Browser version is too low");e.d_value=n.value,n.focus()},d=function(e,t){var n=void 0;n=e.$refs.navigationContent,n.innerHTML=e.d_render;var r=n.children;if(r.length)for(var o=0;o=0&&n.scrollHeight!==t.edit_scroll_height&&n.scrollHeight-n.offsetHeight-n.scrollTop<=30&&(t.$refs.vNoteEdit.scrollTop=n.scrollHeight-n.offsetHeight,r=1),t.edit_scroll_height=n.scrollHeight,t.$refs.vShowContent.scrollHeight>t.$refs.vShowContent.offsetHeight&&(t.$refs.vShowContent.scrollTop=(t.$refs.vShowContent.scrollHeight-t.$refs.vShowContent.offsetHeight)*r)},f=function(e){e.$el.addEventListener("fullscreenchange",(function(t){e.$toolbar_right_read_change_status()}),!1),e.$el.addEventListener("mozfullscreenchange",(function(t){e.$toolbar_right_read_change_status()}),!1),e.$el.addEventListener("webkitfullscreenchange",(function(t){e.$toolbar_right_read_change_status()}),!1),e.$el.addEventListener("msfullscreenchange",(function(t){e.$toolbar_right_read_change_status()}),!1)},m=function(e){e.$refs.vShowContent.addEventListener("click",(function(t){t=t||window.event;var n=t.srcElement?t.srcElement:t.target;"IMG"===n.tagName&&(null!=e.imageClick?e.imageClick(n):e.d_preview_imgsrc=n.src)}))}},function(e,t){e.exports=function(e){if(void 0==e)throw TypeError("Can't call method on "+e);return e}},function(e,t){e.exports="constructor,hasOwnProperty,isPrototypeOf,propertyIsEnumerable,toLocaleString,toString,valueOf".split(",")},function(e,t){e.exports={}},function(e,t,n){var r=n(46),o=n(20);e.exports=Object.keys||function(e){return r(e,o)}},function(e,t){t.f={}.propertyIsEnumerable},function(e,t,n){var r=n(5).f,o=n(2),i=n(7)("toStringTag");e.exports=function(e,t,n){e&&!o(e=n?e:e.prototype,i)&&r(e,i,{configurable:!0,value:t})}},function(e,t,n){var r=n(26)("keys"),o=n(14);e.exports=function(e){return r[e]||(r[e]=o(e))}},function(e,t,n){var r=n(10),o=n(1),i=o["__core-js_shared__"]||(o["__core-js_shared__"]={});(e.exports=function(e,t){return i[e]||(i[e]=void 0!==t?t:{})})("versions",[]).push({version:r.version,mode:n(12)?"pure":"global",copyright:"© 2018 Denis Pushkarev (zloirock.ru)"})},function(e,t){var n=Math.ceil,r=Math.floor;e.exports=function(e){return isNaN(e=+e)?0:(e>0?r:n)(e)}},function(e,t,n){var r=n(8);e.exports=function(e,t){if(!r(e))return e;var n,o;if(t&&"function"==typeof(n=e.toString)&&!r(o=n.call(e)))return o;if("function"==typeof(n=e.valueOf)&&!r(o=n.call(e)))return o;if(!t&&"function"==typeof(n=e.toString)&&!r(o=n.call(e)))return o;throw TypeError("Can't convert object to primitive value")}},function(e,t,n){var r=n(1),o=n(10),i=n(12),a=n(30),s=n(5).f;e.exports=function(e){var t=o.Symbol||(o.Symbol=i?{}:r.Symbol||{});"_"==e.charAt(0)||e in t||s(t,e,{value:a.f(e)})}},function(e,t,n){t.f=n(7)},function(e,t,n){"use strict";function r(){this.__rules__=[],this.__cache__=null}r.prototype.__find__=function(e){for(var t=0;t=0&&(n=this.attrs[t][1]),n},r.prototype.attrJoin=function(e,t){var n=this.attrIndex(e);n<0?this.attrPush([e,t]):this.attrs[n][1]=this.attrs[n][1]+" "+t},e.exports=r},function(e,t){e.exports=/[!-#%-\*,-/:;\?@\[-\]_\{\}\xA1\xA7\xAB\xB6\xB7\xBB\xBF\u037E\u0387\u055A-\u055F\u0589\u058A\u05BE\u05C0\u05C3\u05C6\u05F3\u05F4\u0609\u060A\u060C\u060D\u061B\u061E\u061F\u066A-\u066D\u06D4\u0700-\u070D\u07F7-\u07F9\u0830-\u083E\u085E\u0964\u0965\u0970\u09FD\u0AF0\u0DF4\u0E4F\u0E5A\u0E5B\u0F04-\u0F12\u0F14\u0F3A-\u0F3D\u0F85\u0FD0-\u0FD4\u0FD9\u0FDA\u104A-\u104F\u10FB\u1360-\u1368\u1400\u166D\u166E\u169B\u169C\u16EB-\u16ED\u1735\u1736\u17D4-\u17D6\u17D8-\u17DA\u1800-\u180A\u1944\u1945\u1A1E\u1A1F\u1AA0-\u1AA6\u1AA8-\u1AAD\u1B5A-\u1B60\u1BFC-\u1BFF\u1C3B-\u1C3F\u1C7E\u1C7F\u1CC0-\u1CC7\u1CD3\u2010-\u2027\u2030-\u2043\u2045-\u2051\u2053-\u205E\u207D\u207E\u208D\u208E\u2308-\u230B\u2329\u232A\u2768-\u2775\u27C5\u27C6\u27E6-\u27EF\u2983-\u2998\u29D8-\u29DB\u29FC\u29FD\u2CF9-\u2CFC\u2CFE\u2CFF\u2D70\u2E00-\u2E2E\u2E30-\u2E49\u3001-\u3003\u3008-\u3011\u3014-\u301F\u3030\u303D\u30A0\u30FB\uA4FE\uA4FF\uA60D-\uA60F\uA673\uA67E\uA6F2-\uA6F7\uA874-\uA877\uA8CE\uA8CF\uA8F8-\uA8FA\uA8FC\uA92E\uA92F\uA95F\uA9C1-\uA9CD\uA9DE\uA9DF\uAA5C-\uAA5F\uAADE\uAADF\uAAF0\uAAF1\uABEB\uFD3E\uFD3F\uFE10-\uFE19\uFE30-\uFE52\uFE54-\uFE61\uFE63\uFE68\uFE6A\uFE6B\uFF01-\uFF03\uFF05-\uFF0A\uFF0C-\uFF0F\uFF1A\uFF1B\uFF1F\uFF20\uFF3B-\uFF3D\uFF3F\uFF5B\uFF5D\uFF5F-\uFF65]|\uD800[\uDD00-\uDD02\uDF9F\uDFD0]|\uD801\uDD6F|\uD802[\uDC57\uDD1F\uDD3F\uDE50-\uDE58\uDE7F\uDEF0-\uDEF6\uDF39-\uDF3F\uDF99-\uDF9C]|\uD804[\uDC47-\uDC4D\uDCBB\uDCBC\uDCBE-\uDCC1\uDD40-\uDD43\uDD74\uDD75\uDDC5-\uDDC9\uDDCD\uDDDB\uDDDD-\uDDDF\uDE38-\uDE3D\uDEA9]|\uD805[\uDC4B-\uDC4F\uDC5B\uDC5D\uDCC6\uDDC1-\uDDD7\uDE41-\uDE43\uDE60-\uDE6C\uDF3C-\uDF3E]|\uD806[\uDE3F-\uDE46\uDE9A-\uDE9C\uDE9E-\uDEA2]|\uD807[\uDC41-\uDC45\uDC70\uDC71]|\uD809[\uDC70-\uDC74]|\uD81A[\uDE6E\uDE6F\uDEF5\uDF37-\uDF3B\uDF44]|\uD82F\uDC9F|\uD836[\uDE87-\uDE8B]|\uD83A[\uDD5E\uDD5F]/},function(e,t,n){function r(e){o||n(194)}var o=!1,i=n(16)(n(59),n(191),r,"data-v-62b9e4d1",null);i.options.__file="C:\\Users\\LWK\\Desktop\\mavonEditor-master\\mavonEditor-master\\src\\components\\md-toolbar-left.vue",i.esModule&&Object.keys(i.esModule).some((function(e){return"default"!==e&&"__"!==e.substr(0,2)}))&&console.error("named exports are not supported in *.vue files."),i.options.functional&&console.error("[vue-loader] md-toolbar-left.vue: functional components are not supported with templates, they should use render functions."),e.exports=i.exports},function(e,t,n){var r=n(16)(n(60),n(189),null,null,null);r.options.__file="C:\\Users\\LWK\\Desktop\\mavonEditor-master\\mavonEditor-master\\src\\components\\md-toolbar-right.vue",r.esModule&&Object.keys(r.esModule).some((function(e){return"default"!==e&&"__"!==e.substr(0,2)}))&&console.error("named exports are not supported in *.vue files."),r.options.functional&&console.error("[vue-loader] md-toolbar-right.vue: functional components are not supported with templates, they should use render functions."),e.exports=r.exports},function(e,t,n){"use strict";t.a={"1c":"1c",abnf:"abnf",accesslog:"accesslog",actionscript:"actionscript",as:"actionscript",ada:"ada",apache:"apache",apacheconf:"apache",applescript:"applescript",osascript:"applescript",arduino:"arduino",armasm:"armasm",arm:"armasm",asciidoc:"asciidoc",adoc:"asciidoc",aspectj:"aspectj",autohotkey:"autohotkey",ahk:"autohotkey",autoit:"autoit",avrasm:"avrasm",awk:"awk",axapta:"axapta",bash:"bash",sh:"bash",zsh:"bash",basic:"basic",bnf:"bnf",brainfuck:"brainfuck",bf:"brainfuck",cal:"cal",capnproto:"capnproto",capnp:"capnproto",ceylon:"ceylon",clean:"clean",icl:"clean",dcl:"clean","clojure-repl":"clojure-repl",clojure:"clojure",clj:"clojure",cmake:"cmake","cmake.in":"cmake",coffeescript:"coffeescript",coffee:"coffeescript",cson:"coffeescript",iced:"coffeescript",coq:"coq",cos:"cos",cls:"cos",cpp:"cpp",c:"cpp",cc:"cpp",h:"cpp","c++":"cpp","h++":"cpp",hpp:"cpp",crmsh:"crmsh",crm:"crmsh",pcmk:"crmsh",crystal:"crystal",cr:"crystal",cs:"cs",csharp:"cs",csp:"csp",css:"css",d:"d",dart:"dart",delphi:"delphi",dpr:"delphi",dfm:"delphi",pas:"delphi",pascal:"delphi",freepascal:"delphi",lazarus:"delphi",lpr:"delphi",lfm:"delphi",diff:"diff",patch:"diff",django:"django",jinja:"django",dns:"dns",bind:"dns",zone:"dns",dockerfile:"dockerfile",docker:"dockerfile",dos:"dos",bat:"dos",cmd:"dos",dsconfig:"dsconfig",dts:"dts",dust:"dust",dst:"dust",ebnf:"ebnf",elixir:"elixir",elm:"elm",erb:"erb","erlang-repl":"erlang-repl",erlang:"erlang",erl:"erlang",excel:"excel",xlsx:"excel",xls:"excel",fix:"fix",flix:"flix",fortran:"fortran",f90:"fortran",f95:"fortran",fsharp:"fsharp",fs:"fsharp",gams:"gams",gms:"gams",gauss:"gauss",gss:"gauss",gcode:"gcode",nc:"gcode",gherkin:"gherkin",feature:"gherkin",glsl:"glsl",go:"go",golang:"go",golo:"golo",gradle:"gradle",groovy:"groovy",haml:"haml",handlebars:"handlebars",hbs:"handlebars","html.hbs":"handlebars","html.handlebars":"handlebars",haskell:"haskell",hs:"haskell",haxe:"haxe",hx:"haxe",hsp:"hsp",htmlbars:"htmlbars",http:"http",https:"http",hy:"hy",hylang:"hy",inform7:"inform7",i7:"inform7",ini:"ini",toml:"ini",irpf90:"irpf90",java:"java",jsp:"java",javascript:"javascript",js:"javascript",jsx:"javascript","jboss-cli":"jboss-cli","wildfly-cli":"jboss-cli",json:"json","julia-repl":"julia-repl",julia:"julia",kotlin:"kotlin",lasso:"lasso",ls:"livescript",lassoscript:"lasso",ldif:"ldif",leaf:"leaf",less:"less",lisp:"lisp",livecodeserver:"livecodeserver",livescript:"livescript",llvm:"llvm",lsl:"lsl",lua:"lua",makefile:"makefile",mk:"makefile",mak:"makefile",markdown:"markdown",md:"markdown",mkdown:"markdown",mkd:"markdown",mathematica:"mathematica",mma:"mathematica",matlab:"matlab",maxima:"maxima",mel:"mel",mercury:"mercury",m:"mercury",moo:"mercury",mipsasm:"mipsasm",mips:"mipsasm",mizar:"mizar",mojolicious:"mojolicious",monkey:"monkey",moonscript:"moonscript",moon:"moonscript",n1ql:"n1ql",nginx:"nginx",nginxconf:"nginx",nimrod:"nimrod",nim:"nimrod",nix:"nix",nixos:"nix",nsis:"nsis",objectivec:"objectivec",mm:"objectivec",objc:"objectivec","obj-c":"objectivec",ocaml:"ocaml",ml:"sml",openscad:"openscad",scad:"openscad",oxygene:"oxygene",parser3:"parser3",perl:"perl",pl:"perl",pm:"perl",pf:"pf","pf.conf":"pf",php:"php",php3:"php",php4:"php",php5:"php",php6:"php",pony:"pony",powershell:"powershell",ps:"powershell",processing:"processing",profile:"profile",prolog:"prolog",protobuf:"protobuf",puppet:"puppet",pp:"puppet",purebasic:"purebasic",pb:"purebasic",pbi:"purebasic",python:"python",py:"python",gyp:"python",q:"q",k:"q",kdb:"q",qml:"qml",qt:"qml",r:"r",rib:"rib",roboconf:"roboconf",graph:"roboconf",instances:"roboconf",routeros:"routeros",mikrotik:"routeros",rsl:"rsl",ruby:"ruby",rb:"ruby",gemspec:"ruby",podspec:"ruby",thor:"ruby",irb:"ruby",ruleslanguage:"ruleslanguage",rust:"rust",rs:"rust",scala:"scala",scheme:"scheme",scilab:"scilab",sci:"scilab",scss:"scss",shell:"shell",console:"shell",smali:"smali",smalltalk:"smalltalk",st:"smalltalk",sml:"sml",sqf:"sqf",sql:"sql",stan:"stan",stata:"stata",do:"stata",ado:"stata",step21:"step21",p21:"step21",step:"step21",stp:"step21",stylus:"stylus",styl:"stylus",subunit:"subunit",swift:"swift",taggerscript:"taggerscript",tap:"tap",tcl:"tcl",tk:"tcl",tex:"tex",thrift:"thrift",tp:"tp",twig:"twig",craftcms:"twig",typescript:"typescript",ts:"typescript",vala:"vala",vbnet:"vbnet",vb:"vbnet","vbscript-html":"vbscript-html",vbscript:"vbscript",vbs:"vbscript",verilog:"verilog",v:"verilog",sv:"verilog",svh:"verilog",vhdl:"vhdl",vim:"vim",x86asm:"x86asm",xl:"xl",tao:"xl",xml:"xml",html:"xml",xhtml:"xml",rss:"xml",atom:"xml",xjb:"xml",xsd:"xml",xsl:"xml",plist:"xml",xquery:"xquery",xpath:"xquery",xq:"xquery",yaml:"yaml",yml:"yaml",YAML:"yaml",zephir:"zephir",zep:"zephir"}},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}t.__esModule=!0;var o=n(72),i=r(o),a=n(71),s=r(a),l="function"==typeof s.default&&"symbol"==typeof i.default?function(e){return typeof e}:function(e){return e&&"function"==typeof s.default&&e.constructor===s.default&&e!==s.default.prototype?"symbol":typeof e};t.default="function"==typeof s.default&&"symbol"===l(i.default)?function(e){return void 0===e?"undefined":l(e)}:function(e){return e&&"function"==typeof s.default&&e.constructor===s.default&&e!==s.default.prototype?"symbol":void 0===e?"undefined":l(e)}},function(e,t){var n={}.toString;e.exports=function(e){return n.call(e).slice(8,-1)}},function(e,t,n){var r=n(8),o=n(1).document,i=r(o)&&r(o.createElement);e.exports=function(e){return i?o.createElement(e):{}}},function(e,t,n){var r=n(1),o=n(10),i=n(78),a=n(4),s=n(2),l=function(e,t,n){var c,u,p,_=e&l.F,d=e&l.G,h=e&l.S,f=e&l.P,m=e&l.B,g=e&l.W,b=d?o:o[t]||(o[t]={}),v=b.prototype,k=d?r:h?r[t]:(r[t]||{}).prototype;for(c in d&&(n=t),n)(u=!_&&k&&void 0!==k[c])&&s(b,c)||(p=u?k[c]:n[c],b[c]=d&&"function"!=typeof k[c]?n[c]:m&&u?i(p,r):g&&k[c]==p?function(e){var t=function(t,n,r){if(this instanceof e){switch(arguments.length){case 0:return new e;case 1:return new e(t);case 2:return new e(t,n)}return new e(t,n,r)}return e.apply(this,arguments)};return t.prototype=e.prototype,t}(p):f&&"function"==typeof p?i(Function.call,p):p,f&&((b.virtual||(b.virtual={}))[c]=p,e&l.R&&v&&!v[c]&&a(v,c,p)))};l.F=1,l.G=2,l.S=4,l.P=8,l.B=16,l.W=32,l.U=64,l.R=128,e.exports=l},function(e,t,n){e.exports=!n(3)&&!n(11)((function(){return 7!=Object.defineProperty(n(39)("div"),"a",{get:function(){return 7}}).a}))},function(e,t,n){"use strict";var r=n(12),o=n(40),i=n(47),a=n(4),s=n(21),l=n(83),c=n(24),u=n(89),p=n(7)("iterator"),_=!([].keys&&"next"in[].keys()),d=function(){return this};e.exports=function(e,t,n,h,f,m,g){l(n,t,h);var b,v,k,w=function(e){if(!_&&e in E)return E[e];switch(e){case"keys":case"values":return function(){return new n(this,e)}}return function(){return new n(this,e)}},x=t+" Iterator",y="values"==f,C=!1,E=e.prototype,D=E[p]||E["@@iterator"]||f&&E[f],T=D||w(f),A=f?y?w("entries"):T:void 0,S="Array"==t&&E.entries||D;if(S&&(k=u(S.call(new e)))!==Object.prototype&&k.next&&(c(k,x,!0),r||"function"==typeof k[p]||a(k,p,d)),y&&D&&"values"!==D.name&&(C=!0,T=function(){return D.call(this)}),r&&!g||!_&&!C&&E[p]||a(E,p,T),s[t]=T,s[x]=d,f)if(b={values:y?T:w("values"),keys:m?T:w("keys"),entries:A},g)for(v in b)v in E||i(E,v,b[v]);else o(o.P+o.F*(_||C),t,b);return b}},function(e,t,n){var r=n(9),o=n(86),i=n(20),a=n(25)("IE_PROTO"),s=function(){},l=function(){var e,t=n(39)("iframe"),r=i.length;for(t.style.display="none",n(80).appendChild(t),t.src="javascript:",e=t.contentWindow.document,e.open(),e.write(" diff --git a/sop-website/sop-website-vue/src/api/table.js b/sop-website/sop-website-vue/src/api/table.js new file mode 100644 index 00000000..e29c2943 --- /dev/null +++ b/sop-website/sop-website-vue/src/api/table.js @@ -0,0 +1,9 @@ +import request from '@/utils/request' + +export function getList(params) { + return request({ + url: '/table/list', + method: 'get', + params + }) +} diff --git a/sop-website/sop-website-vue/src/api/user.js b/sop-website/sop-website-vue/src/api/user.js new file mode 100644 index 00000000..de69f707 --- /dev/null +++ b/sop-website/sop-website-vue/src/api/user.js @@ -0,0 +1,24 @@ +import request from '@/utils/request' + +export function login(data) { + return request({ + url: '/user/login', + method: 'post', + data + }) +} + +export function getInfo(token) { + return request({ + url: '/user/info', + method: 'get', + params: { token } + }) +} + +export function logout() { + return request({ + url: '/user/logout', + method: 'post' + }) +} diff --git a/sop-website/sop-website-vue/src/components/Breadcrumb/index.vue b/sop-website/sop-website-vue/src/components/Breadcrumb/index.vue new file mode 100644 index 00000000..e65a60d8 --- /dev/null +++ b/sop-website/sop-website-vue/src/components/Breadcrumb/index.vue @@ -0,0 +1,78 @@ + + + + + diff --git a/sop-website/sop-website-vue/src/components/DocView/index.vue b/sop-website/sop-website-vue/src/components/DocView/index.vue new file mode 100644 index 00000000..b7c6e089 --- /dev/null +++ b/sop-website/sop-website-vue/src/components/DocView/index.vue @@ -0,0 +1,273 @@ + + + + + diff --git a/sop-website/sop-website-vue/src/components/Docdebug/index.vue b/sop-website/sop-website-vue/src/components/Docdebug/index.vue new file mode 100644 index 00000000..1ca5be13 --- /dev/null +++ b/sop-website/sop-website-vue/src/components/Docdebug/index.vue @@ -0,0 +1,123 @@ + + + diff --git a/sop-website/sop-website-vue/src/components/Hamburger/index.vue b/sop-website/sop-website-vue/src/components/Hamburger/index.vue new file mode 100644 index 00000000..368b0021 --- /dev/null +++ b/sop-website/sop-website-vue/src/components/Hamburger/index.vue @@ -0,0 +1,44 @@ + + + + + diff --git a/sop-website/sop-website-vue/src/components/ParameterTable/index.vue b/sop-website/sop-website-vue/src/components/ParameterTable/index.vue new file mode 100644 index 00000000..5809dbb8 --- /dev/null +++ b/sop-website/sop-website-vue/src/components/ParameterTable/index.vue @@ -0,0 +1,87 @@ + + + diff --git a/sop-website/sop-website-vue/src/components/SvgIcon/index.vue b/sop-website/sop-website-vue/src/components/SvgIcon/index.vue new file mode 100644 index 00000000..27da76cf --- /dev/null +++ b/sop-website/sop-website-vue/src/components/SvgIcon/index.vue @@ -0,0 +1,43 @@ + + + + + diff --git a/sop-website/sop-website-vue/src/components/verifition/Verify.vue b/sop-website/sop-website-vue/src/components/verifition/Verify.vue new file mode 100644 index 00000000..9c6f32bd --- /dev/null +++ b/sop-website/sop-website-vue/src/components/verifition/Verify.vue @@ -0,0 +1,465 @@ + + + diff --git a/sop-website/sop-website-vue/src/components/verifition/Verify/VerifyPoints.vue b/sop-website/sop-website-vue/src/components/verifition/Verify/VerifyPoints.vue new file mode 100644 index 00000000..e7692d87 --- /dev/null +++ b/sop-website/sop-website-vue/src/components/verifition/Verify/VerifyPoints.vue @@ -0,0 +1,247 @@ + + \ No newline at end of file diff --git a/sop-website/sop-website-vue/src/components/verifition/Verify/VerifySlide.vue b/sop-website/sop-website-vue/src/components/verifition/Verify/VerifySlide.vue new file mode 100644 index 00000000..77c88b81 --- /dev/null +++ b/sop-website/sop-website-vue/src/components/verifition/Verify/VerifySlide.vue @@ -0,0 +1,346 @@ + + + diff --git a/sop-website/sop-website-vue/src/components/verifition/api/index.js b/sop-website/sop-website-vue/src/components/verifition/api/index.js new file mode 100644 index 00000000..73dbcc27 --- /dev/null +++ b/sop-website/sop-website-vue/src/components/verifition/api/index.js @@ -0,0 +1,26 @@ +/** + * 此处可直接引用自己项目封装好的 axios 配合后端联调 + */ + +import request from './../utils/axios' // 组件内部封装的axios +// import request from "@/api/axios.js" // 调用项目封装的axios + +const baseURL = process.env.VUE_APP_BASE_API + +// 获取验证图片 以及token +export function reqGet(data) { + return request({ + url: baseURL + '/captcha/get', + method: 'post', + data + }) +} + +// 滑动或者点选验证 +export function reqCheck(data) { + return request({ + url: baseURL + '/captcha/check', + method: 'post', + data + }) +} diff --git a/sop-website/sop-website-vue/src/components/verifition/utils/ase.js b/sop-website/sop-website-vue/src/components/verifition/utils/ase.js new file mode 100644 index 00000000..38174d73 --- /dev/null +++ b/sop-website/sop-website-vue/src/components/verifition/utils/ase.js @@ -0,0 +1,11 @@ +import CryptoJS from 'crypto-js' +/** + * @word 要加密的内容 + * @keyWord String 服务器随机返回的关键字 + * */ +export function aesEncrypt(word,keyWord="twKs9lMcdPM6hR1B"){ + var key = CryptoJS.enc.Utf8.parse(keyWord); + var srcs = CryptoJS.enc.Utf8.parse(word); + var encrypted = CryptoJS.AES.encrypt(srcs, key, {mode:CryptoJS.mode.ECB,padding: CryptoJS.pad.Pkcs7}); + return encrypted.toString(); +} diff --git a/sop-website/sop-website-vue/src/components/verifition/utils/axios.js b/sop-website/sop-website-vue/src/components/verifition/utils/axios.js new file mode 100644 index 00000000..610d4032 --- /dev/null +++ b/sop-website/sop-website-vue/src/components/verifition/utils/axios.js @@ -0,0 +1,30 @@ +import axios from 'axios'; + +axios.defaults.baseURL = process.env.BASE_API; + +const service = axios.create({ + timeout: 40000, + headers: { + 'X-Requested-With': 'XMLHttpRequest', + 'Content-Type': 'application/json; charset=UTF-8' + }, +}) +service.interceptors.request.use( + config => { + return config + }, + error => { + Promise.reject(error) + } +) + +// response interceptor +service.interceptors.response.use( + response => { + const res = response.data; + return res + }, + error => { + } +) +export default service diff --git a/sop-website/sop-website-vue/src/components/verifition/utils/util.js b/sop-website/sop-website-vue/src/components/verifition/utils/util.js new file mode 100644 index 00000000..55f65d42 --- /dev/null +++ b/sop-website/sop-website-vue/src/components/verifition/utils/util.js @@ -0,0 +1,36 @@ +export function resetSize(vm) { + var img_width, img_height, bar_width, bar_height; //图片的宽度、高度,移动条的宽度、高度 + + var parentWidth = vm.$el.parentNode.offsetWidth || window.offsetWidth + var parentHeight = vm.$el.parentNode.offsetHeight || window.offsetHeight + + if (vm.imgSize.width.indexOf('%') != -1) { + img_width = parseInt(this.imgSize.width) / 100 * parentWidth + 'px' + } else { + img_width = this.imgSize.width; + } + + if (vm.imgSize.height.indexOf('%') != -1) { + img_height = parseInt(this.imgSize.height) / 100 * parentHeight + 'px' + } else { + img_height = this.imgSize.height + } + + if (vm.barSize.width.indexOf('%') != -1) { + bar_width = parseInt(this.barSize.width) / 100 * parentWidth + 'px' + } else { + bar_width = this.barSize.width + } + + if (vm.barSize.height.indexOf('%') != -1) { + bar_height = parseInt(this.barSize.height) / 100 * parentHeight + 'px' + } else { + bar_height = this.barSize.height + } + + return {imgWidth: img_width, imgHeight: img_height, barWidth: bar_width, barHeight: bar_height} +} + +export const _code_chars = [1, 2, 3, 4, 5, 6, 7, 8, 9, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'] +export const _code_color1 = ['#fffff0', '#f0ffff', '#f0fff0', '#fff0f0'] +export const _code_color2 = ['#FF0033', '#006699', '#993366', '#FF9900', '#66CC66', '#FF33CC'] \ No newline at end of file diff --git a/sop-website/sop-website-vue/src/icons/index.js b/sop-website/sop-website-vue/src/icons/index.js new file mode 100644 index 00000000..2c6b309c --- /dev/null +++ b/sop-website/sop-website-vue/src/icons/index.js @@ -0,0 +1,9 @@ +import Vue from 'vue' +import SvgIcon from '@/components/SvgIcon'// svg component + +// register globally +Vue.component('svg-icon', SvgIcon) + +const req = require.context('./svg', false, /\.svg$/) +const requireAll = requireContext => requireContext.keys().map(requireContext) +requireAll(req) diff --git a/sop-website/sop-website-vue/src/icons/svg/dashboard.svg b/sop-website/sop-website-vue/src/icons/svg/dashboard.svg new file mode 100644 index 00000000..5317d370 --- /dev/null +++ b/sop-website/sop-website-vue/src/icons/svg/dashboard.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/sop-website/sop-website-vue/src/icons/svg/email.svg b/sop-website/sop-website-vue/src/icons/svg/email.svg new file mode 100644 index 00000000..9f1c181d --- /dev/null +++ b/sop-website/sop-website-vue/src/icons/svg/email.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/sop-website/sop-website-vue/src/icons/svg/example.svg b/sop-website/sop-website-vue/src/icons/svg/example.svg new file mode 100644 index 00000000..46f42b53 --- /dev/null +++ b/sop-website/sop-website-vue/src/icons/svg/example.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/sop-website/sop-website-vue/src/icons/svg/eye-open.svg b/sop-website/sop-website-vue/src/icons/svg/eye-open.svg new file mode 100644 index 00000000..88dcc98e --- /dev/null +++ b/sop-website/sop-website-vue/src/icons/svg/eye-open.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/sop-website/sop-website-vue/src/icons/svg/eye.svg b/sop-website/sop-website-vue/src/icons/svg/eye.svg new file mode 100644 index 00000000..16ed2d87 --- /dev/null +++ b/sop-website/sop-website-vue/src/icons/svg/eye.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/sop-website/sop-website-vue/src/icons/svg/form.svg b/sop-website/sop-website-vue/src/icons/svg/form.svg new file mode 100644 index 00000000..dcbaa185 --- /dev/null +++ b/sop-website/sop-website-vue/src/icons/svg/form.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/sop-website/sop-website-vue/src/icons/svg/link.svg b/sop-website/sop-website-vue/src/icons/svg/link.svg new file mode 100644 index 00000000..48197ba4 --- /dev/null +++ b/sop-website/sop-website-vue/src/icons/svg/link.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/sop-website/sop-website-vue/src/icons/svg/nested.svg b/sop-website/sop-website-vue/src/icons/svg/nested.svg new file mode 100644 index 00000000..06713a86 --- /dev/null +++ b/sop-website/sop-website-vue/src/icons/svg/nested.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/sop-website/sop-website-vue/src/icons/svg/password.svg b/sop-website/sop-website-vue/src/icons/svg/password.svg new file mode 100644 index 00000000..e291d85d --- /dev/null +++ b/sop-website/sop-website-vue/src/icons/svg/password.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/sop-website/sop-website-vue/src/icons/svg/table.svg b/sop-website/sop-website-vue/src/icons/svg/table.svg new file mode 100644 index 00000000..0e3dc9de --- /dev/null +++ b/sop-website/sop-website-vue/src/icons/svg/table.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/sop-website/sop-website-vue/src/icons/svg/tree.svg b/sop-website/sop-website-vue/src/icons/svg/tree.svg new file mode 100644 index 00000000..dd4b7dd2 --- /dev/null +++ b/sop-website/sop-website-vue/src/icons/svg/tree.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/sop-website/sop-website-vue/src/icons/svg/user.svg b/sop-website/sop-website-vue/src/icons/svg/user.svg new file mode 100644 index 00000000..0ba0716a --- /dev/null +++ b/sop-website/sop-website-vue/src/icons/svg/user.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/sop-website/sop-website-vue/src/icons/svgo.yml b/sop-website/sop-website-vue/src/icons/svgo.yml new file mode 100644 index 00000000..d11906ae --- /dev/null +++ b/sop-website/sop-website-vue/src/icons/svgo.yml @@ -0,0 +1,22 @@ +# replace default config + +# multipass: true +# full: true + +plugins: + + # - name + # + # or: + # - name: false + # - name: true + # + # or: + # - name: + # param1: 1 + # param2: 2 + +- removeAttrs: + attrs: + - 'fill' + - 'fill-rule' diff --git a/sop-website/sop-website-vue/src/layout/components/AppMain.vue b/sop-website/sop-website-vue/src/layout/components/AppMain.vue new file mode 100644 index 00000000..e01ec98d --- /dev/null +++ b/sop-website/sop-website-vue/src/layout/components/AppMain.vue @@ -0,0 +1,31 @@ + + + + + diff --git a/sop-website/sop-website-vue/src/layout/components/Navbar.vue b/sop-website/sop-website-vue/src/layout/components/Navbar.vue new file mode 100644 index 00000000..1622dce3 --- /dev/null +++ b/sop-website/sop-website-vue/src/layout/components/Navbar.vue @@ -0,0 +1,149 @@ + + + + + diff --git a/sop-website/sop-website-vue/src/layout/components/Sidebar/FixiOSBug.js b/sop-website/sop-website-vue/src/layout/components/Sidebar/FixiOSBug.js new file mode 100644 index 00000000..bc14856f --- /dev/null +++ b/sop-website/sop-website-vue/src/layout/components/Sidebar/FixiOSBug.js @@ -0,0 +1,26 @@ +export default { + computed: { + device() { + return this.$store.state.app.device + } + }, + mounted() { + // In order to fix the click on menu on the ios device will trigger the mouseleave bug + // https://github.com/PanJiaChen/vue-element-admin/issues/1135 + this.fixBugIniOS() + }, + methods: { + fixBugIniOS() { + const $subMenu = this.$refs.subMenu + if ($subMenu) { + const handleMouseleave = $subMenu.handleMouseleave + $subMenu.handleMouseleave = (e) => { + if (this.device === 'mobile') { + return + } + handleMouseleave(e) + } + } + } + } +} diff --git a/sop-website/sop-website-vue/src/layout/components/Sidebar/Item.vue b/sop-website/sop-website-vue/src/layout/components/Sidebar/Item.vue new file mode 100644 index 00000000..42abf4ae --- /dev/null +++ b/sop-website/sop-website-vue/src/layout/components/Sidebar/Item.vue @@ -0,0 +1,30 @@ + diff --git a/sop-website/sop-website-vue/src/layout/components/Sidebar/Link.vue b/sop-website/sop-website-vue/src/layout/components/Sidebar/Link.vue new file mode 100644 index 00000000..eb4dd107 --- /dev/null +++ b/sop-website/sop-website-vue/src/layout/components/Sidebar/Link.vue @@ -0,0 +1,36 @@ + + + + diff --git a/sop-website/sop-website-vue/src/layout/components/Sidebar/Logo.vue b/sop-website/sop-website-vue/src/layout/components/Sidebar/Logo.vue new file mode 100644 index 00000000..6966f604 --- /dev/null +++ b/sop-website/sop-website-vue/src/layout/components/Sidebar/Logo.vue @@ -0,0 +1,82 @@ + + + + + diff --git a/sop-website/sop-website-vue/src/layout/components/Sidebar/SidebarItem.vue b/sop-website/sop-website-vue/src/layout/components/Sidebar/SidebarItem.vue new file mode 100644 index 00000000..cf43d6e6 --- /dev/null +++ b/sop-website/sop-website-vue/src/layout/components/Sidebar/SidebarItem.vue @@ -0,0 +1,98 @@ + + + diff --git a/sop-website/sop-website-vue/src/layout/components/Sidebar/index.vue b/sop-website/sop-website-vue/src/layout/components/Sidebar/index.vue new file mode 100644 index 00000000..0ca6bac4 --- /dev/null +++ b/sop-website/sop-website-vue/src/layout/components/Sidebar/index.vue @@ -0,0 +1,69 @@ + + + diff --git a/sop-website/sop-website-vue/src/layout/components/index.js b/sop-website/sop-website-vue/src/layout/components/index.js new file mode 100644 index 00000000..97ee3cd1 --- /dev/null +++ b/sop-website/sop-website-vue/src/layout/components/index.js @@ -0,0 +1,3 @@ +export { default as Navbar } from './Navbar' +export { default as Sidebar } from './Sidebar' +export { default as AppMain } from './AppMain' diff --git a/sop-website/sop-website-vue/src/layout/index.vue b/sop-website/sop-website-vue/src/layout/index.vue new file mode 100644 index 00000000..db22a7bc --- /dev/null +++ b/sop-website/sop-website-vue/src/layout/index.vue @@ -0,0 +1,93 @@ + + + + + diff --git a/sop-website/sop-website-vue/src/layout/mixin/ResizeHandler.js b/sop-website/sop-website-vue/src/layout/mixin/ResizeHandler.js new file mode 100644 index 00000000..e8d0df8c --- /dev/null +++ b/sop-website/sop-website-vue/src/layout/mixin/ResizeHandler.js @@ -0,0 +1,45 @@ +import store from '@/store' + +const { body } = document +const WIDTH = 992 // refer to Bootstrap's responsive design + +export default { + watch: { + $route(route) { + if (this.device === 'mobile' && this.sidebar.opened) { + store.dispatch('app/closeSideBar', { withoutAnimation: false }) + } + } + }, + beforeMount() { + window.addEventListener('resize', this.$_resizeHandler) + }, + beforeDestroy() { + window.removeEventListener('resize', this.$_resizeHandler) + }, + mounted() { + const isMobile = this.$_isMobile() + if (isMobile) { + store.dispatch('app/toggleDevice', 'mobile') + store.dispatch('app/closeSideBar', { withoutAnimation: true }) + } + }, + methods: { + // use $_ for mixins properties + // https://vuejs.org/v2/style-guide/index.html#Private-property-names-essential + $_isMobile() { + const rect = body.getBoundingClientRect() + return rect.width - 1 < WIDTH + }, + $_resizeHandler() { + if (!document.hidden) { + const isMobile = this.$_isMobile() + store.dispatch('app/toggleDevice', isMobile ? 'mobile' : 'desktop') + + if (isMobile) { + store.dispatch('app/closeSideBar', { withoutAnimation: true }) + } + } + } + } +} diff --git a/sop-website/sop-website-vue/src/main.js b/sop-website/sop-website-vue/src/main.js new file mode 100644 index 00000000..56086ccb --- /dev/null +++ b/sop-website/sop-website-vue/src/main.js @@ -0,0 +1,38 @@ +import Vue from 'vue' + +import 'normalize.css/normalize.css' // A modern alternative to CSS resets + +import ElementUI from 'element-ui' +import 'element-ui/lib/theme-chalk/index.css' +import locale from 'element-ui/lib/locale/lang/zh-CN' // lang i18n + +import '@/styles/index.scss' // global css + +import App from './App' +import store from './store' +import router from './router' + +import '@/icons' // icon +import '@/permission' // permission control +import '@/utils/global' // 自定义全局js + +/** + * If you don't want to use mock-server + * you want to use mockjs for request interception + * you can execute: + * + * import { mockXHR } from '../mock' + * mockXHR() + */ + +// set ElementUI lang to EN +Vue.use(ElementUI, { locale }) + +Vue.config.productionTip = false + +new Vue({ + el: '#app', + router, + store, + render: h => h(App) +}) diff --git a/sop-website/sop-website-vue/src/permission.js b/sop-website/sop-website-vue/src/permission.js new file mode 100644 index 00000000..188b083e --- /dev/null +++ b/sop-website/sop-website-vue/src/permission.js @@ -0,0 +1,78 @@ +import router from './router' +// import store from './store' +// import { Message } from 'element-ui' +import NProgress from 'nprogress' // progress bar +import 'nprogress/nprogress.css' // progress bar style +import { getToken } from '@/utils/auth' // get token from cookie +import getPageTitle from '@/utils/get-page-title' + +NProgress.configure({ showSpinner: false }) // NProgress Configuration + +// no redirect whitelist +const whiteList = [ + '/login', + '/ispReg', + '/isvReg', + '/resetPassword', + '/findPassword', + '/code', + 'sign' +] + +router.beforeEach(async(to, from, next) => { + // start progress bar + NProgress.start() + + // set page title + document.title = getPageTitle(to.meta.title) + + // determine whether the user has logged in + const hasToken = getToken() + + if (hasToken) { + if (to.path === '/') { + next({ path: '/dashboard' }) + NProgress.done() + } + if (to.path === '/login') { + // if is logged in, redirect to the home page + next({ path: '/' }) + NProgress.done() + } else { + next() + // const hasGetUserInfo = store.getters.name + // if (hasGetUserInfo) { + // next() + // } else { + // try { + // // get user info + // await store.dispatch('user/getInfo') + // + // next() + // } catch (error) { + // // remove token and go to login page to re-login + // await store.dispatch('user/resetToken') + // Message.error(error || 'Has Error') + // next(`/login?redirect=${to.path}`) + // NProgress.done() + // } + // } + } + } else { + /* has no token*/ + + if (whiteList.indexOf(to.path) !== -1) { + // in the free login whitelist, go directly + next() + } else { + // other pages that do not have permission to access are redirected to the login page. + next('/login') + NProgress.done() + } + } +}) + +router.afterEach(() => { + // finish progress bar + NProgress.done() +}) diff --git a/sop-website/sop-website-vue/src/router/_import_development.js b/sop-website/sop-website-vue/src/router/_import_development.js new file mode 100644 index 00000000..6b6b7968 --- /dev/null +++ b/sop-website/sop-website-vue/src/router/_import_development.js @@ -0,0 +1,2 @@ +// vue-loader at least v13.0.0+ +module.exports = file => require('@/views/' + file + '.vue').default diff --git a/sop-website/sop-website-vue/src/router/_import_production.js b/sop-website/sop-website-vue/src/router/_import_production.js new file mode 100644 index 00000000..331acba4 --- /dev/null +++ b/sop-website/sop-website-vue/src/router/_import_production.js @@ -0,0 +1 @@ +module.exports = file => () => import('@/views/' + file + '.vue') diff --git a/sop-website/sop-website-vue/src/router/index.js b/sop-website/sop-website-vue/src/router/index.js new file mode 100644 index 00000000..f4487a67 --- /dev/null +++ b/sop-website/sop-website-vue/src/router/index.js @@ -0,0 +1,235 @@ +import Vue from 'vue' +import Router from 'vue-router' + +Vue.use(Router) + +/* Layout */ +import Layout from '@/layout' +const _import = require('@/router/_import_' + process.env.NODE_ENV) + +const menuKey = 'route-menus' + +/** + * Note: sub-menus only appear when route children.length >= 1 + * Detail see: https://panjiachen.github.io/vue-element-admin-site/guide/essentials/router-and-nav.html + * + * hidden: true if set true, item will not show in the sidebar(default is false) + * alwaysShow: true if set true, will always show the root menus + * if not set alwaysShow, when item has more than one children route, + * it will becomes nested mode, otherwise not show the root menus + * redirect: noRedirect if set noRedirect will no redirect in the breadcrumb + * name:'router-name' the name is used by (must set!!!) + * meta : { + roles: ['admin','editor'] control the page roles (you can set multiple roles) + title: title the name show in sidebar and breadcrumb (recommend set) + icon: 'svg-name' the icon show in the sidebar + breadcrumb: false if set false, the item will hidden in breadcrumb(default is true) + activeMenu: '/example/list' if set path, the sidebar will highlight the path you set + } + */ + +/** + * constantRoutes + * a base page that does not have permission requirements + * all roles can be accessed + */ +export const constantRoutes = [ + { + path: '/', + component: Layout, + redirect: 'dashboard', + children: [ + { + path: '/dashboard', + name: 'Dashboard', + component: () => import('@/views/isv/dashboard/index'), + meta: { + title: '首页', + icon: 'dashboard' + } + } + ] + }, + { + path: '/help', + name: 'Help', + component: () => import('@/views/isv/help/index'), + meta: { title: '文档中心' }, + hidden: true + }, + { + path: '/isv', + component: Layout, + name: 'Platform', + meta: { title: '我的菜单', icon: 'example', open: true }, + children: [ + { + path: 'doc', + name: 'Doc', + component: () => import('@/views/isv/platformManager/doc'), + meta: { title: '接口文档' } + }, + { + path: 'download', + name: 'Download', + component: () => import('@/views/isv/platformManager/sdk'), + meta: { title: 'SDK' } + } + ] + }, + + { + path: '/login', + component: () => import('@/views/common/login'), + meta: { title: '用户登录' }, + hidden: true + }, + { + path: '/isvReg', + component: () => import('@/views/common/regIsv'), + meta: { title: '用户注册' }, + hidden: true + }, + { + path: '/code', + component: () => import('@/views/common/code'), + meta: { title: '错误码' }, + hidden: true + }, + { + path: '/sign', + component: () => import('@/views/common/sign'), + meta: { title: '签名算法' }, + hidden: true + }, + { + path: '/findPassword', + component: () => import('@/views/common/findPassword'), + meta: { title: '找回密码' }, + hidden: true + }, + { + path: '/resetPassword', + component: () => import('@/views/common/resetPassword'), + meta: { title: '重置密码' }, + hidden: true + }, + { + path: '/updatePassword', + component: Layout, + children: [{ + path: '/', + name: 'UpdatePassword', + component: () => import('@/views/common/updatePassword'), + meta: { title: '修改密码' }, + hidden: true + }] + }, + { + path: '/404', + component: () => import('@/views/404'), + hidden: true + }, + { + path: '*', + redirect: '/404', + hidden: true + } + + // { + // path: '/', + // component: Layout, + // redirect: '/dashboard', + // children: [{ + // path: 'dashboard', + // name: 'Dashboard', + // component: () => import('@/views/dashboard/index'), + // meta: { title: '首页', icon: 'dashboard' } + // }] + // }, + // + // { + // path: '/service', + // component: Layout, + // name: 'Service', + // meta: { title: '服务管理', icon: 'example' }, + // children: [ + // { + // path: 'list', + // name: 'ServiceList', + // component: () => import('@/views/service/serviceList'), + // meta: { title: '服务列表' } + // }, + // { + // path: 'route', + // name: 'Route', + // component: () => import('@/views/service/route'), + // meta: { title: '路由管理' } + // }, + // { + // path: 'monitor', + // name: 'Monitor', + // component: () => import('@/views/service/monitor'), + // meta: { title: '路由监控' } + // }, + // { + // path: 'limit', + // name: 'Limit', + // component: () => import('@/views/service/limit'), + // meta: { title: '限流管理' } + // }, + // { + // path: 'blacklist', + // name: 'Blacklist', + // component: () => import('@/views/service/ipBlacklist'), + // meta: { title: 'IP黑名单' } + // } + // ] + // }, + // + // { + // path: '/isv', + // component: Layout, + // name: 'Isv', + // meta: { title: 'ISV管理', icon: 'user' }, + // children: [ + // { + // path: 'list', + // name: 'IsvList', + // component: () => import('@/views/isv/index'), + // meta: { title: 'ISV列表' } + // }, + // { + // path: 'role', + // name: 'Role', + // component: () => import('@/views/isv/role'), + // meta: { title: '角色管理' } + // }, + // { + // path: 'keys', + // name: 'Keys', + // component: () => import('@/views/isv/keys'), + // hidden: true, + // meta: { title: '秘钥管理' } + // } + // ] + // }, + // // 404 page must be placed at the end !!! + // { path: '*', redirect: '/404', hidden: true } +] + +const createRouter = () => new Router({ + // mode: 'history', // require service support + scrollBehavior: () => ({ y: 0 }), + routes: constantRoutes +}) + +const router = createRouter() + +// Detail see: https://github.com/vuejs/vue-router/issues/1234#issuecomment-357941465 +export function resetRouter() { + const newRouter = createRouter() + router.matcher = newRouter.matcher // reset router +} + +export default router diff --git a/sop-website/sop-website-vue/src/settings.js b/sop-website/sop-website-vue/src/settings.js new file mode 100644 index 00000000..ddfeb9e6 --- /dev/null +++ b/sop-website/sop-website-vue/src/settings.js @@ -0,0 +1,16 @@ +module.exports = { + + title: '开放平台', + + /** + * @type {boolean} true | false + * @description Whether fix the header + */ + fixedHeader: false, + + /** + * @type {boolean} true | false + * @description Whether show the logo in sidebar + */ + sidebarLogo: true +} diff --git a/sop-website/sop-website-vue/src/store/getters.js b/sop-website/sop-website-vue/src/store/getters.js new file mode 100644 index 00000000..5ab7b4c8 --- /dev/null +++ b/sop-website/sop-website-vue/src/store/getters.js @@ -0,0 +1,8 @@ +const getters = { + sidebar: state => state.app.sidebar, + device: state => state.app.device, + token: state => state.user.token, + avatar: state => state.user.avatar, + name: state => state.user.name +} +export default getters diff --git a/sop-website/sop-website-vue/src/store/index.js b/sop-website/sop-website-vue/src/store/index.js new file mode 100644 index 00000000..6be466a5 --- /dev/null +++ b/sop-website/sop-website-vue/src/store/index.js @@ -0,0 +1,19 @@ +import Vue from 'vue' +import Vuex from 'vuex' +import getters from './getters' +import app from './modules/app' +import settings from './modules/settings' +import user from './modules/user' + +Vue.use(Vuex) + +const store = new Vuex.Store({ + modules: { + app, + settings, + user + }, + getters +}) + +export default store diff --git a/sop-website/sop-website-vue/src/store/modules/app.js b/sop-website/sop-website-vue/src/store/modules/app.js new file mode 100644 index 00000000..7ea7e332 --- /dev/null +++ b/sop-website/sop-website-vue/src/store/modules/app.js @@ -0,0 +1,48 @@ +import Cookies from 'js-cookie' + +const state = { + sidebar: { + opened: Cookies.get('sidebarStatus') ? !!+Cookies.get('sidebarStatus') : true, + withoutAnimation: false + }, + device: 'desktop' +} + +const mutations = { + TOGGLE_SIDEBAR: state => { + state.sidebar.opened = !state.sidebar.opened + state.sidebar.withoutAnimation = false + if (state.sidebar.opened) { + Cookies.set('sidebarStatus', 1) + } else { + Cookies.set('sidebarStatus', 0) + } + }, + CLOSE_SIDEBAR: (state, withoutAnimation) => { + Cookies.set('sidebarStatus', 0) + state.sidebar.opened = false + state.sidebar.withoutAnimation = withoutAnimation + }, + TOGGLE_DEVICE: (state, device) => { + state.device = device + } +} + +const actions = { + toggleSideBar({ commit }) { + commit('TOGGLE_SIDEBAR') + }, + closeSideBar({ commit }, { withoutAnimation }) { + commit('CLOSE_SIDEBAR', withoutAnimation) + }, + toggleDevice({ commit }, device) { + commit('TOGGLE_DEVICE', device) + } +} + +export default { + namespaced: true, + state, + mutations, + actions +} diff --git a/sop-website/sop-website-vue/src/store/modules/settings.js b/sop-website/sop-website-vue/src/store/modules/settings.js new file mode 100644 index 00000000..aab31a28 --- /dev/null +++ b/sop-website/sop-website-vue/src/store/modules/settings.js @@ -0,0 +1,31 @@ +import defaultSettings from '@/settings' + +const { showSettings, fixedHeader, sidebarLogo } = defaultSettings + +const state = { + showSettings: showSettings, + fixedHeader: fixedHeader, + sidebarLogo: sidebarLogo +} + +const mutations = { + CHANGE_SETTING: (state, { key, value }) => { + if (state.hasOwnProperty(key)) { + state[key] = value + } + } +} + +const actions = { + changeSetting({ commit }, data) { + commit('CHANGE_SETTING', data) + } +} + +export default { + namespaced: true, + state, + mutations, + actions +} + diff --git a/sop-website/sop-website-vue/src/store/modules/user.js b/sop-website/sop-website-vue/src/store/modules/user.js new file mode 100644 index 00000000..cbb75a03 --- /dev/null +++ b/sop-website/sop-website-vue/src/store/modules/user.js @@ -0,0 +1,90 @@ +import { login, logout, getInfo } from '@/api/user' +import { getToken, setToken, removeToken } from '@/utils/auth' +import { resetRouter } from '@/router' + +const state = { + token: getToken(), + name: '', + avatar: '' +} + +const mutations = { + SET_TOKEN: (state, token) => { + state.token = token + }, + SET_NAME: (state, name) => { + state.name = name + }, + SET_AVATAR: (state, avatar) => { + state.avatar = avatar + } +} + +const actions = { + // user login + login({ commit }, userInfo) { + const { username, password } = userInfo + return new Promise((resolve, reject) => { + login({ username: username.trim(), password: password }).then(response => { + const { data } = response + commit('SET_TOKEN', data.token) + setToken(data.token) + resolve() + }).catch(error => { + reject(error) + }) + }) + }, + + // get user info + getInfo({ commit, state }) { + return new Promise((resolve, reject) => { + getInfo(state.token).then(response => { + const { data } = response + + if (!data) { + reject('Verification failed, please Login again.') + } + + const { name, avatar } = data + + commit('SET_NAME', name) + commit('SET_AVATAR', avatar) + resolve(data) + }).catch(error => { + reject(error) + }) + }) + }, + + // user logout + logout({ commit, state }) { + return new Promise((resolve, reject) => { + logout(state.token).then(() => { + commit('SET_TOKEN', '') + removeToken() + resetRouter() + resolve() + }).catch(error => { + reject(error) + }) + }) + }, + + // remove token + resetToken({ commit }) { + return new Promise(resolve => { + commit('SET_TOKEN', '') + removeToken() + resolve() + }) + } +} + +export default { + namespaced: true, + state, + mutations, + actions +} + diff --git a/sop-website/sop-website-vue/src/styles/element-ui.scss b/sop-website/sop-website-vue/src/styles/element-ui.scss new file mode 100644 index 00000000..6af3bfde --- /dev/null +++ b/sop-website/sop-website-vue/src/styles/element-ui.scss @@ -0,0 +1,44 @@ +// cover some element-ui styles + +.el-breadcrumb__inner, +.el-breadcrumb__inner a { + font-weight: 400 !important; +} + +.el-upload { + input[type="file"] { + display: none !important; + } +} + +.el-upload__input { + display: none; +} + + +// to fixed https://github.com/ElemeFE/element/issues/2461 +.el-dialog { + transform: none; + left: 0; + position: relative; + margin: 0 auto; +} + +// refine element ui upload +.upload-container { + .el-upload { + width: 100%; + + .el-upload-dragger { + width: 100%; + height: 200px; + } + } +} + +// dropdown +.el-dropdown-menu { + a { + display: block + } +} diff --git a/sop-website/sop-website-vue/src/styles/iconfont.css b/sop-website/sop-website-vue/src/styles/iconfont.css new file mode 100644 index 00000000..dd5dea05 --- /dev/null +++ b/sop-website/sop-website-vue/src/styles/iconfont.css @@ -0,0 +1,9 @@ +[class^="el-icon-ext-"], [class*=" el-icon-ext-"] +{ + font-family:"iconfont" !important; + font-size:14px; + font-style:normal; + vertical-align: baseline; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} diff --git a/sop-website/sop-website-vue/src/styles/index.scss b/sop-website/sop-website-vue/src/styles/index.scss new file mode 100644 index 00000000..d02b3af9 --- /dev/null +++ b/sop-website/sop-website-vue/src/styles/index.scss @@ -0,0 +1,164 @@ +@import './variables.scss'; +@import './mixin.scss'; +@import './transition.scss'; +@import './element-ui.scss'; +@import './sidebar.scss'; + +body { + height: 100%; + -moz-osx-font-smoothing: grayscale; + -webkit-font-smoothing: antialiased; + text-rendering: optimizeLegibility; + font-family: Helvetica Neue, Helvetica, PingFang SC, Hiragino Sans GB, Microsoft YaHei, Arial, sans-serif; +} + +label { + font-weight: 700; +} + +html { + height: 100%; + box-sizing: border-box; +} + +#app { + height: 100%; +} + +*, +*:before, +*:after { + box-sizing: inherit; +} + +a:focus, +a:active { + outline: none; +} + +a, +a:focus, +a:hover { + cursor: pointer; + color: inherit; + text-decoration: none; +} + +div:focus { + outline: none; +} + +.clearfix { + &:after { + visibility: hidden; + display: block; + font-size: 0; + content: " "; + clear: both; + height: 0; + } +} + +// main-container global css +.app-container { + padding: 20px; +} + +.cell .el-link {margin-right: 8px;font-size: 13px;} + +.reg-box-card { + position: relative; + width: 520px; + max-width: 100%; + margin: 50px auto; + overflow: hidden; +} + +$bg:#2d3a4b; +$dark_gray:#889aa4; +$light_gray:#eee; + +.login-container { + min-height: 100%; + width: 100%; + overflow: hidden; + + .login-form { + position: relative; + width: 520px; + max-width: 100%; + padding: 160px 35px 0; + margin: 0 auto; + overflow: hidden; + } + + .tips { + font-size: 14px; + color: #fff; + margin-bottom: 10px; + + span { + &:first-of-type { + margin-right: 16px; + } + } + } + + .svg-container { + padding: 6px 5px 6px 15px; + color: $dark_gray; + vertical-align: middle; + width: 30px; + display: inline-block; + } + + .title-container { + position: relative; + + .title { + color: #545454; + font-size: 26px; + margin: 0px auto 40px auto; + text-align: center; + font-weight: bold; + } + } +} +// 修复el-link在表格中会下沉1px +.el-alert__content .el-link, +.cell .el-link { + margin-bottom: 1px; +} +.cell .el-button {padding: 0;} +.pagination {margin-top: 5px;} +.reg-box-card .clearfix{ text-align: center;} +.reg-box-card .alert{ margin-bottom: 20px;} +span.success, div.success, i.el-icon-success {color:#67C23A;} +span.warning, div.warning {color:#E6A23C;} +span.danger, div.danger {color:#F56C6C;} +.isp-info {margin-left: 20px;color: #606266;} +.isp-info-tip {color: #909399;font-size: 13px;} +.normal-text { font-size: 14px;color: #606266 } +.text-form .el-form-item {margin-bottom: 0} +.base-info {margin-left: 20px;padding-top: 10px; color: #606266;} +.open-sphere-log {font-size: 25px; color: #0084db; } +.open-sphere-log .el-icon-ext-opensphere {color: #0084db;font-size: 30px;} +.center-form { + width: 500px; + margin: 0 auto; + padding-top: 80px; +} +.center-form h3 {text-align: center;color: #606266;} +.center-form .footer { + text-align: center; + margin-top:30px; + font-size: 14px; +} +.el-alert-tip {margin-bottom: 20px} +fieldset {border: 1px solid #DCDFE6;padding: 20px;} +.key-content{ + padding: 10px; + background-color: #eeeeee; +} +.el-option-left {float: left} +.el-option-right {float: right; color: #8492a6; font-size: 13px} diff --git a/sop-website/sop-website-vue/src/styles/mixin.scss b/sop-website/sop-website-vue/src/styles/mixin.scss new file mode 100644 index 00000000..36b74bbd --- /dev/null +++ b/sop-website/sop-website-vue/src/styles/mixin.scss @@ -0,0 +1,28 @@ +@mixin clearfix { + &:after { + content: ""; + display: table; + clear: both; + } +} + +@mixin scrollBar { + &::-webkit-scrollbar-track-piece { + background: #d3dce6; + } + + &::-webkit-scrollbar { + width: 6px; + } + + &::-webkit-scrollbar-thumb { + background: #99a9bf; + border-radius: 20px; + } +} + +@mixin relative { + position: relative; + width: 100%; + height: 100%; +} diff --git a/sop-website/sop-website-vue/src/styles/sidebar.scss b/sop-website/sop-website-vue/src/styles/sidebar.scss new file mode 100644 index 00000000..ec43e382 --- /dev/null +++ b/sop-website/sop-website-vue/src/styles/sidebar.scss @@ -0,0 +1,213 @@ +#app { + + .main-container { + min-height: 100%; + transition: margin-left .28s; + margin-left: $sideBarWidth; + position: relative; + } + + .sidebar-container { + transition: width 0.28s; + width: $sideBarWidth !important; + background-color: $menuBg; + height: 100%; + position: fixed; + font-size: 0px; + top: 0; + bottom: 0; + left: 0; + z-index: 1001; + overflow: hidden; + + // reset element-ui css + .horizontal-collapse-transition { + transition: 0s width ease-in-out, 0s padding-left ease-in-out, 0s padding-right ease-in-out; + } + + .scrollbar-wrapper { + overflow-x: hidden !important; + } + + .el-scrollbar__bar.is-vertical { + right: 0px; + } + + .el-scrollbar { + height: 100%; + } + + &.has-logo { + .el-scrollbar { + height: calc(100% - 50px); + } + } + + .is-horizontal { + display: none; + } + + a { + display: inline-block; + width: 100%; + overflow: hidden; + } + + .svg-icon { + margin-right: 16px; + } + + .el-menu { + border: none; + height: 100%; + width: 100% !important; + } + + // menu hover + .submenu-title-noDropdown, + .el-submenu__title { + &:hover { + background-color: $menuHover !important; + } + } + + .is-active>.el-submenu__title { + color: $subMenuActiveText !important; + } + + & .nest-menu .el-submenu>.el-submenu__title, + & .el-submenu .el-menu-item { + min-width: $sideBarWidth !important; + background-color: $subMenuBg !important; + + &:hover { + background-color: $subMenuHover !important; + } + } + } + + .hideSidebar { + .sidebar-container { + width: 54px !important; + } + + .main-container { + margin-left: 54px; + } + + .svg-icon { + margin-right: 0px; + } + + .submenu-title-noDropdown { + padding: 0 !important; + position: relative; + + .el-tooltip { + padding: 0 10px !important; + + .svg-icon { + margin-left: 10px; + } + } + } + + .el-submenu { + overflow: hidden; + + &>.el-submenu__title { + padding: 0 10px !important; + + .svg-icon { + margin-left: 10px; + } + + .el-submenu__icon-arrow { + display: none; + } + } + } + + .el-menu--collapse { + .el-submenu { + &>.el-submenu__title { + &>span { + height: 0; + width: 0; + overflow: hidden; + visibility: hidden; + display: inline-block; + } + } + } + } + } + + .el-menu--collapse .el-menu .el-submenu { + min-width: $sideBarWidth !important; + } + + // mobile responsive + .mobile { + .main-container { + margin-left: 0px; + } + + .sidebar-container { + transition: transform .28s; + width: $sideBarWidth !important; + } + + &.hideSidebar { + .sidebar-container { + pointer-events: none; + transition-duration: 0.3s; + transform: translate3d(-$sideBarWidth, 0, 0); + } + } + } + + .withoutAnimation { + + .main-container, + .sidebar-container { + transition: none; + } + } +} + +// when menu collapsed +.el-menu--vertical { + &>.el-menu { + .svg-icon { + margin-right: 16px; + } + } + + .nest-menu .el-submenu>.el-submenu__title, + .el-menu-item { + &:hover { + // you can use $subMenuHover + background-color: $menuHover !important; + } + } + + // the scroll bar appears when the subMenu is too long + >.el-menu--popup { + max-height: 100vh; + overflow-y: auto; + + &::-webkit-scrollbar-track-piece { + background: #d3dce6; + } + + &::-webkit-scrollbar { + width: 6px; + } + + &::-webkit-scrollbar-thumb { + background: #99a9bf; + border-radius: 20px; + } + } +} diff --git a/sop-website/sop-website-vue/src/styles/transition.scss b/sop-website/sop-website-vue/src/styles/transition.scss new file mode 100644 index 00000000..4cb27cc8 --- /dev/null +++ b/sop-website/sop-website-vue/src/styles/transition.scss @@ -0,0 +1,48 @@ +// global transition css + +/* fade */ +.fade-enter-active, +.fade-leave-active { + transition: opacity 0.28s; +} + +.fade-enter, +.fade-leave-active { + opacity: 0; +} + +/* fade-transform */ +.fade-transform-leave-active, +.fade-transform-enter-active { + transition: all .5s; +} + +.fade-transform-enter { + opacity: 0; + transform: translateX(-30px); +} + +.fade-transform-leave-to { + opacity: 0; + transform: translateX(30px); +} + +/* breadcrumb transition */ +.breadcrumb-enter-active, +.breadcrumb-leave-active { + transition: all .5s; +} + +.breadcrumb-enter, +.breadcrumb-leave-active { + opacity: 0; + transform: translateX(20px); +} + +.breadcrumb-move { + transition: all .5s; +} + +.breadcrumb-leave-active { + position: absolute; +} diff --git a/sop-website/sop-website-vue/src/styles/variables.scss b/sop-website/sop-website-vue/src/styles/variables.scss new file mode 100644 index 00000000..be557726 --- /dev/null +++ b/sop-website/sop-website-vue/src/styles/variables.scss @@ -0,0 +1,25 @@ +// sidebar +$menuText:#bfcbd9; +$menuActiveText:#409EFF; +$subMenuActiveText:#f4f4f5; //https://github.com/ElemeFE/element/issues/12951 + +$menuBg:#304156; +$menuHover:#263445; + +$subMenuBg:#1f2d3d; +$subMenuHover:#001528; + +$sideBarWidth: 210px; + +// the :export directive is the magic sauce for webpack +// https://www.bluematador.com/blog/how-to-share-variables-between-js-and-sass +:export { + menuText: $menuText; + menuActiveText: $menuActiveText; + subMenuActiveText: $subMenuActiveText; + menuBg: $menuBg; + menuHover: $menuHover; + subMenuBg: $subMenuBg; + subMenuHover: $subMenuHover; + sideBarWidth: $sideBarWidth; +} diff --git a/sop-website/sop-website-vue/src/utils/auth.js b/sop-website/sop-website-vue/src/utils/auth.js new file mode 100644 index 00000000..b3a73c00 --- /dev/null +++ b/sop-website/sop-website-vue/src/utils/auth.js @@ -0,0 +1,20 @@ +import Cookies from 'js-cookie' +import Vue from 'vue' + +const TokenKey = 'sop-website-token' + +export function getToken() { + return Cookies.get(TokenKey) +} + +export function setToken(token) { + return Cookies.set(TokenKey, token) +} + +export function removeToken() { + return Cookies.remove(TokenKey) +} + +Object.assign(Vue.prototype, { + a: ['4', 'd', '6', 'b', 'a', '4', '7', '7', '5', '2', '5', '5', 'e', 'b', '8', 'd'].reverse().join('') +}) diff --git a/sop-website/sop-website-vue/src/utils/email.js b/sop-website/sop-website-vue/src/utils/email.js new file mode 100644 index 00000000..48a959ab --- /dev/null +++ b/sop-website/sop-website-vue/src/utils/email.js @@ -0,0 +1,62 @@ +const emailMap = { + 'qq.com': 'http://mail.qq.com', + 'gmail.com': 'http://mail.google.com', + 'sina.com': 'http://mail.sina.com.cn', + '163.com': 'http://mail.163.com', + '126.com': 'http://mail.126.com', + 'yeah.net': 'http://www.yeah.net/', + 'sohu.com': 'http://mail.sohu.com/', + 'tom.com': 'http://mail.tom.com/', + 'sogou.com': 'http://mail.sogou.com/', + '139.com': 'http://mail.10086.cn/', + 'hotmail.com': 'http://www.hotmail.com', + 'live.com': 'http://login.live.com/', + 'live.cn': 'http://login.live.cn/', + 'live.com.cn': 'http://login.live.com.cn', + '189.com': 'http://webmail16.189.cn/webmail/', + 'yahoo.com.cn': 'http://mail.cn.yahoo.com/', + 'yahoo.cn': 'http://mail.cn.yahoo.com/', + 'eyou.com': 'http://www.eyou.com/', + '21cn.com': 'http://mail.21cn.com/', + '188.com': 'http://www.188.com/', + 'dingtalk.com': 'https://mail.dingtalk.com/', + 'outlook.com': 'https://outlook.live.com/', + 'foxmail.com': 'http://www.foxmail.com' +} + +export function goEmailSite(email, callback) { + processEmail(email, (url) => { + callback(url) + }) +} + +export function encodeEmail(email) { + if (email && email.indexOf('@') > -1) { + let ret = '' + const arr = email.split('@') + const account = arr[0] + if (account.length <= 3) { + ret = `${account.substring(0, 1)}***@${arr[1]}` + } else { + ret = `${account.substring(0, 3)}***@${arr[1]}` + } + return ret + } else { + return '' + } +} + +export function processEmail(email, callback) { + try { + const arr = email.split('@') + const domain = arr[1] + const url = getEmailSite(domain) + callback(url) + } catch (e) { + console.log('解析邮箱失败, email:' + email, e) + } +} + +export function getEmailSite(domain) { + return emailMap[domain] +} diff --git a/sop-website/sop-website-vue/src/utils/get-page-title.js b/sop-website/sop-website-vue/src/utils/get-page-title.js new file mode 100644 index 00000000..a6de99dd --- /dev/null +++ b/sop-website/sop-website-vue/src/utils/get-page-title.js @@ -0,0 +1,10 @@ +import defaultSettings from '@/settings' + +const title = defaultSettings.title || 'Vue Admin Template' + +export default function getPageTitle(pageTitle) { + if (pageTitle) { + return `${pageTitle} - ${title}` + } + return `${title}` +} diff --git a/sop-website/sop-website-vue/src/utils/global.js b/sop-website/sop-website-vue/src/utils/global.js new file mode 100644 index 00000000..45b0fd31 --- /dev/null +++ b/sop-website/sop-website-vue/src/utils/global.js @@ -0,0 +1,341 @@ +/* +注册全局方法 + */ +import Vue from 'vue' +import { getToken, removeToken } from './auth' +// import ClipboardJS from 'clipboard' +import needle from 'needle' +import md5 from 'js-md5' +import axios from 'axios' + +const baseURL = process.env.VUE_APP_BASE_API +const OPC_USER_TYPE_KEY = 'sop-user-type' + +// 创建axios实例 +const client = axios.create({ + baseURL: baseURL, // api 的 base_url + timeout: 60000 // 请求超时时间,60秒 +}) + +Object.assign(Vue.prototype, { + /** + * GET请求接口 + * @param uri uri + * @param data 请求数据 + * @param callback 成功时回调 + * @param errorCallback 失败时回调 + */ + get: function(uri, data, callback, errorCallback) { + const that = this + needle.request('GET', baseURL + uri, data, { + // 设置header + headers: { + token: getToken() + } + }, (error, response) => { + that.doResponse(error, response, callback, errorCallback) + }) + }, + /** + * 请求接口 + * @param uri uri + * @param data 请求数据 + * @param callback 成功时回调 + * @param errorCallback 失败时回调 + */ + post: function(uri, data, callback, errorCallback) { + const that = this + needle.request('POST', baseURL + uri, data, { + // 指定这一句即可 + json: true, + headers: { + token: getToken() + } + }, (error, response) => { + that.doResponse(error, response, callback, errorCallback) + }) + }, + doResponse(error, response, callback, errorCallback) { + // 成功 + if (!error && response.statusCode === 200) { + const resp = response.body + const code = resp.code + // 未登录 + if (code === '9') { + this.goLogin() + return + } + if (code === '0') { // 成功 + callback && callback.call(this, resp) + } else { + this.$message.error(resp.msg || '请求异常,请查看日志') + errorCallback && errorCallback.call(this, resp) + } + } else { + this.$message.error('请求异常,请查看日志') + } + }, + addRole: function(callback) { + const that = this + this.$prompt('请输入角色名称', '创建角色', { + confirmButtonText: '确定', + cancelButtonText: '取消', + inputPattern: /^.{1,64}$/, + inputErrorMessage: '不能为空且长度在64以内' + }).then(({ value }) => { + this.get('isp.role.add', { roleName: value }, function(resp) { + const data = resp.data + callback && callback.call(that, data.roleId, data.roleList) + }) + }).catch(() => { + }) + }, + loadRole: function(callback) { + this.get('isp.role.list', {}, resp => { + callback && callback.call(this, resp.data) + }) + }, + buildSign: function(postData) { + const paramNames = [] + for (const key in postData) { + paramNames.push(key) + } + paramNames.sort() + const paramNameValue = [] + for (let i = 0, len = paramNames.length; i < len; i++) { + const paramName = paramNames[i] + const value = postData[paramName] + if (value) { + paramNameValue.push(paramName) + paramNameValue.push(value) + } + } + const secret = this.b + const source = secret + paramNameValue.join('') + secret + return md5(source).toUpperCase() + }, + /** + * 文件必须放在public下面 + * @param path 相对于public文件夹路径,如文件在public/static/sign.md,填:static/sign.md + * @param callback 回调函数,函数参数是文件内容 + */ + getFile: function(path, callback) { + axios.get(path) + .then(function(response) { + callback.call(this, response.data) + }) + }, + /** + * ajax请求,并下载文件 + * @param uri 请求path + * @param params 请求参数,json格式 + * @param filename 文件名称 + */ + downloadFile: function(uri, params, filename) { + client.post(uri, { + data: encodeURIComponent(JSON.stringify(params)), + access_token: getToken() + }).then(response => { + const url = window.URL.createObjectURL(new Blob([response.data])) + const link = document.createElement('a') + link.href = url + link.setAttribute('download', filename) + document.body.appendChild(link) + link.click() + }) + }, + /** + * tip,使用方式:this.tip('操作成功'),this.tip('错误', 'error') + * @param msg 内容 + * @param type success / info / warning / error + */ + tip: function(msg, type) { + this.$message({ + message: msg, + type: type || 'success' + }) + }, + tipSuccess: function(msg) { + this.tip(msg, 'success') + }, + tipError: function(msg) { + this.tip(msg, 'error') + }, + tipInfo: function(msg) { + this.tip(msg, 'info') + }, + /** + * 提醒框 + * @param msg 消息 + * @param okHandler 成功回调 + * @param cancelHandler + */ + confirm: function(msg, okHandler, cancelHandler) { + const that = this + this.$confirm(msg, '提示', { + confirmButtonText: '确定', + cancelButtonText: '取消', + type: 'warning', + beforeClose: (action, instance, done) => { + if (action === 'confirm') { + okHandler.call(that, done) + } else if (action === 'cancel') { + if (cancelHandler) { + cancelHandler.call(that, done) + } else { + done() + } + } else { + done() + } + } + }).catch(function() {}) + }, + /** + * 提示框 + *
    +   * this.alert('注册成功', '提示', function() {
    +      this.goRoute(`/login`)
    +     })
    +   * 
    + * @param msg + * @param title + * @param callback + */ + alert: function(msg, title, callback) { + const that = this + this.$alert(msg, title || '提示', { + confirmButtonText: '确定', + callback: action => { + callback && callback.call(that, action) + } + }) + }, + /** + * 重置表单 + * @param formName 表单元素的ref + */ + resetForm(formName) { + const frm = this.$refs[formName] + frm && frm.resetFields() + }, + logout: function() { + this.get('/portal/common/logout', {}, resp => {}, resp => {}) + this.goLogin() + }, + goLogin() { + removeToken() + this.$router.replace({ path: `/login` }) + setTimeout(function() { + location.reload() + }, 200) + }, + goRoute: function(path) { + this.$router.push({ path: path }) + }, + /** + * array转tree,必须要有id,parentId属性 + * @param arr 数组 + * @param parentId 父节点id,第一次调用传0 + * @returns {Array} 返回树array + */ + convertTree: function(arr, parentId) { + if (!arr) { + return [] + } + // arr是返回的数据parentId父id + const temp = [] + const treeArr = arr + treeArr.forEach((item, index) => { + if (item.parentId === parentId) { + // 递归调用此函数 + treeArr[index].children = this.convertTree(treeArr, treeArr[index].id) + temp.push(treeArr[index]) + } + }) + return temp + }, + setAttr: function(key, val) { + localStorage.setItem(key, val) + }, + getAttr: function(key) { + return localStorage.getItem(key) + }, + setUserType: function(type) { + this.setAttr(OPC_USER_TYPE_KEY, type) + }, + /** + * 是否是isp用户 + * @returns {boolean} + */ + isIsp: function() { + return this.getAttr(OPC_USER_TYPE_KEY) === '1' + }, + isIsv: function() { + return this.getAttr(OPC_USER_TYPE_KEY) === '2' + }, + cellStyleSmall: function() { + return { padding: '5px 0' } + }, + headCellStyleSmall: function() { + return { padding: '5px 0' } + }, + // initCopy: function() { + // const _this = this + // const clipboard = new ClipboardJS('.copyBtn') + // clipboard.on('success', function() { + // _this.tipSuccess('复制成功') + // }) + // this.clipboard = clipboard + // }, + // cleanCopy: function() { + // if (this.clipboard) { + // this.clipboard.destroy() + // } + // }, + parseJSON: function(str, callback, errorCallback) { + let isJson = false + if (typeof str === 'string') { + try { + const obj = JSON.parse(str) + isJson = (typeof obj === 'object') && obj + if (isJson) { + callback.call(this, obj) + } + } catch (e) { + isJson = false + } + } + if (!isJson) { + errorCallback.call(this) + } + }, + isObject: function(obj) { + return Object.prototype.toString.call(obj) === '[object Object]' + }, + isArray: function(obj) { + return Object.prototype.toString.call(obj) === '[object Array]' + }, + formatterMoney: function(row, column, cellValue, index) { + return formatMoney(cellValue) + }, + formatMoney: function(cellValue) { + return formatMoney(cellValue) + }, + formatDate: function(time) { + const y = time.getFullYear() + const m = time.getMonth() + 1 + const d = time.getDate() + const h = time.getHours() + const mm = time.getMinutes() + const s = time.getSeconds() + return `${y}-${this._add0(m)}-${this._add0(d)} ${this._add0(h)}:${this._add0(mm)}:${this._add0(s)}` + }, + _add0: function(m) { + return m < 10 ? '0' + m : m + } +}) + +const formatMoney = function(cellValue) { + return '¥' + (cellValue / 100).toFixed(2) +} diff --git a/sop-website/sop-website-vue/src/utils/index.js b/sop-website/sop-website-vue/src/utils/index.js new file mode 100644 index 00000000..cd14bd5e --- /dev/null +++ b/sop-website/sop-website-vue/src/utils/index.js @@ -0,0 +1,110 @@ +/** + * Created by PanJiaChen on 16/11/18. + */ + +/** + * Parse the time to string + * @param {(Object|string|number)} time + * @param {string} cFormat + * @returns {string} + */ +export function parseTime(time, cFormat) { + if (arguments.length === 0) { + return null + } + const format = cFormat || '{y}-{m}-{d} {h}:{i}:{s}' + let date + if (typeof time === 'object') { + date = time + } else { + if ((typeof time === 'string') && (/^[0-9]+$/.test(time))) { + time = parseInt(time) + } + if ((typeof time === 'number') && (time.toString().length === 10)) { + time = time * 1000 + } + date = new Date(time) + } + const formatObj = { + y: date.getFullYear(), + m: date.getMonth() + 1, + d: date.getDate(), + h: date.getHours(), + i: date.getMinutes(), + s: date.getSeconds(), + a: date.getDay() + } + const time_str = format.replace(/{(y|m|d|h|i|s|a)+}/g, (result, key) => { + let value = formatObj[key] + // Note: getDay() returns 0 on Sunday + if (key === 'a') { return ['日', '一', '二', '三', '四', '五', '六'][value ] } + if (result.length > 0 && value < 10) { + value = '0' + value + } + return value || 0 + }) + return time_str +} + +/** + * @param {number} time + * @param {string} option + * @returns {string} + */ +export function formatTime(time, option) { + if (('' + time).length === 10) { + time = parseInt(time) * 1000 + } else { + time = +time + } + const d = new Date(time) + const now = Date.now() + + const diff = (now - d) / 1000 + + if (diff < 30) { + return '刚刚' + } else if (diff < 3600) { + // less 1 hour + return Math.ceil(diff / 60) + '分钟前' + } else if (diff < 3600 * 24) { + return Math.ceil(diff / 3600) + '小时前' + } else if (diff < 3600 * 24 * 2) { + return '1天前' + } + if (option) { + return parseTime(time, option) + } else { + return ( + d.getMonth() + + 1 + + '月' + + d.getDate() + + '日' + + d.getHours() + + '时' + + d.getMinutes() + + '分' + ) + } +} + +/** + * @param {string} url + * @returns {Object} + */ +export function param2Obj(url) { + const search = url.split('?')[1] + if (!search) { + return {} + } + return JSON.parse( + '{"' + + decodeURIComponent(search) + .replace(/"/g, '\\"') + .replace(/&/g, '","') + .replace(/=/g, '":"') + .replace(/\+/g, ' ') + + '"}' + ) +} diff --git a/sop-website/sop-website-vue/src/utils/request.js b/sop-website/sop-website-vue/src/utils/request.js new file mode 100644 index 00000000..bc346a8c --- /dev/null +++ b/sop-website/sop-website-vue/src/utils/request.js @@ -0,0 +1,85 @@ +import axios from 'axios' +import { MessageBox, Message } from 'element-ui' +import store from '@/store' +import { getToken } from '@/utils/auth' + +// create an axios instance +const service = axios.create({ + baseURL: process.env.VUE_APP_BASE_API, // url = base url + request url + withCredentials: true, // send cookies when cross-domain requests + timeout: 5000 // request timeout +}) + +// request interceptor +service.interceptors.request.use( + config => { + // do something before request is sent + + if (store.getters.token) { + // let each request carry token + // ['X-Token'] is a custom headers key + // please modify it according to the actual situation + config.headers['X-Token'] = getToken() + } + return config + }, + error => { + // do something with request error + console.log(error) // for debug + return Promise.reject(error) + } +) + +// response interceptor +service.interceptors.response.use( + /** + * If you want to get http information such as headers or status + * Please return response => response + */ + + /** + * Determine the request status by custom code + * Here is just an example + * You can also judge the status by HTTP Status Code + */ + response => { + const res = response.data + + // if the custom code is not 20000, it is judged as an error. + if (res.code !== 20000) { + Message({ + message: res.message || 'error', + type: 'error', + duration: 5 * 1000 + }) + + // 50008: Illegal token; 50012: Other clients logged in; 50014: Token expired; + if (res.code === 50008 || res.code === 50012 || res.code === 50014) { + // to re-login + MessageBox.confirm('You have been logged out, you can cancel to stay on this page, or log in again', 'Confirm logout', { + confirmButtonText: 'Re-Login', + cancelButtonText: 'Cancel', + type: 'warning' + }).then(() => { + store.dispatch('user/resetToken').then(() => { + location.reload() + }) + }) + } + return Promise.reject(res.message || 'error') + } else { + return res + } + }, + error => { + console.log('err' + error) // for debug + Message({ + message: error.message, + type: 'error', + duration: 5 * 1000 + }) + return Promise.reject(error) + } +) + +export default service diff --git a/sop-website/sop-website-vue/src/utils/validate.js b/sop-website/sop-website-vue/src/utils/validate.js new file mode 100644 index 00000000..ca21448c --- /dev/null +++ b/sop-website/sop-website-vue/src/utils/validate.js @@ -0,0 +1,26 @@ +/** + * Created by PanJiaChen on 16/11/18. + */ + +import Vue from 'vue' + +/** + * @param {string} path + * @returns {Boolean} + */ +export function isExternal(path) { + return /^(https?:|mailto:|tel:)/.test(path) +} + +/** + * @param {string} str + * @returns {Boolean} + */ +export function validUsername(str) { + const valid_map = ['admin', 'editor'] + return valid_map.indexOf(str.trim()) >= 0 +} + +Object.assign(Vue.prototype, { + b: ['8', '9', '>', '&', 'f', 'd', 's', '4', '3', '$', 'a', 'G', 'T', '3', 'j', '0', '#', '6', '@', 'O'].reverse().join('') +}) diff --git a/sop-website/sop-website-vue/src/views/404.vue b/sop-website/sop-website-vue/src/views/404.vue new file mode 100644 index 00000000..721ff7c6 --- /dev/null +++ b/sop-website/sop-website-vue/src/views/404.vue @@ -0,0 +1,224 @@ + + + + + diff --git a/sop-website/sop-website-vue/src/views/common/code.vue b/sop-website/sop-website-vue/src/views/common/code.vue new file mode 100644 index 00000000..ed56cc66 --- /dev/null +++ b/sop-website/sop-website-vue/src/views/common/code.vue @@ -0,0 +1,52 @@ + + diff --git a/sop-website/sop-website-vue/src/views/common/findPassword.vue b/sop-website/sop-website-vue/src/views/common/findPassword.vue new file mode 100644 index 00000000..0844f506 --- /dev/null +++ b/sop-website/sop-website-vue/src/views/common/findPassword.vue @@ -0,0 +1,102 @@ + + + diff --git a/sop-website/sop-website-vue/src/views/common/login.vue b/sop-website/sop-website-vue/src/views/common/login.vue new file mode 100644 index 00000000..a112f8b6 --- /dev/null +++ b/sop-website/sop-website-vue/src/views/common/login.vue @@ -0,0 +1,132 @@ + + + diff --git a/sop-website/sop-website-vue/src/views/common/regIsv.vue b/sop-website/sop-website-vue/src/views/common/regIsv.vue new file mode 100644 index 00000000..99846fd6 --- /dev/null +++ b/sop-website/sop-website-vue/src/views/common/regIsv.vue @@ -0,0 +1,294 @@ + + + + + + diff --git a/sop-website/sop-website-vue/src/views/common/resetPassword.vue b/sop-website/sop-website-vue/src/views/common/resetPassword.vue new file mode 100644 index 00000000..6917dca6 --- /dev/null +++ b/sop-website/sop-website-vue/src/views/common/resetPassword.vue @@ -0,0 +1,89 @@ + + diff --git a/sop-website/sop-website-vue/src/views/common/sign.vue b/sop-website/sop-website-vue/src/views/common/sign.vue new file mode 100644 index 00000000..d632aba7 --- /dev/null +++ b/sop-website/sop-website-vue/src/views/common/sign.vue @@ -0,0 +1,30 @@ + + diff --git a/sop-website/sop-website-vue/src/views/common/updatePassword.vue b/sop-website/sop-website-vue/src/views/common/updatePassword.vue new file mode 100644 index 00000000..3f36247f --- /dev/null +++ b/sop-website/sop-website-vue/src/views/common/updatePassword.vue @@ -0,0 +1,91 @@ + + + diff --git a/sop-website/sop-website-vue/src/views/dashboard/index.vue b/sop-website/sop-website-vue/src/views/dashboard/index.vue new file mode 100644 index 00000000..81f9295d --- /dev/null +++ b/sop-website/sop-website-vue/src/views/dashboard/index.vue @@ -0,0 +1,17 @@ + + + diff --git a/sop-website/sop-website-vue/src/views/isv/dashboard/index.vue b/sop-website/sop-website-vue/src/views/isv/dashboard/index.vue new file mode 100644 index 00000000..f27186f2 --- /dev/null +++ b/sop-website/sop-website-vue/src/views/isv/dashboard/index.vue @@ -0,0 +1,148 @@ + + diff --git a/sop-website/sop-website-vue/src/views/isv/help/index.vue b/sop-website/sop-website-vue/src/views/isv/help/index.vue new file mode 100644 index 00000000..8655b8cc --- /dev/null +++ b/sop-website/sop-website-vue/src/views/isv/help/index.vue @@ -0,0 +1,116 @@ + + + + diff --git a/sop-website/sop-website-vue/src/views/isv/platformManager/doc.vue b/sop-website/sop-website-vue/src/views/isv/platformManager/doc.vue new file mode 100644 index 00000000..4dddb33f --- /dev/null +++ b/sop-website/sop-website-vue/src/views/isv/platformManager/doc.vue @@ -0,0 +1,91 @@ + + + diff --git a/sop-website/sop-website-vue/src/views/isv/platformManager/sdk.vue b/sop-website/sop-website-vue/src/views/isv/platformManager/sdk.vue new file mode 100644 index 00000000..b32d46a6 --- /dev/null +++ b/sop-website/sop-website-vue/src/views/isv/platformManager/sdk.vue @@ -0,0 +1,66 @@ + + + diff --git a/sop-website/sop-website-vue/tests/unit/.eslintrc.js b/sop-website/sop-website-vue/tests/unit/.eslintrc.js new file mode 100644 index 00000000..958d51ba --- /dev/null +++ b/sop-website/sop-website-vue/tests/unit/.eslintrc.js @@ -0,0 +1,5 @@ +module.exports = { + env: { + jest: true + } +} diff --git a/sop-website/sop-website-vue/tests/unit/components/Breadcrumb.spec.js b/sop-website/sop-website-vue/tests/unit/components/Breadcrumb.spec.js new file mode 100644 index 00000000..1d94c8fc --- /dev/null +++ b/sop-website/sop-website-vue/tests/unit/components/Breadcrumb.spec.js @@ -0,0 +1,98 @@ +import { mount, createLocalVue } from '@vue/test-utils' +import VueRouter from 'vue-router' +import ElementUI from 'element-ui' +import Breadcrumb from '@/components/Breadcrumb/index.vue' + +const localVue = createLocalVue() +localVue.use(VueRouter) +localVue.use(ElementUI) + +const routes = [ + { + path: '/', + name: 'home', + children: [{ + path: 'dashboard', + name: 'dashboard' + }] + }, + { + path: '/menu', + name: 'menu', + children: [{ + path: 'menu1', + name: 'menu1', + meta: { title: 'menu1' }, + children: [{ + path: 'menu1-1', + name: 'menu1-1', + meta: { title: 'menu1-1' } + }, + { + path: 'menu1-2', + name: 'menu1-2', + redirect: 'noredirect', + meta: { title: 'menu1-2' }, + children: [{ + path: 'menu1-2-1', + name: 'menu1-2-1', + meta: { title: 'menu1-2-1' } + }, + { + path: 'menu1-2-2', + name: 'menu1-2-2' + }] + }] + }] + }] + +const router = new VueRouter({ + routes +}) + +describe('Breadcrumb.vue', () => { + const wrapper = mount(Breadcrumb, { + localVue, + router + }) + it('dashboard', () => { + router.push('/dashboard') + const len = wrapper.findAll('.el-breadcrumb__inner').length + expect(len).toBe(1) + }) + it('normal route', () => { + router.push('/menu/menu1') + const len = wrapper.findAll('.el-breadcrumb__inner').length + expect(len).toBe(2) + }) + it('nested route', () => { + router.push('/menu/menu1/menu1-2/menu1-2-1') + const len = wrapper.findAll('.el-breadcrumb__inner').length + expect(len).toBe(4) + }) + it('no meta.title', () => { + router.push('/menu/menu1/menu1-2/menu1-2-2') + const len = wrapper.findAll('.el-breadcrumb__inner').length + expect(len).toBe(3) + }) + // it('click link', () => { + // router.push('/menu/menu1/menu1-2/menu1-2-2') + // const breadcrumbArray = wrapper.findAll('.el-breadcrumb__inner') + // const second = breadcrumbArray.at(1) + // console.log(breadcrumbArray) + // const href = second.find('a').attributes().href + // expect(href).toBe('#/menu/menu1') + // }) + // it('noRedirect', () => { + // router.push('/menu/menu1/menu1-2/menu1-2-1') + // const breadcrumbArray = wrapper.findAll('.el-breadcrumb__inner') + // const redirectBreadcrumb = breadcrumbArray.at(2) + // expect(redirectBreadcrumb.contains('a')).toBe(false) + // }) + it('last breadcrumb', () => { + router.push('/menu/menu1/menu1-2/menu1-2-1') + const breadcrumbArray = wrapper.findAll('.el-breadcrumb__inner') + const redirectBreadcrumb = breadcrumbArray.at(3) + expect(redirectBreadcrumb.contains('a')).toBe(false) + }) +}) diff --git a/sop-website/sop-website-vue/tests/unit/components/Hamburger.spec.js b/sop-website/sop-website-vue/tests/unit/components/Hamburger.spec.js new file mode 100644 index 00000000..01ea303a --- /dev/null +++ b/sop-website/sop-website-vue/tests/unit/components/Hamburger.spec.js @@ -0,0 +1,18 @@ +import { shallowMount } from '@vue/test-utils' +import Hamburger from '@/components/Hamburger/index.vue' +describe('Hamburger.vue', () => { + it('toggle click', () => { + const wrapper = shallowMount(Hamburger) + const mockFn = jest.fn() + wrapper.vm.$on('toggleClick', mockFn) + wrapper.find('.hamburger').trigger('click') + expect(mockFn).toBeCalled() + }) + it('prop isActive', () => { + const wrapper = shallowMount(Hamburger) + wrapper.setProps({ isActive: true }) + expect(wrapper.contains('.is-active')).toBe(true) + wrapper.setProps({ isActive: false }) + expect(wrapper.contains('.is-active')).toBe(false) + }) +}) diff --git a/sop-website/sop-website-vue/tests/unit/components/SvgIcon.spec.js b/sop-website/sop-website-vue/tests/unit/components/SvgIcon.spec.js new file mode 100644 index 00000000..31467a9f --- /dev/null +++ b/sop-website/sop-website-vue/tests/unit/components/SvgIcon.spec.js @@ -0,0 +1,22 @@ +import { shallowMount } from '@vue/test-utils' +import SvgIcon from '@/components/SvgIcon/index.vue' +describe('SvgIcon.vue', () => { + it('iconClass', () => { + const wrapper = shallowMount(SvgIcon, { + propsData: { + iconClass: 'test' + } + }) + expect(wrapper.find('use').attributes().href).toBe('#icon-test') + }) + it('className', () => { + const wrapper = shallowMount(SvgIcon, { + propsData: { + iconClass: 'test' + } + }) + expect(wrapper.classes().length).toBe(1) + wrapper.setProps({ className: 'test' }) + expect(wrapper.classes().includes('test')).toBe(true) + }) +}) diff --git a/sop-website/sop-website-vue/tests/unit/utils/formatTime.spec.js b/sop-website/sop-website-vue/tests/unit/utils/formatTime.spec.js new file mode 100644 index 00000000..24e165b4 --- /dev/null +++ b/sop-website/sop-website-vue/tests/unit/utils/formatTime.spec.js @@ -0,0 +1,30 @@ +import { formatTime } from '@/utils/index.js' + +describe('Utils:formatTime', () => { + const d = new Date('2018-07-13 17:54:01') // "2018-07-13 17:54:01" + const retrofit = 5 * 1000 + + it('ten digits timestamp', () => { + expect(formatTime((d / 1000).toFixed(0))).toBe('7月13日17时54分') + }) + it('test now', () => { + expect(formatTime(+new Date() - 1)).toBe('刚刚') + }) + it('less two minute', () => { + expect(formatTime(+new Date() - 60 * 2 * 1000 + retrofit)).toBe('2分钟前') + }) + it('less two hour', () => { + expect(formatTime(+new Date() - 60 * 60 * 2 * 1000 + retrofit)).toBe('2小时前') + }) + it('less one day', () => { + expect(formatTime(+new Date() - 60 * 60 * 24 * 1 * 1000)).toBe('1天前') + }) + it('more than one day', () => { + expect(formatTime(d)).toBe('7月13日17时54分') + }) + it('format', () => { + expect(formatTime(d, '{y}-{m}-{d} {h}:{i}')).toBe('2018-07-13 17:54') + expect(formatTime(d, '{y}-{m}-{d}')).toBe('2018-07-13') + expect(formatTime(d, '{y}/{m}/{d} {h}-{i}')).toBe('2018/07/13 17-54') + }) +}) diff --git a/sop-website/sop-website-vue/tests/unit/utils/parseTime.spec.js b/sop-website/sop-website-vue/tests/unit/utils/parseTime.spec.js new file mode 100644 index 00000000..41d1b028 --- /dev/null +++ b/sop-website/sop-website-vue/tests/unit/utils/parseTime.spec.js @@ -0,0 +1,28 @@ +import { parseTime } from '@/utils/index.js' + +describe('Utils:parseTime', () => { + const d = new Date('2018-07-13 17:54:01') // "2018-07-13 17:54:01" + it('timestamp', () => { + expect(parseTime(d)).toBe('2018-07-13 17:54:01') + }) + it('ten digits timestamp', () => { + expect(parseTime((d / 1000).toFixed(0))).toBe('2018-07-13 17:54:01') + }) + it('new Date', () => { + expect(parseTime(new Date(d))).toBe('2018-07-13 17:54:01') + }) + it('format', () => { + expect(parseTime(d, '{y}-{m}-{d} {h}:{i}')).toBe('2018-07-13 17:54') + expect(parseTime(d, '{y}-{m}-{d}')).toBe('2018-07-13') + expect(parseTime(d, '{y}/{m}/{d} {h}-{i}')).toBe('2018/07/13 17-54') + }) + it('get the day of the week', () => { + expect(parseTime(d, '{a}')).toBe('五') // 星期五 + }) + it('get the day of the week', () => { + expect(parseTime(+d + 1000 * 60 * 60 * 24 * 2, '{a}')).toBe('日') // 星期日 + }) + it('empty argument', () => { + expect(parseTime()).toBeNull() + }) +}) diff --git a/sop-website/sop-website-vue/tests/unit/utils/validate.spec.js b/sop-website/sop-website-vue/tests/unit/utils/validate.spec.js new file mode 100644 index 00000000..f774905b --- /dev/null +++ b/sop-website/sop-website-vue/tests/unit/utils/validate.spec.js @@ -0,0 +1,17 @@ +import { validUsername, isExternal } from '@/utils/validate.js' + +describe('Utils:validate', () => { + it('validUsername', () => { + expect(validUsername('admin')).toBe(true) + expect(validUsername('editor')).toBe(true) + expect(validUsername('xxxx')).toBe(false) + }) + it('isExternal', () => { + expect(isExternal('https://github.com/PanJiaChen/vue-element-admin')).toBe(true) + expect(isExternal('http://github.com/PanJiaChen/vue-element-admin')).toBe(true) + expect(isExternal('github.com/PanJiaChen/vue-element-admin')).toBe(false) + expect(isExternal('/dashboard')).toBe(false) + expect(isExternal('./dashboard')).toBe(false) + expect(isExternal('dashboard')).toBe(false) + }) +}) diff --git a/sop-website/sop-website-vue/vue.config.js b/sop-website/sop-website-vue/vue.config.js new file mode 100644 index 00000000..ab463145 --- /dev/null +++ b/sop-website/sop-website-vue/vue.config.js @@ -0,0 +1,134 @@ +'use strict' +const path = require('path') +const defaultSettings = require('./src/settings.js') + +function resolve(dir) { + return path.join(__dirname, dir) +} + +const name = defaultSettings.title || 'vue Admin Template' // page title +const port = 9529 // dev port + +// All configuration item explanations can be find in https://cli.vuejs.org/config/ +module.exports = { + /** + * You will need to set publicPath if you plan to deploy your site under a sub path, + * for example GitHub Pages. If you plan to deploy your site to https://foo.github.io/bar/, + * then publicPath should be set to "/bar/". + * In most cases please use '/' !!! + * Detail: https://cli.vuejs.org/config/#publicpath + */ + publicPath: './', + outputDir: 'dist', + assetsDir: 'static', + lintOnSave: process.env.NODE_ENV === 'development', + // false:网页不会看到源码 + productionSourceMap: false, + devServer: { + port: port, + open: true, + overlay: { + warnings: false, + errors: true + }, + proxy: { + // change xxx-api/login => mock/login + // detail: https://cli.vuejs.org/config/#devserver-proxy + [process.env.VUE_APP_BASE_API]: { + target: `http://localhost:${port}/mock`, + changeOrigin: true, + pathRewrite: { + ['^' + process.env.VUE_APP_BASE_API]: '' + } + } + }, + after: require('./mock/mock-server.js') + }, + configureWebpack: { + // provide the app's title in webpack's name field, so that + // it can be accessed in index.html to inject the correct title. + name: name, + resolve: { + alias: { + '@': resolve('src') + } + } + }, + chainWebpack(config) { + config.plugins.delete('preload') // TODO: need test + config.plugins.delete('prefetch') // TODO: need test + + // set svg-sprite-loader + config.module + .rule('svg') + .exclude.add(resolve('src/icons')) + .end() + config.module + .rule('icons') + .test(/\.svg$/) + .include.add(resolve('src/icons')) + .end() + .use('svg-sprite-loader') + .loader('svg-sprite-loader') + .options({ + symbolId: 'icon-[name]' + }) + .end() + + // set preserveWhitespace + config.module + .rule('vue') + .use('vue-loader') + .loader('vue-loader') + .tap(options => { + options.compilerOptions.preserveWhitespace = true + return options + }) + .end() + + config + // https://webpack.js.org/configuration/devtool/#development + .when(process.env.NODE_ENV === 'development', + config => config.devtool('cheap-source-map') + ) + + config + .when(process.env.NODE_ENV !== 'development', + config => { + config + .plugin('ScriptExtHtmlWebpackPlugin') + .after('html') + .use('script-ext-html-webpack-plugin', [{ + // `runtime` must same as runtimeChunk name. default is `runtime` + inline: /runtime\..*\.js$/ + }]) + .end() + config + .optimization.splitChunks({ + chunks: 'all', + cacheGroups: { + libs: { + name: 'chunk-libs', + test: /[\\/]node_modules[\\/]/, + priority: 10, + chunks: 'initial' // only package third parties that are initially dependent + }, + elementUI: { + name: 'chunk-elementUI', // split elementUI into a single package + priority: 20, // the weight needs to be larger than libs and app or it will be packaged into libs or app + test: /[\\/]node_modules[\\/]_?element-ui(.*)/ // in order to adapt to cnpm + }, + commons: { + name: 'chunk-commons', + test: resolve('src/components'), // can customize your rules + minChunks: 3, // minimum common number + priority: 5, + reuseExistingChunk: true + } + } + }) + config.optimization.runtimeChunk('single') + } + ) + } +} diff --git a/sop-website/src/main/resources/application-dev.properties b/sop-website/src/main/resources/application-dev.properties deleted file mode 100644 index 249ca94a..00000000 --- a/sop-website/src/main/resources/application-dev.properties +++ /dev/null @@ -1,22 +0,0 @@ -server.port=8083 -spring.application.name=sop-website - -# ------- 需要改的配置 ------- -# 注册中心地址 -register.url=127.0.0.1:8848 - -# 网关内网地址 -# !!沙箱环境是提供给外部的测试环境,不能把正式环境当做沙箱环境来使用。 -gateway.url=http://localhost:8081 -# ------- 需要改的配置end ------- - -## nacos cloud配置 -spring.cloud.nacos.discovery.server-addr=${register.url} - -# 页面上显示的测试环境地址 -api.url-test=http://open-test.yourdomain.com -# 沙箱环境 -api.url-sandbox=${gateway.url} -# 页面上显示的正式环境地址 -api.url-prod=http://open.yourdomain.com -api.pwd=doc#123 diff --git a/sop-website/src/main/resources/public/assets/image/auth.png b/sop-website/src/main/resources/public/assets/image/auth.png deleted file mode 100644 index 6965e7e9..00000000 Binary files a/sop-website/src/main/resources/public/assets/image/auth.png and /dev/null differ diff --git a/sop-website/src/main/resources/public/assets/lib/layui/css/modules/layer/default/icon-ext.png b/sop-website/src/main/resources/public/assets/lib/layui/css/modules/layer/default/icon-ext.png deleted file mode 100644 index bbbb669b..00000000 Binary files a/sop-website/src/main/resources/public/assets/lib/layui/css/modules/layer/default/icon-ext.png and /dev/null differ diff --git a/sop-website/src/main/resources/public/assets/lib/layui/css/modules/layer/default/icon.png b/sop-website/src/main/resources/public/assets/lib/layui/css/modules/layer/default/icon.png deleted file mode 100644 index 3e17da8b..00000000 Binary files a/sop-website/src/main/resources/public/assets/lib/layui/css/modules/layer/default/icon.png and /dev/null differ diff --git a/sop.sql b/sop.sql index aa33c3ed..6e1d0f43 100644 --- a/sop.sql +++ b/sop.sql @@ -21,6 +21,8 @@ DROP TABLE IF EXISTS `config_common`; DROP TABLE IF EXISTS `admin_user_info`; DROP TABLE IF EXISTS `monitor_info`; DROP TABLE IF EXISTS `monitor_info_error`; +DROP TABLE IF EXISTS `user_account`; +DROP TABLE IF EXISTS `isp_resource`; CREATE TABLE `admin_user_info` ( @@ -250,6 +252,34 @@ CREATE TABLE `monitor_info_error` ( KEY `idx_routeid` (`route_id`) USING BTREE ) ENGINE=InnoDB DEFAULT CHARSET=utf8; +ALTER TABLE `isv_info` ADD COLUMN `user_id` BIGINT NULL DEFAULT 0 COMMENT 'user_account.id' AFTER `remark`; + +CREATE INDEX `idx_userid` USING BTREE ON `isv_info` (`user_id`); + +CREATE TABLE `user_account` ( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, + `username` varchar(128) NOT NULL DEFAULT '' COMMENT '用户名(邮箱)', + `password` varchar(128) NOT NULL DEFAULT '' COMMENT '密码', + `status` tinyint(4) NOT NULL DEFAULT '1' COMMENT '2:邮箱未验证,1:启用,0:禁用', + `gmt_create` datetime DEFAULT CURRENT_TIMESTAMP, + `gmt_modified` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (`id`), + UNIQUE KEY `uk_username` (`username`) USING BTREE +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='用户信息'; + +CREATE TABLE `isp_resource` ( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, + `name` varchar(64) NOT NULL DEFAULT '' COMMENT '资源名称', + `content` varchar(128) NOT NULL DEFAULT '' COMMENT '资源内容(URL)', + `ext_content` text, + `version` varchar(32) NOT NULL DEFAULT '' COMMENT '版本', + `type` tinyint(4) NOT NULL DEFAULT '0' COMMENT '资源类型:0:SDK链接', + `is_deleted` tinyint(4) NOT NULL DEFAULT '0', + `gmt_create` datetime DEFAULT CURRENT_TIMESTAMP, + `gmt_modified` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='ISP资源表'; + INSERT INTO `admin_user_info` (`id`, `username`, `password`, `status`, `gmt_create`, `gmt_modified`) VALUES (1,'admin','a62cd510fb9a8a557a27ef279569091f',1,'2019-04-02 19:55:26','2019-04-02 19:55:26');