完成前端加密

This commit is contained in:
2019-06-11 21:23:29 +08:00
parent 7473c0e36a
commit 7a3105c414
49 changed files with 769 additions and 738 deletions
Executable
+5
View File
@@ -0,0 +1,5 @@
#!/usr/bin/env sh
cd target
scp bill-1.0.0-SNAPSHOT.jar gc:/tmp
ssh gc "sudo cp /tmp/bill-1.0.0-SNAPSHOT.jar /home/bill/service ; sudo . /home/bill/service/run.sh"
+10 -1
View File
@@ -8,7 +8,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>net.alawn</groupId>
<artifactId>bill</artifactId>
<packaging>war</packaging>
<packaging>jar</packaging>
<version>1.0.0-SNAPSHOT</version>
<name>bill</name>
@@ -34,6 +34,11 @@
<!-- <artifactId>jjwt</artifactId>-->
<!-- <version>0.9.1</version>-->
<!-- </dependency>-->
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>1.9</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
@@ -201,6 +206,10 @@
</includes>
</configuration>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
Executable
+11
View File
@@ -0,0 +1,11 @@
#!/usr/bin/env sh
pid=`ps -ef | grep bill| grep java | awk '{print $2}'`
if [ $pid ]; then
echo $pid
kill -9 $pid
fi
> nohup.out
nohup java -jar target/bill-1.0.0-SNAPSHOT.jar 2>/dev/null &
@@ -1,7 +1,9 @@
package net.alawn.bill.config;
import lombok.extern.slf4j.Slf4j;
import net.alawn.bill.security.JwtAuthenticationEntryPoint;
import net.alawn.bill.security.JwtAuthenticationTokenFilter;
import net.alawn.bill.util.AesEncryptUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
@@ -34,6 +36,8 @@ import java.util.Arrays;
* @Description:
* @Date: Create in 16:53 2019-05-17
*/
@Slf4j
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
@@ -70,6 +74,7 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
/** 装载BCrypt密码编码器 */
@Bean
public PasswordEncoder passwordEncoder() {
log.info("passwordEncoder()");
return new BCryptPasswordEncoder();
}
@@ -154,12 +159,18 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
//对密码进行加密
@Override
public String encode(CharSequence charSequence) {
System.out.println(charSequence.toString());
log.info("encode password:" + charSequence.toString());
// String password = AesEncryptUtil.decrypt(charSequence.toString());
// return DigestUtils.md5DigestAsHex(password.getBytes());
return DigestUtils.md5DigestAsHex(charSequence.toString().getBytes());
}
//对密码进行判断匹配
@Override
public boolean matches(CharSequence charSequence, String s) {
log.info("matches passwordRaw :" + charSequence.toString());
log.info("matches passwordDb:" + charSequence.toString());
// String password = AesEncryptUtil.decrypt(charSequence.toString());
// String encode = DigestUtils.md5DigestAsHex(password.getBytes());
String encode = DigestUtils.md5DigestAsHex(charSequence.toString().getBytes());
boolean res = s.equals( encode );
return res;
@@ -49,7 +49,7 @@ public class BasicController {
@RequestMapping(value = "/posts",method = RequestMethod.POST)
@ResponseBody
public ResponseEntity<Post> addPost(@RequestBody NewPost newPost, Principal principal) throws URISyntaxException {
public ResponseEntity<Post> addPost(@RequestBody NewPost newPost) {
AuthUser authUser = (AuthUser) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
long userId = authUser.getId();
@@ -90,7 +90,7 @@ public class BasicController {
@RequestMapping(value = "/merchants",method = RequestMethod.POST)
@ResponseBody
public ResponseEntity<Merchant> addMerchant(@RequestBody NewMerchant newMerchant) throws URISyntaxException {
public ResponseEntity<Merchant> addMerchant(@RequestBody NewMerchant newMerchant) {
/** 登记 Post */
Merchant merchant = merchantRepo.save(newMerchant.build());
merchantRepo.flush();
@@ -99,7 +99,7 @@ public class BasicController {
@RequestMapping(value = "/merchants",method = RequestMethod.GET)
@ResponseBody
public ResponseEntity<List<Merchant>> fetchMerchants() throws URISyntaxException {
public ResponseEntity<List<Merchant>> fetchMerchants() {
List<Merchant> merchants = merchantRepo.findAll();
return ResponseEntity.ok(merchants);
}
@@ -0,0 +1,41 @@
package net.alawn.bill.entity.po;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import lombok.*;
import org.hibernate.annotations.DynamicUpdate;
import org.hibernate.annotations.NotFound;
import org.hibernate.annotations.NotFoundAction;
import javax.persistence.*;
@Data
@Builder
@Entity
@Table(name = "t_bill_premission")
@ToString(exclude = {"user"})
@NoArgsConstructor
@AllArgsConstructor
@JsonIgnoreProperties(value = { "hibernateLazyInitializer", "handler" },ignoreUnknown = true)
@DynamicUpdate
public class Premission extends EntityId{
@ManyToOne(fetch = FetchType.LAZY)
@NotFound(action = NotFoundAction.IGNORE)
// @JoinColumn(name = "userId", referencedColumnName = "id")
@JoinColumn(name = "userId", referencedColumnName = "id")
// @MapsId("username")
@MapsId("id")
private User user;
@Override
public boolean equals(Object o) {
return this == o || o instanceof Premission
&& id != null
&& id.equals(((Premission) o).id);
}
@Override
public int hashCode() {
return 31;
}
}
@@ -24,6 +24,7 @@ public enum RespMsg {
NO_DATA ( "200017", RespMsgConstants.NO_DATA ) ,
USER_ALREADY_EXIST ( "200018", RespMsgConstants.USER_ALREADY_EXIST ) ,
USER_NOT_EXIST_OR_PASSWORD_INCORRECT ( "200019", RespMsgConstants.USER_NOT_EXIST_OR_PASSWORD_INCORRECT ) ,
USERNAME_OR_PASSWORD_IS_EMPTY ( "200020", RespMsgConstants.USERNAME_OR_PASSWORD_IS_EMPTY ) ,
SUCCESS ( "000000", RespMsgConstants.SUCCESS ) ;
@@ -7,22 +7,23 @@ package net.alawn.bill.exception;
*/
public final class RespMsgConstants {
public final static String NO_STORAGE_FOUND = "没有发现存储" ;
public final static String NO_STORAGE_FOUND = "没有发现存储" ;
public final static String BLOB_DUPLICATE = "发现重复Blob" ;
public final static String BLOB_NOT_EXIST = "Blob对象不存在" ;
public final static String NO_BLOB_FOUND = "Blob对象不存在" ;
public final static String BLOB_NOT_EXIST = "Blob对象不存在" ;
public final static String NO_BLOB_FOUND = "Blob对象不存在" ;
public final static String BUCKET_DUPLICATE = "发现重复Bucket" ;
public final static String FILE_HANDLING = "文件处理异常" ;
public final static String FILE_NOT_EXIST = "文件不存在" ;
public final static String FILE_OCCUPIED = "文件/文件夹已被占用" ;
public final static String NO_ACCESS_PERMISSION = "没有该访问权限" ;
public final static String FILE_HANDLING = "文件处理异常" ;
public final static String FILE_NOT_EXIST = "文件不存在" ;
public final static String FILE_OCCUPIED = "文件/文件夹已被占用" ;
public final static String NO_ACCESS_PERMISSION = "没有该访问权限" ;
public final static String NO_BUCKET_FOUND = "没有找到Bucket" ;
public final static String NO_DEFAULT_BUCKET = "没有找到默认Bucket" ;
public final static String NO_DEFAULT_BUCKET = "没有找到默认Bucket" ;
public final static String NO_DEFAULT_STORAGE = "没有找到默认存储设备" ;
public final static String REQUEST_PARAMS = "请求参数有误" ;
public final static String INTERNAL_ERROR = "内部错误" ;
public final static String NO_DATA = "没有数据" ;
public final static String USER_ALREADY_EXIST = "用户已存在" ;
public final static String USER_NOT_EXIST_OR_PASSWORD_INCORRECT = "用户不存在或密码不正确" ;
public final static String USERNAME_OR_PASSWORD_IS_EMPTY = "用户名或密码不得为空" ;
public final static String SUCCESS = "业务成功" ;
}
@@ -11,6 +11,6 @@ import org.springframework.web.HttpRequestMethodNotSupportedException;
*/
public interface AuthService {
NewUser register(NewUser userDTO) throws HttpRequestMethodNotSupportedException, AuthenticationException;
String login(String username, String password);
String login(String username, String password) throws AuthenticationException;
String refresh(String oldToken);
}
@@ -1,5 +1,6 @@
package net.alawn.bill.service.impl;
import lombok.extern.slf4j.Slf4j;
import net.alawn.bill.entity.dto.NewUser;
import net.alawn.bill.entity.po.Authority;
import net.alawn.bill.entity.po.AuthorityId;
@@ -11,6 +12,7 @@ import net.alawn.bill.repository.UserRepo;
import net.alawn.bill.security.AuthUser;
import net.alawn.bill.security.JwtTokenUtil;
import net.alawn.bill.service.AuthService;
import net.alawn.bill.util.AesEncryptUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
@@ -23,6 +25,7 @@ import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
/**
@@ -31,6 +34,7 @@ import org.springframework.stereotype.Service;
* @Date: Create in 15:59 2019-05-20
*/
@Slf4j
@Service
//@AllArgsConstructor(onConstructor = @__(@Autowired))
public class AuthServiceImpl implements AuthService {
@@ -62,6 +66,8 @@ public class AuthServiceImpl implements AuthService {
@Override
public NewUser register(NewUser userDTO) throws AuthenticationException {
log.info("AuthServiceImpl.register start");
log.info("userDTO:" + userDTO);
final String username = userDTO.getUsername();
if(userRepo.findByUsername(username) != null) {
@@ -69,10 +75,14 @@ public class AuthServiceImpl implements AuthService {
}
BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
final String rawPassword = userDTO.getPassword();
// final String rawPassword = userDTO.getPassword();
final String password = AesEncryptUtil.decrypt(userDTO.getPassword());
User user = User.builder()
.username(username)
.password(encoder.encode(rawPassword)).build();
.password(encoder.encode(password)).build();
// .password(userDTO.getPassword()).build();
User saved = userRepo.save(user);
Authority savedAuth = authorityRepo.save(
Authority.builder()
@@ -90,11 +100,26 @@ public class AuthServiceImpl implements AuthService {
@Override
public String login(String username, String password) {
// public String login(String username, String passwordAES) {
/** 为空检查*/
// if(StringUtils.isEmpty(username) || StringUtils.isEmpty(passwordAES)) {
// throw new CommonException(RespMsg.USER_NOT_EXIST_OR_PASSWORD_INCORRECT);
// }
// BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
password = AesEncryptUtil.decrypt(password);
UsernamePasswordAuthenticationToken upToken = new UsernamePasswordAuthenticationToken(username, password);
final Authentication authentication = authenticationManager.authenticate(upToken);
SecurityContextHolder.getContext().setAuthentication(authentication);
final UserDetails userDetails = userDetailsService.loadUserByUsername(username);
// if(!encoder.matches(password,userDetails.getPassword())) {
// throw new CommonException(RespMsg.USER_NOT_EXIST_OR_PASSWORD_INCORRECT);
// }
final String token = jwtTokenUtil.generateToken(userDetails);
return token;
}
@@ -0,0 +1,128 @@
package net.alawn.bill.util;
import javax.crypto.*;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.UnsupportedEncodingException;
import java.nio.charset.Charset;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
/**
* @Author: Alawn
* @Description:
* @Date: Create in 17:27 2019-06-11
*/
public class AesEncryptUtil {
private static String iv = "0123456789ABCDEF";//偏移量字符串必须是16位 当模式是CBC的时候必须设置偏移量
private static String Algorithm = "AES";
private static String AlgorithmProvider = "AES/CBC/PKCS5Padding"; //算法/模式/补码方式
public static byte[] generatorKey() throws NoSuchAlgorithmException {
KeyGenerator keyGenerator = KeyGenerator.getInstance(Algorithm);
keyGenerator.init(256);//默认128,获得无政策权限后可为192或256
SecretKey secretKey = keyGenerator.generateKey();
return secretKey.getEncoded();
}
public static IvParameterSpec getIv() throws UnsupportedEncodingException {
IvParameterSpec ivParameterSpec = new IvParameterSpec(iv.getBytes("utf-8"));
return ivParameterSpec;
}
/**
* 加密
* @param src
* @param key
* @return
* @throws NoSuchAlgorithmException
* @throws NoSuchPaddingException
* @throws InvalidKeyException
* @throws IllegalBlockSizeException
* @throws BadPaddingException
* @throws UnsupportedEncodingException
* @throws InvalidAlgorithmParameterException
*/
public static byte[] encrypt(String src, byte[] key) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException, UnsupportedEncodingException, InvalidAlgorithmParameterException {
SecretKey secretKey = new SecretKeySpec(key, Algorithm);
IvParameterSpec ivParameterSpec = getIv();
Cipher cipher = Cipher.getInstance(AlgorithmProvider);
cipher.init(Cipher.ENCRYPT_MODE, secretKey, ivParameterSpec);
byte[] cipherBytes = cipher.doFinal(src.getBytes(Charset.forName("utf-8")));
return cipherBytes;
}
/**
* 解密
* @param src 原密码
* @param key 解密后的byte数组
* @return
* @throws Exception
*/
public static byte[] decrypt(String src, byte[] key) throws Exception {
SecretKey secretKey = new SecretKeySpec(key, Algorithm);
IvParameterSpec ivParameterSpec = getIv();
Cipher cipher = Cipher.getInstance(AlgorithmProvider);
cipher.init(Cipher.DECRYPT_MODE, secretKey, ivParameterSpec);
byte[] hexBytes = hexStringToBytes(src);
byte[] plainBytes = cipher.doFinal(hexBytes);
return plainBytes;
}
/**
* 将byte转换为16进制字符串
* @param src
* @return
*/
public static String byteToHexString(byte[] src) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < src.length; i++) {
int v = src[i] & 0xff;
String hv = Integer.toHexString(v);
if (hv.length() < 2) {
sb.append("0");
}
sb.append(hv);
}
return sb.toString();
}
/**
* 将16进制字符串装换为byte数组
* @param hexString
* @return
*/
public static byte[] hexStringToBytes(String hexString) {
hexString = hexString.toUpperCase();
int length = hexString.length() / 2;
char[] hexChars = hexString.toCharArray();
byte[] b = new byte[length];
for (int i = 0; i < length; i++) {
int pos = i * 2;
b[i] = (byte) (charToByte(hexChars[pos]) << 4 | charToByte(hexChars[pos + 1]));
}
return b;
}
private static byte charToByte(char c) {
return (byte) "0123456789ABCDEF".indexOf(c);
}
/**
* 直接传入psd然后解码进行验证使用
* @param psd 前台传过来的String 加密后的数组
* @return
*/
public static String decrypt(String psd) {
try {
byte key[] = "1234567890ABCDEF1234567890ABCDEf".getBytes("utf-8");
return new String(decrypt(psd, key));
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
}
-18
View File
@@ -1,18 +0,0 @@
{
"presets": [
["env", {
"modules": false,
"targets": {
"browsers": ["> 1%", "last 2 versions", "not ie <= 8"]
}
}],
"stage-2"
],
"plugins": ["transform-vue-jsx", "transform-runtime"],
"env": {
"test": {
"presets": ["env", "stage-2"],
"plugins": ["transform-vue-jsx", "transform-es2015-modules-commonjs", "dynamic-import-node"]
}
}
}
-9
View File
@@ -1,9 +0,0 @@
root = true
[*]
charset = utf-8
indent_style = space
indent_size = 2
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
-5
View File
@@ -1,5 +0,0 @@
/build/
/config/
/dist/
/*.js
/test/unit/coverage/
-29
View File
@@ -1,29 +0,0 @@
// https://eslint.org/docs/user-guide/configuring
module.exports = {
root: true,
parserOptions: {
parser: 'babel-eslint'
},
env: {
browser: true,
},
extends: [
// https://github.com/vuejs/eslint-plugin-vue#priority-a-essential-error-prevention
// consider switching to `plugin:vue/strongly-recommended` or `plugin:vue/recommended` for stricter rules.
'plugin:vue/essential',
// https://github.com/standard/standard/blob/master/docs/RULES-en.md
'standard'
],
// required to lint *.vue files
plugins: [
'vue'
],
// add your custom rules here
rules: {
// allow async-await
'generator-star-spacing': 'off',
// allow debugger during development
'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off'
}
}
-17
View File
@@ -1,17 +0,0 @@
.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
-10
View File
@@ -1,10 +0,0 @@
// 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": {}
}
}
+25 -26
View File
@@ -1,30 +1,29 @@
# bill
> bill
## Build Setup
``` bash
# install dependencies
npm install
# serve with hot reload at localhost:8080
npm run dev
# build for production with minification
npm run build
# build for production and view the bundle analyzer report
npm run build --report
# run unit tests
npm run unit
# run e2e tests
npm run e2e
# run all tests
npm test
## Project setup
```
yarn install
```
For a detailed explanation on how things work, check out the [guide](http://vuejs-templates.github.io/webpack/) and [docs for vue-loader](http://vuejs.github.io/vue-loader).
### Compiles and hot-reloads for development
```
yarn run serve
```
### Compiles and minifies for production
```
yarn run build
```
### Run your tests
```
yarn run test
```
### Lints and fixes files
```
yarn run lint
```
### Customize configuration
See [Configuration Reference](https://cli.vuejs.org/config/).
+5
View File
@@ -0,0 +1,5 @@
module.exports = {
presets: [
'@vue/app'
]
}
-7
View File
@@ -1,7 +0,0 @@
'use strict'
const merge = require('webpack-merge')
const prodEnv = require('./prod.env')
module.exports = merge(prodEnv, {
NODE_ENV: '"development"'
})
-76
View File
@@ -1,76 +0,0 @@
'use strict'
// Template version: 1.3.1
// 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: 8080, // can be overwritten by process.env.PORT, if port is in use, a free one will be determined
autoOpenBrowser: false,
errorOverlay: true,
notifyOnErrors: true,
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-module-eval-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,
cssSourceMap: true
},
build: {
// Template for index.html
index: path.resolve(__dirname, '../dist/index.html'),
// Paths
assetsRoot: path.resolve(__dirname, '../dist'),
assetsSubDirectory: 'static',
assetsPublicPath: '/',
/**
* Source Maps
*/
productionSourceMap: true,
// 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
}
}
-4
View File
@@ -1,4 +0,0 @@
'use strict'
module.exports = {
NODE_ENV: '"production"'
}
-7
View File
@@ -1,7 +0,0 @@
'use strict'
const merge = require('webpack-merge')
const devEnv = require('./dev.env')
module.exports = merge(devEnv, {
NODE_ENV: '"testing"'
})
+9
View File
@@ -0,0 +1,9 @@
#!/usr/bin/env sh
cd dist
tar czvf ../dist.tar.gz *
cd ..
scp dist.tar.gz gc:/tmp/
ssh gc "sudo tar xzvf /tmp/dist.tar.gz -C /home/bill/www;sudo systemctl reload nginx"
-12
View File
@@ -1,12 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<title>bill</title>
</head>
<body>
<div id="app"></div>
<!-- built files will be auto injected -->
</body>
</html>
+36 -76
View File
@@ -1,92 +1,52 @@
{
"name": "bill",
"version": "1.0.0",
"description": "bill",
"author": "alawn.cn@gmail.com",
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "webpack-dev-server --inline --progress --config build/webpack.dev.conf.js",
"start": "npm run dev",
"unit": "jest --config test/unit/jest.conf.js --coverage",
"e2e": "node test/e2e/runner.js",
"test": "npm run unit && npm run e2e",
"lint": "eslint --ext .js,.vue src test/unit test/e2e/specs",
"build": "node build/build.js"
"serve": "vue-cli-service serve",
"build": "vue-cli-service build",
"lint": "vue-cli-service lint"
},
"dependencies": {
"axios": "^0.18.0",
"element-ui": "^2.8.2",
"file-saver": "^2.0.2",
"axios": "^0.19.0",
"core-js": "^2.6.5",
"crypto-js": "^3.1.9-1",
"element-ui": "^2.9.1",
"moment": "^2.24.0",
"vue": "^2.5.2",
"vue": "^2.6.10",
"vue-json-excel": "^0.2.98",
"vue-router": "^3.0.1"
"vue-router": "^3.0.6"
},
"devDependencies": {
"autoprefixer": "^7.1.2",
"babel-core": "^6.22.1",
"babel-eslint": "^8.2.1",
"babel-helper-vue-jsx-merge-props": "^2.0.3",
"babel-jest": "^24.8.0",
"babel-loader": "^7.1.1",
"babel-plugin-dynamic-import-node": "^1.2.0",
"babel-plugin-syntax-jsx": "^6.18.0",
"babel-plugin-transform-es2015-modules-commonjs": "^6.26.0",
"babel-plugin-transform-runtime": "^6.22.0",
"babel-plugin-transform-vue-jsx": "^3.5.0",
"babel-preset-env": "^1.3.2",
"babel-preset-stage-2": "^6.22.0",
"babel-register": "^6.22.0",
"chalk": "^2.0.1",
"chromedriver": "^2.27.2",
"copy-webpack-plugin": "^4.0.1",
"cross-spawn": "^5.0.1",
"css-loader": "^2.1.1",
"eslint": "^4.15.0",
"eslint-config-standard": "^10.2.1",
"eslint-friendly-formatter": "^3.0.0",
"eslint-loader": "^1.7.1",
"eslint-plugin-import": "^2.7.0",
"eslint-plugin-node": "^5.2.0",
"eslint-plugin-promise": "^3.4.0",
"eslint-plugin-standard": "^3.0.1",
"eslint-plugin-vue": "^4.0.0",
"extract-text-webpack-plugin": "^3.0.0",
"file-loader": "^1.1.4",
"friendly-errors-webpack-plugin": "^1.6.1",
"html-webpack-plugin": "^2.30.1",
"jest": "^24.8.0",
"jest-serializer-vue": "^0.3.0",
"nightwatch": "^1.1.11",
"node-notifier": "^5.1.2",
"optimize-css-assets-webpack-plugin": "^5.0.1",
"ora": "^1.2.0",
"portfinder": "^1.0.13",
"postcss-import": "^11.0.0",
"postcss-loader": "^2.0.8",
"postcss-url": "^7.2.1",
"rimraf": "^2.6.0",
"selenium-server": "^3.0.1",
"semver": "^5.3.0",
"shelljs": "^0.7.6",
"uglifyjs-webpack-plugin": "^1.1.1",
"url-loader": "^1.1.2",
"vue-jest": "^1.0.2",
"vue-loader": "^13.3.0",
"vue-style-loader": "^3.0.1",
"vue-template-compiler": "^2.5.2",
"webpack": "^3.6.0",
"webpack-bundle-analyzer": "^3.3.2",
"webpack-dev-server": "^2.9.1",
"webpack-merge": "^4.1.0"
"@vue/cli-plugin-babel": "^3.8.0",
"@vue/cli-plugin-eslint": "^3.8.0",
"@vue/cli-service": "^3.8.0",
"babel-eslint": "^10.0.1",
"eslint": "^5.16.0",
"eslint-plugin-vue": "^5.0.0",
"vue-template-compiler": "^2.6.10"
},
"engines": {
"node": ">= 6.0.0",
"npm": ">= 3.0.0"
"eslintConfig": {
"root": true,
"env": {
"node": true
},
"extends": [
"plugin:vue/essential",
"eslint:recommended"
],
"rules": {},
"parserOptions": {
"parser": "babel-eslint"
}
},
"postcss": {
"plugins": {
"autoprefixer": {}
}
},
"browserslist": [
"> 1%",
"last 2 versions",
"not ie <= 8"
"last 2 versions"
]
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

+17
View File
@@ -0,0 +1,17 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
<title>bill</title>
</head>
<body>
<noscript>
<strong>We're sorry but bill doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
</noscript>
<div id="app"></div>
<!-- built files will be auto injected -->
</body>
</html>
+17 -2
View File
@@ -1,13 +1,28 @@
<template>
<div id="app">
<!-- <img src="./assets/logo.png"> -->
<!-- <img alt="logo" src="https://fairy8-1258530674.picsh.myqcloud.com/img/20190611100737.png" width="400px"> -->
<span style="color:#0081FF;font-size:22px"><b>Bills Assistant</b></span>
<!-- <HR align=center width=300 color=#987cb9 SIZE=1> -->
<!-- <hr style="border:1 dashed #987cb9" width="50%" color=#ACACAC size=1/> -->
<br/>
<!-- <router-link to="/login">登录页</router-link> -->
<p/>
<router-view/>
<!-- <HelloWorld msg="Welcome to Your Vue.js App"/> -->
<p/>
<img alt="footer" src="https://fairy8-1258530674.picsh.myqcloud.com/img/20190611100310.png" width="150px">
</div>
</template>
<script>
// import HelloWorld from './components/HelloWorld.vue'
export default {
name: 'App'
name: 'app',
/*components: {
HelloWorld
}*/
}
</script>
+11 -3
View File
@@ -1,6 +1,6 @@
import HttpRequest from '@/axios/api.request'
export const addMerchant = (merc) => {
export const newMerchant = (merc) => {
return HttpRequest.request({
url: 'basic/merchants',
method: 'post',
@@ -20,6 +20,14 @@ export const fetchTitles = () => {
method: 'get'
})
}
export const addTitle = (title) => {
return HttpRequest.request({
url: 'basic/titles',
method: 'post',
data: title
})
}
export const getMerchant = (id) => {
return HttpRequest.request({
url: 'basic/merchants/' + id,
@@ -28,11 +36,11 @@ export const getMerchant = (id) => {
}
export const addPost = (merc) => {
export const addPost = (post) => {
return HttpRequest.request({
url: 'basic/posts',
method: 'post',
data: merc
data: post
})
}
+1 -1
View File
@@ -1,6 +1,6 @@
import HttpRequest from '@/axios/api.request'
export const UserLoginAction = (ruleForm) => {
export const login = (ruleForm) => {
return HttpRequest.request({
url: 'auth',
method: 'post',
+1 -1
View File
@@ -6,6 +6,6 @@ const HttpRequest = axios.create({
// api的base_url
baseURL: baseUrl,
// 请求超时时间
timeout: 5000
timeout: 10000
})
export default HttpRequest
+8 -9
View File
@@ -1,10 +1,9 @@
export default {
/**
* @description api请求基础路径
*/
baseUrl: {
dev: 'http://localhost:30000/',
pro: 'http://localhost:30000/'
// pro: 'http://101.132.124.171:8080/demo-1.0/api/'
}
}
/**
* @description api请求基础路径
*/
baseUrl: {
dev: 'http://localhost:30000',
pro: 'https://bill.fairy8.cn/api'
}
};
+26 -81
View File
@@ -1,84 +1,31 @@
<template>
<div class="hello">
<h1>{{ msg }}</h1>
<h2>Essential Links</h2>
<p>
For a guide and recipes on how to configure / customize this project,<br>
check out the
<a href="https://cli.vuejs.org" target="_blank" rel="noopener">vue-cli documentation</a>.
</p>
<h3>Installed CLI Plugins</h3>
<ul>
<li>
<a
href="https://vuejs.org"
target="_blank"
>
Core Docs
</a>
</li>
<li>
<a
href="https://forum.vuejs.org"
target="_blank"
>
Forum
</a>
</li>
<li>
<a
href="https://chat.vuejs.org"
target="_blank"
>
Community Chat
</a>
</li>
<li>
<a
href="https://twitter.com/vuejs"
target="_blank"
>
Twitter
</a>
</li>
<br>
<li>
<a
href="http://vuejs-templates.github.io/webpack/"
target="_blank"
>
Docs for This Template
</a>
</li>
<li><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-babel" target="_blank" rel="noopener">babel</a></li>
<li><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-eslint" target="_blank" rel="noopener">eslint</a></li>
</ul>
<h2>Ecosystem</h2>
<h3>Essential Links</h3>
<ul>
<li>
<a
href="http://router.vuejs.org/"
target="_blank"
>
vue-router
</a>
</li>
<li>
<a
href="http://vuex.vuejs.org/"
target="_blank"
>
vuex
</a>
</li>
<li>
<a
href="http://vue-loader.vuejs.org/"
target="_blank"
>
vue-loader
</a>
</li>
<li>
<a
href="https://github.com/vuejs/awesome-vue"
target="_blank"
>
awesome-vue
</a>
</li>
<li><a href="https://vuejs.org" target="_blank" rel="noopener">Core Docs</a></li>
<li><a href="https://forum.vuejs.org" target="_blank" rel="noopener">Forum</a></li>
<li><a href="https://chat.vuejs.org" target="_blank" rel="noopener">Community Chat</a></li>
<li><a href="https://twitter.com/vuejs" target="_blank" rel="noopener">Twitter</a></li>
<li><a href="https://news.vuejs.org" target="_blank" rel="noopener">News</a></li>
</ul>
<h3>Ecosystem</h3>
<ul>
<li><a href="https://router.vuejs.org" target="_blank" rel="noopener">vue-router</a></li>
<li><a href="https://vuex.vuejs.org" target="_blank" rel="noopener">vuex</a></li>
<li><a href="https://github.com/vuejs/vue-devtools#vue-devtools" target="_blank" rel="noopener">vue-devtools</a></li>
<li><a href="https://vue-loader.vuejs.org" target="_blank" rel="noopener">vue-loader</a></li>
<li><a href="https://github.com/vuejs/awesome-vue" target="_blank" rel="noopener">awesome-vue</a></li>
</ul>
</div>
</template>
@@ -86,18 +33,16 @@
<script>
export default {
name: 'HelloWorld',
data () {
return {
msg: 'Welcome to Your Vue.js App'
}
props: {
msg: String
}
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
h1, h2 {
font-weight: normal;
h3 {
margin: 40px 0 0;
}
ul {
list-style-type: none;
+62 -11
View File
@@ -1,18 +1,18 @@
// The Vue build version to load with the `import` command
// (runtime-only or standalone) has been set in webpack.base.conf with an alias.
import Vue from 'vue'
import App from './App'
import router from './router'
import App from './App.vue'
import router from '@/router/index.js'
import ElementUI from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css'
import HttpRequest from '@/axios/api.request'
import moment from "moment";
import JsonExcel from 'vue-json-excel'
Vue.config.productionTip = false
Vue.component('downloadExcel', JsonExcel)
Vue.use(ElementUI)
Vue.prototype.$moment = moment;
Vue.config.productionTip = false
HttpRequest.interceptors.request.use(
config => {
@@ -28,10 +28,61 @@ HttpRequest.interceptors.request.use(
}
)
/* eslint-disable no-new */
new Vue({
el: '#app',
router,
components: { App },
template: '<App/>'
// http response 拦截器
HttpRequest.interceptors.response.use(
response => {
return response;
},
error => {
if (error.response) {
switch (error.response.status) {
case 401:
// 返回 401 清除token信息并跳转到登录页面
// store.commit(types.LOGOUT);
localStorage.removeItem('token');
router.replace({
path: 'login',
query: {redirect: router.currentRoute.fullPath}
})
}
}
return Promise.reject(error.response.data) // 返回接口返回的错误信息
});
router.beforeEach((to, from, next) => {
if (to.meta.requireAuth) { // 判断该路由是否需要登录权限
if (localStorage.token) {
next();
}
else {
next({
path: '/login',
query: {redirect: to.fullPath} // 将跳转的路由path作为参数,登录成功后跳转到该路由
})
}
// if (store.state.token) {
// next();
// }
// else {
// next({
// path: '/login',
// query: {redirect: to.fullPath}
// })
// }
}
else {
next();
}
})
new Vue({
router,
render: h => h(App),
}).$mount('#app')
+19 -11
View File
@@ -1,40 +1,48 @@
import Vue from 'vue'
import Router from 'vue-router'
import HelloWorld from '@/components/HelloWorld'
import VueRouter from 'vue-router'
/** import HelloWorld from '@/components/HelloWorld'*/
Vue.use(VueRouter)
Vue.use(Router)
export default new Router({
export default new VueRouter({
routes: [
{
path: '/',
name: 'Index',
redirect: '/login'
redirect: '/gird'
},
{
path: '/merchant',
name: 'Merchant',
component: () => import(/* webpackChunkName: "merchant" */ '../views/Merchant.vue')
meta: {
requireAuth: true, // 添加该字段,表示进入这个路由是需要登录的
},
component: () => import('../views/Merchant.vue')
},
{
path: '/register',
name: 'Register',
component: () => import(/* webpackChunkName: "merchant" */ '../views/Register.vue')
component: () => import('../views/Register.vue')
},
{
path: '/login',
name: 'Login',
component: () => import(/* webpackChunkName: "merchant" */ '../views/Login.vue')
component: () => import('../views/Login.vue')
},
{
path: '/bill',
name: 'Bill',
component: () => import(/* webpackChunkName: "merchant" */ '../views/Bill.vue')
meta: {
requireAuth: true, // 添加该字段,表示进入这个路由是需要登录的
},
component: () => import('../views/Bill.vue')
},
{
path: '/gird',
name: 'Gird',
component: () => import(/* webpackChunkName: "merchant" */ '../views/Gird.vue')
meta: {
requireAuth: true, // 添加该字段,表示进入这个路由是需要登录的
},
component: () => import('../views/Gird.vue')
}
]
})
+50
View File
@@ -0,0 +1,50 @@
import CryptoJS from 'crypto-js/crypto-js'
// 默认的 KEY 与 iv
const KEY = CryptoJS.enc.Hex.parse("3132333435363738393041424344454631323334353637383930414243444566");
const IV = CryptoJS.enc.Hex.parse('30313233343536373839414243444546');
/**
* AES加密
*/
export function encrypt(word, keyStr, ivStr) {
let key = KEY
let iv = IV
if (keyStr) {
key = CryptoJS.enc.Utf8.parse(keyStr);
iv = CryptoJS.enc.Utf8.parse(ivStr);
}
// 传过来的密码
let srcs = CryptoJS.enc.Utf8.parse(word);
var encrypted = CryptoJS.AES.encrypt(srcs, key, {
iv: iv,
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.Pkcs7
});
// console.log("-=-=-=-", encrypted.ciphertext)
return encrypted.ciphertext.toString()
}
/**
* AES 解密
*
*/
export function decrypt(word, keyStr, ivStr) {
let key = KEY
let iv = IV
if (keyStr) {
key = CryptoJS.enc.Utf8.parse(keyStr);
iv = CryptoJS.enc.Utf8.parse(ivStr);
}
let src = CryptoJS.format.Hex.parse(word);
var decrypt = CryptoJS.AES.decrypt(src, key, {
iv: iv,
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.Pkcs7
});
return CryptoJS.enc.Utf8.stringify(decrypt)
}
+165 -89
View File
@@ -30,7 +30,7 @@
</el-col>
<el-col :span="3">
<el-form-item label="" label-width="80px">
<el-checkbox v-model="bill.sentQuick" checked="true" :disabled="disabled">立即发送</el-checkbox>
<el-checkbox v-model="bill.sentQuick" :checked="true" :disabled="disabled">立即发送</el-checkbox>
</el-form-item>
</el-col>
</el-row>
@@ -41,7 +41,7 @@
<!-- <el-form-item label="明细" prop="username"> -->
<!--分割线-->
<!-- <span><el-divider></el-divider></span> -->
<el-table :data="bill.billItems" height="400px" style="width: 100%">
<el-table :data="bill.billItems" height="400px" style="width: 100%" >
<el-table-column prop="dating" label="日期" width="100"></el-table-column>
<el-table-column prop="titleAbbr" label="抬头" width="150">
<template slot-scope="scope">
@@ -57,13 +57,13 @@
<el-table-column prop="merchantAbbr" label="餐饮商户" width="150"></el-table-column>
<el-table-column prop="amount" label="金额" width="80"></el-table-column>
<el-table-column align="right">
<template slot="header" slot-scope="scope">
<template slot="header">
<!-- <el-button class="itemBtn" type="primary" size="medium" @click="clearFocusRow();isAddShow = true;resetForm('addForm');" :disabled="disabled">添加</el-button> -->
<el-button
class="itemBtn"
type="primary"
size="small"
@click="clearFocusRow();isAddShow = true;resetForm('addForm');"
@click="clearFocusRow();isAddShow = true"
:disabled="disabled"
>添加</el-button>
</template>
@@ -106,7 +106,7 @@
<!--增加-->
<div width="600px">
<el-dialog title="增加" :visible.sync="isAddShow" width="700px">
<el-dialog title="增加" :visible.sync="isAddShow" width="700px" @close="resetFormFields('addForm')">
<el-form ref="addForm" :model="focusRow" :rules="rules" label-width="80px">
<el-row>
<el-col :span="24">
@@ -148,7 +148,7 @@
<i
class="el-icon-edit el-input__icon"
slot="suffix"
@click="handleIconClickTitles"
@click="handleIconClickMerchant"
></i>
<template slot-scope="{ item }">
<div class="name">{{ item.abbr }}</div>
@@ -187,17 +187,16 @@
</el-form-item>
</el-col>
</el-row>
<el-form-item>
<el-button type="primary" @click="addItem('addForm')" :disabled="disabled">确 定</el-button>
<el-button @click="resetFormFields('addForm')">重置</el-button>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button type="primary" @click="addItem('addForm')" :disabled="disabled">确 定</el-button>
<el-button @click="resetForm('addForm')">重置</el-button>
</div>
</el-dialog>
</div>
<!--编辑-->
<div width="600px">
<el-dialog title="编辑" :visible.sync="isEditShow" width="700px">
<el-dialog title="编辑" :visible.sync="isEditShow" width="700px" @close="resetFormFields('editForm')">
<el-form ref="editForm" :model="focusRow" :rules="rules" label-width="80px">
<el-row>
<el-col :span="24">
@@ -236,11 +235,7 @@
placeholder="请选择餐饮商户"
@select="handleSelectMerchant"
>
<i
class="el-icon-edit el-input__icon"
slot="suffix"
@click="handleIconClickTitles"
></i>
<i class="el-icon-edit el-input__icon" slot="suffix" @click="handleIconClickMerchant" ></i>
<template slot-scope="{ item }">
<div class="name">{{ item.abbr }}</div>
<span class="subtitle">{{ item.name }}</span>
@@ -278,12 +273,60 @@
</el-form-item>
</el-col>
</el-row>
</el-form>
<div slot="footer" class="dialog-footer">
<!-- <div slot="footer" class="dialog-footer"> -->
<el-form-item>
<el-button type="primary" @click="updateItem('editForm')" :disabled="disabled">确 定</el-button>
<!-- <el-button type="primary" @click="updateItem()" >确 定</el-button> -->
<el-button @click="resetForm('editForm')">重置</el-button>
</div>
<el-button @click="resetFormFields('editForm')">重置</el-button>
</el-form-item>
<!-- </div> -->
</el-form>
</el-dialog>
</div>
<!--添加邮寄地址-->
<div width="600px">
<el-dialog title="增加地址" :visible.sync="isPostEditShow" width="700px">
<el-form ref="addPost" :model="post" :rules="rules" label-width="80px">
<!--地址-->
<el-form-item label="地址">
<el-input v-model="post.address"></el-input>
</el-form-item>
<el-form-item label="联系人">
<el-input v-model="post.contact"></el-input>
</el-form-item>
<el-form-item label="联系电话">
<el-input v-model="post.mobile"></el-input>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="addPost()">添加</el-button>
<!-- <el-button>取消</el-button> -->
</el-form-item>
<!-- <div slot="footer" class="dialog-footer">
<el-button type="primary" @click="addItem('addForm')" :disabled="disabled">确 定</el-button>
<el-button @click="resetForm('addForm')">重置</el-button>
</div> -->
</el-form>
</el-dialog>
</div>
<!--添加抬头-->
<div width="600px">
<el-dialog title="增加抬头" :visible.sync="isTitleEditShow" width="700px">
<el-form ref="addTitle" :model="title" :rules="rules" label-width="80px">
<!--地址-->
<el-form-item label="简称">
<el-input v-model="title.abbr"></el-input>
</el-form-item>
<el-form-item label="抬头">
<el-input v-model="title.contact"></el-input>
</el-form-item>
<el-form-item label="税号">
<el-input v-model="title.taxNo"></el-input>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="addTitle()">添加</el-button>
</el-form-item>
</el-form>
</el-dialog>
</div>
</div>
@@ -292,7 +335,8 @@
<script>
import {
newPost,
addTitle,
addPost,
fetchPosts,
getPost,
fetchTitles,
@@ -305,6 +349,8 @@ export default {
return {
isAddShow: false,
isEditShow: false,
isPostEditShow: false,
isTitleEditShow: false,
posts: [],
titles: [],
merchants: [],
@@ -312,13 +358,15 @@ export default {
editBtnDisabled: false,
disabled: false,
// focusRow: { index: null,dating: "2019-05-28", titleId: null, titleAbbr: "上海沐融",titleName:"",taxNo:"",merchantId: null, merchantAbbr: "华容月",amount: 0.0 },
post:{address:"",contact:"",mobile:""},
title:{abbr:"",name:"",taxNo:""},
focusRow: {},
bill: {
postId: "",
// postAddress: "",
postContact: "",
// postMobile : "",
postId: "",
// postId: "",
// postAddress: "",
sendQuick: false,
billItems: [
@@ -349,14 +397,30 @@ export default {
};
},
methods: {
resetFormFields(formName){
console.info('resetFormFields.formName:' + formName);
this.$nextTick(()=>{
console.info('resetFormFields.resetFields');
this.$refs[formName].resetFields();
});
},
addPost() {
addPost(this.post).then(res=>{
this.loadPosts();
this.isPostEditShow = false;
this.$message({ message: "提交成功", type: "success" });
})
},
cancel() {
this.$router.go(-1);
},
resetForm(formName) {
this.$refs[formName].resetFields();
// this.disabled = false;
// this.clearFocusRow();
},
// resetForm(formName) {
// console.info('formName:' + formName);
// // console.info('this.$refs' + JSON.stringify(this.$refs));
// this.$refs[formName].resetFields();
// // this.disabled = false;
// // this.clearFocusRow();
// },
/** 在表格中添加明细 */
addItem(formName) {
var oriForm = formName;
@@ -374,7 +438,6 @@ export default {
this.clearFocusRow();
console.info("focusRow:" + JSON.stringify(this.focusRow));
this.isAddShow = false;
// this.resetForm(oriForm);
} else {
console.log("error submit!!");
return false;
@@ -383,6 +446,7 @@ export default {
},
/** 在表格中添加明细 */
updateItem(formName) {
var oriForm = formName;
this.$refs[formName].validate(valid => {
if (valid) {
console.info("index:" + index);
@@ -456,44 +520,30 @@ export default {
}
});
},
reset(formName) {
// this.$refs[formName].resetFields();
// this.disabled = false;
},
// querySearch(queryString, cb) {
// var posts = this.posts;
// console.error("posts:" + posts);
// var results = queryString ? posts.filter(this.createFilter(queryString)) : posts;
// // 调用 callback 返回建议列表的数据
// cb(results);
// },
/** AutoComplete Posts 处理*/
querySearchAsyncPosts(queryString, cb) {
var list = [{}];
fetchPosts().then(function(response) {
for (let i of response.data) {
i.value = i.contact; //将想要展示的数据作为value
// i.value = i.contact; //将想要展示的数据作为value
}
list = response.data;
cb(list);
});
// var posts = this.posts;
// console.error("posts:" + posts);
// var results = queryString ? posts.filter(this.createFilter(queryString)) : posts;
// // 调用 callback 返回建议列表的数据
// cb(results);
var results = queryString ? this.posts.filter(this.createFilterPosts(queryString)):this.posts;
cb(results);
},
createFilterPosts(queryString) {
return posts => {
return post => {
return (
posts.contact.toLowerCase().indexOf(queryString.toLowerCase()) === 0
post.contact.toLowerCase().indexOf(queryString.toLowerCase()) === 0
);
};
},
loadPosts() {
console.error("loadPosts");
console.info("loadPosts");
// console.info("$ref:" + );
fetchPosts().then(res=>{
console.info("res:" + res);
console.info("res.data:" + JSON.stringify(res.data));
this.posts = res.data;
for (let i of this.posts) {
i.value = i.contact; //将想要展示的数据作为value
}
})
console.info("posts:" + JSON.stringify(this.posts));
},
handleSelectPosts(item) {
this.bill.postId = item.id;
@@ -501,30 +551,30 @@ export default {
console.log("postContact:" + this.bill.postContact);
},
handleIconClickPosts(ev) {
this.isPostEditShow = true;
console.log('handleIconClickPosts');
console.log(ev);
},
/** AutoComplete Titles 处理*/
querySearchAsyncTitles(queryString, cb) {
var list = [{}];
fetchTitles().then(function(response) {
console.info(response);
for (let i of response.data) {
i.value = i.abbr; // 将想要展示的数据作为value
}
list = response.data;
cb(list);
});
var results = queryString ? this.titles.filter(this.createFilterTitles(queryString)):this.titles;
cb(results);
},
createFilterTitles(queryString) {
return titles => {
return title => {
return (
titles.abbr.toLowerCase().indexOf(queryString.toLowerCase()) === 0
title.abbr.toLowerCase().indexOf(queryString.toLowerCase()) === 0
);
};
},
loadTitles() {
console.error("loadTitles");
fetchTitles().then(res=>{
this.titles = res.data;
for (let i of this.titles) {
i.value = i.abbr; //将想要展示的数据作为value
}
})
},
handleSelectTitles(item) {
this.focusRow.titleId = item.id;
@@ -539,20 +589,13 @@ export default {
},
/** AutoComplete Merchant 处理*/
querySearchAsyncMerchant(queryString, cb) {
var list = [{}];
fetchMerchants().then(function(response) {
for (let i of response.data) {
i.value = i.abbr; // 将想要展示的数据作为value
}
list = response.data;
cb(list);
});
var results = queryString ? this.merchants.filter(this.createFilterMerchant(queryString)):this.merchants;
cb(results);
},
createFilterMerchant(queryString) {
return merchants => {
return merchant => {
return (
merchants.contact.toLowerCase().indexOf(queryString.toLowerCase()) ===
0
merchant.abbr.toLowerCase().indexOf(queryString.toLowerCase()) === 0
);
};
},
@@ -567,27 +610,30 @@ export default {
console.log(ev);
},
loadMerchant() {
console.error("loadTitles");
fetchMerchants().then(res=>{
this.merchants = res.data;
for (let i of this.merchants) {
i.value = i.abbr; //将想要展示的数据作为value
}
})
},
/** 清理当前缓存 */
clearFocusRow() {
// console.log(this.$refs)
this.focusRow = {};
console.info("clearFocusRow");
}
},
mounted() {
// this.posts = this.loadPosts();
// console.info('mounted.this.posts:' + posts);
async mounted() {
await this.loadPosts();
await this.loadTitles();
await this.loadMerchant();
}
};
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
.subtitle {
font-size: 12px;
color: #b4b4b4;
}
h1,
h2 {
font-weight: normal;
@@ -659,6 +705,36 @@ a {
text-align: left;
/* width: 800px; */
}
.name {
text-overflow: ellipsis;
overflow: hidden;
/* line-height: small; */
}
.subtitle {
font-size: 13px;
color: #b4b4b4;
}
/* .highlighted .subtitle {
color: #ddd;
} */
/* .my-autocomplete {
li {
line-height: small;
padding: 1px;
.name {
text-overflow: ellipsis;
overflow: hidden;
}
.subtitle {
font-size: 12px;
color: #b4b4b4;
}
.highlighted .addr {
color: #ddd;
}
}
} */
.posts,
.titles,
.merchants {
+37 -17
View File
@@ -29,24 +29,25 @@
</template>
<script>
import { UserLoginAction } from "../api/user.js";
import { login } from '../api/user.js';
import { encrypt,decrypt } from "../utils/encoder.js";
export default {
name: "Login",
name: 'Login',
data() {
return {
disabled: false,
ruleForm: {
username: "",
password: ""
username: '',
password: ''
},
rules: {
username: [
{ required: true, message: "请输入户名", trigger: "blur" },
{ min: 3, max: 15, message: "长度在 3 到 15 个字符", trigger: "blur" }
{ required: true, message: '请输入户名', trigger: 'blur' },
{ min: 3, max: 15, message: '长度在 3 到 30 个字符', trigger: 'blur' }
],
password: [
{ required: true, message: "请输入密码", trigger: "change" },
{ min: 3, max: 15, message: "长度在 3 到 15 个字符", trigger: "blur" }
{ required: true, message: '请输入密码', trigger: 'change' },
{ min: 3, max: 15, message: '长度在 3 到 30 个字符', trigger: 'blur' }
]
}
};
@@ -57,19 +58,26 @@ export default {
var _this = this;
if (valid) {
this.disabled = true;
UserLoginAction(this.ruleForm)
/** 加密处理 */
var encoderStr = encrypt(this.ruleForm.password);
console.log('密文:' + encoderStr);
console.log('解密:' + decrypt(encoderStr));
this.ruleForm.password = encoderStr;
/** 访问登录服务 */
login(this.ruleForm)
.then(res => {
var token = "Bearer " + res.data.token;
console.log("token:" + token);
window.localStorage.setItem("token", token);
this.$message({ message: "登录成功", type: "success" });
var token = 'Bearer ' + res.data.token;
console.log('token:' + token);
window.localStorage.setItem('token', token);
this.$message({ message: '登录成功', type: 'success' });
this.$router.push({ path: 'Gird' })
})
.catch(function(error) {
// _this.$message({ message: error.response.statusText, type: 'error' });
// _this.$message.error();
_this.disabled = false;
_this.disabled = false;
if (error.response) {
// The request was made and the server responded with a status code
// that falls out of the range of 2xx
@@ -86,20 +94,32 @@ export default {
_this.$message.error('未知错误');
} else {
// Something happened in setting up the request that triggered an Error
console.log("Error", error.message);
_this.$message.error(error.response.message);
// console.log('Error', error.message);
console.log('Error', error.status);
if(error.status == 401 ){
_this.$message.error('用户名或密码有误');
_this.reset('ruleForm');
// _this.ruleForm.password = '';
}
else {
// _this.ruleForm.password = '';
_this.$message.error(error.status.toString() + error.message);
_this.reset('ruleForm');
}
}
console.log(error.config);
// _this.$message.error(error.response.statusText);
});
} else {
console.log("error submit!!");
console.log('error submit!!');
return false;
}
});
},
reset(formName) {
console.log('reset')
this.$refs[formName].resetFields();
this.$refs[formName].focus();
this.disabled = false;
}
}
+24 -10
View File
@@ -9,10 +9,15 @@
<el-input v-model="ruleForm.fullname" clearable placeholder="请输入姓名"></el-input>
</el-form-item>
<el-form-item label="密码" prop="password">
<el-input v-model="ruleForm.password" @keyup.enter.native="onSubmit('ruleForm')" show-password placeholder="请输入密码"></el-input>
<el-input
v-model="ruleForm.password"
@keyup.enter.native="onSubmit('ruleForm')"
show-password
placeholder="请输入密码"
></el-input>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="onSubmit('ruleForm')" :disabled="disabled">登录</el-button>
<el-button type="primary" @click="onSubmit('ruleForm')" :disabled="disabled">注册</el-button>
<el-button @click="reset('ruleForm')">重置</el-button>
</el-form-item>
</el-form>
@@ -22,6 +27,7 @@
<script>
import { register } from "../api/user.js";
import { encrypt,decrypt } from "../utils/encoder.js";
export default {
name: "Login",
data() {
@@ -35,7 +41,7 @@ export default {
rules: {
username: [
{ required: true, message: "请输入用户名", trigger: "blur" },
{ min: 3, max: 15, message: "长度在 3 到 15 个字符", trigger: "blur" }
{ min: 3, max: 15, message: "长度在 3 到 30 个字符", trigger: "blur" }
],
fullname: [
{ required: true, message: "请输入姓名", trigger: "blur" },
@@ -43,7 +49,7 @@ export default {
],
password: [
{ required: true, message: "请输入密码", trigger: "change" },
{ min: 3, max: 15, message: "长度在 3 到 15 个字符", trigger: "blur" }
{ min: 3, max: 15, message: "长度在 3 到 30 个字符", trigger: "blur" }
]
}
};
@@ -53,20 +59,28 @@ export default {
this.$refs[formName].validate(valid => {
var _this = this;
if (valid) {
this.disabled = true;
register(this.ruleForm)
this.disabled = true;
/** 加密处理 */
var encoderStr = encrypt(this.ruleForm.password);
console.log('解密:' + decrypt(encoderStr));
this.ruleForm.password = encoderStr;
/** 访问注册服务 */
register(this.ruleForm)
.then(res => {
this.$message({ message: "注册成功", type: "success" });
this.$router.push({ path: 'Login' })
this.$router.push({ path: "Login" });
})
.catch(function(error) {
// _this.$message({ message: error.response.statusText, type: 'error' });
// _this.$message.error();
_this.disabled = false;
_this.disabled = false;
if (error.response) {
// The request was made and the server responded with a status code
// that falls out of the range of 2xx
_this.$message({ message: error.response.data.message, type: 'error' });
_this.$message({
message: error.response.data.message,
type: "error"
});
console.log(error.response.data);
console.log(error.response.status);
console.log(error.response.headers);
@@ -76,7 +90,7 @@ export default {
// http.ClientRequest in node.js
// _this.$message.error(error.request);
console.log(error.request);
_this.$message.error('未知错误');
_this.$message.error("未知错误");
} else {
// Something happened in setting up the request that triggered an Error
console.log("Error", error.message);
@@ -1,27 +0,0 @@
// A custom Nightwatch assertion.
// The assertion name is the filename.
// Example usage:
//
// browser.assert.elementCount(selector, count)
//
// For more information on custom assertions see:
// http://nightwatchjs.org/guide#writing-custom-assertions
exports.assertion = function (selector, count) {
this.message = 'Testing if element <' + selector + '> has count: ' + count
this.expected = count
this.pass = function (val) {
return val === this.expected
}
this.value = function (res) {
return res.value
}
this.command = function (cb) {
var self = this
return this.api.execute(function (selector) {
return document.querySelectorAll(selector).length
}, [selector], function (res) {
cb.call(self, res)
})
}
}
@@ -1,46 +0,0 @@
require('babel-register')
var config = require('../../config')
// http://nightwatchjs.org/gettingstarted#settings-file
module.exports = {
src_folders: ['test/e2e/specs'],
output_folder: 'test/e2e/reports',
custom_assertions_path: ['test/e2e/custom-assertions'],
selenium: {
start_process: true,
server_path: require('selenium-server').path,
host: '127.0.0.1',
port: 4444,
cli_args: {
'webdriver.chrome.driver': require('chromedriver').path
}
},
test_settings: {
default: {
selenium_port: 4444,
selenium_host: 'localhost',
silent: true,
globals: {
devServerURL: 'http://localhost:' + (process.env.PORT || config.dev.port)
}
},
chrome: {
desiredCapabilities: {
browserName: 'chrome',
javascriptEnabled: true,
acceptSslCerts: true
}
},
firefox: {
desiredCapabilities: {
browserName: 'firefox',
javascriptEnabled: true,
acceptSslCerts: true
}
}
}
}
-48
View File
@@ -1,48 +0,0 @@
// 1. start the dev server using production config
process.env.NODE_ENV = 'testing'
const webpack = require('webpack')
const DevServer = require('webpack-dev-server')
const webpackConfig = require('../../build/webpack.prod.conf')
const devConfigPromise = require('../../build/webpack.dev.conf')
let server
devConfigPromise.then(devConfig => {
const devServerOptions = devConfig.devServer
const compiler = webpack(webpackConfig)
server = new DevServer(compiler, devServerOptions)
const port = devServerOptions.port
const host = devServerOptions.host
return server.listen(port, host)
})
.then(() => {
// 2. run the nightwatch test suite against it
// to run in additional browsers:
// 1. add an entry in test/e2e/nightwatch.conf.js under "test_settings"
// 2. add it to the --env flag below
// or override the environment flag, for example: `npm run e2e -- --env chrome,firefox`
// For more information on Nightwatch's config file, see
// http://nightwatchjs.org/guide#settings-file
let opts = process.argv.slice(2)
if (opts.indexOf('--config') === -1) {
opts = opts.concat(['--config', 'test/e2e/nightwatch.conf.js'])
}
if (opts.indexOf('--env') === -1) {
opts = opts.concat(['--env', 'chrome'])
}
const spawn = require('cross-spawn')
const runner = spawn('./node_modules/.bin/nightwatch', opts, { stdio: 'inherit' })
runner.on('exit', function (code) {
server.close()
process.exit(code)
})
runner.on('error', function (err) {
server.close()
throw err
})
})
-19
View File
@@ -1,19 +0,0 @@
// For authoring Nightwatch tests, see
// http://nightwatchjs.org/guide#usage
module.exports = {
'default e2e tests': function (browser) {
// automatically uses dev Server port from /config.index.js
// default: http://localhost:8080
// see nightwatch.conf.js
const devServer = browser.globals.devServerURL
browser
.url(devServer)
.waitForElementVisible('#app', 5000)
.assert.elementPresent('.hello')
.assert.containsText('h1', 'Welcome to Your Vue.js App')
.assert.elementCount('img', 1)
.end()
}
}
-7
View File
@@ -1,7 +0,0 @@
{
"env": {
"jest": true
},
"globals": {
}
}
-30
View File
@@ -1,30 +0,0 @@
const path = require('path')
module.exports = {
rootDir: path.resolve(__dirname, '../../'),
moduleFileExtensions: [
'js',
'json',
'vue'
],
moduleNameMapper: {
'^@/(.*)$': '<rootDir>/src/$1'
},
transform: {
'^.+\\.js$': '<rootDir>/node_modules/babel-jest',
'.*\\.(vue)$': '<rootDir>/node_modules/vue-jest'
},
testPathIgnorePatterns: [
'<rootDir>/test/e2e'
],
snapshotSerializers: ['<rootDir>/node_modules/jest-serializer-vue'],
setupFiles: ['<rootDir>/test/unit/setup'],
mapCoverage: true,
coverageDirectory: '<rootDir>/test/unit/coverage',
collectCoverageFrom: [
'src/**/*.{js,vue}',
'!src/main.js',
'!src/router/index.js',
'!**/node_modules/**'
]
}
-3
View File
@@ -1,3 +0,0 @@
import Vue from 'vue'
Vue.config.productionTip = false
@@ -1,11 +0,0 @@
import Vue from 'vue'
import HelloWorld from '@/components/HelloWorld'
describe('HelloWorld.vue', () => {
it('should render correct contents', () => {
const Constructor = Vue.extend(HelloWorld)
const vm = new Constructor().$mount()
expect(vm.$el.querySelector('.hello h1').textContent)
.toEqual('Welcome to Your Vue.js App')
})
})
Executable
+8
View File
@@ -0,0 +1,8 @@
#!/usr/bin/env sh
pid=`ps -ef | grep bill| grep java | awk '{print $2}'`
if [ $pid ]; then
echo $pid
kill -9 $pid
fi