commit 6c14c43cba6c9ecdd842271736b96e60054be8d0 Author: Junling Bu Date: Fri Mar 23 00:20:46 2018 +0800 V 0.1.0, 项目架构基本完成。 diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..e58953b0 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +/.idea/ +/private/ +/storage/ diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000..09f77e15 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2018 linlinjava(linlinjava@163.com) + +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. diff --git a/README.md b/README.md new file mode 100644 index 00000000..b7f213c1 --- /dev/null +++ b/README.md @@ -0,0 +1,119 @@ +litemall +===== + +又一个小商场系统。 + +项目代码 +==== + +* [码云](https://gitee.com/linlinjava/litemall) +* [GitHub](https://github.com/linlinjava/litemall) + +项目架构 +==== +![](./doc/pic/1.png) + +技术栈 +=== + +> 1. Spring Boot +> 2. Vue +> 3. 微信小程序 + +![](doc/pic/2.png) + +效果 +== + +### 小商城效果 + +![](doc/pic/3.png) + +* 首页 +* 专题列表、专题详情 +* 分类列表、分类详情 +* 品牌列表、品牌详情 +* 新品首发 +* 人气推荐 +* 商品搜索 +* 商品详情 +* 商品评价列表、商品评价 +* 购物车 +* 下单 +* 我的主页 +* 订单列表、订单详情 +* 地址列表、地址添加、地址删除 +* 我的收藏 +* 我的足迹 + + +### 管理平台效果 + +![](doc/pic/4.png) + +* 会员管理 + +* 商场管理 + +* 商品管理 + +* 推广管理 + +* 系统管理 + + +云演示 +== + +### 小商城演示访问 + +由于没有上线,只能在微信开发工具中测试运行: + +1. 微信开发工具导入litemall-wx项目; +2. 项目配置,启用“不校验合法域名、web-view(业务域名)、TLS 版本以及 HTTPS 证书” +3. 点击“编译”,即可在微信开发工具预览效果; +4. 也可以点击“预览”,然后手机扫描登陆。 + +![](doc/pic/5.gif) + +### 管理平台演示访问 + +1. 浏览器打开,输入以下网址`http://122.152.206.172:8080/#/login` +2. 用户名`admin123`,密码`admin123` + +文档 +== + +1. [系统架构](doc/1.md) +2. [基础子系统](doc/2.md) +3. [小程序子系统](doc/3.md) +4. [管理后台子系统](doc/4.md) +5. [商场子系统](doc/5.md) +6. [下一步计划](doc/6.md) + +更新 +== + +* V 0.1.0,项目架构基本完成。 + +警告 +== + +> 1. 本项目仅用于学习练习 +> 2. 数据库数据来自nideshop +> 3. 项目代码目前还不完善,仍处在开发中 +> 4. 项目开源(MIT),但不承担任何使用后果 + + +致谢 +== + +本项目基于或参考以下项目: +> 1. nideshop-mini-program +> 如果后端希望采用nodejs,用户可以访问nideshop项目 +> 2. platform +> 如果后端希望采用非spring boot版的普通spring版或者更多功能, +> 用户可以访问platform项目 +> 3. vue-element-admin + +本项目所依赖的其他开源项目见相关章节 diff --git a/deploy/.gitignore b/deploy/.gitignore new file mode 100644 index 00000000..846f4515 --- /dev/null +++ b/deploy/.gitignore @@ -0,0 +1,4 @@ +/litemall-admin-api/litemall-admin-api.jar +/litemall-os-api/litemall-os-api.jar +/litemall-wx-api/litemall-wx-api.jar +/litemall-admin/dist.tar diff --git a/deploy/README.md b/deploy/README.md new file mode 100644 index 00000000..975b2602 --- /dev/null +++ b/deploy/README.md @@ -0,0 +1,24 @@ + +1. 项目进一步打包到deploy文件夹中: + * litemall-os-api模块编译得到的litemall-os-api-0.1.0.jar 保存到deploy的litemall-os-api文件夹中,同时重命名成litemall-os-api.jar + * litemall-wx-api模块编译得到的litemall-wx-api-0.1.0.jar 保存到deploy的litemall-wx-api文件夹中,同时重命名成litemall-wx-api.jar + * litemall-admin-api模块编译得到的litemall-admin-api-0.1.0.jar 保存到deploy的litemall-admin-api文件夹中,同时重命名成litemall-admin-api.jar + * litemall-admin模块编译以后,把dist文件夹压缩,然后放到deploy的litemall-admin文件夹中。 + +2. 使用FileZilla把deploy整个文件夹上传到云主机的/home/ubuntu文件夹中 + +3. 使用PuTTY登陆云主机 + +4. 运行脚本部署运行 + + ```bash + sudo ./deploy/bin/deploy.sh + ``` + +5. 测试部署是否成功 +请确保litemall的Spring Boot应用模块所对应的端口已经打开; +然后测试是否能够访问(xxx.xxx.xxx.xxx是云主机IP): + + > http://xxx.xxx.xxx.xxx:8081/storage/index/index + > http://xxx.xxx.xxx.xxx:8082/wx/index/index + > http://xxx.xxx.xxx.xxx:8083/admin/index/index \ No newline at end of file diff --git a/deploy/bin/deploy.sh b/deploy/bin/deploy.sh new file mode 100644 index 00000000..4286f481 --- /dev/null +++ b/deploy/bin/deploy.sh @@ -0,0 +1,24 @@ +#!/bin/bash + +#部署litemall-admin静态文件应用 +cd /home/ubuntu/deploy/litemall-admin +rm -rf dist +mkdir dist +tar -zxvf dist.tar -C dist +cd . + +#部署三个Spring Boot应用 +#如果服务已经启动,则尝试停止 +sudo /etc/init.d/litemall-os-api stop +sudo /etc/init.d/litemall-wx-api stop +sudo /etc/init.d/litemall-admin-api stop + +#部署Spring Boot应用成服务 +sudo ln -f -s /home/ubuntu/deploy/litemall-os-api/litemall-os-api.jar /etc/init.d/litemall-os-api +sudo ln -f -s /home/ubuntu/deploy/litemall-wx-api/litemall-wx-api.jar /etc/init.d/litemall-wx-api +sudo ln -f -s /home/ubuntu/deploy/litemall-admin-api/litemall-admin-api.jar /etc/init.d/litemall-admin-api + +#启动服务 +sudo /etc/init.d/litemall-os-api restart +sudo /etc/init.d/litemall-wx-api restart +sudo /etc/init.d/litemall-admin-api restart \ No newline at end of file diff --git a/deploy/litemall-admin-api/litemall-admin-api.conf b/deploy/litemall-admin-api/litemall-admin-api.conf new file mode 100644 index 00000000..00d1054a --- /dev/null +++ b/deploy/litemall-admin-api/litemall-admin-api.conf @@ -0,0 +1,2 @@ +RUN_ARGS=--spring.profiles.active=prod +JAVA_OPTS= \ No newline at end of file diff --git a/deploy/litemall-os-api/litemall-os-api.conf b/deploy/litemall-os-api/litemall-os-api.conf new file mode 100644 index 00000000..00d1054a --- /dev/null +++ b/deploy/litemall-os-api/litemall-os-api.conf @@ -0,0 +1,2 @@ +RUN_ARGS=--spring.profiles.active=prod +JAVA_OPTS= \ No newline at end of file diff --git a/deploy/litemall-wx-api/litemall-wx-api.conf b/deploy/litemall-wx-api/litemall-wx-api.conf new file mode 100644 index 00000000..00d1054a --- /dev/null +++ b/deploy/litemall-wx-api/litemall-wx-api.conf @@ -0,0 +1,2 @@ +RUN_ARGS=--spring.profiles.active=prod +JAVA_OPTS= \ No newline at end of file diff --git a/doc/1.md b/doc/1.md new file mode 100644 index 00000000..46f40263 --- /dev/null +++ b/doc/1.md @@ -0,0 +1,446 @@ +# 1 litemall系统 + +## 1.1 简介 + +litemall是一个简单的商场系统,基于现有的开源项目,重新实现一个完整的前后端项目,包含小程序客户端和网页管理端。 + +![](./pic1/1-1.png) + + +项目的架构是三个系统和六个模块: + +* 基础系统(core),由数据库、数据库业务模块litemall-db、对象存储模块litemall-os-api组成; +* 小商场系统(wxmall),由litemall-wx-api模块和litemall-wx模块组成; +* 后台管理系统(admin),由litemall-admin-api模块和litemall-admin模块组成。 +* 简单商城系统(mall),这里仅列出,目前没有开发计划。 + +而六个模块的开发设计到三种技术栈: + +* Spring Boot技术栈,采用IDEA开发工具,开发litemall-db、litemall-os-api、litemall-admin-api和litemall-wx-api共四个模块; +* miniprogram(微信小程序)技术栈,采用微信小程序开发工具,开发litemall-wx模块; +* Vue技术栈,采用VSC开发工具,开发litemall-admin模块。 + +## 1.2 系统功能 + +从业务功能上,目前由五个业务模块(参考nideshop-mini-program和platform)组成: + +* 会员业务模块 +* 商场业务模块 +* 商品业务模块 +* 推广业务模块 +* 系统业务模块(仅管理平台) + +### 1.2.1 小程序端功能 + +* 首页 + +* 专题列表 + +* 专题详情 + +* 分类列表 + +* 分类详情 + +* 品牌列表 + +* 品牌详情 + +* 新品首发 + +* 人气推荐 + +* 商品搜索 + +* 商品详情 + +* 商品评价列表 + +* 商品评价 + +* 购物车 + +* 下单 + +* 我的主页 + +* 订单列表 + +* 订单详情 + +* 地址列表 + +* 地址添加 + +* 我的收藏 + +* 我的足迹 + +* 支付页面(待定) + +* 优惠券选择(待定) + +* 我的优惠券(待定) + +### 1.2.2 管理平台功能 + +* 会员管理 + * 会员管理 + * 收货地址管理 + * 会员收藏 + * 会员足迹 + * 搜索历史 + * 购物车 + * 会员等级(待定) + * 会员优惠劵(待定) +* 商城管理 + * 区域配置 + * 品牌制造商 + * 订单管理 + * 商品类目 + * 通用问题 + * 关键词 + * 渠道管理(待定) +* 商品管理 + * 商品管理 + * 商品参数 + * 商品规格 + * 货品管理 + * 用户评论 + * 团购设置(待定) + * 商品满减搭配(待定) +* 推广管理 + * 广告列表 + * 专题管理 + * 优惠劵管理(待定) +* 系统管理 + * 管理员 + * 对象存储 + * 权限管理(待定) + * 定时任务(待定) + * 参数管理(待定) + * 系统日志(待定) + +## 1.3 项目特点 + +存在以下特点: + +* 数据库方面,只是简单的表,表和表之间的依赖关系没有采用外键设计,而是依赖Java代码在service层面或者业务层面保证。这样做的好处是数据库频繁改动很方便,不会因为外键而导致数据库难以修改; +* 涉及三种技术栈,但是每种技术栈仅涉及最基础的技术; + * 后端技术栈,仅涉及 Spring,Spring Boot, Spring MVC和Mybatis技术,其他后端技术暂时不采用; + * 小程序技术栈,仅涉及miniprogram官方文档和nideshop-mini-program项目; + * 前端技术栈,仅涉及vue, vuex, vue-route,element-ui技术和vue-element-admin项目; +* 安全方面,仅采用最基本的代码,提供简单基本的安全服务; +* 性能方面,没有涉及内存数据库缓存功能,而是完全依赖MySQL; +* 对象存储服务(图片上传和下载)方面,没有采用云存储方案,而是实现自有的简单文件上传下载功能。对象只是简单存储在后台主机的一个文件夹中,从而解耦当前项目对对象存储云平台的依赖,同时加强对对象存储概念的理解。 + +总之,目前的系统只是为了学习技术和业务而开发的一个简单商场原型系统。虽然缺失很多企业级功能,但是是完整和合理的原型系统。 + +注意: +> 以上特点并不一定是优点。 + +## 1.4 开发方案 + +![](pic1/1-2.png) + +如图所示,当前开发阶段的方案: + +* MySQL数据访问地址`jdbc:mysql://localhost:3306/litemall` +* litemall-os-api对象存储服务地址`http://localhost:8081` +* litemall-wx-api后台服务地址`http://localhost:8082`,数据则来自MySQL +* litemall-admin-api后台服务地址`http://localhost:8083`,数据则来自MySQL +* litemall-admin前端访问地址`http://localhost:9527`, 数据来自litemall-admin-api +* litemall-wx没有前端访问地址,而是直接在微信小程序工具上编译测试开发,最终会部署到微信官方平台(即不需要自己部署web服务器),而数据则来自litemall-wx-api + + +### 1.4.1 Spring Boot开发环境 + +1. 安装JDK8 +2. 安装IDEA Community +3. 安装maven +4. 安装Git(可选) +5. IDEA导入本项目 +6. 采用maven插件安装依赖库 +7. 编译本项目 +8. 运行litemall-os-api, 打开浏览器,输入 +``` +http://localhost:8081/storage/index/index +``` +如果出现JSON数据,则Spring Boot开发环境部署成功,litemall-os-api模块运行正常。 +9. 同上,运行litemall-wx-api, 打开浏览器,输入 +``` +http://localhost:8082/wx/index/index +``` +如果出现JSON数据,则litemall-wx-api模块运行正常。 +10. 同上,运行litemall-admin-api, 打开浏览器,输入 +``` +http://localhost:8083/admin/index/index +``` +如果出现JSON数据,则litemall-admin-api模块运行正常。 + +注意: +> 由于设置了三个不同的端口,因此开发时,IDEA可以同时运行litemall-os-api、litemall-wx-api和litemall-admin-api三个Spring Boot程序。 + +### 1.4.2 微信小程序开发环境 + +1. 安装微信小程序开发工具 +2. 导入本项目的litemall-wx模块文件夹 +3. 编译前,请确定litemall-wx-api模块已经运行,而litemall-wx模块的config文件夹中的api.js已经设置正确对应的后台数据服务地址; +4. 点击`编译`,如果出现数据和图片,则运行正常 + +### 1.4.3 Vue开发环境 + +1. 安装npm(或者cnpm) +2. 安装VSC(Visual Studio Code) +3. 导入本项目的litemall-admin模块文件夹 +4. 安装依赖库 + + ``` + cnpm install + ``` +5. 编译并运行 + + ``` + cnpm run dev + ``` +然后,打开浏览器,输入`http://localhost:9527`。 +如果出现管理后台登陆页面,则表明管理后台的前端运行正常; +6. 请确定litemall-admin-api模块已经运行,然后点击`登陆`,如果能够成功登陆,则表明管理后台的前端和后端对接成功,运行正常。 + +## 1.5 部署方案 + +在1.4节中介绍的是开发阶段时一些关键性开发流程。本节将介绍代码开发成功以后开始部署项目时一些关键性流程。 + +首先,需要明确的是开发时项目使用的服务地址是本地地址,即localhost;而部署时则应该根据具体情况设置合理的服务器地址和端口。 +其次,需要明确的是各模块之间的关系: + + * litemall-os-api模块会包含litemall-db模块,部署在服务器中 + * litemall-wx-api模块会包含litemall-db模块,部署在服务器中 + * litemall-admin-api模块会包含litemall-db模块,部署在服务器中 + * litemall-wx模块部署在腾讯官方平台中,此外数据API地址指向litemall-wx-api所在服务qi地址 + * litemall-admin编译出的静态文件放在web服务器或者tomcat服务器,此外服务器地址设置指向3中litemall-admin-api所在地址 + +注意 +> * 这里litemall-os-api、litemall-admin-api和litemall-wx-api也可以选择编译成jar模式的可执行文件(因为内嵌tomcat服务器),然后直接运行。 +> * litemall-wx正式部署时需要设置https开头的合法域名,因此litemall-wx-api所在的服务器需要配置合适的域名和SSL证书,具体参见官方文档。 + +实际上,最终的部署方案是灵活的: + +* 可以单一云主机中仅安装一个tomcat服务器并配置一个端口,然后同时部署四个项目的模块,从而提供四种服务 +(即litemall-admin静态页面,litemall-os-api存储服务、litemall-amdin-api后台服务和litemall-wx-api后台服务)。 +* 也可以是同一云主机中安装四个服务器分别配置四个端口,分别提供四种服务 +* 也可以是四个云主机各自安装服务器配置端口,分别提供四种服务 +* 当然,甚至多个服务器,并发提供服务。 + +注意 +> 1. `本机`指的是是当前的开发机 +> 2. `云主机`指的是用户购买并部署的远程主机 + +以下简单列举几种方案。 + +### 1.5.1 windows下本机测试部署方案 + +这里,我们是把window作为开发环境进行本项目的开发工作。 +而项目开发完毕以后,在正式部署之前,可以先进行一个简单的本机测试部署方案。 + +首先,需要确保本地MySQL已经安装并且导入了litemall.sql数据; +其次,项目打包 +``` +cd litemall +mvn clean +mvn package +``` +最后,本机测试性部署三个应用 +``` +cd litemall +java -jar ./litemall-os-api/target/litemall-os-api-0.1.0.jar & +java -jar ./litemall-wx-api/target/litemall-wx-api-0.1.0.jar & +java -jar ./litemall-admin-api/target/litemall-admin-api-0.1.0.jar & +``` +如果,能够访问以下链接的数据,则表明本地测试部署成功: +``` +http://localhost:8081/storage/index/index +http://localhost:8082/wx/index/index +http://localhost:8083/admin/index/index +``` + +注意 +> 由于这里使用`&`设置成后台运行,因此测试结束以后,用户需要自行通过任务管理器或其他软件关闭这三个后台Spring Boot应用。 + +### 1.5.2 简单局域网方案 + +局域网方案,面向的是最终服务器数据和部分应用程序部署在局域网内的场景。 + +### 1.5.3 基于ubuntu腾讯云的单机云部署方案 +单机云部署方案,面向的是云主机单机同时部署演示型场景 +创建云主机,安装ubuntu操作系统,按照JDK和MySQL应用运行环境,部署三个Spring Boot微服务后台应用。 + +![](pic1/1-3.png) + +#### 1.5.3.1 主机 + +腾讯云 ubuntu 16.04.1 + +1. 创建云主机虚拟机 +2. 安装操作系统 +3. 创建安全组 +![](pic1/1-4.png) +4. 安装SSH密钥(可选) +5. 使用PuTTY远程登陆云主机 + +#### 1.5.3.2 JDK8 + +https://www.digitalocean.com/community/tutorials/how-to-install-java-with-apt-get-on-ubuntu-16-04 +http://www.webupd8.org/2012/09/install-oracle-java-8-in-ubuntu-via-ppa.html + +```bash +sudo add-apt-repository ppa:webupd8team/java +sudo apt-get update +sudo apt-get install oracle-java8-installer +sudo apt-get install oracle-java8-set-default +``` + +警告 +> "ppa:webupd8team/java" 不是Oracle官方PPA,可能存在安全隐患。 + +#### 1.5.3.3 MySQL + +https://www.digitalocean.com/community/tutorials/how-to-install-mysql-on-ubuntu-16-04 +https://www.linuxidc.com/Linux/2017-01/139502.htm + +``` +sudo apt-get update +sudo apt-get install mysql-server +sudo apt-get install mysql-client +``` + +下面是可选地设置root账号远程访问MySQL + +首先,MySQL默认不支持远程访问,因此需要修改配置文件 +``` +sudo vi /etc/mysql/mysql.conf.d/mysqld.cnf +``` +添加'#'注释掉其中的`bind-address`, +``` +#bind-address= 127.0.0.1`; +``` + +其次,设置root账号远程访问权限 +``` +mysql -u root -p +GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' IDENTIFIED BY '123456' WITH GRANT OPTION; +FLUSH PRIVILEGES; +quit; +``` + +最后,登陆腾讯云,设置云主机的`安全组`,允许`3306`端口访问,然后重启云主机,使得这些配置生效。 + +警告 +> 1. 因为安全的原因,MySQL不应该设置远程访问, +> 这里仅仅用于方便数据导入操作。 +> 2. MySQL应该部署在专门的服务器中, +> 这里仅仅用于演示远程部署MySQL。 + +#### 1.5.3.4 litemall数据 + +这里可以采用命令行式,也可以采用MySQL自带的Workbench导入。 + +* 命令行导入 + +1. 采用FileZilla把本项目的litemall.sql上传到云主机 +2. 使用PuTTY登陆云主机 +3. 进入MySQL,输入上节所设置的MySQL的root密码 +4. 创建数据库、 创建用户并分配访问权限 +5. 导入数据 +6. 退出 + +* MySQL Workbench导入 + +1. 先确认已经在1.4.3节中设置了root可以远程访问; +2. 创建一个新的连接,设置`Hostname` 、`Username` 和`Password`, +然后点击``Test Connection`测试是否能够连接到云主机; +如果测试成功,则进入; +![](pic1/1-5.png) +3. 用户自行学习文档,完成`创建数据库`、`创建用户`和`分配权限`三个操作; +4. 利用Workbench的`Server`菜单下的`Data Import`完成数据导入。 + +#### 1.5.3.5 Tomcat + +1. 本项目中采用二进制jar包方式来部署Spring Boot后端应用,因此可以不需要部署在tomcat中。 +但是,litemall-admin前端项目最终会编译出静态文件,需要部署在服务器中,因此这里仍需安装tomcat。 + + ```bash + sudo apt-get install tomcat8 + ``` + +2. 为了配置tomcat支持外部文件夹中,修改tomcat的server文件 + + ```bash + sudo vi /var/lib/tomcat8/conf/server.xml + ``` + 在其中添加一行新的内容`` + +3. 然后,重启tomcat + + ``` + sudo service tomcat8 restart + ``` + + +#### 1.5.3.5 项目打包 + +1. Spring Boot打包 + +通常项目打包方案存在两种: + +* 一是打包war格式,因此云主机需要专门安装tomcat +* 二是打包可执行jar格式,此时内部包含嵌入式tomcat + +这里仅采用第二种方案,简化tomcat的安装配置过程。 + +采用如下命令进行项目打包 + +``` +cd litemall +mvn clean +mvn package +``` + +2. Vue项目打包 + +采用如下命令进行项目打包 + +````bash +cnpm run build:prod +```` + +此时,litemall-admin模块的dist文件夹中就是最终部署时的代码。 + +#### 1.5.3.6 项目部署运行 + +https://docs.spring.io/spring-boot/docs/1.5.10.RELEASE/reference/htmlsingle/#deployment-service + +1. 项目进一步打包到deploy文件夹中: + * litemall-os-api模块编译得到的litemall-os-api-0.1.0.jar 保存到deploy的litemall-os-api文件夹中,同时重命名成litemall-os-api.jar + * litemall-wx-api模块编译得到的litemall-wx-api-0.1.0.jar 保存到deploy的litemall-wx-api文件夹中,同时重命名成litemall-wx-api.jar + * litemall-admin-api模块编译得到的litemall-admin-api-0.1.0.jar 保存到deploy的litemall-admin-api文件夹中,同时重命名成litemall-admin-api.jar + * litemall-admin模块编译以后,把dist文件夹压缩,然后放到deploy的litemall-admin文件夹中。 + +2. 使用FileZilla把deploy整个文件夹上传到云主机的/home/ubuntu文件夹中 + +3. 使用PuTTY登陆云主机 + +4. 运行脚本部署运行 + + ```bash + sudo ./deploy/bin/deploy.sh + ``` + ![](pic1/1-6.png) +5. 测试部署是否成功 +请确保litemall的Spring Boot应用模块所对应的端口已经打开; +然后测试是否能够访问(xxx.xxx.xxx.xxx是云主机IP): + + > http://xxx.xxx.xxx.xxx:8081/storage/index/index + > http://xxx.xxx.xxx.xxx:8082/wx/index/index + > http://xxx.xxx.xxx.xxx:8083/admin/index/index + \ No newline at end of file diff --git a/doc/2.md b/doc/2.md new file mode 100644 index 00000000..fcb16570 --- /dev/null +++ b/doc/2.md @@ -0,0 +1,246 @@ + +# 2 litemall基础系统 + +目前litemall基础系统主要由litemall数据库、litemall-db模块和litemall-os-api模块组成。 + +## 2.1 litemall数据库 + +litemall数据库基于nideshop中的[nideshop.sql](https://github.com/tumobi/nideshop/blob/master/nideshop.sql)数据库,然后在实际开发过程中进行了调整和修改: + +* 删除了一些目前不必要的表; +* 删除了表中一些目前不必要的字段; +* 行政区域数据litemall_region没有采用原nideshop中的数据,而是采用了[Administrative-divisions-of-China](https://github.com/modood/Administrative-divisions-of-China); +* 表中的某些字段采用JSON; +* 表中的日期或时间字段采用DATE、DATETIME; +* 字段的数据类型粗粒度化,例如避免MEDIUMINT,而是INT; +* 表的数据做了清理、调整和补充(假数据)。 + +具体不同可以比较litemall-db模块下sql文件夹中nideshop.sql和litemall.sql。 + +以下讨论一些关键性设计 + +注意: +> 以下设计基于个人理解,很可能存在不合理或者与实际系统不符合的地方。 + +### 2.1.1 商品和货品设计 + +这里商品存在商品,商品属性,商品规格,货品四种表 + +商品表是一种商品的基本信息,主要包括商品介绍,商品图片,商品所属类目,商品品牌商等; + +商品参数表其实也是商品的基本信息,但是由于是一对多关系,因此不能直接保存在商品表中(虽然采用JSON也可以但是不合理), +因此采用独立的商品参数表,通常是商品的一些公共基本商品参数; + +商品规格表是商品进一步区分货品的标识,例如同样一款衣服,基本信息一致,基本属性一致,但是在尺寸这个属性上可以 +把衣服区分成多个货品,而且造成对应的数量和价格不一致。 + +商品规格和规格值存在以下几种关系: + +* 单一规格和单一规格值,最常见的,即当前商品存在一种货品; +* 单一规格和多个规格值,较常见,即当前商品基于某个规格存在多种货品,通常价格都是相同的,当然也可能不相同; +* 多个规格和单一规格值,可以简化成第一种情况,或者采用第四种情况,通常实际情况下不常见; +* 多个规格和多个规格值,通常是两种规格或者三种规格较为常见,而且对应的价格不完全相同。 + +货品则是最终面向用户购买的商品标识,存在数量和价格两种属性。 + +因此这里一个商品表项,存在(至少0个)多个商品属性表项目,存在(至少一个)多个商品规格表项, +存在(至少一个)多个货品表项。 + +举例如下: + +* 一个商品“2018春季衣服商品编号1111111”, +* 存在两个商品参数, + * 属性名称“面向人群”,属性值“男士” + * 属性名称“面料”,属性值“100%棉” +* 存在两种规格共八个商品规格项, + * 规格名称“尺寸”,规则值“S” + * 规格名称“尺寸”,规则值“M” + * 规格名称“尺寸”,规则值“L” + * 规格名称“尺寸”,规则值“XL” + * 规格名称“尺寸”,规则值“XXL” + * 规格名称“颜色”,规格值“蓝色” + * 规格名称“颜色”,规格值“灰色” + * 规格名称“颜色”,规格值“黑色” +* 存在15个货品(尺寸*颜色=15个货品) + * 货品“S蓝”,数量 100, 价格 100 + * 货品“M蓝”,数量 100, 价格 100 + * 货品“L蓝”,数量 100, 价格 100 + * 货品“XL蓝”,数量 100, 价格 100 + * 货品“XXL蓝”,数量 100, 价格 100 + * 货品“S灰”,数量 100, 价格 100 + * 货品“M灰”,数量 100, 价格 100 + * 货品“L灰”,数量 100, 价格 100 + * 货品“XL灰”,数量 100, 价格 100 + * 货品“XXL灰”,数量 100, 价格 100 + * 货品“S黑”,数量 100, 价格 100 + * 货品“M黑”,数量 100, 价格 100 + * 货品“L黑”,数量 100, 价格 100 + * 货品“XL黑”,数量 0, 价格 100 + * 货品“XXL黑”,数量 0, 价格 100 + +以下是一些细节的讨论: + +* 商品表中可能存在数量和价格属性,而货品中也存在数量和价格属性, +但是商品表中的数量和价格应该仅用于展示,而不能用于最终的订单价格计算。 +商品表的数量应该是所有货品数量的总和。 +商品表的价格应该和某个货品的价格一样,通常应该是所有货品价格的最小值。 +因此这里商品表的数量和价格属性可能可以采用自动计算。 +* 商品规格可以存在规格图片,效果是规格名称前放置规格图片 +* 货品也可以存在货品图片,效果是所有规格选定以后对应的货品有货,则在货品价格前放置货品图片 + +注意: + +> 这里的设计可能与实际项目设计不一致,但是目前是可行的。 +> 商品的中文用语“商品”和英语用语“goods”,货品的中文用语“货品”和英语用语“product”可能是不正确的。 + +### 2.1.2 用户和微信用户设计 + +目前准备支持用户普通账号登陆和微信登陆两种方式,两种登陆方式仅仅采用一个litemall-user表可能不是很合适。此外,如果进一步支持其他多种第三方登陆,那么这里需要重新设计。 + +### 2.1.3 行政区域设计 + +原nideship.sql中存在region数据,但是litemall.sql的region数据则来自 +[Administrative-divisions-of-China](https://github.com/modood/Administrative-divisions-of-China)项目。从该项目中导入数据到litemall.sql的litemall-province、litemall-city、litemall-area和litemall-street四个表,然后重新生成一个新的litemall-region表。 + +### 2.1.4 订单设计 + +订单信息主要由基本信息、商品信息、地址信息、费用信息、快递信息、支付信息和其他信息组成。 + +* 基本信息 +订单创建时的一些基本信息。 + +* 商品信息 +由于订单可以存在多个商品,因此订单的商品信息是由独立的订单商品表记录(可能更应该称为货品)。 + +* 费用信息 + +* 快递信息 +目前快递信息仅仅记录快递公司、快递单号、快递发出时间、快递接收时间。而如果快递过程中如果存在一些异常,例如物品丢失,则目前系统难以处理。关于快递费的计算,目前采取简单方式,即满88元则免费,否则10元。 + +* 支付信息 + + +* 其他信息 + +#### 2.1.4.1 订单状态 + +![](pic2/2-1.png) + +订单分成几种基本的状态: + +* 下单 +状态码101,此时订单生成,记录订单编号、收货地址信息、订单商品信息和订单相关费用信息; +* 付款 +状态码201,此时用户微信支付付款,系统记录微信支付订单号、支付时间、支付状态; +* 发货 +状态码301,此时商场已经发货,系统记录快递公司、快递单号、快递发送时间。 +当快递公司反馈用户签收后,系统记录快递到达时间。 +* 收货 +状态码401,当用户收到货以后点击确认收货,系统记录确认时间。 +此时,用户可以评价订单商品。 + +除了这几种正常状态以外,还存在一些非普通的状态: + +* 订单取消 +状态码102,用户在生产订单以后未付款之前,点击取消按钮,系统记录结束时间 +* 订单取消并退款 +状态码202,用户付款以后未发货前,点击取消按钮,系统记录结束时间和退款信息 +* 系统自动确认收货 +状态码402,快递反馈商场用户已签收,但是用户却不点击确认收货按钮, +此时系统在快递到达时间的一段时间后,自动确认收货。 +用户不能再点击确认收货按钮,但是可以评价订单商品 + +当然,以上的基本状态和非普通状态,和实际项目相比仍然相对简单。 + +此外,当订单状态码是102、202、401、402时,订单可以设置删除状态,此时 +用户查看自己订单信息时将看不到这些“已删除”的订单。 + + +#### 2.1.4.2 状态码所支持的操作 + +不同的状态码下面,用户能够进行的操作是: + +* 101 +此时,用户可以“订单支付”、“订单取消” +* 102 +此时,用户可以“订单删除” +* 201 +此时,用户可以“订单取消”(并退款) +* 202 +此时,用户可以“订单删除” +* 301 +此时,用户可以“确认收货” +* 401 +此时,用户可以“订单删除”、“评价”、“再次购买” +* 402 +此时,用户可以“订单删除”、“评价”、“再次购买” + +#### 2.1.4.3 售后处理 + +目前不支持售后或退货相关业务。 + +#### 2.1.4.4 黑名单 + +从一些资料看,如果用户订单多次取消,应该加入黑名单。 +目前不支持。 + +## 2.2 litemall-db + +![](./pic2/2-2.png) + +这里litemall-db模块可以分成以下几种代码: + +* mybatis generator自动化代码 +* 业务代码 +* 安全代码 +* JSON支持代码 +* 配置代码 + +### 2.2.1 自动化代码 + +通过mybatis generator插件可以自动生成三种代码: + +* src文件夹`domain` Java代码 +* src文件夹`dao` Java代码 +* resources文件夹`dao` Mapper代码 + +这里的代码封装了对数据库的操作,因此用户不需要直接操作sql代码,而是直接操作Java代码即可完成对数据库的访问处理。 + +当然,为了达到数据库访问效率,用户也可以自定义mapper文件和对应的Java代码,但是至少目前这里不采用。例如,当需要访问两个表的数据时,这里是在业务层通过Java代码遍历的形式来访问两个表。 + +### 2.2.2 业务代码 + +基于2.2.1的代码,业务代码处理一些具体业务相关的操作,对其他模块提供具体的服务。 + +### 2.2.3 安全代码 + +### 2.2.4 JSON支持代码 + +### 2.2.5 配置代码 + +采用Java注解的方式来完成一些特定的配置操作。 + +## 2.3 litemall-os-api + +对象存储服务目前的目标是支持图片的上传下载。 + +### 2.3.1 业务 + +支持服务: + +* 列表 +* 创建 +* 修改 +* 读取 +* 删除 +* 下载,即下载对象数据文件 +* 访问,即直接访问对象数据 + +### 2.3.2 安全 + +警告 +> 目前这里没有任何安全机制,这意味着任何人如果知道对象存储服务的地址,都可以直接存储访问对象数据。 + +这样简化的目的是对象存储服务建议最终采用云服务,因此这里仅仅实现一个简单的服务面向测试开发。 + +如果用户需要局域网部署,那么这里需要加入一定的安全机制。 diff --git a/doc/3.md b/doc/3.md new file mode 100644 index 00000000..8f229a9f --- /dev/null +++ b/doc/3.md @@ -0,0 +1,47 @@ +# 3 litemall小商城 + +技术: + +* 小商城前端,即litemall-wx模块 + * 微信小程序 +* 小商城后端,即litemall-wx-api模块 + * Spring Boot 1.5.10 + * Spring MVC + * [weixin-java-tools](https://gitee.com/binary/weixin-java-tools) + + +目前发现需要完善的: + +* 支付功能 +* 运费计算 +* 优惠券功能 +* 商品搜索 +* 进一步区分商品和货品的关系 +* 地址优化,目前每一次点击都会请求后台,应该缓存已有的数据 +* 商品数量和规格中,如果货品数量不足,则显示不能点击的效果 +* 登陆逻辑重新设计,如果用户没有登陆,则相关页面显示登陆的效果 + +## 3.1 litemall-wx-api + +### 3.1.1 业务 + +### 3.1.2 安全 + +### 3.1.3 支付 + +准备采用weixin-java-tools工具简化微信支付代码的开发。 +由于需要商户相关信息,目前没有开发。 + +## 3.2 litemall-wx + +这里的代码基于[nideshop-mini-program](https://gitee.com/tumobi/nideshop-mini-program),但是做了一定的修改: + +* 数据属性名称调整,原项目中数据属性名称是下划线法命名(例如goods_id),而这里采用骆驼式命名法(例如goodsId),因此代码中需要进行相应调整; +* 代码清理重构,删除了一些目前不必要的文件,梳理一些逻辑功能; +* BUG修补,修改了一些错误; +* 功能完善拓展,例如商品立即购买功能、商品评价功能; + +具体变化可以采用工具进行对比。 + +注意 +> 目前litemall-wx项目代码基于nideshop-mini-program的commit版本[acbf6276eb27abc6a48887cddd223d7261f0088e](https://github.com/tumobi/nideshop-mini-program/commit/acbf6276eb27abc6a48887cddd223d7261f0088e)。由于改动变化较大,因此之后litemall-wx将独立开发,nideshop-mini-program的跟新不一定会合并到litemall-wx中。 diff --git a/doc/4.md b/doc/4.md new file mode 100644 index 00000000..4186903c --- /dev/null +++ b/doc/4.md @@ -0,0 +1,47 @@ + +# 4 litemall后台管理 + +这里的后台管理业务参考了[platform](https://gitee.com/fuyang_lipengjun/platform). + +技术架构: + +* 后台管理前端,即litemall-admin模块 + * vue + * vuex + * vue-router + * axios + * element-ui + * vue-element-admin + * 其他,见package.json +* 后台管理后端, 即litemall-admin-api模块 + * Spring Boot 1.5.10 + * Spring MVC + +目前存在的问题 + +* 大部分页面仅仅是一个表CRUD的效果,交互性一般。 + 例如,显示商品的时候,只是简单显示表中保存的类目ID和品牌商ID,更好的效果可能是显示对应的类目名称和品牌商名称,同时能够显示更详细的类目信息和品牌商信息。 +* 地址优化,目前每一次点击都会请求后台,应该缓存已有的数据 +* 管理员角色和权限设计 +* 首页中实现一些小组件,同时点击能够跳转相应页面 +* 查询时排序功能 +* 业务功能重新设计,例如即使是管理员也不能删除修改用户的相关数据 +* 用户密码加密存储 + +## 4.1 litemall-admin-api + + +### 4.1.1 业务 + +### 4.1.2 安全配置 + +### 4.1.3 CROS配置 + +## 4.2 litemall-admin + +litemall-admin模块的代码基于[vue-element-admin](https://github.com/PanJiaChen/vue-element-admin) + + +## 4.3 开发新组件 + +这里介绍开发一个新的组件的流程。 \ No newline at end of file diff --git a/doc/5.md b/doc/5.md new file mode 100644 index 00000000..f165c400 --- /dev/null +++ b/doc/5.md @@ -0,0 +1,8 @@ + +# 5 litemall商场网站 + +目前不开发 + +## 5.1 litemall-web-api + +## 5.2 litemall-web diff --git a/doc/6.md b/doc/6.md new file mode 100644 index 00000000..1c15ed57 --- /dev/null +++ b/doc/6.md @@ -0,0 +1,109 @@ +# 6 下一步计划 + +6.1节涉及到技术层面需要改进的部分,而6.2节则是业务方面未实现的部分,这两个部分的代码都会在本项目中实现。而6.3节会进一步完善本项目的安全和性能,但是不一定会在本项目中实现,因为会增加相当大的复杂性。 + +## 6.1 技术 + +### 6.1.1 ENUM +数据库中涉及到枚举类型的字段,目前设置成VARCHAR。 +下一步应该设计不同的业务enum类型,然后mybatis自定义BaseTypeHandler来处理不同的枚举类型。 + +### 6.1.2 litemall-db AutoConig +目前,litemall-os-api、litemall-admin-api和litemall-wx-api引入litemall-db的时候,需要在Application类中显式注解查找包和MapperScan来对litemall-db中的mapper查找,此外需要在application.properties配置文件中完成对数据库、数据池等配置。 +例如 +``` +@SpringBootApplication(scanBasePackages={"org.linlinjava.litemall.admin","org.linlinjava.litemall.db"}) +@MapperScan("org.linlinjava.litemall.db.dao") +public class Application { + + public static void main(String[] args) { + SpringApplication.run(Application.class, args); + } + +} +``` +下一步,应该自动部署,即引入litemall-db则会自动引入对mapper的查找, +同时自动部署对数据库、PageHelper和Druid的配置。 + +### 6.1.3 统一对日期时间域的处理 +目前,数据库的日期时间域有的采用DATE,有的是DATETIME,而对应Java用LocalDate和LocalDateTime。 +下一步应该根据具体业务功能采用合适的。 + +### 6.1.4 vue-element-admin跟新 +目前的vue-element-admin基于element 2.0.8, 因此element的很多修复没有跟新。 + +### 6.1.5 事务管理 + +目前没有事务管理,因此如果直接把项目用于正式运行中,可能会存在数据不一致等问题。 + +### 6.1.6 小程序登陆设计 + +个人觉得目前nideshop-mini-program里的登陆设计内部实现不是很合理。 + +### 6.1.n 代码重构 + +技术不断迭代开发中,应该在业务基本完成和功能基本完善以后, +重新审视代码架构和技术,或者重构代码,或者采用更为合理的技术。 + +## 6.2 业务 + +### 6.2.1 支付模块 + +目前微信支付模块因缺乏支付权限而不能开发。 + +### 6.2.2 任务日程模块 + +部分业务需要引入任务管理服务,例如订单下单以后一个小时未支付则自动失效关闭。 + +### 6.2.3 监控管理 + +项目运行的整个运行过程应该需要监控管理 + +### 6.2.4 权限管理 + +目前安全实现非常粗糙,仅仅依赖管理员的用户名和密码。 +更好的做法是引入角色权限机制。 + +## 6.3 企业级 + +### 6.3.1 安全(必须) + +尽在系统所有业务功能完整实现以后。 + +### 6.3.2 性能(建议) + +仅在系统所有业务功能完整实现以后。 + +性能的提升存在多种方案。 + +目前考虑到的技术方案: + +* 采用内存数据库,如redis,缓存一些改动较少的数据,例如行政区域数据。 +* 数据库方面可以引入冗余,例如首页的数据可以先从其他表更新到一个独立的表。 + +### 6.3.3 对象存储云服务(建议) + +仅在系统所有业务功能完整实现以后。 + +对象存储云服务不一定是必须的: + +* 局域网中只能采用内部对象存储服务 +* 对象存储云服务也是一笔开销 +* 引入对象存储云服务也增加了一定的复杂性 +* 导致对具体云平台的耦合 + +当然,对象存储云服务的益处也是毋庸置疑的,因此建议部署使用。 + +### 6.3.4 云数据库(必须) + +同上,如果仅在局域网中部署项目,那么数据库的部署也只能是在局域网中,但是在互联网中部署应用,数据的重要性和优先级是最高的,采用云数据库可以方便带来其提供的安全和性能服务。 + +### 6.3.5 短信服务(必须) + +真正的项目里面肯定需要短信功能。 + +### 6.3.6 推荐系统(可选) + +目前的关键字是管理员设置,然后用户的搜索时匹配关键字,因此需要专业的推荐系统。 + +### 6.3.6 Spring Boot 2.0迁移(可选) \ No newline at end of file diff --git a/doc/pic/1.png b/doc/pic/1.png new file mode 100644 index 00000000..8ba19ba4 Binary files /dev/null and b/doc/pic/1.png differ diff --git a/doc/pic/2.png b/doc/pic/2.png new file mode 100644 index 00000000..a6d4a22a Binary files /dev/null and b/doc/pic/2.png differ diff --git a/doc/pic/3.png b/doc/pic/3.png new file mode 100644 index 00000000..a550a36e Binary files /dev/null and b/doc/pic/3.png differ diff --git a/doc/pic/4.png b/doc/pic/4.png new file mode 100644 index 00000000..45658242 Binary files /dev/null and b/doc/pic/4.png differ diff --git a/doc/pic/5.gif b/doc/pic/5.gif new file mode 100644 index 00000000..08200738 Binary files /dev/null and b/doc/pic/5.gif differ diff --git a/doc/pic/litemall.ico b/doc/pic/litemall.ico new file mode 100644 index 00000000..7fc0eab3 Binary files /dev/null and b/doc/pic/litemall.ico differ diff --git a/doc/pic/litemall.png b/doc/pic/litemall.png new file mode 100644 index 00000000..d16e9afa Binary files /dev/null and b/doc/pic/litemall.png differ diff --git a/doc/pic1/1-1.png b/doc/pic1/1-1.png new file mode 100644 index 00000000..79560e4f Binary files /dev/null and b/doc/pic1/1-1.png differ diff --git a/doc/pic1/1-2.png b/doc/pic1/1-2.png new file mode 100644 index 00000000..d8d2f9e8 Binary files /dev/null and b/doc/pic1/1-2.png differ diff --git a/doc/pic1/1-3.png b/doc/pic1/1-3.png new file mode 100644 index 00000000..df818992 Binary files /dev/null and b/doc/pic1/1-3.png differ diff --git a/doc/pic1/1-4.png b/doc/pic1/1-4.png new file mode 100644 index 00000000..e41208e8 Binary files /dev/null and b/doc/pic1/1-4.png differ diff --git a/doc/pic1/1-5.png b/doc/pic1/1-5.png new file mode 100644 index 00000000..18fd2bf3 Binary files /dev/null and b/doc/pic1/1-5.png differ diff --git a/doc/pic1/1-6.png b/doc/pic1/1-6.png new file mode 100644 index 00000000..ac8bb876 Binary files /dev/null and b/doc/pic1/1-6.png differ diff --git a/doc/pic2/2-1.png b/doc/pic2/2-1.png new file mode 100644 index 00000000..e691d3bf Binary files /dev/null and b/doc/pic2/2-1.png differ diff --git a/doc/pic2/2-2.png b/doc/pic2/2-2.png new file mode 100644 index 00000000..16546492 Binary files /dev/null and b/doc/pic2/2-2.png differ diff --git a/litemall-admin-api/.gitignore b/litemall-admin-api/.gitignore new file mode 100644 index 00000000..ede42044 --- /dev/null +++ b/litemall-admin-api/.gitignore @@ -0,0 +1,2 @@ + +/target/ diff --git a/litemall-admin-api/litemall-admin-api.iml b/litemall-admin-api/litemall-admin-api.iml new file mode 100644 index 00000000..1a53616e --- /dev/null +++ b/litemall-admin-api/litemall-admin-api.iml @@ -0,0 +1,65 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/litemall-admin-api/pom.xml b/litemall-admin-api/pom.xml new file mode 100644 index 00000000..a8243c9f --- /dev/null +++ b/litemall-admin-api/pom.xml @@ -0,0 +1,71 @@ + + 4.0.0 + org.linlinjava + litemall-admin-api + 0.1.0 + jar + + + org.springframework.boot + spring-boot-starter-parent + 1.5.10.RELEASE + + + + + UTF-8 + 1.8 + true + + + + + + org.linlinjava + litemall-db + 0.1.0 + + + + + org.springframework.boot + spring-boot-starter-web + + + + com.fasterxml.jackson.datatype + jackson-datatype-jsr310 + 2.8.10 + + + + + + + + + + + + org.springframework.boot + spring-boot-devtools + true + + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + true + + + + + + + \ No newline at end of file diff --git a/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/Application.java b/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/Application.java new file mode 100644 index 00000000..92417d69 --- /dev/null +++ b/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/Application.java @@ -0,0 +1,15 @@ +package org.linlinjava.litemall.admin; + +import org.mybatis.spring.annotation.MapperScan; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication(scanBasePackages={"org.linlinjava.litemall.admin","org.linlinjava.litemall.db"}) +@MapperScan("org.linlinjava.litemall.db.dao") +public class Application { + + public static void main(String[] args) { + SpringApplication.run(Application.class, args); + } + +} \ No newline at end of file diff --git a/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/annotation/LoginAdmin.java b/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/annotation/LoginAdmin.java new file mode 100644 index 00000000..76b30d22 --- /dev/null +++ b/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/annotation/LoginAdmin.java @@ -0,0 +1,13 @@ +package org.linlinjava.litemall.admin.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + + +@Target(ElementType.PARAMETER) +@Retention(RetentionPolicy.RUNTIME) +public @interface LoginAdmin { + +} diff --git a/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/annotation/support/LoginAdminHandlerMethodArgumentResolver.java b/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/annotation/support/LoginAdminHandlerMethodArgumentResolver.java new file mode 100644 index 00000000..ee3688fd --- /dev/null +++ b/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/annotation/support/LoginAdminHandlerMethodArgumentResolver.java @@ -0,0 +1,31 @@ +package org.linlinjava.litemall.admin.annotation.support; + +import org.linlinjava.litemall.admin.annotation.LoginAdmin; +import org.linlinjava.litemall.admin.service.AdminTokenManager; +import org.springframework.core.MethodParameter; +import org.springframework.web.bind.support.WebDataBinderFactory; +import org.springframework.web.context.request.NativeWebRequest; +import org.springframework.web.method.support.HandlerMethodArgumentResolver; +import org.springframework.web.method.support.ModelAndViewContainer; + + +public class LoginAdminHandlerMethodArgumentResolver implements HandlerMethodArgumentResolver { + public static final String LOGIN_TOKEN_KEY = "X-Token"; + @Override + public boolean supportsParameter(MethodParameter parameter) { + return parameter.getParameterType().isAssignableFrom(Integer.class)&¶meter.hasParameterAnnotation(LoginAdmin.class); + } + + @Override + public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer container, + NativeWebRequest request, WebDataBinderFactory factory) throws Exception { + +// return new Integer(1); + String token = request.getHeader(LOGIN_TOKEN_KEY); + if(token == null || token.isEmpty()){ + return null; + } + + return AdminTokenManager.getUserId(token); + } +} diff --git a/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/config/AdminConfig.java b/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/config/AdminConfig.java new file mode 100644 index 00000000..6abfb83a --- /dev/null +++ b/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/config/AdminConfig.java @@ -0,0 +1,16 @@ +package org.linlinjava.litemall.admin.config; + +import org.linlinjava.litemall.admin.annotation.support.LoginAdminHandlerMethodArgumentResolver; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.method.support.HandlerMethodArgumentResolver; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter; + +import java.util.List; + +@Configuration +public class AdminConfig extends WebMvcConfigurerAdapter { + @Override + public void addArgumentResolvers(List argumentResolvers) { + argumentResolvers.add(new LoginAdminHandlerMethodArgumentResolver()); + } +} diff --git a/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/config/CorsConfig.java b/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/config/CorsConfig.java new file mode 100644 index 00000000..3fb4134f --- /dev/null +++ b/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/config/CorsConfig.java @@ -0,0 +1,25 @@ +package org.linlinjava.litemall.admin.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.cors.CorsConfiguration; +import org.springframework.web.cors.UrlBasedCorsConfigurationSource; +import org.springframework.web.filter.CorsFilter; + +@Configuration +public class CorsConfig { + private CorsConfiguration buildConfig() { + CorsConfiguration corsConfiguration = new CorsConfiguration(); + corsConfiguration.addAllowedOrigin("*"); // 1 设置访问源地址 + corsConfiguration.addAllowedHeader("*"); // 2 设置访问源请求头 + corsConfiguration.addAllowedMethod("*"); // 3 设置访问源请求方法 + return corsConfiguration; + } + + @Bean + public CorsFilter corsFilter() { + UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); + source.registerCorsConfiguration("/**", buildConfig()); // 4 对接口配置跨域设置 + return new CorsFilter(source); + } +} diff --git a/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/config/GlobalExceptionHandler.java b/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/config/GlobalExceptionHandler.java new file mode 100644 index 00000000..3eac9b11 --- /dev/null +++ b/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/config/GlobalExceptionHandler.java @@ -0,0 +1,27 @@ +package org.linlinjava.litemall.admin.config; + +import org.linlinjava.litemall.db.util.ResponseUtil; +import org.springframework.web.bind.annotation.ControllerAdvice; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException; + +@ControllerAdvice +public class GlobalExceptionHandler { + + + @ExceptionHandler(MethodArgumentTypeMismatchException.class) + @ResponseBody + public Object argumentHandler(MethodArgumentTypeMismatchException e){ + e.printStackTrace(); + return ResponseUtil.badArgumentValue(); + } + + @ExceptionHandler(Exception.class) + @ResponseBody + public Object exceptionHandler(Exception e){ + e.printStackTrace(); + return ResponseUtil.serious(); + } + +} diff --git a/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/config/JacksonConfig.java b/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/config/JacksonConfig.java new file mode 100644 index 00000000..947a5479 --- /dev/null +++ b/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/config/JacksonConfig.java @@ -0,0 +1,36 @@ +package org.linlinjava.litemall.admin.config; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationFeature; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; +import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer; +import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer; +import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Primary; +import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder; + +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.time.format.DateTimeFormatter; + +@Configuration +public class JacksonConfig { + @Bean + @Primary + public ObjectMapper objectMapper(Jackson2ObjectMapperBuilder builder) { + JavaTimeModule javaTimeModule = new JavaTimeModule(); + javaTimeModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))); + javaTimeModule.addSerializer(LocalDate.class, new LocalDateSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd"))); + javaTimeModule.addSerializer(LocalTime.class, new LocalTimeSerializer(DateTimeFormatter.ofPattern("HH:mm:ss"))); + + ObjectMapper objectMapper = builder.createXmlMapper(false).build(); + objectMapper.registerModule(javaTimeModule); + objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false); + objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); + return objectMapper; + } +} diff --git a/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/dao/AdminToken.java b/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/dao/AdminToken.java new file mode 100644 index 00000000..1c2b2816 --- /dev/null +++ b/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/dao/AdminToken.java @@ -0,0 +1,42 @@ +package org.linlinjava.litemall.admin.dao; + +import java.time.LocalDateTime; + +public class AdminToken { + private Integer userId; + private String token; + private LocalDateTime expireTime; + private LocalDateTime updateTime; + + public void setUserId(Integer userId) { + this.userId = userId; + } + + public void setToken(String token) { + this.token = token; + } + + public void setExpireTime(LocalDateTime expireTime) { + this.expireTime = expireTime; + } + + public void setUpdateTime(LocalDateTime updateTime) { + this.updateTime = updateTime; + } + + public Integer getUserId() { + return userId; + } + + public String getToken() { + return token; + } + + public LocalDateTime getExpireTime() { + return expireTime; + } + + public LocalDateTime getUpdateTime() { + return updateTime; + } +} diff --git a/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/service/AdminTokenManager.java b/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/service/AdminTokenManager.java new file mode 100644 index 00000000..06cd3ffb --- /dev/null +++ b/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/service/AdminTokenManager.java @@ -0,0 +1,56 @@ +package org.linlinjava.litemall.admin.service; + +import org.linlinjava.litemall.admin.dao.AdminToken; +import org.linlinjava.litemall.db.util.CharUtil; + +import java.time.LocalDateTime; +import java.util.HashMap; +import java.util.Map; + +public class AdminTokenManager { + private static Map tokenMap = new HashMap<>(); + private static Map idMap = new HashMap<>(); + + public static Integer getUserId(String token) { + + AdminToken userToken = tokenMap.get(token); + if(userToken == null){ + return null; + } + + if(userToken.getExpireTime().isBefore(LocalDateTime.now())){ + tokenMap.remove(token); + idMap.remove(userToken.getUserId()); + return null; + } + + return userToken.getUserId(); + } + + + public static AdminToken generateToken(Integer id){ + AdminToken userToken = idMap.get(id); + if(userToken != null) { + tokenMap.remove(userToken.getToken()); + idMap.remove(id); + } + + String token = CharUtil.getRandomString(32); + while (tokenMap.containsKey(token)) { + token = CharUtil.getRandomString(32); + } + + LocalDateTime update = LocalDateTime.now(); + LocalDateTime expire = update.plusDays(1); + + userToken = new AdminToken(); + userToken.setToken(token); + userToken.setUpdateTime(update); + userToken.setExpireTime(expire); + userToken.setUserId(id); + tokenMap.put(token, userToken); + idMap.put(id, userToken); + + return userToken; + } +} diff --git a/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/AdController.java b/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/AdController.java new file mode 100644 index 00000000..2cd8501d --- /dev/null +++ b/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/AdController.java @@ -0,0 +1,84 @@ +package org.linlinjava.litemall.admin.web; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.linlinjava.litemall.admin.annotation.LoginAdmin; +import org.linlinjava.litemall.db.domain.LitemallAd; +import org.linlinjava.litemall.db.service.LitemallAdService; +import org.linlinjava.litemall.db.util.ResponseUtil; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +@RestController +@RequestMapping("/admin/ad") +public class AdController { + private final Log logger = LogFactory.getLog(AdController.class); + + @Autowired + private LitemallAdService adService; + + @GetMapping("/list") + public Object list(@LoginAdmin Integer adminId, + String name, String content, + @RequestParam(value = "page", defaultValue = "1") Integer page, + @RequestParam(value = "limit", defaultValue = "10") Integer limit, + String sort, String order){ + if(adminId == null){ + return ResponseUtil.unlogin(); + } + + List adList = adService.querySelective(name, content, page, limit, sort, order); + int total = adService.countSelective(name, content, page, limit, sort, order); + Map data = new HashMap<>(); + data.put("total", total); + data.put("items", adList); + + return ResponseUtil.ok(data); + } + + @PostMapping("/create") + public Object create(@LoginAdmin Integer adminId, @RequestBody LitemallAd ad){ + if(adminId == null){ + return ResponseUtil.unlogin(); + } + adService.add(ad); + return ResponseUtil.ok(ad); + } + + @GetMapping("/read") + public Object read(@LoginAdmin Integer adminId, Integer id){ + if(adminId == null){ + return ResponseUtil.unlogin(); + } + + if(id == null){ + return ResponseUtil.badArgument(); + } + + LitemallAd brand = adService.findById(id); + return ResponseUtil.ok(brand); + } + + @PostMapping("/update") + public Object update(@LoginAdmin Integer adminId, @RequestBody LitemallAd ad){ + if(adminId == null){ + return ResponseUtil.unlogin(); + } + adService.updateById(ad); + return ResponseUtil.ok(ad); + } + + @PostMapping("/delete") + public Object delete(@LoginAdmin Integer adminId, @RequestBody LitemallAd ad){ + if(adminId == null){ + return ResponseUtil.unlogin(); + } + adService.deleteById(ad.getId()); + return ResponseUtil.ok(); + } + +} diff --git a/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/AddressController.java b/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/AddressController.java new file mode 100644 index 00000000..7b5ad7cc --- /dev/null +++ b/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/AddressController.java @@ -0,0 +1,116 @@ +package org.linlinjava.litemall.admin.web; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.linlinjava.litemall.admin.annotation.LoginAdmin; +import org.linlinjava.litemall.db.domain.LitemallAddress; +import org.linlinjava.litemall.db.service.LitemallAddressService; +import org.linlinjava.litemall.db.service.LitemallRegionService; +import org.linlinjava.litemall.db.util.ResponseUtil; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +@RestController +@RequestMapping("/admin/address") +public class AddressController { + private final Log logger = LogFactory.getLog(AddressController.class); + + @Autowired + private LitemallAddressService addressService; + @Autowired + private LitemallRegionService regionService; + + private Map toVo (LitemallAddress address){ + Map addressVo = new HashMap<>(); + addressVo.put("id", address.getId()); + addressVo.put("userId", address.getUserId()); + addressVo.put("name", address.getName()); + addressVo.put("mobile", address.getMobile()); + addressVo.put("isDefault", address.getIsDefault()); + addressVo.put("provinceId", address.getProvinceId()); + addressVo.put("cityId", address.getCityId()); + addressVo.put("areaId", address.getAreaId()); + addressVo.put("address", address.getAddress()); + String province = regionService.findById(address.getProvinceId()).getName(); + String city = regionService.findById(address.getCityId()).getName(); + String area = regionService.findById(address.getAreaId()).getName(); + addressVo.put("province", province); + addressVo.put("city", city); + addressVo.put("area", area); + return addressVo; + } + + @GetMapping("/list") + public Object list(@LoginAdmin Integer adminId, + Integer userId, String name, + @RequestParam(value = "page", defaultValue = "1") Integer page, + @RequestParam(value = "limit", defaultValue = "10") Integer limit, + String sort, String order){ + if(adminId == null){ + return ResponseUtil.fail401(); + } + + List addressList = addressService.querySelective(userId, name, page, limit, sort, order); + int total = addressService.countSelective(userId, name, page, limit, sort, order); + + List> addressVoList = new ArrayList<>(addressList.size()); + for(LitemallAddress address : addressList){ + Map addressVo = toVo(address); + addressVoList.add(addressVo); + } + + Map data = new HashMap<>(); + data.put("total", total); + data.put("items", addressVoList); + + return ResponseUtil.ok(data); + } + + @PostMapping("/create") + public Object create(@LoginAdmin Integer adminId, @RequestBody LitemallAddress address){ + if(adminId == null){ + return ResponseUtil.fail401(); + } + + addressService.add(address); + + Map addressVo = toVo(address); + return ResponseUtil.ok(addressVo); + } + + @GetMapping("/read") + public Object read(@LoginAdmin Integer adminId, Integer addressId){ + if(adminId == null){ + return ResponseUtil.fail401(); + } + + LitemallAddress address = addressService.findById(addressId); + Map addressVo = toVo(address); + return ResponseUtil.ok(addressVo); + } + + @PostMapping("/update") + public Object update(@LoginAdmin Integer adminId, @RequestBody LitemallAddress address){ + if(adminId == null){ + return ResponseUtil.fail401(); + } + addressService.updateById(address); + Map addressVo = toVo(address); + return ResponseUtil.ok(addressVo); + } + + @PostMapping("/delete") + public Object delete(@LoginAdmin Integer adminId, @RequestBody LitemallAddress address){ + if(adminId == null){ + return ResponseUtil.fail401(); + } + addressService.delete(address.getId()); + return ResponseUtil.ok(); + } + +} diff --git a/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/AdminController.java b/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/AdminController.java new file mode 100644 index 00000000..45e5f211 --- /dev/null +++ b/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/AdminController.java @@ -0,0 +1,109 @@ +package org.linlinjava.litemall.admin.web; + +import org.linlinjava.litemall.admin.annotation.LoginAdmin; +import org.linlinjava.litemall.admin.service.AdminTokenManager; +import org.linlinjava.litemall.db.domain.LitemallAdmin; +import org.linlinjava.litemall.db.service.LitemallAdminService; +import org.linlinjava.litemall.db.util.ResponseUtil; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +@RestController +@RequestMapping("/admin/admin") +public class AdminController { + @Autowired + private LitemallAdminService adminService; + + @GetMapping("/info") + public Object info(String token){ + Integer adminId = AdminTokenManager.getUserId(token); + if(adminId == null){ + return ResponseUtil.badArgumentValue(); + } + LitemallAdmin admin = adminService.findById(adminId); + if(admin == null){ + return ResponseUtil.badArgumentValue(); + } + + Map data = new HashMap<>(); + data.put("name", admin.getUsername()); + data.put("avatar", admin.getAvatar()); + + // 目前roles不支持,这里简单设置admin + List roles = new ArrayList<>(); + roles.add("admin"); + data.put("roles", roles); + data.put("introduction", "admin introduction"); + return ResponseUtil.ok(data); + } + + @GetMapping("/list") + public Object list(@LoginAdmin Integer adminId, + String username, + @RequestParam(value = "page", defaultValue = "1") Integer page, + @RequestParam(value = "limit", defaultValue = "10") Integer limit, + String sort, String order){ + if(adminId == null){ + return ResponseUtil.unlogin(); + } + + List adminList = adminService.querySelective(username, page, limit, sort, order); + int total = adminService.countSelective(username, page, limit, sort, order); + Map data = new HashMap<>(); + data.put("total", total); + data.put("items", adminList); + + return ResponseUtil.ok(data); + } + + @PostMapping("/create") + public Object create(@LoginAdmin Integer adminId, @RequestBody LitemallAdmin admin){ + if(adminId == null){ + return ResponseUtil.unlogin(); + } + adminService.add(admin); + return ResponseUtil.ok(admin); + } + + @GetMapping("/read") + public Object read(@LoginAdmin Integer adminId, Integer id){ + if(adminId == null){ + return ResponseUtil.unlogin(); + } + + if(id == null){ + return ResponseUtil.badArgument(); + } + + LitemallAdmin admin = adminService.findById(id); + return ResponseUtil.ok(admin); + } + + @PostMapping("/update") + public Object update(@LoginAdmin Integer adminId, @RequestBody LitemallAdmin admin){ + if(adminId == null){ + return ResponseUtil.unlogin(); + } + adminService.updateById(admin); + return ResponseUtil.ok(admin); + } + + @PostMapping("/delete") + public Object delete(@LoginAdmin Integer adminId, @RequestBody LitemallAdmin admin){ + if(adminId == null){ + return ResponseUtil.unlogin(); + } + + Integer anotherAdminId = admin.getId(); + if(anotherAdminId.intValue() == 1){ + return ResponseUtil.fail(403, "超级用户不能删除"); + } + adminService.deleteById(anotherAdminId); + return ResponseUtil.ok(); + } +} diff --git a/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/AuthController.java b/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/AuthController.java new file mode 100644 index 00000000..56e3bb14 --- /dev/null +++ b/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/AuthController.java @@ -0,0 +1,67 @@ +package org.linlinjava.litemall.admin.web; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.linlinjava.litemall.admin.dao.AdminToken; +import org.linlinjava.litemall.admin.annotation.LoginAdmin; +import org.linlinjava.litemall.admin.service.AdminTokenManager; +import org.linlinjava.litemall.db.domain.LitemallAdmin; +import org.linlinjava.litemall.db.service.LitemallAdminService; +import org.linlinjava.litemall.db.util.JacksonUtil; +import org.linlinjava.litemall.db.util.ResponseUtil; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.util.Assert; +import org.springframework.util.StringUtils; +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 java.util.List; + +@RestController +@RequestMapping("/admin/login") +public class AuthController { + private final Log logger = LogFactory.getLog(AuthController.class); + + @Autowired + private LitemallAdminService adminService; + + /* + * { username : value, password : value } + */ + @PostMapping("/login") + public Object login(@RequestBody String body){ + String username = JacksonUtil.parseString(body, "username"); + String password = JacksonUtil.parseString(body, "password"); + + if(StringUtils.isEmpty(username) || StringUtils.isEmpty(password)){ + return ResponseUtil.badArgument(); + } + + List adminList = adminService.findAdmin(username, password); + Assert.state(adminList.size() < 2, "同一个用户名存在两个账户"); + if(adminList.size() == 0){ + return ResponseUtil.badArgumentValue(); + } + + LitemallAdmin admin = adminList.get(0); + Integer adminId = admin.getId(); + // token + AdminToken adminToken = AdminTokenManager.generateToken(adminId); + + return ResponseUtil.ok(adminToken.getToken()); + } + + /* + * + */ + @PostMapping("/logout") + public Object login(@LoginAdmin Integer adminId){ + if(adminId == null){ + return ResponseUtil.unlogin(); + } + + return ResponseUtil.ok(); + } +} diff --git a/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/BrandController.java b/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/BrandController.java new file mode 100644 index 00000000..0449ac15 --- /dev/null +++ b/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/BrandController.java @@ -0,0 +1,84 @@ +package org.linlinjava.litemall.admin.web; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.linlinjava.litemall.admin.annotation.LoginAdmin; +import org.linlinjava.litemall.db.domain.LitemallBrand; +import org.linlinjava.litemall.db.service.LitemallBrandService; +import org.linlinjava.litemall.db.util.ResponseUtil; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +@RestController +@RequestMapping("/admin/brand") +public class BrandController { + private final Log logger = LogFactory.getLog(BrandController.class); + + @Autowired + private LitemallBrandService brandService; + + @GetMapping("/list") + public Object list(@LoginAdmin Integer adminId, + String id, String name, + @RequestParam(value = "page", defaultValue = "1") Integer page, + @RequestParam(value = "limit", defaultValue = "10") Integer limit, + String sort, String order){ + if(adminId == null){ + return ResponseUtil.unlogin(); + } + + List brandList = brandService.querySelective(id, name, page, limit, sort, order); + int total = brandService.countSelective(id, name, page, limit, sort, order); + Map data = new HashMap<>(); + data.put("total", total); + data.put("items", brandList); + + return ResponseUtil.ok(data); + } + + @PostMapping("/create") + public Object create(@LoginAdmin Integer adminId, @RequestBody LitemallBrand brand){ + if(adminId == null){ + return ResponseUtil.unlogin(); + } + brandService.add(brand); + return ResponseUtil.ok(brand); + } + + @GetMapping("/read") + public Object read(@LoginAdmin Integer adminId, Integer id){ + if(adminId == null){ + return ResponseUtil.unlogin(); + } + + if(id == null){ + return ResponseUtil.badArgument(); + } + + LitemallBrand brand = brandService.findById(id); + return ResponseUtil.ok(brand); + } + + @PostMapping("/update") + public Object update(@LoginAdmin Integer adminId, @RequestBody LitemallBrand brand){ + if(adminId == null){ + return ResponseUtil.unlogin(); + } + brandService.updateById(brand); + return ResponseUtil.ok(brand); + } + + @PostMapping("/delete") + public Object delete(@LoginAdmin Integer adminId, @RequestBody LitemallBrand brand){ + if(adminId == null){ + return ResponseUtil.unlogin(); + } + brandService.deleteById(brand.getId()); + return ResponseUtil.ok(); + } + +} diff --git a/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/CartController.java b/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/CartController.java new file mode 100644 index 00000000..b5105ce1 --- /dev/null +++ b/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/CartController.java @@ -0,0 +1,94 @@ +package org.linlinjava.litemall.admin.web; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.linlinjava.litemall.admin.annotation.LoginAdmin; +import org.linlinjava.litemall.db.domain.LitemallCart; +import org.linlinjava.litemall.db.service.LitemallCartService; +import org.linlinjava.litemall.db.service.LitemallGoodsService; +import org.linlinjava.litemall.db.service.LitemallProductService; +import org.linlinjava.litemall.db.service.LitemallUserService; +import org.linlinjava.litemall.db.util.ResponseUtil; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +@RestController +@RequestMapping("/admin/cart") +public class CartController { + private final Log logger = LogFactory.getLog(CartController.class); + + @Autowired + private LitemallCartService cartService; + @Autowired + private LitemallUserService userService; + @Autowired + private LitemallGoodsService goodsService; + @Autowired + private LitemallProductService productService; + + @GetMapping("/list") + public Object list(@LoginAdmin Integer adminId, + Integer userId, Integer goodsId, + @RequestParam(value = "page", defaultValue = "1") Integer page, + @RequestParam(value = "limit", defaultValue = "10") Integer limit, + String sort, String order){ + if(adminId == null){ + return ResponseUtil.fail401(); + } + List cartList = cartService.querySelective(userId, goodsId, page, limit, sort, order); + int total = cartService.countSelective(userId, goodsId, page, limit, sort, order); + + Map data = new HashMap<>(); + data.put("total", total); + data.put("items", cartList); + + return ResponseUtil.ok(data); + } + + /* + * 目前的逻辑不支持管理员创建 + */ + @PostMapping("/create") + public Object create(@LoginAdmin Integer adminId, @RequestBody LitemallCart cart){ + if(adminId == null){ + return ResponseUtil.fail401(); + } + + return ResponseUtil.fail501(); + } + + @GetMapping("/read") + public Object read(@LoginAdmin Integer adminId, Integer id){ + if(adminId == null){ + return ResponseUtil.fail401(); + } + + LitemallCart cart = cartService.findById(id); + return ResponseUtil.ok(cart); + } + + /* + * 目前的逻辑不支持管理员创建 + */ + @PostMapping("/update") + public Object update(@LoginAdmin Integer adminId, @RequestBody LitemallCart cart){ + if(adminId == null){ + return ResponseUtil.fail401(); + } + return ResponseUtil.fail501(); + } + + @PostMapping("/delete") + public Object delete(@LoginAdmin Integer adminId, @RequestBody LitemallCart cart){ + if(adminId == null){ + return ResponseUtil.fail401(); + } + cartService.deleteById(cart.getId()); + return ResponseUtil.ok(); + } + +} diff --git a/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/CategoryController.java b/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/CategoryController.java new file mode 100644 index 00000000..ea30945c --- /dev/null +++ b/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/CategoryController.java @@ -0,0 +1,99 @@ +package org.linlinjava.litemall.admin.web; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.linlinjava.litemall.admin.annotation.LoginAdmin; +import org.linlinjava.litemall.db.domain.LitemallCategory; +import org.linlinjava.litemall.db.service.LitemallCategoryService; +import org.linlinjava.litemall.db.util.ResponseUtil; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +@RestController +@RequestMapping("/admin/category") +public class CategoryController { + private final Log logger = LogFactory.getLog(CategoryController.class); + + @Autowired + private LitemallCategoryService categoryService; + + @GetMapping("/list") + public Object list(@LoginAdmin Integer adminId, + String id, String name, + @RequestParam(value = "page", defaultValue = "1") Integer page, + @RequestParam(value = "limit", defaultValue = "10") Integer limit, + String sort, String order){ + if(adminId == null){ + return ResponseUtil.unlogin(); + } + + List collectList = categoryService.querySelective(id, name, page, limit, sort, order); + int total = categoryService.countSelective(id, name, page, limit, sort, order); + Map data = new HashMap<>(); + data.put("total", total); + data.put("items", collectList); + + return ResponseUtil.ok(data); + } + + @PostMapping("/create") + public Object create(@LoginAdmin Integer adminId, @RequestBody LitemallCategory category){ + if(adminId == null){ + return ResponseUtil.unlogin(); + } + categoryService.add(category); + return ResponseUtil.ok(); + } + + @GetMapping("/read") + public Object read(@LoginAdmin Integer adminId, Integer id){ + if(adminId == null){ + return ResponseUtil.unlogin(); + } + + if(id == null){ + return ResponseUtil.badArgument(); + } + + LitemallCategory category = categoryService.findById(id); + return ResponseUtil.ok(category); + } + + @PostMapping("/update") + public Object update(@LoginAdmin Integer adminId, @RequestBody LitemallCategory category){ + if(adminId == null){ + return ResponseUtil.unlogin(); + } + categoryService.updateById(category); + return ResponseUtil.ok(); + } + + @PostMapping("/delete") + public Object delete(@LoginAdmin Integer adminId, @RequestBody LitemallCategory category){ + if(adminId == null){ + return ResponseUtil.unlogin(); + } + categoryService.deleteById(category.getId()); + return ResponseUtil.ok(); + } + + @GetMapping("/l1") + public Object catL1(@LoginAdmin Integer adminId) { + if (adminId == null) { + return ResponseUtil.unlogin(); + } + + // 所有一级分类目录 + List l1CatList = categoryService.queryL1(); + HashMap data = new HashMap<>(l1CatList.size()); + for(LitemallCategory category : l1CatList){ + data.put(category.getId(), category.getName()); + } + return ResponseUtil.ok(data); + } + +} diff --git a/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/CollectController.java b/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/CollectController.java new file mode 100644 index 00000000..3c5dfcba --- /dev/null +++ b/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/CollectController.java @@ -0,0 +1,83 @@ +package org.linlinjava.litemall.admin.web; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.linlinjava.litemall.admin.annotation.LoginAdmin; +import org.linlinjava.litemall.db.domain.LitemallCollect; +import org.linlinjava.litemall.db.service.LitemallCollectService; +import org.linlinjava.litemall.db.util.ResponseUtil; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +@RestController +@RequestMapping("/admin/collect") +public class CollectController { + private final Log logger = LogFactory.getLog(CollectController.class); + + @Autowired + private LitemallCollectService collectService; + + @GetMapping("/list") + public Object list(@LoginAdmin Integer adminId, + String userId, String valueId, + @RequestParam(value = "page", defaultValue = "1") Integer page, + @RequestParam(value = "limit", defaultValue = "10") Integer limit, + String sort, String order){ + if(adminId == null){ + return ResponseUtil.unlogin(); + } + + List collectList = collectService.querySelective(userId, valueId, page, limit, sort, order); + int total = collectService.countSelective(userId, valueId, page, limit, sort, order); + Map data = new HashMap<>(); + data.put("total", total); + data.put("items", collectList); + + return ResponseUtil.ok(data); + } + + @PostMapping("/create") + public Object create(@LoginAdmin Integer adminId, @RequestBody LitemallCollect collect){ + if(adminId == null){ + return ResponseUtil.unlogin(); + } + return ResponseUtil.unsupport(); + } + + @GetMapping("/read") + public Object read(@LoginAdmin Integer adminId, Integer id){ + if(adminId == null){ + return ResponseUtil.unlogin(); + } + + if(id == null){ + return ResponseUtil.badArgument(); + } + + LitemallCollect collect = collectService.findById(id); + return ResponseUtil.ok(collect); + } + + @PostMapping("/update") + public Object update(@LoginAdmin Integer adminId, @RequestBody LitemallCollect collect){ + if(adminId == null){ + return ResponseUtil.unlogin(); + } + collectService.updateById(collect); + return ResponseUtil.ok(); + } + + @PostMapping("/delete") + public Object delete(@LoginAdmin Integer adminId, @RequestBody LitemallCollect collect){ + if(adminId == null){ + return ResponseUtil.unlogin(); + } + collectService.deleteById(collect.getId()); + return ResponseUtil.ok(); + } + +} diff --git a/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/CommentController.java b/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/CommentController.java new file mode 100644 index 00000000..6f29af2c --- /dev/null +++ b/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/CommentController.java @@ -0,0 +1,84 @@ +package org.linlinjava.litemall.admin.web; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.linlinjava.litemall.admin.annotation.LoginAdmin; +import org.linlinjava.litemall.db.domain.LitemallComment; +import org.linlinjava.litemall.db.service.LitemallCommentService; +import org.linlinjava.litemall.db.util.ResponseUtil; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +@RestController +@RequestMapping("/admin/comment") +public class CommentController { + private final Log logger = LogFactory.getLog(CommentController.class); + + @Autowired + private LitemallCommentService commentService; + + @GetMapping("/list") + public Object list(@LoginAdmin Integer adminId, + String userId, String valueId, + @RequestParam(value = "page", defaultValue = "1") Integer page, + @RequestParam(value = "limit", defaultValue = "10") Integer limit, + String sort, String order){ + if(adminId == null){ + return ResponseUtil.unlogin(); + } + + List brandList = commentService.querySelective(userId, valueId, page, limit, sort, order); + int total = commentService.countSelective(userId, valueId, page, limit, sort, order); + Map data = new HashMap<>(); + data.put("total", total); + data.put("items", brandList); + + return ResponseUtil.ok(data); + } + + @PostMapping("/create") + public Object create(@LoginAdmin Integer adminId, @RequestBody LitemallComment comment){ + if(adminId == null){ + return ResponseUtil.unlogin(); + } + commentService.add(comment); + return ResponseUtil.ok(comment); + } + + @GetMapping("/read") + public Object read(@LoginAdmin Integer adminId, Integer id){ + if(adminId == null){ + return ResponseUtil.unlogin(); + } + + if(id == null){ + return ResponseUtil.badArgument(); + } + + LitemallComment comment = commentService.findById(id); + return ResponseUtil.ok(comment); + } + + @PostMapping("/update") + public Object update(@LoginAdmin Integer adminId, @RequestBody LitemallComment comment){ + if(adminId == null){ + return ResponseUtil.unlogin(); + } + commentService.updateById(comment); + return ResponseUtil.ok(comment); + } + + @PostMapping("/delete") + public Object delete(@LoginAdmin Integer adminId, @RequestBody LitemallComment comment){ + if(adminId == null){ + return ResponseUtil.unlogin(); + } + commentService.deleteById(comment.getId()); + return ResponseUtil.ok(); + } + +} diff --git a/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/DashbordController.java b/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/DashbordController.java new file mode 100644 index 00000000..7ed457cd --- /dev/null +++ b/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/DashbordController.java @@ -0,0 +1,50 @@ +package org.linlinjava.litemall.admin.web; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.linlinjava.litemall.admin.annotation.LoginAdmin; +import org.linlinjava.litemall.db.service.LitemallGoodsService; +import org.linlinjava.litemall.db.service.LitemallOrderService; +import org.linlinjava.litemall.db.service.LitemallProductService; +import org.linlinjava.litemall.db.service.LitemallUserService; +import org.linlinjava.litemall.db.util.ResponseUtil; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import java.util.HashMap; +import java.util.Map; + +@RestController +@RequestMapping("/admin/dashboard") +public class DashbordController { + private final Log logger = LogFactory.getLog(DashbordController.class); + + @Autowired + private LitemallUserService userService; + @Autowired + private LitemallGoodsService goodsService; + @Autowired + private LitemallProductService productService; + @Autowired + private LitemallOrderService orderService; + + @GetMapping("") + public Object info(@LoginAdmin Integer adminId){ + if(adminId == null){ + return ResponseUtil.unlogin(); + } + + int userTotal = userService.count(); + int goodsTotal = goodsService.count(); + int productTotal = productService.count(); + int orderTotal = orderService.count(); + Map data = new HashMap<>(); + data.put("userTotal", userTotal); + data.put("goodsTotal", goodsTotal); + data.put("productTotal", productTotal); + data.put("orderTotal", orderTotal); + + return ResponseUtil.ok(data); + } + +} diff --git a/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/FootprintController.java b/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/FootprintController.java new file mode 100644 index 00000000..0d17f2ed --- /dev/null +++ b/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/FootprintController.java @@ -0,0 +1,83 @@ +package org.linlinjava.litemall.admin.web; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.linlinjava.litemall.admin.annotation.LoginAdmin; +import org.linlinjava.litemall.db.domain.LitemallFootprint; +import org.linlinjava.litemall.db.service.LitemallFootprintService; +import org.linlinjava.litemall.db.util.ResponseUtil; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +@RestController +@RequestMapping("/admin/footprint") +public class FootprintController { + private final Log logger = LogFactory.getLog(FootprintController.class); + + @Autowired + private LitemallFootprintService footprintService; + + @GetMapping("/list") + public Object list(@LoginAdmin Integer adminId, + String userId, String goodsId, + @RequestParam(value = "page", defaultValue = "1") Integer page, + @RequestParam(value = "limit", defaultValue = "10") Integer limit, + String sort, String order){ + if(adminId == null){ + return ResponseUtil.unlogin(); + } + + List footprintList = footprintService.querySelective(userId, goodsId, page, limit, sort, order); + int total = footprintService.countSelective(userId, goodsId, page, limit, sort, order); + Map data = new HashMap<>(); + data.put("total", total); + data.put("items", footprintList); + + return ResponseUtil.ok(data); + } + + @PostMapping("/create") + public Object create(@LoginAdmin Integer adminId, @RequestBody LitemallFootprint footprint){ + if(adminId == null){ + return ResponseUtil.unlogin(); + } + return ResponseUtil.unsupport(); + } + + @GetMapping("/read") + public Object read(@LoginAdmin Integer adminId, Integer id){ + if(adminId == null){ + return ResponseUtil.unlogin(); + } + + if(id == null){ + return ResponseUtil.badArgument(); + } + + LitemallFootprint footprint = footprintService.findById(id); + return ResponseUtil.ok(footprint); + } + + @PostMapping("/update") + public Object update(@LoginAdmin Integer adminId, @RequestBody LitemallFootprint footprint){ + if(adminId == null){ + return ResponseUtil.unlogin(); + } + footprintService.updateById(footprint); + return ResponseUtil.ok(); + } + + @PostMapping("/delete") + public Object delete(@LoginAdmin Integer adminId, @RequestBody LitemallFootprint footprint){ + if(adminId == null){ + return ResponseUtil.unlogin(); + } + footprintService.deleteById(footprint.getId()); + return ResponseUtil.ok(); + } + +} diff --git a/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/GoodsAttributeController.java b/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/GoodsAttributeController.java new file mode 100644 index 00000000..d319fd0e --- /dev/null +++ b/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/GoodsAttributeController.java @@ -0,0 +1,84 @@ +package org.linlinjava.litemall.admin.web; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.linlinjava.litemall.admin.annotation.LoginAdmin; +import org.linlinjava.litemall.db.domain.LitemallGoodsAttribute; +import org.linlinjava.litemall.db.service.LitemallGoodsAttributeService; +import org.linlinjava.litemall.db.util.ResponseUtil; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +@RestController +@RequestMapping("/admin/goods-attribute") +public class GoodsAttributeController { + private final Log logger = LogFactory.getLog(GoodsAttributeController.class); + + @Autowired + private LitemallGoodsAttributeService goodsAttributeService; + + @GetMapping("/list") + public Object list(@LoginAdmin Integer adminId, + Integer goodsId, + @RequestParam(value = "page", defaultValue = "1") Integer page, + @RequestParam(value = "limit", defaultValue = "10") Integer limit, + String sort, String order){ + if(adminId == null){ + return ResponseUtil.unlogin(); + } + + List goodsAttributeList = goodsAttributeService.querySelective(goodsId, page, limit, sort, order); + int total = goodsAttributeService.countSelective(goodsId, page, limit, sort, order); + Map data = new HashMap<>(); + data.put("total", total); + data.put("items", goodsAttributeList); + + return ResponseUtil.ok(data); + } + + @PostMapping("/create") + public Object create(@LoginAdmin Integer adminId, @RequestBody LitemallGoodsAttribute goodsAttribute){ + if(adminId == null){ + return ResponseUtil.unlogin(); + } + goodsAttributeService.add(goodsAttribute); + return ResponseUtil.ok(goodsAttribute); + } + + @GetMapping("/read") + public Object read(@LoginAdmin Integer adminId, Integer id){ + if(adminId == null){ + return ResponseUtil.unlogin(); + } + + if(id == null){ + return ResponseUtil.badArgument(); + } + + LitemallGoodsAttribute goodsAttribute = goodsAttributeService.findById(id); + return ResponseUtil.ok(goodsAttribute); + } + + @PostMapping("/update") + public Object update(@LoginAdmin Integer adminId, @RequestBody LitemallGoodsAttribute goodsAttribute){ + if(adminId == null){ + return ResponseUtil.unlogin(); + } + goodsAttributeService.updateById(goodsAttribute); + return ResponseUtil.ok(goodsAttribute); + } + + @PostMapping("/delete") + public Object delete(@LoginAdmin Integer adminId, @RequestBody LitemallGoodsAttribute goodsAttribute){ + if(adminId == null){ + return ResponseUtil.unlogin(); + } + goodsAttributeService.deleteById(goodsAttribute.getId()); + return ResponseUtil.ok(); + } + +} diff --git a/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/GoodsController.java b/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/GoodsController.java new file mode 100644 index 00000000..c27c64ff --- /dev/null +++ b/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/GoodsController.java @@ -0,0 +1,84 @@ +package org.linlinjava.litemall.admin.web; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.linlinjava.litemall.admin.annotation.LoginAdmin; +import org.linlinjava.litemall.db.domain.LitemallGoods; +import org.linlinjava.litemall.db.service.LitemallGoodsService; +import org.linlinjava.litemall.db.util.ResponseUtil; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +@RestController +@RequestMapping("/admin/goods") +public class GoodsController { + private final Log logger = LogFactory.getLog(GoodsController.class); + + @Autowired + private LitemallGoodsService goodsService; + + @GetMapping("/list") + public Object list(@LoginAdmin Integer adminId, + String goodsSn, String name, + @RequestParam(value = "page", defaultValue = "1") Integer page, + @RequestParam(value = "limit", defaultValue = "10") Integer limit, + String sort, String order){ + if(adminId == null){ + return ResponseUtil.unlogin(); + } + + List goodsList = goodsService.querySelective(goodsSn, name, page, limit, sort, order); + int total = goodsService.countSelective(goodsSn, name, page, limit, sort, order); + Map data = new HashMap<>(); + data.put("total", total); + data.put("items", goodsList); + + return ResponseUtil.ok(data); + } + + @PostMapping("/create") + public Object create(@LoginAdmin Integer adminId, @RequestBody LitemallGoods goods){ + if(adminId == null){ + return ResponseUtil.unlogin(); + } + goodsService.add(goods); + return ResponseUtil.ok(goods); + } + + @GetMapping("/read") + public Object read(@LoginAdmin Integer adminId, Integer id){ + if(adminId == null){ + return ResponseUtil.unlogin(); + } + + if(id == null){ + return ResponseUtil.badArgument(); + } + + LitemallGoods goods = goodsService.findById(id); + return ResponseUtil.ok(goods); + } + + @PostMapping("/update") + public Object update(@LoginAdmin Integer adminId, @RequestBody LitemallGoods goods){ + if(adminId == null){ + return ResponseUtil.unlogin(); + } + goodsService.updateById(goods); + return ResponseUtil.ok(goods); + } + + @PostMapping("/delete") + public Object delete(@LoginAdmin Integer adminId, @RequestBody LitemallGoods goods){ + if(adminId == null){ + return ResponseUtil.unlogin(); + } + goodsService.deleteById(goods.getId()); + return ResponseUtil.ok(); + } + +} diff --git a/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/GoodsSpecificationController.java b/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/GoodsSpecificationController.java new file mode 100644 index 00000000..e64ed4c2 --- /dev/null +++ b/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/GoodsSpecificationController.java @@ -0,0 +1,98 @@ +package org.linlinjava.litemall.admin.web; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.linlinjava.litemall.admin.annotation.LoginAdmin; +import org.linlinjava.litemall.db.domain.LitemallGoodsSpecification; +import org.linlinjava.litemall.db.service.LitemallGoodsSpecificationService; +import org.linlinjava.litemall.db.util.ResponseUtil; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +@RestController +@RequestMapping("/admin/goods-specification") +public class GoodsSpecificationController { + private final Log logger = LogFactory.getLog(GoodsSpecificationController.class); + + @Autowired + private LitemallGoodsSpecificationService goodsSpecificationService; + + @GetMapping("/list") + public Object list(@LoginAdmin Integer adminId, + Integer goodsId, + @RequestParam(value = "page", defaultValue = "1") Integer page, + @RequestParam(value = "limit", defaultValue = "10") Integer limit, + String sort, String order){ + if(adminId == null){ + return ResponseUtil.unlogin(); + } + + List goodsSpecificationList = goodsSpecificationService.querySelective(goodsId, page, limit, sort, order); + int total = goodsSpecificationService.countSelective(goodsId, page, limit, sort, order); + Map data = new HashMap<>(); + data.put("total", total); + data.put("items", goodsSpecificationList); + + return ResponseUtil.ok(data); + } + + @PostMapping("/create") + public Object create(@LoginAdmin Integer adminId, @RequestBody LitemallGoodsSpecification goodsSpecification){ + if(adminId == null){ + return ResponseUtil.unlogin(); + } + goodsSpecificationService.add(goodsSpecification); + return ResponseUtil.ok(goodsSpecification); + } + + @GetMapping("/read") + public Object read(@LoginAdmin Integer adminId, Integer id){ + if(adminId == null){ + return ResponseUtil.unlogin(); + } + + if(id == null){ + return ResponseUtil.badArgument(); + } + + LitemallGoodsSpecification goodsSpecification = goodsSpecificationService.findById(id); + return ResponseUtil.ok(goodsSpecification); + } + + @PostMapping("/update") + public Object update(@LoginAdmin Integer adminId, @RequestBody LitemallGoodsSpecification goodsSpecification){ + if(adminId == null){ + return ResponseUtil.unlogin(); + } + goodsSpecificationService.updateById(goodsSpecification); + return ResponseUtil.ok(goodsSpecification); + } + + @PostMapping("/delete") + public Object delete(@LoginAdmin Integer adminId, @RequestBody LitemallGoodsSpecification goodsSpecification){ + if(adminId == null){ + return ResponseUtil.unlogin(); + } + goodsSpecificationService.deleteById(goodsSpecification.getId()); + return ResponseUtil.ok(); + } + + @GetMapping("/volist") + public Object volist(@LoginAdmin Integer adminId, Integer id){ + if(adminId == null){ + return ResponseUtil.unlogin(); + } + + if(id == null){ + return ResponseUtil.badArgument(); + } + + Object goodsSpecificationVoList = goodsSpecificationService.getSpecificationVoList(id); + return ResponseUtil.ok(goodsSpecificationVoList); + } + +} diff --git a/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/HistoryController.java b/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/HistoryController.java new file mode 100644 index 00000000..901237c1 --- /dev/null +++ b/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/HistoryController.java @@ -0,0 +1,83 @@ +package org.linlinjava.litemall.admin.web; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.linlinjava.litemall.admin.annotation.LoginAdmin; +import org.linlinjava.litemall.db.domain.LitemallSearchHistory; +import org.linlinjava.litemall.db.service.LitemallSearchHistoryService; +import org.linlinjava.litemall.db.util.ResponseUtil; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +@RestController +@RequestMapping("/admin/history") +public class HistoryController { + private final Log logger = LogFactory.getLog(HistoryController.class); + + @Autowired + private LitemallSearchHistoryService searchHistoryService; + + @GetMapping("/list") + public Object list(@LoginAdmin Integer adminId, + String userId, String keyword, + @RequestParam(value = "page", defaultValue = "1") Integer page, + @RequestParam(value = "limit", defaultValue = "10") Integer limit, + String sort, String order){ + if(adminId == null){ + return ResponseUtil.unlogin(); + } + + List footprintList = searchHistoryService.querySelective(userId, keyword, page, limit, sort, order); + int total = searchHistoryService.countSelective(userId, keyword, page, limit, sort, order); + Map data = new HashMap<>(); + data.put("total", total); + data.put("items", footprintList); + + return ResponseUtil.ok(data); + } + + @PostMapping("/create") + public Object create(@LoginAdmin Integer adminId, @RequestBody LitemallSearchHistory history){ + if(adminId == null){ + return ResponseUtil.fail401(); + } + return ResponseUtil.fail501(); + } + + @GetMapping("/read") + public Object read(@LoginAdmin Integer adminId, Integer id){ + if(adminId == null){ + return ResponseUtil.unlogin(); + } + + if(id == null){ + return ResponseUtil.badArgument(); + } + + LitemallSearchHistory history = searchHistoryService.findById(id); + return ResponseUtil.ok(history); + } + + @PostMapping("/update") + public Object update(@LoginAdmin Integer adminId, @RequestBody LitemallSearchHistory history){ + if(adminId == null){ + return ResponseUtil.unlogin(); + } + searchHistoryService.updateById(history); + return ResponseUtil.ok(); + } + + @PostMapping("/delete") + public Object delete(@LoginAdmin Integer adminId, @RequestBody LitemallSearchHistory history){ + if(adminId == null){ + return ResponseUtil.unlogin(); + } + searchHistoryService.deleteById(history.getId()); + return ResponseUtil.ok(); + } + +} diff --git a/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/IndexController.java b/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/IndexController.java new file mode 100644 index 00000000..2366952a --- /dev/null +++ b/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/IndexController.java @@ -0,0 +1,20 @@ +package org.linlinjava.litemall.admin.web; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.linlinjava.litemall.db.util.ResponseUtil; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping("/admin/index") +public class IndexController { + private final Log logger = LogFactory.getLog(IndexController.class); + + @RequestMapping("/index") + public Object index(){ + return ResponseUtil.ok("hello world"); + } + + +} diff --git a/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/IssueController.java b/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/IssueController.java new file mode 100644 index 00000000..0ffd75ff --- /dev/null +++ b/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/IssueController.java @@ -0,0 +1,84 @@ +package org.linlinjava.litemall.admin.web; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.linlinjava.litemall.admin.annotation.LoginAdmin; +import org.linlinjava.litemall.db.domain.LitemallIssue; +import org.linlinjava.litemall.db.service.LitemallIssueService; +import org.linlinjava.litemall.db.util.ResponseUtil; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +@RestController +@RequestMapping("/admin/issue") +public class IssueController { + private final Log logger = LogFactory.getLog(IssueController.class); + + @Autowired + private LitemallIssueService issueService; + + @GetMapping("/list") + public Object list(@LoginAdmin Integer adminId, + String question, + @RequestParam(value = "page", defaultValue = "1") Integer page, + @RequestParam(value = "limit", defaultValue = "10") Integer limit, + String sort, String order){ + if(adminId == null){ + return ResponseUtil.unlogin(); + } + + List issueList = issueService.querySelective(question, page, limit, sort, order); + int total = issueService.countSelective(question, page, limit, sort, order); + Map data = new HashMap<>(); + data.put("total", total); + data.put("items", issueList); + + return ResponseUtil.ok(data); + } + + @PostMapping("/create") + public Object create(@LoginAdmin Integer adminId, @RequestBody LitemallIssue brand){ + if(adminId == null){ + return ResponseUtil.unlogin(); + } + issueService.add(brand); + return ResponseUtil.ok(brand); + } + + @GetMapping("/read") + public Object read(@LoginAdmin Integer adminId, Integer id){ + if(adminId == null){ + return ResponseUtil.unlogin(); + } + + if(id == null){ + return ResponseUtil.badArgument(); + } + + LitemallIssue brand = issueService.findById(id); + return ResponseUtil.ok(brand); + } + + @PostMapping("/update") + public Object update(@LoginAdmin Integer adminId, @RequestBody LitemallIssue brand){ + if(adminId == null){ + return ResponseUtil.unlogin(); + } + issueService.updateById(brand); + return ResponseUtil.ok(brand); + } + + @PostMapping("/delete") + public Object delete(@LoginAdmin Integer adminId, @RequestBody LitemallIssue brand){ + if(adminId == null){ + return ResponseUtil.unlogin(); + } + issueService.deleteById(brand.getId()); + return ResponseUtil.ok(); + } + +} diff --git a/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/KeywordController.java b/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/KeywordController.java new file mode 100644 index 00000000..0e1edaf7 --- /dev/null +++ b/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/KeywordController.java @@ -0,0 +1,84 @@ +package org.linlinjava.litemall.admin.web; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.linlinjava.litemall.admin.annotation.LoginAdmin; +import org.linlinjava.litemall.db.domain.LitemallKeyword; +import org.linlinjava.litemall.db.service.LitemallKeywordService; +import org.linlinjava.litemall.db.util.ResponseUtil; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +@RestController +@RequestMapping("/admin/keyword") +public class KeywordController { + private final Log logger = LogFactory.getLog(KeywordController.class); + + @Autowired + private LitemallKeywordService keywordService; + + @GetMapping("/list") + public Object list(@LoginAdmin Integer adminId, + String keyword, String url, + @RequestParam(value = "page", defaultValue = "1") Integer page, + @RequestParam(value = "limit", defaultValue = "10") Integer limit, + String sort, String order){ + if(adminId == null){ + return ResponseUtil.unlogin(); + } + + List brandList = keywordService.querySelective(keyword, url, page, limit, sort, order); + int total = keywordService.countSelective(keyword, url, page, limit, sort, order); + Map data = new HashMap<>(); + data.put("total", total); + data.put("items", brandList); + + return ResponseUtil.ok(data); + } + + @PostMapping("/create") + public Object create(@LoginAdmin Integer adminId, @RequestBody LitemallKeyword keywords){ + if(adminId == null){ + return ResponseUtil.unlogin(); + } + keywordService.add(keywords); + return ResponseUtil.ok(keywords); + } + + @GetMapping("/read") + public Object read(@LoginAdmin Integer adminId, Integer id){ + if(adminId == null){ + return ResponseUtil.unlogin(); + } + + if(id == null){ + return ResponseUtil.badArgument(); + } + + LitemallKeyword brand = keywordService.findById(id); + return ResponseUtil.ok(brand); + } + + @PostMapping("/update") + public Object update(@LoginAdmin Integer adminId, @RequestBody LitemallKeyword keywords){ + if(adminId == null){ + return ResponseUtil.unlogin(); + } + keywordService.updateById(keywords); + return ResponseUtil.ok(keywords); + } + + @PostMapping("/delete") + public Object delete(@LoginAdmin Integer adminId, @RequestBody LitemallKeyword brand){ + if(adminId == null){ + return ResponseUtil.unlogin(); + } + keywordService.deleteById(brand.getId()); + return ResponseUtil.ok(); + } + +} diff --git a/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/OrderController.java b/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/OrderController.java new file mode 100644 index 00000000..7968bb6c --- /dev/null +++ b/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/OrderController.java @@ -0,0 +1,110 @@ +package org.linlinjava.litemall.admin.web; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.linlinjava.litemall.admin.annotation.LoginAdmin; +import org.linlinjava.litemall.db.domain.LitemallOrder; +import org.linlinjava.litemall.db.service.LitemallOrderService; +import org.linlinjava.litemall.db.util.OrderUtil; +import org.linlinjava.litemall.db.util.ResponseUtil; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +@RestController +@RequestMapping("/admin/order") +public class OrderController { + private final Log logger = LogFactory.getLog(OrderController.class); + + @Autowired + private LitemallOrderService orderService; + + @GetMapping("/list") + public Object list(@LoginAdmin Integer adminId, + Integer userId, String orderSn, + @RequestParam(value = "page", defaultValue = "1") Integer page, + @RequestParam(value = "limit", defaultValue = "10") Integer limit, + String sort, String order){ + if(adminId == null){ + return ResponseUtil.fail401(); + } + List orderList = orderService.querySelective(userId, orderSn, page, limit, sort, order); + int total = orderService.countSelective(userId, orderSn, page, limit, sort, order); + + Map data = new HashMap<>(); + data.put("total", total); + data.put("items", orderList); + + return ResponseUtil.ok(data); + } + + /* + * 目前的逻辑不支持管理员创建 + */ + @PostMapping("/create") + public Object create(@LoginAdmin Integer adminId, @RequestBody LitemallOrder order){ + if(adminId == null){ + return ResponseUtil.unlogin(); + } + return ResponseUtil.unsupport(); + } + + @GetMapping("/read") + public Object read(@LoginAdmin Integer adminId, Integer id){ + if(adminId == null){ + return ResponseUtil.fail401(); + } + + LitemallOrder order = orderService.findById(id); + return ResponseUtil.ok(order); + } + + /* + * 目前仅仅支持管理员设置发货相关的信息 + */ + @PostMapping("/update") + public Object update(@LoginAdmin Integer adminId, @RequestBody LitemallOrder order){ + if(adminId == null){ + return ResponseUtil.unlogin(); + } + + Integer orderId = order.getId(); + if(orderId == null){ + return ResponseUtil.badArgument(); + } + + LitemallOrder zmallOrder = orderService.findById(orderId); + if(zmallOrder == null){ + return ResponseUtil.badArgumentValue(); + } + + if(OrderUtil.isPayStatus(zmallOrder) || OrderUtil.isShipStatus(zmallOrder)){ + LitemallOrder newOrder = new LitemallOrder(); + newOrder.setId(orderId); + newOrder.setShipChannel(order.getShipChannel()); + newOrder.setShipSn(order.getOrderSn()); + newOrder.setShipStartTime(order.getShipStartTime()); + newOrder.setShipEndTime(order.getShipEndTime()); + newOrder.setOrderStatus(OrderUtil.STATUS_SHIP); + orderService.update(newOrder); + } + else { + return ResponseUtil.badArgumentValue(); + } + + zmallOrder = orderService.findById(orderId); + return ResponseUtil.ok(zmallOrder); + } + + @PostMapping("/delete") + public Object delete(@LoginAdmin Integer adminId, @RequestBody LitemallOrder order){ + if(adminId == null){ + return ResponseUtil.unlogin(); + } + return ResponseUtil.unsupport(); + } + +} diff --git a/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/ProductController.java b/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/ProductController.java new file mode 100644 index 00000000..15f5a909 --- /dev/null +++ b/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/ProductController.java @@ -0,0 +1,125 @@ +package org.linlinjava.litemall.admin.web; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.linlinjava.litemall.admin.annotation.LoginAdmin; +import org.linlinjava.litemall.db.domain.LitemallGoods; +import org.linlinjava.litemall.db.domain.LitemallProduct; +import org.linlinjava.litemall.db.service.LitemallGoodsService; +import org.linlinjava.litemall.db.service.LitemallGoodsSpecificationService; +import org.linlinjava.litemall.db.service.LitemallProductService; +import org.linlinjava.litemall.db.util.ResponseUtil; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import java.math.BigDecimal; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +@RestController +@RequestMapping("/admin/product") +public class ProductController { + private final Log logger = LogFactory.getLog(ProductController.class); + + @Autowired + private LitemallProductService productService; + @Autowired + private LitemallGoodsService goodsService; + @Autowired + private LitemallGoodsSpecificationService goodsSpecificationService; + + @GetMapping("/list") + public Object list(@LoginAdmin Integer adminId, + Integer goodsId, + @RequestParam(value = "page", defaultValue = "1") Integer page, + @RequestParam(value = "limit", defaultValue = "10") Integer limit, + String sort, String order){ + if(adminId == null){ + return ResponseUtil.unlogin(); + } + + List productList = productService.querySelective(goodsId, page, limit, sort, order); + int total = productService.countSelective(goodsId, page, limit, sort, order); + Map data = new HashMap<>(); + data.put("total", total); + data.put("items", productList); + + return ResponseUtil.ok(data); + } + + /** + * + * @param adminId + * @param litemallProduct + * @return + */ + @PostMapping("/create") + public Object create(@LoginAdmin Integer adminId, @RequestBody LitemallProduct litemallProduct){ + if(adminId == null){ + return ResponseUtil.unlogin(); + } + + Integer goodsId = litemallProduct.getGoodsId(); + if(goodsId == null){ + return ResponseUtil.badArgument(); + } + + LitemallGoods goods = goodsService.findById(goodsId); + if(goods == null){ + return ResponseUtil.badArgumentValue(); + } + + List productList = productService.queryByGid(goodsId); + if(productList.size() != 0){ + return ResponseUtil.badArgumentValue(); + } + + Integer[] goodsSpecificationIds = goodsSpecificationService.queryIdsByGid(goodsId); + if(goodsSpecificationIds.length == 0) { + return ResponseUtil.serious(); + } + + LitemallProduct product = new LitemallProduct(); + product.setGoodsId(goodsId); + product.setGoodsNumber(0); + product.setRetailPrice(new BigDecimal(0.00)); + product.setGoodsSpecificationIds(goodsSpecificationIds); + productService.add(product); + + return ResponseUtil.ok(); + } + + @GetMapping("/read") + public Object read(@LoginAdmin Integer adminId, Integer id){ + if(adminId == null){ + return ResponseUtil.unlogin(); + } + + if(id == null){ + return ResponseUtil.badArgument(); + } + + LitemallProduct product = productService.findById(id); + return ResponseUtil.ok(product); + } + + @PostMapping("/update") + public Object update(@LoginAdmin Integer adminId, @RequestBody LitemallProduct product){ + if(adminId == null){ + return ResponseUtil.unlogin(); + } + productService.updateById(product); + return ResponseUtil.ok(product); + } + + @PostMapping("/delete") + public Object delete(@LoginAdmin Integer adminId, @RequestBody LitemallProduct product){ + if(adminId == null){ + return ResponseUtil.unlogin(); + } + productService.deleteById(product.getId()); + return ResponseUtil.ok(); + } + +} diff --git a/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/RegionController.java b/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/RegionController.java new file mode 100644 index 00000000..116fc44d --- /dev/null +++ b/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/RegionController.java @@ -0,0 +1,56 @@ +package org.linlinjava.litemall.admin.web; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.linlinjava.litemall.admin.annotation.LoginAdmin; +import org.linlinjava.litemall.db.domain.LitemallRegion; +import org.linlinjava.litemall.db.service.LitemallRegionService; +import org.linlinjava.litemall.db.util.ResponseUtil; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +@RestController +@RequestMapping("/admin/region") +public class RegionController { + private final Log logger = LogFactory.getLog(RegionController.class); + + @Autowired + private LitemallRegionService regionService; + + @GetMapping("/clist") + public Object clist(@LoginAdmin Integer adminId, Integer id) { + if (id == null) { + return ResponseUtil.badArgument(); + } + + List regionList = regionService.queryByPid(id); + + return ResponseUtil.ok(regionList); + } + + @GetMapping("/list") + public Object list(@LoginAdmin Integer adminId, + String name, Integer code, + @RequestParam(value = "page", defaultValue = "1") Integer page, + @RequestParam(value = "limit", defaultValue = "10") Integer limit, + String sort, String order){ + if(adminId == null){ + return ResponseUtil.unlogin(); + } + + List regionList = regionService.querySelective(name, code, page, limit, sort, order); + int total = regionService.countSelective(name, code, page, limit, sort, order); + Map data = new HashMap<>(); + data.put("total", total); + data.put("items", regionList); + + return ResponseUtil.ok(data); + } +} diff --git a/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/TopicController.java b/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/TopicController.java new file mode 100644 index 00000000..473dbe13 --- /dev/null +++ b/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/TopicController.java @@ -0,0 +1,84 @@ +package org.linlinjava.litemall.admin.web; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.linlinjava.litemall.admin.annotation.LoginAdmin; +import org.linlinjava.litemall.db.domain.LitemallTopic; +import org.linlinjava.litemall.db.service.LitemallTopicService; +import org.linlinjava.litemall.db.util.ResponseUtil; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +@RestController +@RequestMapping("/admin/topic") +public class TopicController { + private final Log logger = LogFactory.getLog(TopicController.class); + + @Autowired + private LitemallTopicService topicService; + + @GetMapping("/list") + public Object list(@LoginAdmin Integer adminId, + String title, String subtitle, + @RequestParam(value = "page", defaultValue = "1") Integer page, + @RequestParam(value = "limit", defaultValue = "10") Integer limit, + String sort, String order){ + if(adminId == null){ + return ResponseUtil.unlogin(); + } + + List topicList = topicService.querySelective(title, subtitle, page, limit, sort, order); + int total = topicService.countSelective(title, subtitle, page, limit, sort, order); + Map data = new HashMap<>(); + data.put("total", total); + data.put("items", topicList); + + return ResponseUtil.ok(data); + } + + @PostMapping("/create") + public Object create(@LoginAdmin Integer adminId, @RequestBody LitemallTopic topic){ + if(adminId == null){ + return ResponseUtil.unlogin(); + } + topicService.add(topic); + return ResponseUtil.ok(topic); + } + + @GetMapping("/read") + public Object read(@LoginAdmin Integer adminId, Integer id){ + if(adminId == null){ + return ResponseUtil.unlogin(); + } + + if(id == null){ + return ResponseUtil.badArgument(); + } + + LitemallTopic brand = topicService.findById(id); + return ResponseUtil.ok(brand); + } + + @PostMapping("/update") + public Object update(@LoginAdmin Integer adminId, @RequestBody LitemallTopic topic){ + if(adminId == null){ + return ResponseUtil.unlogin(); + } + topicService.updateById(topic); + return ResponseUtil.ok(topic); + } + + @PostMapping("/delete") + public Object delete(@LoginAdmin Integer adminId, @RequestBody LitemallTopic topic){ + if(adminId == null){ + return ResponseUtil.unlogin(); + } + topicService.deleteById(topic.getId()); + return ResponseUtil.ok(); + } + +} diff --git a/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/UserController.java b/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/UserController.java new file mode 100644 index 00000000..c137ea72 --- /dev/null +++ b/litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/UserController.java @@ -0,0 +1,72 @@ +package org.linlinjava.litemall.admin.web; + +import com.github.pagehelper.util.StringUtil; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.linlinjava.litemall.admin.annotation.LoginAdmin; +import org.linlinjava.litemall.db.domain.LitemallUser; +import org.linlinjava.litemall.db.service.LitemallUserService; +import org.linlinjava.litemall.db.util.ResponseUtil; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +@RestController +@RequestMapping("/admin/user") +public class UserController { + private final Log logger = LogFactory.getLog(UserController.class); + + @Autowired + private LitemallUserService userService; + + @GetMapping("/list") + public Object list(@LoginAdmin Integer adminId, + String username, String mobile, + @RequestParam(value = "page", defaultValue = "1") Integer page, + @RequestParam(value = "limit", defaultValue = "10") Integer limit, + String sort, String order){ + if(adminId == null){ + return ResponseUtil.fail401(); + } + List userList = userService.querySelective(username, mobile, page, limit, sort, order); + int total = userService.countSeletive(username, mobile, page, limit, sort, order); + Map data = new HashMap<>(); + data.put("total", total); + data.put("items", userList); + + return ResponseUtil.ok(data); + } + + @GetMapping("/username") + public Object username(String username){ + if(StringUtil.isEmpty(username)){ + return ResponseUtil.fail402(); + } + + int total = userService.countSeletive(username, null, null, null, null, null); + if(total == 0){ + return ResponseUtil.ok("不存在"); + } + return ResponseUtil.ok("已存在"); + } + + + @PostMapping("/create") + public Object create(@LoginAdmin Integer adminId, @RequestBody LitemallUser user){ + logger.debug(user); + + userService.add(user); + return ResponseUtil.ok(user); + } + + @PostMapping("/update") + public Object update(@LoginAdmin Integer adminId, @RequestBody LitemallUser user){ + logger.debug(user); + + userService.update(user); + return ResponseUtil.ok(user); + } +} diff --git a/litemall-admin-api/src/main/resources/application-dev.properties b/litemall-admin-api/src/main/resources/application-dev.properties new file mode 100644 index 00000000..f9a6f906 --- /dev/null +++ b/litemall-admin-api/src/main/resources/application-dev.properties @@ -0,0 +1,28 @@ +pagehelper.helperDialect=mysql +pagehelper.reasonable=true +pagehelper.supportMethodsArguments=true +pagehelper.params=count=countSql + +spring.datasource.druid.url=jdbc:mysql://localhost:3306/litemall?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC&verifyServerCertificate=false&useSSL=false +spring.datasource.druid.driver-class-name=com.mysql.jdbc.Driver +spring.datasource.druid.username=litemall +spring.datasource.druid.password=litemall123456 +spring.datasource.druid.initial-size=50 +spring.datasource.druid.max-active=100 +spring.datasource.druid.min-idle=20 +spring.datasource.druid.max-wait=60000 +spring.datasource.druid.pool-prepared-statements=true +spring.datasource.druid.max-pool-prepared-statement-per-connection-size=20 +spring.datasource.druid.validation-query=SELECT 1 FROM DUAL +spring.datasource.druid.test-on-borrow=false +spring.datasource.druid.test-on-return=false +spring.datasource.druid.test-while-idle=true +spring.datasource.druid.time-between-eviction-runs-millis=60000 +spring.datasource.druid.filters=stat,wall,log4j + + +logging.level.root=ERROR +logging.level.org.springframework=ERROR +logging.level.org.mybatis=ERROR +logging.level.org.linlinjava.litemall.db=ERROR +logging.level.org.linlinjava.litemall=DEBUG \ No newline at end of file diff --git a/litemall-admin-api/src/main/resources/application-prod.properties b/litemall-admin-api/src/main/resources/application-prod.properties new file mode 100644 index 00000000..0a09fe15 --- /dev/null +++ b/litemall-admin-api/src/main/resources/application-prod.properties @@ -0,0 +1,27 @@ +pagehelper.helperDialect=mysql +pagehelper.reasonable=true +pagehelper.supportMethodsArguments=true +pagehelper.params=count=countSql + +spring.datasource.druid.url=jdbc:mysql://localhost:3306/litemall?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC&verifyServerCertificate=false&useSSL=false +spring.datasource.druid.driver-class-name=com.mysql.jdbc.Driver +spring.datasource.druid.username=litemall +spring.datasource.druid.password=litemall123456 +spring.datasource.druid.initial-size=50 +spring.datasource.druid.max-active=100 +spring.datasource.druid.min-idle=20 +spring.datasource.druid.max-wait=60000 +spring.datasource.druid.pool-prepared-statements=true +spring.datasource.druid.max-pool-prepared-statement-per-connection-size=20 +spring.datasource.druid.validation-query=SELECT 1 FROM DUAL +spring.datasource.druid.test-on-borrow=false +spring.datasource.druid.test-on-return=false +spring.datasource.druid.test-while-idle=true +spring.datasource.druid.time-between-eviction-runs-millis=60000 +spring.datasource.druid.filters=stat,wall,log4j + +logging.level.root=ERROR +logging.level.org.springframework=ERROR +logging.level.org.mybatis=ERROR +logging.level.org.linlinjava.litemall.db=ERROR +logging.level.org.linlinjava.litemall=DEBUG \ No newline at end of file diff --git a/litemall-admin-api/src/main/resources/application.properties b/litemall-admin-api/src/main/resources/application.properties new file mode 100644 index 00000000..64b63792 --- /dev/null +++ b/litemall-admin-api/src/main/resources/application.properties @@ -0,0 +1,3 @@ +spring.profiles.active=dev +server.port=8083 +logging.level.org.linlinjava.litemall.admin.Application=DEBUG diff --git a/litemall-admin/.babelrc b/litemall-admin/.babelrc new file mode 100644 index 00000000..3a280ba3 --- /dev/null +++ b/litemall-admin/.babelrc @@ -0,0 +1,12 @@ +{ + "presets": [ + ["env", { + "modules": false, + "targets": { + "browsers": ["> 1%", "last 2 versions", "not ie <= 8"] + } + }], + "stage-2" + ], + "plugins": ["transform-vue-jsx", "transform-runtime"] +} diff --git a/litemall-admin/.editorconfig b/litemall-admin/.editorconfig new file mode 100644 index 00000000..ea6e20f5 --- /dev/null +++ b/litemall-admin/.editorconfig @@ -0,0 +1,14 @@ +# http://editorconfig.org +root = true + +[*] +charset = utf-8 +indent_style = space +indent_size = 2 +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true + +[*.md] +insert_final_newline = false +trim_trailing_whitespace = false diff --git a/litemall-admin/.eslintignore b/litemall-admin/.eslintignore new file mode 100644 index 00000000..e3a4037e --- /dev/null +++ b/litemall-admin/.eslintignore @@ -0,0 +1,3 @@ +build/*.js +config/*.js +src/assets diff --git a/litemall-admin/.eslintrc.js b/litemall-admin/.eslintrc.js new file mode 100644 index 00000000..00d60805 --- /dev/null +++ b/litemall-admin/.eslintrc.js @@ -0,0 +1,199 @@ +module.exports = { + root: true, + parser: 'babel-eslint', + parserOptions: { + sourceType: 'module' + }, + env: { + browser: true, + node: true, + es6: true, + }, + extends: 'eslint:recommended', + // required to lint *.vue files + plugins: [ + 'html' + ], + // check if imports actually resolve + 'settings': { + 'import/resolver': { + 'webpack': { + 'config': 'build/webpack.base.conf.js' + } + } + }, + // add your custom rules here + //it is base on https://github.com/vuejs/eslint-config-vue + 'rules': { + 'accessor-pairs': 2, + 'arrow-spacing': [2, { + 'before': true, + 'after': true + }], + 'block-spacing': [2, 'always'], + 'brace-style': [2, '1tbs', { + 'allowSingleLine': true + }], + 'camelcase': [0, { + 'properties': 'always' + }], + 'comma-dangle': [2, 'never'], + 'comma-spacing': [2, { + 'before': false, + 'after': true + }], + 'comma-style': [2, 'last'], + 'constructor-super': 2, + 'curly': [2, 'multi-line'], + 'dot-location': [2, 'property'], + 'eol-last': 2, + 'eqeqeq': [2, 'allow-null'], + 'generator-star-spacing': [2, { + 'before': true, + 'after': true + }], + 'handle-callback-err': [2, '^(err|error)$'], + 'indent': [2, 2, { + 'SwitchCase': 1 + }], + 'jsx-quotes': [2, 'prefer-single'], + 'key-spacing': [2, { + 'beforeColon': false, + 'afterColon': true + }], + 'keyword-spacing': [2, { + 'before': true, + 'after': true + }], + 'new-cap': [2, { + 'newIsCap': true, + 'capIsNew': false + }], + 'new-parens': 2, + 'no-array-constructor': 2, + 'no-caller': 2, + 'no-console': 'off', + 'no-class-assign': 2, + 'no-cond-assign': 2, + 'no-const-assign': 2, + 'no-control-regex': 0, + 'no-delete-var': 2, + 'no-dupe-args': 2, + 'no-dupe-class-members': 2, + 'no-dupe-keys': 2, + 'no-duplicate-case': 2, + 'no-empty-character-class': 2, + 'no-empty-pattern': 2, + 'no-eval': 2, + 'no-ex-assign': 2, + 'no-extend-native': 2, + 'no-extra-bind': 2, + 'no-extra-boolean-cast': 2, + 'no-extra-parens': [2, 'functions'], + 'no-fallthrough': 2, + 'no-floating-decimal': 2, + 'no-func-assign': 2, + 'no-implied-eval': 2, + 'no-inner-declarations': [2, 'functions'], + 'no-invalid-regexp': 2, + 'no-irregular-whitespace': 2, + 'no-iterator': 2, + 'no-label-var': 2, + 'no-labels': [2, { + 'allowLoop': false, + 'allowSwitch': false + }], + 'no-lone-blocks': 2, + 'no-mixed-spaces-and-tabs': 2, + 'no-multi-spaces': 2, + 'no-multi-str': 2, + 'no-multiple-empty-lines': [2, { + 'max': 1 + }], + 'no-native-reassign': 2, + 'no-negated-in-lhs': 2, + 'no-new-object': 2, + 'no-new-require': 2, + 'no-new-symbol': 2, + 'no-new-wrappers': 2, + 'no-obj-calls': 2, + 'no-octal': 2, + 'no-octal-escape': 2, + 'no-path-concat': 2, + 'no-proto': 2, + 'no-redeclare': 2, + 'no-regex-spaces': 2, + 'no-return-assign': [2, 'except-parens'], + 'no-self-assign': 2, + 'no-self-compare': 2, + 'no-sequences': 2, + 'no-shadow-restricted-names': 2, + 'no-spaced-func': 2, + 'no-sparse-arrays': 2, + 'no-this-before-super': 2, + 'no-throw-literal': 2, + 'no-trailing-spaces': 2, + 'no-undef': 2, + 'no-undef-init': 2, + 'no-unexpected-multiline': 2, + 'no-unmodified-loop-condition': 2, + 'no-unneeded-ternary': [2, { + 'defaultAssignment': false + }], + 'no-unreachable': 2, + 'no-unsafe-finally': 2, + 'no-unused-vars': [2, { + 'vars': 'all', + 'args': 'none' + }], + 'no-useless-call': 2, + 'no-useless-computed-key': 2, + 'no-useless-constructor': 2, + 'no-useless-escape': 0, + 'no-whitespace-before-property': 2, + 'no-with': 2, + 'one-var': [2, { + 'initialized': 'never' + }], + 'operator-linebreak': [2, 'after', { + 'overrides': { + '?': 'before', + ':': 'before' + } + }], + 'padded-blocks': [2, 'never'], + 'quotes': [2, 'single', { + 'avoidEscape': true, + 'allowTemplateLiterals': true + }], + 'semi': [2, 'never'], + 'semi-spacing': [2, { + 'before': false, + 'after': true + }], + 'space-before-blocks': [2, 'always'], + 'space-before-function-paren': [2, 'never'], + 'space-in-parens': [2, 'never'], + 'space-infix-ops': 2, + 'space-unary-ops': [2, { + 'words': true, + 'nonwords': false + }], + 'spaced-comment': [2, 'always', { + 'markers': ['global', 'globals', 'eslint', 'eslint-disable', '*package', '!', ','] + }], + 'template-curly-spacing': [2, 'never'], + 'use-isnan': 2, + 'valid-typeof': 2, + 'wrap-iife': [2, 'any'], + 'yield-star-spacing': [2, 'both'], + 'yoda': [2, 'never'], + 'prefer-const': 2, + 'no-debugger': process.env.NODE_ENV === 'production' ? 2 : 0, + 'object-curly-spacing': [2, 'always', { + objectsInObjects: false + }], + 'array-bracket-spacing': [2, 'never'] + } +} + diff --git a/litemall-admin/.gitignore b/litemall-admin/.gitignore new file mode 100644 index 00000000..01ea4565 --- /dev/null +++ b/litemall-admin/.gitignore @@ -0,0 +1,19 @@ +.DS_Store +node_modules/ +dist/ +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +test/unit/coverage +test/e2e/reports +selenium-debug.log + +# Editor directories and files +.idea +.vscode +*.suo +*.ntvs* +*.njsproj +*.sln + diff --git a/litemall-admin/.postcssrc.js b/litemall-admin/.postcssrc.js new file mode 100644 index 00000000..eee3e92d --- /dev/null +++ b/litemall-admin/.postcssrc.js @@ -0,0 +1,10 @@ +// https://github.com/michael-ciniawsky/postcss-load-config + +module.exports = { + "plugins": { + "postcss-import": {}, + "postcss-url": {}, + // to edit target browsers: use "browserslist" field in package.json + "autoprefixer": {} + } +} diff --git a/litemall-admin/.travis.yml b/litemall-admin/.travis.yml new file mode 100644 index 00000000..16574d97 --- /dev/null +++ b/litemall-admin/.travis.yml @@ -0,0 +1,5 @@ +language: node_js +node_js: stable +script: npm run test +notifications: + email: false diff --git a/litemall-admin/build/build.js b/litemall-admin/build/build.js new file mode 100644 index 00000000..fc793972 --- /dev/null +++ b/litemall-admin/build/build.js @@ -0,0 +1,48 @@ +'use strict' +require('./check-versions')() + +const ora = require('ora') +const rm = require('rimraf') +const path = require('path') +const chalk = require('chalk') +const webpack = require('webpack') +const config = require('../config') +const webpackConfig = require('./webpack.prod.conf') +const server = require('pushstate-server') + +var spinner = ora('building for '+ process.env.env_config+ ' environment...' ) +spinner.start() + +rm(path.join(config.build.assetsRoot, config.build.assetsSubDirectory), err => { + if (err) throw err + webpack(webpackConfig, (err, stats) => { + spinner.stop() + if (err) throw err + process.stdout.write(stats.toString({ + colors: true, + modules: false, + children: false, + chunks: false, + chunkModules: false + }) + '\n\n') + + if (stats.hasErrors()) { + console.log(chalk.red(' Build failed with errors.\n')) + process.exit(1) + } + + console.log(chalk.cyan(' Build complete.\n')) + console.log(chalk.yellow( + ' Tip: built files are meant to be served over an HTTP server.\n' + + ' Opening index.html over file:// won\'t work.\n' + )) + if(process.env.npm_config_preview){ + server.start({ + port: 9526, + directory: './dist', + file: '/index.html' + }); + console.log('> Listening at ' + 'http://localhost:9526' + '\n') + } + }) +}) diff --git a/litemall-admin/build/check-versions.js b/litemall-admin/build/check-versions.js new file mode 100644 index 00000000..3ef972a0 --- /dev/null +++ b/litemall-admin/build/check-versions.js @@ -0,0 +1,54 @@ +'use strict' +const chalk = require('chalk') +const semver = require('semver') +const packageConfig = require('../package.json') +const shell = require('shelljs') + +function exec (cmd) { + return require('child_process').execSync(cmd).toString().trim() +} + +const versionRequirements = [ + { + name: 'node', + currentVersion: semver.clean(process.version), + versionRequirement: packageConfig.engines.node + } +] + +if (shell.which('npm')) { + versionRequirements.push({ + name: 'npm', + currentVersion: exec('npm --version'), + versionRequirement: packageConfig.engines.npm + }) +} + +module.exports = function () { + const warnings = [] + + for (let i = 0; i < versionRequirements.length; i++) { + const mod = versionRequirements[i] + + if (!semver.satisfies(mod.currentVersion, mod.versionRequirement)) { + warnings.push(mod.name + ': ' + + chalk.red(mod.currentVersion) + ' should be ' + + chalk.green(mod.versionRequirement) + ) + } + } + + if (warnings.length) { + console.log('') + console.log(chalk.yellow('To use this template, you must update following to modules:')) + console.log() + + for (let i = 0; i < warnings.length; i++) { + const warning = warnings[i] + console.log(' ' + warning) + } + + console.log() + process.exit(1) + } +} diff --git a/litemall-admin/build/logo.png b/litemall-admin/build/logo.png new file mode 100644 index 00000000..f3d2503f Binary files /dev/null and b/litemall-admin/build/logo.png differ diff --git a/litemall-admin/build/utils.js b/litemall-admin/build/utils.js new file mode 100644 index 00000000..e534fb0f --- /dev/null +++ b/litemall-admin/build/utils.js @@ -0,0 +1,101 @@ +'use strict' +const path = require('path') +const config = require('../config') +const ExtractTextPlugin = require('extract-text-webpack-plugin') +const packageConfig = require('../package.json') + +exports.assetsPath = function (_path) { + const assetsSubDirectory = process.env.NODE_ENV === 'production' + ? config.build.assetsSubDirectory + : config.dev.assetsSubDirectory + + return path.posix.join(assetsSubDirectory, _path) +} + +exports.cssLoaders = function (options) { + options = options || {} + + const cssLoader = { + loader: 'css-loader', + options: { + sourceMap: options.sourceMap + } + } + + const postcssLoader = { + loader: 'postcss-loader', + options: { + sourceMap: options.sourceMap + } + } + + // generate loader string to be used with extract text plugin + function generateLoaders (loader, loaderOptions) { + const loaders = options.usePostCSS ? [cssLoader, postcssLoader] : [cssLoader] + + if (loader) { + loaders.push({ + loader: loader + '-loader', + options: Object.assign({}, loaderOptions, { + sourceMap: options.sourceMap + }) + }) + } + + // Extract CSS when that option is specified + // (which is the case during production build) + if (options.extract) { + return ExtractTextPlugin.extract({ + use: loaders, + fallback: 'vue-style-loader' + }) + } else { + return ['vue-style-loader'].concat(loaders) + } + } + + // https://vue-loader.vuejs.org/en/configurations/extract-css.html + return { + css: generateLoaders(), + postcss: generateLoaders(), + less: generateLoaders('less'), + sass: generateLoaders('sass', { indentedSyntax: true }), + scss: generateLoaders('sass'), + stylus: generateLoaders('stylus'), + styl: generateLoaders('stylus') + } +} + +// Generate loaders for standalone style files (outside of .vue) +exports.styleLoaders = function (options) { + const output = [] + const loaders = exports.cssLoaders(options) + + for (const extension in loaders) { + const loader = loaders[extension] + output.push({ + test: new RegExp('\\.' + extension + '$'), + use: loader + }) + } + + return output +} + +exports.createNotifierCallback = () => { + const notifier = require('node-notifier') + + return (severity, errors) => { + if (severity !== 'error') return + + const error = errors[0] + const filename = error.file && error.file.split('!').pop() + + notifier.notify({ + title: packageConfig.name, + message: severity + ': ' + error.name, + subtitle: filename || '', + icon: path.join(__dirname, 'logo.png') + }) + } +} diff --git a/litemall-admin/build/vue-loader.conf.js b/litemall-admin/build/vue-loader.conf.js new file mode 100644 index 00000000..33ed58bc --- /dev/null +++ b/litemall-admin/build/vue-loader.conf.js @@ -0,0 +1,22 @@ +'use strict' +const utils = require('./utils') +const config = require('../config') +const isProduction = process.env.NODE_ENV === 'production' +const sourceMapEnabled = isProduction + ? config.build.productionSourceMap + : config.dev.cssSourceMap + +module.exports = { + loaders: utils.cssLoaders({ + sourceMap: sourceMapEnabled, + extract: isProduction + }), + cssSourceMap: sourceMapEnabled, + cacheBusting: config.dev.cacheBusting, + transformToRequire: { + video: ['src', 'poster'], + source: 'src', + img: 'src', + image: 'xlink:href' + } +} diff --git a/litemall-admin/build/webpack.base.conf.js b/litemall-admin/build/webpack.base.conf.js new file mode 100644 index 00000000..97bc6e2b --- /dev/null +++ b/litemall-admin/build/webpack.base.conf.js @@ -0,0 +1,101 @@ +'use strict' +const path = require('path') +const utils = require('./utils') +const config = require('../config') +const vueLoaderConfig = require('./vue-loader.conf') + +function resolve (dir) { + return path.join(__dirname, '..', dir) +} + +const createLintingRule = () => ({ + test: /\.(js|vue)$/, + loader: 'eslint-loader', + enforce: 'pre', + include: [resolve('src'), resolve('test')], + options: { + formatter: require('eslint-friendly-formatter'), + emitWarning: !config.dev.showEslintErrorsInOverlay + } +}) + +module.exports = { + context: path.resolve(__dirname, '../'), + entry: { + app: './src/main.js' + }, + output: { + path: config.build.assetsRoot, + filename: '[name].js', + publicPath: process.env.NODE_ENV === 'production' + ? config.build.assetsPublicPath + : config.dev.assetsPublicPath + }, + resolve: { + extensions: ['.js', '.vue', '.json'], + alias: { + 'vue$': 'vue/dist/vue.esm.js', + '@': resolve('src'), + } + }, + module: { + rules: [ + ...(config.dev.useEslint ? [createLintingRule()] : []), + { + test: /\.vue$/, + loader: 'vue-loader', + options: vueLoaderConfig + }, + { + test: /\.js$/, + loader: 'babel-loader?cacheDirectory', + include: [resolve('src'), resolve('test'), resolve('node_modules/webpack-dev-server/client')] + }, + { + test: /\.svg$/, + loader: 'svg-sprite-loader', + include: [resolve('src/icons')], + options: { + symbolId: 'icon-[name]' + } + }, + { + test: /\.(png|jpe?g|gif|svg)(\?.*)?$/, + loader: 'url-loader', + exclude: [resolve('src/icons')], + options: { + limit: 10000, + name: utils.assetsPath('img/[name].[hash:7].[ext]') + } + }, + { + test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/, + loader: 'url-loader', + options: { + limit: 10000, + name: utils.assetsPath('media/[name].[hash:7].[ext]') + } + }, + { + test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/, + loader: 'url-loader', + options: { + limit: 10000, + name: utils.assetsPath('fonts/[name].[hash:7].[ext]') + } + } + ] + }, + node: { + // prevent webpack from injecting useless setImmediate polyfill because Vue + // source contains it (although only uses it if it's native). + setImmediate: false, + // prevent webpack from injecting mocks to Node native modules + // that does not make sense for the client + dgram: 'empty', + fs: 'empty', + net: 'empty', + tls: 'empty', + child_process: 'empty' + } +} diff --git a/litemall-admin/build/webpack.dev.conf.js b/litemall-admin/build/webpack.dev.conf.js new file mode 100644 index 00000000..56d7ecdf --- /dev/null +++ b/litemall-admin/build/webpack.dev.conf.js @@ -0,0 +1,88 @@ +'use strict' +const path = require('path') +const utils = require('./utils') +const webpack = require('webpack') +const config = require('../config') +const merge = require('webpack-merge') +const baseWebpackConfig = require('./webpack.base.conf') +const HtmlWebpackPlugin = require('html-webpack-plugin') +const FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin') +const portfinder = require('portfinder') + +function resolve (dir) { + return path.join(__dirname, '..', dir) +} + +const HOST = process.env.HOST +const PORT = process.env.PORT && Number(process.env.PORT) + +const devWebpackConfig = merge(baseWebpackConfig, { + module: { + rules: utils.styleLoaders({ sourceMap: config.dev.cssSourceMap, usePostCSS: true }) + }, + // cheap-module-eval-source-map is faster for development + devtool: config.dev.devtool, + + // these devServer options should be customized in /config/index.js + devServer: { + clientLogLevel: 'warning', + historyApiFallback: true, + hot: true, + compress: true, + host: HOST || config.dev.host, + port: PORT || config.dev.port, + open: config.dev.autoOpenBrowser, + overlay: config.dev.errorOverlay + ? { warnings: false, errors: true } + : false, + publicPath: config.dev.assetsPublicPath, + proxy: config.dev.proxyTable, + quiet: true, // necessary for FriendlyErrorsPlugin + watchOptions: { + poll: config.dev.poll, + } + }, + plugins: [ + new webpack.DefinePlugin({ + 'process.env': require('../config/dev.env') + }), + new webpack.HotModuleReplacementPlugin(), + new webpack.NamedModulesPlugin(), // HMR shows correct file names in console on update. + new webpack.NoEmitOnErrorsPlugin(), + // https://github.com/ampedandwired/html-webpack-plugin + new HtmlWebpackPlugin({ + filename: 'index.html', + template: 'index.html', + inject: true, + favicon: resolve('favicon.ico'), + title: 'zmall-admin', + path: config.dev.assetsPublicPath + config.dev.assetsSubDirectory + }), + ] +}) + +module.exports = new Promise((resolve, reject) => { + portfinder.basePort = process.env.PORT || config.dev.port + portfinder.getPort((err, port) => { + if (err) { + reject(err) + } else { + // publish the new Port, necessary for e2e tests + process.env.PORT = port + // add port to devServer config + devWebpackConfig.devServer.port = port + + // Add FriendlyErrorsPlugin + devWebpackConfig.plugins.push(new FriendlyErrorsPlugin({ + compilationSuccessInfo: { + messages: [`Your application is running here: http://${devWebpackConfig.devServer.host}:${port}`], + }, + onErrors: config.dev.notifyOnErrors + ? utils.createNotifierCallback() + : undefined + })) + + resolve(devWebpackConfig) + } + }) +}) diff --git a/litemall-admin/build/webpack.prod.conf.js b/litemall-admin/build/webpack.prod.conf.js new file mode 100644 index 00000000..7f710543 --- /dev/null +++ b/litemall-admin/build/webpack.prod.conf.js @@ -0,0 +1,175 @@ +'use strict' +const path = require('path') +const utils = require('./utils') +const webpack = require('webpack') +const config = require('../config') +const merge = require('webpack-merge') +const baseWebpackConfig = require('./webpack.base.conf') +const CopyWebpackPlugin = require('copy-webpack-plugin') +const HtmlWebpackPlugin = require('html-webpack-plugin') +const ExtractTextPlugin = require('extract-text-webpack-plugin') +const OptimizeCSSPlugin = require('optimize-css-assets-webpack-plugin') +const UglifyJsPlugin = require('uglifyjs-webpack-plugin') + +function resolve (dir) { + return path.join(__dirname, '..', dir) +} + +const env = require('../config/'+process.env.env_config+'.env') + +const webpackConfig = merge(baseWebpackConfig, { + module: { + rules: utils.styleLoaders({ + sourceMap: config.build.productionSourceMap, + extract: true, + usePostCSS: true + }) + }, + devtool: config.build.productionSourceMap ? config.build.devtool : false, + output: { + path: config.build.assetsRoot, + filename: utils.assetsPath('js/[name].[chunkhash].js'), + chunkFilename: utils.assetsPath('js/[id].[chunkhash].js') + }, + plugins: [ + // http://vuejs.github.io/vue-loader/en/workflow/production.html + new webpack.DefinePlugin({ + 'process.env': env + }), + new UglifyJsPlugin({ + uglifyOptions: { + compress: { + warnings: false + } + }, + sourceMap: config.build.productionSourceMap, + parallel: true + }), + // extract css into its own file + new ExtractTextPlugin({ + filename: utils.assetsPath('css/[name].[contenthash].css'), + // Setting the following option to `false` will not extract CSS from codesplit chunks. + // Their CSS will instead be inserted dynamically with style-loader when the codesplit chunk has been loaded by webpack. + // increasing file size: https://github.com/vuejs-templates/webpack/issues/1110 + allChunks: false, + }), + // Compress extracted CSS. We are using this plugin so that possible + // duplicated CSS from different components can be deduped. + new OptimizeCSSPlugin({ + cssProcessorOptions: config.build.productionSourceMap + ? { safe: true, map: { inline: false } } + : { safe: true } + }), + // generate dist index.html with correct asset hash for caching. + // you can customize output by editing /index.html + // see https://github.com/ampedandwired/html-webpack-plugin + new HtmlWebpackPlugin({ + filename: config.build.index, + template: 'index.html', + inject: true, + favicon: resolve('favicon.ico'), + title: 'vue-element-admin', + path: config.build.assetsPublicPath + config.build.assetsSubDirectory, + minify: { + removeComments: true, + collapseWhitespace: true, + removeAttributeQuotes: true + // more options: + // https://github.com/kangax/html-minifier#options-quick-reference + }, + // necessary to consistently work with multiple chunks via CommonsChunkPlugin + chunksSortMode: 'dependency' + }), + // keep module.id stable when vender modules does not change + new webpack.HashedModuleIdsPlugin(), + // enable scope hoisting + new webpack.optimize.ModuleConcatenationPlugin(), + // split vendor js into its own file + new webpack.optimize.CommonsChunkPlugin({ + name: 'vendor', + minChunks (module) { + // any required modules inside node_modules are extracted to vendor + return ( + module.resource && + /\.js$/.test(module.resource) && + module.resource.indexOf( + path.join(__dirname, '../node_modules') + ) === 0 + ) + } + }), + // extract webpack runtime and module manifest to its own file in order to + // prevent vendor hash from being updated whenever app bundle is updated + new webpack.optimize.CommonsChunkPlugin({ + name: 'manifest', + minChunks: Infinity + }), + // This instance extracts shared chunks from code splitted chunks and bundles them + // in a separate chunk, similar to the vendor chunk + // see: https://webpack.js.org/plugins/commons-chunk-plugin/#extra-async-commons-chunk + new webpack.optimize.CommonsChunkPlugin({ + name: 'app', + async: 'vendor-async', + children: true, + minChunks: 3 + }), + // split echarts into its own file + new webpack.optimize.CommonsChunkPlugin({ + async: 'echarts', + minChunks(module) { + var context = module.context; + return context && (context.indexOf('echarts') >= 0 || context.indexOf('zrender') >= 0); + } + }), + // split xlsx into its own file + new webpack.optimize.CommonsChunkPlugin({ + async: 'xlsx', + minChunks(module) { + var context = module.context; + return context && (context.indexOf('xlsx') >= 0); + } + }), + // split codemirror into its own file + new webpack.optimize.CommonsChunkPlugin({ + async: 'codemirror', + minChunks(module) { + var context = module.context; + return context && (context.indexOf('codemirror') >= 0); + } + }), + + // copy custom static assets + new CopyWebpackPlugin([ + { + from: path.resolve(__dirname, '../static'), + to: config.build.assetsSubDirectory, + ignore: ['.*'] + } + ]) + ] +}) + +if (config.build.productionGzip) { + const CompressionWebpackPlugin = require('compression-webpack-plugin') + + webpackConfig.plugins.push( + new CompressionWebpackPlugin({ + asset: '[path].gz[query]', + algorithm: 'gzip', + test: new RegExp( + '\\.(' + + config.build.productionGzipExtensions.join('|') + + ')$' + ), + threshold: 10240, + minRatio: 0.8 + }) + ) +} + +if (config.build.bundleAnalyzerReport) { + const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin + webpackConfig.plugins.push(new BundleAnalyzerPlugin()) +} + +module.exports = webpackConfig diff --git a/litemall-admin/config/dev.env.js b/litemall-admin/config/dev.env.js new file mode 100644 index 00000000..7962dcc3 --- /dev/null +++ b/litemall-admin/config/dev.env.js @@ -0,0 +1,6 @@ +module.exports = { + NODE_ENV: '"development"', + ENV_CONFIG: '"dev"', + BASE_API: '"http://localhost:8083/admin"', + OS_API: '"http://localhost:8081/storage"' +} diff --git a/litemall-admin/config/index.js b/litemall-admin/config/index.js new file mode 100644 index 00000000..92ac0ed1 --- /dev/null +++ b/litemall-admin/config/index.js @@ -0,0 +1,83 @@ +'use strict' +// Template version: 1.2.6 +// see http://vuejs-templates.github.io/webpack for documentation. + +const path = require('path') + +module.exports = { + dev: { + + // Paths + assetsSubDirectory: 'static', + assetsPublicPath: '/', + proxyTable: {}, + + // Various Dev Server settings + host: 'localhost', // can be overwritten by process.env.HOST + port: 9527, // can be overwritten by process.env.PORT, if port is in use, a free one will be determined + autoOpenBrowser: true, + errorOverlay: true, + notifyOnErrors: false, + poll: false, // https://webpack.js.org/configuration/dev-server/#devserver-watchoptions- + + // Use Eslint Loader? + // If true, your code will be linted during bundling and + // linting errors and warnings will be shown in the console. + useEslint: true, + // If true, eslint errors and warnings will also be shown in the error overlay + // in the browser. + showEslintErrorsInOverlay: false, + + /** + * Source Maps + */ + + // https://webpack.js.org/configuration/devtool/#development + devtool: '#cheap-source-map', + + // If you have problems debugging vue-files in devtools, + // set this to false - it *may* help + // https://vue-loader.vuejs.org/en/options.html#cachebusting + cacheBusting: true, + + // CSS Sourcemaps off by default because relative paths are "buggy" + // with this option, according to the CSS-Loader README + // (https://github.com/webpack/css-loader#sourcemaps) + // In our experience, they generally work as expected, + // just be aware of this issue when enabling this option. + cssSourceMap: false, + }, + + build: { + // Template for index.html + index: path.resolve(__dirname, '../dist/index.html'), + + // Paths + assetsRoot: path.resolve(__dirname, '../dist'), + assetsSubDirectory: 'static', + + // you can set by youself according to actual condition + assetsPublicPath: './', + + /** + * Source Maps + */ + + productionSourceMap: false, + // https://webpack.js.org/configuration/devtool/#production + devtool: '#source-map', + + // Gzip off by default as many popular static hosts such as + // Surge or Netlify already gzip all static assets for you. + // Before setting to `true`, make sure to: + // npm install --save-dev compression-webpack-plugin + productionGzip: false, + productionGzipExtensions: ['js', 'css'], + + // Run the build command with an extra argument to + // View the bundle analyzer report after build finishes: + // `npm run build --report` + // Set to `true` or `false` to always turn it on or off + bundleAnalyzerReport: process.env.npm_config_report + } +} diff --git a/litemall-admin/config/prod.env.js b/litemall-admin/config/prod.env.js new file mode 100644 index 00000000..adfe9111 --- /dev/null +++ b/litemall-admin/config/prod.env.js @@ -0,0 +1,6 @@ +module.exports = { + NODE_ENV: '"production"', + ENV_CONFIG: '"prod"', + BASE_API: '"http://122.152.206.172:8083/admin"', + OS_API: '"http://122.152.206.172:8081/storage"' +} diff --git a/litemall-admin/favicon.ico b/litemall-admin/favicon.ico new file mode 100644 index 00000000..7fc0eab3 Binary files /dev/null and b/litemall-admin/favicon.ico differ diff --git a/litemall-admin/index.html b/litemall-admin/index.html new file mode 100644 index 00000000..a5d77ea8 --- /dev/null +++ b/litemall-admin/index.html @@ -0,0 +1,15 @@ + + + + + + + + zmall-admin + + + +
+ + + diff --git a/litemall-admin/package.json b/litemall-admin/package.json new file mode 100644 index 00000000..ca4a9139 --- /dev/null +++ b/litemall-admin/package.json @@ -0,0 +1,90 @@ +{ + "name": "litemall-admin", + "version": "0.1.0", + "description": "litemall-admin basing on vue-element-admin 3.6.2", + "author": "linlinjava ", + "license": "MIT", + "private": true, + "scripts": { + "dev": "webpack-dev-server --inline --progress --config build/webpack.dev.conf.js", + "build:prod": "cross-env NODE_ENV=production env_config=prod node build/build.js", + "lint": "eslint --ext .js,.vue src", + "test": "npm run lint" + }, + "dependencies": { + "axios": "0.17.1", + "clipboard": "1.7.1", + "echarts": "3.8.5", + "element-ui": "2.0.8", + "file-saver": "1.3.3", + "font-awesome": "4.7.0", + "js-cookie": "2.2.0", + "mockjs": "1.0.1-beta3", + "normalize.css": "7.0.0", + "nprogress": "0.2.0", + "screenfull": "3.3.2", + "vue": "2.5.10", + "vue-count-to": "1.0.13", + "vue-router": "3.0.1", + "vue-splitpane": "1.0.2", + "vuex": "3.0.1", + "xlsx": "^0.11.16" + }, + "devDependencies": { + "autoprefixer": "7.2.3", + "babel-core": "6.26.0", + "babel-eslint": "8.0.3", + "babel-helper-vue-jsx-merge-props": "2.0.3", + "babel-loader": "7.1.2", + "babel-plugin-syntax-jsx": "6.18.0", + "babel-plugin-transform-runtime": "6.23.0", + "babel-plugin-transform-vue-jsx": "3.5.0", + "babel-preset-env": "1.6.1", + "babel-preset-stage-2": "6.24.1", + "chalk": "2.3.0", + "copy-webpack-plugin": "4.3.0", + "cross-env": "5.1.1", + "css-loader": "0.28.7", + "eslint": "4.13.1", + "eslint-friendly-formatter": "3.0.0", + "eslint-loader": "1.9.0", + "eslint-plugin-html": "4.0.1", + "extract-text-webpack-plugin": "3.0.2", + "file-loader": "1.1.5", + "friendly-errors-webpack-plugin": "1.6.1", + "html-webpack-plugin": "2.30.1", + "node-notifier": "5.1.2", + "node-sass": "^4.7.2", + "optimize-css-assets-webpack-plugin": "3.2.0", + "ora": "1.3.0", + "portfinder": "1.0.13", + "postcss-import": "11.0.0", + "postcss-loader": "2.0.9", + "postcss-url": "7.3.0", + "pushstate-server": "3.0.1", + "rimraf": "2.6.2", + "sass-loader": "6.0.6", + "script-loader": "0.7.2", + "semver": "5.4.1", + "shelljs": "0.7.8", + "svg-sprite-loader": "3.5.2", + "uglifyjs-webpack-plugin": "1.1.3", + "url-loader": "0.6.2", + "vue-loader": "13.5.0", + "vue-style-loader": "3.0.3", + "vue-template-compiler": "2.5.10", + "webpack": "3.10.0", + "webpack-bundle-analyzer": "2.9.1", + "webpack-dev-server": "2.9.7", + "webpack-merge": "4.1.1" + }, + "engines": { + "node": ">= 4.0.0", + "npm": ">= 3.0.0" + }, + "browserslist": [ + "> 1%", + "last 2 versions", + "not ie <= 8" + ] +} diff --git a/litemall-admin/src/App.vue b/litemall-admin/src/App.vue new file mode 100644 index 00000000..b39696e2 --- /dev/null +++ b/litemall-admin/src/App.vue @@ -0,0 +1,11 @@ + + + diff --git a/litemall-admin/src/api/ad.js b/litemall-admin/src/api/ad.js new file mode 100644 index 00000000..f556ffc7 --- /dev/null +++ b/litemall-admin/src/api/ad.js @@ -0,0 +1,41 @@ +import request from '@/utils/request' + +export function listAd(query) { + return request({ + url: '/ad/list', + method: 'get', + params: query + }) +} + +export function createAd(data) { + return request({ + url: '/ad/create', + method: 'post', + data + }) +} + +export function readAd(data) { + return request({ + url: '/ad/read', + method: 'get', + data + }) +} + +export function updateAd(data) { + return request({ + url: '/ad/update', + method: 'post', + data + }) +} + +export function deleteAd(data) { + return request({ + url: '/ad/delete', + method: 'post', + data + }) +} diff --git a/litemall-admin/src/api/address.js b/litemall-admin/src/api/address.js new file mode 100644 index 00000000..2056434e --- /dev/null +++ b/litemall-admin/src/api/address.js @@ -0,0 +1,41 @@ +import request from '@/utils/request' + +export function listAddress(query) { + return request({ + url: '/address/list', + method: 'get', + params: query + }) +} + +export function createAddress(data) { + return request({ + url: '/address/create', + method: 'post', + data + }) +} + +export function readAddress(data) { + return request({ + url: '/address/read', + method: 'get', + data + }) +} + +export function updateAddress(data) { + return request({ + url: '/address/update', + method: 'post', + data + }) +} + +export function deleteAddress(data) { + return request({ + url: '/address/delete', + method: 'post', + data + }) +} diff --git a/litemall-admin/src/api/admin.js b/litemall-admin/src/api/admin.js new file mode 100644 index 00000000..e7db202a --- /dev/null +++ b/litemall-admin/src/api/admin.js @@ -0,0 +1,41 @@ +import request from '@/utils/request' + +export function listAdmin(query) { + return request({ + url: '/admin/list', + method: 'get', + params: query + }) +} + +export function createAdmin(data) { + return request({ + url: '/admin/create', + method: 'post', + data + }) +} + +export function readminAdmin(data) { + return request({ + url: '/admin/readmin', + method: 'get', + data + }) +} + +export function updateAdmin(data) { + return request({ + url: '/admin/update', + method: 'post', + data + }) +} + +export function deleteAdmin(data) { + return request({ + url: '/admin/delete', + method: 'post', + data + }) +} diff --git a/litemall-admin/src/api/brand.js b/litemall-admin/src/api/brand.js new file mode 100644 index 00000000..612d3404 --- /dev/null +++ b/litemall-admin/src/api/brand.js @@ -0,0 +1,41 @@ +import request from '@/utils/request' + +export function listBrand(query) { + return request({ + url: '/brand/list', + method: 'get', + params: query + }) +} + +export function createBrand(data) { + return request({ + url: '/brand/create', + method: 'post', + data + }) +} + +export function readBrand(data) { + return request({ + url: '/brand/read', + method: 'get', + data + }) +} + +export function updateBrand(data) { + return request({ + url: '/brand/update', + method: 'post', + data + }) +} + +export function deleteBrand(data) { + return request({ + url: '/brand/delete', + method: 'post', + data + }) +} diff --git a/litemall-admin/src/api/cart.js b/litemall-admin/src/api/cart.js new file mode 100644 index 00000000..7825d527 --- /dev/null +++ b/litemall-admin/src/api/cart.js @@ -0,0 +1,41 @@ +import request from '@/utils/request' + +export function listCart(query) { + return request({ + url: '/cart/list', + method: 'get', + params: query + }) +} + +export function createCart(data) { + return request({ + url: '/cart/create', + method: 'post', + data + }) +} + +export function readCart(data) { + return request({ + url: '/cart/read', + method: 'get', + data + }) +} + +export function updateCart(data) { + return request({ + url: '/cart/update', + method: 'post', + data + }) +} + +export function deleteCart(data) { + return request({ + url: '/cart/delete', + method: 'post', + data + }) +} diff --git a/litemall-admin/src/api/category.js b/litemall-admin/src/api/category.js new file mode 100644 index 00000000..5e76bf0c --- /dev/null +++ b/litemall-admin/src/api/category.js @@ -0,0 +1,48 @@ +import request from '@/utils/request' + +export function listCategory(query) { + return request({ + url: '/category/list', + method: 'get', + params: query + }) +} + +export function listCatL1() { + return request({ + url: '/category/l1', + method: 'get' + }) +} + +export function createCategory(data) { + return request({ + url: '/category/create', + method: 'post', + data + }) +} + +export function readCategory(data) { + return request({ + url: '/category/read', + method: 'get', + data + }) +} + +export function updateCategory(data) { + return request({ + url: '/category/update', + method: 'post', + data + }) +} + +export function deleteCategory(data) { + return request({ + url: '/category/delete', + method: 'post', + data + }) +} diff --git a/litemall-admin/src/api/collect.js b/litemall-admin/src/api/collect.js new file mode 100644 index 00000000..8499c6bf --- /dev/null +++ b/litemall-admin/src/api/collect.js @@ -0,0 +1,41 @@ +import request from '@/utils/request' + +export function listCollect(query) { + return request({ + url: '/collect/list', + method: 'get', + params: query + }) +} + +export function createCollect(data) { + return request({ + url: '/collect/create', + method: 'post', + data + }) +} + +export function readCollect(data) { + return request({ + url: '/collect/read', + method: 'get', + data + }) +} + +export function updateCollect(data) { + return request({ + url: '/collect/update', + method: 'post', + data + }) +} + +export function deleteCollect(data) { + return request({ + url: '/collect/delete', + method: 'post', + data + }) +} diff --git a/litemall-admin/src/api/comment.js b/litemall-admin/src/api/comment.js new file mode 100644 index 00000000..d0abe8d0 --- /dev/null +++ b/litemall-admin/src/api/comment.js @@ -0,0 +1,41 @@ +import request from '@/utils/request' + +export function listComment(query) { + return request({ + url: '/comment/list', + method: 'get', + params: query + }) +} + +export function createComment(data) { + return request({ + url: '/comment/create', + method: 'post', + data + }) +} + +export function readComment(data) { + return request({ + url: '/comment/read', + method: 'get', + data + }) +} + +export function updateComment(data) { + return request({ + url: '/comment/update', + method: 'post', + data + }) +} + +export function deleteComment(data) { + return request({ + url: '/comment/delete', + method: 'post', + data + }) +} diff --git a/litemall-admin/src/api/dashboard.js b/litemall-admin/src/api/dashboard.js new file mode 100644 index 00000000..c357f87f --- /dev/null +++ b/litemall-admin/src/api/dashboard.js @@ -0,0 +1,9 @@ +import request from '@/utils/request' + +export function info(query) { + return request({ + url: '/dashboard', + method: 'get', + params: query + }) +} diff --git a/litemall-admin/src/api/footprint.js b/litemall-admin/src/api/footprint.js new file mode 100644 index 00000000..9d9cee3e --- /dev/null +++ b/litemall-admin/src/api/footprint.js @@ -0,0 +1,41 @@ +import request from '@/utils/request' + +export function listFootprint(query) { + return request({ + url: '/footprint/list', + method: 'get', + params: query + }) +} + +export function createFootprint(data) { + return request({ + url: '/footprint/create', + method: 'post', + data + }) +} + +export function readFootprint(data) { + return request({ + url: '/footprint/read', + method: 'get', + data + }) +} + +export function updateFootprint(data) { + return request({ + url: '/footprint/update', + method: 'post', + data + }) +} + +export function deleteFootprint(data) { + return request({ + url: '/footprint/delete', + method: 'post', + data + }) +} diff --git a/litemall-admin/src/api/goods-attribute.js b/litemall-admin/src/api/goods-attribute.js new file mode 100644 index 00000000..e8500604 --- /dev/null +++ b/litemall-admin/src/api/goods-attribute.js @@ -0,0 +1,41 @@ +import request from '@/utils/request' + +export function listGoodsAttribute(query) { + return request({ + url: '/goods-attribute/list', + method: 'get', + params: query + }) +} + +export function createGoodsAttribute(data) { + return request({ + url: '/goods-attribute/create', + method: 'post', + data + }) +} + +export function readGoodsAttribute(data) { + return request({ + url: '/goods-attribute/read', + method: 'get', + data + }) +} + +export function updateGoodsAttribute(data) { + return request({ + url: '/goods-attribute/update', + method: 'post', + data + }) +} + +export function deleteGoodsAttribute(data) { + return request({ + url: '/goods-attribute/delete', + method: 'post', + data + }) +} diff --git a/litemall-admin/src/api/goods-specification.js b/litemall-admin/src/api/goods-specification.js new file mode 100644 index 00000000..69b6eb82 --- /dev/null +++ b/litemall-admin/src/api/goods-specification.js @@ -0,0 +1,49 @@ +import request from '@/utils/request' + +export function listGoodsSpecification(query) { + return request({ + url: '/goods-specification/list', + method: 'get', + params: query + }) +} + +export function createGoodsSpecification(data) { + return request({ + url: '/goods-specification/create', + method: 'post', + data + }) +} + +export function readGoodsSpecification(data) { + return request({ + url: '/goods-specification/read', + method: 'get', + data + }) +} + +export function updateGoodsSpecification(data) { + return request({ + url: '/goods-specification/update', + method: 'post', + data + }) +} + +export function deleteGoodsSpecification(data) { + return request({ + url: '/goods-specification/delete', + method: 'post', + data + }) +} + +export function listGoodsSpecificationVo(query) { + return request({ + url: '/goods-specification/volist', + method: 'get', + params: query + }) +} diff --git a/litemall-admin/src/api/goods.js b/litemall-admin/src/api/goods.js new file mode 100644 index 00000000..fc45aa04 --- /dev/null +++ b/litemall-admin/src/api/goods.js @@ -0,0 +1,41 @@ +import request from '@/utils/request' + +export function listGoods(query) { + return request({ + url: '/goods/list', + method: 'get', + params: query + }) +} + +export function createGoods(data) { + return request({ + url: '/goods/create', + method: 'post', + data + }) +} + +export function readGoods(data) { + return request({ + url: '/goods/read', + method: 'get', + data + }) +} + +export function updateGoods(data) { + return request({ + url: '/goods/update', + method: 'post', + data + }) +} + +export function deleteGoods(data) { + return request({ + url: '/goods/delete', + method: 'post', + data + }) +} diff --git a/litemall-admin/src/api/history.js b/litemall-admin/src/api/history.js new file mode 100644 index 00000000..0c792867 --- /dev/null +++ b/litemall-admin/src/api/history.js @@ -0,0 +1,41 @@ +import request from '@/utils/request' + +export function listHistory(query) { + return request({ + url: '/history/list', + method: 'get', + params: query + }) +} + +export function createHistory(data) { + return request({ + url: '/history/create', + method: 'post', + data + }) +} + +export function readHistory(data) { + return request({ + url: '/history/read', + method: 'get', + data + }) +} + +export function updateHistory(data) { + return request({ + url: '/history/update', + method: 'post', + data + }) +} + +export function deleteHistory(data) { + return request({ + url: '/history/delete', + method: 'post', + data + }) +} diff --git a/litemall-admin/src/api/issue.js b/litemall-admin/src/api/issue.js new file mode 100644 index 00000000..667079b8 --- /dev/null +++ b/litemall-admin/src/api/issue.js @@ -0,0 +1,41 @@ +import request from '@/utils/request' + +export function listIssue(query) { + return request({ + url: '/issue/list', + method: 'get', + params: query + }) +} + +export function createIssue(data) { + return request({ + url: '/issue/create', + method: 'post', + data + }) +} + +export function readIssue(data) { + return request({ + url: '/issue/read', + method: 'get', + data + }) +} + +export function updateIssue(data) { + return request({ + url: '/issue/update', + method: 'post', + data + }) +} + +export function deleteIssue(data) { + return request({ + url: '/issue/delete', + method: 'post', + data + }) +} diff --git a/litemall-admin/src/api/keyword.js b/litemall-admin/src/api/keyword.js new file mode 100644 index 00000000..a24b1d75 --- /dev/null +++ b/litemall-admin/src/api/keyword.js @@ -0,0 +1,41 @@ +import request from '@/utils/request' + +export function listKeyword(query) { + return request({ + url: '/keyword/list', + method: 'get', + params: query + }) +} + +export function createKeyword(data) { + return request({ + url: '/keyword/create', + method: 'post', + data + }) +} + +export function readKeyword(data) { + return request({ + url: '/keyword/read', + method: 'get', + data + }) +} + +export function updateKeyword(data) { + return request({ + url: '/keyword/update', + method: 'post', + data + }) +} + +export function deleteKeyword(data) { + return request({ + url: '/keyword/delete', + method: 'post', + data + }) +} diff --git a/litemall-admin/src/api/login.js b/litemall-admin/src/api/login.js new file mode 100644 index 00000000..a2f79f06 --- /dev/null +++ b/litemall-admin/src/api/login.js @@ -0,0 +1,29 @@ +import request from '@/utils/request' + +export function loginByUsername(username, password) { + const data = { + username, + password + } + return request({ + url: '/login/login', + method: 'post', + data + }) +} + +export function logout() { + return request({ + url: '/login/logout', + method: 'post' + }) +} + +export function getUserInfo(token) { + return request({ + url: '/admin/info', + method: 'get', + params: { token } + }) +} + diff --git a/litemall-admin/src/api/order.js b/litemall-admin/src/api/order.js new file mode 100644 index 00000000..de6e814e --- /dev/null +++ b/litemall-admin/src/api/order.js @@ -0,0 +1,41 @@ +import request from '@/utils/request' + +export function listOrder(query) { + return request({ + url: '/order/list', + method: 'get', + params: query + }) +} + +export function createOrder(data) { + return request({ + url: '/order/create', + method: 'post', + data + }) +} + +export function readOrder(data) { + return request({ + url: '/order/read', + method: 'get', + data + }) +} + +export function updateOrder(data) { + return request({ + url: '/order/update', + method: 'post', + data + }) +} + +export function deleteOrder(data) { + return request({ + url: '/order/delete', + method: 'post', + data + }) +} diff --git a/litemall-admin/src/api/product.js b/litemall-admin/src/api/product.js new file mode 100644 index 00000000..5e0bf765 --- /dev/null +++ b/litemall-admin/src/api/product.js @@ -0,0 +1,41 @@ +import request from '@/utils/request' + +export function listProduct(query) { + return request({ + url: '/product/list', + method: 'get', + params: query + }) +} + +export function createProduct(data) { + return request({ + url: '/product/create', + method: 'post', + data + }) +} + +export function readProduct(data) { + return request({ + url: '/product/read', + method: 'get', + data + }) +} + +export function updateProduct(data) { + return request({ + url: '/product/update', + method: 'post', + data + }) +} + +export function deleteProduct(data) { + return request({ + url: '/product/delete', + method: 'post', + data + }) +} diff --git a/litemall-admin/src/api/qiniu.js b/litemall-admin/src/api/qiniu.js new file mode 100644 index 00000000..a0375844 --- /dev/null +++ b/litemall-admin/src/api/qiniu.js @@ -0,0 +1,8 @@ +import request from '@/utils/request' + +export function getToken() { + return request({ + url: '/qiniu/upload/token', // 假地址 自行替换 + method: 'get' + }) +} diff --git a/litemall-admin/src/api/region.js b/litemall-admin/src/api/region.js new file mode 100644 index 00000000..dd705ee4 --- /dev/null +++ b/litemall-admin/src/api/region.js @@ -0,0 +1,17 @@ +import request from '@/utils/request' + +export function listRegion(query) { + return request({ + url: '/region/list', + method: 'get', + params: query + }) +} + +export function listSubRegion(query) { + return request({ + url: '/region/clist', + method: 'get', + params: query + }) +} diff --git a/litemall-admin/src/api/storage.js b/litemall-admin/src/api/storage.js new file mode 100644 index 00000000..3640677d --- /dev/null +++ b/litemall-admin/src/api/storage.js @@ -0,0 +1,47 @@ +import axios from 'axios' + +// create an axios instance +const service = axios.create({ + baseURL: process.env.OS_API, // api的base_url + timeout: 5000 // request timeout +}) + +export function listStorage(query) { + return service({ + url: '/storage/list', + method: 'get', + params: query + }) +} + +export function createStorage(data) { + return service({ + url: '/storage/create', + method: 'post', + data + }) +} + +export function readStorage(data) { + return service({ + url: '/storage/read', + method: 'get', + data + }) +} + +export function updateStorage(data) { + return service({ + url: '/storage/update', + method: 'post', + data + }) +} + +export function deleteStorage(data) { + return service({ + url: '/storage/delete', + method: 'post', + data + }) +} diff --git a/litemall-admin/src/api/topic.js b/litemall-admin/src/api/topic.js new file mode 100644 index 00000000..9b45f4f5 --- /dev/null +++ b/litemall-admin/src/api/topic.js @@ -0,0 +1,41 @@ +import request from '@/utils/request' + +export function listTopic(query) { + return request({ + url: '/topic/list', + method: 'get', + params: query + }) +} + +export function createTopic(data) { + return request({ + url: '/topic/create', + method: 'post', + data + }) +} + +export function readTopic(data) { + return request({ + url: '/topic/read', + method: 'get', + data + }) +} + +export function updateTopic(data) { + return request({ + url: '/topic/update', + method: 'post', + data + }) +} + +export function deleteTopic(data) { + return request({ + url: '/topic/delete', + method: 'post', + data + }) +} diff --git a/litemall-admin/src/api/user.js b/litemall-admin/src/api/user.js new file mode 100644 index 00000000..e7cf88b0 --- /dev/null +++ b/litemall-admin/src/api/user.js @@ -0,0 +1,33 @@ +import request from '@/utils/request' + +export function fetchList(query) { + return request({ + url: '/user/list', + method: 'get', + params: query + }) +} + +export function createUser(data) { + return request({ + url: '/user/create', + method: 'post', + data + }) +} + +export function readUser(data) { + return request({ + url: '/user/detail', + method: 'get', + data + }) +} + +export function updateUser(data) { + return request({ + url: '/user/update', + method: 'post', + data + }) +} diff --git a/litemall-admin/src/assets/401_images/401.gif b/litemall-admin/src/assets/401_images/401.gif new file mode 100644 index 00000000..cd6e0d94 Binary files /dev/null and b/litemall-admin/src/assets/401_images/401.gif differ diff --git a/litemall-admin/src/assets/404_images/404.png b/litemall-admin/src/assets/404_images/404.png new file mode 100644 index 00000000..3d8e2305 Binary files /dev/null and b/litemall-admin/src/assets/404_images/404.png differ diff --git a/litemall-admin/src/assets/404_images/404_cloud.png b/litemall-admin/src/assets/404_images/404_cloud.png new file mode 100644 index 00000000..c6281d09 Binary files /dev/null and b/litemall-admin/src/assets/404_images/404_cloud.png differ diff --git a/litemall-admin/src/assets/echarts-macarons.js b/litemall-admin/src/assets/echarts-macarons.js new file mode 100644 index 00000000..0678ef83 --- /dev/null +++ b/litemall-admin/src/assets/echarts-macarons.js @@ -0,0 +1,199 @@ +/* eslint-disable */ +(function (root, factory) { + if (typeof define === 'function' && define.amd) { + // AMD. Register as an anonymous module. + define(['exports', 'echarts'], factory); + } else if (typeof exports === 'object' && typeof exports.nodeName !== 'string') { + // CommonJS + factory(exports, require('echarts')); + } else { + // Browser globals + factory({}, root.echarts); + } +}(this, function (exports, echarts) { + var log = function (msg) { + if (typeof console !== 'undefined') { + console && console.error && console.error(msg); + } + }; + if (!echarts) { + log('ECharts is not Loaded'); + return; + } + + var colorPalette = [ + '#2ec7c9','#b6a2de','#5ab1ef','#ffb980','#d87a80', + '#8d98b3','#e5cf0d','#97b552','#95706d','#dc69aa', + '#07a2a4','#9a7fd1','#588dd5','#f5994e','#c05050', + '#59678c','#c9ab00','#7eb00a','#6f5553','#c14089' + ]; + + + var theme = { + color: colorPalette, + + title: { + textStyle: { + fontWeight: 'normal', + color: '#008acd' + } + }, + + visualMap: { + itemWidth: 15, + color: ['#5ab1ef','#e0ffff'] + }, + + toolbox: { + iconStyle: { + normal: { + borderColor: colorPalette[0] + } + } + }, + + tooltip: { + backgroundColor: 'rgba(50,50,50,0.5)', + axisPointer : { + type : 'line', + lineStyle : { + color: '#008acd' + }, + crossStyle: { + color: '#008acd' + }, + shadowStyle : { + color: 'rgba(200,200,200,0.2)' + } + } + }, + + dataZoom: { + dataBackgroundColor: '#efefff', + fillerColor: 'rgba(182,162,222,0.2)', + handleColor: '#008acd' + }, + + grid: { + borderColor: '#eee' + }, + + categoryAxis: { + axisLine: { + lineStyle: { + color: '#008acd' + } + }, + splitLine: { + lineStyle: { + color: ['#eee'] + } + } + }, + + valueAxis: { + axisLine: { + lineStyle: { + color: '#008acd' + } + }, + splitArea : { + show : true, + areaStyle : { + color: ['rgba(250,250,250,0.1)','rgba(200,200,200,0.1)'] + } + }, + splitLine: { + lineStyle: { + color: ['#eee'] + } + } + }, + + timeline : { + lineStyle : { + color : '#008acd' + }, + controlStyle : { + normal : { color : '#008acd'}, + emphasis : { color : '#008acd'} + }, + symbol : 'emptyCircle', + symbolSize : 3 + }, + + line: { + smooth : true, + symbol: 'emptyCircle', + symbolSize: 3 + }, + + candlestick: { + itemStyle: { + normal: { + color: '#d87a80', + color0: '#2ec7c9', + lineStyle: { + color: '#d87a80', + color0: '#2ec7c9' + } + } + } + }, + + scatter: { + symbol: 'circle', + symbolSize: 4 + }, + + map: { + label: { + normal: { + textStyle: { + color: '#d87a80' + } + } + }, + itemStyle: { + normal: { + borderColor: '#eee', + areaColor: '#ddd' + }, + emphasis: { + areaColor: '#fe994e' + } + } + }, + + graph: { + color: colorPalette + }, + + gauge : { + axisLine: { + lineStyle: { + color: [[0.2, '#2ec7c9'],[0.8, '#5ab1ef'],[1, '#d87a80']], + width: 10 + } + }, + axisTick: { + splitNumber: 10, + length :15, + lineStyle: { + color: 'auto' + } + }, + splitLine: { + length :22, + lineStyle: { + color: 'auto' + } + }, + pointer : { + width : 5 + } + } + }; + + echarts.registerTheme('macarons', theme); +})); diff --git a/litemall-admin/src/components/BackToTop/index.vue b/litemall-admin/src/components/BackToTop/index.vue new file mode 100644 index 00000000..cbb1f21d --- /dev/null +++ b/litemall-admin/src/components/BackToTop/index.vue @@ -0,0 +1,110 @@ + + + + + diff --git a/litemall-admin/src/components/Breadcrumb/index.vue b/litemall-admin/src/components/Breadcrumb/index.vue new file mode 100644 index 00000000..13c1a2ab --- /dev/null +++ b/litemall-admin/src/components/Breadcrumb/index.vue @@ -0,0 +1,52 @@ + + + + + diff --git a/litemall-admin/src/components/Charts/keyboard.vue b/litemall-admin/src/components/Charts/keyboard.vue new file mode 100644 index 00000000..4c2a0bbd --- /dev/null +++ b/litemall-admin/src/components/Charts/keyboard.vue @@ -0,0 +1,150 @@ + + + diff --git a/litemall-admin/src/components/Charts/lineMarker.vue b/litemall-admin/src/components/Charts/lineMarker.vue new file mode 100644 index 00000000..e8dce6da --- /dev/null +++ b/litemall-admin/src/components/Charts/lineMarker.vue @@ -0,0 +1,225 @@ + + + diff --git a/litemall-admin/src/components/Charts/mixChart.vue b/litemall-admin/src/components/Charts/mixChart.vue new file mode 100644 index 00000000..6454dd36 --- /dev/null +++ b/litemall-admin/src/components/Charts/mixChart.vue @@ -0,0 +1,268 @@ + + + diff --git a/litemall-admin/src/components/Hamburger/index.vue b/litemall-admin/src/components/Hamburger/index.vue new file mode 100644 index 00000000..fb6aeacd --- /dev/null +++ b/litemall-admin/src/components/Hamburger/index.vue @@ -0,0 +1,45 @@ + + + + + diff --git a/litemall-admin/src/components/Screenfull/index.vue b/litemall-admin/src/components/Screenfull/index.vue new file mode 100644 index 00000000..56de914e --- /dev/null +++ b/litemall-admin/src/components/Screenfull/index.vue @@ -0,0 +1,65 @@ + + + + + diff --git a/litemall-admin/src/components/ScrollBar/index.vue b/litemall-admin/src/components/ScrollBar/index.vue new file mode 100644 index 00000000..dc32f5c0 --- /dev/null +++ b/litemall-admin/src/components/ScrollBar/index.vue @@ -0,0 +1,57 @@ + + + + + diff --git a/litemall-admin/src/components/ScrollPane/index.vue b/litemall-admin/src/components/ScrollPane/index.vue new file mode 100644 index 00000000..1ce68c11 --- /dev/null +++ b/litemall-admin/src/components/ScrollPane/index.vue @@ -0,0 +1,72 @@ + + + + + diff --git a/litemall-admin/src/components/Sticky/index.vue b/litemall-admin/src/components/Sticky/index.vue new file mode 100644 index 00000000..2c9657be --- /dev/null +++ b/litemall-admin/src/components/Sticky/index.vue @@ -0,0 +1,76 @@ + + + diff --git a/litemall-admin/src/components/SvgIcon/index.vue b/litemall-admin/src/components/SvgIcon/index.vue new file mode 100644 index 00000000..e331a27e --- /dev/null +++ b/litemall-admin/src/components/SvgIcon/index.vue @@ -0,0 +1,42 @@ + + + + + diff --git a/litemall-admin/src/components/Tinymce/components/editorImage.vue b/litemall-admin/src/components/Tinymce/components/editorImage.vue new file mode 100644 index 00000000..fca21fba --- /dev/null +++ b/litemall-admin/src/components/Tinymce/components/editorImage.vue @@ -0,0 +1,95 @@ + + + + + diff --git a/litemall-admin/src/components/Tinymce/index.vue b/litemall-admin/src/components/Tinymce/index.vue new file mode 100644 index 00000000..53f6b934 --- /dev/null +++ b/litemall-admin/src/components/Tinymce/index.vue @@ -0,0 +1,171 @@ + + + + + diff --git a/litemall-admin/src/components/Tinymce/plugins.js b/litemall-admin/src/components/Tinymce/plugins.js new file mode 100644 index 00000000..a1a64793 --- /dev/null +++ b/litemall-admin/src/components/Tinymce/plugins.js @@ -0,0 +1,7 @@ +// Any plugins you want to use has to be imported +// Detail plugins list see https://www.tinymce.com/docs/plugins/ +// Custom builds see https://www.tinymce.com/download/custom-builds/ + +const plugins = ['advlist anchor autolink autoresize autosave bbcode code codesample colorpicker colorpicker contextmenu directionality emoticons fullpage fullscreen hr image imagetools importcss insertdatetime legacyoutput link lists media nonbreaking noneditable pagebreak paste preview print save searchreplace spellchecker tabfocus table template textcolor textpattern visualblocks visualchars wordcount'] + +export default plugins diff --git a/litemall-admin/src/components/Tinymce/toolbar.js b/litemall-admin/src/components/Tinymce/toolbar.js new file mode 100644 index 00000000..de4906fc --- /dev/null +++ b/litemall-admin/src/components/Tinymce/toolbar.js @@ -0,0 +1,6 @@ +// Here is a list of the toolbar +// Detail list see https://www.tinymce.com/docs/advanced/editor-control-identifiers/#toolbarcontrols + +const toolbar = ['bold italic underline strikethrough alignleft aligncenter alignright outdent indent blockquote undo redo removeformat subscript superscript code codesample', 'hr bullist numlist link image charmap preview anchor pagebreak fullscreen insertdatetime media table emoticons forecolor backcolor'] + +export default toolbar diff --git a/litemall-admin/src/components/Upload/singleImage.vue b/litemall-admin/src/components/Upload/singleImage.vue new file mode 100644 index 00000000..18b892fe --- /dev/null +++ b/litemall-admin/src/components/Upload/singleImage.vue @@ -0,0 +1,123 @@ + + + + + diff --git a/litemall-admin/src/components/Upload/singleImage2.vue b/litemall-admin/src/components/Upload/singleImage2.vue new file mode 100644 index 00000000..a353713a --- /dev/null +++ b/litemall-admin/src/components/Upload/singleImage2.vue @@ -0,0 +1,118 @@ + + + + + diff --git a/litemall-admin/src/components/Upload/singleImage3.vue b/litemall-admin/src/components/Upload/singleImage3.vue new file mode 100644 index 00000000..92717699 --- /dev/null +++ b/litemall-admin/src/components/Upload/singleImage3.vue @@ -0,0 +1,145 @@ + + + + + diff --git a/litemall-admin/src/directive/sticky.js b/litemall-admin/src/directive/sticky.js new file mode 100644 index 00000000..bc234660 --- /dev/null +++ b/litemall-admin/src/directive/sticky.js @@ -0,0 +1,91 @@ +const vueSticky = {} +let listenAction +vueSticky.install = Vue => { + Vue.directive('sticky', { + inserted(el, binding) { + const params = binding.value || {} + const stickyTop = params.stickyTop || 0 + const zIndex = params.zIndex || 1000 + const elStyle = el.style + + elStyle.position = '-webkit-sticky' + elStyle.position = 'sticky' + // if the browser support css sticky(Currently Safari, Firefox and Chrome Canary) + // if (~elStyle.position.indexOf('sticky')) { + // elStyle.top = `${stickyTop}px`; + // elStyle.zIndex = zIndex; + // return + // } + const elHeight = el.getBoundingClientRect().height + const elWidth = el.getBoundingClientRect().width + elStyle.cssText = `top: ${stickyTop}px; z-index: ${zIndex}` + + const parentElm = el.parentNode || document.documentElement + const placeholder = document.createElement('div') + placeholder.style.display = 'none' + placeholder.style.width = `${elWidth}px` + placeholder.style.height = `${elHeight}px` + parentElm.insertBefore(placeholder, el) + + let active = false + + const getScroll = (target, top) => { + const prop = top ? 'pageYOffset' : 'pageXOffset' + const method = top ? 'scrollTop' : 'scrollLeft' + let ret = target[prop] + if (typeof ret !== 'number') { + ret = window.document.documentElement[method] + } + return ret + } + + const sticky = () => { + if (active) { + return + } + if (!elStyle.height) { + elStyle.height = `${el.offsetHeight}px` + } + + elStyle.position = 'fixed' + elStyle.width = `${elWidth}px` + placeholder.style.display = 'inline-block' + active = true + } + + const reset = () => { + if (!active) { + return + } + + elStyle.position = '' + placeholder.style.display = 'none' + active = false + } + + const check = () => { + const scrollTop = getScroll(window, true) + const offsetTop = el.getBoundingClientRect().top + if (offsetTop < stickyTop) { + sticky() + } else { + if (scrollTop < elHeight + stickyTop) { + reset() + } + } + } + listenAction = () => { + check() + } + + window.addEventListener('scroll', listenAction) + }, + + unbind() { + window.removeEventListener('scroll', listenAction) + } + }) +} + +export default vueSticky + diff --git a/litemall-admin/src/directive/waves/index.js b/litemall-admin/src/directive/waves/index.js new file mode 100644 index 00000000..65f9b308 --- /dev/null +++ b/litemall-admin/src/directive/waves/index.js @@ -0,0 +1,13 @@ +import waves from './waves' + +const install = function(Vue) { + Vue.directive('waves', waves) +} + +if (window.Vue) { + window.waves = waves + Vue.use(install); // eslint-disable-line +} + +waves.install = install +export default waves diff --git a/litemall-admin/src/directive/waves/waves.css b/litemall-admin/src/directive/waves/waves.css new file mode 100644 index 00000000..af7a7efd --- /dev/null +++ b/litemall-admin/src/directive/waves/waves.css @@ -0,0 +1,26 @@ +.waves-ripple { + position: absolute; + border-radius: 100%; + background-color: rgba(0, 0, 0, 0.15); + background-clip: padding-box; + pointer-events: none; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + -webkit-transform: scale(0); + -ms-transform: scale(0); + transform: scale(0); + opacity: 1; +} + +.waves-ripple.z-active { + opacity: 0; + -webkit-transform: scale(2); + -ms-transform: scale(2); + transform: scale(2); + -webkit-transition: opacity 1.2s ease-out, -webkit-transform 0.6s ease-out; + transition: opacity 1.2s ease-out, -webkit-transform 0.6s ease-out; + transition: opacity 1.2s ease-out, transform 0.6s ease-out; + transition: opacity 1.2s ease-out, transform 0.6s ease-out, -webkit-transform 0.6s ease-out; +} \ No newline at end of file diff --git a/litemall-admin/src/directive/waves/waves.js b/litemall-admin/src/directive/waves/waves.js new file mode 100644 index 00000000..ac1d8611 --- /dev/null +++ b/litemall-admin/src/directive/waves/waves.js @@ -0,0 +1,42 @@ +import './waves.css' + +export default{ + bind(el, binding) { + el.addEventListener('click', e => { + const customOpts = Object.assign({}, binding.value) + const opts = Object.assign({ + ele: el, // 波纹作用元素 + type: 'hit', // hit点击位置扩散center中心点扩展 + color: 'rgba(0, 0, 0, 0.15)' // 波纹颜色 + }, customOpts) + const target = opts.ele + if (target) { + target.style.position = 'relative' + target.style.overflow = 'hidden' + const rect = target.getBoundingClientRect() + let ripple = target.querySelector('.waves-ripple') + if (!ripple) { + ripple = document.createElement('span') + ripple.className = 'waves-ripple' + ripple.style.height = ripple.style.width = Math.max(rect.width, rect.height) + 'px' + target.appendChild(ripple) + } else { + ripple.className = 'waves-ripple' + } + switch (opts.type) { + case 'center': + ripple.style.top = (rect.height / 2 - ripple.offsetHeight / 2) + 'px' + ripple.style.left = (rect.width / 2 - ripple.offsetWidth / 2) + 'px' + break + default: + ripple.style.top = (e.pageY - rect.top - ripple.offsetHeight / 2 - document.body.scrollTop) + 'px' + ripple.style.left = (e.pageX - rect.left - ripple.offsetWidth / 2 - document.body.scrollLeft) + 'px' + } + ripple.style.backgroundColor = opts.color + ripple.className = 'waves-ripple z-active' + return false + } + }, false) + } +} + diff --git a/litemall-admin/src/icons/index.js b/litemall-admin/src/icons/index.js new file mode 100644 index 00000000..14e2e130 --- /dev/null +++ b/litemall-admin/src/icons/index.js @@ -0,0 +1,9 @@ +import Vue from 'vue' +import SvgIcon from '@/components/SvgIcon'// svg组件 + +// register globally +Vue.component('svg-icon', SvgIcon) + +const requireAll = requireContext => requireContext.keys().map(requireContext) +const req = require.context('./svg', false, /\.svg$/) +requireAll(req) diff --git a/litemall-admin/src/icons/svg/404.svg b/litemall-admin/src/icons/svg/404.svg new file mode 100644 index 00000000..bc5bc9fa --- /dev/null +++ b/litemall-admin/src/icons/svg/404.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/litemall-admin/src/icons/svg/bug.svg b/litemall-admin/src/icons/svg/bug.svg new file mode 100644 index 00000000..a12a9394 --- /dev/null +++ b/litemall-admin/src/icons/svg/bug.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/litemall-admin/src/icons/svg/chart.svg b/litemall-admin/src/icons/svg/chart.svg new file mode 100644 index 00000000..b1b31336 --- /dev/null +++ b/litemall-admin/src/icons/svg/chart.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/litemall-admin/src/icons/svg/clipboard.svg b/litemall-admin/src/icons/svg/clipboard.svg new file mode 100644 index 00000000..cf1c9b0c --- /dev/null +++ b/litemall-admin/src/icons/svg/clipboard.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/litemall-admin/src/icons/svg/component.svg b/litemall-admin/src/icons/svg/component.svg new file mode 100644 index 00000000..a8008c84 --- /dev/null +++ b/litemall-admin/src/icons/svg/component.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/litemall-admin/src/icons/svg/dashboard.svg b/litemall-admin/src/icons/svg/dashboard.svg new file mode 100644 index 00000000..bee42507 --- /dev/null +++ b/litemall-admin/src/icons/svg/dashboard.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/litemall-admin/src/icons/svg/documentation.svg b/litemall-admin/src/icons/svg/documentation.svg new file mode 100644 index 00000000..405c2ad7 --- /dev/null +++ b/litemall-admin/src/icons/svg/documentation.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/litemall-admin/src/icons/svg/drag.svg b/litemall-admin/src/icons/svg/drag.svg new file mode 100644 index 00000000..819c8d50 --- /dev/null +++ b/litemall-admin/src/icons/svg/drag.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/litemall-admin/src/icons/svg/email.svg b/litemall-admin/src/icons/svg/email.svg new file mode 100644 index 00000000..8a87e147 --- /dev/null +++ b/litemall-admin/src/icons/svg/email.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/litemall-admin/src/icons/svg/example.svg b/litemall-admin/src/icons/svg/example.svg new file mode 100644 index 00000000..681422ea --- /dev/null +++ b/litemall-admin/src/icons/svg/example.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/litemall-admin/src/icons/svg/excel.svg b/litemall-admin/src/icons/svg/excel.svg new file mode 100644 index 00000000..e5dd5cec --- /dev/null +++ b/litemall-admin/src/icons/svg/excel.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/litemall-admin/src/icons/svg/eye.svg b/litemall-admin/src/icons/svg/eye.svg new file mode 100644 index 00000000..194aa45c --- /dev/null +++ b/litemall-admin/src/icons/svg/eye.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/litemall-admin/src/icons/svg/form.svg b/litemall-admin/src/icons/svg/form.svg new file mode 100644 index 00000000..79716f06 --- /dev/null +++ b/litemall-admin/src/icons/svg/form.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/litemall-admin/src/icons/svg/icon.svg b/litemall-admin/src/icons/svg/icon.svg new file mode 100644 index 00000000..906af96a --- /dev/null +++ b/litemall-admin/src/icons/svg/icon.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/litemall-admin/src/icons/svg/international.svg b/litemall-admin/src/icons/svg/international.svg new file mode 100644 index 00000000..6912767d --- /dev/null +++ b/litemall-admin/src/icons/svg/international.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/litemall-admin/src/icons/svg/language.svg b/litemall-admin/src/icons/svg/language.svg new file mode 100644 index 00000000..2baf7431 --- /dev/null +++ b/litemall-admin/src/icons/svg/language.svg @@ -0,0 +1 @@ + diff --git a/litemall-admin/src/icons/svg/lock.svg b/litemall-admin/src/icons/svg/lock.svg new file mode 100644 index 00000000..37c60701 --- /dev/null +++ b/litemall-admin/src/icons/svg/lock.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/litemall-admin/src/icons/svg/message.svg b/litemall-admin/src/icons/svg/message.svg new file mode 100644 index 00000000..d807b002 --- /dev/null +++ b/litemall-admin/src/icons/svg/message.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/litemall-admin/src/icons/svg/money.svg b/litemall-admin/src/icons/svg/money.svg new file mode 100644 index 00000000..d4fcb9ca --- /dev/null +++ b/litemall-admin/src/icons/svg/money.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/litemall-admin/src/icons/svg/password.svg b/litemall-admin/src/icons/svg/password.svg new file mode 100644 index 00000000..920b500b --- /dev/null +++ b/litemall-admin/src/icons/svg/password.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/litemall-admin/src/icons/svg/people.svg b/litemall-admin/src/icons/svg/people.svg new file mode 100644 index 00000000..3985ab51 --- /dev/null +++ b/litemall-admin/src/icons/svg/people.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/litemall-admin/src/icons/svg/peoples.svg b/litemall-admin/src/icons/svg/peoples.svg new file mode 100644 index 00000000..2dccfcc0 --- /dev/null +++ b/litemall-admin/src/icons/svg/peoples.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/litemall-admin/src/icons/svg/qq.svg b/litemall-admin/src/icons/svg/qq.svg new file mode 100644 index 00000000..97aee717 --- /dev/null +++ b/litemall-admin/src/icons/svg/qq.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/litemall-admin/src/icons/svg/shoppingCard.svg b/litemall-admin/src/icons/svg/shoppingCard.svg new file mode 100644 index 00000000..cdebbdb4 --- /dev/null +++ b/litemall-admin/src/icons/svg/shoppingCard.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/litemall-admin/src/icons/svg/star.svg b/litemall-admin/src/icons/svg/star.svg new file mode 100644 index 00000000..685a301d --- /dev/null +++ b/litemall-admin/src/icons/svg/star.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/litemall-admin/src/icons/svg/tab.svg b/litemall-admin/src/icons/svg/tab.svg new file mode 100644 index 00000000..17aa088b --- /dev/null +++ b/litemall-admin/src/icons/svg/tab.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/litemall-admin/src/icons/svg/table.svg b/litemall-admin/src/icons/svg/table.svg new file mode 100644 index 00000000..083bc8cc --- /dev/null +++ b/litemall-admin/src/icons/svg/table.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/litemall-admin/src/icons/svg/theme.svg b/litemall-admin/src/icons/svg/theme.svg new file mode 100644 index 00000000..d5c2e5ad --- /dev/null +++ b/litemall-admin/src/icons/svg/theme.svg @@ -0,0 +1 @@ + diff --git a/litemall-admin/src/icons/svg/user.svg b/litemall-admin/src/icons/svg/user.svg new file mode 100644 index 00000000..5971deeb --- /dev/null +++ b/litemall-admin/src/icons/svg/user.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/litemall-admin/src/icons/svg/wechat.svg b/litemall-admin/src/icons/svg/wechat.svg new file mode 100644 index 00000000..d88a64bc --- /dev/null +++ b/litemall-admin/src/icons/svg/wechat.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/litemall-admin/src/icons/svg/zip.svg b/litemall-admin/src/icons/svg/zip.svg new file mode 100644 index 00000000..e9a9d012 --- /dev/null +++ b/litemall-admin/src/icons/svg/zip.svg @@ -0,0 +1 @@ + diff --git a/litemall-admin/src/main.js b/litemall-admin/src/main.js new file mode 100644 index 00000000..0a4f40c1 --- /dev/null +++ b/litemall-admin/src/main.js @@ -0,0 +1,30 @@ +import Vue from 'vue' + +import 'normalize.css/normalize.css'// A modern alternative to CSS resets + +import Element from 'element-ui' +import 'element-ui/lib/theme-chalk/index.css' + +import '@/styles/index.scss' // global css + +import App from './App' +import router from './router' +import store from './store' + +import './icons' // icon +import './permission' // permission control +// import './mock' // simulation data + +Vue.use(Element, { + size: 'medium' // set element-ui default size +}) + +Vue.config.productionTip = false + +new Vue({ + el: '#app', + router, + store, + template: '', + components: { App } +}) diff --git a/litemall-admin/src/mock/index.js b/litemall-admin/src/mock/index.js new file mode 100644 index 00000000..d96477a0 --- /dev/null +++ b/litemall-admin/src/mock/index.js @@ -0,0 +1,11 @@ +import Mock from 'mockjs' +import userAPI from './user' + +// 用户相关 +Mock.mock(/\/user\/list/, 'get', userAPI.getList) +Mock.mock(/\/user\/create/, 'post', userAPI.createUser) +Mock.mock(/\/user\/update/, 'post', userAPI.updateUser) +Mock.mock(/\/user\/read/, 'get', userAPI.readUser) +Mock.mock(/\/user\/delete/, 'post', userAPI.deleteUser) + +export default Mock diff --git a/litemall-admin/src/mock/res.js b/litemall-admin/src/mock/res.js new file mode 100644 index 00000000..e04ad71f --- /dev/null +++ b/litemall-admin/src/mock/res.js @@ -0,0 +1,11 @@ +export default { + ok: data => ({ + errno: 0, + errmsg: 'success', + data: data + }), + fail: () => ({ + errno: -1, + errmsg: 'fail' + }) +} diff --git a/litemall-admin/src/mock/user.js b/litemall-admin/src/mock/user.js new file mode 100644 index 00000000..03d3cf61 --- /dev/null +++ b/litemall-admin/src/mock/user.js @@ -0,0 +1,53 @@ +import Mock from 'mockjs' +import { param2Obj } from '@/utils' +import resAPI from './res' + +const List = [] +const count = 100 + +for (let i = 0; i < count; i++) { + List.push(Mock.mock({ + 'id': '@increment', + 'username': '@cname', + 'mobile': '@string("number", 11)', + 'age|10-30': 0, + 'birthday': '@date("yyyy-MM-dd")', + 'gender': '@integer(0, 2)', + 'status|1': ['可用', '禁用', '删除'] + })) +} + +export default { + getList: config => { + const { username, mobile, sort, page = 1, limit = 20 } = param2Obj(config.url) + + let mockList = List.filter(item => { + if (username && item.username.indexOf(username) < 0) return false + if (mobile && (item.mobile !== mobile)) return false + return true + }) + + if (sort === '-id') { + mockList = mockList.reverse() + } + + const pageList = mockList.filter((item, index) => index < limit * page && index >= limit * (page - 1)) + + return resAPI.ok({ + total: mockList.length, + items: pageList + }) + }, + createUser: () => { + return resAPI.ok() + }, + readUser: () => { + return resAPI.ok() + }, + updateUser: () => { + return resAPI.ok() + }, + deleteUser: () => { + return resAPI.ok() + } +} diff --git a/litemall-admin/src/permission.js b/litemall-admin/src/permission.js new file mode 100644 index 00000000..73ec8946 --- /dev/null +++ b/litemall-admin/src/permission.js @@ -0,0 +1,63 @@ +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' // getToken from cookie + +NProgress.configure({ showSpinner: false })// NProgress Configuration + +// permissiom judge function +function hasPermission(roles, permissionRoles) { + if (roles.indexOf('admin') >= 0) return true // admin permission passed directly + if (!permissionRoles) return true + return roles.some(role => permissionRoles.indexOf(role) >= 0) +} + +const whiteList = ['/login', '/authredirect']// no redirect whitelist + +router.beforeEach((to, from, next) => { + NProgress.start() // start progress bar + if (getToken()) { // determine if there has token + /* has token*/ + if (to.path === '/login') { + next({ path: '/' }) + NProgress.done() // if current page is dashboard will not trigger afterEach hook, so manually handle it + } else { + if (store.getters.roles.length === 0) { // 判断当前用户是否已拉取完user_info信息 + store.dispatch('GetUserInfo').then(res => { // 拉取user_info + const roles = res.data.data.roles // note: roles must be a array! such as: ['editor','develop'] + store.dispatch('GenerateRoutes', { roles }).then(() => { // 根据roles权限生成可访问的路由表 + router.addRoutes(store.getters.addRouters) // 动态添加可访问路由表 + next({ ...to, replace: true }) // hack方法 确保addRoutes已完成 ,set the replace: true so the navigation will not leave a history record + }) + }).catch(() => { + store.dispatch('FedLogOut').then(() => { + Message.error('Verification failed, please login again') + next({ path: '/login' }) + }) + }) + } else { + // 没有动态改变权限的需求可直接next() 删除下方权限判断 ↓ + if (hasPermission(store.getters.roles, to.meta.roles)) { + next()// + } else { + next({ path: '/401', replace: true, query: { noGoBack: true }}) + } + // 可删 ↑ + } + } + } else { + /* has no token*/ + if (whiteList.indexOf(to.path) !== -1) { // 在免登录白名单,直接进入 + next() + } else { + next('/login') // 否则全部重定向到登录页 + NProgress.done() // if current page is login will not trigger afterEach hook, so manually handle it + } + } +}) + +router.afterEach(() => { + NProgress.done() // finish progress bar +}) diff --git a/litemall-admin/src/router/_import_development.js b/litemall-admin/src/router/_import_development.js new file mode 100644 index 00000000..7c8b5e6b --- /dev/null +++ b/litemall-admin/src/router/_import_development.js @@ -0,0 +1 @@ +module.exports = file => require('@/views/' + file + '.vue').default // vue-loader at least v13.0.0+ diff --git a/litemall-admin/src/router/_import_production.js b/litemall-admin/src/router/_import_production.js new file mode 100644 index 00000000..331acba4 --- /dev/null +++ b/litemall-admin/src/router/_import_production.js @@ -0,0 +1 @@ +module.exports = file => () => import('@/views/' + file + '.vue') diff --git a/litemall-admin/src/router/index.js b/litemall-admin/src/router/index.js new file mode 100644 index 00000000..d1ec6071 --- /dev/null +++ b/litemall-admin/src/router/index.js @@ -0,0 +1,140 @@ +import Vue from 'vue' +import Router from 'vue-router' +const _import = require('./_import_' + process.env.NODE_ENV) +// in development-env not use lazy-loading, because lazy-loading too many pages will cause webpack hot update too slow. so only in production use lazy-loading; +// detail: https://panjiachen.github.io/vue-element-admin-site/#/lazy-loading + +Vue.use(Router) + +/* Layout */ +import Layout from '../views/layout/Layout' + +/** note: submenu only apppear when children.length>=1 +* detail see https://panjiachen.github.io/vue-element-admin-site/#/router-and-nav?id=sidebar +**/ + +/** +* hidden: true if `hidden:true` will not show in the sidebar(default is false) +* alwaysShow: true if set true, will always show the root menu, whatever its child routes length +* if not set alwaysShow, only more than one route under the children +* it will becomes nested mode, otherwise not show the root menu +* redirect: noredirect if `redirect:noredirect` will no redirct in the breadcrumb +* name:'router-name' the name is used by (must set!!!) +* meta : { + roles: ['admin','editor'] will control the page roles (you can set multiple roles) + title: 'title' the name show in submenu and breadcrumb (recommend set) + icon: 'svg-name' the icon show in the sidebar, + noCache: true if fasle ,the page will no be cached(default is false) + } +**/ +export const constantRouterMap = [ + { path: '/login', component: _import('login/index'), hidden: true }, + { path: '/authredirect', component: _import('login/authredirect'), hidden: true }, + { path: '/404', component: _import('error/404'), hidden: true }, + { path: '/401', component: _import('error/401'), hidden: true }, + { + path: '', + component: Layout, + redirect: 'dashboard', + children: [{ + path: 'dashboard', + component: _import('dashboard/index'), + name: 'dashboard', + meta: { title: '首页', icon: 'dashboard', noCache: true } + }] + } +] + +export default new Router({ + // mode: 'history', // require service support + scrollBehavior: () => ({ y: 0 }), + routes: constantRouterMap +}) + +export const asyncRouterMap = [ + { + path: '/user', + component: Layout, + redirect: 'noredirect', + name: 'userManage', + meta: { + title: '用户管理', + icon: 'chart' + }, + children: [ + { path: 'user', component: _import('user/user'), name: 'user', meta: { title: '会员管理', noCache: true }}, + { path: 'address', component: _import('user/address'), name: 'address', meta: { title: '收货地址', noCache: true }}, + { path: 'collect', component: _import('user/collect'), name: 'collect', meta: { title: '会员收藏', noCache: true }}, + { path: 'footprint', component: _import('user/footprint'), name: 'footprint', meta: { title: '会员足迹', noCache: true }}, + { path: 'history', component: _import('user/history'), name: 'history', meta: { title: '搜索历史', noCache: true }}, + { path: 'cart', component: _import('user/cart'), name: 'cart', meta: { title: '购物车', noCache: true }} + ] + }, + + { + path: '/mall', + component: Layout, + redirect: 'noredirect', + name: 'mallManage', + meta: { + title: '商场管理', + icon: 'chart' + }, + children: [ + { path: 'region', component: _import('mall/region'), name: 'region', meta: { title: '行政区域', noCache: true }}, + { path: 'brand', component: _import('mall/brand'), name: 'brand', meta: { title: '品牌制造商', noCache: true }}, + { path: 'category', component: _import('mall/category'), name: 'category', meta: { title: '商品类目', noCache: true }}, + { path: 'order', component: _import('mall/order'), name: 'order', meta: { title: '订单管理', noCache: true }}, + { path: 'issue', component: _import('mall/issue'), name: 'issue', meta: { title: '通用问题', noCache: true }}, + { path: 'keyword', component: _import('mall/keyword'), name: 'keyword', meta: { title: '关键词', noCache: true }} + ] + }, + + { + path: '/goods', + component: Layout, + redirect: 'noredirect', + name: 'goodsManage', + meta: { + title: '商品管理', + icon: 'chart' + }, + children: [ + { path: 'goods', component: _import('goods/goods'), name: 'goods', meta: { title: '商品管理', noCache: true }}, + { path: 'attribute', component: _import('goods/attribute'), name: 'attribute', meta: { title: '商品参数', noCache: true }}, + { path: 'specification', component: _import('goods/specification'), name: 'specification', meta: { title: '商品规格', noCache: true }}, + { path: 'product', component: _import('goods/product'), name: 'product', meta: { title: '货品管理', noCache: true }}, + { path: 'comment', component: _import('goods/comment'), name: 'comment', meta: { title: '用户评论', noCache: true }} + ] + }, + { + path: '/promotion ', + component: Layout, + redirect: 'noredirect', + name: 'promotionManage', + meta: { + title: '推广管理', + icon: 'chart' + }, + children: [ + { path: 'ad', component: _import('promotion/ad'), name: 'ad', meta: { title: '广告列表', noCache: true }}, + { path: 'topic', component: _import('promotion/topic'), name: 'topic', meta: { title: '专题管理', noCache: true }} + ] + }, + { + path: '/sys', + component: Layout, + redirect: 'noredirect', + name: 'sysManage', + meta: { + title: '系统管理', + icon: 'chart' + }, + children: [ + { path: 'admin', component: _import('sys/admin'), name: 'admin', meta: { title: '管理员', noCache: true }}, + { path: 'os', component: _import('sys/os'), name: 'os', meta: { title: '对象存储', noCache: true }} + ] + }, + + { path: '*', redirect: '/404', hidden: true } +] diff --git a/litemall-admin/src/store/getters.js b/litemall-admin/src/store/getters.js new file mode 100644 index 00000000..2227a606 --- /dev/null +++ b/litemall-admin/src/store/getters.js @@ -0,0 +1,16 @@ +const getters = { + sidebar: state => state.app.sidebar, + language: state => state.app.language, + visitedViews: state => state.tagsView.visitedViews, + cachedViews: state => state.tagsView.cachedViews, + token: state => state.user.token, + avatar: state => state.user.avatar, + name: state => state.user.name, + introduction: state => state.user.introduction, + status: state => state.user.status, + roles: state => state.user.roles, + setting: state => state.user.setting, + permission_routers: state => state.permission.routers, + addRouters: state => state.permission.addRouters +} +export default getters diff --git a/litemall-admin/src/store/index.js b/litemall-admin/src/store/index.js new file mode 100644 index 00000000..0a3fa8be --- /dev/null +++ b/litemall-admin/src/store/index.js @@ -0,0 +1,21 @@ +import Vue from 'vue' +import Vuex from 'vuex' +import app from './modules/app' +import permission from './modules/permission' +import tagsView from './modules/tagsView' +import user from './modules/user' +import getters from './getters' + +Vue.use(Vuex) + +const store = new Vuex.Store({ + modules: { + app, + permission, + tagsView, + user + }, + getters +}) + +export default store diff --git a/litemall-admin/src/store/modules/app.js b/litemall-admin/src/store/modules/app.js new file mode 100644 index 00000000..1dc7940e --- /dev/null +++ b/litemall-admin/src/store/modules/app.js @@ -0,0 +1,34 @@ +import Cookies from 'js-cookie' + +const app = { + state: { + sidebar: { + opened: !+Cookies.get('sidebarStatus') + }, + language: Cookies.get('language') || 'zh' + }, + mutations: { + TOGGLE_SIDEBAR: state => { + if (state.sidebar.opened) { + Cookies.set('sidebarStatus', 1) + } else { + Cookies.set('sidebarStatus', 0) + } + state.sidebar.opened = !state.sidebar.opened + }, + SET_LANGUAGE: (state, language) => { + state.language = language + Cookies.set('language', language) + } + }, + actions: { + toggleSideBar({ commit }) { + commit('TOGGLE_SIDEBAR') + }, + setLanguage({ commit }, language) { + commit('SET_LANGUAGE', language) + } + } +} + +export default app diff --git a/litemall-admin/src/store/modules/permission.js b/litemall-admin/src/store/modules/permission.js new file mode 100644 index 00000000..ce1aafa6 --- /dev/null +++ b/litemall-admin/src/store/modules/permission.js @@ -0,0 +1,62 @@ +import { asyncRouterMap, constantRouterMap } from '@/router' + +/** + * 通过meta.role判断是否与当前用户权限匹配 + * @param roles + * @param route + */ +function hasPermission(roles, route) { + if (route.meta && route.meta.roles) { + return roles.some(role => route.meta.roles.indexOf(role) >= 0) + } else { + return true + } +} + +/** + * 递归过滤异步路由表,返回符合用户角色权限的路由表 + * @param asyncRouterMap + * @param roles + */ +function filterAsyncRouter(asyncRouterMap, roles) { + const accessedRouters = asyncRouterMap.filter(route => { + if (hasPermission(roles, route)) { + if (route.children && route.children.length) { + route.children = filterAsyncRouter(route.children, roles) + } + return true + } + return false + }) + return accessedRouters +} + +const permission = { + state: { + routers: constantRouterMap, + addRouters: [] + }, + mutations: { + SET_ROUTERS: (state, routers) => { + state.addRouters = routers + state.routers = constantRouterMap.concat(routers) + } + }, + actions: { + GenerateRoutes({ commit }, data) { + return new Promise(resolve => { + const { roles } = data + let accessedRouters + if (roles.indexOf('admin') >= 0) { + accessedRouters = asyncRouterMap + } else { + accessedRouters = filterAsyncRouter(asyncRouterMap, roles) + } + commit('SET_ROUTERS', accessedRouters) + resolve() + }) + } + } +} + +export default permission diff --git a/litemall-admin/src/store/modules/tagsView.js b/litemall-admin/src/store/modules/tagsView.js new file mode 100644 index 00000000..273d33bd --- /dev/null +++ b/litemall-admin/src/store/modules/tagsView.js @@ -0,0 +1,78 @@ +const tagsView = { + state: { + visitedViews: [], + cachedViews: [] + }, + mutations: { + ADD_VISITED_VIEWS: (state, view) => { + if (state.visitedViews.some(v => v.path === view.path)) return + state.visitedViews.push({ + name: view.name, + path: view.path, + title: view.meta.title || 'no-name' + }) + if (!view.meta.noCache) { + state.cachedViews.push(view.name) + } + }, + DEL_VISITED_VIEWS: (state, view) => { + for (const [i, v] of state.visitedViews.entries()) { + if (v.path === view.path) { + state.visitedViews.splice(i, 1) + break + } + } + for (const i of state.cachedViews) { + if (i === view.name) { + const index = state.cachedViews.indexOf(i) + state.cachedViews.splice(index, 1) + break + } + } + }, + DEL_OTHERS_VIEWS: (state, view) => { + for (const [i, v] of state.visitedViews.entries()) { + if (v.path === view.path) { + state.visitedViews = state.visitedViews.slice(i, i + 1) + break + } + } + for (const i of state.cachedViews) { + if (i === view.name) { + const index = state.cachedViews.indexOf(i) + state.cachedViews = state.cachedViews.slice(index, i + 1) + break + } + } + }, + DEL_ALL_VIEWS: (state) => { + state.visitedViews = [] + state.cachedViews = [] + } + }, + actions: { + addVisitedViews({ commit }, view) { + commit('ADD_VISITED_VIEWS', view) + }, + delVisitedViews({ commit, state }, view) { + return new Promise((resolve) => { + commit('DEL_VISITED_VIEWS', view) + resolve([...state.visitedViews]) + }) + }, + delOthersViews({ commit, state }, view) { + return new Promise((resolve) => { + commit('DEL_OTHERS_VIEWS', view) + resolve([...state.visitedViews]) + }) + }, + delAllViews({ commit, state }) { + return new Promise((resolve) => { + commit('DEL_ALL_VIEWS') + resolve([...state.visitedViews]) + }) + } + } +} + +export default tagsView diff --git a/litemall-admin/src/store/modules/user.js b/litemall-admin/src/store/modules/user.js new file mode 100644 index 00000000..5a8acdbe --- /dev/null +++ b/litemall-admin/src/store/modules/user.js @@ -0,0 +1,133 @@ +import { loginByUsername, logout, getUserInfo } from '@/api/login' +import { getToken, setToken, removeToken } from '@/utils/auth' + +const user = { + state: { + user: '', + status: '', + code: '', + token: getToken(), + name: '', + avatar: '', + introduction: '', + roles: [], + setting: { + articlePlatform: [] + } + }, + + mutations: { + SET_CODE: (state, code) => { + state.code = code + }, + SET_TOKEN: (state, token) => { + state.token = token + }, + SET_INTRODUCTION: (state, introduction) => { + state.introduction = introduction + }, + SET_SETTING: (state, setting) => { + state.setting = setting + }, + SET_STATUS: (state, status) => { + state.status = status + }, + SET_NAME: (state, name) => { + state.name = name + }, + SET_AVATAR: (state, avatar) => { + state.avatar = avatar + }, + SET_ROLES: (state, roles) => { + state.roles = roles + } + }, + + actions: { + // 用户名登录 + LoginByUsername({ commit }, userInfo) { + const username = userInfo.username.trim() + return new Promise((resolve, reject) => { + loginByUsername(username, userInfo.password).then(response => { + const token = response.data.data + commit('SET_TOKEN', token) + setToken(token) + resolve() + }).catch(error => { + reject(error) + }) + }) + }, + + // 获取用户信息 + GetUserInfo({ commit, state }) { + return new Promise((resolve, reject) => { + getUserInfo(state.token).then(response => { + const data = response.data.data + commit('SET_ROLES', data.roles) + commit('SET_NAME', data.name) + commit('SET_AVATAR', data.avatar) + commit('SET_INTRODUCTION', data.introduction) + resolve(response) + }).catch(error => { + reject(error) + }) + }) + }, + + // 第三方验证登录 + // LoginByThirdparty({ commit, state }, code) { + // return new Promise((resolve, reject) => { + // commit('SET_CODE', code) + // loginByThirdparty(state.status, state.email, state.code).then(response => { + // commit('SET_TOKEN', response.data.token) + // setToken(response.data.token) + // resolve() + // }).catch(error => { + // reject(error) + // }) + // }) + // }, + + // 登出 + LogOut({ commit, state }) { + return new Promise((resolve, reject) => { + logout(state.token).then(() => { + commit('SET_TOKEN', '') + commit('SET_ROLES', []) + removeToken() + resolve() + }).catch(error => { + reject(error) + }) + }) + }, + + // 前端 登出 + FedLogOut({ commit }) { + return new Promise(resolve => { + commit('SET_TOKEN', '') + removeToken() + resolve() + }) + }, + + // 动态修改权限 + ChangeRoles({ commit }, role) { + return new Promise(resolve => { + commit('SET_TOKEN', role) + setToken(role) + getUserInfo(role).then(response => { + const data = response.data.data + commit('SET_ROLES', data.roles) + commit('SET_NAME', data.name) + commit('SET_AVATAR', data.avatar) + commit('SET_INTRODUCTION', data.introduction) + resolve() + }) + }) + } + } +} + +export default user diff --git a/litemall-admin/src/styles/btn.scss b/litemall-admin/src/styles/btn.scss new file mode 100644 index 00000000..f3f75c16 --- /dev/null +++ b/litemall-admin/src/styles/btn.scss @@ -0,0 +1,95 @@ +@import './variables.scss'; + +@mixin colorBtn($color) { + background: $color; + &:hover { + color: $color; + &:before, + &:after { + background: $color; + } + } +} + +.blue-btn { + @include colorBtn($blue) +} + +.light-blue-btn { + @include colorBtn($light-blue) +} + +.red-btn { + @include colorBtn($red) +} + +.pink-btn { + @include colorBtn($pink) +} + +.green-btn { + @include colorBtn($green) +} + +.tiffany-btn { + @include colorBtn($tiffany) +} + +.yellow-btn { + @include colorBtn($yellow) +} + +.pan-btn { + font-size: 14px; + color: #fff; + padding: 14px 36px; + border-radius: 8px; + border: none; + outline: none; + margin-right: 25px; + transition: 600ms ease all; + position: relative; + display: inline-block; + &:hover { + background: #fff; + &:before, + &:after { + width: 100%; + transition: 600ms ease all; + } + } + &:before, + &:after { + content: ''; + position: absolute; + top: 0; + right: 0; + height: 2px; + width: 0; + transition: 400ms ease all; + } + &::after { + right: inherit; + top: inherit; + left: 0; + bottom: 0; + } +} + +.custom-button { + display: inline-block; + line-height: 1; + white-space: nowrap; + cursor: pointer; + background: #fff; + color: #fff; + -webkit-appearance: none; + text-align: center; + box-sizing: border-box; + outline: 0; + margin: 0; + padding: 10px 15px; + font-size: 14px; + border-radius: 4px; +} + diff --git a/litemall-admin/src/styles/element-ui.scss b/litemall-admin/src/styles/element-ui.scss new file mode 100644 index 00000000..08a7388a --- /dev/null +++ b/litemall-admin/src/styles/element-ui.scss @@ -0,0 +1,75 @@ + //覆盖一些element-ui样式 + + .el-breadcrumb__inner, .el-breadcrumb__inner a{ + font-weight: 400!important; +} + + .el-upload { + input[type="file"] { + display: none !important; + } + } + + .el-upload__input { + display: none; + } + + .cell { + .el-tag { + margin-right: 0px; + } + } + + .small-padding { + .cell { + padding-left: 5px; + padding-right: 5px; + } + } + + .fixed-width{ + .el-button--mini{ + padding: 7px 10px; + width: 60px; + } + } + + .status-col { + .cell { + padding: 0 10px; + text-align: center; + .el-tag { + margin-right: 0px; + } + } + } + + //暂时性解决diolag 问题 https://github.com/ElemeFE/element/issues/2461 + .el-dialog { + transform: none; + left: 0; + position: relative; + margin: 0 auto; + } + + //文章页textarea修改样式 + .article-textarea { + textarea { + padding-right: 40px; + resize: none; + border: none; + border-radius: 0px; + border-bottom: 1px solid #bfcbd9; + } + } + + //element ui upload + .upload-container { + .el-upload { + width: 100%; + .el-upload-dragger { + width: 100%; + height: 200px; + } + } + } diff --git a/litemall-admin/src/styles/index.scss b/litemall-admin/src/styles/index.scss new file mode 100644 index 00000000..b6fd924e --- /dev/null +++ b/litemall-admin/src/styles/index.scss @@ -0,0 +1,193 @@ +@import './variables.scss'; +@import './mixin.scss'; +@import './transition.scss'; +@import './element-ui.scss'; +@import './sidebar.scss'; +@import './btn.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; +} + +.no-padding { + padding: 0px !important; +} + +.padding-content { + padding: 4px 0; +} + +a:focus, +a:active { + outline: none; +} + +a, +a:focus, +a:hover { + cursor: pointer; + color: inherit; + text-decoration: none; +} + +div:focus{ + outline: none; + } + +.fr { + float: right; +} + +.fl { + float: left; +} + +.pr-5 { + padding-right: 5px; +} + +.pl-5 { + padding-left: 5px; +} + +.block { + display: block; +} + +.pointer { + cursor: pointer; +} + +.inlineBlock { + display: block; +} + +.clearfix { + &:after { + visibility: hidden; + display: block; + font-size: 0; + content: " "; + clear: both; + height: 0; + } +} + +code { + background: #eef1f6; + padding: 15px 16px; + margin-bottom: 20px; + display: block; + line-height: 36px; + font-size: 15px; + font-family: "Source Sans Pro", "Helvetica Neue", Arial, sans-serif; + a { + color: #337ab7; + cursor: pointer; + &:hover { + color: rgb(32, 160, 255); + } + } +} + +.warn-content{ + background: rgba(66,185,131,.1); + border-radius: 2px; + padding: 16px; + padding: 1rem; + line-height: 1.6rem; + word-spacing: .05rem; + a{ + color: #42b983; + font-weight: 600; + } +} + +//main-container全局样式 +.app-container { + padding: 20px; +} + +.components-container { + margin: 30px 50px; + position: relative; +} + +.pagination-container { + margin-top: 30px; +} + +.text-center { + text-align: center +} + +.sub-navbar { + height: 50px; + line-height: 50px; + position: relative; + width: 100%; + text-align: right; + padding-right: 20px; + transition: 600ms ease position; + background: linear-gradient(90deg, rgba(32, 182, 249, 1) 0%, rgba(32, 182, 249, 1) 0%, rgba(33, 120, 241, 1) 100%, rgba(33, 120, 241, 1) 100%); + .subtitle { + font-size: 20px; + color: #fff; + } + &.draft { + background: #d0d0d0; + } + &.deleted { + background: #d0d0d0; + } +} + +.link-type, +.link-type:focus { + color: #337ab7; + cursor: pointer; + &:hover { + color: rgb(32, 160, 255); + } +} + +.filter-container { + padding-bottom: 10px; + .filter-item { + display: inline-block; + vertical-align: middle; + margin-bottom: 10px; + } +} + +//refine vue-multiselect plugin +.multiselect { + line-height: 16px; +} + +.multiselect--active { + z-index: 1000 !important; +} diff --git a/litemall-admin/src/styles/mixin.scss b/litemall-admin/src/styles/mixin.scss new file mode 100644 index 00000000..822ab92a --- /dev/null +++ b/litemall-admin/src/styles/mixin.scss @@ -0,0 +1,60 @@ +@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%; +} + +@mixin pct($pct) { + width: #{$pct}; + position: relative; + margin: 0 auto; +} + +@mixin triangle($width, $height, $color, $direction) { + $width: $width/2; + $color-border-style: $height solid $color; + $transparent-border-style: $width solid transparent; + height: 0; + width: 0; + @if $direction==up { + border-bottom: $color-border-style; + border-left: $transparent-border-style; + border-right: $transparent-border-style; + } + @else if $direction==right { + border-left: $color-border-style; + border-top: $transparent-border-style; + border-bottom: $transparent-border-style; + } + @else if $direction==down { + border-top: $color-border-style; + border-left: $transparent-border-style; + border-right: $transparent-border-style; + } + @else if $direction==left { + border-right: $color-border-style; + border-top: $transparent-border-style; + border-bottom: $transparent-border-style; + } +} diff --git a/litemall-admin/src/styles/sidebar.scss b/litemall-admin/src/styles/sidebar.scss new file mode 100644 index 00000000..2fed2b1d --- /dev/null +++ b/litemall-admin/src/styles/sidebar.scss @@ -0,0 +1,100 @@ +#app { + // 主体区域 + .main-container { + min-height: 100%; + transition: margin-left 0.28s; + margin-left: 180px; + } // 侧边栏 + .sidebar-container { + transition: width 0.28s; + width: 180px!important; + height: 100%; + position: fixed; + top: 0; + bottom: 0; + left: 0; + z-index: 1001; + a { + display: inline-block; + width: 100%; + } + .svg-icon { + margin-right: 16px; + } + .el-menu { + border: none; + width: 100%; + } + } + .hideSidebar { + .sidebar-container,.sidebar-container .el-menu { + width: 36px!important; + // overflow: inherit; + } + .main-container { + margin-left: 36px; + } + } + .hideSidebar { + .submenu-title-noDropdown { + padding-left: 10px!important; + position: relative; + span { + height: 0; + width: 0; + overflow: hidden; + visibility: hidden; + transition: opacity .3s cubic-bezier(.55, 0, .1, 1); + opacity: 0; + display: inline-block; + } + &:hover { + span { + display: block; + border-radius: 3px; + z-index: 1002; + width: 140px; + height: 56px; + visibility: visible; + position: absolute; + right: -145px; + text-align: left; + text-indent: 20px; + top: 0px; + background-color: $subMenuBg!important; + opacity: 1; + } + } + } + .el-submenu { + &>.el-submenu__title { + padding-left: 10px!important; + &>span { + display: none; + } + .el-submenu__icon-arrow { + display: none; + } + } + .nest-menu { + .el-submenu__icon-arrow { + display: block!important; + } + span { + display: inline-block!important; + } + } + } + } + .nest-menu .el-submenu>.el-submenu__title, + .el-submenu .el-menu-item { + min-width: 180px!important; + background-color: $subMenuBg!important; + &:hover { + background-color: $menuHover!important; + } + } + .el-menu--collapse .el-menu .el-submenu{ + min-width: 180px!important; + } +} diff --git a/litemall-admin/src/styles/transition.scss b/litemall-admin/src/styles/transition.scss new file mode 100644 index 00000000..85c03286 --- /dev/null +++ b/litemall-admin/src/styles/transition.scss @@ -0,0 +1,33 @@ +//globl transition css + +/*fade*/ +.fade-enter-active, +.fade-leave-active { + transition: opacity 0.28s; +} + +.fade-enter, +.fade-leave-active { + opacity: 0; +} + +/*fade*/ +.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/litemall-admin/src/styles/variables.scss b/litemall-admin/src/styles/variables.scss new file mode 100644 index 00000000..acc77a82 --- /dev/null +++ b/litemall-admin/src/styles/variables.scss @@ -0,0 +1,13 @@ +$blue:#324157; +$light-blue:#3A71A8; +$red:#C03639; +$pink: #E65D6E; +$green: #30B08F; +$tiffany: #4AB7BD; +$yellow:#FEC171; +$panGreen: #30B08F; + +//sidebar +$menuBg:#304156; +$subMenuBg:#1f2d3d; +$menuHover:#001528; diff --git a/litemall-admin/src/utils/auth.js b/litemall-admin/src/utils/auth.js new file mode 100644 index 00000000..08a43d6e --- /dev/null +++ b/litemall-admin/src/utils/auth.js @@ -0,0 +1,15 @@ +import Cookies from 'js-cookie' + +const TokenKey = 'Admin-Token' + +export function getToken() { + return Cookies.get(TokenKey) +} + +export function setToken(token) { + return Cookies.set(TokenKey, token) +} + +export function removeToken() { + return Cookies.remove(TokenKey) +} diff --git a/litemall-admin/src/utils/index.js b/litemall-admin/src/utils/index.js new file mode 100644 index 00000000..2140fdff --- /dev/null +++ b/litemall-admin/src/utils/index.js @@ -0,0 +1,267 @@ +/** + * Created by jiachenpan on 16/11/18. + */ + +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 (('' + time).length === 10) time = parseInt(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] + if (key === 'a') return ['一', '二', '三', '四', '五', '六', '日'][value - 1] + if (result.length > 0 && value < 10) { + value = '0' + value + } + return value || 0 + }) + return time_str +} + +export function formatTime(time, option) { + time = +time * 1000 + 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() + '分' + } +} + +// 格式化时间 +export function getQueryObject(url) { + url = url == null ? window.location.href : url + const search = url.substring(url.lastIndexOf('?') + 1) + const obj = {} + const reg = /([^?&=]+)=([^?&=]*)/g + search.replace(reg, (rs, $1, $2) => { + const name = decodeURIComponent($1) + let val = decodeURIComponent($2) + val = String(val) + obj[name] = val + return rs + }) + return obj +} + +/** + *get getByteLen + * @param {Sting} val input value + * @returns {number} output value + */ +export function getByteLen(val) { + let len = 0 + for (let i = 0; i < val.length; i++) { + if (val[i].match(/[^\x00-\xff]/ig) != null) { + len += 1 + } else { len += 0.5 } + } + return Math.floor(len) +} + +export function cleanArray(actual) { + const newArray = [] + for (let i = 0; i < actual.length; i++) { + if (actual[i]) { + newArray.push(actual[i]) + } + } + return newArray +} + +export function param(json) { + if (!json) return '' + return cleanArray(Object.keys(json).map(key => { + if (json[key] === undefined) return '' + return encodeURIComponent(key) + '=' + + encodeURIComponent(json[key]) + })).join('&') +} + +export function param2Obj(url) { + const search = url.split('?')[1] + if (!search) { + return {} + } + return JSON.parse('{"' + decodeURIComponent(search).replace(/"/g, '\\"').replace(/&/g, '","').replace(/=/g, '":"') + '"}') +} + +export function html2Text(val) { + const div = document.createElement('div') + div.innerHTML = val + return div.textContent || div.innerText +} + +export function objectMerge(target, source) { + /* Merges two objects, + giving the last one precedence */ + + if (typeof target !== 'object') { + target = {} + } + if (Array.isArray(source)) { + return source.slice() + } + for (const property in source) { + if (source.hasOwnProperty(property)) { + const sourceProperty = source[property] + if (typeof sourceProperty === 'object') { + target[property] = objectMerge(target[property], sourceProperty) + continue + } + target[property] = sourceProperty + } + } + return target +} + +export function scrollTo(element, to, duration) { + if (duration <= 0) return + const difference = to - element.scrollTop + const perTick = difference / duration * 10 + setTimeout(() => { + console.log(new Date()) + element.scrollTop = element.scrollTop + perTick + if (element.scrollTop === to) return + scrollTo(element, to, duration - 10) + }, 10) +} + +export function toggleClass(element, className) { + if (!element || !className) { + return + } + let classString = element.className + const nameIndex = classString.indexOf(className) + if (nameIndex === -1) { + classString += '' + className + } else { + classString = classString.substr(0, nameIndex) + classString.substr(nameIndex + className.length) + } + element.className = classString +} + +export const pickerOptions = [ + { + text: '今天', + onClick(picker) { + const end = new Date() + const start = new Date(new Date().toDateString()) + end.setTime(start.getTime()) + picker.$emit('pick', [start, end]) + } + }, { + text: '最近一周', + onClick(picker) { + const end = new Date(new Date().toDateString()) + const start = new Date() + start.setTime(end.getTime() - 3600 * 1000 * 24 * 7) + picker.$emit('pick', [start, end]) + } + }, { + text: '最近一个月', + onClick(picker) { + const end = new Date(new Date().toDateString()) + const start = new Date() + start.setTime(start.getTime() - 3600 * 1000 * 24 * 30) + picker.$emit('pick', [start, end]) + } + }, { + text: '最近三个月', + onClick(picker) { + const end = new Date(new Date().toDateString()) + const start = new Date() + start.setTime(start.getTime() - 3600 * 1000 * 24 * 90) + picker.$emit('pick', [start, end]) + } + }] + +export function getTime(type) { + if (type === 'start') { + return new Date().getTime() - 3600 * 1000 * 24 * 90 + } else { + return new Date(new Date().toDateString()) + } +} + +export function debounce(func, wait, immediate) { + let timeout, args, context, timestamp, result + + const later = function() { + // 据上一次触发时间间隔 + const last = +new Date() - timestamp + + // 上次被包装函数被调用时间间隔last小于设定时间间隔wait + if (last < wait && last > 0) { + timeout = setTimeout(later, wait - last) + } else { + timeout = null + // 如果设定为immediate===true,因为开始边界已经调用过了此处无需调用 + if (!immediate) { + result = func.apply(context, args) + if (!timeout) context = args = null + } + } + } + + return function(...args) { + context = this + timestamp = +new Date() + const callNow = immediate && !timeout + // 如果延时不存在,重新设定延时 + if (!timeout) timeout = setTimeout(later, wait) + if (callNow) { + result = func.apply(context, args) + context = args = null + } + + return result + } +} + +export function deepClone(source) { + if (!source && typeof source !== 'object') { + throw new Error('error arguments', 'shallowClone') + } + const targetObj = source.constructor === Array ? [] : {} + for (const keys in source) { + if (source.hasOwnProperty(keys)) { + if (source[keys] && typeof source[keys] === 'object') { + targetObj[keys] = source[keys].constructor === Array ? [] : {} + targetObj[keys] = deepClone(source[keys]) + } else { + targetObj[keys] = source[keys] + } + } + } + return targetObj +} diff --git a/litemall-admin/src/utils/openWindow.js b/litemall-admin/src/utils/openWindow.js new file mode 100644 index 00000000..b63dfbb4 --- /dev/null +++ b/litemall-admin/src/utils/openWindow.js @@ -0,0 +1,26 @@ +/** + *Created by jiachenpan on 16/11/29. + * @param {Sting} url + * @param {Sting} title + * @param {Number} w + * @param {Number} h + */ + +export default function openWindow(url, title, w, h) { + // Fixes dual-screen position Most browsers Firefox + const dualScreenLeft = window.screenLeft !== undefined ? window.screenLeft : screen.left + const dualScreenTop = window.screenTop !== undefined ? window.screenTop : screen.top + + const width = window.innerWidth ? window.innerWidth : document.documentElement.clientWidth ? document.documentElement.clientWidth : screen.width + const height = window.innerHeight ? window.innerHeight : document.documentElement.clientHeight ? document.documentElement.clientHeight : screen.height + + const left = ((width / 2) - (w / 2)) + dualScreenLeft + const top = ((height / 2) - (h / 2)) + dualScreenTop + const newWindow = window.open(url, title, 'toolbar=no, location=no, directories=no, status=no, menubar=no, scrollbars=no, resizable=yes, copyhistory=no, width=' + w + ', height=' + h + ', top=' + top + ', left=' + left) + + // Puts focus on the newWindow + if (window.focus) { + newWindow.focus() + } +} + diff --git a/litemall-admin/src/utils/request.js b/litemall-admin/src/utils/request.js new file mode 100644 index 00000000..68e4d5b1 --- /dev/null +++ b/litemall-admin/src/utils/request.js @@ -0,0 +1,61 @@ +import axios from 'axios' +import { Message } from 'element-ui' +import store from '@/store' +import { getToken } from '@/utils/auth' + +// create an axios instance +const service = axios.create({ + baseURL: process.env.BASE_API, // api的base_url + timeout: 5000 // request timeout +}) + +// request interceptor +service.interceptors.request.use(config => { + // Do something before request is sent + if (store.getters.token) { + config.headers['X-Token'] = getToken() // 让每个请求携带token-- ['X-Token']为自定义key 请根据实际情况自行修改 + } + return config +}, error => { + // Do something with request error + console.log(error) // for debug + Promise.reject(error) +}) + +// respone interceptor +service.interceptors.response.use( + response => { + const res = response.data + if (res.errno !== 0) { + Message({ + message: res.errno + ' ' + res.errmsg, + type: 'error', + duration: 5 * 1000 + }) + // 50008:非法的token; 50012:其他客户端登录了; 50014:Token 过期了; + // if (res.errno === 50008 || res.errno === 50012 || res.errno === 50014) { + // Message.confirm('你已被登出,可以取消继续留在该页面,或者重新登录', '确定登出', { + // confirmButtonText: '重新登录', + // cancelButtonText: '取消', + // type: 'warning' + // }).then(() => { + // store.dispatch('FedLogOut').then(() => { + // location.reload() // 为了重新实例化vue-router对象 避免bug + // }) + // }) + // } + return Promise.reject('error') + } else { + return response + } + }, 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/litemall-admin/src/utils/validate.js b/litemall-admin/src/utils/validate.js new file mode 100644 index 00000000..a0ca0497 --- /dev/null +++ b/litemall-admin/src/utils/validate.js @@ -0,0 +1,38 @@ +/** + * Created by jiachenpan on 16/11/18. + */ + +/* 合法uri*/ +export function validateURL(textval) { + const urlregex = /^(https?|ftp):\/\/([a-zA-Z0-9.-]+(:[a-zA-Z0-9.&%$-]+)*@)*((25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9][0-9]?)(\.(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3}|([a-zA-Z0-9-]+\.)*[a-zA-Z0-9-]+\.(com|edu|gov|int|mil|net|org|biz|arpa|info|name|pro|aero|coop|museum|[a-zA-Z]{2}))(:[0-9]+)*(\/($|[a-zA-Z0-9.,?'\\+&%$#=~_-]+))*$/ + return urlregex.test(textval) +} + +/* 小写字母*/ +export function validateLowerCase(str) { + const reg = /^[a-z]+$/ + return reg.test(str) +} + +/* 大写字母*/ +export function validateUpperCase(str) { + const reg = /^[A-Z]+$/ + return reg.test(str) +} + +/* 大小写字母*/ +export function validatAlphabets(str) { + const reg = /^[A-Za-z]+$/ + return reg.test(str) +} + +/** + * validate email + * @param email + * @returns {boolean} + */ +export function validateEmail(email) { + const re = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/ + return re.test(email) +} + diff --git a/litemall-admin/src/vendor/Blob.js b/litemall-admin/src/vendor/Blob.js new file mode 100644 index 00000000..26382ccd --- /dev/null +++ b/litemall-admin/src/vendor/Blob.js @@ -0,0 +1,179 @@ +/* eslint-disable */ +/* Blob.js + * A Blob implementation. + * 2014-05-27 + * + * By Eli Grey, http://eligrey.com + * By Devin Samarin, https://github.com/eboyjr + * License: X11/MIT + * See LICENSE.md + */ + +/*global self, unescape */ +/*jslint bitwise: true, regexp: true, confusion: true, es5: true, vars: true, white: true, + plusplus: true */ + +/*! @source http://purl.eligrey.com/github/Blob.js/blob/master/Blob.js */ + +(function (view) { + "use strict"; + + view.URL = view.URL || view.webkitURL; + + if (view.Blob && view.URL) { + try { + new Blob; + return; + } catch (e) {} + } + + // Internally we use a BlobBuilder implementation to base Blob off of + // in order to support older browsers that only have BlobBuilder + var BlobBuilder = view.BlobBuilder || view.WebKitBlobBuilder || view.MozBlobBuilder || (function(view) { + var + get_class = function(object) { + return Object.prototype.toString.call(object).match(/^\[object\s(.*)\]$/)[1]; + } + , FakeBlobBuilder = function BlobBuilder() { + this.data = []; + } + , FakeBlob = function Blob(data, type, encoding) { + this.data = data; + this.size = data.length; + this.type = type; + this.encoding = encoding; + } + , FBB_proto = FakeBlobBuilder.prototype + , FB_proto = FakeBlob.prototype + , FileReaderSync = view.FileReaderSync + , FileException = function(type) { + this.code = this[this.name = type]; + } + , file_ex_codes = ( + "NOT_FOUND_ERR SECURITY_ERR ABORT_ERR NOT_READABLE_ERR ENCODING_ERR " + + "NO_MODIFICATION_ALLOWED_ERR INVALID_STATE_ERR SYNTAX_ERR" + ).split(" ") + , file_ex_code = file_ex_codes.length + , real_URL = view.URL || view.webkitURL || view + , real_create_object_URL = real_URL.createObjectURL + , real_revoke_object_URL = real_URL.revokeObjectURL + , URL = real_URL + , btoa = view.btoa + , atob = view.atob + + , ArrayBuffer = view.ArrayBuffer + , Uint8Array = view.Uint8Array + ; + FakeBlob.fake = FB_proto.fake = true; + while (file_ex_code--) { + FileException.prototype[file_ex_codes[file_ex_code]] = file_ex_code + 1; + } + if (!real_URL.createObjectURL) { + URL = view.URL = {}; + } + URL.createObjectURL = function(blob) { + var + type = blob.type + , data_URI_header + ; + if (type === null) { + type = "application/octet-stream"; + } + if (blob instanceof FakeBlob) { + data_URI_header = "data:" + type; + if (blob.encoding === "base64") { + return data_URI_header + ";base64," + blob.data; + } else if (blob.encoding === "URI") { + return data_URI_header + "," + decodeURIComponent(blob.data); + } if (btoa) { + return data_URI_header + ";base64," + btoa(blob.data); + } else { + return data_URI_header + "," + encodeURIComponent(blob.data); + } + } else if (real_create_object_URL) { + return real_create_object_URL.call(real_URL, blob); + } + }; + URL.revokeObjectURL = function(object_URL) { + if (object_URL.substring(0, 5) !== "data:" && real_revoke_object_URL) { + real_revoke_object_URL.call(real_URL, object_URL); + } + }; + FBB_proto.append = function(data/*, endings*/) { + var bb = this.data; + // decode data to a binary string + if (Uint8Array && (data instanceof ArrayBuffer || data instanceof Uint8Array)) { + var + str = "" + , buf = new Uint8Array(data) + , i = 0 + , buf_len = buf.length + ; + for (; i < buf_len; i++) { + str += String.fromCharCode(buf[i]); + } + bb.push(str); + } else if (get_class(data) === "Blob" || get_class(data) === "File") { + if (FileReaderSync) { + var fr = new FileReaderSync; + bb.push(fr.readAsBinaryString(data)); + } else { + // async FileReader won't work as BlobBuilder is sync + throw new FileException("NOT_READABLE_ERR"); + } + } else if (data instanceof FakeBlob) { + if (data.encoding === "base64" && atob) { + bb.push(atob(data.data)); + } else if (data.encoding === "URI") { + bb.push(decodeURIComponent(data.data)); + } else if (data.encoding === "raw") { + bb.push(data.data); + } + } else { + if (typeof data !== "string") { + data += ""; // convert unsupported types to strings + } + // decode UTF-16 to binary string + bb.push(unescape(encodeURIComponent(data))); + } + }; + FBB_proto.getBlob = function(type) { + if (!arguments.length) { + type = null; + } + return new FakeBlob(this.data.join(""), type, "raw"); + }; + FBB_proto.toString = function() { + return "[object BlobBuilder]"; + }; + FB_proto.slice = function(start, end, type) { + var args = arguments.length; + if (args < 3) { + type = null; + } + return new FakeBlob( + this.data.slice(start, args > 1 ? end : this.data.length) + , type + , this.encoding + ); + }; + FB_proto.toString = function() { + return "[object Blob]"; + }; + FB_proto.close = function() { + this.size = this.data.length = 0; + }; + return FakeBlobBuilder; + }(view)); + + view.Blob = function Blob(blobParts, options) { + var type = options ? (options.type || "") : ""; + var builder = new BlobBuilder(); + if (blobParts) { + for (var i = 0, len = blobParts.length; i < len; i++) { + builder.append(blobParts[i]); + } + } + return builder.getBlob(type); + }; +}(typeof self !== "undefined" && self || typeof window !== "undefined" && window || this.content || this)); diff --git a/litemall-admin/src/vendor/Export2Excel.js b/litemall-admin/src/vendor/Export2Excel.js new file mode 100644 index 00000000..caca3aa6 --- /dev/null +++ b/litemall-admin/src/vendor/Export2Excel.js @@ -0,0 +1,171 @@ +/* eslint-disable */ +require('script-loader!file-saver'); +require('script-loader!@/vendor/Blob'); +import XLSX from 'xlsx' + +function generateArray(table) { + var out = []; + var rows = table.querySelectorAll('tr'); + var ranges = []; + for (var R = 0; R < rows.length; ++R) { + var outRow = []; + var row = rows[R]; + var columns = row.querySelectorAll('td'); + for (var C = 0; C < columns.length; ++C) { + var cell = columns[C]; + var colspan = cell.getAttribute('colspan'); + var rowspan = cell.getAttribute('rowspan'); + var cellValue = cell.innerText; + if (cellValue !== "" && cellValue == +cellValue) cellValue = +cellValue; + + //Skip ranges + ranges.forEach(function (range) { + if (R >= range.s.r && R <= range.e.r && outRow.length >= range.s.c && outRow.length <= range.e.c) { + for (var i = 0; i <= range.e.c - range.s.c; ++i) outRow.push(null); + } + }); + + //Handle Row Span + if (rowspan || colspan) { + rowspan = rowspan || 1; + colspan = colspan || 1; + ranges.push({s: {r: R, c: outRow.length}, e: {r: R + rowspan - 1, c: outRow.length + colspan - 1}}); + } + ; + + //Handle Value + outRow.push(cellValue !== "" ? cellValue : null); + + //Handle Colspan + if (colspan) for (var k = 0; k < colspan - 1; ++k) outRow.push(null); + } + out.push(outRow); + } + return [out, ranges]; +}; + +function datenum(v, date1904) { + if (date1904) v += 1462; + var epoch = Date.parse(v); + return (epoch - new Date(Date.UTC(1899, 11, 30))) / (24 * 60 * 60 * 1000); +} + +function sheet_from_array_of_arrays(data, opts) { + var ws = {}; + var range = {s: {c: 10000000, r: 10000000}, e: {c: 0, r: 0}}; + for (var R = 0; R != data.length; ++R) { + for (var C = 0; C != data[R].length; ++C) { + if (range.s.r > R) range.s.r = R; + if (range.s.c > C) range.s.c = C; + if (range.e.r < R) range.e.r = R; + if (range.e.c < C) range.e.c = C; + var cell = {v: data[R][C]}; + if (cell.v == null) continue; + var cell_ref = XLSX.utils.encode_cell({c: C, r: R}); + + if (typeof cell.v === 'number') cell.t = 'n'; + else if (typeof cell.v === 'boolean') cell.t = 'b'; + else if (cell.v instanceof Date) { + cell.t = 'n'; + cell.z = XLSX.SSF._table[14]; + cell.v = datenum(cell.v); + } + else cell.t = 's'; + + ws[cell_ref] = cell; + } + } + if (range.s.c < 10000000) ws['!ref'] = XLSX.utils.encode_range(range); + return ws; +} + +function Workbook() { + if (!(this instanceof Workbook)) return new Workbook(); + this.SheetNames = []; + this.Sheets = {}; +} + +function s2ab(s) { + var buf = new ArrayBuffer(s.length); + var view = new Uint8Array(buf); + for (var i = 0; i != s.length; ++i) view[i] = s.charCodeAt(i) & 0xFF; + return buf; +} + +export function export_table_to_excel(id) { + var theTable = document.getElementById(id); + var oo = generateArray(theTable); + var ranges = oo[1]; + + /* original data */ + var data = oo[0]; + var ws_name = "SheetJS"; + + var wb = new Workbook(), ws = sheet_from_array_of_arrays(data); + + /* add ranges to worksheet */ + // ws['!cols'] = ['apple', 'banan']; + ws['!merges'] = ranges; + + /* add worksheet to workbook */ + wb.SheetNames.push(ws_name); + wb.Sheets[ws_name] = ws; + + var wbout = XLSX.write(wb, {bookType: 'xlsx', bookSST: false, type: 'binary'}); + + saveAs(new Blob([s2ab(wbout)], {type: "application/octet-stream"}), "test.xlsx") +} + +export function export_json_to_excel(th, jsonData, defaultTitle) { + + /* original data */ + + var data = jsonData; + data.unshift(th); + var ws_name = "SheetJS"; + + var wb = new Workbook(), ws = sheet_from_array_of_arrays(data); + + /*设置worksheet每列的最大宽度*/ + const colWidth = data.map(row => row.map(val => { + /*先判断是否为null/undefined*/ + if (val == null) { + return {'wch': 10}; + } + /*再判断是否为中文*/ + else if (val.toString().charCodeAt(0) > 255) { + return {'wch': val.toString().length * 2}; + } else { + return {'wch': val.toString().length}; + } + })) + /*以第一行为初始值*/ + let result = colWidth[0]; + for (let i = 1; i < colWidth.length; i++) { + for (let j = 0; j < colWidth[i].length; j++) { + if (result[j]['wch'] < colWidth[i][j]['wch']) { + result[j]['wch'] = colWidth[i][j]['wch']; + } + } + } + ws['!cols'] = result; + + /* add worksheet to workbook */ + wb.SheetNames.push(ws_name); + wb.Sheets[ws_name] = ws; + + var wbout = XLSX.write(wb, {bookType: 'xlsx', bookSST: false, type: 'binary'}); + var title = defaultTitle || 'excel-list' + saveAs(new Blob([s2ab(wbout)], {type: "application/octet-stream"}), title + ".xlsx") +} + +function formatJson(jsonSource, jsonFillter) { + return jsonSource.map(v => jsonFillter.map(j => { + return v[j] + })) +} + +export function export_json_to_excel2(th, jsonSource, jsonFillter, defaultTitle) { + const data = formatJson(jsonSource, jsonFillter) + export_json_to_excel(th, data, defaultTitle) +} \ No newline at end of file diff --git a/litemall-admin/src/views/dashboard/index.vue b/litemall-admin/src/views/dashboard/index.vue new file mode 100644 index 00000000..876dee81 --- /dev/null +++ b/litemall-admin/src/views/dashboard/index.vue @@ -0,0 +1,156 @@ + + + + + diff --git a/litemall-admin/src/views/error/401.vue b/litemall-admin/src/views/error/401.vue new file mode 100644 index 00000000..e36c83a2 --- /dev/null +++ b/litemall-admin/src/views/error/401.vue @@ -0,0 +1,88 @@ + + + + + diff --git a/litemall-admin/src/views/error/404.vue b/litemall-admin/src/views/error/404.vue new file mode 100644 index 00000000..42eb3421 --- /dev/null +++ b/litemall-admin/src/views/error/404.vue @@ -0,0 +1,231 @@ + + + + + diff --git a/litemall-admin/src/views/goods/attribute.vue b/litemall-admin/src/views/goods/attribute.vue new file mode 100644 index 00000000..1544c1c5 --- /dev/null +++ b/litemall-admin/src/views/goods/attribute.vue @@ -0,0 +1,218 @@ + + + diff --git a/litemall-admin/src/views/goods/comment.vue b/litemall-admin/src/views/goods/comment.vue new file mode 100644 index 00000000..d37950a7 --- /dev/null +++ b/litemall-admin/src/views/goods/comment.vue @@ -0,0 +1,268 @@ + + + + + \ No newline at end of file diff --git a/litemall-admin/src/views/goods/goods.vue b/litemall-admin/src/views/goods/goods.vue new file mode 100644 index 00000000..159fb85d --- /dev/null +++ b/litemall-admin/src/views/goods/goods.vue @@ -0,0 +1,384 @@ + + + + + \ No newline at end of file diff --git a/litemall-admin/src/views/goods/product.vue b/litemall-admin/src/views/goods/product.vue new file mode 100644 index 00000000..0c8b61a6 --- /dev/null +++ b/litemall-admin/src/views/goods/product.vue @@ -0,0 +1,275 @@ + + + + + diff --git a/litemall-admin/src/views/goods/specification.vue b/litemall-admin/src/views/goods/specification.vue new file mode 100644 index 00000000..3614033a --- /dev/null +++ b/litemall-admin/src/views/goods/specification.vue @@ -0,0 +1,226 @@ + + + diff --git a/litemall-admin/src/views/layout/Layout.vue b/litemall-admin/src/views/layout/Layout.vue new file mode 100644 index 00000000..0811866c --- /dev/null +++ b/litemall-admin/src/views/layout/Layout.vue @@ -0,0 +1,39 @@ + + + + + diff --git a/litemall-admin/src/views/layout/components/AppMain.vue b/litemall-admin/src/views/layout/components/AppMain.vue new file mode 100644 index 00000000..653d1810 --- /dev/null +++ b/litemall-admin/src/views/layout/components/AppMain.vue @@ -0,0 +1,23 @@ + + + diff --git a/litemall-admin/src/views/layout/components/Navbar.vue b/litemall-admin/src/views/layout/components/Navbar.vue new file mode 100644 index 00000000..8ef56715 --- /dev/null +++ b/litemall-admin/src/views/layout/components/Navbar.vue @@ -0,0 +1,123 @@ + + + + + diff --git a/litemall-admin/src/views/layout/components/Sidebar/SidebarItem.vue b/litemall-admin/src/views/layout/components/Sidebar/SidebarItem.vue new file mode 100644 index 00000000..85365d1f --- /dev/null +++ b/litemall-admin/src/views/layout/components/Sidebar/SidebarItem.vue @@ -0,0 +1,49 @@ + + + + diff --git a/litemall-admin/src/views/layout/components/Sidebar/index.vue b/litemall-admin/src/views/layout/components/Sidebar/index.vue new file mode 100644 index 00000000..151575ee --- /dev/null +++ b/litemall-admin/src/views/layout/components/Sidebar/index.vue @@ -0,0 +1,26 @@ + + + diff --git a/litemall-admin/src/views/layout/components/TagsView.vue b/litemall-admin/src/views/layout/components/TagsView.vue new file mode 100644 index 00000000..0d936e1b --- /dev/null +++ b/litemall-admin/src/views/layout/components/TagsView.vue @@ -0,0 +1,202 @@ + + + + + + + diff --git a/litemall-admin/src/views/layout/components/index.js b/litemall-admin/src/views/layout/components/index.js new file mode 100644 index 00000000..07cc9947 --- /dev/null +++ b/litemall-admin/src/views/layout/components/index.js @@ -0,0 +1,4 @@ +export { default as Navbar } from './Navbar' +export { default as Sidebar } from './Sidebar/index.vue' +export { default as TagsView } from './TagsView' +export { default as AppMain } from './AppMain' diff --git a/litemall-admin/src/views/login/authredirect.vue b/litemall-admin/src/views/login/authredirect.vue new file mode 100644 index 00000000..7cf37e81 --- /dev/null +++ b/litemall-admin/src/views/login/authredirect.vue @@ -0,0 +1,10 @@ + diff --git a/litemall-admin/src/views/login/index.vue b/litemall-admin/src/views/login/index.vue new file mode 100644 index 00000000..0cad9c65 --- /dev/null +++ b/litemall-admin/src/views/login/index.vue @@ -0,0 +1,235 @@ + + + + + + + diff --git a/litemall-admin/src/views/login/socialsignin.vue b/litemall-admin/src/views/login/socialsignin.vue new file mode 100644 index 00000000..bdd11120 --- /dev/null +++ b/litemall-admin/src/views/login/socialsignin.vue @@ -0,0 +1,68 @@ + + + + + diff --git a/litemall-admin/src/views/mall/brand.vue b/litemall-admin/src/views/mall/brand.vue new file mode 100644 index 00000000..a3a89bc8 --- /dev/null +++ b/litemall-admin/src/views/mall/brand.vue @@ -0,0 +1,340 @@ + + + + + diff --git a/litemall-admin/src/views/mall/category.vue b/litemall-admin/src/views/mall/category.vue new file mode 100644 index 00000000..f18043fe --- /dev/null +++ b/litemall-admin/src/views/mall/category.vue @@ -0,0 +1,344 @@ + + + + + diff --git a/litemall-admin/src/views/mall/issue.vue b/litemall-admin/src/views/mall/issue.vue new file mode 100644 index 00000000..7da53200 --- /dev/null +++ b/litemall-admin/src/views/mall/issue.vue @@ -0,0 +1,209 @@ + + + diff --git a/litemall-admin/src/views/mall/keyword.vue b/litemall-admin/src/views/mall/keyword.vue new file mode 100644 index 00000000..5789e0cb --- /dev/null +++ b/litemall-admin/src/views/mall/keyword.vue @@ -0,0 +1,275 @@ + + + + + \ No newline at end of file diff --git a/litemall-admin/src/views/mall/order.vue b/litemall-admin/src/views/mall/order.vue new file mode 100644 index 00000000..efda9744 --- /dev/null +++ b/litemall-admin/src/views/mall/order.vue @@ -0,0 +1,261 @@ + + + + + diff --git a/litemall-admin/src/views/mall/region.vue b/litemall-admin/src/views/mall/region.vue new file mode 100644 index 00000000..d3cebe8d --- /dev/null +++ b/litemall-admin/src/views/mall/region.vue @@ -0,0 +1,121 @@ + + + \ No newline at end of file diff --git a/litemall-admin/src/views/promotion/ad.vue b/litemall-admin/src/views/promotion/ad.vue new file mode 100644 index 00000000..6e8277aa --- /dev/null +++ b/litemall-admin/src/views/promotion/ad.vue @@ -0,0 +1,285 @@ + + + + + \ No newline at end of file diff --git a/litemall-admin/src/views/promotion/topic.vue b/litemall-admin/src/views/promotion/topic.vue new file mode 100644 index 00000000..7ddabd16 --- /dev/null +++ b/litemall-admin/src/views/promotion/topic.vue @@ -0,0 +1,278 @@ + + + + + \ No newline at end of file diff --git a/litemall-admin/src/views/sys/admin.vue b/litemall-admin/src/views/sys/admin.vue new file mode 100644 index 00000000..e20a7fb3 --- /dev/null +++ b/litemall-admin/src/views/sys/admin.vue @@ -0,0 +1,258 @@ + + + \ No newline at end of file diff --git a/litemall-admin/src/views/sys/os.vue b/litemall-admin/src/views/sys/os.vue new file mode 100644 index 00000000..eb76bad7 --- /dev/null +++ b/litemall-admin/src/views/sys/os.vue @@ -0,0 +1,211 @@ + + + diff --git a/litemall-admin/src/views/user/address.vue b/litemall-admin/src/views/user/address.vue new file mode 100644 index 00000000..9fc75ce0 --- /dev/null +++ b/litemall-admin/src/views/user/address.vue @@ -0,0 +1,318 @@ + + + \ No newline at end of file diff --git a/litemall-admin/src/views/user/cart.vue b/litemall-admin/src/views/user/cart.vue new file mode 100644 index 00000000..a2aac59f --- /dev/null +++ b/litemall-admin/src/views/user/cart.vue @@ -0,0 +1,236 @@ + + + \ No newline at end of file diff --git a/litemall-admin/src/views/user/collect.vue b/litemall-admin/src/views/user/collect.vue new file mode 100644 index 00000000..fd247999 --- /dev/null +++ b/litemall-admin/src/views/user/collect.vue @@ -0,0 +1,221 @@ + + + diff --git a/litemall-admin/src/views/user/footprint.vue b/litemall-admin/src/views/user/footprint.vue new file mode 100644 index 00000000..8aee4bf0 --- /dev/null +++ b/litemall-admin/src/views/user/footprint.vue @@ -0,0 +1,221 @@ + + + diff --git a/litemall-admin/src/views/user/history.vue b/litemall-admin/src/views/user/history.vue new file mode 100644 index 00000000..1a89a3ab --- /dev/null +++ b/litemall-admin/src/views/user/history.vue @@ -0,0 +1,229 @@ + + + diff --git a/litemall-admin/src/views/user/user.vue b/litemall-admin/src/views/user/user.vue new file mode 100644 index 00000000..f0c68ad6 --- /dev/null +++ b/litemall-admin/src/views/user/user.vue @@ -0,0 +1,316 @@ + + + diff --git a/litemall-admin/static/tinymce4.7.5/langs/zh_CN.js b/litemall-admin/static/tinymce4.7.5/langs/zh_CN.js new file mode 100644 index 00000000..e11f322c --- /dev/null +++ b/litemall-admin/static/tinymce4.7.5/langs/zh_CN.js @@ -0,0 +1,230 @@ +tinymce.addI18n('zh_CN',{ +"Cut": "\u526a\u5207", +"Heading 5": "\u6807\u98985", +"Header 2": "\u6807\u98982", +"Your browser doesn't support direct access to the clipboard. Please use the Ctrl+X\/C\/V keyboard shortcuts instead.": "\u4f60\u7684\u6d4f\u89c8\u5668\u4e0d\u652f\u6301\u5bf9\u526a\u8d34\u677f\u7684\u8bbf\u95ee\uff0c\u8bf7\u4f7f\u7528Ctrl+X\/C\/V\u952e\u8fdb\u884c\u590d\u5236\u7c98\u8d34\u3002", +"Heading 4": "\u6807\u98984", +"Div": "Div\u533a\u5757", +"Heading 2": "\u6807\u98982", +"Paste": "\u7c98\u8d34", +"Close": "\u5173\u95ed", +"Font Family": "\u5b57\u4f53", +"Pre": "\u9884\u683c\u5f0f\u6587\u672c", +"Align right": "\u53f3\u5bf9\u9f50", +"New document": "\u65b0\u6587\u6863", +"Blockquote": "\u5f15\u7528", +"Numbered list": "\u7f16\u53f7\u5217\u8868", +"Heading 1": "\u6807\u98981", +"Headings": "\u6807\u9898", +"Increase indent": "\u589e\u52a0\u7f29\u8fdb", +"Formats": "\u683c\u5f0f", +"Headers": "\u6807\u9898", +"Select all": "\u5168\u9009", +"Header 3": "\u6807\u98983", +"Blocks": "\u533a\u5757", +"Undo": "\u64a4\u6d88", +"Strikethrough": "\u5220\u9664\u7ebf", +"Bullet list": "\u9879\u76ee\u7b26\u53f7", +"Header 1": "\u6807\u98981", +"Superscript": "\u4e0a\u6807", +"Clear formatting": "\u6e05\u9664\u683c\u5f0f", +"Font Sizes": "\u5b57\u53f7", +"Subscript": "\u4e0b\u6807", +"Header 6": "\u6807\u98986", +"Redo": "\u91cd\u590d", +"Paragraph": "\u6bb5\u843d", +"Ok": "\u786e\u5b9a", +"Bold": "\u7c97\u4f53", +"Code": "\u4ee3\u7801", +"Italic": "\u659c\u4f53", +"Align center": "\u5c45\u4e2d", +"Header 5": "\u6807\u98985", +"Heading 6": "\u6807\u98986", +"Heading 3": "\u6807\u98983", +"Decrease indent": "\u51cf\u5c11\u7f29\u8fdb", +"Header 4": "\u6807\u98984", +"Paste is now in plain text mode. Contents will now be pasted as plain text until you toggle this option off.": "\u5f53\u524d\u4e3a\u7eaf\u6587\u672c\u7c98\u8d34\u6a21\u5f0f\uff0c\u518d\u6b21\u70b9\u51fb\u53ef\u4ee5\u56de\u5230\u666e\u901a\u7c98\u8d34\u6a21\u5f0f\u3002", +"Underline": "\u4e0b\u5212\u7ebf", +"Cancel": "\u53d6\u6d88", +"Justify": "\u4e24\u7aef\u5bf9\u9f50", +"Inline": "\u6587\u672c", +"Copy": "\u590d\u5236", +"Align left": "\u5de6\u5bf9\u9f50", +"Visual aids": "\u7f51\u683c\u7ebf", +"Lower Greek": "\u5c0f\u5199\u5e0c\u814a\u5b57\u6bcd", +"Square": "\u65b9\u5757", +"Default": "\u9ed8\u8ba4", +"Lower Alpha": "\u5c0f\u5199\u82f1\u6587\u5b57\u6bcd", +"Circle": "\u7a7a\u5fc3\u5706", +"Disc": "\u5b9e\u5fc3\u5706", +"Upper Alpha": "\u5927\u5199\u82f1\u6587\u5b57\u6bcd", +"Upper Roman": "\u5927\u5199\u7f57\u9a6c\u5b57\u6bcd", +"Lower Roman": "\u5c0f\u5199\u7f57\u9a6c\u5b57\u6bcd", +"Id should start with a letter, followed only by letters, numbers, dashes, dots, colons or underscores.": "\u6807\u8bc6\u7b26\u5e94\u8be5\u4ee5\u5b57\u6bcd\u5f00\u5934\uff0c\u540e\u8ddf\u5b57\u6bcd\u3001\u6570\u5b57\u3001\u7834\u6298\u53f7\u3001\u70b9\u3001\u5192\u53f7\u6216\u4e0b\u5212\u7ebf\u3002", +"Name": "\u540d\u79f0", +"Anchor": "\u951a\u70b9", +"Id": "\u6807\u8bc6\u7b26", +"You have unsaved changes are you sure you want to navigate away?": "\u4f60\u8fd8\u6709\u6587\u6863\u5c1a\u672a\u4fdd\u5b58\uff0c\u786e\u5b9a\u8981\u79bb\u5f00\uff1f", +"Restore last draft": "\u6062\u590d\u4e0a\u6b21\u7684\u8349\u7a3f", +"Special character": "\u7279\u6b8a\u7b26\u53f7", +"Source code": "\u6e90\u4ee3\u7801", +"Language": "\u8bed\u8a00", +"Insert\/Edit code sample": "\u63d2\u5165\/\u7f16\u8f91\u4ee3\u7801\u793a\u4f8b", +"B": "B", +"R": "R", +"G": "G", +"Color": "\u989c\u8272", +"Right to left": "\u4ece\u53f3\u5230\u5de6", +"Left to right": "\u4ece\u5de6\u5230\u53f3", +"Emoticons": "\u8868\u60c5", +"Robots": "\u673a\u5668\u4eba", +"Document properties": "\u6587\u6863\u5c5e\u6027", +"Title": "\u6807\u9898", +"Keywords": "\u5173\u952e\u8bcd", +"Encoding": "\u7f16\u7801", +"Description": "\u63cf\u8ff0", +"Author": "\u4f5c\u8005", +"Fullscreen": "\u5168\u5c4f", +"Horizontal line": "\u6c34\u5e73\u5206\u5272\u7ebf", +"Horizontal space": "\u6c34\u5e73\u8fb9\u8ddd", +"Insert\/edit image": "\u63d2\u5165\/\u7f16\u8f91\u56fe\u7247", +"General": "\u666e\u901a", +"Advanced": "\u9ad8\u7ea7", +"Source": "\u5730\u5740", +"Border": "\u8fb9\u6846", +"Constrain proportions": "\u4fdd\u6301\u7eb5\u6a2a\u6bd4", +"Vertical space": "\u5782\u76f4\u8fb9\u8ddd", +"Image description": "\u56fe\u7247\u63cf\u8ff0", +"Style": "\u6837\u5f0f", +"Dimensions": "\u5927\u5c0f", +"Insert image": "\u63d2\u5165\u56fe\u7247", +"Image": "\u56fe\u7247", +"Zoom in": "\u653e\u5927", +"Contrast": "\u5bf9\u6bd4\u5ea6", +"Back": "\u540e\u9000", +"Gamma": "\u4f3d\u9a6c\u503c", +"Flip horizontally": "\u6c34\u5e73\u7ffb\u8f6c", +"Resize": "\u8c03\u6574\u5927\u5c0f", +"Sharpen": "\u9510\u5316", +"Zoom out": "\u7f29\u5c0f", +"Image options": "\u56fe\u7247\u9009\u9879", +"Apply": "\u5e94\u7528", +"Brightness": "\u4eae\u5ea6", +"Rotate clockwise": "\u987a\u65f6\u9488\u65cb\u8f6c", +"Rotate counterclockwise": "\u9006\u65f6\u9488\u65cb\u8f6c", +"Edit image": "\u7f16\u8f91\u56fe\u7247", +"Color levels": "\u989c\u8272\u5c42\u6b21", +"Crop": "\u88c1\u526a", +"Orientation": "\u65b9\u5411", +"Flip vertically": "\u5782\u76f4\u7ffb\u8f6c", +"Invert": "\u53cd\u8f6c", +"Date\/time": "\u65e5\u671f\/\u65f6\u95f4", +"Insert date\/time": "\u63d2\u5165\u65e5\u671f\/\u65f6\u95f4", +"Remove link": "\u5220\u9664\u94fe\u63a5", +"Url": "\u5730\u5740", +"Text to display": "\u663e\u793a\u6587\u5b57", +"Anchors": "\u951a\u70b9", +"Insert link": "\u63d2\u5165\u94fe\u63a5", +"Link": "\u94fe\u63a5", +"New window": "\u5728\u65b0\u7a97\u53e3\u6253\u5f00", +"None": "\u65e0", +"The URL you entered seems to be an external link. Do you want to add the required http:\/\/ prefix?": "\u4f60\u6240\u586b\u5199\u7684URL\u5730\u5740\u5c5e\u4e8e\u5916\u90e8\u94fe\u63a5\uff0c\u9700\u8981\u52a0\u4e0ahttp:\/\/:\u524d\u7f00\u5417\uff1f", +"Paste or type a link": "\u7c98\u8d34\u6216\u8f93\u5165\u94fe\u63a5", +"Target": "\u6253\u5f00\u65b9\u5f0f", +"The URL you entered seems to be an email address. Do you want to add the required mailto: prefix?": "\u4f60\u6240\u586b\u5199\u7684URL\u5730\u5740\u4e3a\u90ae\u4ef6\u5730\u5740\uff0c\u9700\u8981\u52a0\u4e0amailto:\u524d\u7f00\u5417\uff1f", +"Insert\/edit link": "\u63d2\u5165\/\u7f16\u8f91\u94fe\u63a5", +"Insert\/edit video": "\u63d2\u5165\/\u7f16\u8f91\u89c6\u9891", +"Media": "\u5a92\u4f53", +"Alternative source": "\u955c\u50cf", +"Paste your embed code below:": "\u5c06\u5185\u5d4c\u4ee3\u7801\u7c98\u8d34\u5728\u4e0b\u9762:", +"Insert video": "\u63d2\u5165\u89c6\u9891", +"Poster": "\u5c01\u9762", +"Insert\/edit media": "\u63d2\u5165\/\u7f16\u8f91\u5a92\u4f53", +"Embed": "\u5185\u5d4c", +"Nonbreaking space": "\u4e0d\u95f4\u65ad\u7a7a\u683c", +"Page break": "\u5206\u9875\u7b26", +"Paste as text": "\u7c98\u8d34\u4e3a\u6587\u672c", +"Preview": "\u9884\u89c8", +"Print": "\u6253\u5370", +"Save": "\u4fdd\u5b58", +"Could not find the specified string.": "\u672a\u627e\u5230\u641c\u7d22\u5185\u5bb9.", +"Replace": "\u66ff\u6362", +"Next": "\u4e0b\u4e00\u4e2a", +"Whole words": "\u5168\u5b57\u5339\u914d", +"Find and replace": "\u67e5\u627e\u548c\u66ff\u6362", +"Replace with": "\u66ff\u6362\u4e3a", +"Find": "\u67e5\u627e", +"Replace all": "\u5168\u90e8\u66ff\u6362", +"Match case": "\u533a\u5206\u5927\u5c0f\u5199", +"Prev": "\u4e0a\u4e00\u4e2a", +"Spellcheck": "\u62fc\u5199\u68c0\u67e5", +"Finish": "\u5b8c\u6210", +"Ignore all": "\u5168\u90e8\u5ffd\u7565", +"Ignore": "\u5ffd\u7565", +"Add to Dictionary": "\u6dfb\u52a0\u5230\u5b57\u5178", +"Insert row before": "\u5728\u4e0a\u65b9\u63d2\u5165", +"Rows": "\u884c", +"Height": "\u9ad8", +"Paste row after": "\u7c98\u8d34\u5230\u4e0b\u65b9", +"Alignment": "\u5bf9\u9f50\u65b9\u5f0f", +"Border color": "\u8fb9\u6846\u989c\u8272", +"Column group": "\u5217\u7ec4", +"Row": "\u884c", +"Insert column before": "\u5728\u5de6\u4fa7\u63d2\u5165", +"Split cell": "\u62c6\u5206\u5355\u5143\u683c", +"Cell padding": "\u5355\u5143\u683c\u5185\u8fb9\u8ddd", +"Cell spacing": "\u5355\u5143\u683c\u5916\u95f4\u8ddd", +"Row type": "\u884c\u7c7b\u578b", +"Insert table": "\u63d2\u5165\u8868\u683c", +"Body": "\u8868\u4f53", +"Caption": "\u6807\u9898", +"Footer": "\u8868\u5c3e", +"Delete row": "\u5220\u9664\u884c", +"Paste row before": "\u7c98\u8d34\u5230\u4e0a\u65b9", +"Scope": "\u8303\u56f4", +"Delete table": "\u5220\u9664\u8868\u683c", +"H Align": "\u6c34\u5e73\u5bf9\u9f50", +"Top": "\u9876\u90e8\u5bf9\u9f50", +"Header cell": "\u8868\u5934\u5355\u5143\u683c", +"Column": "\u5217", +"Row group": "\u884c\u7ec4", +"Cell": "\u5355\u5143\u683c", +"Middle": "\u5782\u76f4\u5c45\u4e2d", +"Cell type": "\u5355\u5143\u683c\u7c7b\u578b", +"Copy row": "\u590d\u5236\u884c", +"Row properties": "\u884c\u5c5e\u6027", +"Table properties": "\u8868\u683c\u5c5e\u6027", +"Bottom": "\u5e95\u90e8\u5bf9\u9f50", +"V Align": "\u5782\u76f4\u5bf9\u9f50", +"Header": "\u8868\u5934", +"Right": "\u53f3\u5bf9\u9f50", +"Insert column after": "\u5728\u53f3\u4fa7\u63d2\u5165", +"Cols": "\u5217", +"Insert row after": "\u5728\u4e0b\u65b9\u63d2\u5165", +"Width": "\u5bbd", +"Cell properties": "\u5355\u5143\u683c\u5c5e\u6027", +"Left": "\u5de6\u5bf9\u9f50", +"Cut row": "\u526a\u5207\u884c", +"Delete column": "\u5220\u9664\u5217", +"Center": "\u5c45\u4e2d", +"Merge cells": "\u5408\u5e76\u5355\u5143\u683c", +"Insert template": "\u63d2\u5165\u6a21\u677f", +"Templates": "\u6a21\u677f", +"Background color": "\u80cc\u666f\u8272", +"Custom...": "\u81ea\u5b9a\u4e49...", +"Custom color": "\u81ea\u5b9a\u4e49\u989c\u8272", +"No color": "\u65e0", +"Text color": "\u6587\u5b57\u989c\u8272", +"Table of Contents": "\u5185\u5bb9\u5217\u8868", +"Show blocks": "\u663e\u793a\u533a\u5757\u8fb9\u6846", +"Show invisible characters": "\u663e\u793a\u4e0d\u53ef\u89c1\u5b57\u7b26", +"Words: {0}": "\u5b57\u6570\uff1a{0}", +"Insert": "\u63d2\u5165", +"File": "\u6587\u4ef6", +"Edit": "\u7f16\u8f91", +"Rich Text Area. Press ALT-F9 for menu. Press ALT-F10 for toolbar. Press ALT-0 for help": "\u5728\u7f16\u8f91\u533a\u6309ALT-F9\u6253\u5f00\u83dc\u5355\uff0c\u6309ALT-F10\u6253\u5f00\u5de5\u5177\u680f\uff0c\u6309ALT-0\u67e5\u770b\u5e2e\u52a9", +"Tools": "\u5de5\u5177", +"View": "\u89c6\u56fe", +"Table": "\u8868\u683c", +"Format": "\u683c\u5f0f" +}); \ No newline at end of file diff --git a/litemall-admin/static/tinymce4.7.5/plugins/codesample/css/prism.css b/litemall-admin/static/tinymce4.7.5/plugins/codesample/css/prism.css new file mode 100644 index 00000000..128237fb --- /dev/null +++ b/litemall-admin/static/tinymce4.7.5/plugins/codesample/css/prism.css @@ -0,0 +1,138 @@ +/* http://prismjs.com/download.html?themes=prism&languages=markup+css+clike+javascript */ +/** + * prism.js default theme for JavaScript, CSS and HTML + * Based on dabblet (http://dabblet.com) + * @author Lea Verou + */ + +code[class*="language-"], +pre[class*="language-"] { + color: black; + text-shadow: 0 1px white; + font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace; + direction: ltr; + text-align: left; + white-space: pre; + word-spacing: normal; + word-break: normal; + word-wrap: normal; + line-height: 1.5; + + -moz-tab-size: 4; + -o-tab-size: 4; + tab-size: 4; + + -webkit-hyphens: none; + -moz-hyphens: none; + -ms-hyphens: none; + hyphens: none; +} + +pre[class*="language-"]::-moz-selection, pre[class*="language-"] ::-moz-selection, +code[class*="language-"]::-moz-selection, code[class*="language-"] ::-moz-selection { + text-shadow: none; + background: #b3d4fc; +} + +pre[class*="language-"]::selection, pre[class*="language-"] ::selection, +code[class*="language-"]::selection, code[class*="language-"] ::selection { + text-shadow: none; + background: #b3d4fc; +} + +@media print { + code[class*="language-"], + pre[class*="language-"] { + text-shadow: none; + } +} + +/* Code blocks */ +pre[class*="language-"] { + padding: 1em; + margin: .5em 0; + overflow: auto; +} + +:not(pre) > code[class*="language-"], +pre[class*="language-"] { + background: #f5f2f0; +} + +/* Inline code */ +:not(pre) > code[class*="language-"] { + padding: .1em; + border-radius: .3em; +} + +.token.comment, +.token.prolog, +.token.doctype, +.token.cdata { + color: slategray; +} + +.token.punctuation { + color: #999; +} + +.namespace { + opacity: .7; +} + +.token.property, +.token.tag, +.token.boolean, +.token.number, +.token.constant, +.token.symbol, +.token.deleted { + color: #905; +} + +.token.selector, +.token.attr-name, +.token.string, +.token.char, +.token.builtin, +.token.inserted { + color: #690; +} + +.token.operator, +.token.entity, +.token.url, +.language-css .token.string, +.style .token.string { + color: #a67f59; + background: hsla(0, 0%, 100%, .5); +} + +.token.atrule, +.token.attr-value, +.token.keyword { + color: #07a; +} + +.token.function { + color: #DD4A68; +} + +.token.regex, +.token.important, +.token.variable { + color: #e90; +} + +.token.important, +.token.bold { + font-weight: bold; +} +.token.italic { + font-style: italic; +} + +.token.entity { + cursor: help; +} + diff --git a/litemall-admin/static/tinymce4.7.5/plugins/emoticons/img/smiley-cool.gif b/litemall-admin/static/tinymce4.7.5/plugins/emoticons/img/smiley-cool.gif new file mode 100644 index 00000000..ba90cc36 Binary files /dev/null and b/litemall-admin/static/tinymce4.7.5/plugins/emoticons/img/smiley-cool.gif differ diff --git a/litemall-admin/static/tinymce4.7.5/plugins/emoticons/img/smiley-cry.gif b/litemall-admin/static/tinymce4.7.5/plugins/emoticons/img/smiley-cry.gif new file mode 100644 index 00000000..74d897a4 Binary files /dev/null and b/litemall-admin/static/tinymce4.7.5/plugins/emoticons/img/smiley-cry.gif differ diff --git a/litemall-admin/static/tinymce4.7.5/plugins/emoticons/img/smiley-embarassed.gif b/litemall-admin/static/tinymce4.7.5/plugins/emoticons/img/smiley-embarassed.gif new file mode 100644 index 00000000..963a96b8 Binary files /dev/null and b/litemall-admin/static/tinymce4.7.5/plugins/emoticons/img/smiley-embarassed.gif differ diff --git a/litemall-admin/static/tinymce4.7.5/plugins/emoticons/img/smiley-foot-in-mouth.gif b/litemall-admin/static/tinymce4.7.5/plugins/emoticons/img/smiley-foot-in-mouth.gif new file mode 100644 index 00000000..c7cf1011 Binary files /dev/null and b/litemall-admin/static/tinymce4.7.5/plugins/emoticons/img/smiley-foot-in-mouth.gif differ diff --git a/litemall-admin/static/tinymce4.7.5/plugins/emoticons/img/smiley-frown.gif b/litemall-admin/static/tinymce4.7.5/plugins/emoticons/img/smiley-frown.gif new file mode 100644 index 00000000..716f55e1 Binary files /dev/null and b/litemall-admin/static/tinymce4.7.5/plugins/emoticons/img/smiley-frown.gif differ diff --git a/litemall-admin/static/tinymce4.7.5/plugins/emoticons/img/smiley-innocent.gif b/litemall-admin/static/tinymce4.7.5/plugins/emoticons/img/smiley-innocent.gif new file mode 100644 index 00000000..334d49e0 Binary files /dev/null and b/litemall-admin/static/tinymce4.7.5/plugins/emoticons/img/smiley-innocent.gif differ diff --git a/litemall-admin/static/tinymce4.7.5/plugins/emoticons/img/smiley-kiss.gif b/litemall-admin/static/tinymce4.7.5/plugins/emoticons/img/smiley-kiss.gif new file mode 100644 index 00000000..4efd549e Binary files /dev/null and b/litemall-admin/static/tinymce4.7.5/plugins/emoticons/img/smiley-kiss.gif differ diff --git a/litemall-admin/static/tinymce4.7.5/plugins/emoticons/img/smiley-laughing.gif b/litemall-admin/static/tinymce4.7.5/plugins/emoticons/img/smiley-laughing.gif new file mode 100644 index 00000000..82c5b182 Binary files /dev/null and b/litemall-admin/static/tinymce4.7.5/plugins/emoticons/img/smiley-laughing.gif differ diff --git a/litemall-admin/static/tinymce4.7.5/plugins/emoticons/img/smiley-money-mouth.gif b/litemall-admin/static/tinymce4.7.5/plugins/emoticons/img/smiley-money-mouth.gif new file mode 100644 index 00000000..ca2451e1 Binary files /dev/null and b/litemall-admin/static/tinymce4.7.5/plugins/emoticons/img/smiley-money-mouth.gif differ diff --git a/litemall-admin/static/tinymce4.7.5/plugins/emoticons/img/smiley-sealed.gif b/litemall-admin/static/tinymce4.7.5/plugins/emoticons/img/smiley-sealed.gif new file mode 100644 index 00000000..fe66220c Binary files /dev/null and b/litemall-admin/static/tinymce4.7.5/plugins/emoticons/img/smiley-sealed.gif differ diff --git a/litemall-admin/static/tinymce4.7.5/plugins/emoticons/img/smiley-smile.gif b/litemall-admin/static/tinymce4.7.5/plugins/emoticons/img/smiley-smile.gif new file mode 100644 index 00000000..fd27edfa Binary files /dev/null and b/litemall-admin/static/tinymce4.7.5/plugins/emoticons/img/smiley-smile.gif differ diff --git a/litemall-admin/static/tinymce4.7.5/plugins/emoticons/img/smiley-surprised.gif b/litemall-admin/static/tinymce4.7.5/plugins/emoticons/img/smiley-surprised.gif new file mode 100644 index 00000000..0cc9bb71 Binary files /dev/null and b/litemall-admin/static/tinymce4.7.5/plugins/emoticons/img/smiley-surprised.gif differ diff --git a/litemall-admin/static/tinymce4.7.5/plugins/emoticons/img/smiley-tongue-out.gif b/litemall-admin/static/tinymce4.7.5/plugins/emoticons/img/smiley-tongue-out.gif new file mode 100644 index 00000000..2075dc16 Binary files /dev/null and b/litemall-admin/static/tinymce4.7.5/plugins/emoticons/img/smiley-tongue-out.gif differ diff --git a/litemall-admin/static/tinymce4.7.5/plugins/emoticons/img/smiley-undecided.gif b/litemall-admin/static/tinymce4.7.5/plugins/emoticons/img/smiley-undecided.gif new file mode 100644 index 00000000..bef7e257 Binary files /dev/null and b/litemall-admin/static/tinymce4.7.5/plugins/emoticons/img/smiley-undecided.gif differ diff --git a/litemall-admin/static/tinymce4.7.5/plugins/emoticons/img/smiley-wink.gif b/litemall-admin/static/tinymce4.7.5/plugins/emoticons/img/smiley-wink.gif new file mode 100644 index 00000000..0631c761 Binary files /dev/null and b/litemall-admin/static/tinymce4.7.5/plugins/emoticons/img/smiley-wink.gif differ diff --git a/litemall-admin/static/tinymce4.7.5/plugins/emoticons/img/smiley-yell.gif b/litemall-admin/static/tinymce4.7.5/plugins/emoticons/img/smiley-yell.gif new file mode 100644 index 00000000..648e6e87 Binary files /dev/null and b/litemall-admin/static/tinymce4.7.5/plugins/emoticons/img/smiley-yell.gif differ diff --git a/litemall-admin/static/tinymce4.7.5/plugins/visualblocks/css/visualblocks.css b/litemall-admin/static/tinymce4.7.5/plugins/visualblocks/css/visualblocks.css new file mode 100644 index 00000000..96e4d7c5 --- /dev/null +++ b/litemall-admin/static/tinymce4.7.5/plugins/visualblocks/css/visualblocks.css @@ -0,0 +1,154 @@ +.mce-visualblocks p { + padding-top: 10px; + border: 1px dashed #BBB; + margin-left: 3px; + background-image: url(data:image/gif;base64,R0lGODlhCQAJAJEAAAAAAP///7u7u////yH5BAEAAAMALAAAAAAJAAkAAAIQnG+CqCN/mlyvsRUpThG6AgA7); + background-repeat: no-repeat; +} + +.mce-visualblocks h1 { + padding-top: 10px; + border: 1px dashed #BBB; + margin-left: 3px; + background-image: url(data:image/gif;base64,R0lGODlhDQAKAIABALu7u////yH5BAEAAAEALAAAAAANAAoAAAIXjI8GybGu1JuxHoAfRNRW3TWXyF2YiRUAOw==); + background-repeat: no-repeat; +} + +.mce-visualblocks h2 { + padding-top: 10px; + border: 1px dashed #BBB; + margin-left: 3px; + background-image: url(data:image/gif;base64,R0lGODlhDgAKAIABALu7u////yH5BAEAAAEALAAAAAAOAAoAAAIajI8Hybbx4oOuqgTynJd6bGlWg3DkJzoaUAAAOw==); + background-repeat: no-repeat; +} + +.mce-visualblocks h3 { + padding-top: 10px; + border: 1px dashed #BBB; + margin-left: 3px; + background-image: url(data:image/gif;base64,R0lGODlhDgAKAIABALu7u////yH5BAEAAAEALAAAAAAOAAoAAAIZjI8Hybbx4oOuqgTynJf2Ln2NOHpQpmhAAQA7); + background-repeat: no-repeat; +} + +.mce-visualblocks h4 { + padding-top: 10px; + border: 1px dashed #BBB; + margin-left: 3px; + background-image: url(data:image/gif;base64,R0lGODlhDgAKAIABALu7u////yH5BAEAAAEALAAAAAAOAAoAAAIajI8HybbxInR0zqeAdhtJlXwV1oCll2HaWgAAOw==); + background-repeat: no-repeat; +} + +.mce-visualblocks h5 { + padding-top: 10px; + border: 1px dashed #BBB; + margin-left: 3px; + background-image: url(data:image/gif;base64,R0lGODlhDgAKAIABALu7u////yH5BAEAAAEALAAAAAAOAAoAAAIajI8HybbxIoiuwjane4iq5GlW05GgIkIZUAAAOw==); + background-repeat: no-repeat; +} + +.mce-visualblocks h6 { + padding-top: 10px; + border: 1px dashed #BBB; + margin-left: 3px; + background-image: url(data:image/gif;base64,R0lGODlhDgAKAIABALu7u////yH5BAEAAAEALAAAAAAOAAoAAAIajI8HybbxIoiuwjan04jep1iZ1XRlAo5bVgAAOw==); + background-repeat: no-repeat; +} + +.mce-visualblocks div:not([data-mce-bogus]) { + padding-top: 10px; + border: 1px dashed #BBB; + margin-left: 3px; + background-image: url(data:image/gif;base64,R0lGODlhEgAKAIABALu7u////yH5BAEAAAEALAAAAAASAAoAAAIfjI9poI0cgDywrhuxfbrzDEbQM2Ei5aRjmoySW4pAAQA7); + background-repeat: no-repeat; +} + +.mce-visualblocks section { + padding-top: 10px; + border: 1px dashed #BBB; + margin: 0 0 1em 3px; + background-image: url(data:image/gif;base64,R0lGODlhKAAKAIABALu7u////yH5BAEAAAEALAAAAAAoAAoAAAI5jI+pywcNY3sBWHdNrplytD2ellDeSVbp+GmWqaDqDMepc8t17Y4vBsK5hDyJMcI6KkuYU+jpjLoKADs=); + background-repeat: no-repeat; +} + +.mce-visualblocks article { + padding-top: 10px; + border: 1px dashed #BBB; + margin: 0 0 1em 3px; + background-image: url(data:image/gif;base64,R0lGODlhKgAKAIABALu7u////yH5BAEAAAEALAAAAAAqAAoAAAI6jI+pywkNY3wG0GBvrsd2tXGYSGnfiF7ikpXemTpOiJScasYoDJJrjsG9gkCJ0ag6KhmaIe3pjDYBBQA7); + background-repeat: no-repeat; +} + +.mce-visualblocks blockquote { + padding-top: 10px; + border: 1px dashed #BBB; + background-image: url(data:image/gif;base64,R0lGODlhPgAKAIABALu7u////yH5BAEAAAEALAAAAAA+AAoAAAJPjI+py+0Knpz0xQDyuUhvfoGgIX5iSKZYgq5uNL5q69asZ8s5rrf0yZmpNkJZzFesBTu8TOlDVAabUyatguVhWduud3EyiUk45xhTTgMBBQA7); + background-repeat: no-repeat; +} + +.mce-visualblocks address { + padding-top: 10px; + border: 1px dashed #BBB; + margin: 0 0 1em 3px; + background-image: url(data:image/gif;base64,R0lGODlhLQAKAIABALu7u////yH5BAEAAAEALAAAAAAtAAoAAAI/jI+pywwNozSP1gDyyZcjb3UaRpXkWaXmZW4OqKLhBmLs+K263DkJK7OJeifh7FicKD9A1/IpGdKkyFpNmCkAADs=); + background-repeat: no-repeat; +} + +.mce-visualblocks pre { + padding-top: 10px; + border: 1px dashed #BBB; + margin-left: 3px; + background-image: url(data:image/gif;base64,R0lGODlhFQAKAIABALu7uwAAACH5BAEAAAEALAAAAAAVAAoAAAIjjI+ZoN0cgDwSmnpz1NCueYERhnibZVKLNnbOq8IvKpJtVQAAOw==); + background-repeat: no-repeat; +} + +.mce-visualblocks figure { + padding-top: 10px; + border: 1px dashed #BBB; + margin: 0 0 1em 3px; + background-image: url(data:image/gif;base64,R0lGODlhJAAKAIAAALu7u////yH5BAEAAAEALAAAAAAkAAoAAAI0jI+py+2fwAHUSFvD3RlvG4HIp4nX5JFSpnZUJ6LlrM52OE7uSWosBHScgkSZj7dDKnWAAgA7); + background-repeat: no-repeat; +} + +.mce-visualblocks hgroup { + padding-top: 10px; + border: 1px dashed #BBB; + margin: 0 0 1em 3px; + background-image: url(data:image/gif;base64,R0lGODlhJwAKAIABALu7uwAAACH5BAEAAAEALAAAAAAnAAoAAAI3jI+pywYNI3uB0gpsRtt5fFnfNZaVSYJil4Wo03Hv6Z62uOCgiXH1kZIIJ8NiIxRrAZNMZAtQAAA7); + background-repeat: no-repeat; +} + +.mce-visualblocks aside { + padding-top: 10px; + border: 1px dashed #BBB; + margin: 0 0 1em 3px; + background-image: url(data:image/gif;base64,R0lGODlhHgAKAIABAKqqqv///yH5BAEAAAEALAAAAAAeAAoAAAItjI+pG8APjZOTzgtqy7I3f1yehmQcFY4WKZbqByutmW4aHUd6vfcVbgudgpYCADs=); + background-repeat: no-repeat; +} + +.mce-visualblocks figcaption { + border: 1px dashed #BBB; +} + +.mce-visualblocks ul { + padding-top: 10px; + border: 1px dashed #BBB; + margin: 0 0 1em 3px; + background-image: url(data:image/gif;base64,R0lGODlhDQAKAIAAALu7u////yH5BAEAAAEALAAAAAANAAoAAAIXjI8GybGuYnqUVSjvw26DzzXiqIDlVwAAOw==); + background-repeat: no-repeat; +} + +.mce-visualblocks ol { + padding-top: 10px; + border: 1px dashed #BBB; + margin: 0 0 1em 3px; + background-image: url(data:image/gif;base64,R0lGODlhDQAKAIABALu7u////yH5BAEAAAEALAAAAAANAAoAAAIXjI8GybH6HHt0qourxC6CvzXieHyeWQAAOw==); + background-repeat: no-repeat; +} + +.mce-visualblocks dl { + padding-top: 10px; + border: 1px dashed #BBB; + margin: 0 0 1em 3px; + background-image: url(data:image/gif;base64,R0lGODlhDQAKAIABALu7u////yH5BAEAAAEALAAAAAANAAoAAAIXjI8GybEOnmOvUoWznTqeuEjNSCqeGRUAOw==); + background-repeat: no-repeat; +} diff --git a/litemall-admin/static/tinymce4.7.5/skins/lightgray/content.inline.min.css b/litemall-admin/static/tinymce4.7.5/skins/lightgray/content.inline.min.css new file mode 100644 index 00000000..7b45d339 --- /dev/null +++ b/litemall-admin/static/tinymce4.7.5/skins/lightgray/content.inline.min.css @@ -0,0 +1 @@ +.word-wrap{word-wrap:break-word;-ms-word-break:break-all;word-break:break-all;word-break:break-word;-ms-hyphens:auto;-moz-hyphens:auto;-webkit-hyphens:auto;hyphens:auto}.mce-content-body .mce-reset{margin:0;padding:0;border:0;outline:0;vertical-align:top;background:transparent;text-decoration:none;color:black;font-family:Arial;font-size:11px;text-shadow:none;float:none;position:static;width:auto;height:auto;white-space:nowrap;cursor:inherit;line-height:normal;font-weight:normal;text-align:left;-webkit-tap-highlight-color:transparent;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box;direction:ltr;max-width:none}.mce-object{border:1px dotted #3A3A3A;background:#D5D5D5 url(img/object.gif) no-repeat center}.mce-preview-object{display:inline-block;position:relative;margin:0 2px 0 2px;line-height:0;border:1px solid gray}.mce-preview-object[data-mce-selected="2"] .mce-shim{display:none}.mce-preview-object .mce-shim{position:absolute;top:0;left:0;width:100%;height:100%;background:url(data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7)}figure.align-left{float:left}figure.align-right{float:right}figure.image.align-center{display:table;margin-left:auto;margin-right:auto}figure.image{display:inline-block;border:1px solid gray;margin:0 2px 0 1px;background:#f5f2f0}figure.image img{margin:8px 8px 0 8px}figure.image figcaption{margin:6px 8px 6px 8px;text-align:center}.mce-toc{border:1px solid gray}.mce-toc h2{margin:4px}.mce-toc li{list-style-type:none}.mce-pagebreak{cursor:default;display:block;border:0;width:100%;height:5px;border:1px dashed #666;margin-top:15px;page-break-before:always}@media print{.mce-pagebreak{border:0}}.mce-item-anchor{cursor:default;display:inline-block;-webkit-user-select:all;-webkit-user-modify:read-only;-moz-user-select:all;-moz-user-modify:read-only;user-select:all;user-modify:read-only;width:9px !important;height:9px !important;border:1px dotted #3A3A3A;background:#D5D5D5 url(img/anchor.gif) no-repeat center}.mce-nbsp,.mce-shy{background:#AAA}.mce-shy::after{content:'-'}.mce-match-marker{background:#AAA;color:#fff}.mce-match-marker-selected{background:#3399ff;color:#fff}.mce-spellchecker-word{border-bottom:2px solid rgba(208,2,27,0.5);cursor:default}.mce-spellchecker-grammar{border-bottom:2px solid #008000;cursor:default}.mce-item-table,.mce-item-table td,.mce-item-table th,.mce-item-table caption{border:1px dashed #BBB}td[data-mce-selected],th[data-mce-selected]{background-color:#2276d2 !important}.mce-edit-focus{outline:1px dotted #333}.mce-content-body *[contentEditable=false] *[contentEditable=true]:focus{outline:2px solid #2276d2}.mce-content-body *[contentEditable=false] *[contentEditable=true]:hover{outline:2px solid #2276d2}.mce-content-body *[contentEditable=false][data-mce-selected]{outline:2px solid #2276d2}.mce-content-body *[data-mce-selected="inline-boundary"]{background:#bfe6ff}.mce-content-body .mce-item-anchor[data-mce-selected]{background:#D5D5D5 url(img/anchor.gif) no-repeat center}.mce-content-body hr{cursor:default}.ephox-snooker-resizer-bar{background-color:#2276d2;opacity:0}.ephox-snooker-resizer-cols{cursor:col-resize}.ephox-snooker-resizer-rows{cursor:row-resize}.ephox-snooker-resizer-bar.ephox-snooker-resizer-bar-dragging{opacity:.2}.mce-content-body{line-height:1.3} \ No newline at end of file diff --git a/litemall-admin/static/tinymce4.7.5/skins/lightgray/content.min.css b/litemall-admin/static/tinymce4.7.5/skins/lightgray/content.min.css new file mode 100644 index 00000000..bad168cf --- /dev/null +++ b/litemall-admin/static/tinymce4.7.5/skins/lightgray/content.min.css @@ -0,0 +1 @@ +body{background-color:#FFFFFF;color:#000000;font-family:Verdana,Arial,Helvetica,sans-serif;font-size:14px;line-height:1.3;scrollbar-3dlight-color:#F0F0EE;scrollbar-arrow-color:#676662;scrollbar-base-color:#F0F0EE;scrollbar-darkshadow-color:#DDDDDD;scrollbar-face-color:#E0E0DD;scrollbar-highlight-color:#F0F0EE;scrollbar-shadow-color:#F0F0EE;scrollbar-track-color:#F5F5F5}td,th{font-family:Verdana,Arial,Helvetica,sans-serif;font-size:14px}.word-wrap{word-wrap:break-word;-ms-word-break:break-all;word-break:break-all;word-break:break-word;-ms-hyphens:auto;-moz-hyphens:auto;-webkit-hyphens:auto;hyphens:auto}.mce-content-body .mce-reset{margin:0;padding:0;border:0;outline:0;vertical-align:top;background:transparent;text-decoration:none;color:black;font-family:Arial;font-size:11px;text-shadow:none;float:none;position:static;width:auto;height:auto;white-space:nowrap;cursor:inherit;line-height:normal;font-weight:normal;text-align:left;-webkit-tap-highlight-color:transparent;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box;direction:ltr;max-width:none}.mce-object{border:1px dotted #3A3A3A;background:#D5D5D5 url(img/object.gif) no-repeat center}.mce-preview-object{display:inline-block;position:relative;margin:0 2px 0 2px;line-height:0;border:1px solid gray}.mce-preview-object[data-mce-selected="2"] .mce-shim{display:none}.mce-preview-object .mce-shim{position:absolute;top:0;left:0;width:100%;height:100%;background:url(data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7)}figure.align-left{float:left}figure.align-right{float:right}figure.image.align-center{display:table;margin-left:auto;margin-right:auto}figure.image{display:inline-block;border:1px solid gray;margin:0 2px 0 1px;background:#f5f2f0}figure.image img{margin:8px 8px 0 8px}figure.image figcaption{margin:6px 8px 6px 8px;text-align:center}.mce-toc{border:1px solid gray}.mce-toc h2{margin:4px}.mce-toc li{list-style-type:none}.mce-pagebreak{cursor:default;display:block;border:0;width:100%;height:5px;border:1px dashed #666;margin-top:15px;page-break-before:always}@media print{.mce-pagebreak{border:0}}.mce-item-anchor{cursor:default;display:inline-block;-webkit-user-select:all;-webkit-user-modify:read-only;-moz-user-select:all;-moz-user-modify:read-only;user-select:all;user-modify:read-only;width:9px !important;height:9px !important;border:1px dotted #3A3A3A;background:#D5D5D5 url(img/anchor.gif) no-repeat center}.mce-nbsp,.mce-shy{background:#AAA}.mce-shy::after{content:'-'}.mce-match-marker{background:#AAA;color:#fff}.mce-match-marker-selected{background:#3399ff;color:#fff}.mce-spellchecker-word{border-bottom:2px solid rgba(208,2,27,0.5);cursor:default}.mce-spellchecker-grammar{border-bottom:2px solid #008000;cursor:default}.mce-item-table,.mce-item-table td,.mce-item-table th,.mce-item-table caption{border:1px dashed #BBB}td[data-mce-selected],th[data-mce-selected]{background-color:#2276d2 !important}.mce-edit-focus{outline:1px dotted #333}.mce-content-body *[contentEditable=false] *[contentEditable=true]:focus{outline:2px solid #2276d2}.mce-content-body *[contentEditable=false] *[contentEditable=true]:hover{outline:2px solid #2276d2}.mce-content-body *[contentEditable=false][data-mce-selected]{outline:2px solid #2276d2}.mce-content-body *[data-mce-selected="inline-boundary"]{background:#bfe6ff}.mce-content-body .mce-item-anchor[data-mce-selected]{background:#D5D5D5 url(img/anchor.gif) no-repeat center}.mce-content-body hr{cursor:default}.ephox-snooker-resizer-bar{background-color:#2276d2;opacity:0}.ephox-snooker-resizer-cols{cursor:col-resize}.ephox-snooker-resizer-rows{cursor:row-resize}.ephox-snooker-resizer-bar.ephox-snooker-resizer-bar-dragging{opacity:.2} a {color: #1478F0;} diff --git a/litemall-admin/static/tinymce4.7.5/skins/lightgray/fonts/tinymce-mobile.woff b/litemall-admin/static/tinymce4.7.5/skins/lightgray/fonts/tinymce-mobile.woff new file mode 100644 index 00000000..1e3be038 Binary files /dev/null and b/litemall-admin/static/tinymce4.7.5/skins/lightgray/fonts/tinymce-mobile.woff differ diff --git a/litemall-admin/static/tinymce4.7.5/skins/lightgray/fonts/tinymce-small.eot b/litemall-admin/static/tinymce4.7.5/skins/lightgray/fonts/tinymce-small.eot new file mode 100644 index 00000000..b144ba0b Binary files /dev/null and b/litemall-admin/static/tinymce4.7.5/skins/lightgray/fonts/tinymce-small.eot differ diff --git a/litemall-admin/static/tinymce4.7.5/skins/lightgray/fonts/tinymce-small.svg b/litemall-admin/static/tinymce4.7.5/skins/lightgray/fonts/tinymce-small.svg new file mode 100644 index 00000000..b4ee6f40 --- /dev/null +++ b/litemall-admin/static/tinymce4.7.5/skins/lightgray/fonts/tinymce-small.svg @@ -0,0 +1,63 @@ + + + +Generated by IcoMoon + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/litemall-admin/static/tinymce4.7.5/skins/lightgray/fonts/tinymce-small.ttf b/litemall-admin/static/tinymce4.7.5/skins/lightgray/fonts/tinymce-small.ttf new file mode 100644 index 00000000..a983e2dc Binary files /dev/null and b/litemall-admin/static/tinymce4.7.5/skins/lightgray/fonts/tinymce-small.ttf differ diff --git a/litemall-admin/static/tinymce4.7.5/skins/lightgray/fonts/tinymce-small.woff b/litemall-admin/static/tinymce4.7.5/skins/lightgray/fonts/tinymce-small.woff new file mode 100644 index 00000000..d8962df7 Binary files /dev/null and b/litemall-admin/static/tinymce4.7.5/skins/lightgray/fonts/tinymce-small.woff differ diff --git a/litemall-admin/static/tinymce4.7.5/skins/lightgray/fonts/tinymce.eot b/litemall-admin/static/tinymce4.7.5/skins/lightgray/fonts/tinymce.eot new file mode 100644 index 00000000..5336c38f Binary files /dev/null and b/litemall-admin/static/tinymce4.7.5/skins/lightgray/fonts/tinymce.eot differ diff --git a/litemall-admin/static/tinymce4.7.5/skins/lightgray/fonts/tinymce.svg b/litemall-admin/static/tinymce4.7.5/skins/lightgray/fonts/tinymce.svg new file mode 100644 index 00000000..9fa215f3 --- /dev/null +++ b/litemall-admin/static/tinymce4.7.5/skins/lightgray/fonts/tinymce.svg @@ -0,0 +1,131 @@ + + + +Generated by IcoMoon + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/litemall-admin/static/tinymce4.7.5/skins/lightgray/fonts/tinymce.ttf b/litemall-admin/static/tinymce4.7.5/skins/lightgray/fonts/tinymce.ttf new file mode 100644 index 00000000..61a48a51 Binary files /dev/null and b/litemall-admin/static/tinymce4.7.5/skins/lightgray/fonts/tinymce.ttf differ diff --git a/litemall-admin/static/tinymce4.7.5/skins/lightgray/fonts/tinymce.woff b/litemall-admin/static/tinymce4.7.5/skins/lightgray/fonts/tinymce.woff new file mode 100644 index 00000000..aace5d9c Binary files /dev/null and b/litemall-admin/static/tinymce4.7.5/skins/lightgray/fonts/tinymce.woff differ diff --git a/litemall-admin/static/tinymce4.7.5/skins/lightgray/img/anchor.gif b/litemall-admin/static/tinymce4.7.5/skins/lightgray/img/anchor.gif new file mode 100644 index 00000000..606348c7 Binary files /dev/null and b/litemall-admin/static/tinymce4.7.5/skins/lightgray/img/anchor.gif differ diff --git a/litemall-admin/static/tinymce4.7.5/skins/lightgray/img/loader.gif b/litemall-admin/static/tinymce4.7.5/skins/lightgray/img/loader.gif new file mode 100644 index 00000000..c69e9372 Binary files /dev/null and b/litemall-admin/static/tinymce4.7.5/skins/lightgray/img/loader.gif differ diff --git a/litemall-admin/static/tinymce4.7.5/skins/lightgray/img/object.gif b/litemall-admin/static/tinymce4.7.5/skins/lightgray/img/object.gif new file mode 100644 index 00000000..cccd7f02 Binary files /dev/null and b/litemall-admin/static/tinymce4.7.5/skins/lightgray/img/object.gif differ diff --git a/litemall-admin/static/tinymce4.7.5/skins/lightgray/img/trans.gif b/litemall-admin/static/tinymce4.7.5/skins/lightgray/img/trans.gif new file mode 100644 index 00000000..38848651 Binary files /dev/null and b/litemall-admin/static/tinymce4.7.5/skins/lightgray/img/trans.gif differ diff --git a/litemall-admin/static/tinymce4.7.5/skins/lightgray/skin.min.css b/litemall-admin/static/tinymce4.7.5/skins/lightgray/skin.min.css new file mode 100644 index 00000000..4ad815bf --- /dev/null +++ b/litemall-admin/static/tinymce4.7.5/skins/lightgray/skin.min.css @@ -0,0 +1 @@ +.mce-container,.mce-container *,.mce-widget,.mce-widget *,.mce-reset{margin:0;padding:0;border:0;outline:0;vertical-align:top;background:transparent;text-decoration:none;color:#595959;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;text-shadow:none;float:none;position:static;width:auto;height:auto;white-space:nowrap;cursor:inherit;-webkit-tap-highlight-color:transparent;line-height:normal;font-weight:normal;text-align:left;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box;direction:ltr;max-width:none}.mce-widget button{-moz-box-sizing:border-box;-webkit-box-sizing:border-box;box-sizing:border-box}.mce-container *[unselectable]{-moz-user-select:none;-webkit-user-select:none;-o-user-select:none;user-select:none}.word-wrap{word-wrap:break-word;-ms-word-break:break-all;word-break:break-all;word-break:break-word;-ms-hyphens:auto;-moz-hyphens:auto;-webkit-hyphens:auto;hyphens:auto}.mce-fade{opacity:0;-webkit-transition:opacity .15s linear;transition:opacity .15s linear}.mce-fade.mce-in{opacity:1}.mce-tinymce{visibility:inherit !important;position:relative}.mce-fullscreen{border:0;padding:0;margin:0;overflow:hidden;height:100%;z-index:100}div.mce-fullscreen{position:fixed;top:0;left:0;width:100%;height:auto}.mce-tinymce{display:block;-webkit-box-shadow:0 1px 2px rgba(0, 0, 0, 0.2);-moz-box-shadow:0 1px 2px rgba(0, 0, 0, 0.2);box-shadow:0 1px 2px rgba(0, 0, 0, 0.2)}.mce-statusbar>.mce-container-body{display:flex;padding-right:16px}.mce-statusbar>.mce-container-body .mce-path{flex:1}.mce-wordcount{font-size:inherit;text-transform:uppercase;padding:8px 0}div.mce-edit-area{background:#FFF;filter:none}.mce-statusbar{position:relative}.mce-statusbar .mce-container-body{position:relative;font-size:11px}.mce-fullscreen .mce-resizehandle{display:none}.mce-statusbar .mce-flow-layout-item{margin:0}.mce-charmap{border-collapse:collapse}.mce-charmap td{cursor:default;border:1px solid #c5c5c5;width:20px;height:20px;line-height:20px;text-align:center;vertical-align:middle;padding:2px}.mce-charmap td div{text-align:center}.mce-charmap td:hover{background:white}.mce-grid td.mce-grid-cell div{border:1px solid #c5c5c5;width:15px;height:15px;margin:0;cursor:pointer}.mce-grid td.mce-grid-cell div:focus{border-color:#91bbe9}.mce-grid td.mce-grid-cell div[disabled]{cursor:not-allowed}.mce-grid{border-spacing:2px;border-collapse:separate}.mce-grid a{display:block;border:1px solid transparent}.mce-grid a:hover,.mce-grid a:focus{border-color:#91bbe9}.mce-grid-border{margin:0 4px 0 4px}.mce-grid-border a{border-color:#c5c5c5;width:13px;height:13px}.mce-grid-border a:hover,.mce-grid-border a.mce-active{border-color:#91bbe9;background:#bdd6f2}.mce-text-center{text-align:center}div.mce-tinymce-inline{width:100%}.mce-colorbtn-trans div{text-align:center;vertical-align:middle;font-weight:bold;font-size:20px;line-height:16px;color:#8b8b8b}.mce-monospace{font-family:"Courier New",Courier,monospace}.mce-toolbar-grp .mce-flow-layout-item{margin-bottom:0}.mce-container b{font-weight:bold}.mce-container p{margin-bottom:5px}.mce-container a{cursor:pointer;color:#2276d2}.mce-container a:hover{text-decoration:underline}.mce-container ul{margin-left:15px}.mce-container .mce-table-striped{border-collapse:collapse;margin:10px}.mce-container .mce-table-striped thead>tr{background-color:#fafafa}.mce-container .mce-table-striped thead>tr th{font-weight:bold}.mce-container .mce-table-striped td,.mce-container .mce-table-striped th{padding:5px}.mce-container .mce-table-striped tr:nth-child(even){background-color:#fafafa}.mce-container .mce-table-striped tbody>tr:hover{background-color:#e1e1e1}.mce-branding{font-size:inherit;text-transform:uppercase;white-space:pre;padding:8px 0}.mce-branding a{font-size:inherit;color:inherit}.mce-top-part{position:relative}.mce-top-part::before{content:'';position:absolute;-webkit-box-shadow:0 1px 2px rgba(0, 0, 0, 0.2);-moz-box-shadow:0 1px 2px rgba(0, 0, 0, 0.2);box-shadow:0 1px 2px rgba(0, 0, 0, 0.2);top:0;right:0;bottom:0;left:0;pointer-events:none}.mce-rtl .mce-wordcount{left:0;right:auto}.mce-rtl .mce-statusbar>.mce-container-body>*:last-child{padding-right:0;padding-left:10px}.mce-rtl .mce-path{text-align:right;padding-right:16px}.mce-croprect-container{position:absolute;top:0;left:0}.mce-croprect-handle{position:absolute;top:0;left:0;width:20px;height:20px;border:2px solid white}.mce-croprect-handle-nw{border-width:2px 0 0 2px;margin:-2px 0 0 -2px;cursor:nw-resize;top:100px;left:100px}.mce-croprect-handle-ne{border-width:2px 2px 0 0;margin:-2px 0 0 -20px;cursor:ne-resize;top:100px;left:200px}.mce-croprect-handle-sw{border-width:0 0 2px 2px;margin:-20px 2px 0 -2px;cursor:sw-resize;top:200px;left:100px}.mce-croprect-handle-se{border-width:0 2px 2px 0;margin:-20px 0 0 -20px;cursor:se-resize;top:200px;left:200px}.mce-croprect-handle-move{position:absolute;cursor:move;border:0}.mce-croprect-block{opacity:.5;filter:alpha(opacity=50);zoom:1;position:absolute;background:black}.mce-croprect-handle:focus{border-color:#2276d2}.mce-croprect-handle-move:focus{outline:1px solid #2276d2}.mce-imagepanel{overflow:auto;background:black}.mce-imagepanel-bg{position:absolute;background:url('data:image/gif;base64,R0lGODdhDAAMAIABAMzMzP///ywAAAAADAAMAAACFoQfqYeabNyDMkBQb81Uat85nxguUAEAOw==')}.mce-imagepanel img{position:absolute}.mce-imagetool.mce-btn .mce-ico{display:block;width:20px;height:20px;text-align:center;line-height:20px;font-size:20px;padding:5px}.mce-arrow-up{margin-top:12px}.mce-arrow-down{margin-top:-12px}.mce-arrow:before,.mce-arrow:after{position:absolute;left:50%;display:block;width:0;height:0;border-style:solid;border-color:transparent;content:""}.mce-arrow.mce-arrow-up:before{top:-9px;border-bottom-color:#c5c5c5;border-width:0 9px 9px;margin-left:-9px}.mce-arrow.mce-arrow-down:before{bottom:-9px;border-top-color:#c5c5c5;border-width:9px 9px 0;margin-left:-9px}.mce-arrow.mce-arrow-up:after{top:-8px;border-bottom-color:#fff;border-width:0 8px 8px;margin-left:-8px}.mce-arrow.mce-arrow-down:after{bottom:-8px;border-top-color:#fff;border-width:8px 8px 0;margin-left:-8px}.mce-arrow.mce-arrow-left:before,.mce-arrow.mce-arrow-left:after{margin:0}.mce-arrow.mce-arrow-left:before{left:8px}.mce-arrow.mce-arrow-left:after{left:9px}.mce-arrow.mce-arrow-right:before,.mce-arrow.mce-arrow-right:after{left:auto;margin:0}.mce-arrow.mce-arrow-right:before{right:8px}.mce-arrow.mce-arrow-right:after{right:9px}.mce-arrow.mce-arrow-center.mce-arrow.mce-arrow-left:before{left:-9px;top:50%;border-right-color:#c5c5c5;border-width:9px 9px 9px 0;margin-top:-9px}.mce-arrow.mce-arrow-center.mce-arrow.mce-arrow-left:after{left:-8px;top:50%;border-right-color:#fff;border-width:8px 8px 8px 0;margin-top:-8px}.mce-arrow.mce-arrow-center.mce-arrow.mce-arrow-left{margin-left:12px}.mce-arrow.mce-arrow-center.mce-arrow.mce-arrow-right:before{right:-9px;top:50%;border-left-color:#c5c5c5;border-width:9px 0 9px 9px;margin-top:-9px}.mce-arrow.mce-arrow-center.mce-arrow.mce-arrow-right:after{right:-8px;top:50%;border-left-color:#fff;border-width:8px 0 8px 8px;margin-top:-8px}.mce-arrow.mce-arrow-center.mce-arrow.mce-arrow-right{margin-left:-14px}.mce-edit-aria-container>.mce-container-body{display:flex}.mce-edit-aria-container>.mce-container-body .mce-edit-area{flex:1}.mce-edit-aria-container>.mce-container-body .mce-sidebar>.mce-container-body{display:flex;align-items:stretch;height:100%}.mce-edit-aria-container>.mce-container-body .mce-sidebar-panel{min-width:250px;max-width:250px;position:relative}.mce-edit-aria-container>.mce-container-body .mce-sidebar-panel>.mce-container-body{position:absolute;width:100%;height:100%;overflow:auto;top:0;left:0}.mce-sidebar-toolbar{border:0 solid #c5c5c5;border-left-width:1px}.mce-sidebar-toolbar .mce-btn{border-left:0;border-right:0}.mce-sidebar-toolbar .mce-btn.mce-active,.mce-sidebar-toolbar .mce-btn.mce-active:hover{background-color:#555c66}.mce-sidebar-toolbar .mce-btn.mce-active button,.mce-sidebar-toolbar .mce-btn.mce-active:hover button,.mce-sidebar-toolbar .mce-btn.mce-active button i,.mce-sidebar-toolbar .mce-btn.mce-active:hover button i{color:white;text-shadow:1px 1px none}.mce-sidebar-panel{border:0 solid #c5c5c5;border-left-width:1px}.mce-container,.mce-container-body{display:block}.mce-autoscroll{overflow:hidden}.mce-scrollbar{position:absolute;width:7px;height:100%;top:2px;right:2px;opacity:.4;filter:alpha(opacity=40);zoom:1}.mce-scrollbar-h{top:auto;right:auto;left:2px;bottom:2px;width:100%;height:7px}.mce-scrollbar-thumb{position:absolute;background-color:#000;border:1px solid #888;border-color:rgba(85,85,85,0.6);width:5px;height:100%}.mce-scrollbar-h .mce-scrollbar-thumb{width:100%;height:5px}.mce-scrollbar:hover,.mce-scrollbar.mce-active{background-color:#AAA;opacity:.6;filter:alpha(opacity=60);zoom:1}.mce-scroll{position:relative}.mce-panel{border:0 solid #f3f3f3;border:0 solid #c5c5c5;background-color:#fff}.mce-floatpanel{position:absolute;-webkit-box-shadow:0 1px 2px rgba(0, 0, 0, 0.2);-moz-box-shadow:0 1px 2px rgba(0, 0, 0, 0.2);box-shadow:0 1px 2px rgba(0, 0, 0, 0.2)}.mce-floatpanel.mce-fixed{position:fixed}.mce-floatpanel .mce-arrow,.mce-floatpanel .mce-arrow:after{position:absolute;display:block;width:0;height:0;border-color:transparent;border-style:solid}.mce-floatpanel .mce-arrow{border-width:11px}.mce-floatpanel .mce-arrow:after{border-width:10px;content:""}.mce-floatpanel.mce-popover{filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);background:transparent;-webkit-box-shadow:0 1px 2px rgba(0, 0, 0, 0.2);-moz-box-shadow:0 1px 2px rgba(0, 0, 0, 0.2);box-shadow:0 1px 2px rgba(0, 0, 0, 0.2);top:0;left:0;background:#FFF;border:1px solid #c5c5c5;border:1px solid rgba(0,0,0,0.25)}.mce-floatpanel.mce-popover.mce-bottom{margin-top:10px;*margin-top:0}.mce-floatpanel.mce-popover.mce-bottom>.mce-arrow{left:50%;margin-left:-11px;border-top-width:0;border-bottom-color:#c5c5c5;border-bottom-color:rgba(0,0,0,0.25);top:-11px}.mce-floatpanel.mce-popover.mce-bottom>.mce-arrow:after{top:1px;margin-left:-10px;border-top-width:0;border-bottom-color:#FFF}.mce-floatpanel.mce-popover.mce-bottom.mce-start{margin-left:-22px}.mce-floatpanel.mce-popover.mce-bottom.mce-start>.mce-arrow{left:20px}.mce-floatpanel.mce-popover.mce-bottom.mce-end{margin-left:22px}.mce-floatpanel.mce-popover.mce-bottom.mce-end>.mce-arrow{right:10px;left:auto}.mce-fullscreen{border:0;padding:0;margin:0;overflow:hidden;height:100%}div.mce-fullscreen{position:fixed;top:0;left:0}#mce-modal-block{opacity:0;filter:alpha(opacity=0);zoom:1;position:fixed;left:0;top:0;width:100%;height:100%;background:#FFF}#mce-modal-block.mce-in{opacity:.5;filter:alpha(opacity=50);zoom:1}.mce-window-move{cursor:move}.mce-window{-webkit-box-shadow:0 3px 7px rgba(0, 0, 0, 0.3);-moz-box-shadow:0 3px 7px rgba(0, 0, 0, 0.3);box-shadow:0 3px 7px rgba(0, 0, 0, 0.3);filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);background:transparent;background:#FFF;position:fixed;top:0;left:0;opacity:0;transform:scale(.1);transition:transform 100ms ease-in,opacity 150ms ease-in}.mce-window.mce-in{transform:scale(1);opacity:1}.mce-window-head{padding:9px 15px;border-bottom:1px solid #c5c5c5;position:relative}.mce-window-head .mce-close{position:absolute;right:0;top:0;height:38px;width:38px;text-align:center;cursor:pointer}.mce-window-head .mce-close i{color:#9b9b9b}.mce-close:hover i{color:#bdbdbd}.mce-window-head .mce-title{line-height:20px;font-size:20px;font-weight:bold;text-rendering:optimizelegibility;padding-right:20px}.mce-window .mce-container-body{display:block}.mce-foot{display:block;background-color:#FFF;border-top:1px solid #c5c5c5}.mce-window-head .mce-dragh{position:absolute;top:0;left:0;cursor:move;width:90%;height:100%}.mce-window iframe{width:100%;height:100%}.mce-window-body .mce-listbox{border-color:#e2e4e7}.mce-window .mce-btn:hover{border-color:#c5c5c5}.mce-window .mce-btn:focus{border-color:#2276d2}.mce-window-body .mce-btn,.mce-foot .mce-btn{border-color:#c5c5c5}.mce-foot .mce-btn.mce-primary{border-color:transparent}.mce-rtl .mce-window-head .mce-close{position:absolute;right:auto;left:15px}.mce-rtl .mce-window-head .mce-dragh{left:auto;right:0}.mce-rtl .mce-window-head .mce-title{direction:rtl;text-align:right}.mce-tooltip{position:absolute;padding:5px;opacity:.8;filter:alpha(opacity=80);zoom:1;margin-top:1px}.mce-tooltip-inner{font-size:11px;background-color:#000;color:white;max-width:200px;padding:5px 8px 4px 8px;text-align:center;white-space:normal}.mce-tooltip-inner{-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none}.mce-tooltip-arrow{position:absolute;width:0;height:0;line-height:0;border:5px dashed #000}.mce-tooltip-arrow-n{border-bottom-color:#000}.mce-tooltip-arrow-s{border-top-color:#000}.mce-tooltip-arrow-e{border-left-color:#000}.mce-tooltip-arrow-w{border-right-color:#000}.mce-tooltip-nw,.mce-tooltip-sw{margin-left:-14px}.mce-tooltip-ne,.mce-tooltip-se{margin-left:14px}.mce-tooltip-n .mce-tooltip-arrow{top:0;left:50%;margin-left:-5px;border-bottom-style:solid;border-top:none;border-left-color:transparent;border-right-color:transparent}.mce-tooltip-nw .mce-tooltip-arrow{top:0;left:10px;border-bottom-style:solid;border-top:none;border-left-color:transparent;border-right-color:transparent}.mce-tooltip-ne .mce-tooltip-arrow{top:0;right:10px;border-bottom-style:solid;border-top:none;border-left-color:transparent;border-right-color:transparent}.mce-tooltip-s .mce-tooltip-arrow{bottom:0;left:50%;margin-left:-5px;border-top-style:solid;border-bottom:none;border-left-color:transparent;border-right-color:transparent}.mce-tooltip-sw .mce-tooltip-arrow{bottom:0;left:10px;border-top-style:solid;border-bottom:none;border-left-color:transparent;border-right-color:transparent}.mce-tooltip-se .mce-tooltip-arrow{bottom:0;right:10px;border-top-style:solid;border-bottom:none;border-left-color:transparent;border-right-color:transparent}.mce-tooltip-e .mce-tooltip-arrow{right:0;top:50%;margin-top:-5px;border-left-style:solid;border-right:none;border-top-color:transparent;border-bottom-color:transparent}.mce-tooltip-w .mce-tooltip-arrow{left:0;top:50%;margin-top:-5px;border-right-style:solid;border-left:none;border-top-color:transparent;border-bottom-color:transparent}.mce-progress{display:inline-block;position:relative;height:20px}.mce-progress .mce-bar-container{display:inline-block;width:100px;height:100%;margin-right:8px;border:1px solid #ccc;overflow:hidden}.mce-progress .mce-text{display:inline-block;margin-top:auto;margin-bottom:auto;font-size:14px;width:40px;color:#595959}.mce-bar{display:block;width:0;height:100%;background-color:#dfdfdf;-webkit-transition:width .2s ease;transition:width .2s ease}.mce-notification{position:absolute;background-color:#fff;padding:5px;margin-top:5px;border-width:1px;border-style:solid;border-color:#c5c5c5;transition:transform 100ms ease-in,opacity 150ms ease-in;opacity:0;box-sizing:border-box}.mce-notification.mce-in{opacity:1}.mce-notification-success{background-color:#dff0d8;border-color:#d6e9c6}.mce-notification-info{background-color:#d9edf7;border-color:#779ECB}.mce-notification-warning{background-color:#fcf8e3;border-color:#faebcc}.mce-notification-error{background-color:#f2dede;border-color:#ebccd1}.mce-notification.mce-has-close{padding-right:15px}.mce-notification .mce-ico{margin-top:5px}.mce-notification-inner{word-wrap:break-word;-ms-word-break:break-all;word-break:break-all;word-break:break-word;-ms-hyphens:auto;-moz-hyphens:auto;-webkit-hyphens:auto;hyphens:auto;display:inline-block;font-size:14px;margin:5px 8px 4px 8px;text-align:center;white-space:normal;color:#31708f}.mce-notification-inner a{text-decoration:underline;cursor:pointer}.mce-notification .mce-progress{margin-right:8px}.mce-notification .mce-progress .mce-text{margin-top:5px}.mce-notification *,.mce-notification .mce-progress .mce-text{color:#595959}.mce-notification .mce-progress .mce-bar-container{border-color:#c5c5c5}.mce-notification .mce-progress .mce-bar-container .mce-bar{background-color:#595959}.mce-notification-success *,.mce-notification-success .mce-progress .mce-text{color:#3c763d}.mce-notification-success .mce-progress .mce-bar-container{border-color:#d6e9c6}.mce-notification-success .mce-progress .mce-bar-container .mce-bar{background-color:#3c763d}.mce-notification-info *,.mce-notification-info .mce-progress .mce-text{color:#31708f}.mce-notification-info .mce-progress .mce-bar-container{border-color:#779ECB}.mce-notification-info .mce-progress .mce-bar-container .mce-bar{background-color:#31708f}.mce-notification-warning *,.mce-notification-warning .mce-progress .mce-text{color:#8a6d3b}.mce-notification-warning .mce-progress .mce-bar-container{border-color:#faebcc}.mce-notification-warning .mce-progress .mce-bar-container .mce-bar{background-color:#8a6d3b}.mce-notification-error *,.mce-notification-error .mce-progress .mce-text{color:#a94442}.mce-notification-error .mce-progress .mce-bar-container{border-color:#ebccd1}.mce-notification-error .mce-progress .mce-bar-container .mce-bar{background-color:#a94442}.mce-notification .mce-close{position:absolute;top:6px;right:8px;font-size:20px;font-weight:bold;line-height:20px;color:#9b9b9b;cursor:pointer}.mce-abs-layout{position:relative}body .mce-abs-layout-item,.mce-abs-end{position:absolute}.mce-abs-end{width:1px;height:1px}.mce-container-body.mce-abs-layout{overflow:hidden}.mce-btn{border:1px solid #b3b3b3;border-color:transparent transparent transparent transparent;position:relative;text-shadow:0 1px 1px rgba(255,255,255,0.75);background:white;display:inline-block;*display:inline;*zoom:1;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none}.mce-btn:hover,.mce-btn:active{background:white;color:#595959;border-color:#e2e4e7}.mce-btn:focus{background:white;color:#595959;border-color:#e2e4e7}.mce-btn.mce-disabled button,.mce-btn.mce-disabled:hover button{cursor:default;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;opacity:.4;filter:alpha(opacity=40);zoom:1}.mce-btn.mce-active,.mce-btn.mce-active:hover,.mce-btn.mce-active:focus,.mce-btn.mce-active:active{-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;background:#555c66;color:white;border-color:transparent}.mce-btn.mce-active button,.mce-btn.mce-active:hover button,.mce-btn.mce-active i,.mce-btn.mce-active:hover i{color:white}.mce-btn:hover .mce-caret{border-top-color:#b5bcc2}.mce-btn.mce-active .mce-caret,.mce-btn.mce-active:hover .mce-caret{border-top-color:white}.mce-btn button{padding:4px 6px;font-size:14px;line-height:20px;*line-height:16px;cursor:pointer;color:#595959;text-align:center;overflow:visible;-webkit-appearance:none}.mce-btn button::-moz-focus-inner{border:0;padding:0}.mce-btn i{text-shadow:1px 1px none}.mce-primary.mce-btn-has-text{min-width:50px}.mce-primary{color:white;border:1px solid transparent;border-color:transparent;background-color:#2276d2}.mce-primary:hover,.mce-primary:focus{background-color:#1e6abc;border-color:transparent}.mce-primary.mce-disabled button,.mce-primary.mce-disabled:hover button{cursor:default;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;opacity:.4;filter:alpha(opacity=40);zoom:1}.mce-primary.mce-active,.mce-primary.mce-active:hover,.mce-primary:not(.mce-disabled):active{background-color:#1e6abc;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none}.mce-primary button,.mce-primary button i{color:white;text-shadow:1px 1px none}.mce-btn .mce-txt{font-size:inherit;line-height:inherit;color:inherit}.mce-btn-large button{padding:9px 14px;font-size:16px;line-height:normal}.mce-btn-large i{margin-top:2px}.mce-btn-small button{padding:1px 5px;font-size:12px;*padding-bottom:2px}.mce-btn-small i{line-height:20px;vertical-align:top;*line-height:18px}.mce-btn .mce-caret{margin-top:8px;margin-left:0}.mce-btn-small .mce-caret{margin-top:8px;margin-left:0}.mce-caret{display:inline-block;*display:inline;*zoom:1;width:0;height:0;vertical-align:top;border-top:4px solid #b5bcc2;border-right:4px solid transparent;border-left:4px solid transparent;content:""}.mce-disabled .mce-caret{border-top-color:#aaa}.mce-caret.mce-up{border-bottom:4px solid #b5bcc2;border-top:0}.mce-btn-flat{border:0;background:transparent;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;filter:none}.mce-btn-flat:hover,.mce-btn-flat.mce-active,.mce-btn-flat:focus,.mce-btn-flat:active{border:0;background:#e6e6e6;filter:none;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none}.mce-btn-has-text .mce-ico{padding-right:5px}.mce-rtl .mce-btn button{direction:rtl}.mce-toolbar .mce-btn-group{margin:0;padding:2px 0}.mce-btn-group .mce-btn{border-width:1px;margin:0;margin-left:2px}.mce-btn-group:not(:first-child){border-left:1px solid #d9d9d9;padding-left:0;margin-left:2px}.mce-btn-group{margin-left:2px}.mce-btn-group .mce-btn.mce-flow-layout-item{margin:0}.mce-rtl .mce-btn-group .mce-btn{margin-left:0;margin-right:2px}.mce-rtl .mce-btn-group .mce-first{margin-right:0}.mce-rtl .mce-btn-group:not(:first-child){border-left:none;border-right:1px solid #d9d9d9;padding-right:4px;margin-right:4px}.mce-checkbox{cursor:pointer}i.mce-i-checkbox{margin:0 3px 0 0;border:1px solid #c5c5c5;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;background-color:white;text-indent:-10em;overflow:hidden}.mce-checked i.mce-i-checkbox{color:#595959;font-size:16px;line-height:16px;text-indent:0}.mce-checkbox:focus i.mce-i-checkbox,.mce-checkbox.mce-focus i.mce-i-checkbox{border:1px solid #2276d2;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none}.mce-checkbox.mce-disabled .mce-label,.mce-checkbox.mce-disabled i.mce-i-checkbox{color:#bdbdbd}.mce-checkbox .mce-label{vertical-align:middle}.mce-rtl .mce-checkbox{direction:rtl;text-align:right}.mce-rtl i.mce-i-checkbox{margin:0 0 0 3px}.mce-combobox{position:relative;display:inline-block;*display:inline;*zoom:1;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;*height:32px}.mce-combobox input{border:1px solid #c5c5c5;border-right-color:#c5c5c5;height:28px}.mce-combobox.mce-disabled input{color:#bdbdbd}.mce-combobox .mce-btn{border:1px solid #c5c5c5;border-left:0;margin:0}.mce-combobox button{padding-right:8px;padding-left:8px}.mce-combobox.mce-disabled .mce-btn button{cursor:default;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;opacity:.4;filter:alpha(opacity=40);zoom:1}.mce-combobox .mce-status{position:absolute;right:2px;top:50%;line-height:16px;margin-top:-8px;font-size:12px;width:15px;height:15px;text-align:center;cursor:pointer}.mce-combobox.mce-has-status input{padding-right:20px}.mce-combobox.mce-has-open .mce-status{right:37px}.mce-combobox .mce-status.mce-i-warning{color:#c09853}.mce-combobox .mce-status.mce-i-checkmark{color:#468847}.mce-menu.mce-combobox-menu{border-top:0;margin-top:0;max-height:200px}.mce-menu.mce-combobox-menu .mce-menu-item{padding:4px 6px 4px 4px;font-size:11px}.mce-menu.mce-combobox-menu .mce-menu-item-sep{padding:0}.mce-menu.mce-combobox-menu .mce-text{font-size:11px}.mce-menu.mce-combobox-menu .mce-menu-item-link,.mce-menu.mce-combobox-menu .mce-menu-item-link b{font-size:11px}.mce-menu.mce-combobox-menu .mce-text b{font-size:11px}.mce-colorbox i{border:1px solid #c5c5c5;width:14px;height:14px}.mce-colorbutton .mce-ico{position:relative}.mce-colorbutton-grid{margin:4px}.mce-colorbutton .mce-preview{padding-right:3px;display:block;position:absolute;left:50%;top:50%;margin-left:-17px;margin-top:7px;background:gray;width:13px;height:2px;overflow:hidden}.mce-colorbutton.mce-btn-small .mce-preview{margin-left:-16px;padding-right:0;width:16px}.mce-rtl .mce-colorbutton{direction:rtl}.mce-rtl .mce-colorbutton .mce-preview{margin-left:0;padding-right:0;padding-left:3px}.mce-rtl .mce-colorbutton.mce-btn-small .mce-preview{margin-left:0;padding-right:0;padding-left:2px}.mce-rtl .mce-colorbutton .mce-open{padding-left:4px;padding-right:4px;border-left:0}.mce-colorpicker{position:relative;width:250px;height:220px}.mce-colorpicker-sv{position:absolute;top:0;left:0;width:90%;height:100%;border:1px solid #c5c5c5;cursor:crosshair;overflow:hidden}.mce-colorpicker-h-chunk{width:100%}.mce-colorpicker-overlay1,.mce-colorpicker-overlay2{width:100%;height:100%;position:absolute;top:0;left:0}.mce-colorpicker-overlay1{filter:progid:DXImageTransform.Microsoft.gradient(GradientType=1, startColorstr='#ffffff', endColorstr='#00ffffff');-ms-filter:"progid:DXImageTransform.Microsoft.gradient(GradientType=1,startColorstr='#ffffff', endColorstr='#00ffffff')";background:linear-gradient(to right, #fff, rgba(255,255,255,0))}.mce-colorpicker-overlay2{filter:progid:DXImageTransform.Microsoft.gradient(GradientType=0, startColorstr='#00000000', endColorstr='#000000');-ms-filter:"progid:DXImageTransform.Microsoft.gradient(GradientType=0,startColorstr='#00000000', endColorstr='#000000')";background:linear-gradient(to bottom, rgba(0,0,0,0), #000)}.mce-colorpicker-selector1{background:none;position:absolute;width:12px;height:12px;margin:-8px 0 0 -8px;border:1px solid black;border-radius:50%}.mce-colorpicker-selector2{position:absolute;width:10px;height:10px;border:1px solid white;border-radius:50%}.mce-colorpicker-h{position:absolute;top:0;right:0;width:6.5%;height:100%;border:1px solid #c5c5c5;cursor:crosshair}.mce-colorpicker-h-marker{margin-top:-4px;position:absolute;top:0;left:-1px;width:100%;border:1px solid black;background:white;height:4px;z-index:100}.mce-path{display:inline-block;*display:inline;*zoom:1;padding:8px;white-space:normal;font-size:inherit}.mce-path .mce-txt{display:inline-block;padding-right:3px}.mce-path .mce-path-body{display:inline-block}.mce-path-item{display:inline-block;*display:inline;*zoom:1;cursor:pointer;color:#595959;font-size:inherit;text-transform:uppercase}.mce-path-item:hover{text-decoration:underline}.mce-path-item:focus{background:#555c66;color:white}.mce-path .mce-divider{display:inline;font-size:inherit}.mce-disabled .mce-path-item{color:#aaa}.mce-rtl .mce-path{direction:rtl}.mce-fieldset{border:0 solid #9E9E9E}.mce-fieldset>.mce-container-body{margin-top:-15px}.mce-fieldset-title{margin-left:5px;padding:0 5px 0 5px}.mce-fit-layout{display:inline-block;*display:inline;*zoom:1}.mce-fit-layout-item{position:absolute}.mce-flow-layout-item{display:inline-block;*display:inline;*zoom:1}.mce-flow-layout-item{margin:2px 0 2px 2px}.mce-flow-layout-item.mce-last{margin-right:2px}.mce-flow-layout{white-space:normal}.mce-tinymce-inline .mce-flow-layout{white-space:nowrap}.mce-rtl .mce-flow-layout{text-align:right;direction:rtl}.mce-rtl .mce-flow-layout-item{margin:2px 2px 2px 0}.mce-rtl .mce-flow-layout-item.mce-last{margin-left:2px}.mce-iframe{border:0 solid #c5c5c5;width:100%;height:100%}.mce-infobox{display:inline-block;*display:inline;*zoom:1;text-shadow:0 1px 1px rgba(255,255,255,0.75);overflow:hidden;border:1px solid red}.mce-infobox div{display:block;margin:5px}.mce-infobox div button{position:absolute;top:50%;right:4px;cursor:pointer;margin-top:-8px;display:none}.mce-infobox div button:focus{outline:2px solid #e2e4e7}.mce-infobox.mce-has-help div{margin-right:25px}.mce-infobox.mce-has-help button{display:block}.mce-infobox.mce-success{background:#dff0d8;border-color:#d6e9c6}.mce-infobox.mce-success div{color:#3c763d}.mce-infobox.mce-warning{background:#fcf8e3;border-color:#faebcc}.mce-infobox.mce-warning div{color:#8a6d3b}.mce-infobox.mce-error{background:#f2dede;border-color:#ebccd1}.mce-infobox.mce-error div{color:#a94442}.mce-rtl .mce-infobox div{text-align:right;direction:rtl}.mce-label{display:inline-block;*display:inline;*zoom:1;text-shadow:0 1px 1px rgba(255,255,255,0.75);overflow:hidden}.mce-label.mce-autoscroll{overflow:auto}.mce-label.mce-disabled{color:#aaa}.mce-label.mce-multiline{white-space:pre-wrap}.mce-label.mce-success{color:#468847}.mce-label.mce-warning{color:#c09853}.mce-label.mce-error{color:#b94a48}.mce-rtl .mce-label{text-align:right;direction:rtl}.mce-menubar{border:1px solid #e2e4e7}.mce-menubar .mce-menubtn{border-color:transparent;background:transparent;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;filter:none}.mce-menubar .mce-menubtn button span{color:#595959}.mce-menubar .mce-caret{border-top-color:#b5bcc2}.mce-menubar .mce-active .mce-caret,.mce-menubar .mce-menubtn:hover .mce-caret{border-top-color:#b5bcc2}.mce-menubar .mce-menubtn:hover,.mce-menubar .mce-menubtn.mce-active,.mce-menubar .mce-menubtn:focus{border-color:#e2e4e7;background:white;filter:none;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none}.mce-menubar .mce-menubtn.mce-active{border-bottom:none;z-index:65537}div.mce-menubtn.mce-opened{border-bottom-color:white;z-index:65537}.mce-menubtn button{color:#595959}.mce-menubtn.mce-btn-small span{font-size:12px}.mce-menubtn.mce-fixed-width span{display:inline-block;overflow-x:hidden;text-overflow:ellipsis;width:90px}.mce-menubtn.mce-fixed-width.mce-btn-small span{width:70px}.mce-menubtn .mce-caret{*margin-top:6px}.mce-rtl .mce-menubtn button{direction:rtl;text-align:right}.mce-rtl .mce-menubtn.mce-fixed-width span{direction:rtl;text-align:right}.mce-menu-item{display:block;padding:6px 4px 6px 4px;clear:both;font-weight:normal;line-height:20px;color:#595959;white-space:nowrap;cursor:pointer;line-height:normal;border-left:4px solid transparent;margin-bottom:1px}.mce-menu-item .mce-caret{margin-top:4px;margin-right:6px;border-top:4px solid transparent;border-bottom:4px solid transparent;border-left:4px solid #595959}.mce-menu-item .mce-menu-shortcut{display:inline-block;padding:0 10px 0 20px;color:#aaa}.mce-menu-item .mce-ico{padding-right:4px}.mce-menu-item:hover,.mce-menu-item:focus{background:#ededee}.mce-menu-item:hover .mce-menu-shortcut,.mce-menu-item:focus .mce-menu-shortcut{color:#aaa}.mce-menu-item:hover .mce-text,.mce-menu-item:focus .mce-text,.mce-menu-item:hover .mce-ico,.mce-menu-item:focus .mce-ico{color:#595959}.mce-menu-item.mce-selected{background:#ededee}.mce-menu-item.mce-selected .mce-text,.mce-menu-item.mce-selected .mce-ico{color:#595959}.mce-menu-item.mce-active.mce-menu-item-normal{background:#555c66}.mce-menu-item.mce-active.mce-menu-item-normal .mce-text,.mce-menu-item.mce-active.mce-menu-item-normal .mce-ico{color:white}.mce-menu-item.mce-active.mce-menu-item-checkbox .mce-ico{visibility:visible}.mce-menu-item.mce-disabled,.mce-menu-item.mce-disabled:hover{background:white}.mce-menu-item.mce-disabled:focus,.mce-menu-item.mce-disabled:hover:focus{background:#ededee}.mce-menu-item.mce-disabled .mce-text,.mce-menu-item.mce-disabled:hover .mce-text,.mce-menu-item.mce-disabled .mce-ico,.mce-menu-item.mce-disabled:hover .mce-ico{color:#aaa}.mce-menu-item.mce-menu-item-preview.mce-active{border-left:5px solid #555c66;background:white}.mce-menu-item.mce-menu-item-preview.mce-active .mce-text,.mce-menu-item.mce-menu-item-preview.mce-active .mce-ico{color:#595959}.mce-menu-item.mce-menu-item-preview.mce-active:hover{background:#ededee}.mce-menu-item-link{color:#093;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.mce-menu-item-link b{color:#093}.mce-menu-item-ellipsis{display:block;text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.mce-menu-item:hover *,.mce-menu-item.mce-selected *,.mce-menu-item:focus *{color:#595959}div.mce-menu .mce-menu-item-sep,.mce-menu-item-sep:hover{border:0;padding:0;height:1px;margin:9px 1px;overflow:hidden;background:transparent;border-bottom:1px solid rgba(0,0,0,0.1);cursor:default;filter:none}div.mce-menu .mce-menu-item b{font-weight:bold}.mce-menu-item-indent-1{padding-left:20px}.mce-menu-item-indent-2{padding-left:35px}.mce-menu-item-indent-2{padding-left:35px}.mce-menu-item-indent-3{padding-left:40px}.mce-menu-item-indent-4{padding-left:45px}.mce-menu-item-indent-5{padding-left:50px}.mce-menu-item-indent-6{padding-left:55px}.mce-menu.mce-rtl{direction:rtl}.mce-rtl .mce-menu-item{text-align:right;direction:rtl;padding:6px 12px 6px 15px}.mce-rtl .mce-menu-item .mce-caret{margin-left:6px;margin-right:0;border-right:4px solid #595959;border-left:0}.mce-rtl .mce-menu-item.mce-selected .mce-caret,.mce-rtl .mce-menu-item:focus .mce-caret,.mce-rtl .mce-menu-item:hover .mce-caret{border-left-color:transparent;border-right-color:#595959}.mce-rtl .mce-menu-item .mce-ico{padding-right:0;padding-left:4px}.mce-throbber{position:absolute;top:0;left:0;width:100%;height:100%;opacity:.6;filter:alpha(opacity=60);zoom:1;background:#fff url('img/loader.gif') no-repeat center center}.mce-throbber-inline{position:static;height:50px}.mce-menu .mce-throbber-inline{height:25px;background-size:contain}.mce-menu{position:absolute;left:0;top:0;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);background:transparent;z-index:1000;padding:5px 0 5px 0;margin:-1px 0 0;min-width:180px;background:white;border:1px solid #c5c9cf;border:1px solid #e2e4e7;z-index:1002;-webkit-box-shadow:0 1px 2px rgba(0, 0, 0, 0.2);-moz-box-shadow:0 1px 2px rgba(0, 0, 0, 0.2);box-shadow:0 1px 2px rgba(0, 0, 0, 0.2);max-height:500px;overflow:auto;overflow-x:hidden}.mce-menu.mce-animate{opacity:.01;transform:rotateY(10deg) rotateX(-10deg);transform-origin:left top}.mce-menu.mce-menu-align .mce-menu-shortcut,.mce-menu.mce-menu-align .mce-caret{position:absolute;right:0}.mce-menu i{display:none}.mce-menu-has-icons i{display:inline-block}.mce-menu.mce-in.mce-animate{opacity:1;transform:rotateY(0) rotateX(0);transition:opacity .075s ease,transform .1s ease}.mce-menu-sub-tr-tl{margin:-6px 0 0 -1px}.mce-menu-sub-br-bl{margin:6px 0 0 -1px}.mce-menu-sub-tl-tr{margin:-6px 0 0 1px}.mce-menu-sub-bl-br{margin:6px 0 0 1px}.mce-rtl .mce-menu-item .mce-ico{padding-right:0;padding-left:4px}.mce-rtl.mce-menu-align .mce-caret,.mce-rtl .mce-menu-shortcut{right:auto;left:0}.mce-listbox button{text-align:left;padding-right:20px;position:relative}.mce-listbox .mce-caret{position:absolute;margin-top:-2px;right:8px;top:50%}.mce-rtl .mce-listbox .mce-caret{right:auto;left:8px}.mce-rtl .mce-listbox button{padding-right:10px;padding-left:20px}.mce-container-body .mce-resizehandle{position:absolute;right:0;bottom:0;width:16px;height:16px;visibility:visible;cursor:s-resize;margin:0}.mce-container-body .mce-resizehandle-both{cursor:se-resize}i.mce-i-resize{color:#595959}.mce-selectbox{background:#fff;border:1px solid #c5c5c5}.mce-slider{border:1px solid #c5c5c5;background:#fff;width:100px;height:10px;position:relative;display:block}.mce-slider.mce-vertical{width:10px;height:100px}.mce-slider-handle{border:1px solid #c5c5c5;background:#e6e6e6;display:block;width:13px;height:13px;position:absolute;top:0;left:0;margin-left:-1px;margin-top:-2px}.mce-slider-handle:focus{border-color:#2276d2}.mce-spacer{visibility:hidden}.mce-splitbtn:hover .mce-open{border-left:1px solid #e2e4e7}.mce-splitbtn .mce-open{border-left:1px solid transparent;padding-right:4px;padding-left:4px}.mce-splitbtn .mce-open:focus{border-left:1px solid #e2e4e7}.mce-splitbtn .mce-open:hover,.mce-splitbtn .mce-open:active{border-left:1px solid #e2e4e7}.mce-splitbtn.mce-active:hover .mce-open{border-left:1px solid white}.mce-splitbtn.mce-opened{border-color:#e2e4e7}.mce-splitbtn.mce-btn-small .mce-open{padding:0 3px 0 3px}.mce-rtl .mce-splitbtn{direction:rtl;text-align:right}.mce-rtl .mce-splitbtn button{padding-right:4px;padding-left:4px}.mce-rtl .mce-splitbtn .mce-open{border-left:0}.mce-stack-layout-item{display:block}.mce-tabs{display:block;border-bottom:1px solid #c5c5c5}.mce-tabs,.mce-tabs+.mce-container-body{background:#fff}.mce-tab{display:inline-block;*display:inline;*zoom:1;border:1px solid #c5c5c5;border-width:0 1px 0 0;background:#fff;padding:8px 15px;text-shadow:0 1px 1px rgba(255,255,255,0.75);height:13px;cursor:pointer}.mce-tab:hover{background:#FDFDFD}.mce-tab.mce-active{background:#FDFDFD;border-bottom-color:transparent;margin-bottom:-1px;height:14px}.mce-tab:focus{color:#2276d2}.mce-rtl .mce-tabs{text-align:right;direction:rtl}.mce-rtl .mce-tab{border-width:0 0 0 1px}.mce-textbox{background:#fff;border:1px solid #c5c5c5;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;display:inline-block;-webkit-transition:border linear .2s, box-shadow linear .2s;transition:border linear .2s, box-shadow linear .2s;height:28px;resize:none;padding:0 4px 0 4px;white-space:pre-wrap;*white-space:pre;color:#595959}.mce-textbox:focus,.mce-textbox.mce-focus{border-color:#2276d2;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none}.mce-placeholder .mce-textbox{color:#aaa}.mce-textbox.mce-multiline{padding:4px;height:auto}.mce-textbox.mce-disabled{color:#bdbdbd}.mce-rtl .mce-textbox{text-align:right;direction:rtl}.mce-dropzone{border:3px dashed gray;text-align:center}.mce-dropzone span{text-transform:uppercase;display:inline-block;vertical-align:middle}.mce-dropzone:after{content:"";height:100%;display:inline-block;vertical-align:middle}.mce-dropzone.mce-disabled{opacity:.4;filter:alpha(opacity=40);zoom:1}.mce-dropzone.mce-disabled.mce-dragenter{cursor:not-allowed}.mce-browsebutton{position:relative;overflow:hidden}.mce-browsebutton button{position:relative;z-index:1}.mce-browsebutton input{opacity:0;filter:alpha(opacity=0);zoom:1;position:absolute;top:0;left:0;width:100%;height:100%;z-index:0}@font-face{font-family:'tinymce';src:url('fonts/tinymce.eot');src:url('fonts/tinymce.eot?#iefix') format('embedded-opentype'),url('fonts/tinymce.woff') format('woff'),url('fonts/tinymce.ttf') format('truetype'),url('fonts/tinymce.svg#tinymce') format('svg');font-weight:normal;font-style:normal}@font-face{font-family:'tinymce-small';src:url('fonts/tinymce-small.eot');src:url('fonts/tinymce-small.eot?#iefix') format('embedded-opentype'),url('fonts/tinymce-small.woff') format('woff'),url('fonts/tinymce-small.ttf') format('truetype'),url('fonts/tinymce-small.svg#tinymce') format('svg');font-weight:normal;font-style:normal}.mce-ico{font-family:'tinymce',Arial;font-style:normal;font-weight:normal;font-variant:normal;font-size:16px;line-height:16px;speak:none;vertical-align:text-top;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;display:inline-block;background:transparent center center;background-size:cover;width:16px;height:16px;color:#595959}.mce-btn-small .mce-ico{font-family:'tinymce-small',Arial}.mce-i-save:before{content:"\e000"}.mce-i-newdocument:before{content:"\e001"}.mce-i-fullpage:before{content:"\e002"}.mce-i-alignleft:before{content:"\e003"}.mce-i-aligncenter:before{content:"\e004"}.mce-i-alignright:before{content:"\e005"}.mce-i-alignjustify:before{content:"\e006"}.mce-i-alignnone:before{content:"\e003"}.mce-i-cut:before{content:"\e007"}.mce-i-paste:before{content:"\e008"}.mce-i-searchreplace:before{content:"\e009"}.mce-i-bullist:before{content:"\e00a"}.mce-i-numlist:before{content:"\e00b"}.mce-i-indent:before{content:"\e00c"}.mce-i-outdent:before{content:"\e00d"}.mce-i-blockquote:before{content:"\e00e"}.mce-i-undo:before{content:"\e00f"}.mce-i-redo:before{content:"\e010"}.mce-i-link:before{content:"\e011"}.mce-i-unlink:before{content:"\e012"}.mce-i-anchor:before{content:"\e013"}.mce-i-image:before{content:"\e014"}.mce-i-media:before{content:"\e015"}.mce-i-help:before{content:"\e016"}.mce-i-code:before{content:"\e017"}.mce-i-insertdatetime:before{content:"\e018"}.mce-i-preview:before{content:"\e019"}.mce-i-forecolor:before{content:"\e01a"}.mce-i-backcolor:before{content:"\e01a"}.mce-i-table:before{content:"\e01b"}.mce-i-hr:before{content:"\e01c"}.mce-i-removeformat:before{content:"\e01d"}.mce-i-subscript:before{content:"\e01e"}.mce-i-superscript:before{content:"\e01f"}.mce-i-charmap:before{content:"\e020"}.mce-i-emoticons:before{content:"\e021"}.mce-i-print:before{content:"\e022"}.mce-i-fullscreen:before{content:"\e023"}.mce-i-spellchecker:before{content:"\e024"}.mce-i-nonbreaking:before{content:"\e025"}.mce-i-template:before{content:"\e026"}.mce-i-pagebreak:before{content:"\e027"}.mce-i-restoredraft:before{content:"\e028"}.mce-i-bold:before{content:"\e02a"}.mce-i-italic:before{content:"\e02b"}.mce-i-underline:before{content:"\e02c"}.mce-i-strikethrough:before{content:"\e02d"}.mce-i-visualchars:before{content:"\e02e"}.mce-i-visualblocks:before{content:"\e02e"}.mce-i-ltr:before{content:"\e02f"}.mce-i-rtl:before{content:"\e030"}.mce-i-copy:before{content:"\e031"}.mce-i-resize:before{content:"\e032"}.mce-i-browse:before{content:"\e034"}.mce-i-pastetext:before{content:"\e035"}.mce-i-rotateleft:before{content:"\eaa8"}.mce-i-rotateright:before{content:"\eaa9"}.mce-i-crop:before{content:"\ee78"}.mce-i-editimage:before{content:"\e915"}.mce-i-options:before{content:"\ec6a"}.mce-i-flipv:before{content:"\eaaa"}.mce-i-fliph:before{content:"\eaac"}.mce-i-zoomin:before{content:"\eb35"}.mce-i-zoomout:before{content:"\eb36"}.mce-i-sun:before{content:"\eccc"}.mce-i-moon:before{content:"\eccd"}.mce-i-arrowleft:before{content:"\edc0"}.mce-i-arrowright:before{content:"\e93c"}.mce-i-drop:before{content:"\e935"}.mce-i-contrast:before{content:"\ecd4"}.mce-i-sharpen:before{content:"\eba7"}.mce-i-resize2:before{content:"\edf9"}.mce-i-orientation:before{content:"\e601"}.mce-i-invert:before{content:"\e602"}.mce-i-gamma:before{content:"\e600"}.mce-i-remove:before{content:"\ed6a"}.mce-i-tablerowprops:before{content:"\e604"}.mce-i-tablecellprops:before{content:"\e605"}.mce-i-table2:before{content:"\e606"}.mce-i-tablemergecells:before{content:"\e607"}.mce-i-tableinsertcolbefore:before{content:"\e608"}.mce-i-tableinsertcolafter:before{content:"\e609"}.mce-i-tableinsertrowbefore:before{content:"\e60a"}.mce-i-tableinsertrowafter:before{content:"\e60b"}.mce-i-tablesplitcells:before{content:"\e60d"}.mce-i-tabledelete:before{content:"\e60e"}.mce-i-tableleftheader:before{content:"\e62a"}.mce-i-tabletopheader:before{content:"\e62b"}.mce-i-tabledeleterow:before{content:"\e800"}.mce-i-tabledeletecol:before{content:"\e801"}.mce-i-codesample:before{content:"\e603"}.mce-i-fill:before{content:"\e902"}.mce-i-borderwidth:before{content:"\e903"}.mce-i-line:before{content:"\e904"}.mce-i-count:before{content:"\e905"}.mce-i-translate:before{content:"\e907"}.mce-i-drag:before{content:"\e908"}.mce-i-home:before{content:"\e90b"}.mce-i-upload:before{content:"\e914"}.mce-i-bubble:before{content:"\e91c"}.mce-i-user:before{content:"\e91d"}.mce-i-lock:before{content:"\e926"}.mce-i-unlock:before{content:"\e927"}.mce-i-settings:before{content:"\e928"}.mce-i-remove2:before{content:"\e92a"}.mce-i-menu:before{content:"\e92d"}.mce-i-warning:before{content:"\e930"}.mce-i-question:before{content:"\e931"}.mce-i-pluscircle:before{content:"\e932"}.mce-i-info:before{content:"\e933"}.mce-i-notice:before{content:"\e934"}.mce-i-arrowup:before{content:"\e93b"}.mce-i-arrowdown:before{content:"\e93d"}.mce-i-arrowup2:before{content:"\e93f"}.mce-i-arrowdown2:before{content:"\e940"}.mce-i-menu2:before{content:"\e941"}.mce-i-newtab:before{content:"\e961"}.mce-i-a11y:before{content:"\e900"}.mce-i-plus:before{content:"\e93a"}.mce-i-insert:before{content:"\e93a"}.mce-i-minus:before{content:"\e939"}.mce-i-books:before{content:"\e911"}.mce-i-reload:before{content:"\e906"}.mce-i-toc:before{content:"\e901"}.mce-i-checkmark:before{content:"\e033"}.mce-i-checkbox:before,.mce-i-selected:before{content:"\e033"}.mce-i-insert{font-size:14px}.mce-i-selected{visibility:hidden}i.mce-i-backcolor{text-shadow:none;background:#BBB}.mce-rtl .mce-filepicker input{direction:ltr}/*# sourceMappingURL=skin.min.css.map */ \ No newline at end of file diff --git a/litemall-admin/static/tinymce4.7.5/skins/lightgray/skin.min.css.map b/litemall-admin/static/tinymce4.7.5/skins/lightgray/skin.min.css.map new file mode 100644 index 00000000..c8763dcc --- /dev/null +++ b/litemall-admin/static/tinymce4.7.5/skins/lightgray/skin.min.css.map @@ -0,0 +1 @@ +{"version":3,"sources":["./src/skins/lightgray/main/less/desktop/Reset.less","./src/skins/lightgray/main/less/desktop/Variables.less","./src/skins/lightgray/main/less/desktop/Mixins.less","./src/skins/lightgray/main/less/desktop/Animations.less","./src/skins/lightgray/main/less/desktop/TinyMCE.less","./src/skins/lightgray/main/less/desktop/CropRect.less","./src/skins/lightgray/main/less/desktop/ImagePanel.less","./src/skins/lightgray/main/less/desktop/Arrows.less","./src/skins/lightgray/main/less/desktop/Sidebar.less","./src/skins/lightgray/main/less/desktop/Container.less","./src/skins/lightgray/main/less/desktop/Scrollable.less","./src/skins/lightgray/main/less/desktop/Panel.less","./src/skins/lightgray/main/less/desktop/FloatPanel.less","./src/skins/lightgray/main/less/desktop/Window.less","./src/skins/lightgray/main/less/desktop/ToolTip.less","./src/skins/lightgray/main/less/desktop/Progress.less","./src/skins/lightgray/main/less/desktop/Notification.less","./src/skins/lightgray/main/less/desktop/AbsoluteLayout.less","./src/skins/lightgray/main/less/desktop/Button.less","./src/skins/lightgray/main/less/desktop/ButtonGroup.less","./src/skins/lightgray/main/less/desktop/Checkbox.less","./src/skins/lightgray/main/less/desktop/ComboBox.less","./src/skins/lightgray/main/less/desktop/ColorBox.less","./src/skins/lightgray/main/less/desktop/ColorButton.less","./src/skins/lightgray/main/less/desktop/ColorPicker.less","./src/skins/lightgray/main/less/desktop/Path.less","./src/skins/lightgray/main/less/desktop/FieldSet.less","./src/skins/lightgray/main/less/desktop/FitLayout.less","./src/skins/lightgray/main/less/desktop/FlowLayout.less","./src/skins/lightgray/main/less/desktop/Iframe.less","./src/skins/lightgray/main/less/desktop/InfoBox.less","./src/skins/lightgray/main/less/desktop/Label.less","./src/skins/lightgray/main/less/desktop/MenuBar.less","./src/skins/lightgray/main/less/desktop/MenuButton.less","./src/skins/lightgray/main/less/desktop/MenuItem.less","./src/skins/lightgray/main/less/desktop/Throbber.less","./src/skins/lightgray/main/less/desktop/Menu.less","./src/skins/lightgray/main/less/desktop/ListBox.less","./src/skins/lightgray/main/less/desktop/ResizeHandle.less","./src/skins/lightgray/main/less/desktop/SelectBox.less","./src/skins/lightgray/main/less/desktop/Slider.less","./src/skins/lightgray/main/less/desktop/Spacer.less","./src/skins/lightgray/main/less/desktop/SplitButton.less","./src/skins/lightgray/main/less/desktop/StackLayout.less","./src/skins/lightgray/main/less/desktop/TabPanel.less","./src/skins/lightgray/main/less/desktop/TextBox.less","./src/skins/lightgray/main/less/desktop/DropZone.less","./src/skins/lightgray/main/less/desktop/BrowseButton.less","./src/skins/lightgray/main/less/desktop/Icons.less","./src/skins/lightgray/main/less/desktop/FilePicker.less"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/litemall-admin/static/tinymce4.7.5/tinymce.min.js b/litemall-admin/static/tinymce4.7.5/tinymce.min.js new file mode 100644 index 00000000..d7fcac80 --- /dev/null +++ b/litemall-admin/static/tinymce4.7.5/tinymce.min.js @@ -0,0 +1,2 @@ +// 4.7.5 (2018-01-22) +!function(){"use strict";var e,t,n,r,o,i,a,s,u,c,l,f,d,m,p,g,h,v=function(e){return function(){return e}},y={noop:function(){},noarg:function(e){return function(){return e()}},compose:function(e,t){return function(){return e(t.apply(null,arguments))}},constant:v,identity:function(e){return e},tripleEquals:function(e,t){return e===t},curry:function(e){for(var t=new Array(arguments.length-1),n=1;n-1},T=function(e,t){for(var n=e.length,r=new Array(n),o=0;o=0;n--)t(e[n],n,e)},B=function(e,t){for(var n=[],r=0,o=e.length;r=534,transparentSrc:"data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7",caretAfter:8!==a,range:window.getSelection&&"Range"in window,documentMode:a&&!u?document.documentMode||7:10,fileApi:m,ceFalse:!1===a||a>8,cacheSuffix:"",container:null,overrideViewPort:null,experimentalShadowDom:!1,canHaveCSP:!1===a||a>11,desktop:!p&&!g,windowsPhone:h},pe=window.Promise?window.Promise:function(){function e(e,t){return function(){e.apply(t,arguments)}}var t=Array.isArray||function(e){return"[object Array]"===Object.prototype.toString.call(e)},n=function(t){if("object"!=typeof this)throw new TypeError("Promises must be constructed via new");if("function"!=typeof t)throw new TypeError("not a function");this._state=null,this._value=null,this._deferreds=[],u(t,e(i,this),e(a,this))},r=n.immediateFn||"function"==typeof setImmediate&&setImmediate||function(e){setTimeout(e,1)};function o(e){var t=this;null!==this._state?r(function(){var n=t._state?e.onFulfilled:e.onRejected;if(null!==n){var r;try{r=n(t._value)}catch(o){return void e.reject(o)}e.resolve(r)}else(t._state?e.resolve:e.reject)(t._value)}):this._deferreds.push(e)}function i(t){try{if(t===this)throw new TypeError("A promise cannot be resolved with itself.");if(t&&("object"==typeof t||"function"==typeof t)){var n=t.then;if("function"==typeof n)return void u(e(n,t),e(i,this),e(a,this))}this._state=!0,this._value=t,s.call(this)}catch(r){a.call(this,r)}}function a(e){this._state=!1,this._value=e,s.call(this)}function s(){for(var e=0,t=this._deferreds.length;e0&&(a=i[0]),r.deepPath&&(i=r.deepPath())&&i.length>0&&(a=i[0]),a)),e&&be.test(e.type)&&e.pageX===undefined&&e.clientX!==undefined){var c=u.target.ownerDocument||document,l=c.documentElement,f=c.body;u.pageX=e.clientX+(l&&l.scrollLeft||f&&f.scrollLeft||0)-(l&&l.clientLeft||f&&f.clientLeft||0),u.pageY=e.clientY+(l&&l.scrollTop||f&&f.scrollTop||0)-(l&&l.clientTop||f&&f.clientTop||0)}return u.preventDefault=function(){u.isDefaultPrevented=we,e&&(e.preventDefault?e.preventDefault():e.returnValue=!1)},u.stopPropagation=function(){u.isPropagationStopped=we,e&&(e.stopPropagation?e.stopPropagation():e.cancelBubble=!0)},u.stopImmediatePropagation=function(){u.isImmediatePropagationStopped=we,u.stopPropagation()},0==((s=u).isDefaultPrevented===we||s.isDefaultPrevented===xe)&&(u.isDefaultPrevented=xe,u.isPropagationStopped=xe,u.isImmediatePropagationStopped=xe),"undefined"==typeof u.metaKey&&(u.metaKey=!1),u},ke=function(e,t,n){var r=e.document,o={type:"ready"};if(n.domLoaded)t(o);else{var i=function(){return"complete"===r.readyState||"interactive"===r.readyState&&r.body},a=function(){n.domLoaded||(n.domLoaded=!0,t(o))},s=function(){i()&&(Ee(r,"readystatechange",s),a())},u=function(){try{r.documentElement.doScroll("left")}catch(e){return void ye.setTimeout(u)}a()};!r.addEventListener||me.ie&&me.ie<11?(Ne(r,"readystatechange",s),r.documentElement.doScroll&&e.self===e.top&&u()):i()?a():Ne(e,"DOMContentLoaded",a),Ne(e,"load",a)}},Te=function(){var e,t,n,r,o,i=this,a={};t="mce-data-"+(+new Date).toString(32),r="onmouseenter"in document.documentElement,n="onfocusin"in document.documentElement,o={mouseenter:"mouseover",mouseleave:"mouseout"},e=1,i.domLoaded=!1,i.events=a;var s=function(e,t){var n,r,o,i,s=a[t];if(n=s&&s[e.type])for(r=0,o=n.length;r+~]|"+$e+")"+$e+"*"),Qe=new RegExp("="+$e+"*([^\\]'\"]*?)"+$e+"*\\]","g"),Ze=new RegExp(Xe),et=new RegExp("^"+We+"$"),tt={ID:new RegExp("^#("+We+")"),CLASS:new RegExp("^\\.("+We+")"),TAG:new RegExp("^("+We+"|[*])"),ATTR:new RegExp("^"+Ke),PSEUDO:new RegExp("^"+Xe),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+$e+"*(even|odd|(([+-]|)(\\d*)n|)"+$e+"*(?:([+-]|)"+$e+"*(\\d+)|))"+$e+"*\\)|)","i"),bool:new RegExp("^(?:checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped)$","i"),needsContext:new RegExp("^"+$e+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+$e+"*((?:-\\d)?\\d*)"+$e+"*\\)|)(?=[^-]|$)","i")},nt=/^(?:input|select|textarea|button)$/i,rt=/^h\d$/i,ot=/^[^{]+\{\s*\[native \w/,it=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,at=/[+~]/,st=/'|\\/g,ut=new RegExp("\\\\([\\da-f]{1,6}"+$e+"?|("+$e+")|.)","ig"),ct=function(e,t,n){var r="0x"+t-65536;return r!=r||n?t:r<0?String.fromCharCode(r+65536):String.fromCharCode(r>>10|55296,1023&r|56320)};try{He.apply(ze=qe.call(_e.childNodes),_e.childNodes),ze[_e.childNodes.length].nodeType}catch(yC){He={apply:ze.length?function(e,t){Ve.apply(e,qe.call(t))}:function(e,t){for(var n=e.length,r=0;e[n++]=t[r++];);e.length=n-1}}}var lt=function(e,t,n,r){var o,i,a,s,u,c,l,f,d,m;if((t?t.ownerDocument||t:_e)!==se&&ae(t),t=t||se,n=n||[],!e||"string"!=typeof e)return n;if(1!==(s=t.nodeType)&&9!==s)return[];if(ce&&!r){if(o=it.exec(e))if(a=o[1]){if(9===s){if(!(i=t.getElementById(a))||!i.parentNode)return n;if(i.id===a)return n.push(i),n}else if(t.ownerDocument&&(i=t.ownerDocument.getElementById(a))&&de(t,i)&&i.id===a)return n.push(i),n}else{if(o[2])return He.apply(n,t.getElementsByTagName(e)),n;if((a=o[3])&&G.getElementsByClassName)return He.apply(n,t.getElementsByClassName(a)),n}if(G.qsa&&(!le||!le.test(e))){if(f=l=Ae,d=t,m=9===s&&e,1===s&&"object"!==t.nodeName.toLowerCase()){for(c=ee(e),(l=t.getAttribute("id"))?f=l.replace(st,"\\$&"):t.setAttribute("id",f),f="[id='"+f+"'] ",u=c.length;u--;)c[u]=f+bt(c[u]);d=at.test(e)&&vt(t.parentNode)||t,m=c.join(",")}if(m)try{return He.apply(n,d.querySelectorAll(m)),n}catch(p){}finally{l||t.removeAttribute("id")}}}return ne(e.replace(Ye,"$1"),t,n,r)};function ft(){var e=[];return function t(n,r){return e.push(n+" ")>J.cacheLength&&delete t[e.shift()],t[n+" "]=r}}function dt(e){return e[Ae]=!0,e}function mt(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&(~t.sourceIndex||Me)-(~e.sourceIndex||Me);if(r)return r;if(n)for(;n=n.nextSibling;)if(n===t)return-1;return e?1:-1}function pt(e){return function(t){return"input"===t.nodeName.toLowerCase()&&t.type===e}}function gt(e){return function(t){var n=t.nodeName.toLowerCase();return("input"===n||"button"===n)&&t.type===e}}function ht(e){return dt(function(t){return t=+t,dt(function(n,r){for(var o,i=e([],n.length,t),a=i.length;a--;)n[o=i[a]]&&(n[o]=!(r[o]=n[o]))})})}function vt(e){return e&&typeof e.getElementsByTagName!==Le&&e}for(Y in G=lt.support={},Z=lt.isXML=function(e){var t=e&&(e.ownerDocument||e).documentElement;return!!t&&"HTML"!==t.nodeName},ae=lt.setDocument=function(e){var t,n=e?e.ownerDocument||e:_e,r=n.defaultView;return n!==se&&9===n.nodeType&&n.documentElement?(se=n,ue=n.documentElement,ce=!Z(n),r&&r!==function(e){try{return e.top}catch(t){}return null}(r)&&(r.addEventListener?r.addEventListener("unload",function(){ae()},!1):r.attachEvent&&r.attachEvent("onunload",function(){ae()})),G.attributes=!0,G.getElementsByTagName=!0,G.getElementsByClassName=ot.test(n.getElementsByClassName),G.getById=!0,J.find.ID=function(e,t){if(typeof t.getElementById!==Le&&ce){var n=t.getElementById(e);return n&&n.parentNode?[n]:[]}},J.filter.ID=function(e){var t=e.replace(ut,ct);return function(e){return e.getAttribute("id")===t}},J.find.TAG=G.getElementsByTagName?function(e,t){if(typeof t.getElementsByTagName!==Le)return t.getElementsByTagName(e)}:function(e,t){var n,r=[],o=0,i=t.getElementsByTagName(e);if("*"===e){for(;n=i[o++];)1===n.nodeType&&r.push(n);return r}return i},J.find.CLASS=G.getElementsByClassName&&function(e,t){if(ce)return t.getElementsByClassName(e)},fe=[],le=[],G.disconnectedMatch=!0,le=le.length&&new RegExp(le.join("|")),fe=fe.length&&new RegExp(fe.join("|")),t=ot.test(ue.compareDocumentPosition),de=t||ot.test(ue.contains)?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)for(;t=t.parentNode;)if(t===e)return!0;return!1},Ie=t?function(e,t){if(e===t)return ie=!0,0;var r=!e.compareDocumentPosition-!t.compareDocumentPosition;return r||(1&(r=(e.ownerDocument||e)===(t.ownerDocument||t)?e.compareDocumentPosition(t):1)||!G.sortDetached&&t.compareDocumentPosition(e)===r?e===n||e.ownerDocument===_e&&de(_e,e)?-1:t===n||t.ownerDocument===_e&&de(_e,t)?1:oe?je.call(oe,e)-je.call(oe,t):0:4&r?-1:1)}:function(e,t){if(e===t)return ie=!0,0;var r,o=0,i=e.parentNode,a=t.parentNode,s=[e],u=[t];if(!i||!a)return e===n?-1:t===n?1:i?-1:a?1:oe?je.call(oe,e)-je.call(oe,t):0;if(i===a)return mt(e,t);for(r=e;r=r.parentNode;)s.unshift(r);for(r=t;r=r.parentNode;)u.unshift(r);for(;s[o]===u[o];)o++;return o?mt(s[o],u[o]):s[o]===_e?-1:u[o]===_e?1:0},n):se},lt.matches=function(e,t){return lt(e,null,null,t)},lt.matchesSelector=function(e,t){if((e.ownerDocument||e)!==se&&ae(e),t=t.replace(Qe,"='$1']"),G.matchesSelector&&ce&&(!fe||!fe.test(t))&&(!le||!le.test(t)))try{var n=(void 0).call(e,t);if(n||G.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(yC){}return lt(t,se,null,[e]).length>0},lt.contains=function(e,t){return(e.ownerDocument||e)!==se&&ae(e),de(e,t)},lt.attr=function(e,t){(e.ownerDocument||e)!==se&&ae(e);var n=J.attrHandle[t.toLowerCase()],r=n&&Fe.call(J.attrHandle,t.toLowerCase())?n(e,t,!ce):undefined;return r!==undefined?r:G.attributes||!ce?e.getAttribute(t):(r=e.getAttributeNode(t))&&r.specified?r.value:null},lt.error=function(e){throw new Error("Syntax error, unrecognized expression: "+e)},lt.uniqueSort=function(e){var t,n=[],r=0,o=0;if(ie=!G.detectDuplicates,oe=!G.sortStable&&e.slice(0),e.sort(Ie),ie){for(;t=e[o++];)t===e[o]&&(r=n.push(o));for(;r--;)e.splice(n[r],1)}return oe=null,e},Q=lt.getText=function(e){var t,n="",r=0,o=e.nodeType;if(o){if(1===o||9===o||11===o){if("string"==typeof e.textContent)return e.textContent;for(e=e.firstChild;e;e=e.nextSibling)n+=Q(e)}else if(3===o||4===o)return e.nodeValue}else for(;t=e[r++];)n+=Q(t);return n},(J=lt.selectors={cacheLength:50,createPseudo:dt,match:tt,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(ut,ct),e[3]=(e[3]||e[4]||e[5]||"").replace(ut,ct),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||lt.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&<.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return tt.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&Ze.test(n)&&(t=ee(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(ut,ct).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=De[e+" "];return t||(t=new RegExp("(^|"+$e+")"+e+"("+$e+"|$)"))&&De(e,function(e){return t.test("string"==typeof e.className&&e.className||typeof e.getAttribute!==Le&&e.getAttribute("class")||"")})},ATTR:function(e,t,n){return function(r){var o=lt.attr(r,e);return null==o?"!="===t:!t||(o+="","="===t?o===n:"!="===t?o!==n:"^="===t?n&&0===o.indexOf(n):"*="===t?n&&o.indexOf(n)>-1:"$="===t?n&&o.slice(-n.length)===n:"~="===t?(" "+o+" ").indexOf(n)>-1:"|="===t&&(o===n||o.slice(0,n.length+1)===n+"-"))}},CHILD:function(e,t,n,r,o){var i="nth"!==e.slice(0,3),a="last"!==e.slice(-4),s="of-type"===t;return 1===r&&0===o?function(e){return!!e.parentNode}:function(t,n,u){var c,l,f,d,m,p,g=i!==a?"nextSibling":"previousSibling",h=t.parentNode,v=s&&t.nodeName.toLowerCase(),y=!u&&!s;if(h){if(i){for(;g;){for(f=t;f=f[g];)if(s?f.nodeName.toLowerCase()===v:1===f.nodeType)return!1;p=g="only"===e&&!p&&"nextSibling"}return!0}if(p=[a?h.firstChild:h.lastChild],a&&y){for(m=(c=(l=h[Ae]||(h[Ae]={}))[e]||[])[0]===Be&&c[1],d=c[0]===Be&&c[2],f=m&&h.childNodes[m];f=++m&&f&&f[g]||(d=m=0)||p.pop();)if(1===f.nodeType&&++d&&f===t){l[e]=[Be,m,d];break}}else if(y&&(c=(t[Ae]||(t[Ae]={}))[e])&&c[0]===Be)d=c[1];else for(;(f=++m&&f&&f[g]||(d=m=0)||p.pop())&&((s?f.nodeName.toLowerCase()!==v:1!==f.nodeType)||!++d||(y&&((f[Ae]||(f[Ae]={}))[e]=[Be,d]),f!==t)););return(d-=o)===r||d%r==0&&d/r>=0}}},PSEUDO:function(e,t){var n,r=J.pseudos[e]||J.setFilters[e.toLowerCase()]||lt.error("unsupported pseudo: "+e);return r[Ae]?r(t):r.length>1?(n=[e,e,"",t],J.setFilters.hasOwnProperty(e.toLowerCase())?dt(function(e,n){for(var o,i=r(e,t),a=i.length;a--;)e[o=je.call(e,i[a])]=!(n[o]=i[a])}):function(e){return r(e,0,n)}):r}},pseudos:{not:dt(function(e){var t=[],n=[],r=te(e.replace(Ye,"$1"));return r[Ae]?dt(function(e,t,n,o){for(var i,a=r(e,null,o,[]),s=e.length;s--;)(i=a[s])&&(e[s]=!(t[s]=i))}):function(e,o,i){return t[0]=e,r(t,null,i,n),!n.pop()}}),has:dt(function(e){return function(t){return lt(e,t).length>0}}),contains:dt(function(e){return e=e.replace(ut,ct),function(t){return(t.textContent||t.innerText||Q(t)).indexOf(e)>-1}}),lang:dt(function(e){return et.test(e||"")||lt.error("unsupported lang: "+e),e=e.replace(ut,ct).toLowerCase(),function(t){var n;do{if(n=ce?t.lang:t.getAttribute("xml:lang")||t.getAttribute("lang"))return(n=n.toLowerCase())===e||0===n.indexOf(e+"-")}while((t=t.parentNode)&&1===t.nodeType);return!1}}),target:function(e){var t=window.location&&window.location.hash;return t&&t.slice(1)===e.id},root:function(e){return e===ue},focus:function(e){return e===se.activeElement&&(!se.hasFocus||se.hasFocus())&&!!(e.type||e.href||~e.tabIndex)},enabled:function(e){return!1===e.disabled},disabled:function(e){return!0===e.disabled},checked:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&!!e.checked||"option"===t&&!!e.selected},selected:function(e){return e.parentNode&&e.parentNode.selectedIndex,!0===e.selected},empty:function(e){for(e=e.firstChild;e;e=e.nextSibling)if(e.nodeType<6)return!1;return!0},parent:function(e){return!J.pseudos.empty(e)},header:function(e){return rt.test(e.nodeName)},input:function(e){return nt.test(e.nodeName)},button:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&"button"===e.type||"button"===t},text:function(e){var t;return"input"===e.nodeName.toLowerCase()&&"text"===e.type&&(null==(t=e.getAttribute("type"))||"text"===t.toLowerCase())},first:ht(function(){return[0]}),last:ht(function(e,t){return[t-1]}),eq:ht(function(e,t,n){return[n<0?n+t:n]}),even:ht(function(e,t){for(var n=0;n=0;)e.push(r);return e}),gt:ht(function(e,t,n){for(var r=n<0?n+t:n;++r1?function(t,n,r){for(var o=e.length;o--;)if(!e[o](t,n,r))return!1;return!0}:e[0]}function wt(e,t,n,r,o){for(var i,a=[],s=0,u=e.length,c=null!=t;s-1&&(i[c]=!(a[c]=f))}}else v=wt(v===a?v.splice(p,v.length):v),o?o(null,a,v,u):He.apply(a,v)})}function Et(e){for(var t,n,r,o=e.length,i=J.relative[e[0].type],a=i||J.relative[" "],s=i?1:0,u=Ct(function(e){return e===t},a,!0),c=Ct(function(e){return je.call(t,e)>-1},a,!0),l=[function(e,n,r){return!i&&(r||n!==re)||((t=n).nodeType?u(e,n,r):c(e,n,r))}];s1&&xt(l),s>1&&bt(e.slice(0,s-1).concat({value:" "===e[s-2].type?"*":""})).replace(Ye,"$1"),n,s0,a=r.length>0,s=function(e,t,n,s,u){var c,l,f,d=0,m="0",p=e&&[],g=[],h=re,v=e||a&&J.find.TAG("*",u),y=Be+=null==h?1:Math.random()||.1,b=v.length;for(u&&(re=t!==se&&t);m!==b&&null!=(c=v[m]);m++){if(a&&c){for(l=0;f=r[l++];)if(f(c,t,n)){s.push(c);break}u&&(Be=y)}i&&((c=!f&&c)&&d--,e&&p.push(c))}if(d+=m,i&&m!==d){for(l=0;f=o[l++];)f(p,g,t,n);if(e){if(d>0)for(;m--;)p[m]||g[m]||(g[m]=Ue.call(s));g=wt(g)}He.apply(s,g),u&&!e&&g.length>0&&d+o.length>1&<.uniqueSort(s)}return u&&(Be=y,re=h),p},i?dt(s):s))).selector=e}return l},ne=lt.select=function(e,t,n,r){var o,i,a,s,u,c="function"==typeof e&&e,l=!r&&ee(e=c.selector||e);if(n=n||[],1===l.length){if((i=l[0]=l[0].slice(0)).length>2&&"ID"===(a=i[0]).type&&G.getById&&9===t.nodeType&&ce&&J.relative[i[1].type]){if(!(t=(J.find.ID(a.matches[0].replace(ut,ct),t)||[])[0]))return n;c&&(t=t.parentNode),e=e.slice(i.shift().value.length)}for(o=tt.needsContext.test(e)?0:i.length;o--&&(a=i[o],!J.relative[s=a.type]);)if((u=J.find[s])&&(r=u(a.matches[0].replace(ut,ct),at.test(i[0].type)&&vt(t.parentNode)||t))){if(i.splice(o,1),!(e=r.length&&bt(i)))return He.apply(n,r),n;break}}return(c||te(e,l))(r,t,!ce,n,at.test(e)&&vt(t.parentNode)||t),n},G.sortStable=Ae.split("").sort(Ie).join("")===Ae,G.detectDuplicates=!!ie,ae(),G.sortDetached=!0;var St=Array.isArray||function(e){return"[object Array]"===Object.prototype.toString.call(e)},kt=function(e,t,n){var r,o;if(!e)return 0;if(n=n||e,e.length!==undefined){for(r=0,o=e.length;r)[^>]*$|#([\w\-]*)$)/,Ft=Te.Event,zt=Ot.makeMap("children,contents,next,prev"),Ut=function(e){return void 0!==e},Vt=function(e){return"string"==typeof e},Ht=function(e,t){var n,r,o;for(o=(t=t||Pt).createElement("div"),n=t.createDocumentFragment(),o.innerHTML=e;r=o.firstChild;)n.appendChild(r);return n},qt=function(e,t,n,r){var o;if(Vt(t))t=Ht(t,rn(e[0]));else if(t.length&&!t.nodeType){if(t=Qt.makeArray(t),r)for(o=t.length-1;o>=0;o--)qt(e,t[o],n,r);else for(o=0;o"===e.charAt(e.length-1)&&e.length>=3?[null,e,null]:Mt.exec(e)))return Qt(t).find(e);if(n[1])for(r=Ht(e,rn(t)).firstChild;r;)It.call(o,r),r=r.nextSibling;else{if(!(r=rn(t).getElementById(n[2])))return o;if(r.id!==n[2])return o.find(e);o.length=1,o[0]=r}}else this.add(e,!1);return o},toArray:function(){return Ot.toArray(this)},add:function(e,t){var n,r,o=this;if(Vt(e))return o.add(Qt(e));if(!1!==t)for(n=Qt.unique(o.toArray().concat(Qt.makeArray(e))),o.length=n.length,r=0;r1&&(zt[e]||(r=Qt.unique(r)),0===e.indexOf("parents")&&(r=r.reverse())),r=Qt(r),n?r.filter(n):r}}),tn({parentsUntil:function(e,t){return on(e,"parentNode",t)},nextUntil:function(e,t){return an(e,"nextSibling",1,t).slice(1)},prevUntil:function(e,t){return an(e,"previousSibling",1,t).slice(1)}},function(e,t){Qt.fn[e]=function(n,r){var o=[];return this.each(function(){var e=t.call(o,this,n,o);e&&(Qt.isArray(e)?o.push.apply(o,e):o.push(e))}),this.length>1&&(o=Qt.unique(o),0!==e.indexOf("parents")&&"prevUntil"!==e||(o=o.reverse())),o=Qt(o),r?o.filter(r):o}}),Qt.fn.is=function(e){return!!e&&this.filter(e).length>0},Qt.fn.init.prototype=Qt.fn,Qt.overrideDefaults=function(e){var t,n=function(r,o){return t=t||e(),0===arguments.length&&(r=t.element),o||(o=t.context),new n.fn.init(r,o)};return Qt.extend(n,this),n};var un=function(e,t,n){tn(n,function(n,r){e[n]=e[n]||{},e[n][t]=r})};me.ie&&me.ie<8&&(un(Gt,"get",{maxlength:function(e){var t=e.maxLength;return 2147483647===t?undefined:t},size:function(e){var t=e.size;return 20===t?undefined:t},"class":function(e){return e.className},style:function(e){var t=e.style.cssText;return 0===t.length?undefined:t}}),un(Gt,"set",{"class":function(e,t){e.className=t},style:function(e,t){e.style.cssText=t}})),me.ie&&me.ie<9&&(Yt["float"]="styleFloat",un(Jt,"set",{opacity:function(e,t){var n=e.style;null===t||""===t?n.removeAttribute("filter"):(n.zoom=1,n.filter="alpha(opacity="+100*t+")")}})),Qt.attrHooks=Gt,Qt.cssHooks=Jt;var cn,ln,fn,dn,mn=function(e){var t,n=!1;return function(){return n||(n=!0,t=e.apply(null,arguments)),t}},pn=function(e,t){var n=function(e,t){for(var n=0;n1)throw console.error("HTML does not have a single root node",e),"HTML must have a single root node";return Vn(n.childNodes[0])},fromTag:function(e,t){var n=(t||document).createElement(e);return Vn(n)},fromText:function(e,t){var n=(t||document).createTextNode(e);return Vn(n)},fromDom:Vn,fromPoint:function(e,t,n){return E.from(e.dom().elementFromPoint(t,n)).map(Vn)}},qn=8,jn=9,$n=1,Wn=3,Kn=function(e){return e.dom().nodeName.toLowerCase()},Xn=function(e){return e.dom().nodeType},Yn=function(e){return function(t){return Xn(t)===e}},Gn=Yn($n),Jn=Yn(Wn),Qn=Yn(jn),Zn={name:Kn,type:Xn,value:function(e){return e.dom().nodeValue},isElement:Gn,isText:Jn,isDocument:Qn,isComment:function(e){return Xn(e)===qn||"#comment"===Kn(e)}},er=function(e){return function(t){return function(e){if(null===e)return"null";var t=typeof e;return"object"===t&&Array.prototype.isPrototypeOf(e)?"array":"object"===t&&String.prototype.isPrototypeOf(e)?"string":t}(t)===e}},tr={isString:er("string"),isObject:er("object"),isArray:er("array"),isNull:er("null"),isBoolean:er("boolean"),isUndefined:er("undefined"),isFunction:er("function"),isNumber:er("number")},nr=(cn=Object.keys)===undefined?function(e){var t=[];for(var n in e)e.hasOwnProperty(n)&&t.push(n);return t}:cn,rr=function(e,t){for(var n=nr(e),r=0,o=n.length;r0&&t=e.length&&t(n)}))})})},Gr=function(e){return Yr(e,Xr.nu)},Jr=Gr,Qr=function(e){return{is:function(t){return e===t},isValue:y.constant(!0),isError:y.constant(!1),getOr:y.constant(e),getOrThunk:y.constant(e),getOrDie:y.constant(e),or:function(t){return Qr(e)},orThunk:function(t){return Qr(e)},fold:function(t,n){return n(e)},map:function(t){return Qr(t(e))},each:function(t){t(e)},bind:function(t){return t(e)},exists:function(t){return t(e)},forall:function(t){return t(e)},toOption:function(){return E.some(e)}}},Zr=function(e){return{is:y.constant(!1),isValue:y.constant(!1),isError:y.constant(!0),getOr:y.identity,getOrThunk:function(e){return e()},getOrDie:function(){return y.die(e)()},or:function(e){return e},orThunk:function(e){return e()},fold:function(t,n){return t(e)},map:function(t){return Zr(e)},each:y.noop,bind:function(t){return Zr(e)},exists:y.constant(!1),forall:y.constant(!0),toOption:E.none}},eo={value:Qr,error:Zr},to=function(e,t){var n=e,r=function(e,n,r,o){var i,a;if(e){if(!o&&e[n])return e[n];if(e!==t){if(i=e[r])return i;for(a=e.parentNode;a&&a!==t;a=a.parentNode)if(i=a[r])return i}}};this.current=function(){return n},this.next=function(e){return n=r(n,"firstChild","nextSibling",e)},this.prev=function(e){return n=r(n,"lastChild","previousSibling",e)},this.prev2=function(e){return n=function(e,n,r,o){var i,a,s;if(e){if(i=e[r],t&&i===t)return;if(i){if(!o)for(s=i[n];s;s=s[n])if(!s[n])return s;return i}if((a=e.parentNode)&&a!==t)return a}}(n,"lastChild","previousSibling",e)}},no=function(e){var t;return function(n){return(t=t||M.mapToObject(e,y.constant(!0))).hasOwnProperty(Zn.name(n))}},ro=no(["h1","h2","h3","h4","h5","h6"]),oo=no(["article","aside","details","div","dt","figcaption","footer","form","fieldset","header","hgroup","html","main","nav","section","summary","body","p","dl","multicol","dd","figure","address","center","blockquote","h1","h2","h3","h4","h5","h6","listing","xmp","pre","plaintext","menu","dir","ul","ol","li","hr","table","tbody","thead","tfoot","th","tr","td","caption"]),io={isBlock:oo,isInline:function(e){return Zn.isElement(e)&&!oo(e)},isHeading:ro,isTextBlock:no(["h1","h2","h3","h4","h5","h6","p","div","address","pre","form","blockquote","center","dir","fieldset","header","footer","article","section","hgroup","aside","nav","figure"]),isList:no(["ul","ol","dl"]),isListItem:no(["li","dd","dt"]),isVoid:no(["area","base","basefont","br","col","frame","hr","img","input","isindex","link","meta","param","embed","source","wbr","track"]),isTableSection:no(["thead","tbody","tfoot"]),isTableCell:no(["td","th"]),isBr:function(e){return Zn.isElement(e)&&"br"===Zn.name(e)}},ao=function(e){return function(t){return!!t&&t.nodeType===e}},so=ao(1),uo=function(e){var t=e.toLowerCase().split(" ");return function(e){var n,r;if(e&&e.nodeType)for(r=e.nodeName.toLowerCase(),n=0;n=0;n--)bo(e,r[n]);if(!1===vo.isDocument(t)){if(vo.isText(t)&&t.nodeValue.length>0){var o=Ot.trim(t.nodeValue).length;if(e.isBlock(t.parentNode)||o>0)return;if(0===o&&(a=(i=t).previousSibling&&"SPAN"===i.previousSibling.nodeName,s=i.nextSibling&&"SPAN"===i.nextSibling.nodeName,a&&s))return}else if(vo.isElement(t)&&(1===(r=t.childNodes).length&&yo(r[0])&&t.parentNode.insertBefore(r[0],t),r.length||io.isVoid(Hn.fromDom(t))))return;e.remove(t)}var i,a,s;return t}},Co={trimNode:bo},xo=Ot.makeMap,wo=/[&<>\"\u0060\u007E-\uD7FF\uE000-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,No=/[<>&\u007E-\uD7FF\uE000-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,Eo=/[<>&\"\']/g,So=/&#([a-z0-9]+);?|&([a-z0-9]+);/gi,ko={128:"\u20ac",130:"\u201a",131:"\u0192",132:"\u201e",133:"\u2026",134:"\u2020",135:"\u2021",136:"\u02c6",137:"\u2030",138:"\u0160",139:"\u2039",140:"\u0152",142:"\u017d",145:"\u2018",146:"\u2019",147:"\u201c",148:"\u201d",149:"\u2022",150:"\u2013",151:"\u2014",152:"\u02dc",153:"\u2122",154:"\u0161",155:"\u203a",156:"\u0153",158:"\u017e",159:"\u0178"};fn={'"':""","'":"'","<":"<",">":">","&":"&","`":"`"},dn={"<":"<",">":">","&":"&",""":'"',"'":"'"};var To=function(e,t){var n,r,o,i={};if(e){for(e=e.split(","),t=t||10,n=0;n1?"&#"+(1024*(e.charCodeAt(0)-55296)+(e.charCodeAt(1)-56320)+65536)+";":fn[e]||"&#"+e.charCodeAt(0)+";"})},encodeNamed:function(e,t,n){return n=n||ln,e.replace(t?wo:No,function(e){return fn[e]||n[e]||e})},getEncodeFunc:function(e,t){return t=To(t)||ln,(e=xo(e.replace(/\+/g,","))).named&&e.numeric?function(e,n){return e.replace(n?wo:No,function(e){return fn[e]!==undefined?fn[e]:t[e]!==undefined?t[e]:e.length>1?"&#"+(1024*(e.charCodeAt(0)-55296)+(e.charCodeAt(1)-56320)+65536)+";":"&#"+e.charCodeAt(0)+";"})}:e.named?t?function(e,n){return Ao.encodeNamed(e,n,t)}:Ao.encodeNamed:e.numeric?Ao.encodeNumeric:Ao.encodeRaw},decode:function(e){return e.replace(So,function(e,t){return t?(t="x"===t.charAt(0).toLowerCase()?parseInt(t.substr(1),16):parseInt(t,10))>65535?(t-=65536,String.fromCharCode(55296+(t>>10),56320+(1023&t))):ko[t]||String.fromCharCode(t):dn[e]||ln[e]||(n=e,(r=Hn.fromTag("div").dom()).innerHTML=n,r.textContent||r.innerText||n);var n,r})}},_o={},Bo={},Ro=Ot.makeMap,Do=Ot.each,Oo=Ot.extend,Po=Ot.explode,Io=Ot.inArray,Lo=function(e,t){return(e=Ot.trim(e))?e.split(t||" "):[]},Mo=function(e,t){var n;return e&&(n={},"string"==typeof e&&(e={"*":e}),Do(e,function(e,r){n[r]=n[r.toUpperCase()]="map"===t?Ro(e,/[, ]/):Po(e,/[, ]/)})),n},Fo=function(e){var t,n,r,o,i,a,s,u,c,l,f,d,m,p,g,h,v,y,b,C,x,w,N,E={},S={},k={},T=[],A={},_={},B=function(t,n,r){var o=e[t];return o?o=Ro(o,/[, ]/,Ro(o.toUpperCase(),/[, ]/)):(o=_o[t])||(o=Ro(n," ",Ro(n.toUpperCase()," ")),o=Oo(o,r),_o[t]=o),o};p=(e=e||{}).schema,x={},w=function(e,t,n){var r,o,i,a=function(e,t){var n,r,o={};for(n=0,r=e.length;n